1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <rte_string_fns.h> 6 #include <rte_acl.h> 7 #include <getopt.h> 8 #include <string.h> 9 10 #include <rte_cycles.h> 11 #include <rte_per_lcore.h> 12 #include <rte_lcore.h> 13 #include <rte_ip.h> 14 15 #define PRINT_USAGE_START "%s [EAL options] --\n" 16 17 #define RTE_LOGTYPE_TESTACL RTE_LOGTYPE_USER1 18 19 #define APP_NAME "TESTACL" 20 21 #define GET_CB_FIELD(in, fd, base, lim, dlm) do { \ 22 unsigned long val; \ 23 char *end_fld; \ 24 errno = 0; \ 25 val = strtoul((in), &end_fld, (base)); \ 26 if (errno != 0 || end_fld[0] != (dlm) || val > (lim)) \ 27 return -EINVAL; \ 28 (fd) = (typeof(fd))val; \ 29 (in) = end_fld + 1; \ 30 } while (0) 31 32 #define OPT_RULE_FILE "rulesf" 33 #define OPT_TRACE_FILE "tracef" 34 #define OPT_RULE_NUM "rulenum" 35 #define OPT_TRACE_NUM "tracenum" 36 #define OPT_TRACE_STEP "tracestep" 37 #define OPT_SEARCH_ALG "alg" 38 #define OPT_BLD_CATEGORIES "bldcat" 39 #define OPT_RUN_CATEGORIES "runcat" 40 #define OPT_MAX_SIZE "maxsize" 41 #define OPT_ITER_NUM "iter" 42 #define OPT_VERBOSE "verbose" 43 #define OPT_IPV6 "ipv6" 44 45 #define TRACE_DEFAULT_NUM 0x10000 46 #define TRACE_STEP_MAX 0x1000 47 #define TRACE_STEP_DEF 0x100 48 49 #define RULE_NUM 0x10000 50 51 #define COMMENT_LEAD_CHAR '#' 52 53 enum { 54 DUMP_NONE, 55 DUMP_SEARCH, 56 DUMP_PKT, 57 DUMP_MAX 58 }; 59 60 struct acl_alg { 61 const char *name; 62 enum rte_acl_classify_alg alg; 63 }; 64 65 static const struct acl_alg acl_alg[] = { 66 { 67 .name = "scalar", 68 .alg = RTE_ACL_CLASSIFY_SCALAR, 69 }, 70 { 71 .name = "sse", 72 .alg = RTE_ACL_CLASSIFY_SSE, 73 }, 74 { 75 .name = "avx2", 76 .alg = RTE_ACL_CLASSIFY_AVX2, 77 }, 78 { 79 .name = "neon", 80 .alg = RTE_ACL_CLASSIFY_NEON, 81 }, 82 { 83 .name = "altivec", 84 .alg = RTE_ACL_CLASSIFY_ALTIVEC, 85 }, 86 { 87 .name = "avx512x16", 88 .alg = RTE_ACL_CLASSIFY_AVX512X16, 89 }, 90 { 91 .name = "avx512x32", 92 .alg = RTE_ACL_CLASSIFY_AVX512X32, 93 }, 94 }; 95 96 static struct { 97 const char *prgname; 98 const char *rule_file; 99 const char *trace_file; 100 size_t max_size; 101 uint32_t bld_categories; 102 uint32_t run_categories; 103 uint32_t nb_rules; 104 uint32_t nb_traces; 105 uint32_t trace_step; 106 uint32_t trace_sz; 107 uint32_t iter_num; 108 uint32_t verbose; 109 uint32_t ipv6; 110 struct acl_alg alg; 111 uint32_t used_traces; 112 void *traces; 113 struct rte_acl_ctx *acx; 114 } config = { 115 .bld_categories = 3, 116 .run_categories = 1, 117 .nb_rules = RULE_NUM, 118 .nb_traces = TRACE_DEFAULT_NUM, 119 .trace_step = TRACE_STEP_DEF, 120 .iter_num = 1, 121 .verbose = DUMP_MAX, 122 .alg = { 123 .name = "default", 124 .alg = RTE_ACL_CLASSIFY_DEFAULT, 125 }, 126 .ipv6 = 0 127 }; 128 129 static struct rte_acl_param prm = { 130 .name = APP_NAME, 131 .socket_id = SOCKET_ID_ANY, 132 }; 133 134 /* 135 * Rule and trace formats definitions. 136 */ 137 138 struct ipv4_5tuple { 139 uint8_t proto; 140 uint32_t ip_src; 141 uint32_t ip_dst; 142 uint16_t port_src; 143 uint16_t port_dst; 144 }; 145 146 enum { 147 PROTO_FIELD_IPV4, 148 SRC_FIELD_IPV4, 149 DST_FIELD_IPV4, 150 SRCP_FIELD_IPV4, 151 DSTP_FIELD_IPV4, 152 NUM_FIELDS_IPV4 153 }; 154 155 /* 156 * That effectively defines order of IPV4VLAN classifications: 157 * - PROTO 158 * - VLAN (TAG and DOMAIN) 159 * - SRC IP ADDRESS 160 * - DST IP ADDRESS 161 * - PORTS (SRC and DST) 162 */ 163 enum { 164 RTE_ACL_IPV4VLAN_PROTO, 165 RTE_ACL_IPV4VLAN_VLAN, 166 RTE_ACL_IPV4VLAN_SRC, 167 RTE_ACL_IPV4VLAN_DST, 168 RTE_ACL_IPV4VLAN_PORTS, 169 RTE_ACL_IPV4VLAN_NUM 170 }; 171 172 struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = { 173 { 174 .type = RTE_ACL_FIELD_TYPE_BITMASK, 175 .size = sizeof(uint8_t), 176 .field_index = PROTO_FIELD_IPV4, 177 .input_index = RTE_ACL_IPV4VLAN_PROTO, 178 .offset = offsetof(struct ipv4_5tuple, proto), 179 }, 180 { 181 .type = RTE_ACL_FIELD_TYPE_MASK, 182 .size = sizeof(uint32_t), 183 .field_index = SRC_FIELD_IPV4, 184 .input_index = RTE_ACL_IPV4VLAN_SRC, 185 .offset = offsetof(struct ipv4_5tuple, ip_src), 186 }, 187 { 188 .type = RTE_ACL_FIELD_TYPE_MASK, 189 .size = sizeof(uint32_t), 190 .field_index = DST_FIELD_IPV4, 191 .input_index = RTE_ACL_IPV4VLAN_DST, 192 .offset = offsetof(struct ipv4_5tuple, ip_dst), 193 }, 194 { 195 .type = RTE_ACL_FIELD_TYPE_RANGE, 196 .size = sizeof(uint16_t), 197 .field_index = SRCP_FIELD_IPV4, 198 .input_index = RTE_ACL_IPV4VLAN_PORTS, 199 .offset = offsetof(struct ipv4_5tuple, port_src), 200 }, 201 { 202 .type = RTE_ACL_FIELD_TYPE_RANGE, 203 .size = sizeof(uint16_t), 204 .field_index = DSTP_FIELD_IPV4, 205 .input_index = RTE_ACL_IPV4VLAN_PORTS, 206 .offset = offsetof(struct ipv4_5tuple, port_dst), 207 }, 208 }; 209 210 #define IPV6_ADDR_LEN 16 211 #define IPV6_ADDR_U16 (IPV6_ADDR_LEN / sizeof(uint16_t)) 212 #define IPV6_ADDR_U32 (IPV6_ADDR_LEN / sizeof(uint32_t)) 213 214 struct ipv6_5tuple { 215 uint8_t proto; 216 uint32_t ip_src[IPV6_ADDR_U32]; 217 uint32_t ip_dst[IPV6_ADDR_U32]; 218 uint16_t port_src; 219 uint16_t port_dst; 220 }; 221 222 enum { 223 PROTO_FIELD_IPV6, 224 SRC1_FIELD_IPV6, 225 SRC2_FIELD_IPV6, 226 SRC3_FIELD_IPV6, 227 SRC4_FIELD_IPV6, 228 DST1_FIELD_IPV6, 229 DST2_FIELD_IPV6, 230 DST3_FIELD_IPV6, 231 DST4_FIELD_IPV6, 232 SRCP_FIELD_IPV6, 233 DSTP_FIELD_IPV6, 234 NUM_FIELDS_IPV6 235 }; 236 237 struct rte_acl_field_def ipv6_defs[NUM_FIELDS_IPV6] = { 238 { 239 .type = RTE_ACL_FIELD_TYPE_BITMASK, 240 .size = sizeof(uint8_t), 241 .field_index = PROTO_FIELD_IPV6, 242 .input_index = PROTO_FIELD_IPV6, 243 .offset = offsetof(struct ipv6_5tuple, proto), 244 }, 245 { 246 .type = RTE_ACL_FIELD_TYPE_MASK, 247 .size = sizeof(uint32_t), 248 .field_index = SRC1_FIELD_IPV6, 249 .input_index = SRC1_FIELD_IPV6, 250 .offset = offsetof(struct ipv6_5tuple, ip_src[0]), 251 }, 252 { 253 .type = RTE_ACL_FIELD_TYPE_MASK, 254 .size = sizeof(uint32_t), 255 .field_index = SRC2_FIELD_IPV6, 256 .input_index = SRC2_FIELD_IPV6, 257 .offset = offsetof(struct ipv6_5tuple, ip_src[1]), 258 }, 259 { 260 .type = RTE_ACL_FIELD_TYPE_MASK, 261 .size = sizeof(uint32_t), 262 .field_index = SRC3_FIELD_IPV6, 263 .input_index = SRC3_FIELD_IPV6, 264 .offset = offsetof(struct ipv6_5tuple, ip_src[2]), 265 }, 266 { 267 .type = RTE_ACL_FIELD_TYPE_MASK, 268 .size = sizeof(uint32_t), 269 .field_index = SRC4_FIELD_IPV6, 270 .input_index = SRC4_FIELD_IPV6, 271 .offset = offsetof(struct ipv6_5tuple, ip_src[3]), 272 }, 273 { 274 .type = RTE_ACL_FIELD_TYPE_MASK, 275 .size = sizeof(uint32_t), 276 .field_index = DST1_FIELD_IPV6, 277 .input_index = DST1_FIELD_IPV6, 278 .offset = offsetof(struct ipv6_5tuple, ip_dst[0]), 279 }, 280 { 281 .type = RTE_ACL_FIELD_TYPE_MASK, 282 .size = sizeof(uint32_t), 283 .field_index = DST2_FIELD_IPV6, 284 .input_index = DST2_FIELD_IPV6, 285 .offset = offsetof(struct ipv6_5tuple, ip_dst[1]), 286 }, 287 { 288 .type = RTE_ACL_FIELD_TYPE_MASK, 289 .size = sizeof(uint32_t), 290 .field_index = DST3_FIELD_IPV6, 291 .input_index = DST3_FIELD_IPV6, 292 .offset = offsetof(struct ipv6_5tuple, ip_dst[2]), 293 }, 294 { 295 .type = RTE_ACL_FIELD_TYPE_MASK, 296 .size = sizeof(uint32_t), 297 .field_index = DST4_FIELD_IPV6, 298 .input_index = DST4_FIELD_IPV6, 299 .offset = offsetof(struct ipv6_5tuple, ip_dst[3]), 300 }, 301 { 302 .type = RTE_ACL_FIELD_TYPE_RANGE, 303 .size = sizeof(uint16_t), 304 .field_index = SRCP_FIELD_IPV6, 305 .input_index = SRCP_FIELD_IPV6, 306 .offset = offsetof(struct ipv6_5tuple, port_src), 307 }, 308 { 309 .type = RTE_ACL_FIELD_TYPE_RANGE, 310 .size = sizeof(uint16_t), 311 .field_index = DSTP_FIELD_IPV6, 312 .input_index = SRCP_FIELD_IPV6, 313 .offset = offsetof(struct ipv6_5tuple, port_dst), 314 }, 315 }; 316 317 318 enum { 319 CB_FLD_SRC_ADDR, 320 CB_FLD_DST_ADDR, 321 CB_FLD_SRC_PORT_LOW, 322 CB_FLD_SRC_PORT_DLM, 323 CB_FLD_SRC_PORT_HIGH, 324 CB_FLD_DST_PORT_LOW, 325 CB_FLD_DST_PORT_DLM, 326 CB_FLD_DST_PORT_HIGH, 327 CB_FLD_PROTO, 328 CB_FLD_NUM, 329 }; 330 331 enum { 332 CB_TRC_SRC_ADDR, 333 CB_TRC_DST_ADDR, 334 CB_TRC_SRC_PORT, 335 CB_TRC_DST_PORT, 336 CB_TRC_PROTO, 337 CB_TRC_NUM, 338 }; 339 340 RTE_ACL_RULE_DEF(acl_rule, RTE_ACL_MAX_FIELDS); 341 342 static const char cb_port_delim[] = ":"; 343 344 static char line[LINE_MAX]; 345 346 #define dump_verbose(lvl, fh, fmt, args...) do { \ 347 if ((lvl) <= (int32_t)config.verbose) \ 348 fprintf(fh, fmt, ##args); \ 349 } while (0) 350 351 352 /* 353 * Parse ClassBench input trace (test vectors and expected results) file. 354 * Expected format: 355 * <src_ipv4_addr> <space> <dst_ipv4_addr> <space> \ 356 * <src_port> <space> <dst_port> <space> <proto> 357 */ 358 static int 359 parse_cb_ipv4_trace(char *str, struct ipv4_5tuple *v) 360 { 361 int i; 362 char *s, *sp, *in[CB_TRC_NUM]; 363 static const char *dlm = " \t\n"; 364 365 s = str; 366 for (i = 0; i != RTE_DIM(in); i++) { 367 in[i] = strtok_r(s, dlm, &sp); 368 if (in[i] == NULL) 369 return -EINVAL; 370 s = NULL; 371 } 372 373 GET_CB_FIELD(in[CB_TRC_SRC_ADDR], v->ip_src, 0, UINT32_MAX, 0); 374 GET_CB_FIELD(in[CB_TRC_DST_ADDR], v->ip_dst, 0, UINT32_MAX, 0); 375 GET_CB_FIELD(in[CB_TRC_SRC_PORT], v->port_src, 0, UINT16_MAX, 0); 376 GET_CB_FIELD(in[CB_TRC_DST_PORT], v->port_dst, 0, UINT16_MAX, 0); 377 GET_CB_FIELD(in[CB_TRC_PROTO], v->proto, 0, UINT8_MAX, 0); 378 379 /* convert to network byte order. */ 380 v->ip_src = rte_cpu_to_be_32(v->ip_src); 381 v->ip_dst = rte_cpu_to_be_32(v->ip_dst); 382 v->port_src = rte_cpu_to_be_16(v->port_src); 383 v->port_dst = rte_cpu_to_be_16(v->port_dst); 384 385 return 0; 386 } 387 388 /* 389 * Parses IPV6 address, exepcts the following format: 390 * XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX (where X - is a hexedecimal digit). 391 */ 392 static int 393 parse_ipv6_addr(const char *in, const char **end, uint32_t v[IPV6_ADDR_U32], 394 char dlm) 395 { 396 uint32_t addr[IPV6_ADDR_U16]; 397 398 GET_CB_FIELD(in, addr[0], 16, UINT16_MAX, ':'); 399 GET_CB_FIELD(in, addr[1], 16, UINT16_MAX, ':'); 400 GET_CB_FIELD(in, addr[2], 16, UINT16_MAX, ':'); 401 GET_CB_FIELD(in, addr[3], 16, UINT16_MAX, ':'); 402 GET_CB_FIELD(in, addr[4], 16, UINT16_MAX, ':'); 403 GET_CB_FIELD(in, addr[5], 16, UINT16_MAX, ':'); 404 GET_CB_FIELD(in, addr[6], 16, UINT16_MAX, ':'); 405 GET_CB_FIELD(in, addr[7], 16, UINT16_MAX, dlm); 406 407 *end = in; 408 409 v[0] = (addr[0] << 16) + addr[1]; 410 v[1] = (addr[2] << 16) + addr[3]; 411 v[2] = (addr[4] << 16) + addr[5]; 412 v[3] = (addr[6] << 16) + addr[7]; 413 414 return 0; 415 } 416 417 static int 418 parse_cb_ipv6_addr_trace(const char *in, uint32_t v[IPV6_ADDR_U32]) 419 { 420 int32_t rc; 421 const char *end; 422 423 rc = parse_ipv6_addr(in, &end, v, 0); 424 if (rc != 0) 425 return rc; 426 427 v[0] = rte_cpu_to_be_32(v[0]); 428 v[1] = rte_cpu_to_be_32(v[1]); 429 v[2] = rte_cpu_to_be_32(v[2]); 430 v[3] = rte_cpu_to_be_32(v[3]); 431 432 return 0; 433 } 434 435 /* 436 * Parse ClassBench input trace (test vectors and expected results) file. 437 * Expected format: 438 * <src_ipv6_addr> <space> <dst_ipv6_addr> <space> \ 439 * <src_port> <space> <dst_port> <space> <proto> 440 */ 441 static int 442 parse_cb_ipv6_trace(char *str, struct ipv6_5tuple *v) 443 { 444 int32_t i, rc; 445 char *s, *sp, *in[CB_TRC_NUM]; 446 static const char *dlm = " \t\n"; 447 448 s = str; 449 for (i = 0; i != RTE_DIM(in); i++) { 450 in[i] = strtok_r(s, dlm, &sp); 451 if (in[i] == NULL) 452 return -EINVAL; 453 s = NULL; 454 } 455 456 /* get ip6 src address. */ 457 rc = parse_cb_ipv6_addr_trace(in[CB_TRC_SRC_ADDR], v->ip_src); 458 if (rc != 0) 459 return rc; 460 461 /* get ip6 dst address. */ 462 rc = parse_cb_ipv6_addr_trace(in[CB_TRC_DST_ADDR], v->ip_dst); 463 if (rc != 0) 464 return rc; 465 466 GET_CB_FIELD(in[CB_TRC_SRC_PORT], v->port_src, 0, UINT16_MAX, 0); 467 GET_CB_FIELD(in[CB_TRC_DST_PORT], v->port_dst, 0, UINT16_MAX, 0); 468 GET_CB_FIELD(in[CB_TRC_PROTO], v->proto, 0, UINT8_MAX, 0); 469 470 /* convert to network byte order. */ 471 v->port_src = rte_cpu_to_be_16(v->port_src); 472 v->port_dst = rte_cpu_to_be_16(v->port_dst); 473 474 return 0; 475 } 476 477 /* Bypass comment and empty lines */ 478 static int 479 skip_line(const char *buf) 480 { 481 uint32_t i; 482 483 for (i = 0; isspace(buf[i]) != 0; i++) 484 ; 485 486 if (buf[i] == 0 || buf[i] == COMMENT_LEAD_CHAR) 487 return 1; 488 489 return 0; 490 } 491 492 static void 493 tracef_init(void) 494 { 495 static const char name[] = APP_NAME; 496 FILE *f; 497 size_t sz; 498 uint32_t i, k, n; 499 struct ipv4_5tuple *v; 500 struct ipv6_5tuple *w; 501 502 sz = config.nb_traces * (config.ipv6 ? sizeof(*w) : sizeof(*v)); 503 config.traces = rte_zmalloc_socket(name, sz, RTE_CACHE_LINE_SIZE, 504 SOCKET_ID_ANY); 505 if (config.traces == NULL) 506 rte_exit(EXIT_FAILURE, "Cannot allocate %zu bytes for " 507 "requested %u number of trace records\n", 508 sz, config.nb_traces); 509 510 f = fopen(config.trace_file, "r"); 511 if (f == NULL) 512 rte_exit(-EINVAL, "failed to open file: %s\n", 513 config.trace_file); 514 515 v = config.traces; 516 w = config.traces; 517 k = 0; 518 n = 0; 519 for (i = 0; n != config.nb_traces; i++) { 520 521 if (fgets(line, sizeof(line), f) == NULL) 522 break; 523 524 if (skip_line(line) != 0) { 525 k++; 526 continue; 527 } 528 529 n = i - k; 530 531 if (config.ipv6) { 532 if (parse_cb_ipv6_trace(line, w + n) != 0) 533 rte_exit(EXIT_FAILURE, 534 "%s: failed to parse ipv6 trace " 535 "record at line %u\n", 536 config.trace_file, i + 1); 537 } else { 538 if (parse_cb_ipv4_trace(line, v + n) != 0) 539 rte_exit(EXIT_FAILURE, 540 "%s: failed to parse ipv4 trace " 541 "record at line %u\n", 542 config.trace_file, i + 1); 543 } 544 } 545 546 config.used_traces = i - k; 547 fclose(f); 548 } 549 550 static int 551 parse_ipv6_net(const char *in, struct rte_acl_field field[4]) 552 { 553 int32_t rc; 554 const char *mp; 555 uint32_t i, m, v[4]; 556 const uint32_t nbu32 = sizeof(uint32_t) * CHAR_BIT; 557 558 /* get address. */ 559 rc = parse_ipv6_addr(in, &mp, v, '/'); 560 if (rc != 0) 561 return rc; 562 563 /* get mask. */ 564 GET_CB_FIELD(mp, m, 0, CHAR_BIT * sizeof(v), 0); 565 566 /* put all together. */ 567 for (i = 0; i != RTE_DIM(v); i++) { 568 if (m >= (i + 1) * nbu32) 569 field[i].mask_range.u32 = nbu32; 570 else 571 field[i].mask_range.u32 = m > (i * nbu32) ? 572 m - (i * 32) : 0; 573 574 field[i].value.u32 = v[i]; 575 } 576 577 return 0; 578 } 579 580 581 static int 582 parse_cb_ipv6_rule(char *str, struct acl_rule *v) 583 { 584 int i, rc; 585 char *s, *sp, *in[CB_FLD_NUM]; 586 static const char *dlm = " \t\n"; 587 588 /* 589 * Skip leading '@' 590 */ 591 if (strchr(str, '@') != str) 592 return -EINVAL; 593 594 s = str + 1; 595 596 for (i = 0; i != RTE_DIM(in); i++) { 597 in[i] = strtok_r(s, dlm, &sp); 598 if (in[i] == NULL) 599 return -EINVAL; 600 s = NULL; 601 } 602 603 rc = parse_ipv6_net(in[CB_FLD_SRC_ADDR], v->field + SRC1_FIELD_IPV6); 604 if (rc != 0) { 605 RTE_LOG(ERR, TESTACL, 606 "failed to read source address/mask: %s\n", 607 in[CB_FLD_SRC_ADDR]); 608 return rc; 609 } 610 611 rc = parse_ipv6_net(in[CB_FLD_DST_ADDR], v->field + DST1_FIELD_IPV6); 612 if (rc != 0) { 613 RTE_LOG(ERR, TESTACL, 614 "failed to read destination address/mask: %s\n", 615 in[CB_FLD_DST_ADDR]); 616 return rc; 617 } 618 619 /* source port. */ 620 GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW], 621 v->field[SRCP_FIELD_IPV6].value.u16, 622 0, UINT16_MAX, 0); 623 GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH], 624 v->field[SRCP_FIELD_IPV6].mask_range.u16, 625 0, UINT16_MAX, 0); 626 627 if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim, 628 sizeof(cb_port_delim)) != 0) 629 return -EINVAL; 630 631 /* destination port. */ 632 GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW], 633 v->field[DSTP_FIELD_IPV6].value.u16, 634 0, UINT16_MAX, 0); 635 GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH], 636 v->field[DSTP_FIELD_IPV6].mask_range.u16, 637 0, UINT16_MAX, 0); 638 639 if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim, 640 sizeof(cb_port_delim)) != 0) 641 return -EINVAL; 642 643 GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].value.u8, 644 0, UINT8_MAX, '/'); 645 GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV6].mask_range.u8, 646 0, UINT8_MAX, 0); 647 648 return 0; 649 } 650 651 static int 652 parse_ipv4_net(const char *in, uint32_t *addr, uint32_t *mask_len) 653 { 654 uint8_t a, b, c, d, m; 655 656 GET_CB_FIELD(in, a, 0, UINT8_MAX, '.'); 657 GET_CB_FIELD(in, b, 0, UINT8_MAX, '.'); 658 GET_CB_FIELD(in, c, 0, UINT8_MAX, '.'); 659 GET_CB_FIELD(in, d, 0, UINT8_MAX, '/'); 660 GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0); 661 662 addr[0] = RTE_IPV4(a, b, c, d); 663 mask_len[0] = m; 664 665 return 0; 666 } 667 /* 668 * Parse ClassBench rules file. 669 * Expected format: 670 * '@'<src_ipv4_addr>'/'<masklen> <space> \ 671 * <dst_ipv4_addr>'/'<masklen> <space> \ 672 * <src_port_low> <space> ":" <src_port_high> <space> \ 673 * <dst_port_low> <space> ":" <dst_port_high> <space> \ 674 * <proto>'/'<mask> 675 */ 676 static int 677 parse_cb_ipv4_rule(char *str, struct acl_rule *v) 678 { 679 int i, rc; 680 char *s, *sp, *in[CB_FLD_NUM]; 681 static const char *dlm = " \t\n"; 682 683 /* 684 * Skip leading '@' 685 */ 686 if (strchr(str, '@') != str) 687 return -EINVAL; 688 689 s = str + 1; 690 691 for (i = 0; i != RTE_DIM(in); i++) { 692 in[i] = strtok_r(s, dlm, &sp); 693 if (in[i] == NULL) 694 return -EINVAL; 695 s = NULL; 696 } 697 698 rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR], 699 &v->field[SRC_FIELD_IPV4].value.u32, 700 &v->field[SRC_FIELD_IPV4].mask_range.u32); 701 if (rc != 0) { 702 RTE_LOG(ERR, TESTACL, 703 "failed to read source address/mask: %s\n", 704 in[CB_FLD_SRC_ADDR]); 705 return rc; 706 } 707 708 rc = parse_ipv4_net(in[CB_FLD_DST_ADDR], 709 &v->field[DST_FIELD_IPV4].value.u32, 710 &v->field[DST_FIELD_IPV4].mask_range.u32); 711 if (rc != 0) { 712 RTE_LOG(ERR, TESTACL, 713 "failed to read destination address/mask: %s\n", 714 in[CB_FLD_DST_ADDR]); 715 return rc; 716 } 717 718 /* source port. */ 719 GET_CB_FIELD(in[CB_FLD_SRC_PORT_LOW], 720 v->field[SRCP_FIELD_IPV4].value.u16, 721 0, UINT16_MAX, 0); 722 GET_CB_FIELD(in[CB_FLD_SRC_PORT_HIGH], 723 v->field[SRCP_FIELD_IPV4].mask_range.u16, 724 0, UINT16_MAX, 0); 725 726 if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim, 727 sizeof(cb_port_delim)) != 0) 728 return -EINVAL; 729 730 /* destination port. */ 731 GET_CB_FIELD(in[CB_FLD_DST_PORT_LOW], 732 v->field[DSTP_FIELD_IPV4].value.u16, 733 0, UINT16_MAX, 0); 734 GET_CB_FIELD(in[CB_FLD_DST_PORT_HIGH], 735 v->field[DSTP_FIELD_IPV4].mask_range.u16, 736 0, UINT16_MAX, 0); 737 738 if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim, 739 sizeof(cb_port_delim)) != 0) 740 return -EINVAL; 741 742 GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].value.u8, 743 0, UINT8_MAX, '/'); 744 GET_CB_FIELD(in[CB_FLD_PROTO], v->field[PROTO_FIELD_IPV4].mask_range.u8, 745 0, UINT8_MAX, 0); 746 747 return 0; 748 } 749 750 typedef int (*parse_5tuple)(char *text, struct acl_rule *rule); 751 752 static int 753 add_cb_rules(FILE *f, struct rte_acl_ctx *ctx) 754 { 755 int rc; 756 uint32_t i, k, n; 757 struct acl_rule v; 758 parse_5tuple parser; 759 760 memset(&v, 0, sizeof(v)); 761 parser = (config.ipv6 != 0) ? parse_cb_ipv6_rule : parse_cb_ipv4_rule; 762 763 k = 0; 764 for (i = 1; fgets(line, sizeof(line), f) != NULL; i++) { 765 766 if (skip_line(line) != 0) { 767 k++; 768 continue; 769 } 770 771 n = i - k; 772 rc = parser(line, &v); 773 if (rc != 0) { 774 RTE_LOG(ERR, TESTACL, "line %u: parse_cb_ipv4vlan_rule" 775 " failed, error code: %d (%s)\n", 776 i, rc, strerror(-rc)); 777 return rc; 778 } 779 780 v.data.category_mask = RTE_LEN2MASK(RTE_ACL_MAX_CATEGORIES, 781 typeof(v.data.category_mask)); 782 v.data.priority = RTE_ACL_MAX_PRIORITY - n; 783 v.data.userdata = n; 784 785 rc = rte_acl_add_rules(ctx, (struct rte_acl_rule *)&v, 1); 786 if (rc != 0) { 787 RTE_LOG(ERR, TESTACL, "line %u: failed to add rules " 788 "into ACL context, error code: %d (%s)\n", 789 i, rc, strerror(-rc)); 790 return rc; 791 } 792 } 793 794 return 0; 795 } 796 797 static void 798 acx_init(void) 799 { 800 int ret; 801 FILE *f; 802 struct rte_acl_config cfg; 803 804 memset(&cfg, 0, sizeof(cfg)); 805 806 /* setup ACL build config. */ 807 if (config.ipv6) { 808 cfg.num_fields = RTE_DIM(ipv6_defs); 809 memcpy(&cfg.defs, ipv6_defs, sizeof(ipv6_defs)); 810 } else { 811 cfg.num_fields = RTE_DIM(ipv4_defs); 812 memcpy(&cfg.defs, ipv4_defs, sizeof(ipv4_defs)); 813 } 814 cfg.num_categories = config.bld_categories; 815 cfg.max_size = config.max_size; 816 817 /* setup ACL creation parameters. */ 818 prm.rule_size = RTE_ACL_RULE_SZ(cfg.num_fields); 819 prm.max_rule_num = config.nb_rules; 820 821 config.acx = rte_acl_create(&prm); 822 if (config.acx == NULL) 823 rte_exit(rte_errno, "failed to create ACL context\n"); 824 825 /* set default classify method for this context. */ 826 if (config.alg.alg != RTE_ACL_CLASSIFY_DEFAULT) { 827 ret = rte_acl_set_ctx_classify(config.acx, config.alg.alg); 828 if (ret != 0) 829 rte_exit(ret, "failed to setup %s method " 830 "for ACL context\n", config.alg.name); 831 } 832 833 /* add ACL rules. */ 834 f = fopen(config.rule_file, "r"); 835 if (f == NULL) 836 rte_exit(-EINVAL, "failed to open file %s\n", 837 config.rule_file); 838 839 ret = add_cb_rules(f, config.acx); 840 if (ret != 0) 841 rte_exit(ret, "failed to add rules into ACL context\n"); 842 843 fclose(f); 844 845 /* perform build. */ 846 ret = rte_acl_build(config.acx, &cfg); 847 848 dump_verbose(DUMP_NONE, stdout, 849 "rte_acl_build(%u) finished with %d\n", 850 config.bld_categories, ret); 851 852 rte_acl_dump(config.acx); 853 854 if (ret != 0) 855 rte_exit(ret, "failed to build search context\n"); 856 } 857 858 static uint32_t 859 search_ip5tuples_once(uint32_t categories, uint32_t step, const char *alg) 860 { 861 int ret; 862 uint32_t i, j, k, n, r; 863 const uint8_t *data[step], *v; 864 uint32_t results[step * categories]; 865 866 v = config.traces; 867 for (i = 0; i != config.used_traces; i += n) { 868 869 n = RTE_MIN(step, config.used_traces - i); 870 871 for (j = 0; j != n; j++) { 872 data[j] = v; 873 v += config.trace_sz; 874 } 875 876 ret = rte_acl_classify(config.acx, data, results, 877 n, categories); 878 879 if (ret != 0) 880 rte_exit(ret, "classify for ipv%c_5tuples returns %d\n", 881 config.ipv6 ? '6' : '4', ret); 882 883 for (r = 0, j = 0; j != n; j++) { 884 for (k = 0; k != categories; k++, r++) { 885 dump_verbose(DUMP_PKT, stdout, 886 "ipv%c_5tuple: %u, category: %u, " 887 "result: %u\n", 888 config.ipv6 ? '6' : '4', 889 i + j + 1, k, results[r] - 1); 890 } 891 892 } 893 } 894 895 dump_verbose(DUMP_SEARCH, stdout, 896 "%s(%u, %u, %s) returns %u\n", __func__, 897 categories, step, alg, i); 898 return i; 899 } 900 901 static int 902 search_ip5tuples(__rte_unused void *arg) 903 { 904 uint64_t pkt, start, tm; 905 uint32_t i, lcore; 906 long double st; 907 908 lcore = rte_lcore_id(); 909 start = rte_rdtsc_precise(); 910 pkt = 0; 911 912 for (i = 0; i != config.iter_num; i++) { 913 pkt += search_ip5tuples_once(config.run_categories, 914 config.trace_step, config.alg.name); 915 } 916 917 tm = rte_rdtsc_precise() - start; 918 919 st = (long double)tm / rte_get_timer_hz(); 920 dump_verbose(DUMP_NONE, stdout, 921 "%s @lcore %u: %" PRIu32 " iterations, %" PRIu64 " pkts, %" 922 PRIu32 " categories, %" PRIu64 " cycles (%.2Lf sec), " 923 "%.2Lf cycles/pkt, %.2Lf pkt/sec\n", 924 __func__, lcore, i, pkt, 925 config.run_categories, tm, st, 926 (pkt == 0) ? 0 : (long double)tm / pkt, pkt / st); 927 928 return 0; 929 } 930 931 static unsigned long 932 get_ulong_opt(const char *opt, const char *name, size_t min, size_t max) 933 { 934 unsigned long val; 935 char *end; 936 937 errno = 0; 938 val = strtoul(opt, &end, 0); 939 if (errno != 0 || end[0] != 0 || val > max || val < min) 940 rte_exit(-EINVAL, "invalid value: \"%s\" for option: %s\n", 941 opt, name); 942 return val; 943 } 944 945 static void 946 get_alg_opt(const char *opt, const char *name) 947 { 948 uint32_t i; 949 950 for (i = 0; i != RTE_DIM(acl_alg); i++) { 951 if (strcmp(opt, acl_alg[i].name) == 0) { 952 config.alg = acl_alg[i]; 953 return; 954 } 955 } 956 957 rte_exit(-EINVAL, "invalid value: \"%s\" for option: %s\n", 958 opt, name); 959 } 960 961 static void 962 print_usage(const char *prgname) 963 { 964 uint32_t i, n, rc; 965 char buf[PATH_MAX]; 966 967 n = 0; 968 buf[0] = 0; 969 970 for (i = 0; i < RTE_DIM(acl_alg) - 1; i++) { 971 rc = snprintf(buf + n, sizeof(buf) - n, "%s|", 972 acl_alg[i].name); 973 if (rc > sizeof(buf) - n) 974 break; 975 n += rc; 976 } 977 978 strlcpy(buf + n, acl_alg[i].name, sizeof(buf) - n); 979 980 fprintf(stdout, 981 PRINT_USAGE_START 982 "--" OPT_RULE_FILE "=<rules set file>\n" 983 "[--" OPT_TRACE_FILE "=<input traces file>]\n" 984 "[--" OPT_RULE_NUM 985 "=<maximum number of rules for ACL context>]\n" 986 "[--" OPT_TRACE_NUM 987 "=<number of traces to read binary file in>]\n" 988 "[--" OPT_TRACE_STEP 989 "=<number of traces to classify per one call>]\n" 990 "[--" OPT_BLD_CATEGORIES 991 "=<number of categories to build with>]\n" 992 "[--" OPT_RUN_CATEGORIES 993 "=<number of categories to run with> " 994 "should be either 1 or multiple of %zu, " 995 "but not greater then %u]\n" 996 "[--" OPT_MAX_SIZE 997 "=<size limit (in bytes) for runtime ACL strucutures> " 998 "leave 0 for default behaviour]\n" 999 "[--" OPT_ITER_NUM "=<number of iterations to perform>]\n" 1000 "[--" OPT_VERBOSE "=<verbose level>]\n" 1001 "[--" OPT_SEARCH_ALG "=%s]\n" 1002 "[--" OPT_IPV6 "=<IPv6 rules and trace files>]\n", 1003 prgname, RTE_ACL_RESULTS_MULTIPLIER, 1004 (uint32_t)RTE_ACL_MAX_CATEGORIES, 1005 buf); 1006 } 1007 1008 static void 1009 dump_config(FILE *f) 1010 { 1011 fprintf(f, "%s:\n", __func__); 1012 fprintf(f, "%s:%s\n", OPT_RULE_FILE, config.rule_file); 1013 fprintf(f, "%s:%s\n", OPT_TRACE_FILE, config.trace_file); 1014 fprintf(f, "%s:%u\n", OPT_RULE_NUM, config.nb_rules); 1015 fprintf(f, "%s:%u\n", OPT_TRACE_NUM, config.nb_traces); 1016 fprintf(f, "%s:%u\n", OPT_TRACE_STEP, config.trace_step); 1017 fprintf(f, "%s:%u\n", OPT_BLD_CATEGORIES, config.bld_categories); 1018 fprintf(f, "%s:%u\n", OPT_RUN_CATEGORIES, config.run_categories); 1019 fprintf(f, "%s:%zu\n", OPT_MAX_SIZE, config.max_size); 1020 fprintf(f, "%s:%u\n", OPT_ITER_NUM, config.iter_num); 1021 fprintf(f, "%s:%u\n", OPT_VERBOSE, config.verbose); 1022 fprintf(f, "%s:%u(%s)\n", OPT_SEARCH_ALG, config.alg.alg, 1023 config.alg.name); 1024 fprintf(f, "%s:%u\n", OPT_IPV6, config.ipv6); 1025 } 1026 1027 static void 1028 check_config(void) 1029 { 1030 if (config.rule_file == NULL) { 1031 print_usage(config.prgname); 1032 rte_exit(-EINVAL, "mandatory option %s is not specified\n", 1033 OPT_RULE_FILE); 1034 } 1035 } 1036 1037 1038 static void 1039 get_input_opts(int argc, char **argv) 1040 { 1041 static struct option lgopts[] = { 1042 {OPT_RULE_FILE, 1, 0, 0}, 1043 {OPT_TRACE_FILE, 1, 0, 0}, 1044 {OPT_TRACE_NUM, 1, 0, 0}, 1045 {OPT_RULE_NUM, 1, 0, 0}, 1046 {OPT_MAX_SIZE, 1, 0, 0}, 1047 {OPT_TRACE_STEP, 1, 0, 0}, 1048 {OPT_BLD_CATEGORIES, 1, 0, 0}, 1049 {OPT_RUN_CATEGORIES, 1, 0, 0}, 1050 {OPT_ITER_NUM, 1, 0, 0}, 1051 {OPT_VERBOSE, 1, 0, 0}, 1052 {OPT_SEARCH_ALG, 1, 0, 0}, 1053 {OPT_IPV6, 0, 0, 0}, 1054 {NULL, 0, 0, 0} 1055 }; 1056 1057 int opt, opt_idx; 1058 1059 while ((opt = getopt_long(argc, argv, "", lgopts, &opt_idx)) != EOF) { 1060 1061 if (opt != 0) { 1062 print_usage(config.prgname); 1063 rte_exit(-EINVAL, "unknown option: %c", opt); 1064 } 1065 1066 if (strcmp(lgopts[opt_idx].name, OPT_RULE_FILE) == 0) { 1067 config.rule_file = optarg; 1068 } else if (strcmp(lgopts[opt_idx].name, OPT_TRACE_FILE) == 0) { 1069 config.trace_file = optarg; 1070 } else if (strcmp(lgopts[opt_idx].name, OPT_RULE_NUM) == 0) { 1071 config.nb_rules = get_ulong_opt(optarg, 1072 lgopts[opt_idx].name, 1, RTE_ACL_MAX_INDEX + 1); 1073 } else if (strcmp(lgopts[opt_idx].name, OPT_MAX_SIZE) == 0) { 1074 config.max_size = get_ulong_opt(optarg, 1075 lgopts[opt_idx].name, 0, SIZE_MAX); 1076 } else if (strcmp(lgopts[opt_idx].name, OPT_TRACE_NUM) == 0) { 1077 config.nb_traces = get_ulong_opt(optarg, 1078 lgopts[opt_idx].name, 1, UINT32_MAX); 1079 } else if (strcmp(lgopts[opt_idx].name, OPT_TRACE_STEP) == 0) { 1080 config.trace_step = get_ulong_opt(optarg, 1081 lgopts[opt_idx].name, 1, TRACE_STEP_MAX); 1082 } else if (strcmp(lgopts[opt_idx].name, 1083 OPT_BLD_CATEGORIES) == 0) { 1084 config.bld_categories = get_ulong_opt(optarg, 1085 lgopts[opt_idx].name, 1, 1086 RTE_ACL_MAX_CATEGORIES); 1087 } else if (strcmp(lgopts[opt_idx].name, 1088 OPT_RUN_CATEGORIES) == 0) { 1089 config.run_categories = get_ulong_opt(optarg, 1090 lgopts[opt_idx].name, 1, 1091 RTE_ACL_MAX_CATEGORIES); 1092 } else if (strcmp(lgopts[opt_idx].name, OPT_ITER_NUM) == 0) { 1093 config.iter_num = get_ulong_opt(optarg, 1094 lgopts[opt_idx].name, 1, INT32_MAX); 1095 } else if (strcmp(lgopts[opt_idx].name, OPT_VERBOSE) == 0) { 1096 config.verbose = get_ulong_opt(optarg, 1097 lgopts[opt_idx].name, DUMP_NONE, DUMP_MAX); 1098 } else if (strcmp(lgopts[opt_idx].name, 1099 OPT_SEARCH_ALG) == 0) { 1100 get_alg_opt(optarg, lgopts[opt_idx].name); 1101 } else if (strcmp(lgopts[opt_idx].name, OPT_IPV6) == 0) { 1102 config.ipv6 = 1; 1103 } 1104 } 1105 config.trace_sz = config.ipv6 ? sizeof(struct ipv6_5tuple) : 1106 sizeof(struct ipv4_5tuple); 1107 1108 } 1109 1110 int 1111 main(int argc, char **argv) 1112 { 1113 int ret; 1114 uint32_t lcore; 1115 1116 ret = rte_eal_init(argc, argv); 1117 if (ret < 0) 1118 rte_panic("Cannot init EAL\n"); 1119 1120 argc -= ret; 1121 argv += ret; 1122 1123 config.prgname = argv[0]; 1124 1125 get_input_opts(argc, argv); 1126 dump_config(stdout); 1127 check_config(); 1128 1129 acx_init(); 1130 1131 if (config.trace_file != NULL) 1132 tracef_init(); 1133 1134 RTE_LCORE_FOREACH_WORKER(lcore) 1135 rte_eal_remote_launch(search_ip5tuples, NULL, lcore); 1136 1137 search_ip5tuples(NULL); 1138 1139 rte_eal_mp_wait_lcore(); 1140 1141 rte_acl_free(config.acx); 1142 return 0; 1143 } 1144