1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2019 Intel Corporation 3 */ 4 5 #include <getopt.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <arpa/inet.h> 9 #include <sys/socket.h> 10 11 #include <rte_cycles.h> 12 #include <rte_errno.h> 13 #include <rte_ip.h> 14 #include <rte_random.h> 15 #include <rte_malloc.h> 16 #include <rte_lpm.h> 17 #include <rte_lpm6.h> 18 #include <rte_fib.h> 19 #include <rte_fib6.h> 20 21 #define PRINT_USAGE_START "%s [EAL options] --\n" 22 23 #define GET_CB_FIELD(in, fd, base, lim, dlm) do { \ 24 unsigned long val; \ 25 char *end_fld; \ 26 errno = 0; \ 27 val = strtoul((in), &end_fld, (base)); \ 28 if (errno != 0 || end_fld[0] != (dlm) || val > (lim)) \ 29 return -EINVAL; \ 30 (fd) = (typeof(fd))val; \ 31 (in) = end_fld + 1; \ 32 } while (0) 33 34 #define DEF_ROUTES_NUM 0x10000 35 #define DEF_LOOKUP_IPS_NUM 0x100000 36 #define BURST_SZ 64 37 #define DEFAULT_LPM_TBL8 100000U 38 39 #define CMP_FLAG (1 << 0) 40 #define CMP_ALL_FLAG (1 << 1) 41 #define IPV6_FLAG (1 << 2) 42 #define FIB_RIB_TYPE (1 << 3) 43 #define FIB_V4_DIR_TYPE (1 << 4) 44 #define FIB_V6_TRIE_TYPE (1 << 4) 45 #define FIB_TYPE_MASK (FIB_RIB_TYPE|FIB_V4_DIR_TYPE|FIB_V6_TRIE_TYPE) 46 #define SHUFFLE_FLAG (1 << 7) 47 #define DRY_RUN_FLAG (1 << 8) 48 49 static char *distrib_string; 50 static char line[LINE_MAX]; 51 52 enum { 53 RT_PREFIX, 54 RT_NEXTHOP, 55 RT_NUM 56 }; 57 58 #ifndef NIPQUAD 59 #define NIPQUAD_FMT "%u.%u.%u.%u" 60 #define NIPQUAD(addr) \ 61 (unsigned)((unsigned char *)&addr)[3], \ 62 (unsigned)((unsigned char *)&addr)[2], \ 63 (unsigned)((unsigned char *)&addr)[1], \ 64 (unsigned)((unsigned char *)&addr)[0] 65 66 #define NIPQUAD6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" 67 #define NIPQUAD6(addr) \ 68 ((uint8_t *)addr)[0] << 8 | \ 69 ((uint8_t *)addr)[1], \ 70 ((uint8_t *)addr)[2] << 8 | \ 71 ((uint8_t *)addr)[3], \ 72 ((uint8_t *)addr)[4] << 8 | \ 73 ((uint8_t *)addr)[5], \ 74 ((uint8_t *)addr)[6] << 8 | \ 75 ((uint8_t *)addr)[7], \ 76 ((uint8_t *)addr)[8] << 8 | \ 77 ((uint8_t *)addr)[9], \ 78 ((uint8_t *)addr)[10] << 8 | \ 79 ((uint8_t *)addr)[11], \ 80 ((uint8_t *)addr)[12] << 8 | \ 81 ((uint8_t *)addr)[13], \ 82 ((uint8_t *)addr)[14] << 8 | \ 83 ((uint8_t *)addr)[15] 84 #endif 85 86 static struct { 87 const char *prgname; 88 const char *routes_file; 89 const char *lookup_ips_file; 90 const char *routes_file_s; 91 const char *lookup_ips_file_s; 92 void *rt; 93 void *lookup_tbl; 94 uint32_t nb_routes; 95 uint32_t nb_lookup_ips; 96 uint32_t nb_lookup_ips_rnd; 97 uint32_t nb_routes_per_depth[128 + 1]; 98 uint32_t flags; 99 uint32_t tbl8; 100 uint8_t ent_sz; 101 uint8_t rnd_lookup_ips_ratio; 102 uint8_t print_fract; 103 uint8_t lookup_fn; 104 } config = { 105 .routes_file = NULL, 106 .lookup_ips_file = NULL, 107 .nb_routes = DEF_ROUTES_NUM, 108 .nb_lookup_ips = DEF_LOOKUP_IPS_NUM, 109 .nb_lookup_ips_rnd = 0, 110 .nb_routes_per_depth = {0}, 111 .flags = FIB_V4_DIR_TYPE, 112 .tbl8 = DEFAULT_LPM_TBL8, 113 .ent_sz = 4, 114 .rnd_lookup_ips_ratio = 0, 115 .print_fract = 10, 116 .lookup_fn = 0 117 }; 118 119 struct rt_rule_4 { 120 uint32_t addr; 121 uint8_t depth; 122 uint64_t nh; 123 }; 124 125 struct rt_rule_6 { 126 uint8_t addr[16]; 127 uint8_t depth; 128 uint64_t nh; 129 }; 130 131 static uint64_t 132 get_rnd_rng(uint64_t l, uint64_t u) 133 { 134 if (l == u) 135 return l; 136 else 137 return (rte_rand() % (u - l) + l); 138 } 139 140 static __rte_always_inline __rte_pure uint8_t 141 bits_in_nh(uint8_t nh_sz) 142 { 143 return 8 * (1 << nh_sz); 144 } 145 146 static __rte_always_inline __rte_pure uint64_t 147 get_max_nh(uint8_t nh_sz) 148 { 149 /* min between fib and lpm6 which is 21 bits */ 150 return RTE_MIN(((1ULL << (bits_in_nh(nh_sz) - 1)) - 1), 151 (1ULL << 21) - 1); 152 } 153 154 static int 155 get_fib_type(void) 156 { 157 if (config.flags & IPV6_FLAG) { 158 if ((config.flags & FIB_TYPE_MASK) == FIB_V6_TRIE_TYPE) 159 return RTE_FIB6_TRIE; 160 else 161 return RTE_FIB6_DUMMY; 162 } else { 163 if ((config.flags & FIB_TYPE_MASK) == FIB_V4_DIR_TYPE) 164 return RTE_FIB_DIR24_8; 165 if ((config.flags & FIB_TYPE_MASK) == FIB_RIB_TYPE) 166 return RTE_FIB_DUMMY; 167 } 168 return -1; 169 } 170 171 static int 172 complete_distrib(uint8_t depth_lim, const uint32_t n, uint8_t rpd[], 173 uint32_t nrpd[]) 174 { 175 uint8_t depth; 176 uint32_t nr = 0; 177 uint8_t m = 0; 178 179 /* 180 * complete number of routes for every depth 181 * that was configured with ratio 182 */ 183 for (depth = 0; depth <= depth_lim; depth++) { 184 if (rpd[depth] != 0) { 185 if (rpd[depth] == UINT8_MAX) 186 config.nb_routes_per_depth[depth] = 187 nrpd[depth]; 188 else 189 config.nb_routes_per_depth[depth] = 190 (n * rpd[depth]) / 100; 191 192 nr += config.nb_routes_per_depth[depth]; 193 m++; 194 } 195 } 196 197 if (nr > n) { 198 printf("Too much configured routes\n"); 199 return -1; 200 } 201 202 /*complete number of routes for every unspecified depths*/ 203 for (depth = 0; depth <= depth_lim; depth++) { 204 if (rpd[depth] == 0) { 205 /*we don't need more than two /1 routes*/ 206 uint64_t max_routes_per_depth = 207 1ULL << RTE_MIN(depth, 63); 208 uint32_t avg_routes_left = (n - nr) / 209 (depth_lim + 1 - m++); 210 config.nb_routes_per_depth[depth] = 211 RTE_MIN(max_routes_per_depth, avg_routes_left); 212 nr += config.nb_routes_per_depth[depth]; 213 } 214 } 215 216 return 0; 217 } 218 219 static int 220 parse_distrib(uint8_t depth_lim, const uint32_t n) 221 { 222 uint8_t rpd[128 + 1] = {0}; /*routes ratios per depth including /0 */ 223 uint32_t nrpd[128 + 1] = {0}; /* number of routes per depth */ 224 uint32_t n_routes; 225 uint8_t depth, ratio, ratio_acc = 0; 226 char *in; 227 228 in = strtok(distrib_string, ","); 229 230 /*parse configures routes percentage ratios*/ 231 while (in != NULL) { 232 GET_CB_FIELD(in, depth, 0, UINT8_MAX, ':'); 233 if (in[strlen(in) - 1] == '%') { 234 in[strlen(in) - 1] = 0; 235 GET_CB_FIELD(in, ratio, 0, UINT8_MAX, '\0'); 236 if (depth > depth_lim) { 237 printf("Depth /%d is bigger than maximum " 238 "allowed depth /%d for this AF\n", 239 depth, depth_lim); 240 return -EINVAL; 241 } 242 if (ratio > 100) { 243 printf("Ratio for depth /%d is bigger " 244 "than 100%%\n", depth); 245 return -EINVAL; 246 } 247 if ((depth < 64) && ((n * ratio) / 100) > 248 (1ULL << depth)) { 249 printf("Configured ratio %d%% for depth /%d " 250 "has %d different routes, but maximum " 251 "is %lu\n", ratio, depth, 252 ((n * ratio) / 100), (1UL << depth)); 253 return -EINVAL; 254 } 255 rpd[depth] = ratio; 256 /*configured zero routes for a given depth*/ 257 if (ratio == 0) 258 rpd[depth] = UINT8_MAX; 259 /*sum of all percentage ratios*/ 260 ratio_acc += ratio; 261 } else { 262 GET_CB_FIELD(in, n_routes, 0, UINT32_MAX, '\0'); 263 rpd[depth] = UINT8_MAX; 264 nrpd[depth] = n_routes; 265 } 266 267 /*number of configured depths in*/ 268 in = strtok(NULL, ","); 269 } 270 271 if (ratio_acc > 100) { 272 printf("Total ratio's sum is bigger than 100%%\n"); 273 return -EINVAL; 274 } 275 276 return complete_distrib(depth_lim, n, rpd, nrpd); 277 } 278 279 static void 280 shuffle_rt_4(struct rt_rule_4 *rt, int n) 281 { 282 struct rt_rule_4 tmp; 283 int i, j; 284 285 for (i = 0; i < n; i++) { 286 j = rte_rand() % n; 287 tmp.addr = rt[i].addr; 288 tmp.depth = rt[i].depth; 289 tmp.nh = rt[i].nh; 290 291 rt[i].addr = rt[j].addr; 292 rt[i].depth = rt[j].depth; 293 rt[i].nh = rt[j].nh; 294 295 rt[j].addr = tmp.addr; 296 rt[j].depth = tmp.depth; 297 rt[j].nh = tmp.nh; 298 } 299 } 300 301 static void 302 shuffle_rt_6(struct rt_rule_6 *rt, int n) 303 { 304 struct rt_rule_6 tmp; 305 int i, j; 306 307 for (i = 0; i < n; i++) { 308 j = rte_rand() % n; 309 memcpy(tmp.addr, rt[i].addr, 16); 310 tmp.depth = rt[i].depth; 311 tmp.nh = rt[i].nh; 312 313 memcpy(rt[i].addr, rt[j].addr, 16); 314 rt[i].depth = rt[j].depth; 315 rt[i].nh = rt[j].nh; 316 317 memcpy(rt[j].addr, tmp.addr, 16); 318 rt[j].depth = tmp.depth; 319 rt[j].nh = tmp.nh; 320 } 321 } 322 323 static void 324 gen_random_rt_4(struct rt_rule_4 *rt, int nh_sz) 325 { 326 uint32_t i, j, k = 0; 327 328 if (config.nb_routes_per_depth[0] != 0) { 329 rt[k].addr = 0; 330 rt[k].depth = 0; 331 rt[k++].nh = rte_rand() & get_max_nh(nh_sz); 332 } 333 334 for (i = 1; i <= 32; i++) { 335 double edge = 0; 336 double step; 337 step = (double)(1ULL << i) / config.nb_routes_per_depth[i]; 338 for (j = 0; j < config.nb_routes_per_depth[i]; 339 j++, k++, edge += step) { 340 uint64_t rnd_val = get_rnd_rng((uint64_t)edge, 341 (uint64_t)(edge + step)); 342 rt[k].addr = rnd_val << (32 - i); 343 rt[k].depth = i; 344 rt[k].nh = rte_rand() & get_max_nh(nh_sz); 345 } 346 } 347 } 348 349 static void 350 complete_v6_addr(uint32_t *addr, uint32_t rnd, int n) 351 { 352 int i; 353 354 for (i = 0; i < n; i++) 355 addr[i] = rte_rand(); 356 addr[i++] = rnd; 357 for (; i < 4; i++) 358 addr[i] = 0; 359 } 360 361 static void 362 gen_random_rt_6(struct rt_rule_6 *rt, int nh_sz) 363 { 364 uint32_t a, i, j, k = 0; 365 366 if (config.nb_routes_per_depth[0] != 0) { 367 memset(rt[k].addr, 0, 16); 368 rt[k].depth = 0; 369 rt[k++].nh = rte_rand() & get_max_nh(nh_sz); 370 } 371 372 for (a = 0; a < 4; a++) { 373 for (i = 1; i <= 32; i++) { 374 uint32_t rnd; 375 double edge = 0; 376 double step = (double)(1ULL << i) / 377 config.nb_routes_per_depth[(a * 32) + i]; 378 for (j = 0; j < config.nb_routes_per_depth[a * 32 + i]; 379 j++, k++, edge += step) { 380 uint64_t rnd_val = get_rnd_rng((uint64_t)edge, 381 (uint64_t)(edge + step)); 382 rnd = rte_cpu_to_be_32(rnd_val << (32 - i)); 383 complete_v6_addr((uint32_t *)rt[k].addr, 384 rnd, a); 385 rt[k].depth = (a * 32) + i; 386 rt[k].nh = rte_rand() & get_max_nh(nh_sz); 387 } 388 } 389 } 390 } 391 392 static inline void 393 set_rnd_ipv6(uint8_t *addr, uint8_t *route, int depth) 394 { 395 int i; 396 397 for (i = 0; i < 16; i++) 398 addr[i] = rte_rand(); 399 400 for (i = 0; i < 16; i++) { 401 if (depth >= 8) 402 addr[i] = route[i]; 403 else if (depth > 0) { 404 addr[i] &= (uint16_t)UINT8_MAX >> depth; 405 addr[i] |= route[i] & UINT8_MAX << (8 - depth); 406 } else 407 return; 408 depth -= 8; 409 } 410 } 411 412 static void 413 gen_rnd_lookup_tbl(int af) 414 { 415 uint32_t *tbl4 = config.lookup_tbl; 416 uint8_t *tbl6 = config.lookup_tbl; 417 struct rt_rule_4 *rt4 = (struct rt_rule_4 *)config.rt; 418 struct rt_rule_6 *rt6 = (struct rt_rule_6 *)config.rt; 419 uint32_t i, j; 420 421 if (af == AF_INET) { 422 for (i = 0, j = 0; i < config.nb_lookup_ips; 423 i++, j = (j + 1) % config.nb_routes) { 424 if ((rte_rand() % 100) < config.rnd_lookup_ips_ratio) { 425 tbl4[i] = rte_rand(); 426 config.nb_lookup_ips_rnd++; 427 } else 428 tbl4[i] = rt4[j].addr | (rte_rand() & 429 ((1ULL << (32 - rt4[j].depth)) - 1)); 430 } 431 } else { 432 for (i = 0, j = 0; i < config.nb_lookup_ips; 433 i++, j = (j + 1) % config.nb_routes) { 434 if ((rte_rand() % 100) < config.rnd_lookup_ips_ratio) { 435 set_rnd_ipv6(&tbl6[i * 16], rt6[j].addr, 0); 436 config.nb_lookup_ips_rnd++; 437 } else { 438 set_rnd_ipv6(&tbl6[i * 16], rt6[j].addr, 439 rt6[j].depth); 440 } 441 } 442 } 443 } 444 445 static int 446 _inet_net_pton(int af, char *prefix, void *addr) 447 { 448 const char *dlm = "/"; 449 char *s, *sp; 450 int ret, depth; 451 unsigned int max_depth; 452 453 if ((prefix == NULL) || (addr == NULL)) 454 return -EINVAL; 455 456 s = strtok_r(prefix, dlm, &sp); 457 if (s == NULL) 458 return -EINVAL; 459 460 ret = inet_pton(af, s, addr); 461 if (ret != 1) 462 return -errno; 463 464 s = strtok_r(NULL, dlm, &sp); 465 max_depth = (af == AF_INET) ? 32 : 128; 466 GET_CB_FIELD(s, depth, 0, max_depth, 0); 467 468 return depth; 469 } 470 471 static int 472 parse_rt_4(FILE *f) 473 { 474 int ret, i, j = 0; 475 char *s, *sp, *in[RT_NUM]; 476 static const char *dlm = " \t\n"; 477 int string_tok_nb = RTE_DIM(in); 478 struct rt_rule_4 *rt; 479 480 rt = (struct rt_rule_4 *)config.rt; 481 482 while (fgets(line, sizeof(line), f) != NULL) { 483 s = line; 484 for (i = 0; i != string_tok_nb; i++) { 485 in[i] = strtok_r(s, dlm, &sp); 486 if (in[i] == NULL) 487 return -EINVAL; 488 s = NULL; 489 } 490 491 ret = _inet_net_pton(AF_INET, in[RT_PREFIX], &rt[j].addr); 492 if (ret == -1) 493 return -errno; 494 495 rt[j].addr = rte_be_to_cpu_32(rt[j].addr); 496 rt[j].depth = ret; 497 config.nb_routes_per_depth[ret]++; 498 GET_CB_FIELD(in[RT_NEXTHOP], rt[j].nh, 0, 499 UINT32_MAX, 0); 500 j++; 501 } 502 return 0; 503 } 504 505 static int 506 parse_rt_6(FILE *f) 507 { 508 int ret, i, j = 0; 509 char *s, *sp, *in[RT_NUM]; 510 static const char *dlm = " \t\n"; 511 int string_tok_nb = RTE_DIM(in); 512 struct rt_rule_6 *rt; 513 514 rt = (struct rt_rule_6 *)config.rt; 515 516 while (fgets(line, sizeof(line), f) != NULL) { 517 s = line; 518 for (i = 0; i != string_tok_nb; i++) { 519 in[i] = strtok_r(s, dlm, &sp); 520 if (in[i] == NULL) 521 return -EINVAL; 522 s = NULL; 523 } 524 525 ret = _inet_net_pton(AF_INET6, in[RT_PREFIX], rt[j].addr); 526 if (ret < 0) 527 return ret; 528 529 rt[j].depth = ret; 530 config.nb_routes_per_depth[ret]++; 531 GET_CB_FIELD(in[RT_NEXTHOP], rt[j].nh, 0, 532 UINT32_MAX, 0); 533 j++; 534 } 535 536 return 0; 537 } 538 539 static int 540 parse_lookup(FILE *f, int af) 541 { 542 int ret, i = 0; 543 uint8_t *tbl = (uint8_t *)config.lookup_tbl; 544 int step = (af == AF_INET) ? 4 : 16; 545 char *s; 546 547 while (fgets(line, sizeof(line), f) != NULL) { 548 s = strtok(line, " \t\n"); 549 if (s == NULL) 550 return -EINVAL; 551 ret = inet_pton(af, s, &tbl[i]); 552 if (ret != 1) 553 return -EINVAL; 554 i += step; 555 } 556 return 0; 557 } 558 559 static int 560 dump_lookup(int af) 561 { 562 FILE *f; 563 uint32_t *tbl4 = config.lookup_tbl; 564 uint8_t *tbl6 = config.lookup_tbl; 565 uint32_t i; 566 567 f = fopen(config.lookup_ips_file_s, "w"); 568 if (f == NULL) { 569 printf("Can not open file %s\n", config.lookup_ips_file_s); 570 return -1; 571 } 572 573 if (af == AF_INET) { 574 for (i = 0; i < config.nb_lookup_ips; i++) 575 fprintf(f, NIPQUAD_FMT"\n", NIPQUAD(tbl4[i])); 576 } else { 577 for (i = 0; i < config.nb_lookup_ips; i++) 578 fprintf(f, NIPQUAD6_FMT"\n", NIPQUAD6(&tbl6[i * 16])); 579 } 580 fclose(f); 581 return 0; 582 } 583 584 static void 585 print_config(void) 586 { 587 uint8_t depth_lim; 588 char dlm; 589 int i; 590 591 depth_lim = ((config.flags & IPV6_FLAG) == IPV6_FLAG) ? 128 : 32; 592 593 fprintf(stdout, 594 "Routes total: %u\n" 595 "Routes distribution:\n", config.nb_routes); 596 597 for (i = 1; i <= depth_lim; i++) { 598 fprintf(stdout, 599 "depth /%d:%u", i, config.nb_routes_per_depth[i]); 600 if (i % 4 == 0) 601 dlm = '\n'; 602 else 603 dlm = '\t'; 604 fprintf(stdout, "%c", dlm); 605 } 606 607 fprintf(stdout, 608 "Lookup tuples: %u\n" 609 "Configured ratios of random ips for lookup: %u\n" 610 "Random lookup ips: %u\n", 611 config.nb_lookup_ips, config.rnd_lookup_ips_ratio, 612 config.nb_lookup_ips_rnd); 613 } 614 615 static void 616 print_usage(void) 617 { 618 fprintf(stdout, 619 PRINT_USAGE_START 620 "[-f <routes file>]\n" 621 "[-t <ip's file for lookup>]\n" 622 "[-n <number of routes (if -f is not specified)>]\n" 623 "[-l <number of ip's for lookup (if -t is not specified)>]\n" 624 "[-d <\",\" separated \"depth:n%%\"routes depth distribution" 625 "(if -f is not specified)>]\n" 626 "[-r <percentage ratio of random ip's to lookup" 627 "(if -t is not specified)>]\n" 628 "[-c <do comparison with LPM library>]\n" 629 "[-6 <do tests with ipv6 (default ipv4)>]\n" 630 "[-s <shuffle randomly generated routes>]\n" 631 "[-a <check nexthops for all ipv4 address space" 632 "(only valid with -c)>]\n" 633 "[-b <fib algorithm>]\n\tavailable options for ipv4\n" 634 "\t\trib - RIB based FIB\n" 635 "\t\tdir - DIR24_8 based FIB\n" 636 "\tavailable options for ipv6:\n" 637 "\t\trib - RIB based FIB\n" 638 "\t\ttrie - TRIE based FIB\n" 639 "defaults are: dir for ipv4 and trie for ipv6\n" 640 "[-e <entry size (valid only for dir and trie fib types): " 641 "1/2/4/8 (default 4)>]\n" 642 "[-g <number of tbl8's for dir24_8 or trie FIBs>]\n" 643 "[-w <path to the file to dump routing table>]\n" 644 "[-u <path to the file to dump ip's for lookup>]\n" 645 "[-v <type of lookup function:" 646 "\ts1, s2, s3 (3 types of scalar), v (vector) -" 647 " for DIR24_8 based FIB\n" 648 "\ts, v - for TRIE based ipv6 FIB>]\n", 649 config.prgname); 650 } 651 652 static int 653 check_config(void) 654 { 655 if ((config.routes_file == NULL) && (config.lookup_ips_file != NULL)) { 656 printf("-t option only valid with -f option\n"); 657 return -1; 658 } 659 660 if ((config.flags & CMP_ALL_FLAG) && (config.flags & IPV6_FLAG)) { 661 printf("-a flag is only valid for ipv4\n"); 662 return -1; 663 } 664 665 if ((config.flags & CMP_ALL_FLAG) && 666 ((config.flags & CMP_FLAG) != CMP_FLAG)) { 667 printf("-a flag is valid only with -c flag\n"); 668 return -1; 669 } 670 671 if (!((config.ent_sz == 1) || (config.ent_sz == 2) || 672 (config.ent_sz == 4) || (config.ent_sz == 8))) { 673 printf("wrong -e option %d, can be 1 or 2 or 4 or 8\n", 674 config.ent_sz); 675 return -1; 676 } 677 678 if ((config.ent_sz == 1) && (config.flags & IPV6_FLAG)) { 679 printf("-e 1 is valid only for ipv4\n"); 680 return -1; 681 } 682 return 0; 683 } 684 685 static void 686 parse_opts(int argc, char **argv) 687 { 688 int opt; 689 char *endptr; 690 691 while ((opt = getopt(argc, argv, "f:t:n:d:l:r:c6ab:e:g:w:u:sv:")) != 692 -1) { 693 switch (opt) { 694 case 'f': 695 config.routes_file = optarg; 696 break; 697 case 't': 698 config.lookup_ips_file = optarg; 699 break; 700 case 'w': 701 config.routes_file_s = optarg; 702 config.flags |= DRY_RUN_FLAG; 703 break; 704 case 'u': 705 config.lookup_ips_file_s = optarg; 706 config.flags |= DRY_RUN_FLAG; 707 break; 708 case 'n': 709 errno = 0; 710 config.nb_routes = strtoul(optarg, &endptr, 10); 711 if ((errno != 0) || (config.nb_routes == 0)) { 712 print_usage(); 713 rte_exit(-EINVAL, "Invalid option -n\n"); 714 } 715 716 if (config.nb_routes < config.print_fract) 717 config.print_fract = config.nb_routes; 718 719 break; 720 case 'd': 721 distrib_string = optarg; 722 break; 723 case 'l': 724 errno = 0; 725 config.nb_lookup_ips = strtoul(optarg, &endptr, 10); 726 if ((errno != 0) || (config.nb_lookup_ips == 0)) { 727 print_usage(); 728 rte_exit(-EINVAL, "Invalid option -l\n"); 729 } 730 break; 731 case 'r': 732 errno = 0; 733 config.rnd_lookup_ips_ratio = 734 strtoul(optarg, &endptr, 10); 735 if ((errno != 0) || 736 (config.rnd_lookup_ips_ratio == 0) || 737 (config.rnd_lookup_ips_ratio >= 100)) { 738 print_usage(); 739 rte_exit(-EINVAL, "Invalid option -r\n"); 740 } 741 break; 742 case 's': 743 config.flags |= SHUFFLE_FLAG; 744 break; 745 case 'c': 746 config.flags |= CMP_FLAG; 747 break; 748 case '6': 749 config.flags |= IPV6_FLAG; 750 break; 751 case 'a': 752 config.flags |= CMP_ALL_FLAG; 753 break; 754 case 'b': 755 if (strcmp(optarg, "rib") == 0) { 756 config.flags &= ~FIB_TYPE_MASK; 757 config.flags |= FIB_RIB_TYPE; 758 } else if (strcmp(optarg, "dir") == 0) { 759 config.flags &= ~FIB_TYPE_MASK; 760 config.flags |= FIB_V4_DIR_TYPE; 761 } else if (strcmp(optarg, "trie") == 0) { 762 config.flags &= ~FIB_TYPE_MASK; 763 config.flags |= FIB_V6_TRIE_TYPE; 764 } else 765 rte_exit(-EINVAL, "Invalid option -b\n"); 766 break; 767 case 'e': 768 errno = 0; 769 config.ent_sz = strtoul(optarg, &endptr, 10); 770 if (errno != 0) { 771 print_usage(); 772 rte_exit(-EINVAL, "Invalid option -e\n"); 773 } 774 break; 775 case 'g': 776 errno = 0; 777 config.tbl8 = strtoul(optarg, &endptr, 10); 778 if ((errno != 0) || (config.tbl8 == 0)) { 779 print_usage(); 780 rte_exit(-EINVAL, "Invalid option -g\n"); 781 } 782 break; 783 case 'v': 784 if ((strcmp(optarg, "s1") == 0) || 785 (strcmp(optarg, "s") == 0)) { 786 config.lookup_fn = 1; 787 break; 788 } else if (strcmp(optarg, "v") == 0) { 789 config.lookup_fn = 2; 790 break; 791 } else if (strcmp(optarg, "s2") == 0) { 792 config.lookup_fn = 3; 793 break; 794 } else if (strcmp(optarg, "s3") == 0) { 795 config.lookup_fn = 4; 796 break; 797 } 798 print_usage(); 799 rte_exit(-EINVAL, "Invalid option -v %s\n", optarg); 800 default: 801 print_usage(); 802 rte_exit(-EINVAL, "Invalid options\n"); 803 } 804 } 805 } 806 807 static int 808 dump_rt_4(struct rt_rule_4 *rt) 809 { 810 FILE *f; 811 uint32_t i; 812 813 f = fopen(config.routes_file_s, "w"); 814 if (f == NULL) { 815 printf("Can not open file %s\n", config.routes_file_s); 816 return -1; 817 } 818 819 for (i = 0; i < config.nb_routes; i++) 820 fprintf(f, NIPQUAD_FMT"/%d %"PRIu64"\n", NIPQUAD(rt[i].addr), 821 rt[i].depth, rt[i].nh); 822 823 fclose(f); 824 return 0; 825 } 826 827 static inline void 828 print_depth_err(void) 829 { 830 printf("LPM does not support /0 prefix length (default route), use " 831 "-d 0:0 option or remove /0 prefix from routes file\n"); 832 } 833 834 static int 835 run_v4(void) 836 { 837 uint64_t start, acc; 838 uint64_t def_nh = 0; 839 struct rte_fib *fib; 840 struct rte_fib_conf conf = {0}; 841 struct rt_rule_4 *rt; 842 uint32_t i, j, k; 843 int ret = 0; 844 struct rte_lpm *lpm = NULL; 845 struct rte_lpm_config lpm_conf; 846 uint32_t *tbl4 = config.lookup_tbl; 847 uint64_t fib_nh[BURST_SZ]; 848 uint32_t lpm_nh[BURST_SZ]; 849 850 rt = (struct rt_rule_4 *)config.rt; 851 852 if (config.flags & DRY_RUN_FLAG) { 853 if (config.routes_file_s != NULL) 854 ret = dump_rt_4(rt); 855 if (ret != 0) 856 return ret; 857 if (config.lookup_ips_file_s != NULL) 858 ret = dump_lookup(AF_INET); 859 return ret; 860 } 861 862 conf.type = get_fib_type(); 863 conf.default_nh = def_nh; 864 conf.max_routes = config.nb_routes * 2; 865 conf.rib_ext_sz = 0; 866 if (conf.type == RTE_FIB_DIR24_8) { 867 conf.dir24_8.nh_sz = rte_ctz32(config.ent_sz); 868 conf.dir24_8.num_tbl8 = RTE_MIN(config.tbl8, 869 get_max_nh(conf.dir24_8.nh_sz)); 870 } 871 872 fib = rte_fib_create("test", -1, &conf); 873 if (fib == NULL) { 874 printf("Can not alloc FIB, err %d\n", rte_errno); 875 return -rte_errno; 876 } 877 878 if (config.lookup_fn != 0) { 879 if (config.lookup_fn == 1) 880 ret = rte_fib_select_lookup(fib, 881 RTE_FIB_LOOKUP_DIR24_8_SCALAR_MACRO); 882 else if (config.lookup_fn == 2) 883 ret = rte_fib_select_lookup(fib, 884 RTE_FIB_LOOKUP_DIR24_8_VECTOR_AVX512); 885 else if (config.lookup_fn == 3) 886 ret = rte_fib_select_lookup(fib, 887 RTE_FIB_LOOKUP_DIR24_8_SCALAR_INLINE); 888 else if (config.lookup_fn == 4) 889 ret = rte_fib_select_lookup(fib, 890 RTE_FIB_LOOKUP_DIR24_8_SCALAR_UNI); 891 else 892 ret = -EINVAL; 893 if (ret != 0) { 894 printf("Can not init lookup function\n"); 895 return ret; 896 } 897 } 898 899 for (k = config.print_fract, i = 0; k > 0; k--) { 900 start = rte_rdtsc_precise(); 901 for (j = 0; j < (config.nb_routes - i) / k; j++) { 902 ret = rte_fib_add(fib, rt[i + j].addr, rt[i + j].depth, 903 rt[i + j].nh); 904 if (unlikely(ret != 0)) { 905 printf("Can not add a route to FIB, err %d\n", 906 ret); 907 return -ret; 908 } 909 } 910 printf("AVG FIB add %"PRIu64"\n", 911 (rte_rdtsc_precise() - start) / j); 912 i += j; 913 } 914 915 if (config.flags & CMP_FLAG) { 916 lpm_conf.max_rules = config.nb_routes * 2; 917 lpm_conf.number_tbl8s = RTE_MAX(conf.dir24_8.num_tbl8, 918 config.tbl8); 919 920 lpm = rte_lpm_create("test_lpm", -1, &lpm_conf); 921 if (lpm == NULL) { 922 printf("Can not alloc LPM, err %d\n", rte_errno); 923 return -rte_errno; 924 } 925 for (k = config.print_fract, i = 0; k > 0; k--) { 926 start = rte_rdtsc_precise(); 927 for (j = 0; j < (config.nb_routes - i) / k; j++) { 928 ret = rte_lpm_add(lpm, rt[i + j].addr, 929 rt[i + j].depth, rt[i + j].nh); 930 if (ret != 0) { 931 if (rt[i + j].depth == 0) 932 print_depth_err(); 933 printf("Can not add a route to LPM, " 934 "err %d\n", ret); 935 return -ret; 936 } 937 } 938 printf("AVG LPM add %"PRIu64"\n", 939 (rte_rdtsc_precise() - start) / j); 940 i += j; 941 } 942 } 943 944 acc = 0; 945 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 946 start = rte_rdtsc_precise(); 947 ret = rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ); 948 acc += rte_rdtsc_precise() - start; 949 if (ret != 0) { 950 printf("FIB lookup fails, err %d\n", ret); 951 return -ret; 952 } 953 } 954 printf("AVG FIB lookup %.1f\n", (double)acc / (double)i); 955 956 if (config.flags & CMP_FLAG) { 957 acc = 0; 958 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 959 start = rte_rdtsc_precise(); 960 ret = rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh, 961 BURST_SZ); 962 acc += rte_rdtsc_precise() - start; 963 if (ret != 0) { 964 printf("LPM lookup fails, err %d\n", ret); 965 return -ret; 966 } 967 } 968 printf("AVG LPM lookup %.1f\n", (double)acc / (double)i); 969 970 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 971 rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ); 972 rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh, BURST_SZ); 973 for (j = 0; j < BURST_SZ; j++) { 974 struct rte_lpm_tbl_entry *tbl; 975 tbl = (struct rte_lpm_tbl_entry *)&lpm_nh[j]; 976 if ((fib_nh[j] != tbl->next_hop) && 977 !((tbl->valid == 0) && 978 (fib_nh[j] == def_nh))) { 979 printf("FAIL\n"); 980 return -1; 981 } 982 } 983 } 984 printf("FIB and LPM lookup returns same values\n"); 985 } 986 987 for (k = config.print_fract, i = 0; k > 0; k--) { 988 start = rte_rdtsc_precise(); 989 for (j = 0; j < (config.nb_routes - i) / k; j++) 990 rte_fib_delete(fib, rt[i + j].addr, rt[i + j].depth); 991 992 printf("AVG FIB delete %"PRIu64"\n", 993 (rte_rdtsc_precise() - start) / j); 994 i += j; 995 } 996 997 if (config.flags & CMP_FLAG) { 998 for (k = config.print_fract, i = 0; k > 0; k--) { 999 start = rte_rdtsc_precise(); 1000 for (j = 0; j < (config.nb_routes - i) / k; j++) 1001 rte_lpm_delete(lpm, rt[i + j].addr, 1002 rt[i + j].depth); 1003 1004 printf("AVG LPM delete %"PRIu64"\n", 1005 (rte_rdtsc_precise() - start) / j); 1006 i += j; 1007 } 1008 } 1009 1010 return 0; 1011 } 1012 1013 static int 1014 dump_rt_6(struct rt_rule_6 *rt) 1015 { 1016 FILE *f; 1017 uint32_t i; 1018 1019 f = fopen(config.routes_file_s, "w"); 1020 if (f == NULL) { 1021 printf("Can not open file %s\n", config.routes_file_s); 1022 return -1; 1023 } 1024 1025 for (i = 0; i < config.nb_routes; i++) { 1026 fprintf(f, NIPQUAD6_FMT"/%d %"PRIu64"\n", NIPQUAD6(rt[i].addr), 1027 rt[i].depth, rt[i].nh); 1028 1029 } 1030 fclose(f); 1031 return 0; 1032 } 1033 1034 static int 1035 run_v6(void) 1036 { 1037 uint64_t start, acc; 1038 uint64_t def_nh = 0; 1039 struct rte_fib6 *fib; 1040 struct rte_fib6_conf conf = {0}; 1041 struct rt_rule_6 *rt; 1042 uint32_t i, j, k; 1043 int ret = 0; 1044 struct rte_lpm6 *lpm = NULL; 1045 struct rte_lpm6_config lpm_conf; 1046 uint8_t *tbl6; 1047 uint64_t fib_nh[BURST_SZ]; 1048 int32_t lpm_nh[BURST_SZ]; 1049 1050 rt = (struct rt_rule_6 *)config.rt; 1051 tbl6 = config.lookup_tbl; 1052 1053 if (config.flags & DRY_RUN_FLAG) { 1054 if (config.routes_file_s != NULL) 1055 ret = dump_rt_6(rt); 1056 if (ret != 0) 1057 return ret; 1058 if (config.lookup_ips_file_s != NULL) 1059 ret = dump_lookup(AF_INET6); 1060 return ret; 1061 } 1062 1063 conf.type = get_fib_type(); 1064 conf.default_nh = def_nh; 1065 conf.max_routes = config.nb_routes * 2; 1066 conf.rib_ext_sz = 0; 1067 if (conf.type == RTE_FIB6_TRIE) { 1068 conf.trie.nh_sz = rte_ctz32(config.ent_sz); 1069 conf.trie.num_tbl8 = RTE_MIN(config.tbl8, 1070 get_max_nh(conf.trie.nh_sz)); 1071 } 1072 1073 fib = rte_fib6_create("test", -1, &conf); 1074 if (fib == NULL) { 1075 printf("Can not alloc FIB, err %d\n", rte_errno); 1076 return -rte_errno; 1077 } 1078 1079 if (config.lookup_fn != 0) { 1080 if (config.lookup_fn == 1) 1081 ret = rte_fib6_select_lookup(fib, 1082 RTE_FIB6_LOOKUP_TRIE_SCALAR); 1083 else if (config.lookup_fn == 2) 1084 ret = rte_fib6_select_lookup(fib, 1085 RTE_FIB6_LOOKUP_TRIE_VECTOR_AVX512); 1086 else 1087 ret = -EINVAL; 1088 if (ret != 0) { 1089 printf("Can not init lookup function\n"); 1090 return ret; 1091 } 1092 } 1093 1094 for (k = config.print_fract, i = 0; k > 0; k--) { 1095 start = rte_rdtsc_precise(); 1096 for (j = 0; j < (config.nb_routes - i) / k; j++) { 1097 ret = rte_fib6_add(fib, rt[i + j].addr, 1098 rt[i + j].depth, rt[i + j].nh); 1099 if (unlikely(ret != 0)) { 1100 printf("Can not add a route to FIB, err %d\n", 1101 ret); 1102 return -ret; 1103 } 1104 } 1105 printf("AVG FIB add %"PRIu64"\n", 1106 (rte_rdtsc_precise() - start) / j); 1107 i += j; 1108 } 1109 1110 if (config.flags & CMP_FLAG) { 1111 lpm_conf.max_rules = config.nb_routes * 2; 1112 lpm_conf.number_tbl8s = RTE_MAX(conf.trie.num_tbl8, 1113 config.tbl8); 1114 1115 lpm = rte_lpm6_create("test_lpm", -1, &lpm_conf); 1116 if (lpm == NULL) { 1117 printf("Can not alloc LPM, err %d\n", rte_errno); 1118 return -rte_errno; 1119 } 1120 for (k = config.print_fract, i = 0; k > 0; k--) { 1121 start = rte_rdtsc_precise(); 1122 for (j = 0; j < (config.nb_routes - i) / k; j++) { 1123 ret = rte_lpm6_add(lpm, rt[i + j].addr, 1124 rt[i + j].depth, rt[i + j].nh); 1125 if (ret != 0) { 1126 if (rt[i + j].depth == 0) 1127 print_depth_err(); 1128 printf("Can not add a route to LPM, " 1129 "err %d\n", ret); 1130 return -ret; 1131 } 1132 } 1133 printf("AVG LPM add %"PRIu64"\n", 1134 (rte_rdtsc_precise() - start) / j); 1135 i += j; 1136 } 1137 } 1138 1139 acc = 0; 1140 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 1141 start = rte_rdtsc_precise(); 1142 ret = rte_fib6_lookup_bulk(fib, (uint8_t (*)[16])(tbl6 + i*16), 1143 fib_nh, BURST_SZ); 1144 acc += rte_rdtsc_precise() - start; 1145 if (ret != 0) { 1146 printf("FIB lookup fails, err %d\n", ret); 1147 return -ret; 1148 } 1149 } 1150 printf("AVG FIB lookup %.1f\n", (double)acc / (double)i); 1151 1152 if (config.flags & CMP_FLAG) { 1153 acc = 0; 1154 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 1155 start = rte_rdtsc_precise(); 1156 ret = rte_lpm6_lookup_bulk_func(lpm, 1157 (uint8_t (*)[16])(tbl6 + i*16), 1158 lpm_nh, BURST_SZ); 1159 acc += rte_rdtsc_precise() - start; 1160 if (ret != 0) { 1161 printf("LPM lookup fails, err %d\n", ret); 1162 return -ret; 1163 } 1164 } 1165 printf("AVG LPM lookup %.1f\n", (double)acc / (double)i); 1166 1167 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 1168 rte_fib6_lookup_bulk(fib, 1169 (uint8_t (*)[16])(tbl6 + i*16), 1170 fib_nh, BURST_SZ); 1171 rte_lpm6_lookup_bulk_func(lpm, 1172 (uint8_t (*)[16])(tbl6 + i*16), 1173 lpm_nh, BURST_SZ); 1174 for (j = 0; j < BURST_SZ; j++) { 1175 if ((fib_nh[j] != (uint32_t)lpm_nh[j]) && 1176 !((lpm_nh[j] == -1) && 1177 (fib_nh[j] == def_nh))) { 1178 printf("FAIL\n"); 1179 return -1; 1180 } 1181 } 1182 } 1183 printf("FIB and LPM lookup returns same values\n"); 1184 } 1185 1186 for (k = config.print_fract, i = 0; k > 0; k--) { 1187 start = rte_rdtsc_precise(); 1188 for (j = 0; j < (config.nb_routes - i) / k; j++) 1189 rte_fib6_delete(fib, rt[i + j].addr, rt[i + j].depth); 1190 1191 printf("AVG FIB delete %"PRIu64"\n", 1192 (rte_rdtsc_precise() - start) / j); 1193 i += j; 1194 } 1195 1196 if (config.flags & CMP_FLAG) { 1197 for (k = config.print_fract, i = 0; k > 0; k--) { 1198 start = rte_rdtsc_precise(); 1199 for (j = 0; j < (config.nb_routes - i) / k; j++) 1200 rte_lpm6_delete(lpm, rt[i + j].addr, 1201 rt[i + j].depth); 1202 1203 printf("AVG LPM delete %"PRIu64"\n", 1204 (rte_rdtsc_precise() - start) / j); 1205 i += j; 1206 } 1207 } 1208 return 0; 1209 } 1210 1211 int 1212 main(int argc, char **argv) 1213 { 1214 int ret, af, rt_ent_sz, lookup_ent_sz; 1215 FILE *fr = NULL; 1216 FILE *fl = NULL; 1217 uint8_t depth_lim; 1218 1219 ret = rte_eal_init(argc, argv); 1220 if (ret < 0) 1221 rte_panic("Cannot init EAL\n"); 1222 1223 argc -= ret; 1224 argv += ret; 1225 1226 config.prgname = argv[0]; 1227 1228 parse_opts(argc, argv); 1229 1230 ret = check_config(); 1231 if (ret != 0) 1232 rte_exit(-ret, "Bad configuration\n"); 1233 1234 af = ((config.flags & IPV6_FLAG) == 0) ? AF_INET : AF_INET6; 1235 depth_lim = (af == AF_INET) ? 32 : 128; 1236 rt_ent_sz = (af == AF_INET) ? sizeof(struct rt_rule_4) : 1237 sizeof(struct rt_rule_6); 1238 lookup_ent_sz = (af == AF_INET) ? 4 : 16; 1239 1240 /* Count number of rules in file*/ 1241 if (config.routes_file != NULL) { 1242 fr = fopen(config.routes_file, "r"); 1243 if (fr == NULL) 1244 rte_exit(-errno, "Can not open file with routes %s\n", 1245 config.routes_file); 1246 1247 config.nb_routes = 0; 1248 while (fgets(line, sizeof(line), fr) != NULL) 1249 config.nb_routes++; 1250 1251 if (config.nb_routes < config.print_fract) 1252 config.print_fract = config.nb_routes; 1253 1254 rewind(fr); 1255 } 1256 1257 /* Count number of ip's in file*/ 1258 if (config.lookup_ips_file != NULL) { 1259 fl = fopen(config.lookup_ips_file, "r"); 1260 if (fl == NULL) 1261 rte_exit(-errno, "Can not open file with ip's %s\n", 1262 config.lookup_ips_file); 1263 1264 config.nb_lookup_ips = 0; 1265 while (fgets(line, sizeof(line), fl) != NULL) 1266 config.nb_lookup_ips++; 1267 rewind(fl); 1268 } 1269 1270 /* Alloc routes table*/ 1271 config.rt = rte_malloc(NULL, rt_ent_sz * config.nb_routes, 0); 1272 if (config.rt == NULL) 1273 rte_exit(-ENOMEM, "Can not alloc rt\n"); 1274 1275 /* Alloc table with ip's for lookup*/ 1276 config.lookup_tbl = rte_malloc(NULL, lookup_ent_sz * 1277 config.nb_lookup_ips, 0); 1278 if (config.lookup_tbl == NULL) 1279 rte_exit(-ENOMEM, "Can not alloc lookup table\n"); 1280 1281 /* Fill routes table */ 1282 if (fr == NULL) { 1283 if (distrib_string != NULL) 1284 ret = parse_distrib(depth_lim, config.nb_routes); 1285 else { 1286 uint8_t rpd[129] = {0}; 1287 uint32_t nrpd[129] = {0}; 1288 ret = complete_distrib(depth_lim, config.nb_routes, 1289 rpd, nrpd); 1290 } 1291 if (ret != 0) 1292 rte_exit(-ret, 1293 "Bad routes distribution configuration\n"); 1294 if (af == AF_INET) { 1295 gen_random_rt_4(config.rt, 1296 rte_ctz32(config.ent_sz)); 1297 if (config.flags & SHUFFLE_FLAG) 1298 shuffle_rt_4(config.rt, config.nb_routes); 1299 } else { 1300 gen_random_rt_6(config.rt, 1301 rte_ctz32(config.ent_sz)); 1302 if (config.flags & SHUFFLE_FLAG) 1303 shuffle_rt_6(config.rt, config.nb_routes); 1304 } 1305 } else { 1306 if (af == AF_INET) 1307 ret = parse_rt_4(fr); 1308 else 1309 ret = parse_rt_6(fr); 1310 1311 if (ret != 0) { 1312 rte_exit(-ret, "failed to parse routes file %s\n", 1313 config.routes_file); 1314 } 1315 } 1316 1317 /* Fill lookup table with ip's*/ 1318 if (fl == NULL) 1319 gen_rnd_lookup_tbl(af); 1320 else { 1321 ret = parse_lookup(fl, af); 1322 if (ret != 0) 1323 rte_exit(-ret, "failed to parse lookup file\n"); 1324 } 1325 1326 print_config(); 1327 1328 if (af == AF_INET) 1329 ret = run_v4(); 1330 else 1331 ret = run_v6(); 1332 1333 return ret; 1334 } 1335