test mbuf attach
[dpdk.git] / app / test-sad / main.c
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;
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_MASTER);
661
662         lookup(sad);
663         if (config.parallel_lookup)
664                 RTE_LCORE_FOREACH_SLAVE(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 }