xref: /dpdk/app/test-fib/main.c (revision 9cd9d3e702fba4700539c1a2eddac13dd14ecf70)
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 		ret = inet_pton(af, s, &tbl[i]);
547 		if (ret != 1)
548 			return -EINVAL;
549 		i += step;
550 	}
551 	return 0;
552 }
553 
554 static int
555 dump_lookup(int af)
556 {
557 	FILE *f;
558 	uint32_t *tbl4 = config.lookup_tbl;
559 	uint8_t *tbl6 = config.lookup_tbl;
560 	uint32_t i;
561 
562 	f = fopen(config.lookup_ips_file_s, "w");
563 	if (f == NULL) {
564 		printf("Can not open file %s\n", config.lookup_ips_file_s);
565 		return -1;
566 	}
567 
568 	if (af == AF_INET) {
569 		for (i = 0; i < config.nb_lookup_ips; i++)
570 			fprintf(f, NIPQUAD_FMT"\n", NIPQUAD(tbl4[i]));
571 	} else {
572 		for (i = 0; i < config.nb_lookup_ips; i++)
573 			fprintf(f, NIPQUAD6_FMT"\n", NIPQUAD6(&tbl6[i * 16]));
574 	}
575 	fclose(f);
576 	return 0;
577 }
578 
579 static void
580 print_config(void)
581 {
582 	uint8_t depth_lim;
583 	char dlm;
584 	int i;
585 
586 	depth_lim = ((config.flags & IPV6_FLAG) == IPV6_FLAG) ? 128 : 32;
587 
588 	fprintf(stdout,
589 		"Routes total: %u\n"
590 		"Routes distribution:\n", config.nb_routes);
591 
592 	for (i = 1; i <= depth_lim; i++) {
593 		fprintf(stdout,
594 			"depth /%d:%u", i, config.nb_routes_per_depth[i]);
595 		if (i % 4 == 0)
596 			dlm = '\n';
597 		else
598 			dlm = '\t';
599 		fprintf(stdout, "%c", dlm);
600 	}
601 
602 	fprintf(stdout,
603 		"Lookup tuples: %u\n"
604 		"Configured ratios of random ips for lookup: %u\n"
605 		"Random lookup ips: %u\n",
606 		config.nb_lookup_ips, config.rnd_lookup_ips_ratio,
607 		config.nb_lookup_ips_rnd);
608 }
609 
610 static void
611 print_usage(void)
612 {
613 	fprintf(stdout,
614 		PRINT_USAGE_START
615 		"[-f <routes file>]\n"
616 		"[-t <ip's file for lookup>]\n"
617 		"[-n <number of routes (if -f is not specified)>]\n"
618 		"[-l <number of ip's for lookup (if -t is not specified)>]\n"
619 		"[-d <\",\" separated \"depth:n%%\"routes depth distribution"
620 		"(if -f is not specified)>]\n"
621 		"[-r <percentage ratio of random ip's to lookup"
622 		"(if -t is not specified)>]\n"
623 		"[-c <do comarison with LPM library>]\n"
624 		"[-6 <do tests with ipv6 (default ipv4)>]\n"
625 		"[-s <shuffle randomly generated routes>]\n"
626 		"[-a <check nexthops for all ipv4 address space"
627 		"(only valid with -c)>]\n"
628 		"[-b <fib algorithm>]\n\tavailible options for ipv4\n"
629 		"\t\trib - RIB based FIB\n"
630 		"\t\tdir - DIR24_8 based FIB\n"
631 		"\tavailible options for ipv6:\n"
632 		"\t\trib - RIB based FIB\n"
633 		"\t\ttrie - TRIE based FIB\n"
634 		"defaults are: dir for ipv4 and trie for ipv6\n"
635 		"[-e <entry size (valid only for dir and trie fib types): "
636 		"1/2/4/8 (default 4)>]\n"
637 		"[-g <number of tbl8's for dir24_8 or trie FIBs>]\n"
638 		"[-w <path to the file to dump routing table>]\n"
639 		"[-u <path to the file to dump ip's for lookup>]\n",
640 		config.prgname);
641 }
642 
643 static int
644 check_config(void)
645 {
646 	if ((config.routes_file == NULL) && (config.lookup_ips_file != NULL)) {
647 		printf("-t option only valid with -f option\n");
648 		return -1;
649 	}
650 
651 	if ((config.flags & CMP_ALL_FLAG) && (config.flags & IPV6_FLAG)) {
652 		printf("-a flag is only valid for ipv4\n");
653 		return -1;
654 	}
655 
656 	if ((config.flags & CMP_ALL_FLAG) &&
657 			((config.flags & CMP_FLAG) != CMP_FLAG)) {
658 		printf("-a flag is valid only with -c flag\n");
659 		return -1;
660 	}
661 
662 	if (!((config.ent_sz == 1) || (config.ent_sz == 2) ||
663 			(config.ent_sz == 4) || (config.ent_sz == 8))) {
664 		printf("wrong -e option %d, can be 1 or 2 or 4 or 8\n",
665 			config.ent_sz);
666 		return -1;
667 	}
668 
669 	if ((config.ent_sz == 1) && (config.flags & IPV6_FLAG)) {
670 		printf("-e 1 is valid only for ipv4\n");
671 		return -1;
672 	}
673 	return 0;
674 }
675 
676 static void
677 parse_opts(int argc, char **argv)
678 {
679 	int opt;
680 	char *endptr;
681 
682 	while ((opt = getopt(argc, argv, "f:t:n:d:l:r:c6ab:e:g:w:u:s")) !=
683 			-1) {
684 		switch (opt) {
685 		case 'f':
686 			config.routes_file = optarg;
687 			break;
688 		case 't':
689 			config.lookup_ips_file = optarg;
690 			break;
691 		case 'w':
692 			config.routes_file_s = optarg;
693 			config.flags |= DRY_RUN_FLAG;
694 			break;
695 		case 'u':
696 			config.lookup_ips_file_s = optarg;
697 			config.flags |= DRY_RUN_FLAG;
698 			break;
699 		case 'n':
700 			errno = 0;
701 			config.nb_routes = strtoul(optarg, &endptr, 10);
702 			if ((errno != 0) || (config.nb_routes == 0)) {
703 				print_usage();
704 				rte_exit(-EINVAL, "Invalid option -n\n");
705 			}
706 			break;
707 		case 'd':
708 			distrib_string = optarg;
709 			break;
710 		case 'l':
711 			errno = 0;
712 			config.nb_lookup_ips = strtoul(optarg, &endptr, 10);
713 			if ((errno != 0) || (config.nb_lookup_ips == 0)) {
714 				print_usage();
715 				rte_exit(-EINVAL, "Invalid option -l\n");
716 			}
717 			break;
718 		case 'r':
719 			errno = 0;
720 			config.rnd_lookup_ips_ratio =
721 				strtoul(optarg, &endptr, 10);
722 			if ((errno != 0) ||
723 					(config.rnd_lookup_ips_ratio == 0) ||
724 					(config.rnd_lookup_ips_ratio >= 100)) {
725 				print_usage();
726 				rte_exit(-EINVAL, "Invalid option -r\n");
727 			}
728 			break;
729 		case 's':
730 			config.flags |= SHUFFLE_FLAG;
731 			break;
732 		case 'c':
733 			config.flags |= CMP_FLAG;
734 			break;
735 		case '6':
736 			config.flags |= IPV6_FLAG;
737 			break;
738 		case 'a':
739 			config.flags |= CMP_ALL_FLAG;
740 			break;
741 		case 'b':
742 			if (strcmp(optarg, "rib") == 0) {
743 				config.flags &= ~FIB_TYPE_MASK;
744 				config.flags |= FIB_RIB_TYPE;
745 			} else if (strcmp(optarg, "dir") == 0) {
746 				config.flags &= ~FIB_TYPE_MASK;
747 				config.flags |= FIB_V4_DIR_TYPE;
748 			} else if (strcmp(optarg, "trie") == 0) {
749 				config.flags &= ~FIB_TYPE_MASK;
750 				config.flags |= FIB_V6_TRIE_TYPE;
751 			} else
752 				rte_exit(-EINVAL, "Invalid option -b\n");
753 			break;
754 		case 'e':
755 			errno = 0;
756 			config.ent_sz = strtoul(optarg, &endptr, 10);
757 			if (errno != 0) {
758 				print_usage();
759 				rte_exit(-EINVAL, "Invalid option -e\n");
760 			}
761 			break;
762 		case 'g':
763 			errno = 0;
764 			config.tbl8 = strtoul(optarg, &endptr, 10);
765 			if ((errno != 0) || (config.tbl8 == 0)) {
766 				print_usage();
767 				rte_exit(-EINVAL, "Invalid option -g\n");
768 			}
769 			break;
770 		default:
771 			print_usage();
772 			rte_exit(-EINVAL, "Invalid options\n");
773 		}
774 	}
775 }
776 
777 static int
778 dump_rt_4(struct rt_rule_4 *rt)
779 {
780 	FILE *f;
781 	uint32_t i;
782 
783 	f = fopen(config.routes_file_s, "w");
784 	if (f == NULL) {
785 		printf("Can not open file %s\n", config.routes_file_s);
786 		return -1;
787 	}
788 
789 	for (i = 0; i < config.nb_routes; i++)
790 		fprintf(f, NIPQUAD_FMT"/%d %"PRIu64"\n", NIPQUAD(rt[i].addr),
791 			rt[i].depth, rt[i].nh);
792 
793 	fclose(f);
794 	return 0;
795 }
796 
797 static inline void
798 print_depth_err(void)
799 {
800 	printf("LPM does not support /0 prefix length (default route), use "
801 		"-d 0:0 option or remove /0 prefix from routes file\n");
802 }
803 
804 static int
805 run_v4(void)
806 {
807 	uint64_t start, acc;
808 	uint64_t def_nh = 0;
809 	struct rte_fib *fib;
810 	struct rte_fib_conf conf = {0};
811 	struct rt_rule_4 *rt;
812 	uint32_t i, j, k;
813 	int ret = 0;
814 	struct rte_lpm	*lpm = NULL;
815 	struct rte_lpm_config lpm_conf;
816 	uint32_t *tbl4 = config.lookup_tbl;
817 	uint64_t fib_nh[BURST_SZ];
818 	uint32_t lpm_nh[BURST_SZ];
819 
820 	rt = (struct rt_rule_4 *)config.rt;
821 
822 	if (config.flags & DRY_RUN_FLAG) {
823 		if (config.routes_file_s != NULL)
824 			ret = dump_rt_4(rt);
825 		if (ret != 0)
826 			return ret;
827 		if (config.lookup_ips_file_s != NULL)
828 			ret = dump_lookup(AF_INET);
829 		return ret;
830 	}
831 
832 	conf.type = get_fib_type();
833 	conf.default_nh = def_nh;
834 	conf.max_routes = config.nb_routes * 2;
835 	if (conf.type == RTE_FIB_DIR24_8) {
836 		conf.dir24_8.nh_sz = __builtin_ctz(config.ent_sz);
837 		conf.dir24_8.num_tbl8 = RTE_MIN(config.tbl8,
838 			get_max_nh(conf.dir24_8.nh_sz));
839 	}
840 
841 	fib = rte_fib_create("test", -1, &conf);
842 	if (fib == NULL) {
843 		printf("Can not alloc FIB, err %d\n", rte_errno);
844 		return -rte_errno;
845 	}
846 
847 	for (k = config.print_fract, i = 0; k > 0; k--) {
848 		start = rte_rdtsc_precise();
849 		for (j = 0; j < (config.nb_routes - i) / k; j++) {
850 			ret = rte_fib_add(fib, rt[i + j].addr, rt[i + j].depth,
851 				rt[i + j].nh);
852 			if (unlikely(ret != 0)) {
853 				printf("Can not add a route to FIB, err %d\n",
854 					ret);
855 				return -ret;
856 			}
857 		}
858 		printf("AVG FIB add %"PRIu64"\n",
859 			(rte_rdtsc_precise() - start) / j);
860 		i += j;
861 	}
862 
863 	if (config.flags & CMP_FLAG) {
864 		lpm_conf.max_rules = config.nb_routes * 2;
865 		lpm_conf.number_tbl8s = RTE_MAX(conf.dir24_8.num_tbl8,
866 			config.tbl8);
867 
868 		lpm = rte_lpm_create("test_lpm", -1, &lpm_conf);
869 		if (lpm == NULL) {
870 			printf("Can not alloc LPM, err %d\n", rte_errno);
871 			return -rte_errno;
872 		}
873 		for (k = config.print_fract, i = 0; k > 0; k--) {
874 			start = rte_rdtsc_precise();
875 			for (j = 0; j < (config.nb_routes - i) / k; j++) {
876 				ret = rte_lpm_add(lpm, rt[i + j].addr,
877 					rt[i + j].depth, rt[i + j].nh);
878 				if (ret != 0) {
879 					if (rt[i + j].depth == 0)
880 						print_depth_err();
881 					printf("Can not add a route to LPM, "
882 						"err %d\n", ret);
883 					return -ret;
884 				}
885 			}
886 			printf("AVG LPM add %"PRIu64"\n",
887 				(rte_rdtsc_precise() - start) / j);
888 			i += j;
889 		}
890 	}
891 
892 	acc = 0;
893 	for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
894 		start = rte_rdtsc_precise();
895 		ret = rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ);
896 		acc += rte_rdtsc_precise() - start;
897 		if (ret != 0) {
898 			printf("FIB lookup fails, err %d\n", ret);
899 			return -ret;
900 		}
901 	}
902 	printf("AVG FIB lookup %.1f\n", (double)acc / (double)i);
903 
904 	if (config.flags & CMP_FLAG) {
905 		acc = 0;
906 		for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
907 			start = rte_rdtsc_precise();
908 			ret = rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh,
909 				BURST_SZ);
910 			acc += rte_rdtsc_precise() - start;
911 			if (ret != 0) {
912 				printf("LPM lookup fails, err %d\n", ret);
913 				return -ret;
914 			}
915 		}
916 		printf("AVG LPM lookup %.1f\n", (double)acc / (double)i);
917 
918 		for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
919 			rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ);
920 			rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh, BURST_SZ);
921 			for (j = 0; j < BURST_SZ; j++) {
922 				struct rte_lpm_tbl_entry *tbl;
923 				tbl = (struct rte_lpm_tbl_entry *)&lpm_nh[j];
924 				if ((fib_nh[j] != tbl->next_hop) &&
925 						!((tbl->valid == 0) &&
926 						(fib_nh[j] == def_nh))) {
927 					printf("FAIL\n");
928 					return -1;
929 				}
930 			}
931 		}
932 		printf("FIB and LPM lookup returns same values\n");
933 	}
934 
935 	for (k = config.print_fract, i = 0; k > 0; k--) {
936 		start = rte_rdtsc_precise();
937 		for (j = 0; j < (config.nb_routes - i) / k; j++)
938 			rte_fib_delete(fib, rt[i + j].addr, rt[i + j].depth);
939 
940 		printf("AVG FIB delete %"PRIu64"\n",
941 			(rte_rdtsc_precise() - start) / j);
942 		i += j;
943 	}
944 
945 	if (config.flags & CMP_FLAG) {
946 		for (k = config.print_fract, i = 0; k > 0; k--) {
947 			start = rte_rdtsc_precise();
948 			for (j = 0; j < (config.nb_routes - i) / k; j++)
949 				rte_lpm_delete(lpm, rt[i + j].addr,
950 					rt[i + j].depth);
951 
952 			printf("AVG LPM delete %"PRIu64"\n",
953 				(rte_rdtsc_precise() - start) / j);
954 			i += j;
955 		}
956 	}
957 
958 	return 0;
959 }
960 
961 static int
962 dump_rt_6(struct rt_rule_6 *rt)
963 {
964 	FILE *f;
965 	uint32_t i;
966 
967 	f = fopen(config.routes_file_s, "w");
968 	if (f == NULL) {
969 		printf("Can not open file %s\n", config.routes_file_s);
970 		return -1;
971 	}
972 
973 	for (i = 0; i < config.nb_routes; i++) {
974 		fprintf(f, NIPQUAD6_FMT"/%d %"PRIu64"\n", NIPQUAD6(rt[i].addr),
975 			rt[i].depth, rt[i].nh);
976 
977 	}
978 	fclose(f);
979 	return 0;
980 }
981 
982 static int
983 run_v6(void)
984 {
985 	uint64_t start, acc;
986 	uint64_t def_nh = 0;
987 	struct rte_fib6 *fib;
988 	struct rte_fib6_conf conf = {0};
989 	struct rt_rule_6 *rt;
990 	uint32_t i, j, k;
991 	int ret = 0;
992 	struct rte_lpm6	*lpm = NULL;
993 	struct rte_lpm6_config lpm_conf;
994 	uint8_t *tbl6;
995 	uint64_t fib_nh[BURST_SZ];
996 	int32_t lpm_nh[BURST_SZ];
997 
998 	rt = (struct rt_rule_6 *)config.rt;
999 	tbl6 = config.lookup_tbl;
1000 
1001 	if (config.flags & DRY_RUN_FLAG) {
1002 		if (config.routes_file_s != NULL)
1003 			ret =  dump_rt_6(rt);
1004 		if (ret != 0)
1005 			return ret;
1006 		if (config.lookup_ips_file_s != NULL)
1007 			ret = dump_lookup(AF_INET6);
1008 		return ret;
1009 	}
1010 
1011 	conf.type = get_fib_type();
1012 	conf.default_nh = def_nh;
1013 	conf.max_routes = config.nb_routes * 2;
1014 	if (conf.type == RTE_FIB6_TRIE) {
1015 		conf.trie.nh_sz = __builtin_ctz(config.ent_sz);
1016 		conf.trie.num_tbl8 = RTE_MIN(config.tbl8,
1017 			get_max_nh(conf.trie.nh_sz));
1018 	}
1019 
1020 	fib = rte_fib6_create("test", -1, &conf);
1021 	if (fib == NULL) {
1022 		printf("Can not alloc FIB, err %d\n", rte_errno);
1023 		return -rte_errno;
1024 	}
1025 
1026 	for (k = config.print_fract, i = 0; k > 0; k--) {
1027 		start = rte_rdtsc_precise();
1028 		for (j = 0; j < (config.nb_routes - i) / k; j++) {
1029 			ret = rte_fib6_add(fib, rt[i + j].addr,
1030 				rt[i + j].depth, rt[i + j].nh);
1031 			if (unlikely(ret != 0)) {
1032 				printf("Can not add a route to FIB, err %d\n",
1033 					ret);
1034 				return -ret;
1035 			}
1036 		}
1037 		printf("AVG FIB add %"PRIu64"\n",
1038 			(rte_rdtsc_precise() - start) / j);
1039 		i += j;
1040 	}
1041 
1042 	if (config.flags & CMP_FLAG) {
1043 		lpm_conf.max_rules = config.nb_routes * 2;
1044 		lpm_conf.number_tbl8s = RTE_MAX(conf.trie.num_tbl8,
1045 			config.tbl8);
1046 
1047 		lpm = rte_lpm6_create("test_lpm", -1, &lpm_conf);
1048 		if (lpm == NULL) {
1049 			printf("Can not alloc LPM, err %d\n", rte_errno);
1050 			return -rte_errno;
1051 		}
1052 		for (k = config.print_fract, i = 0; k > 0; k--) {
1053 			start = rte_rdtsc_precise();
1054 			for (j = 0; j < (config.nb_routes - i) / k; j++) {
1055 				ret = rte_lpm6_add(lpm, rt[i + j].addr,
1056 					rt[i + j].depth, rt[i + j].nh);
1057 				if (ret != 0) {
1058 					if (rt[i + j].depth == 0)
1059 						print_depth_err();
1060 					printf("Can not add a route to LPM, "
1061 						"err %d\n", ret);
1062 					return -ret;
1063 				}
1064 			}
1065 			printf("AVG LPM add %"PRIu64"\n",
1066 				(rte_rdtsc_precise() - start) / j);
1067 			i += j;
1068 		}
1069 	}
1070 
1071 	acc = 0;
1072 	for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
1073 		start = rte_rdtsc_precise();
1074 		ret = rte_fib6_lookup_bulk(fib, (uint8_t (*)[16])(tbl6 + i*16),
1075 			fib_nh, BURST_SZ);
1076 		acc += rte_rdtsc_precise() - start;
1077 		if (ret != 0) {
1078 			printf("FIB lookup fails, err %d\n", ret);
1079 			return -ret;
1080 		}
1081 	}
1082 	printf("AVG FIB lookup %.1f\n", (double)acc / (double)i);
1083 
1084 	if (config.flags & CMP_FLAG) {
1085 		acc = 0;
1086 		for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
1087 			start = rte_rdtsc_precise();
1088 			ret = rte_lpm6_lookup_bulk_func(lpm,
1089 				(uint8_t (*)[16])(tbl6 + i*16),
1090 				lpm_nh, BURST_SZ);
1091 			acc += rte_rdtsc_precise() - start;
1092 			if (ret != 0) {
1093 				printf("LPM lookup fails, err %d\n", ret);
1094 				return -ret;
1095 			}
1096 		}
1097 		printf("AVG LPM lookup %.1f\n", (double)acc / (double)i);
1098 
1099 		for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
1100 			rte_fib6_lookup_bulk(fib,
1101 				(uint8_t (*)[16])(tbl6 + i*16),
1102 				fib_nh, BURST_SZ);
1103 			rte_lpm6_lookup_bulk_func(lpm,
1104 				(uint8_t (*)[16])(tbl6 + i*16),
1105 				lpm_nh, BURST_SZ);
1106 			for (j = 0; j < BURST_SZ; j++) {
1107 				if ((fib_nh[j] != (uint32_t)lpm_nh[j]) &&
1108 						!((lpm_nh[j] == -1) &&
1109 						(fib_nh[j] == def_nh))) {
1110 					printf("FAIL\n");
1111 					return -1;
1112 				}
1113 			}
1114 		}
1115 		printf("FIB and LPM lookup returns same values\n");
1116 	}
1117 
1118 	for (k = config.print_fract, i = 0; k > 0; k--) {
1119 		start = rte_rdtsc_precise();
1120 		for (j = 0; j < (config.nb_routes - i) / k; j++)
1121 			rte_fib6_delete(fib, rt[i + j].addr, rt[i + j].depth);
1122 
1123 		printf("AVG FIB delete %"PRIu64"\n",
1124 			(rte_rdtsc_precise() - start) / j);
1125 		i += j;
1126 	}
1127 
1128 	if (config.flags & CMP_FLAG) {
1129 		for (k = config.print_fract, i = 0; k > 0; k--) {
1130 			start = rte_rdtsc_precise();
1131 			for (j = 0; j < (config.nb_routes - i) / k; j++)
1132 				rte_lpm6_delete(lpm, rt[i + j].addr,
1133 					rt[i + j].depth);
1134 
1135 			printf("AVG LPM delete %"PRIu64"\n",
1136 				(rte_rdtsc_precise() - start) / j);
1137 			i += j;
1138 		}
1139 	}
1140 	return 0;
1141 }
1142 
1143 int
1144 main(int argc, char **argv)
1145 {
1146 	int ret, af, rt_ent_sz, lookup_ent_sz;
1147 	FILE *fr = NULL;
1148 	FILE *fl = NULL;
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 (fr == 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 (fl == 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