xref: /dpdk/app/test-fib/main.c (revision d19034ae63e95978c6017d18db1924f515f93cc7)
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 a, 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 (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 %"PRIu64"\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 %"PRIu64"\n",
862 			(rte_rdtsc_precise() - start) / j);
863 		i += j;
864 	}
865 
866 	if (config.flags & CMP_FLAG) {
867 		lpm_conf.max_rules = config.nb_routes * 2;
868 		lpm_conf.number_tbl8s = RTE_MAX(conf.dir24_8.num_tbl8,
869 			config.tbl8);
870 
871 		lpm = rte_lpm_create("test_lpm", -1, &lpm_conf);
872 		if (lpm == NULL) {
873 			printf("Can not alloc LPM, err %d\n", rte_errno);
874 			return -rte_errno;
875 		}
876 		for (k = config.print_fract, i = 0; k > 0; k--) {
877 			start = rte_rdtsc_precise();
878 			for (j = 0; j < (config.nb_routes - i) / k; j++) {
879 				ret = rte_lpm_add(lpm, rt[i + j].addr,
880 					rt[i + j].depth, rt[i + j].nh);
881 				if (ret != 0) {
882 					if (rt[i + j].depth == 0)
883 						print_depth_err();
884 					printf("Can not add a route to LPM, "
885 						"err %d\n", ret);
886 					return -ret;
887 				}
888 			}
889 			printf("AVG LPM add %"PRIu64"\n",
890 				(rte_rdtsc_precise() - start) / j);
891 			i += j;
892 		}
893 	}
894 
895 	acc = 0;
896 	for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
897 		start = rte_rdtsc_precise();
898 		ret = rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ);
899 		acc += rte_rdtsc_precise() - start;
900 		if (ret != 0) {
901 			printf("FIB lookup fails, err %d\n", ret);
902 			return -ret;
903 		}
904 	}
905 	printf("AVG FIB lookup %.1f\n", (double)acc / (double)i);
906 
907 	if (config.flags & CMP_FLAG) {
908 		acc = 0;
909 		for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
910 			start = rte_rdtsc_precise();
911 			ret = rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh,
912 				BURST_SZ);
913 			acc += rte_rdtsc_precise() - start;
914 			if (ret != 0) {
915 				printf("LPM lookup fails, err %d\n", ret);
916 				return -ret;
917 			}
918 		}
919 		printf("AVG LPM lookup %.1f\n", (double)acc / (double)i);
920 
921 		for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
922 			rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ);
923 			rte_lpm_lookup_bulk(lpm, tbl4 + i, lpm_nh, BURST_SZ);
924 			for (j = 0; j < BURST_SZ; j++) {
925 				struct rte_lpm_tbl_entry *tbl;
926 				tbl = (struct rte_lpm_tbl_entry *)&lpm_nh[j];
927 				if ((fib_nh[j] != tbl->next_hop) &&
928 						!((tbl->valid == 0) &&
929 						(fib_nh[j] == def_nh))) {
930 					printf("FAIL\n");
931 					return -1;
932 				}
933 			}
934 		}
935 		printf("FIB and LPM lookup returns same values\n");
936 	}
937 
938 	for (k = config.print_fract, i = 0; k > 0; k--) {
939 		start = rte_rdtsc_precise();
940 		for (j = 0; j < (config.nb_routes - i) / k; j++)
941 			rte_fib_delete(fib, rt[i + j].addr, rt[i + j].depth);
942 
943 		printf("AVG FIB delete %"PRIu64"\n",
944 			(rte_rdtsc_precise() - start) / j);
945 		i += j;
946 	}
947 
948 	if (config.flags & CMP_FLAG) {
949 		for (k = config.print_fract, i = 0; k > 0; k--) {
950 			start = rte_rdtsc_precise();
951 			for (j = 0; j < (config.nb_routes - i) / k; j++)
952 				rte_lpm_delete(lpm, rt[i + j].addr,
953 					rt[i + j].depth);
954 
955 			printf("AVG LPM delete %"PRIu64"\n",
956 				(rte_rdtsc_precise() - start) / j);
957 			i += j;
958 		}
959 	}
960 
961 	return 0;
962 }
963 
964 static int
965 dump_rt_6(struct rt_rule_6 *rt)
966 {
967 	FILE *f;
968 	uint32_t i;
969 
970 	f = fopen(config.routes_file_s, "w");
971 	if (f == NULL) {
972 		printf("Can not open file %s\n", config.routes_file_s);
973 		return -1;
974 	}
975 
976 	for (i = 0; i < config.nb_routes; i++) {
977 		fprintf(f, NIPQUAD6_FMT"/%d %"PRIu64"\n", NIPQUAD6(rt[i].addr),
978 			rt[i].depth, rt[i].nh);
979 
980 	}
981 	fclose(f);
982 	return 0;
983 }
984 
985 static int
986 run_v6(void)
987 {
988 	uint64_t start, acc;
989 	uint64_t def_nh = 0;
990 	struct rte_fib6 *fib;
991 	struct rte_fib6_conf conf = {0};
992 	struct rt_rule_6 *rt;
993 	uint32_t i, j, k;
994 	int ret = 0;
995 	struct rte_lpm6	*lpm = NULL;
996 	struct rte_lpm6_config lpm_conf;
997 	uint8_t *tbl6;
998 	uint64_t fib_nh[BURST_SZ];
999 	int32_t lpm_nh[BURST_SZ];
1000 
1001 	rt = (struct rt_rule_6 *)config.rt;
1002 	tbl6 = config.lookup_tbl;
1003 
1004 	if (config.flags & DRY_RUN_FLAG) {
1005 		if (config.routes_file_s != NULL)
1006 			ret =  dump_rt_6(rt);
1007 		if (ret != 0)
1008 			return ret;
1009 		if (config.lookup_ips_file_s != NULL)
1010 			ret = dump_lookup(AF_INET6);
1011 		return ret;
1012 	}
1013 
1014 	conf.type = get_fib_type();
1015 	conf.default_nh = def_nh;
1016 	conf.max_routes = config.nb_routes * 2;
1017 	if (conf.type == RTE_FIB6_TRIE) {
1018 		conf.trie.nh_sz = __builtin_ctz(config.ent_sz);
1019 		conf.trie.num_tbl8 = RTE_MIN(config.tbl8,
1020 			get_max_nh(conf.trie.nh_sz));
1021 	}
1022 
1023 	fib = rte_fib6_create("test", -1, &conf);
1024 	if (fib == NULL) {
1025 		printf("Can not alloc FIB, err %d\n", rte_errno);
1026 		return -rte_errno;
1027 	}
1028 
1029 	for (k = config.print_fract, i = 0; k > 0; k--) {
1030 		start = rte_rdtsc_precise();
1031 		for (j = 0; j < (config.nb_routes - i) / k; j++) {
1032 			ret = rte_fib6_add(fib, rt[i + j].addr,
1033 				rt[i + j].depth, rt[i + j].nh);
1034 			if (unlikely(ret != 0)) {
1035 				printf("Can not add a route to FIB, err %d\n",
1036 					ret);
1037 				return -ret;
1038 			}
1039 		}
1040 		printf("AVG FIB add %"PRIu64"\n",
1041 			(rte_rdtsc_precise() - start) / j);
1042 		i += j;
1043 	}
1044 
1045 	if (config.flags & CMP_FLAG) {
1046 		lpm_conf.max_rules = config.nb_routes * 2;
1047 		lpm_conf.number_tbl8s = RTE_MAX(conf.trie.num_tbl8,
1048 			config.tbl8);
1049 
1050 		lpm = rte_lpm6_create("test_lpm", -1, &lpm_conf);
1051 		if (lpm == NULL) {
1052 			printf("Can not alloc LPM, err %d\n", rte_errno);
1053 			return -rte_errno;
1054 		}
1055 		for (k = config.print_fract, i = 0; k > 0; k--) {
1056 			start = rte_rdtsc_precise();
1057 			for (j = 0; j < (config.nb_routes - i) / k; j++) {
1058 				ret = rte_lpm6_add(lpm, rt[i + j].addr,
1059 					rt[i + j].depth, rt[i + j].nh);
1060 				if (ret != 0) {
1061 					if (rt[i + j].depth == 0)
1062 						print_depth_err();
1063 					printf("Can not add a route to LPM, "
1064 						"err %d\n", ret);
1065 					return -ret;
1066 				}
1067 			}
1068 			printf("AVG LPM add %"PRIu64"\n",
1069 				(rte_rdtsc_precise() - start) / j);
1070 			i += j;
1071 		}
1072 	}
1073 
1074 	acc = 0;
1075 	for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
1076 		start = rte_rdtsc_precise();
1077 		ret = rte_fib6_lookup_bulk(fib, (uint8_t (*)[16])(tbl6 + i*16),
1078 			fib_nh, BURST_SZ);
1079 		acc += rte_rdtsc_precise() - start;
1080 		if (ret != 0) {
1081 			printf("FIB lookup fails, err %d\n", ret);
1082 			return -ret;
1083 		}
1084 	}
1085 	printf("AVG FIB lookup %.1f\n", (double)acc / (double)i);
1086 
1087 	if (config.flags & CMP_FLAG) {
1088 		acc = 0;
1089 		for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
1090 			start = rte_rdtsc_precise();
1091 			ret = rte_lpm6_lookup_bulk_func(lpm,
1092 				(uint8_t (*)[16])(tbl6 + i*16),
1093 				lpm_nh, BURST_SZ);
1094 			acc += rte_rdtsc_precise() - start;
1095 			if (ret != 0) {
1096 				printf("LPM lookup fails, err %d\n", ret);
1097 				return -ret;
1098 			}
1099 		}
1100 		printf("AVG LPM lookup %.1f\n", (double)acc / (double)i);
1101 
1102 		for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) {
1103 			rte_fib6_lookup_bulk(fib,
1104 				(uint8_t (*)[16])(tbl6 + i*16),
1105 				fib_nh, BURST_SZ);
1106 			rte_lpm6_lookup_bulk_func(lpm,
1107 				(uint8_t (*)[16])(tbl6 + i*16),
1108 				lpm_nh, BURST_SZ);
1109 			for (j = 0; j < BURST_SZ; j++) {
1110 				if ((fib_nh[j] != (uint32_t)lpm_nh[j]) &&
1111 						!((lpm_nh[j] == -1) &&
1112 						(fib_nh[j] == def_nh))) {
1113 					printf("FAIL\n");
1114 					return -1;
1115 				}
1116 			}
1117 		}
1118 		printf("FIB and LPM lookup returns same values\n");
1119 	}
1120 
1121 	for (k = config.print_fract, i = 0; k > 0; k--) {
1122 		start = rte_rdtsc_precise();
1123 		for (j = 0; j < (config.nb_routes - i) / k; j++)
1124 			rte_fib6_delete(fib, rt[i + j].addr, rt[i + j].depth);
1125 
1126 		printf("AVG FIB delete %"PRIu64"\n",
1127 			(rte_rdtsc_precise() - start) / j);
1128 		i += j;
1129 	}
1130 
1131 	if (config.flags & CMP_FLAG) {
1132 		for (k = config.print_fract, i = 0; k > 0; k--) {
1133 			start = rte_rdtsc_precise();
1134 			for (j = 0; j < (config.nb_routes - i) / k; j++)
1135 				rte_lpm6_delete(lpm, rt[i + j].addr,
1136 					rt[i + j].depth);
1137 
1138 			printf("AVG LPM delete %"PRIu64"\n",
1139 				(rte_rdtsc_precise() - start) / j);
1140 			i += j;
1141 		}
1142 	}
1143 	return 0;
1144 }
1145 
1146 int
1147 main(int argc, char **argv)
1148 {
1149 	int ret, af, rt_ent_sz, lookup_ent_sz;
1150 	FILE *fr = NULL;
1151 	FILE *fl = NULL;
1152 	uint8_t depth_lim;
1153 
1154 	ret = rte_eal_init(argc, argv);
1155 	if (ret < 0)
1156 		rte_panic("Cannot init EAL\n");
1157 
1158 	argc -= ret;
1159 	argv += ret;
1160 
1161 	config.prgname = argv[0];
1162 
1163 	parse_opts(argc, argv);
1164 
1165 	ret = check_config();
1166 	if (ret != 0)
1167 		rte_exit(-ret, "Bad configuration\n");
1168 
1169 	af = ((config.flags & IPV6_FLAG) == 0) ? AF_INET : AF_INET6;
1170 	depth_lim = (af == AF_INET) ? 32 : 128;
1171 	rt_ent_sz = (af == AF_INET) ? sizeof(struct rt_rule_4) :
1172 		sizeof(struct rt_rule_6);
1173 	lookup_ent_sz = (af == AF_INET) ? 4 : 16;
1174 
1175 	/* Count number of rules in file*/
1176 	if (config.routes_file != NULL) {
1177 		fr = fopen(config.routes_file, "r");
1178 		if (fr == NULL)
1179 			rte_exit(-errno, "Can not open file with routes %s\n",
1180 				config.routes_file);
1181 
1182 		config.nb_routes = 0;
1183 		while (fgets(line, sizeof(line), fr) != NULL)
1184 			config.nb_routes++;
1185 		rewind(fr);
1186 	}
1187 
1188 	/* Count number of ip's in file*/
1189 	if (config.lookup_ips_file != NULL) {
1190 		fl = fopen(config.lookup_ips_file, "r");
1191 		if (fl == NULL)
1192 			rte_exit(-errno, "Can not open file with ip's %s\n",
1193 				config.lookup_ips_file);
1194 
1195 		config.nb_lookup_ips = 0;
1196 		while (fgets(line, sizeof(line), fl) != NULL)
1197 			config.nb_lookup_ips++;
1198 		rewind(fl);
1199 	}
1200 
1201 	/* Alloc routes table*/
1202 	config.rt  = rte_malloc(NULL, rt_ent_sz * config.nb_routes, 0);
1203 	if (config.rt == NULL)
1204 		rte_exit(-ENOMEM, "Can not alloc rt\n");
1205 
1206 	/* Alloc table with ip's for lookup*/
1207 	config.lookup_tbl  = rte_malloc(NULL, lookup_ent_sz *
1208 		config.nb_lookup_ips, 0);
1209 	if (config.lookup_tbl == NULL)
1210 		rte_exit(-ENOMEM, "Can not alloc lookup table\n");
1211 
1212 	/* Fill routes table */
1213 	if (fr == NULL) {
1214 		if (distrib_string != NULL)
1215 			ret = parse_distrib(depth_lim, config.nb_routes);
1216 		else {
1217 			uint8_t rpd[129] = {0};
1218 			uint32_t nrpd[129] = {0};
1219 			ret = complete_distrib(depth_lim, config.nb_routes,
1220 				rpd, nrpd);
1221 		}
1222 		if (ret != 0)
1223 			rte_exit(-ret,
1224 				"Bad routes distribution configuration\n");
1225 		if (af == AF_INET) {
1226 			gen_random_rt_4(config.rt,
1227 				__builtin_ctz(config.ent_sz));
1228 			if (config.flags & SHUFFLE_FLAG)
1229 				shuffle_rt_4(config.rt, config.nb_routes);
1230 		} else {
1231 			gen_random_rt_6(config.rt,
1232 				__builtin_ctz(config.ent_sz));
1233 			if (config.flags & SHUFFLE_FLAG)
1234 				shuffle_rt_6(config.rt, config.nb_routes);
1235 		}
1236 	} else {
1237 		if (af == AF_INET)
1238 			ret = parse_rt_4(fr);
1239 		else
1240 			ret = parse_rt_6(fr);
1241 
1242 		if (ret != 0) {
1243 			rte_exit(-ret, "failed to parse routes file %s\n",
1244 				config.routes_file);
1245 		}
1246 	}
1247 
1248 	/* Fill lookup table with ip's*/
1249 	if (fl == NULL)
1250 		gen_rnd_lookup_tbl(af);
1251 	else {
1252 		ret = parse_lookup(fl, af);
1253 		if (ret != 0)
1254 			rte_exit(-ret, "failed to parse lookup file\n");
1255 	}
1256 
1257 	print_config();
1258 
1259 	if (af == AF_INET)
1260 		ret = run_v4();
1261 	else
1262 		ret = run_v6();
1263 
1264 	return ret;
1265 }
1266