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