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