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