app/test-fib: add test application for FIB
authorVladimir Medvedkin <vladimir.medvedkin@intel.com>
Wed, 11 Dec 2019 18:45:04 +0000 (18:45 +0000)
committerThomas Monjalon <thomas@monjalon.net>
Sun, 16 Feb 2020 18:31:09 +0000 (19:31 +0100)
Introduce new application to provide user to evaluate and perform
custom functional and performance tests for FIB library.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
MAINTAINERS
app/test-fib/Makefile [new file with mode: 0644]
app/test-fib/main.c [new file with mode: 0644]
app/test-fib/meson.build [new file with mode: 0644]

index 99d080b..762c13b 100644 (file)
@@ -1374,6 +1374,7 @@ F: lib/librte_rib/
 F: app/test/test_rib*
 F: lib/librte_fib/
 F: app/test/test_fib*
+F: app/test-fib/
 
 Traffic metering
 M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
diff --git a/app/test-fib/Makefile b/app/test-fib/Makefile
new file mode 100644 (file)
index 0000000..78b45fe
--- /dev/null
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2014 Intel Corporation
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifeq ($(CONFIG_RTE_LIBRTE_FIB),y)
+
+APP = testfib
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+
+LDFLAGS += -lresolv
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+include $(RTE_SDK)/mk/rte.app.mk
+
+endif
diff --git a/app/test-fib/main.c b/app/test-fib/main.c
new file mode 100644 (file)
index 0000000..3c29ca4
--- /dev/null
@@ -0,0 +1,1262 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <rte_string_fns.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <rte_cycles.h>
+#include <rte_errno.h>
+#include <rte_ip.h>
+#include <rte_random.h>
+#include <rte_malloc.h>
+#include <rte_lpm.h>
+#include <rte_lpm6.h>
+#include <rte_fib.h>
+#include <rte_fib6.h>
+
+#define        PRINT_USAGE_START       "%s [EAL options] --\n"
+
+#define GET_CB_FIELD(in, fd, base, lim, dlm)   do {            \
+       unsigned long val;                                      \
+       char *end_fld;                                          \
+       errno = 0;                                              \
+       val = strtoul((in), &end_fld, (base));                  \
+       if (errno != 0 || end_fld[0] != (dlm) || val > (lim))   \
+               return -EINVAL;                                 \
+       (fd) = (typeof(fd))val;                                 \
+       (in) = end_fld + 1;                                     \
+} while (0)
+
+#define        DEF_ROUTES_NUM          0x10000
+#define        DEF_LOOKUP_IPS_NUM      0x100000
+#define BURST_SZ               64
+#define DEFAULT_LPM_TBL8       100000U
+
+#define CMP_FLAG               (1 << 0)
+#define CMP_ALL_FLAG           (1 << 1)
+#define IPV6_FLAG              (1 << 2)
+#define FIB_RIB_TYPE           (1 << 3)
+#define FIB_V4_DIR_TYPE                (1 << 4)
+#define FIB_V6_TRIE_TYPE       (1 << 4)
+#define FIB_TYPE_MASK          (FIB_RIB_TYPE|FIB_V4_DIR_TYPE|FIB_V6_TRIE_TYPE)
+#define SHUFFLE_FLAG           (1 << 7)
+#define DRY_RUN_FLAG           (1 << 8)
+
+static char *distrib_string;
+static char line[LINE_MAX];
+
+enum {
+       RT_PREFIX,
+       RT_NEXTHOP,
+       RT_NUM
+};
+
+#ifndef NIPQUAD
+#define NIPQUAD_FMT "%u.%u.%u.%u"
+#define NIPQUAD(addr)                          \
+       (unsigned)((unsigned char *)&addr)[3],  \
+       (unsigned)((unsigned char *)&addr)[2],  \
+       (unsigned)((unsigned char *)&addr)[1],  \
+       (unsigned)((unsigned char *)&addr)[0]
+
+#define NIPQUAD6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+#define NIPQUAD6(addr)                         \
+       ((uint8_t *)addr)[0] << 8 |     \
+       ((uint8_t *)addr)[1],           \
+       ((uint8_t *)addr)[2] << 8 |     \
+       ((uint8_t *)addr)[3],           \
+       ((uint8_t *)addr)[4] << 8 |     \
+       ((uint8_t *)addr)[5],           \
+       ((uint8_t *)addr)[6] << 8 |     \
+       ((uint8_t *)addr)[7],           \
+       ((uint8_t *)addr)[8] << 8 |     \
+       ((uint8_t *)addr)[9],           \
+       ((uint8_t *)addr)[10] << 8 |    \
+       ((uint8_t *)addr)[11],          \
+       ((uint8_t *)addr)[12] << 8 |    \
+       ((uint8_t *)addr)[13],          \
+       ((uint8_t *)addr)[14] << 8 |    \
+       ((uint8_t *)addr)[15]
+#endif
+
+static struct {
+       const char      *prgname;
+       const char      *routes_file;
+       const char      *lookup_ips_file;
+       const char      *routes_file_s;
+       const char      *lookup_ips_file_s;
+       void            *rt;
+       void            *lookup_tbl;
+       uint32_t        nb_routes;
+       uint32_t        nb_lookup_ips;
+       uint32_t        nb_lookup_ips_rnd;
+       uint32_t        nb_routes_per_depth[128 + 1];
+       uint32_t        flags;
+       uint32_t        tbl8;
+       uint8_t         ent_sz;
+       uint8_t         rnd_lookup_ips_ratio;
+       uint8_t         print_fract;
+} config = {
+       .routes_file = NULL,
+       .lookup_ips_file = NULL,
+       .nb_routes = DEF_ROUTES_NUM,
+       .nb_lookup_ips = DEF_LOOKUP_IPS_NUM,
+       .nb_lookup_ips_rnd = 0,
+       .nb_routes_per_depth = {0},
+       .flags = FIB_V4_DIR_TYPE,
+       .tbl8 = DEFAULT_LPM_TBL8,
+       .ent_sz = 4,
+       .rnd_lookup_ips_ratio = 0,
+       .print_fract = 10
+};
+
+struct rt_rule_4 {
+       uint32_t        addr;
+       uint8_t         depth;
+       uint64_t        nh;
+};
+
+struct rt_rule_6 {
+       uint8_t         addr[16];
+       uint8_t         depth;
+       uint64_t        nh;
+};
+
+static uint64_t
+get_rnd_rng(uint64_t l, uint64_t u)
+{
+       if (l == u)
+               return l;
+       else
+               return (rte_rand() % (u - l) + l);
+}
+
+static __rte_always_inline __attribute__((pure)) uint8_t
+bits_in_nh(uint8_t nh_sz)
+{
+       return 8 * (1 << nh_sz);
+}
+
+static  __rte_always_inline __attribute__((pure)) uint64_t
+get_max_nh(uint8_t nh_sz)
+{
+       /* min between fib and lpm6 which is 21 bits */
+       return RTE_MIN(((1ULL << (bits_in_nh(nh_sz) - 1)) - 1),
+                       (1ULL << 21) - 1);
+}
+
+static int
+get_fib_type(void)
+{
+       if (config.flags & IPV6_FLAG) {
+               if ((config.flags & FIB_TYPE_MASK) == FIB_V6_TRIE_TYPE)
+                       return RTE_FIB6_TRIE;
+               else
+                       return RTE_FIB6_DUMMY;
+       } else {
+               if ((config.flags & FIB_TYPE_MASK) == FIB_V4_DIR_TYPE)
+                       return RTE_FIB_DIR24_8;
+               if ((config.flags & FIB_TYPE_MASK) == FIB_RIB_TYPE)
+                       return RTE_FIB_DUMMY;
+       }
+       return -1;
+}
+
+static int
+complete_distrib(uint8_t depth_lim, const uint32_t n, uint8_t rpd[],
+       uint32_t nrpd[])
+{
+       uint8_t depth;
+       uint32_t nr = 0;
+       uint8_t m = 0;
+
+       /*
+        * complete number of routes for every depth
+        * that was configured with ratio
+        */
+       for (depth = 0; depth <= depth_lim; depth++) {
+               if (rpd[depth] != 0) {
+                       if (rpd[depth] == UINT8_MAX)
+                               config.nb_routes_per_depth[depth] =
+                                       nrpd[depth];
+                       else
+                               config.nb_routes_per_depth[depth] =
+                                       (n * rpd[depth]) / 100;
+
+                       nr += config.nb_routes_per_depth[depth];
+                       m++;
+               }
+       }
+
+       if (nr > n) {
+               printf("Too much configured routes\n");
+               return -1;
+       }
+
+       /*complete number of routes for every unspecified depths*/
+       for (depth = 0; depth <= depth_lim; depth++) {
+               if (rpd[depth] == 0) {
+                       /*we don't need more than two /1 routes*/
+                       uint64_t max_routes_per_depth =
+                               1ULL << RTE_MIN(depth, 63);
+                       uint32_t avg_routes_left = (n - nr) /
+                               (depth_lim + 1 - m++);
+                       config.nb_routes_per_depth[depth] =
+                               RTE_MIN(max_routes_per_depth, avg_routes_left);
+                       nr += config.nb_routes_per_depth[depth];
+               }
+       }
+
+       return 0;
+}
+
+static int
+parse_distrib(uint8_t depth_lim, const uint32_t n)
+{
+       uint8_t rpd[128 + 1] = {0}; /*routes ratios per depth including /0 */
+       uint32_t nrpd[128 + 1] = {0}; /* number of routes per depth */
+       uint32_t n_routes;
+       uint8_t depth, ratio, ratio_acc = 0;
+       char *in;
+
+       in = strtok(distrib_string, ",");
+
+       /*parse configures routes percentage ratios*/
+       while (in != NULL) {
+               GET_CB_FIELD(in, depth, 0, UINT8_MAX, ':');
+               if (in[strlen(in) - 1] == '%') {
+                       in[strlen(in) - 1] = 0;
+                       GET_CB_FIELD(in, ratio, 0, UINT8_MAX, '\0');
+                       if (depth > depth_lim) {
+                               printf("Depth /%d is bigger than maximum "
+                                       "allowed depth /%d for this AF\n",
+                                       depth, depth_lim);
+                               return -EINVAL;
+                       }
+                       if (ratio > 100) {
+                               printf("Ratio for depth /%d is bigger "
+                                       "than 100%%\n", depth);
+                               return -EINVAL;
+                       }
+                       if ((depth < 64) && ((n * ratio) / 100) >
+                                       (1ULL << depth)) {
+                               printf("Configured ratio %d%% for depth /%d "
+                                       "has %d different routes, but maximum "
+                                       "is %lu\n", ratio, depth,
+                                       ((n * ratio) / 100), (1UL << depth));
+                               return -EINVAL;
+                       }
+                       rpd[depth] = ratio;
+                       /*configured zero routes for a given depth*/
+                       if (ratio == 0)
+                               rpd[depth] = UINT8_MAX;
+                       /*sum of all percentage ratios*/
+                       ratio_acc += ratio;
+               } else {
+                       GET_CB_FIELD(in, n_routes, 0, UINT32_MAX, '\0');
+                       rpd[depth] = UINT8_MAX;
+                       nrpd[depth] = n_routes;
+               }
+
+               /*number of configured depths in*/
+               in = strtok(NULL, ",");
+       }
+
+       if (ratio_acc > 100) {
+               printf("Total ratio's sum is bigger than 100%%\n");
+               return -EINVAL;
+       }
+
+       return complete_distrib(depth_lim, n, rpd, nrpd);
+}
+
+static void
+shuffle_rt_4(struct rt_rule_4 *rt, int n)
+{
+       struct rt_rule_4 tmp;
+       int i, j;
+
+       for (i = 0; i < n; i++) {
+               j = rte_rand() % n;
+               tmp.addr = rt[i].addr;
+               tmp.depth = rt[i].depth;
+               tmp.nh = rt[i].nh;
+
+               rt[i].addr = rt[j].addr;
+               rt[i].depth = rt[j].depth;
+               rt[i].nh = rt[j].nh;
+
+               rt[j].addr = tmp.addr;
+               rt[j].depth = tmp.depth;
+               rt[j].nh = tmp.nh;
+       }
+}
+
+static void
+shuffle_rt_6(struct rt_rule_6 *rt, int n)
+{
+       struct rt_rule_6 tmp;
+       int i, j;
+
+       for (i = 0; i < n; i++) {
+               j = rte_rand() % n;
+               memcpy(tmp.addr, rt[i].addr, 16);
+               tmp.depth = rt[i].depth;
+               tmp.nh = rt[i].nh;
+
+               memcpy(rt[i].addr, rt[j].addr, 16);
+               rt[i].depth = rt[j].depth;
+               rt[i].nh = rt[j].nh;
+
+               memcpy(rt[j].addr, tmp.addr, 16);
+               rt[j].depth = tmp.depth;
+               rt[j].nh = tmp.nh;
+       }
+}
+
+static void
+gen_random_rt_4(struct rt_rule_4 *rt, int nh_sz)
+{
+       uint32_t i, j, k = 0;
+
+       if (config.nb_routes_per_depth[0] != 0) {
+               rt[k].addr = 0;
+               rt[k].depth = 0;
+               rt[k++].nh = rte_rand() & get_max_nh(nh_sz);
+       }
+
+       for (i = 1; i <= 32; i++) {
+               double edge = 0;
+               double step;
+               step = (double)(1ULL << i) / config.nb_routes_per_depth[i];
+               for (j = 0; j < config.nb_routes_per_depth[i];
+                               j++, k++, edge += step) {
+                       uint64_t rnd_val = get_rnd_rng((uint64_t)edge,
+                               (uint64_t)(edge + step));
+                       rt[k].addr = rnd_val << (32 - i);
+                       rt[k].depth = i;
+                       rt[k].nh = rte_rand() & get_max_nh(nh_sz);
+               }
+       }
+}
+
+static void
+complete_v6_addr(uint32_t *addr, uint32_t rnd, int n)
+{
+       int i;
+
+       for (i = 0; i < n; i++)
+               addr[i] = rte_rand();
+       addr[i++] = rnd;
+       for (; i < 4; i++)
+               addr[i] = 0;
+}
+
+static void
+gen_random_rt_6(struct rt_rule_6 *rt, int nh_sz)
+{
+       uint32_t i, j, k = 0;
+
+       if (config.nb_routes_per_depth[0] != 0) {
+               memset(rt[k].addr, 0, 16);
+               rt[k].depth = 0;
+               rt[k++].nh = rte_rand() & get_max_nh(nh_sz);
+       }
+
+       for (int a = 0; a < 4; a++) {
+               for (i = 1; i <= 32; i++) {
+                       uint32_t rnd;
+                       double edge = 0;
+                       double step = (double)(1ULL << i) /
+                               config.nb_routes_per_depth[(a * 32) + i];
+                       for (j = 0; j < config.nb_routes_per_depth[a * 32 + i];
+                                       j++, k++, edge += step) {
+                               uint64_t rnd_val = get_rnd_rng((uint64_t)edge,
+                                       (uint64_t)(edge + step));
+                               rnd = rte_cpu_to_be_32(rnd_val << (32 - i));
+                               complete_v6_addr((uint32_t *)rt[k].addr,
+                                       rnd, a);
+                               rt[k].depth = (a * 32) + i;
+                               rt[k].nh = rte_rand() & get_max_nh(nh_sz);
+                       }
+               }
+       }
+}
+
+static inline void
+set_rnd_ipv6(uint8_t *addr, uint8_t *route, int depth)
+{
+       int i;
+
+       for (i = 0; i < 16; i++)
+               addr[i] = rte_rand();
+
+       for (i = 0; i < 16; i++) {
+               if (depth >= 8)
+                       addr[i] = route[i];
+               else if (depth > 0) {
+                       addr[i] &= (uint16_t)UINT8_MAX >> depth;
+                       addr[i] |= route[i] & UINT8_MAX << (8 - depth);
+               } else
+                       return;
+               depth -= 8;
+       }
+}
+
+static void
+gen_rnd_lookup_tbl(int af)
+{
+       uint32_t *tbl4 = config.lookup_tbl;
+       uint8_t *tbl6 = config.lookup_tbl;
+       struct rt_rule_4 *rt4 = (struct rt_rule_4 *)config.rt;
+       struct rt_rule_6 *rt6 = (struct rt_rule_6 *)config.rt;
+       uint32_t i, j;
+
+       if (af == AF_INET) {
+               for (i = 0, j = 0; i < config.nb_lookup_ips;
+                               i++, j = (j + 1) % config.nb_routes) {
+                       if ((rte_rand() % 100) < config.rnd_lookup_ips_ratio) {
+                               tbl4[i] = rte_rand();
+                               config.nb_lookup_ips_rnd++;
+                       } else
+                               tbl4[i] = rt4[j].addr | (rte_rand() &
+                                       ((1ULL << (32 - rt4[j].depth)) - 1));
+               }
+       } else {
+               for (i = 0, j = 0; i < config.nb_lookup_ips;
+                               i++, j = (j + 1) % config.nb_routes) {
+                       if ((rte_rand() % 100) < config.rnd_lookup_ips_ratio) {
+                               set_rnd_ipv6(&tbl6[i * 16], rt6[j].addr, 0);
+                               config.nb_lookup_ips_rnd++;
+                       } else {
+                               set_rnd_ipv6(&tbl6[i * 16], rt6[j].addr,
+                                       rt6[j].depth);
+                       }
+               }
+       }
+}
+
+static int
+parse_rt_4(FILE *f)
+{
+       int ret, i, j = 0;
+       char *s, *sp, *in[RT_NUM];
+       static const char *dlm = " \t\n";
+       int string_tok_nb = RTE_DIM(in);
+       struct rt_rule_4 *rt;
+
+       rt = (struct rt_rule_4 *)config.rt;
+
+       while (fgets(line, sizeof(line), f) != NULL) {
+               s = line;
+               for (i = 0; i != string_tok_nb; i++) {
+                       in[i] = strtok_r(s, dlm, &sp);
+                       if (in[i] == NULL)
+                               return -EINVAL;
+                       s = NULL;
+               }
+
+               ret = inet_net_pton(AF_INET, in[RT_PREFIX], &rt[j].addr,
+                       sizeof(rt[j].addr));
+               if (ret == -1)
+                       return -errno;
+
+               rt[j].addr = rte_be_to_cpu_32(rt[j].addr);
+               rt[j].depth = ret;
+               config.nb_routes_per_depth[ret]++;
+               GET_CB_FIELD(in[RT_NEXTHOP], rt[j].nh, 0,
+                               UINT32_MAX, 0);
+               j++;
+       }
+       return 0;
+}
+
+static int
+__inet_net_pton6(char *prefix, uint8_t *addr)
+{
+       const char *dlm = "/";
+       char *s, *sp;
+       int ret, depth;
+
+       if ((prefix == NULL) || (addr == NULL))
+               return -EINVAL;
+
+       s = strtok_r(prefix, dlm, &sp);
+       if (s == NULL)
+               return -EINVAL;
+
+       ret = inet_pton(AF_INET6, s, addr);
+       if (ret != 1)
+               return -errno;
+
+       s = strtok_r(NULL, dlm, &sp);
+       GET_CB_FIELD(s, depth, 0, 128, 0);
+
+       return depth;
+}
+
+static int
+parse_rt_6(FILE *f)
+{
+       int ret, i, j = 0;
+       char *s, *sp, *in[RT_NUM];
+       static const char *dlm = " \t\n";
+       int string_tok_nb = RTE_DIM(in);
+       struct rt_rule_6 *rt;
+
+       rt = (struct rt_rule_6 *)config.rt;
+
+       while (fgets(line, sizeof(line), f) != NULL) {
+               s = line;
+               for (i = 0; i != string_tok_nb; i++) {
+                       in[i] = strtok_r(s, dlm, &sp);
+                       if (in[i] == NULL)
+                               return -EINVAL;
+                       s = NULL;
+               }
+
+               ret = __inet_net_pton6(in[RT_PREFIX], rt[j].addr);
+               if (ret < 0)
+                       return ret;
+
+               rt[j].depth = ret;
+               config.nb_routes_per_depth[ret]++;
+               GET_CB_FIELD(in[RT_NEXTHOP], rt[j].nh, 0,
+                               UINT32_MAX, 0);
+               j++;
+       }
+
+       return 0;
+}
+
+static int
+parse_lookup(FILE *f, int af)
+{
+       int ret, i = 0;
+       uint8_t *tbl = (uint8_t *)config.lookup_tbl;
+       int step = (af == AF_INET) ? 4 : 16;
+       char *s;
+
+       while (fgets(line, sizeof(line), f) != NULL) {
+               s = strtok(line, " \t\n");
+               ret = inet_pton(af, s, &tbl[i]);
+               if (ret != 1)
+                       return -EINVAL;
+               i += step;
+       }
+       return 0;
+}
+
+static int
+dump_lookup(int af)
+{
+       FILE *f;
+       uint32_t *tbl4 = config.lookup_tbl;
+       uint8_t *tbl6 = config.lookup_tbl;
+       uint32_t i;
+
+       f = fopen(config.lookup_ips_file_s, "w");
+       if (f == NULL) {
+               printf("Can not open file %s\n", config.lookup_ips_file_s);
+               return -1;
+       }
+
+       if (af == AF_INET) {
+               for (i = 0; i < config.nb_lookup_ips; i++)
+                       fprintf(f, NIPQUAD_FMT"\n", NIPQUAD(tbl4[i]));
+       } else {
+               for (i = 0; i < config.nb_lookup_ips; i++)
+                       fprintf(f, NIPQUAD6_FMT"\n", NIPQUAD6(&tbl6[i * 16]));
+       }
+       fclose(f);
+       return 0;
+}
+
+static void
+print_config(void)
+{
+       uint8_t depth_lim;
+       char dlm;
+       int i;
+
+       depth_lim = ((config.flags & IPV6_FLAG) == IPV6_FLAG) ? 128 : 32;
+
+       fprintf(stdout,
+               "Routes total: %u\n"
+               "Routes distribution:\n", config.nb_routes);
+
+       for (i = 1; i <= depth_lim; i++) {
+               fprintf(stdout,
+                       "depth /%d:%u", i, config.nb_routes_per_depth[i]);
+               if (i % 4 == 0)
+                       dlm = '\n';
+               else
+                       dlm = '\t';
+               fprintf(stdout, "%c", dlm);
+       }
+
+       fprintf(stdout,
+               "Lookup tuples: %u\n"
+               "Configured ratios of random ips for lookup: %u\n"
+               "Random lookup ips: %u\n",
+               config.nb_lookup_ips, config.rnd_lookup_ips_ratio,
+               config.nb_lookup_ips_rnd);
+}
+
+static void
+print_usage(void)
+{
+       fprintf(stdout,
+               PRINT_USAGE_START
+               "[-f <routes file>]\n"
+               "[-t <ip's file for lookup>]\n"
+               "[-n <number of routes (if -f is not specified)>]\n"
+               "[-l <number of ip's for lookup (if -t is not specified)>]\n"
+               "[-d <\",\" separated \"depth:n%%\"routes depth distribution"
+               "(if -f is not specified)>]\n"
+               "[-r <percentage ratio of random ip's to lookup"
+               "(if -t is not specified)>]\n"
+               "[-c <do comarison with LPM library>]\n"
+               "[-6 <do tests with ipv6 (default ipv4)>]\n"
+               "[-s <shuffle randomly generated routes>]\n"
+               "[-a <check nexthops for all ipv4 address space"
+               "(only valid with -c)>]\n"
+               "[-b <fib algorithm>]\n\tavailible options for ipv4\n"
+               "\t\trib - RIB based FIB\n"
+               "\t\tdir - DIR24_8 based FIB\n"
+               "\tavailible options for ipv6:\n"
+               "\t\trib - RIB based FIB\n"
+               "\t\ttrie - TRIE based FIB\n"
+               "defaults are: dir for ipv4 and trie for ipv6\n"
+               "[-e <entry size (valid only for dir and trie fib types): "
+               "1/2/4/8 (default 4)>]\n"
+               "[-g <number of tbl8's for dir24_8 or trie FIBs>]\n"
+               "[-w <path to the file to dump routing table>]\n"
+               "[-u <path to the file to dump ip's for lookup>]\n",
+               config.prgname);
+}
+
+static int
+check_config(void)
+{
+       if ((config.routes_file == NULL) && (config.lookup_ips_file != NULL)) {
+               printf("-t option only valid with -f option\n");
+               return -1;
+       }
+
+       if ((config.flags & CMP_ALL_FLAG) && (config.flags & IPV6_FLAG)) {
+               printf("-a flag is only valid for ipv4\n");
+               return -1;
+       }
+
+       if ((config.flags & CMP_ALL_FLAG) &&
+                       ((config.flags & CMP_FLAG) != CMP_FLAG)) {
+               printf("-a flag is valid only with -c flag\n");
+               return -1;
+       }
+
+       if (!((config.ent_sz == 1) || (config.ent_sz == 2) ||
+                       (config.ent_sz == 4) || (config.ent_sz == 8))) {
+               printf("wrong -e option %d, can be 1 or 2 or 4 or 8\n",
+                       config.ent_sz);
+               return -1;
+       }
+
+       if ((config.ent_sz == 1) && (config.flags & IPV6_FLAG)) {
+               printf("-e 1 is valid only for ipv4\n");
+               return -1;
+       }
+       return 0;
+}
+
+static void
+parse_opts(int argc, char **argv)
+{
+       int opt;
+       char *endptr;
+
+       while ((opt = getopt(argc, argv, "f:t:n:d:l:r:c6ab:e:g:w:u:s")) !=
+                       -1) {
+               switch (opt) {
+               case 'f':
+                       config.routes_file = optarg;
+                       break;
+               case 't':
+                       config.lookup_ips_file = optarg;
+                       break;
+               case 'w':
+                       config.routes_file_s = optarg;
+                       config.flags |= DRY_RUN_FLAG;
+                       break;
+               case 'u':
+                       config.lookup_ips_file_s = optarg;
+                       config.flags |= DRY_RUN_FLAG;
+                       break;
+               case 'n':
+                       errno = 0;
+                       config.nb_routes = strtoul(optarg, &endptr, 10);
+                       if ((errno != 0) || (config.nb_routes == 0)) {
+                               print_usage();
+                               rte_exit(-EINVAL, "Invalid option -n\n");
+                       }
+                       break;
+               case 'd':
+                       distrib_string = optarg;
+                       break;
+               case 'l':
+                       errno = 0;
+                       config.nb_lookup_ips = strtoul(optarg, &endptr, 10);
+                       if ((errno != 0) || (config.nb_lookup_ips == 0)) {
+                               print_usage();
+                               rte_exit(-EINVAL, "Invalid option -l\n");
+                       }
+                       break;
+               case 'r':
+                       errno = 0;
+                       config.rnd_lookup_ips_ratio =
+                               strtoul(optarg, &endptr, 10);
+                       if ((errno != 0) ||
+                                       (config.rnd_lookup_ips_ratio == 0) ||
+                                       (config.rnd_lookup_ips_ratio >= 100)) {
+                               print_usage();
+                               rte_exit(-EINVAL, "Invalid option -r\n");
+                       }
+                       break;
+               case 's':
+                       config.flags |= SHUFFLE_FLAG;
+                       break;
+               case 'c':
+                       config.flags |= CMP_FLAG;
+                       break;
+               case '6':
+                       config.flags |= IPV6_FLAG;
+                       break;
+               case 'a':
+                       config.flags |= CMP_ALL_FLAG;
+                       break;
+               case 'b':
+                       if (strcmp(optarg, "rib") == 0) {
+                               config.flags &= ~FIB_TYPE_MASK;
+                               config.flags |= FIB_RIB_TYPE;
+                       } else if (strcmp(optarg, "dir") == 0) {
+                               config.flags &= ~FIB_TYPE_MASK;
+                               config.flags |= FIB_V4_DIR_TYPE;
+                       } else if (strcmp(optarg, "trie") == 0) {
+                               config.flags &= ~FIB_TYPE_MASK;
+                               config.flags |= FIB_V6_TRIE_TYPE;
+                       } else
+                               rte_exit(-EINVAL, "Invalid option -b\n");
+                       break;
+               case 'e':
+                       errno = 0;
+                       config.ent_sz = strtoul(optarg, &endptr, 10);
+                       if (errno != 0) {
+                               print_usage();
+                               rte_exit(-EINVAL, "Invalid option -e\n");
+                       }
+                       break;
+               case 'g':
+                       errno = 0;
+                       config.tbl8 = strtoul(optarg, &endptr, 10);
+                       if ((errno != 0) || (config.tbl8 == 0)) {
+                               print_usage();
+                               rte_exit(-EINVAL, "Invalid option -g\n");
+                       }
+                       break;
+               default:
+                       print_usage();
+                       rte_exit(-EINVAL, "Invalid options\n");
+               }
+       }
+}
+
+static int
+dump_rt_4(struct rt_rule_4 *rt)
+{
+       FILE *f;
+       uint32_t i;
+
+       f = fopen(config.routes_file_s, "w");
+       if (f == NULL) {
+               printf("Can not open file %s\n", config.routes_file_s);
+               return -1;
+       }
+
+       for (i = 0; i < config.nb_routes; i++)
+               fprintf(f, NIPQUAD_FMT"/%d %lu\n", NIPQUAD(rt[i].addr),
+                       rt[i].depth, rt[i].nh);
+
+       fclose(f);
+       return 0;
+}
+
+static inline void
+print_depth_err(void)
+{
+       printf("LPM does not support /0 prefix length (default route), use "
+               "-d 0:0 option or remove /0 prefix from routes file\n");
+}
+
+static int
+run_v4(void)
+{
+       uint64_t start, acc;
+       uint64_t def_nh = 0;
+       struct rte_fib *fib;
+       struct rte_fib_conf conf = {0};
+       struct rt_rule_4 *rt;
+       uint32_t i, j, k;
+       int ret = 0;
+       struct rte_lpm  *lpm = NULL;
+       struct rte_lpm_config lpm_conf;
+       uint32_t *tbl4 = config.lookup_tbl;
+       uint64_t fib_nh[BURST_SZ];
+       uint32_t lpm_nh[BURST_SZ];
+
+       rt = (struct rt_rule_4 *)config.rt;
+
+       if (config.flags & DRY_RUN_FLAG) {
+               if (config.routes_file_s != NULL)
+                       ret = dump_rt_4(rt);
+               if (ret != 0)
+                       return ret;
+               if (config.lookup_ips_file_s != NULL)
+                       ret = dump_lookup(AF_INET);
+               return ret;
+       }
+
+       conf.type = get_fib_type();
+       conf.default_nh = def_nh;
+       conf.max_routes = config.nb_routes * 2;
+       if (conf.type == RTE_FIB_DIR24_8) {
+               conf.dir24_8.nh_sz = __builtin_ctz(config.ent_sz);
+               conf.dir24_8.num_tbl8 = RTE_MIN(config.tbl8,
+                       get_max_nh(conf.dir24_8.nh_sz));
+       }
+
+       fib = rte_fib_create("test", -1, &conf);
+       if (fib == NULL) {
+               printf("Can not alloc FIB, err %d\n", rte_errno);
+               return -rte_errno;
+       }
+
+       for (k = config.print_fract, i = 0; k > 0; k--) {
+               start = rte_rdtsc_precise();
+               for (j = 0; j < (config.nb_routes - i) / k; j++) {
+                       ret = rte_fib_add(fib, rt[i + j].addr, rt[i + j].depth,
+                               rt[i + j].nh);
+                       if (unlikely(ret != 0)) {
+                               printf("Can not add a route to FIB, err %d\n",
+                                       ret);
+                               return -ret;
+                       }
+               }
+               printf("AVG FIB add %lu\n", (rte_rdtsc_precise() - start) / j);
+               i += j;
+       }
+
+       if (config.flags & CMP_FLAG) {
+               lpm_conf.max_rules = config.nb_routes * 2;
+               lpm_conf.number_tbl8s = RTE_MAX(conf.dir24_8.num_tbl8,
+                       config.tbl8);
+
+               lpm = rte_lpm_create("test_lpm", -1, &lpm_conf);
+               if (lpm == NULL) {
+                       printf("Can not alloc LPM, err %d\n", rte_errno);
+                       return -rte_errno;
+               }
+               for (k = config.print_fract, i = 0; k > 0; k--) {
+                       start = rte_rdtsc_precise();
+                       for (j = 0; j < (config.nb_routes - i) / k; j++) {
+                               ret = rte_lpm_add(lpm, rt[i + j].addr,
+                                       rt[i + j].depth, rt[i + j].nh);
+                               if (ret != 0) {
+                                       if (rt[i + j].depth == 0)
+                                               print_depth_err();
+                                       printf("Can not add a route to LPM, "
+                                               "err %d\n", ret);
+                                       return -ret;
+                               }
+                       }
+                       printf("AVG LPM add %lu\n",
+                               (rte_rdtsc_precise() - start) / j);
+                       i += j;
+               }
+       }
+
+       acc = 0;
+       for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
+               start = rte_rdtsc_precise();
+               ret = rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ);
+               acc += rte_rdtsc_precise() - start;
+               if (ret != 0) {
+                       printf("FIB lookup fails, err %d\n", ret);
+                       return -ret;
+               }
+       }
+       printf("AVG FIB lookup %.1f\n", (double)acc / (double)i);
+
+       if (config.flags & CMP_FLAG) {
+               acc = 0;
+               for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
+                       start = rte_rdtsc_precise();
+                       ret = rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh,
+                               BURST_SZ);
+                       acc += rte_rdtsc_precise() - start;
+                       if (ret != 0) {
+                               printf("LPM lookup fails, err %d\n", ret);
+                               return -ret;
+                       }
+               }
+               printf("AVG LPM lookup %.1f\n", (double)acc / (double)i);
+
+               for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
+                       rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ);
+                       rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh, BURST_SZ);
+                       for (j = 0; j < BURST_SZ; j++) {
+                               struct rte_lpm_tbl_entry *tbl;
+                               tbl = (struct rte_lpm_tbl_entry *)&lpm_nh[j];
+                               if ((fib_nh[j] != tbl->next_hop) &&
+                                               !((tbl->valid == 0) &&
+                                               (fib_nh[j] == def_nh))) {
+                                       printf("FAIL\n");
+                                       return -1;
+                               }
+                       }
+               }
+               printf("FIB and LPM lookup returns same values\n");
+       }
+
+       for (k = config.print_fract, i = 0; k > 0; k--) {
+               start = rte_rdtsc_precise();
+               for (j = 0; j < (config.nb_routes - i) / k; j++)
+                       rte_fib_delete(fib, rt[i + j].addr, rt[i + j].depth);
+
+               printf("AVG FIB delete %lu\n",
+                       (rte_rdtsc_precise() - start) / j);
+               i += j;
+       }
+
+       if (config.flags & CMP_FLAG) {
+               for (k = config.print_fract, i = 0; k > 0; k--) {
+                       start = rte_rdtsc_precise();
+                       for (j = 0; j < (config.nb_routes - i) / k; j++)
+                               rte_lpm_delete(lpm, rt[i + j].addr,
+                                       rt[i + j].depth);
+
+                       printf("AVG LPM delete %lu\n",
+                               (rte_rdtsc_precise() - start) / j);
+                       i += j;
+               }
+       }
+
+       return 0;
+}
+
+static int
+dump_rt_6(struct rt_rule_6 *rt)
+{
+       FILE *f;
+       uint32_t i;
+
+       f = fopen(config.routes_file_s, "w");
+       if (f == NULL) {
+               printf("Can not open file %s\n", config.routes_file_s);
+               return -1;
+       }
+
+       for (i = 0; i < config.nb_routes; i++) {
+               fprintf(f, NIPQUAD6_FMT"/%d %lu\n", NIPQUAD6(rt[i].addr),
+                       rt[i].depth, rt[i].nh);
+
+       }
+       fclose(f);
+       return 0;
+}
+
+static int
+run_v6(void)
+{
+       uint64_t start, acc;
+       uint64_t def_nh = 0;
+       struct rte_fib6 *fib;
+       struct rte_fib6_conf conf = {0};
+       struct rt_rule_6 *rt;
+       uint32_t i, j, k;
+       int ret = 0;
+       struct rte_lpm6 *lpm = NULL;
+       struct rte_lpm6_config lpm_conf;
+       uint8_t *tbl6;
+       uint64_t fib_nh[BURST_SZ];
+       int32_t lpm_nh[BURST_SZ];
+
+       rt = (struct rt_rule_6 *)config.rt;
+       tbl6 = config.lookup_tbl;
+
+       if (config.flags & DRY_RUN_FLAG) {
+               if (config.routes_file_s != NULL)
+                       ret =  dump_rt_6(rt);
+               if (ret != 0)
+                       return ret;
+               if (config.lookup_ips_file_s != NULL)
+                       ret = dump_lookup(AF_INET6);
+               return ret;
+       }
+
+       conf.type = get_fib_type();
+       conf.default_nh = def_nh;
+       conf.max_routes = config.nb_routes * 2;
+       if (conf.type == RTE_FIB6_TRIE) {
+               conf.trie.nh_sz = __builtin_ctz(config.ent_sz);
+               conf.trie.num_tbl8 = RTE_MIN(config.tbl8,
+                       get_max_nh(conf.trie.nh_sz));
+       }
+
+       fib = rte_fib6_create("test", -1, &conf);
+       if (fib == NULL) {
+               printf("Can not alloc FIB, err %d\n", rte_errno);
+               return -rte_errno;
+       }
+
+       for (k = config.print_fract, i = 0; k > 0; k--) {
+               start = rte_rdtsc_precise();
+               for (j = 0; j < (config.nb_routes - i) / k; j++) {
+                       ret = rte_fib6_add(fib, rt[i + j].addr,
+                               rt[i + j].depth, rt[i + j].nh);
+                       if (unlikely(ret != 0)) {
+                               printf("Can not add a route to FIB, err %d\n",
+                                       ret);
+                               return -ret;
+                       }
+               }
+               printf("AVG FIB add %lu\n", (rte_rdtsc_precise() - start) / j);
+               i += j;
+       }
+
+       if (config.flags & CMP_FLAG) {
+               lpm_conf.max_rules = config.nb_routes * 2;
+               lpm_conf.number_tbl8s = RTE_MAX(conf.trie.num_tbl8,
+                       config.tbl8);
+
+               lpm = rte_lpm6_create("test_lpm", -1, &lpm_conf);
+               if (lpm == NULL) {
+                       printf("Can not alloc LPM, err %d\n", rte_errno);
+                       return -rte_errno;
+               }
+               for (k = config.print_fract, i = 0; k > 0; k--) {
+                       start = rte_rdtsc_precise();
+                       for (j = 0; j < (config.nb_routes - i) / k; j++) {
+                               ret = rte_lpm6_add(lpm, rt[i + j].addr,
+                                       rt[i + j].depth, rt[i + j].nh);
+                               if (ret != 0) {
+                                       if (rt[i + j].depth == 0)
+                                               print_depth_err();
+                                       printf("Can not add a route to LPM, "
+                                               "err %d\n", ret);
+                                       return -ret;
+                               }
+                       }
+                       printf("AVG LPM add %lu\n",
+                               (rte_rdtsc_precise() - start) / j);
+                       i += j;
+               }
+       }
+
+       acc = 0;
+       for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
+               start = rte_rdtsc_precise();
+               ret = rte_fib6_lookup_bulk(fib, (uint8_t (*)[16])(tbl6 + i*16),
+                       fib_nh, BURST_SZ);
+               acc += rte_rdtsc_precise() - start;
+               if (ret != 0) {
+                       printf("FIB lookup fails, err %d\n", ret);
+                       return -ret;
+               }
+       }
+       printf("AVG FIB lookup %.1f\n", (double)acc / (double)i);
+
+       if (config.flags & CMP_FLAG) {
+               acc = 0;
+               for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
+                       start = rte_rdtsc_precise();
+                       ret = rte_lpm6_lookup_bulk_func(lpm,
+                               (uint8_t (*)[16])(tbl6 + i*16),
+                               lpm_nh, BURST_SZ);
+                       acc += rte_rdtsc_precise() - start;
+                       if (ret != 0) {
+                               printf("LPM lookup fails, err %d\n", ret);
+                               return -ret;
+                       }
+               }
+               printf("AVG LPM lookup %.1f\n", (double)acc / (double)i);
+
+               for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
+                       rte_fib6_lookup_bulk(fib,
+                               (uint8_t (*)[16])(tbl6 + i*16),
+                               fib_nh, BURST_SZ);
+                       rte_lpm6_lookup_bulk_func(lpm,
+                               (uint8_t (*)[16])(tbl6 + i*16),
+                               lpm_nh, BURST_SZ);
+                       for (j = 0; j < BURST_SZ; j++) {
+                               if ((fib_nh[j] != (uint32_t)lpm_nh[j]) &&
+                                               !((lpm_nh[j] == -1) &&
+                                               (fib_nh[j] == def_nh))) {
+                                       printf("FAIL\n");
+                                       return -1;
+                               }
+                       }
+               }
+               printf("FIB and LPM lookup returns same values\n");
+       }
+
+       for (k = config.print_fract, i = 0; k > 0; k--) {
+               start = rte_rdtsc_precise();
+               for (j = 0; j < (config.nb_routes - i) / k; j++)
+                       rte_fib6_delete(fib, rt[i + j].addr, rt[i + j].depth);
+
+               printf("AVG FIB delete %lu\n",
+                       (rte_rdtsc_precise() - start) / j);
+               i += j;
+       }
+
+       if (config.flags & CMP_FLAG) {
+               for (k = config.print_fract, i = 0; k > 0; k--) {
+                       start = rte_rdtsc_precise();
+                       for (j = 0; j < (config.nb_routes - i) / k; j++)
+                               rte_lpm6_delete(lpm, rt[i + j].addr,
+                                       rt[i + j].depth);
+
+                       printf("AVG LPM delete %lu\n",
+                               (rte_rdtsc_precise() - start) / j);
+                       i += j;
+               }
+       }
+       return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+       int ret, af, rt_ent_sz, lookup_ent_sz;
+       FILE    *fr, *fl;
+       uint8_t depth_lim;
+
+       ret = rte_eal_init(argc, argv);
+       if (ret < 0)
+               rte_panic("Cannot init EAL\n");
+
+       argc -= ret;
+       argv += ret;
+
+       config.prgname = argv[0];
+
+       parse_opts(argc, argv);
+
+       ret = check_config();
+       if (ret != 0)
+               rte_exit(-ret, "Bad configuration\n");
+
+       af = ((config.flags & IPV6_FLAG) == 0) ? AF_INET : AF_INET6;
+       depth_lim = (af == AF_INET) ? 32 : 128;
+       rt_ent_sz = (af == AF_INET) ? sizeof(struct rt_rule_4) :
+               sizeof(struct rt_rule_6);
+       lookup_ent_sz = (af == AF_INET) ? 4 : 16;
+
+       /* Count number of rules in file*/
+       if (config.routes_file != NULL) {
+               fr = fopen(config.routes_file, "r");
+               if (fr == NULL)
+                       rte_exit(-errno, "Can not open file with routes %s\n",
+                               config.routes_file);
+
+               config.nb_routes = 0;
+               while (fgets(line, sizeof(line), fr) != NULL)
+                       config.nb_routes++;
+               rewind(fr);
+       }
+
+       /* Count number of ip's in file*/
+       if (config.lookup_ips_file != NULL) {
+               fl = fopen(config.lookup_ips_file, "r");
+               if (fl == NULL)
+                       rte_exit(-errno, "Can not open file with ip's %s\n",
+                               config.lookup_ips_file);
+
+               config.nb_lookup_ips = 0;
+               while (fgets(line, sizeof(line), fl) != NULL)
+                       config.nb_lookup_ips++;
+               rewind(fl);
+       }
+
+       /* Alloc routes table*/
+       config.rt  = rte_malloc(NULL, rt_ent_sz * config.nb_routes, 0);
+       if (config.rt == NULL)
+               rte_exit(-ENOMEM, "Can not alloc rt\n");
+
+       /* Alloc table with ip's for lookup*/
+       config.lookup_tbl  = rte_malloc(NULL, lookup_ent_sz *
+               config.nb_lookup_ips, 0);
+       if (config.lookup_tbl == NULL)
+               rte_exit(-ENOMEM, "Can not alloc lookup table\n");
+
+       /* Fill routes table */
+       if (config.routes_file == NULL) {
+               if (distrib_string != NULL)
+                       ret = parse_distrib(depth_lim, config.nb_routes);
+               else {
+                       uint8_t rpd[129] = {0};
+                       uint32_t nrpd[129] = {0};
+                       ret = complete_distrib(depth_lim, config.nb_routes,
+                               rpd, nrpd);
+               }
+               if (ret != 0)
+                       rte_exit(-ret,
+                               "Bad routes distribution configuration\n");
+               if (af == AF_INET) {
+                       gen_random_rt_4(config.rt,
+                               __builtin_ctz(config.ent_sz));
+                       if (config.flags & SHUFFLE_FLAG)
+                               shuffle_rt_4(config.rt, config.nb_routes);
+               } else {
+                       gen_random_rt_6(config.rt,
+                               __builtin_ctz(config.ent_sz));
+                       if (config.flags & SHUFFLE_FLAG)
+                               shuffle_rt_6(config.rt, config.nb_routes);
+               }
+       } else {
+               if (af == AF_INET)
+                       ret = parse_rt_4(fr);
+               else
+                       ret = parse_rt_6(fr);
+
+               if (ret != 0) {
+                       rte_exit(-ret, "failed to parse routes file %s\n",
+                               config.routes_file);
+               }
+       }
+
+       /* Fill lookup table with ip's*/
+       if (config.lookup_ips_file == NULL)
+               gen_rnd_lookup_tbl(af);
+       else {
+               ret = parse_lookup(fl, af);
+               if (ret != 0)
+                       rte_exit(-ret, "failed to parse lookup file\n");
+       }
+
+       print_config();
+
+       if (af == AF_INET)
+               ret = run_v4();
+       else
+               ret = run_v6();
+
+       return ret;
+}
diff --git a/app/test-fib/meson.build b/app/test-fib/meson.build
new file mode 100644 (file)
index 0000000..4db0010
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2019 Intel Corporation
+
+allow_experimental_apis = true
+sources = files('main.c')
+deps += ['fib', 'lpm', 'net']