]> git.proxmox.com Git - ceph.git/blame - ceph/src/spdk/dpdk/app/test-fib/main.c
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / spdk / dpdk / app / test-fib / main.c
CommitLineData
f67539c2
TL
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
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} 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
116struct rt_rule_4 {
117 uint32_t addr;
118 uint8_t depth;
119 uint64_t nh;
120};
121
122struct rt_rule_6 {
123 uint8_t addr[16];
124 uint8_t depth;
125 uint64_t nh;
126};
127
128static uint64_t
129get_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
137static __rte_always_inline __attribute__((pure)) uint8_t
138bits_in_nh(uint8_t nh_sz)
139{
140 return 8 * (1 << nh_sz);
141}
142
143static __rte_always_inline __attribute__((pure)) uint64_t
144get_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
151static int
152get_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
168static int
169complete_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
216static int
217parse_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
276static void
277shuffle_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
298static void
299shuffle_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
320static void
321gen_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
346static void
347complete_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
358static void
359gen_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
389static inline void
390set_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
409static void
410gen_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
442static 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
468static int
469parse_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
502static int
503parse_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
536static int
537parse_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
556static int
557dump_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
581static void
582print_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
612static void
613print_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
645static int
646check_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
678static void
679parse_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
779static int
780dump_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
799static inline void
800print_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
806static int
807run_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
963static int
964dump_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
984static int
985run_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
1145int
1146main(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}