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