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