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