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