xref: /dpdk/app/test-fib/main.c (revision 10b71caecbe1cddcbb65c050ca775fba575e88db)
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 }
1265