xref: /dpdk/app/test-sad/main.c (revision 68a03efeed657e6e05f281479b33b51102797e15)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2019 Intel Corporation
3  */
4 
5 #include <rte_string_fns.h>
6 #include <rte_ipsec_sad.h>
7 #include <getopt.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 
15 #include <rte_cycles.h>
16 #include <rte_errno.h>
17 #include <rte_ip.h>
18 #include <rte_random.h>
19 #include <rte_malloc.h>
20 
21 #define	PRINT_USAGE_START	"%s [EAL options] --\n"
22 
23 #define GET_CB_FIELD(in, fd, base, lim, dlm)	do {		\
24 	unsigned long val;					\
25 	char *end_fld;						\
26 	errno = 0;						\
27 	val = strtoul((in), &end_fld, (base));			\
28 	if (errno != 0 || end_fld[0] != (dlm) || val > (lim))	\
29 		return -EINVAL;					\
30 	(fd) = (typeof(fd))val;					\
31 	(in) = end_fld + 1;					\
32 } while (0)
33 
34 #define	DEF_RULE_NUM		0x10000
35 #define	DEF_TUPLES_NUM	0x100000
36 #define BURST_SZ_MAX	64
37 
38 static struct {
39 	const char	*prgname;
40 	const char	*rules_file;
41 	const char	*tuples_file;
42 	uint32_t	nb_rules;
43 	uint32_t	nb_tuples;
44 	uint32_t	nb_rules_32;
45 	uint32_t	nb_rules_64;
46 	uint32_t	nb_rules_96;
47 	uint32_t	nb_tuples_rnd;
48 	uint32_t	burst_sz;
49 	uint8_t		fract_32;
50 	uint8_t		fract_64;
51 	uint8_t		fract_96;
52 	uint8_t		fract_rnd_tuples;
53 	int		ipv6;
54 	int		verbose;
55 	int		parallel_lookup;
56 	int		concurrent_rw;
57 } config = {
58 	.rules_file = NULL,
59 	.tuples_file = NULL,
60 	.nb_rules = DEF_RULE_NUM,
61 	.nb_tuples = DEF_TUPLES_NUM,
62 	.nb_rules_32 = 0,
63 	.nb_rules_64 = 0,
64 	.nb_rules_96 = 0,
65 	.nb_tuples_rnd = 0,
66 	.burst_sz = BURST_SZ_MAX,
67 	.fract_32 = 90,
68 	.fract_64 = 9,
69 	.fract_96 = 1,
70 	.fract_rnd_tuples = 0,
71 	.ipv6 = 0,
72 	.verbose = 0,
73 	.parallel_lookup = 0,
74 	.concurrent_rw = 0
75 };
76 
77 enum {
78 	CB_RULE_SPI,
79 	CB_RULE_DIP,
80 	CB_RULE_SIP,
81 	CB_RULE_LEN,
82 	CB_RULE_NUM,
83 };
84 
85 static char line[LINE_MAX];
86 struct rule {
87 	union rte_ipsec_sad_key	tuple;
88 	int rule_type;
89 };
90 
91 static struct rule *rules_tbl;
92 static struct rule *tuples_tbl;
93 
94 static int
95 parse_distrib(const char *in)
96 {
97 	int a, b, c;
98 
99 	GET_CB_FIELD(in, a, 0, UINT8_MAX, '/');
100 	GET_CB_FIELD(in, b, 0, UINT8_MAX, '/');
101 	GET_CB_FIELD(in, c, 0, UINT8_MAX, 0);
102 
103 	if ((a + b + c) != 100)
104 		return -EINVAL;
105 
106 	config.fract_32 = a;
107 	config.fract_64 = b;
108 	config.fract_96 = c;
109 
110 	return 0;
111 }
112 
113 static void
114 print_config(void)
115 {
116 	fprintf(stdout,
117 		"Rules total: %u\n"
118 		"Configured rules distribution SPI/SPI_DIP/SIP_DIP_SIP:"
119 		"%u/%u/%u\n"
120 		"SPI only rules: %u\n"
121 		"SPI_DIP  rules: %u\n"
122 		"SPI_DIP_SIP rules: %u\n"
123 		"Lookup tuples: %u\n"
124 		"Lookup burst size %u\n"
125 		"Configured fraction of random tuples: %u\n"
126 		"Random lookup tuples: %u\n",
127 		config.nb_rules, config.fract_32, config.fract_64,
128 		config.fract_96, config.nb_rules_32, config.nb_rules_64,
129 		config.nb_rules_96, config.nb_tuples, config.burst_sz,
130 		config.fract_rnd_tuples, config.nb_tuples_rnd);
131 }
132 
133 static void
134 print_usage(void)
135 {
136 	fprintf(stdout,
137 		PRINT_USAGE_START
138 		"[-f <rules file>]\n"
139 		"[-t <tuples file for lookup>]\n"
140 		"[-n <rules number (if -f is not specified)>]\n"
141 		"[-l <lookup tuples number (if -t is not specified)>]\n"
142 		"[-6 <ipv6 tests>]\n"
143 		"[-d <\"/\" separated rules length distribution"
144 		"(if -f is not specified)>]\n"
145 		"[-r <random tuples fraction to lookup"
146 		"(if -t is not specified)>]\n"
147 		"[-b <lookup burst size: 1-64 >]\n"
148 		"[-v <verbose, print results on lookup>]\n"
149 		"[-p <parallel lookup on all available cores>]\n"
150 		"[-c <init sad supporting read/write concurrency>]\n",
151 		config.prgname);
152 
153 }
154 
155 static int
156 get_str_num(FILE *f, int num)
157 {
158 	int n_lines = 0;
159 
160 	if (f != NULL) {
161 		while (fgets(line, sizeof(line), f) != NULL)
162 			n_lines++;
163 		rewind(f);
164 	} else {
165 		n_lines = num;
166 	}
167 	return n_lines;
168 }
169 
170 static int
171 parse_file(FILE *f, struct rule *tbl, int rule_tbl)
172 {
173 	int ret, i, j = 0;
174 	char *s, *sp, *in[CB_RULE_NUM];
175 	static const char *dlm = " \t\n";
176 	int string_tok_nb = RTE_DIM(in);
177 
178 	string_tok_nb -= (rule_tbl == 0) ? 1 : 0;
179 	while (fgets(line, sizeof(line), f) != NULL) {
180 		s = line;
181 		for (i = 0; i != string_tok_nb; i++) {
182 			in[i] = strtok_r(s, dlm, &sp);
183 			if (in[i] == NULL)
184 				return -EINVAL;
185 			s = NULL;
186 		}
187 		GET_CB_FIELD(in[CB_RULE_SPI], tbl[j].tuple.v4.spi, 0,
188 				UINT32_MAX, 0);
189 
190 		if (config.ipv6)
191 			ret = inet_pton(AF_INET6, in[CB_RULE_DIP],
192 				&tbl[j].tuple.v6.dip);
193 		else
194 			ret = inet_pton(AF_INET, in[CB_RULE_DIP],
195 				&tbl[j].tuple.v4.dip);
196 		if (ret != 1)
197 			return -EINVAL;
198 		if (config.ipv6)
199 			ret = inet_pton(AF_INET6, in[CB_RULE_SIP],
200 				&tbl[j].tuple.v6.sip);
201 		else
202 			ret = inet_pton(AF_INET, in[CB_RULE_SIP],
203 				&tbl[j].tuple.v4.sip);
204 		if (ret != 1)
205 			return -EINVAL;
206 		if ((rule_tbl) && (in[CB_RULE_LEN] != NULL)) {
207 			if (strcmp(in[CB_RULE_LEN], "SPI_DIP_SIP") == 0) {
208 				tbl[j].rule_type = RTE_IPSEC_SAD_SPI_DIP_SIP;
209 				config.nb_rules_96++;
210 			} else if (strcmp(in[CB_RULE_LEN], "SPI_DIP") == 0) {
211 				tbl[j].rule_type = RTE_IPSEC_SAD_SPI_DIP;
212 				config.nb_rules_64++;
213 			} else if (strcmp(in[CB_RULE_LEN], "SPI") == 0) {
214 				tbl[j].rule_type = RTE_IPSEC_SAD_SPI_ONLY;
215 				config.nb_rules_32++;
216 			} else {
217 				return -EINVAL;
218 			}
219 		}
220 		j++;
221 	}
222 	return 0;
223 }
224 
225 static uint64_t
226 get_rnd_rng(uint64_t l, uint64_t u)
227 {
228 	if (l == u)
229 		return l;
230 	else
231 		return (rte_rand() % (u - l) + l);
232 }
233 
234 static void
235 get_random_rules(struct rule *tbl, uint32_t nb_rules, int rule_tbl)
236 {
237 	unsigned int i, j, rnd;
238 	int rule_type;
239 	double edge = 0;
240 	double step;
241 
242 	step = (double)UINT32_MAX / nb_rules;
243 	for (i = 0; i < nb_rules; i++, edge += step) {
244 		rnd = rte_rand() % 100;
245 		if (rule_tbl) {
246 			tbl[i].tuple.v4.spi = get_rnd_rng((uint64_t)edge,
247 						(uint64_t)(edge + step));
248 			if (config.ipv6) {
249 				for (j = 0; j < 16; j++) {
250 					tbl[i].tuple.v6.dip[j] = rte_rand();
251 					tbl[i].tuple.v6.sip[j] = rte_rand();
252 				}
253 			} else {
254 				tbl[i].tuple.v4.dip = rte_rand();
255 				tbl[i].tuple.v4.sip = rte_rand();
256 			}
257 			if (rnd >= (100UL - config.fract_32)) {
258 				rule_type = RTE_IPSEC_SAD_SPI_ONLY;
259 				config.nb_rules_32++;
260 			} else if (rnd >= (100UL - (config.fract_32 +
261 					config.fract_64))) {
262 				rule_type = RTE_IPSEC_SAD_SPI_DIP;
263 				config.nb_rules_64++;
264 			} else {
265 				rule_type = RTE_IPSEC_SAD_SPI_DIP_SIP;
266 				config.nb_rules_96++;
267 			}
268 			tbl[i].rule_type = rule_type;
269 		} else {
270 			if (rnd >= 100UL - config.fract_rnd_tuples) {
271 				tbl[i].tuple.v4.spi =
272 					get_rnd_rng((uint64_t)edge,
273 					(uint64_t)(edge + step));
274 				if (config.ipv6) {
275 					for (j = 0; j < 16; j++) {
276 						tbl[i].tuple.v6.dip[j] =
277 								rte_rand();
278 						tbl[i].tuple.v6.sip[j] =
279 								rte_rand();
280 					}
281 				} else {
282 					tbl[i].tuple.v4.dip = rte_rand();
283 					tbl[i].tuple.v4.sip = rte_rand();
284 				}
285 				config.nb_tuples_rnd++;
286 			} else {
287 				tbl[i].tuple.v4.spi = rules_tbl[i %
288 					config.nb_rules].tuple.v4.spi;
289 				if (config.ipv6) {
290 					int r_idx = i % config.nb_rules;
291 					memcpy(tbl[i].tuple.v6.dip,
292 						rules_tbl[r_idx].tuple.v6.dip,
293 						sizeof(tbl[i].tuple.v6.dip));
294 					memcpy(tbl[i].tuple.v6.sip,
295 						rules_tbl[r_idx].tuple.v6.sip,
296 						sizeof(tbl[i].tuple.v6.sip));
297 				} else {
298 					tbl[i].tuple.v4.dip = rules_tbl[i %
299 						config.nb_rules].tuple.v4.dip;
300 					tbl[i].tuple.v4.sip = rules_tbl[i %
301 						config.nb_rules].tuple.v4.sip;
302 				}
303 			}
304 		}
305 	}
306 }
307 
308 static void
309 tbl_init(struct rule **tbl, uint32_t *n_entries,
310 	const char *file_name, int rule_tbl)
311 {
312 	FILE *f = NULL;
313 	int ret;
314 	const char *rules = "rules";
315 	const char *tuples = "tuples";
316 
317 	if (file_name != NULL) {
318 		f = fopen(file_name, "r");
319 		if (f == NULL)
320 			rte_exit(-EINVAL, "failed to open file: %s\n",
321 				file_name);
322 	}
323 
324 	printf("init %s table...", (rule_tbl) ? rules : tuples);
325 	*n_entries = get_str_num(f, *n_entries);
326 	printf("%d entries\n", *n_entries);
327 	*tbl = rte_zmalloc(NULL, sizeof(struct rule) * *n_entries,
328 		RTE_CACHE_LINE_SIZE);
329 	if (*tbl == NULL)
330 		rte_exit(-ENOMEM, "failed to allocate tbl\n");
331 
332 	if (f != NULL) {
333 		printf("parse file %s\n", file_name);
334 		ret = parse_file(f, *tbl, rule_tbl);
335 		if (ret != 0)
336 			rte_exit(-EINVAL, "failed to parse file %s\n"
337 				"rules file must be: "
338 				"<uint32_t: spi> <space> "
339 				"<ip_addr: dip> <space> "
340 				"<ip_addr: sip> <space> "
341 				"<string: SPI|SPI_DIP|SIP_DIP_SIP>\n"
342 				"tuples file must be: "
343 				"<uint32_t: spi> <space> "
344 				"<ip_addr: dip> <space> "
345 				"<ip_addr: sip>\n",
346 				file_name);
347 	} else {
348 		printf("generate random values in %s table\n",
349 			(rule_tbl) ? rules : tuples);
350 		get_random_rules(*tbl, *n_entries, rule_tbl);
351 	}
352 	if (f != NULL)
353 		fclose(f);
354 }
355 
356 static void
357 parse_opts(int argc, char **argv)
358 {
359 	int opt, ret;
360 	char *endptr;
361 
362 	while ((opt = getopt(argc, argv, "f:t:n:d:l:r:6b:vpc")) != -1) {
363 		switch (opt) {
364 		case 'f':
365 			config.rules_file = optarg;
366 			break;
367 		case 't':
368 			config.tuples_file = optarg;
369 			break;
370 		case 'n':
371 			errno = 0;
372 			config.nb_rules = strtoul(optarg, &endptr, 10);
373 			if ((errno != 0) || (config.nb_rules == 0) ||
374 					(endptr[0] != 0)) {
375 				print_usage();
376 				rte_exit(-EINVAL, "Invalid option -n\n");
377 			}
378 			break;
379 		case 'd':
380 			ret = parse_distrib(optarg);
381 			if (ret != 0) {
382 				print_usage();
383 				rte_exit(-EINVAL, "Invalid option -d\n");
384 			}
385 			break;
386 		case 'b':
387 			errno = 0;
388 			config.burst_sz = strtoul(optarg, &endptr, 10);
389 			if ((errno != 0) || (config.burst_sz == 0) ||
390 					(config.burst_sz > BURST_SZ_MAX) ||
391 					(endptr[0] != 0)) {
392 				print_usage();
393 				rte_exit(-EINVAL, "Invalid option -b\n");
394 			}
395 			break;
396 		case 'l':
397 			errno = 0;
398 			config.nb_tuples = strtoul(optarg, &endptr, 10);
399 			if ((errno != 0) || (config.nb_tuples == 0) ||
400 					(endptr[0] != 0)) {
401 				print_usage();
402 				rte_exit(-EINVAL, "Invalid option -l\n");
403 			}
404 			break;
405 		case 'r':
406 			errno = 0;
407 			config.fract_rnd_tuples = strtoul(optarg, &endptr, 10);
408 			if ((errno != 0) || (config.fract_rnd_tuples == 0) ||
409 					(config.fract_rnd_tuples >= 100) ||
410 					(endptr[0] != 0)) {
411 				print_usage();
412 				rte_exit(-EINVAL, "Invalid option -r\n");
413 			}
414 			break;
415 		case '6':
416 			config.ipv6 = 1;
417 			break;
418 		case 'v':
419 			config.verbose = 1;
420 			break;
421 		case 'p':
422 			config.parallel_lookup = 1;
423 			break;
424 		case 'c':
425 			config.concurrent_rw = 1;
426 			break;
427 		default:
428 			print_usage();
429 			rte_exit(-EINVAL, "Invalid options\n");
430 		}
431 	}
432 }
433 
434 static void
435 print_addr(int af, const void *addr)
436 {
437 	char str[INET6_ADDRSTRLEN];
438 	const char *ret;
439 
440 	ret = inet_ntop(af, addr, str, sizeof(str));
441 	if (ret != NULL)
442 		printf("%s", str);
443 }
444 
445 static void
446 print_tuple(int af, uint32_t spi, const void *dip, const void *sip)
447 {
448 
449 	printf("<SPI: %u DIP: ", spi);
450 	print_addr(af, dip);
451 	printf(" SIP: ");
452 	print_addr(af, sip);
453 	printf(">");
454 }
455 
456 static void
457 print_result(const union rte_ipsec_sad_key *key, void *res)
458 {
459 	struct rule *rule = res;
460 	const struct rte_ipsec_sadv4_key *v4;
461 	const struct rte_ipsec_sadv6_key *v6;
462 	const char *spi_only = "SPI_ONLY";
463 	const char *spi_dip = "SPI_DIP";
464 	const char *spi_dip_sip = "SPI_DIP_SIP";
465 	const char *rule_type;
466 	const void *dip, *sip;
467 	uint32_t spi;
468 	int af;
469 
470 	af = (config.ipv6) ? AF_INET6 : AF_INET;
471 	v4 = &key->v4;
472 	v6 = &key->v6;
473 	spi = (config.ipv6 == 0) ? v4->spi : v6->spi;
474 	dip = (config.ipv6 == 0) ? &v4->dip : (const void *)v6->dip;
475 	sip = (config.ipv6 == 0) ? &v4->sip : (const void *)v6->sip;
476 
477 	if (res == NULL) {
478 		printf("TUPLE: ");
479 		print_tuple(af, spi, dip, sip);
480 		printf(" not found\n");
481 		return;
482 	}
483 
484 	switch (rule->rule_type) {
485 	case RTE_IPSEC_SAD_SPI_ONLY:
486 		rule_type = spi_only;
487 		break;
488 	case RTE_IPSEC_SAD_SPI_DIP:
489 		rule_type = spi_dip;
490 		break;
491 	case RTE_IPSEC_SAD_SPI_DIP_SIP:
492 		rule_type = spi_dip_sip;
493 		break;
494 	default:
495 		return;
496 	}
497 
498 	print_tuple(af, spi, dip, sip);
499 	v4 = &rule->tuple.v4;
500 	v6 = &rule->tuple.v6;
501 	spi = (config.ipv6 == 0) ? v4->spi : v6->spi;
502 	dip = (config.ipv6 == 0) ? &v4->dip : (const void *)v6->dip;
503 	sip = (config.ipv6 == 0) ? &v4->sip : (const void *)v6->sip;
504 	printf("\n\tpoints to RULE ID %zu ",
505 		RTE_PTR_DIFF(res, rules_tbl)/sizeof(struct rule));
506 	print_tuple(af, spi, dip, sip);
507 	printf(" %s\n", rule_type);
508 }
509 
510 static int
511 lookup(void *arg)
512 {
513 	int ret;
514 	unsigned int i, j;
515 	const union rte_ipsec_sad_key *keys[BURST_SZ_MAX];
516 	void *vals[BURST_SZ_MAX];
517 	uint64_t start, acc = 0;
518 	uint32_t burst_sz;
519 	struct rte_ipsec_sad *sad = arg;
520 
521 	if (config.nb_tuples == 0)
522 		return 0;
523 
524 	burst_sz = RTE_MIN(config.burst_sz, config.nb_tuples);
525 	for (i = 0; i < config.nb_tuples; i += burst_sz) {
526 		for (j = 0; j < burst_sz; j++)
527 			keys[j] = (union rte_ipsec_sad_key *)
528 				(&tuples_tbl[i + j].tuple);
529 		start = rte_rdtsc_precise();
530 		ret = rte_ipsec_sad_lookup(sad, keys, vals, burst_sz);
531 		acc += rte_rdtsc_precise() - start;
532 		if (ret < 0)
533 			rte_exit(-EINVAL, "Lookup failed\n");
534 		if (config.verbose) {
535 			for (j = 0; j < burst_sz; j++)
536 				print_result(keys[j], vals[j]);
537 		}
538 	}
539 	acc = (acc == 0) ? UINT64_MAX : acc;
540 	printf("Average lookup cycles %.2Lf, lookups/sec: %.2Lf\n",
541 		(long double)acc / config.nb_tuples,
542 		(long double)config.nb_tuples * rte_get_tsc_hz() / acc);
543 
544 	return 0;
545 }
546 
547 static void
548 add_rules(struct rte_ipsec_sad *sad, uint32_t fract)
549 {
550 	int32_t ret;
551 	uint32_t i, j, f, fn, n;
552 	uint64_t start, tm[fract + 1];
553 	uint32_t nm[fract + 1];
554 
555 	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
556 
557 	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
558 
559 		fn = n + f;
560 		fn = fn > config.nb_rules ? config.nb_rules : fn;
561 
562 		start = rte_rdtsc_precise();
563 		for (i = n; i != fn; i++) {
564 			ret = rte_ipsec_sad_add(sad,
565 				&rules_tbl[i].tuple,
566 				rules_tbl[i].rule_type, &rules_tbl[i]);
567 			if (ret != 0)
568 				rte_exit(ret, "%s failed @ %u-th rule\n",
569 					__func__, i);
570 		}
571 		tm[j] = rte_rdtsc_precise() - start;
572 		nm[j] = fn - n;
573 	}
574 
575 	for (i = 0; i != j; i++)
576 		printf("ADD %u rules, %.2Lf cycles/rule, %.2Lf ADD/sec\n",
577 			nm[i], (long double)tm[i] / nm[i],
578 			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
579 }
580 
581 static void
582 del_rules(struct rte_ipsec_sad *sad, uint32_t fract)
583 {
584 	int32_t ret;
585 	uint32_t i, j, f, fn, n;
586 	uint64_t start, tm[fract + 1];
587 	uint32_t nm[fract + 1];
588 
589 	f = (config.nb_rules > fract) ? config.nb_rules / fract : 1;
590 
591 	for (n = 0, j = 0; n != config.nb_rules; n = fn, j++) {
592 
593 		fn = n + f;
594 		fn = fn > config.nb_rules ? config.nb_rules : fn;
595 
596 		start = rte_rdtsc_precise();
597 		for (i = n; i != fn; i++) {
598 			ret = rte_ipsec_sad_del(sad,
599 				&rules_tbl[i].tuple,
600 				rules_tbl[i].rule_type);
601 			if (ret != 0 && ret != -ENOENT)
602 				rte_exit(ret, "%s failed @ %u-th rule\n",
603 					__func__, i);
604 		}
605 		tm[j] = rte_rdtsc_precise() - start;
606 		nm[j] = fn - n;
607 	}
608 
609 	for (i = 0; i != j; i++)
610 		printf("DEL %u rules, %.2Lf cycles/rule, %.2Lf DEL/sec\n",
611 			nm[i], (long double)tm[i] / nm[i],
612 			(long double)nm[i] * rte_get_tsc_hz() / tm[i]);
613 }
614 
615 int
616 main(int argc, char **argv)
617 {
618 	int ret;
619 	struct rte_ipsec_sad *sad;
620 	struct rte_ipsec_sad_conf conf = {0};
621 	unsigned int lcore_id;
622 
623 	ret = rte_eal_init(argc, argv);
624 	if (ret < 0)
625 		rte_panic("Cannot init EAL\n");
626 
627 	argc -= ret;
628 	argv += ret;
629 
630 	config.prgname = argv[0];
631 
632 	parse_opts(argc, argv);
633 	tbl_init(&rules_tbl, &config.nb_rules, config.rules_file, 1);
634 	tbl_init(&tuples_tbl, &config.nb_tuples, config.tuples_file, 0);
635 	if (config.rules_file != NULL) {
636 		config.fract_32 = (100 * config.nb_rules_32) / config.nb_rules;
637 		config.fract_64 = (100 * config.nb_rules_64) / config.nb_rules;
638 		config.fract_96 = (100 * config.nb_rules_96) / config.nb_rules;
639 	}
640 	if (config.tuples_file != NULL) {
641 		config.fract_rnd_tuples = 0;
642 		config.nb_tuples_rnd = 0;
643 	}
644 	conf.socket_id = -1;
645 	conf.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = config.nb_rules_32 * 5 / 4;
646 	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = config.nb_rules_64 * 5 / 4;
647 	conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = config.nb_rules_96 * 5 / 4;
648 	if (config.ipv6)
649 		conf.flags |= RTE_IPSEC_SAD_FLAG_IPV6;
650 	if (config.concurrent_rw)
651 		conf.flags |= RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY;
652 	sad = rte_ipsec_sad_create("test", &conf);
653 	if (sad == NULL)
654 		rte_exit(-rte_errno, "can not allocate SAD table\n");
655 
656 	print_config();
657 
658 	add_rules(sad, 10);
659 	if (config.parallel_lookup)
660 		rte_eal_mp_remote_launch(lookup, sad, SKIP_MAIN);
661 
662 	lookup(sad);
663 	if (config.parallel_lookup)
664 		RTE_LCORE_FOREACH_WORKER(lcore_id)
665 			if (rte_eal_wait_lcore(lcore_id) < 0)
666 				return -1;
667 
668 	del_rules(sad, 10);
669 
670 	return 0;
671 }
672