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