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