app/test-fib: fix 32-bits build
[dpdk.git] / app / test-fib / main.c
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 }