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