1
2
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
38static 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
77enum {
78 CB_RULE_SPI,
79 CB_RULE_DIP,
80 CB_RULE_SIP,
81 CB_RULE_LEN,
82 CB_RULE_NUM,
83};
84
85static char line[LINE_MAX];
86struct rule {
87 union rte_ipsec_sad_key tuple;
88 int rule_type;
89};
90
91static struct rule *rules_tbl;
92static struct rule *tuples_tbl;
93
94static int
95parse_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
113static void
114print_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
133static void
134print_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
155static int
156get_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
170static int
171parse_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
225static uint64_t
226get_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
234static void
235get_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
308static void
309tbl_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
356static void
357parse_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
434static void
435print_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
445static void
446print_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
456static void
457print_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
510static int
511lookup(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
547static void
548add_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
581static void
582del_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
615int
616main(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