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