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 if (s == NULL) 547 return -EINVAL; 548 ret = inet_pton(af, s, &tbl[i]); 549 if (ret != 1) 550 return -EINVAL; 551 i += step; 552 } 553 return 0; 554 } 555 556 static int 557 dump_lookup(int af) 558 { 559 FILE *f; 560 uint32_t *tbl4 = config.lookup_tbl; 561 uint8_t *tbl6 = config.lookup_tbl; 562 uint32_t i; 563 564 f = fopen(config.lookup_ips_file_s, "w"); 565 if (f == NULL) { 566 printf("Can not open file %s\n", config.lookup_ips_file_s); 567 return -1; 568 } 569 570 if (af == AF_INET) { 571 for (i = 0; i < config.nb_lookup_ips; i++) 572 fprintf(f, NIPQUAD_FMT"\n", NIPQUAD(tbl4[i])); 573 } else { 574 for (i = 0; i < config.nb_lookup_ips; i++) 575 fprintf(f, NIPQUAD6_FMT"\n", NIPQUAD6(&tbl6[i * 16])); 576 } 577 fclose(f); 578 return 0; 579 } 580 581 static void 582 print_config(void) 583 { 584 uint8_t depth_lim; 585 char dlm; 586 int i; 587 588 depth_lim = ((config.flags & IPV6_FLAG) == IPV6_FLAG) ? 128 : 32; 589 590 fprintf(stdout, 591 "Routes total: %u\n" 592 "Routes distribution:\n", config.nb_routes); 593 594 for (i = 1; i <= depth_lim; i++) { 595 fprintf(stdout, 596 "depth /%d:%u", i, config.nb_routes_per_depth[i]); 597 if (i % 4 == 0) 598 dlm = '\n'; 599 else 600 dlm = '\t'; 601 fprintf(stdout, "%c", dlm); 602 } 603 604 fprintf(stdout, 605 "Lookup tuples: %u\n" 606 "Configured ratios of random ips for lookup: %u\n" 607 "Random lookup ips: %u\n", 608 config.nb_lookup_ips, config.rnd_lookup_ips_ratio, 609 config.nb_lookup_ips_rnd); 610 } 611 612 static void 613 print_usage(void) 614 { 615 fprintf(stdout, 616 PRINT_USAGE_START 617 "[-f <routes file>]\n" 618 "[-t <ip's file for lookup>]\n" 619 "[-n <number of routes (if -f is not specified)>]\n" 620 "[-l <number of ip's for lookup (if -t is not specified)>]\n" 621 "[-d <\",\" separated \"depth:n%%\"routes depth distribution" 622 "(if -f is not specified)>]\n" 623 "[-r <percentage ratio of random ip's to lookup" 624 "(if -t is not specified)>]\n" 625 "[-c <do comarison with LPM library>]\n" 626 "[-6 <do tests with ipv6 (default ipv4)>]\n" 627 "[-s <shuffle randomly generated routes>]\n" 628 "[-a <check nexthops for all ipv4 address space" 629 "(only valid with -c)>]\n" 630 "[-b <fib algorithm>]\n\tavailible options for ipv4\n" 631 "\t\trib - RIB based FIB\n" 632 "\t\tdir - DIR24_8 based FIB\n" 633 "\tavailible options for ipv6:\n" 634 "\t\trib - RIB based FIB\n" 635 "\t\ttrie - TRIE based FIB\n" 636 "defaults are: dir for ipv4 and trie for ipv6\n" 637 "[-e <entry size (valid only for dir and trie fib types): " 638 "1/2/4/8 (default 4)>]\n" 639 "[-g <number of tbl8's for dir24_8 or trie FIBs>]\n" 640 "[-w <path to the file to dump routing table>]\n" 641 "[-u <path to the file to dump ip's for lookup>]\n", 642 config.prgname); 643 } 644 645 static int 646 check_config(void) 647 { 648 if ((config.routes_file == NULL) && (config.lookup_ips_file != NULL)) { 649 printf("-t option only valid with -f option\n"); 650 return -1; 651 } 652 653 if ((config.flags & CMP_ALL_FLAG) && (config.flags & IPV6_FLAG)) { 654 printf("-a flag is only valid for ipv4\n"); 655 return -1; 656 } 657 658 if ((config.flags & CMP_ALL_FLAG) && 659 ((config.flags & CMP_FLAG) != CMP_FLAG)) { 660 printf("-a flag is valid only with -c flag\n"); 661 return -1; 662 } 663 664 if (!((config.ent_sz == 1) || (config.ent_sz == 2) || 665 (config.ent_sz == 4) || (config.ent_sz == 8))) { 666 printf("wrong -e option %d, can be 1 or 2 or 4 or 8\n", 667 config.ent_sz); 668 return -1; 669 } 670 671 if ((config.ent_sz == 1) && (config.flags & IPV6_FLAG)) { 672 printf("-e 1 is valid only for ipv4\n"); 673 return -1; 674 } 675 return 0; 676 } 677 678 static void 679 parse_opts(int argc, char **argv) 680 { 681 int opt; 682 char *endptr; 683 684 while ((opt = getopt(argc, argv, "f:t:n:d:l:r:c6ab:e:g:w:u:s")) != 685 -1) { 686 switch (opt) { 687 case 'f': 688 config.routes_file = optarg; 689 break; 690 case 't': 691 config.lookup_ips_file = optarg; 692 break; 693 case 'w': 694 config.routes_file_s = optarg; 695 config.flags |= DRY_RUN_FLAG; 696 break; 697 case 'u': 698 config.lookup_ips_file_s = optarg; 699 config.flags |= DRY_RUN_FLAG; 700 break; 701 case 'n': 702 errno = 0; 703 config.nb_routes = strtoul(optarg, &endptr, 10); 704 if ((errno != 0) || (config.nb_routes == 0)) { 705 print_usage(); 706 rte_exit(-EINVAL, "Invalid option -n\n"); 707 } 708 break; 709 case 'd': 710 distrib_string = optarg; 711 break; 712 case 'l': 713 errno = 0; 714 config.nb_lookup_ips = strtoul(optarg, &endptr, 10); 715 if ((errno != 0) || (config.nb_lookup_ips == 0)) { 716 print_usage(); 717 rte_exit(-EINVAL, "Invalid option -l\n"); 718 } 719 break; 720 case 'r': 721 errno = 0; 722 config.rnd_lookup_ips_ratio = 723 strtoul(optarg, &endptr, 10); 724 if ((errno != 0) || 725 (config.rnd_lookup_ips_ratio == 0) || 726 (config.rnd_lookup_ips_ratio >= 100)) { 727 print_usage(); 728 rte_exit(-EINVAL, "Invalid option -r\n"); 729 } 730 break; 731 case 's': 732 config.flags |= SHUFFLE_FLAG; 733 break; 734 case 'c': 735 config.flags |= CMP_FLAG; 736 break; 737 case '6': 738 config.flags |= IPV6_FLAG; 739 break; 740 case 'a': 741 config.flags |= CMP_ALL_FLAG; 742 break; 743 case 'b': 744 if (strcmp(optarg, "rib") == 0) { 745 config.flags &= ~FIB_TYPE_MASK; 746 config.flags |= FIB_RIB_TYPE; 747 } else if (strcmp(optarg, "dir") == 0) { 748 config.flags &= ~FIB_TYPE_MASK; 749 config.flags |= FIB_V4_DIR_TYPE; 750 } else if (strcmp(optarg, "trie") == 0) { 751 config.flags &= ~FIB_TYPE_MASK; 752 config.flags |= FIB_V6_TRIE_TYPE; 753 } else 754 rte_exit(-EINVAL, "Invalid option -b\n"); 755 break; 756 case 'e': 757 errno = 0; 758 config.ent_sz = strtoul(optarg, &endptr, 10); 759 if (errno != 0) { 760 print_usage(); 761 rte_exit(-EINVAL, "Invalid option -e\n"); 762 } 763 break; 764 case 'g': 765 errno = 0; 766 config.tbl8 = strtoul(optarg, &endptr, 10); 767 if ((errno != 0) || (config.tbl8 == 0)) { 768 print_usage(); 769 rte_exit(-EINVAL, "Invalid option -g\n"); 770 } 771 break; 772 default: 773 print_usage(); 774 rte_exit(-EINVAL, "Invalid options\n"); 775 } 776 } 777 } 778 779 static int 780 dump_rt_4(struct rt_rule_4 *rt) 781 { 782 FILE *f; 783 uint32_t i; 784 785 f = fopen(config.routes_file_s, "w"); 786 if (f == NULL) { 787 printf("Can not open file %s\n", config.routes_file_s); 788 return -1; 789 } 790 791 for (i = 0; i < config.nb_routes; i++) 792 fprintf(f, NIPQUAD_FMT"/%d %"PRIu64"\n", NIPQUAD(rt[i].addr), 793 rt[i].depth, rt[i].nh); 794 795 fclose(f); 796 return 0; 797 } 798 799 static inline void 800 print_depth_err(void) 801 { 802 printf("LPM does not support /0 prefix length (default route), use " 803 "-d 0:0 option or remove /0 prefix from routes file\n"); 804 } 805 806 static int 807 run_v4(void) 808 { 809 uint64_t start, acc; 810 uint64_t def_nh = 0; 811 struct rte_fib *fib; 812 struct rte_fib_conf conf = {0}; 813 struct rt_rule_4 *rt; 814 uint32_t i, j, k; 815 int ret = 0; 816 struct rte_lpm *lpm = NULL; 817 struct rte_lpm_config lpm_conf; 818 uint32_t *tbl4 = config.lookup_tbl; 819 uint64_t fib_nh[BURST_SZ]; 820 uint32_t lpm_nh[BURST_SZ]; 821 822 rt = (struct rt_rule_4 *)config.rt; 823 824 if (config.flags & DRY_RUN_FLAG) { 825 if (config.routes_file_s != NULL) 826 ret = dump_rt_4(rt); 827 if (ret != 0) 828 return ret; 829 if (config.lookup_ips_file_s != NULL) 830 ret = dump_lookup(AF_INET); 831 return ret; 832 } 833 834 conf.type = get_fib_type(); 835 conf.default_nh = def_nh; 836 conf.max_routes = config.nb_routes * 2; 837 if (conf.type == RTE_FIB_DIR24_8) { 838 conf.dir24_8.nh_sz = __builtin_ctz(config.ent_sz); 839 conf.dir24_8.num_tbl8 = RTE_MIN(config.tbl8, 840 get_max_nh(conf.dir24_8.nh_sz)); 841 } 842 843 fib = rte_fib_create("test", -1, &conf); 844 if (fib == NULL) { 845 printf("Can not alloc FIB, err %d\n", rte_errno); 846 return -rte_errno; 847 } 848 849 for (k = config.print_fract, i = 0; k > 0; k--) { 850 start = rte_rdtsc_precise(); 851 for (j = 0; j < (config.nb_routes - i) / k; j++) { 852 ret = rte_fib_add(fib, rt[i + j].addr, rt[i + j].depth, 853 rt[i + j].nh); 854 if (unlikely(ret != 0)) { 855 printf("Can not add a route to FIB, err %d\n", 856 ret); 857 return -ret; 858 } 859 } 860 printf("AVG FIB add %"PRIu64"\n", 861 (rte_rdtsc_precise() - start) / j); 862 i += j; 863 } 864 865 if (config.flags & CMP_FLAG) { 866 lpm_conf.max_rules = config.nb_routes * 2; 867 lpm_conf.number_tbl8s = RTE_MAX(conf.dir24_8.num_tbl8, 868 config.tbl8); 869 870 lpm = rte_lpm_create("test_lpm", -1, &lpm_conf); 871 if (lpm == NULL) { 872 printf("Can not alloc LPM, err %d\n", rte_errno); 873 return -rte_errno; 874 } 875 for (k = config.print_fract, i = 0; k > 0; k--) { 876 start = rte_rdtsc_precise(); 877 for (j = 0; j < (config.nb_routes - i) / k; j++) { 878 ret = rte_lpm_add(lpm, rt[i + j].addr, 879 rt[i + j].depth, rt[i + j].nh); 880 if (ret != 0) { 881 if (rt[i + j].depth == 0) 882 print_depth_err(); 883 printf("Can not add a route to LPM, " 884 "err %d\n", ret); 885 return -ret; 886 } 887 } 888 printf("AVG LPM add %"PRIu64"\n", 889 (rte_rdtsc_precise() - start) / j); 890 i += j; 891 } 892 } 893 894 acc = 0; 895 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 896 start = rte_rdtsc_precise(); 897 ret = rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ); 898 acc += rte_rdtsc_precise() - start; 899 if (ret != 0) { 900 printf("FIB lookup fails, err %d\n", ret); 901 return -ret; 902 } 903 } 904 printf("AVG FIB lookup %.1f\n", (double)acc / (double)i); 905 906 if (config.flags & CMP_FLAG) { 907 acc = 0; 908 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 909 start = rte_rdtsc_precise(); 910 ret = rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh, 911 BURST_SZ); 912 acc += rte_rdtsc_precise() - start; 913 if (ret != 0) { 914 printf("LPM lookup fails, err %d\n", ret); 915 return -ret; 916 } 917 } 918 printf("AVG LPM lookup %.1f\n", (double)acc / (double)i); 919 920 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 921 rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ); 922 rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh, BURST_SZ); 923 for (j = 0; j < BURST_SZ; j++) { 924 struct rte_lpm_tbl_entry *tbl; 925 tbl = (struct rte_lpm_tbl_entry *)&lpm_nh[j]; 926 if ((fib_nh[j] != tbl->next_hop) && 927 !((tbl->valid == 0) && 928 (fib_nh[j] == def_nh))) { 929 printf("FAIL\n"); 930 return -1; 931 } 932 } 933 } 934 printf("FIB and LPM lookup returns same values\n"); 935 } 936 937 for (k = config.print_fract, i = 0; k > 0; k--) { 938 start = rte_rdtsc_precise(); 939 for (j = 0; j < (config.nb_routes - i) / k; j++) 940 rte_fib_delete(fib, rt[i + j].addr, rt[i + j].depth); 941 942 printf("AVG FIB delete %"PRIu64"\n", 943 (rte_rdtsc_precise() - start) / j); 944 i += j; 945 } 946 947 if (config.flags & CMP_FLAG) { 948 for (k = config.print_fract, i = 0; k > 0; k--) { 949 start = rte_rdtsc_precise(); 950 for (j = 0; j < (config.nb_routes - i) / k; j++) 951 rte_lpm_delete(lpm, rt[i + j].addr, 952 rt[i + j].depth); 953 954 printf("AVG LPM delete %"PRIu64"\n", 955 (rte_rdtsc_precise() - start) / j); 956 i += j; 957 } 958 } 959 960 return 0; 961 } 962 963 static int 964 dump_rt_6(struct rt_rule_6 *rt) 965 { 966 FILE *f; 967 uint32_t i; 968 969 f = fopen(config.routes_file_s, "w"); 970 if (f == NULL) { 971 printf("Can not open file %s\n", config.routes_file_s); 972 return -1; 973 } 974 975 for (i = 0; i < config.nb_routes; i++) { 976 fprintf(f, NIPQUAD6_FMT"/%d %"PRIu64"\n", NIPQUAD6(rt[i].addr), 977 rt[i].depth, rt[i].nh); 978 979 } 980 fclose(f); 981 return 0; 982 } 983 984 static int 985 run_v6(void) 986 { 987 uint64_t start, acc; 988 uint64_t def_nh = 0; 989 struct rte_fib6 *fib; 990 struct rte_fib6_conf conf = {0}; 991 struct rt_rule_6 *rt; 992 uint32_t i, j, k; 993 int ret = 0; 994 struct rte_lpm6 *lpm = NULL; 995 struct rte_lpm6_config lpm_conf; 996 uint8_t *tbl6; 997 uint64_t fib_nh[BURST_SZ]; 998 int32_t lpm_nh[BURST_SZ]; 999 1000 rt = (struct rt_rule_6 *)config.rt; 1001 tbl6 = config.lookup_tbl; 1002 1003 if (config.flags & DRY_RUN_FLAG) { 1004 if (config.routes_file_s != NULL) 1005 ret = dump_rt_6(rt); 1006 if (ret != 0) 1007 return ret; 1008 if (config.lookup_ips_file_s != NULL) 1009 ret = dump_lookup(AF_INET6); 1010 return ret; 1011 } 1012 1013 conf.type = get_fib_type(); 1014 conf.default_nh = def_nh; 1015 conf.max_routes = config.nb_routes * 2; 1016 if (conf.type == RTE_FIB6_TRIE) { 1017 conf.trie.nh_sz = __builtin_ctz(config.ent_sz); 1018 conf.trie.num_tbl8 = RTE_MIN(config.tbl8, 1019 get_max_nh(conf.trie.nh_sz)); 1020 } 1021 1022 fib = rte_fib6_create("test", -1, &conf); 1023 if (fib == NULL) { 1024 printf("Can not alloc FIB, err %d\n", rte_errno); 1025 return -rte_errno; 1026 } 1027 1028 for (k = config.print_fract, i = 0; k > 0; k--) { 1029 start = rte_rdtsc_precise(); 1030 for (j = 0; j < (config.nb_routes - i) / k; j++) { 1031 ret = rte_fib6_add(fib, rt[i + j].addr, 1032 rt[i + j].depth, rt[i + j].nh); 1033 if (unlikely(ret != 0)) { 1034 printf("Can not add a route to FIB, err %d\n", 1035 ret); 1036 return -ret; 1037 } 1038 } 1039 printf("AVG FIB add %"PRIu64"\n", 1040 (rte_rdtsc_precise() - start) / j); 1041 i += j; 1042 } 1043 1044 if (config.flags & CMP_FLAG) { 1045 lpm_conf.max_rules = config.nb_routes * 2; 1046 lpm_conf.number_tbl8s = RTE_MAX(conf.trie.num_tbl8, 1047 config.tbl8); 1048 1049 lpm = rte_lpm6_create("test_lpm", -1, &lpm_conf); 1050 if (lpm == NULL) { 1051 printf("Can not alloc LPM, err %d\n", rte_errno); 1052 return -rte_errno; 1053 } 1054 for (k = config.print_fract, i = 0; k > 0; k--) { 1055 start = rte_rdtsc_precise(); 1056 for (j = 0; j < (config.nb_routes - i) / k; j++) { 1057 ret = rte_lpm6_add(lpm, rt[i + j].addr, 1058 rt[i + j].depth, rt[i + j].nh); 1059 if (ret != 0) { 1060 if (rt[i + j].depth == 0) 1061 print_depth_err(); 1062 printf("Can not add a route to LPM, " 1063 "err %d\n", ret); 1064 return -ret; 1065 } 1066 } 1067 printf("AVG LPM add %"PRIu64"\n", 1068 (rte_rdtsc_precise() - start) / j); 1069 i += j; 1070 } 1071 } 1072 1073 acc = 0; 1074 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 1075 start = rte_rdtsc_precise(); 1076 ret = rte_fib6_lookup_bulk(fib, (uint8_t (*)[16])(tbl6 + i*16), 1077 fib_nh, BURST_SZ); 1078 acc += rte_rdtsc_precise() - start; 1079 if (ret != 0) { 1080 printf("FIB lookup fails, err %d\n", ret); 1081 return -ret; 1082 } 1083 } 1084 printf("AVG FIB lookup %.1f\n", (double)acc / (double)i); 1085 1086 if (config.flags & CMP_FLAG) { 1087 acc = 0; 1088 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 1089 start = rte_rdtsc_precise(); 1090 ret = rte_lpm6_lookup_bulk_func(lpm, 1091 (uint8_t (*)[16])(tbl6 + i*16), 1092 lpm_nh, BURST_SZ); 1093 acc += rte_rdtsc_precise() - start; 1094 if (ret != 0) { 1095 printf("LPM lookup fails, err %d\n", ret); 1096 return -ret; 1097 } 1098 } 1099 printf("AVG LPM lookup %.1f\n", (double)acc / (double)i); 1100 1101 for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { 1102 rte_fib6_lookup_bulk(fib, 1103 (uint8_t (*)[16])(tbl6 + i*16), 1104 fib_nh, BURST_SZ); 1105 rte_lpm6_lookup_bulk_func(lpm, 1106 (uint8_t (*)[16])(tbl6 + i*16), 1107 lpm_nh, BURST_SZ); 1108 for (j = 0; j < BURST_SZ; j++) { 1109 if ((fib_nh[j] != (uint32_t)lpm_nh[j]) && 1110 !((lpm_nh[j] == -1) && 1111 (fib_nh[j] == def_nh))) { 1112 printf("FAIL\n"); 1113 return -1; 1114 } 1115 } 1116 } 1117 printf("FIB and LPM lookup returns same values\n"); 1118 } 1119 1120 for (k = config.print_fract, i = 0; k > 0; k--) { 1121 start = rte_rdtsc_precise(); 1122 for (j = 0; j < (config.nb_routes - i) / k; j++) 1123 rte_fib6_delete(fib, rt[i + j].addr, rt[i + j].depth); 1124 1125 printf("AVG FIB delete %"PRIu64"\n", 1126 (rte_rdtsc_precise() - start) / j); 1127 i += j; 1128 } 1129 1130 if (config.flags & CMP_FLAG) { 1131 for (k = config.print_fract, i = 0; k > 0; k--) { 1132 start = rte_rdtsc_precise(); 1133 for (j = 0; j < (config.nb_routes - i) / k; j++) 1134 rte_lpm6_delete(lpm, rt[i + j].addr, 1135 rt[i + j].depth); 1136 1137 printf("AVG LPM delete %"PRIu64"\n", 1138 (rte_rdtsc_precise() - start) / j); 1139 i += j; 1140 } 1141 } 1142 return 0; 1143 } 1144 1145 int 1146 main(int argc, char **argv) 1147 { 1148 int ret, af, rt_ent_sz, lookup_ent_sz; 1149 FILE *fr = NULL; 1150 FILE *fl = NULL; 1151 uint8_t depth_lim; 1152 1153 ret = rte_eal_init(argc, argv); 1154 if (ret < 0) 1155 rte_panic("Cannot init EAL\n"); 1156 1157 argc -= ret; 1158 argv += ret; 1159 1160 config.prgname = argv[0]; 1161 1162 parse_opts(argc, argv); 1163 1164 ret = check_config(); 1165 if (ret != 0) 1166 rte_exit(-ret, "Bad configuration\n"); 1167 1168 af = ((config.flags & IPV6_FLAG) == 0) ? AF_INET : AF_INET6; 1169 depth_lim = (af == AF_INET) ? 32 : 128; 1170 rt_ent_sz = (af == AF_INET) ? sizeof(struct rt_rule_4) : 1171 sizeof(struct rt_rule_6); 1172 lookup_ent_sz = (af == AF_INET) ? 4 : 16; 1173 1174 /* Count number of rules in file*/ 1175 if (config.routes_file != NULL) { 1176 fr = fopen(config.routes_file, "r"); 1177 if (fr == NULL) 1178 rte_exit(-errno, "Can not open file with routes %s\n", 1179 config.routes_file); 1180 1181 config.nb_routes = 0; 1182 while (fgets(line, sizeof(line), fr) != NULL) 1183 config.nb_routes++; 1184 rewind(fr); 1185 } 1186 1187 /* Count number of ip's in file*/ 1188 if (config.lookup_ips_file != NULL) { 1189 fl = fopen(config.lookup_ips_file, "r"); 1190 if (fl == NULL) 1191 rte_exit(-errno, "Can not open file with ip's %s\n", 1192 config.lookup_ips_file); 1193 1194 config.nb_lookup_ips = 0; 1195 while (fgets(line, sizeof(line), fl) != NULL) 1196 config.nb_lookup_ips++; 1197 rewind(fl); 1198 } 1199 1200 /* Alloc routes table*/ 1201 config.rt = rte_malloc(NULL, rt_ent_sz * config.nb_routes, 0); 1202 if (config.rt == NULL) 1203 rte_exit(-ENOMEM, "Can not alloc rt\n"); 1204 1205 /* Alloc table with ip's for lookup*/ 1206 config.lookup_tbl = rte_malloc(NULL, lookup_ent_sz * 1207 config.nb_lookup_ips, 0); 1208 if (config.lookup_tbl == NULL) 1209 rte_exit(-ENOMEM, "Can not alloc lookup table\n"); 1210 1211 /* Fill routes table */ 1212 if (fr == NULL) { 1213 if (distrib_string != NULL) 1214 ret = parse_distrib(depth_lim, config.nb_routes); 1215 else { 1216 uint8_t rpd[129] = {0}; 1217 uint32_t nrpd[129] = {0}; 1218 ret = complete_distrib(depth_lim, config.nb_routes, 1219 rpd, nrpd); 1220 } 1221 if (ret != 0) 1222 rte_exit(-ret, 1223 "Bad routes distribution configuration\n"); 1224 if (af == AF_INET) { 1225 gen_random_rt_4(config.rt, 1226 __builtin_ctz(config.ent_sz)); 1227 if (config.flags & SHUFFLE_FLAG) 1228 shuffle_rt_4(config.rt, config.nb_routes); 1229 } else { 1230 gen_random_rt_6(config.rt, 1231 __builtin_ctz(config.ent_sz)); 1232 if (config.flags & SHUFFLE_FLAG) 1233 shuffle_rt_6(config.rt, config.nb_routes); 1234 } 1235 } else { 1236 if (af == AF_INET) 1237 ret = parse_rt_4(fr); 1238 else 1239 ret = parse_rt_6(fr); 1240 1241 if (ret != 0) { 1242 rte_exit(-ret, "failed to parse routes file %s\n", 1243 config.routes_file); 1244 } 1245 } 1246 1247 /* Fill lookup table with ip's*/ 1248 if (fl == NULL) 1249 gen_rnd_lookup_tbl(af); 1250 else { 1251 ret = parse_lookup(fl, af); 1252 if (ret != 0) 1253 rte_exit(-ret, "failed to parse lookup file\n"); 1254 } 1255 1256 print_config(); 1257 1258 if (af == AF_INET) 1259 ret = run_v4(); 1260 else 1261 ret = run_v6(); 1262 1263 return ret; 1264 } 1265