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