1 /*- 2 * BSD LICENSE 3 * 4 * Copyright 2016 6WIND S.A. 5 * Copyright 2016 Mellanox. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of 6WIND S.A. nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <stddef.h> 35 #include <stdint.h> 36 #include <stdio.h> 37 #include <inttypes.h> 38 #include <errno.h> 39 #include <ctype.h> 40 #include <string.h> 41 #include <arpa/inet.h> 42 #include <sys/socket.h> 43 44 #include <rte_common.h> 45 #include <rte_ethdev.h> 46 #include <rte_byteorder.h> 47 #include <cmdline_parse.h> 48 #include <cmdline_parse_etheraddr.h> 49 #include <rte_flow.h> 50 51 #include "testpmd.h" 52 53 /** Parser token indices. */ 54 enum index { 55 /* Special tokens. */ 56 ZERO = 0, 57 END, 58 59 /* Common tokens. */ 60 INTEGER, 61 UNSIGNED, 62 PREFIX, 63 BOOLEAN, 64 STRING, 65 MAC_ADDR, 66 IPV4_ADDR, 67 IPV6_ADDR, 68 RULE_ID, 69 PORT_ID, 70 GROUP_ID, 71 PRIORITY_LEVEL, 72 73 /* Top-level command. */ 74 FLOW, 75 76 /* Sub-level commands. */ 77 VALIDATE, 78 CREATE, 79 DESTROY, 80 FLUSH, 81 QUERY, 82 LIST, 83 84 /* Destroy arguments. */ 85 DESTROY_RULE, 86 87 /* Query arguments. */ 88 QUERY_ACTION, 89 90 /* List arguments. */ 91 LIST_GROUP, 92 93 /* Validate/create arguments. */ 94 GROUP, 95 PRIORITY, 96 INGRESS, 97 EGRESS, 98 99 /* Validate/create pattern. */ 100 PATTERN, 101 ITEM_PARAM_IS, 102 ITEM_PARAM_SPEC, 103 ITEM_PARAM_LAST, 104 ITEM_PARAM_MASK, 105 ITEM_PARAM_PREFIX, 106 ITEM_NEXT, 107 ITEM_END, 108 ITEM_VOID, 109 ITEM_INVERT, 110 ITEM_ANY, 111 ITEM_ANY_NUM, 112 ITEM_PF, 113 ITEM_VF, 114 ITEM_VF_ID, 115 ITEM_PORT, 116 ITEM_PORT_INDEX, 117 ITEM_RAW, 118 ITEM_RAW_RELATIVE, 119 ITEM_RAW_SEARCH, 120 ITEM_RAW_OFFSET, 121 ITEM_RAW_LIMIT, 122 ITEM_RAW_PATTERN, 123 ITEM_ETH, 124 ITEM_ETH_DST, 125 ITEM_ETH_SRC, 126 ITEM_ETH_TYPE, 127 ITEM_VLAN, 128 ITEM_VLAN_TPID, 129 ITEM_VLAN_TCI, 130 ITEM_VLAN_PCP, 131 ITEM_VLAN_DEI, 132 ITEM_VLAN_VID, 133 ITEM_IPV4, 134 ITEM_IPV4_TOS, 135 ITEM_IPV4_TTL, 136 ITEM_IPV4_PROTO, 137 ITEM_IPV4_SRC, 138 ITEM_IPV4_DST, 139 ITEM_IPV6, 140 ITEM_IPV6_TC, 141 ITEM_IPV6_FLOW, 142 ITEM_IPV6_PROTO, 143 ITEM_IPV6_HOP, 144 ITEM_IPV6_SRC, 145 ITEM_IPV6_DST, 146 ITEM_ICMP, 147 ITEM_ICMP_TYPE, 148 ITEM_ICMP_CODE, 149 ITEM_UDP, 150 ITEM_UDP_SRC, 151 ITEM_UDP_DST, 152 ITEM_TCP, 153 ITEM_TCP_SRC, 154 ITEM_TCP_DST, 155 ITEM_SCTP, 156 ITEM_SCTP_SRC, 157 ITEM_SCTP_DST, 158 ITEM_SCTP_TAG, 159 ITEM_SCTP_CKSUM, 160 ITEM_VXLAN, 161 ITEM_VXLAN_VNI, 162 163 /* Validate/create actions. */ 164 ACTIONS, 165 ACTION_NEXT, 166 ACTION_END, 167 ACTION_VOID, 168 ACTION_PASSTHRU, 169 ACTION_MARK, 170 ACTION_MARK_ID, 171 ACTION_FLAG, 172 ACTION_QUEUE, 173 ACTION_QUEUE_INDEX, 174 ACTION_DROP, 175 ACTION_COUNT, 176 ACTION_DUP, 177 ACTION_DUP_INDEX, 178 ACTION_RSS, 179 ACTION_RSS_QUEUES, 180 ACTION_RSS_QUEUE, 181 ACTION_PF, 182 ACTION_VF, 183 ACTION_VF_ORIGINAL, 184 ACTION_VF_ID, 185 }; 186 187 /** Size of pattern[] field in struct rte_flow_item_raw. */ 188 #define ITEM_RAW_PATTERN_SIZE 36 189 190 /** Storage size for struct rte_flow_item_raw including pattern. */ 191 #define ITEM_RAW_SIZE \ 192 (offsetof(struct rte_flow_item_raw, pattern) + ITEM_RAW_PATTERN_SIZE) 193 194 /** Number of queue[] entries in struct rte_flow_action_rss. */ 195 #define ACTION_RSS_NUM 32 196 197 /** Storage size for struct rte_flow_action_rss including queues. */ 198 #define ACTION_RSS_SIZE \ 199 (offsetof(struct rte_flow_action_rss, queue) + \ 200 sizeof(*((struct rte_flow_action_rss *)0)->queue) * ACTION_RSS_NUM) 201 202 /** Maximum number of subsequent tokens and arguments on the stack. */ 203 #define CTX_STACK_SIZE 16 204 205 /** Parser context. */ 206 struct context { 207 /** Stack of subsequent token lists to process. */ 208 const enum index *next[CTX_STACK_SIZE]; 209 /** Arguments for stacked tokens. */ 210 const void *args[CTX_STACK_SIZE]; 211 enum index curr; /**< Current token index. */ 212 enum index prev; /**< Index of the last token seen. */ 213 int next_num; /**< Number of entries in next[]. */ 214 int args_num; /**< Number of entries in args[]. */ 215 uint32_t reparse:1; /**< Start over from the beginning. */ 216 uint32_t eol:1; /**< EOL has been detected. */ 217 uint32_t last:1; /**< No more arguments. */ 218 uint16_t port; /**< Current port ID (for completions). */ 219 uint32_t objdata; /**< Object-specific data. */ 220 void *object; /**< Address of current object for relative offsets. */ 221 void *objmask; /**< Object a full mask must be written to. */ 222 }; 223 224 /** Token argument. */ 225 struct arg { 226 uint32_t hton:1; /**< Use network byte ordering. */ 227 uint32_t sign:1; /**< Value is signed. */ 228 uint32_t offset; /**< Relative offset from ctx->object. */ 229 uint32_t size; /**< Field size. */ 230 const uint8_t *mask; /**< Bit-mask to use instead of offset/size. */ 231 }; 232 233 /** Parser token definition. */ 234 struct token { 235 /** Type displayed during completion (defaults to "TOKEN"). */ 236 const char *type; 237 /** Help displayed during completion (defaults to token name). */ 238 const char *help; 239 /** Private data used by parser functions. */ 240 const void *priv; 241 /** 242 * Lists of subsequent tokens to push on the stack. Each call to the 243 * parser consumes the last entry of that stack. 244 */ 245 const enum index *const *next; 246 /** Arguments stack for subsequent tokens that need them. */ 247 const struct arg *const *args; 248 /** 249 * Token-processing callback, returns -1 in case of error, the 250 * length of the matched string otherwise. If NULL, attempts to 251 * match the token name. 252 * 253 * If buf is not NULL, the result should be stored in it according 254 * to context. An error is returned if not large enough. 255 */ 256 int (*call)(struct context *ctx, const struct token *token, 257 const char *str, unsigned int len, 258 void *buf, unsigned int size); 259 /** 260 * Callback that provides possible values for this token, used for 261 * completion. Returns -1 in case of error, the number of possible 262 * values otherwise. If NULL, the token name is used. 263 * 264 * If buf is not NULL, entry index ent is written to buf and the 265 * full length of the entry is returned (same behavior as 266 * snprintf()). 267 */ 268 int (*comp)(struct context *ctx, const struct token *token, 269 unsigned int ent, char *buf, unsigned int size); 270 /** Mandatory token name, no default value. */ 271 const char *name; 272 }; 273 274 /** Static initializer for the next field. */ 275 #define NEXT(...) (const enum index *const []){ __VA_ARGS__, NULL, } 276 277 /** Static initializer for a NEXT() entry. */ 278 #define NEXT_ENTRY(...) (const enum index []){ __VA_ARGS__, ZERO, } 279 280 /** Static initializer for the args field. */ 281 #define ARGS(...) (const struct arg *const []){ __VA_ARGS__, NULL, } 282 283 /** Static initializer for ARGS() to target a field. */ 284 #define ARGS_ENTRY(s, f) \ 285 (&(const struct arg){ \ 286 .offset = offsetof(s, f), \ 287 .size = sizeof(((s *)0)->f), \ 288 }) 289 290 /** Static initializer for ARGS() to target a bit-field. */ 291 #define ARGS_ENTRY_BF(s, f, b) \ 292 (&(const struct arg){ \ 293 .size = sizeof(s), \ 294 .mask = (const void *)&(const s){ .f = (1 << (b)) - 1 }, \ 295 }) 296 297 /** Static initializer for ARGS() to target an arbitrary bit-mask. */ 298 #define ARGS_ENTRY_MASK(s, f, m) \ 299 (&(const struct arg){ \ 300 .offset = offsetof(s, f), \ 301 .size = sizeof(((s *)0)->f), \ 302 .mask = (const void *)(m), \ 303 }) 304 305 /** Same as ARGS_ENTRY_MASK() using network byte ordering for the value. */ 306 #define ARGS_ENTRY_MASK_HTON(s, f, m) \ 307 (&(const struct arg){ \ 308 .hton = 1, \ 309 .offset = offsetof(s, f), \ 310 .size = sizeof(((s *)0)->f), \ 311 .mask = (const void *)(m), \ 312 }) 313 314 /** Static initializer for ARGS() to target a pointer. */ 315 #define ARGS_ENTRY_PTR(s, f) \ 316 (&(const struct arg){ \ 317 .size = sizeof(*((s *)0)->f), \ 318 }) 319 320 /** Static initializer for ARGS() with arbitrary size. */ 321 #define ARGS_ENTRY_USZ(s, f, sz) \ 322 (&(const struct arg){ \ 323 .offset = offsetof(s, f), \ 324 .size = (sz), \ 325 }) 326 327 /** Same as ARGS_ENTRY() using network byte ordering. */ 328 #define ARGS_ENTRY_HTON(s, f) \ 329 (&(const struct arg){ \ 330 .hton = 1, \ 331 .offset = offsetof(s, f), \ 332 .size = sizeof(((s *)0)->f), \ 333 }) 334 335 /** Parser output buffer layout expected by cmd_flow_parsed(). */ 336 struct buffer { 337 enum index command; /**< Flow command. */ 338 uint16_t port; /**< Affected port ID. */ 339 union { 340 struct { 341 struct rte_flow_attr attr; 342 struct rte_flow_item *pattern; 343 struct rte_flow_action *actions; 344 uint32_t pattern_n; 345 uint32_t actions_n; 346 uint8_t *data; 347 } vc; /**< Validate/create arguments. */ 348 struct { 349 uint32_t *rule; 350 uint32_t rule_n; 351 } destroy; /**< Destroy arguments. */ 352 struct { 353 uint32_t rule; 354 enum rte_flow_action_type action; 355 } query; /**< Query arguments. */ 356 struct { 357 uint32_t *group; 358 uint32_t group_n; 359 } list; /**< List arguments. */ 360 } args; /**< Command arguments. */ 361 }; 362 363 /** Private data for pattern items. */ 364 struct parse_item_priv { 365 enum rte_flow_item_type type; /**< Item type. */ 366 uint32_t size; /**< Size of item specification structure. */ 367 }; 368 369 #define PRIV_ITEM(t, s) \ 370 (&(const struct parse_item_priv){ \ 371 .type = RTE_FLOW_ITEM_TYPE_ ## t, \ 372 .size = s, \ 373 }) 374 375 /** Private data for actions. */ 376 struct parse_action_priv { 377 enum rte_flow_action_type type; /**< Action type. */ 378 uint32_t size; /**< Size of action configuration structure. */ 379 }; 380 381 #define PRIV_ACTION(t, s) \ 382 (&(const struct parse_action_priv){ \ 383 .type = RTE_FLOW_ACTION_TYPE_ ## t, \ 384 .size = s, \ 385 }) 386 387 static const enum index next_vc_attr[] = { 388 GROUP, 389 PRIORITY, 390 INGRESS, 391 EGRESS, 392 PATTERN, 393 ZERO, 394 }; 395 396 static const enum index next_destroy_attr[] = { 397 DESTROY_RULE, 398 END, 399 ZERO, 400 }; 401 402 static const enum index next_list_attr[] = { 403 LIST_GROUP, 404 END, 405 ZERO, 406 }; 407 408 static const enum index item_param[] = { 409 ITEM_PARAM_IS, 410 ITEM_PARAM_SPEC, 411 ITEM_PARAM_LAST, 412 ITEM_PARAM_MASK, 413 ITEM_PARAM_PREFIX, 414 ZERO, 415 }; 416 417 static const enum index next_item[] = { 418 ITEM_END, 419 ITEM_VOID, 420 ITEM_INVERT, 421 ITEM_ANY, 422 ITEM_PF, 423 ITEM_VF, 424 ITEM_PORT, 425 ITEM_RAW, 426 ITEM_ETH, 427 ITEM_VLAN, 428 ITEM_IPV4, 429 ITEM_IPV6, 430 ITEM_ICMP, 431 ITEM_UDP, 432 ITEM_TCP, 433 ITEM_SCTP, 434 ITEM_VXLAN, 435 ZERO, 436 }; 437 438 static const enum index item_any[] = { 439 ITEM_ANY_NUM, 440 ITEM_NEXT, 441 ZERO, 442 }; 443 444 static const enum index item_vf[] = { 445 ITEM_VF_ID, 446 ITEM_NEXT, 447 ZERO, 448 }; 449 450 static const enum index item_port[] = { 451 ITEM_PORT_INDEX, 452 ITEM_NEXT, 453 ZERO, 454 }; 455 456 static const enum index item_raw[] = { 457 ITEM_RAW_RELATIVE, 458 ITEM_RAW_SEARCH, 459 ITEM_RAW_OFFSET, 460 ITEM_RAW_LIMIT, 461 ITEM_RAW_PATTERN, 462 ITEM_NEXT, 463 ZERO, 464 }; 465 466 static const enum index item_eth[] = { 467 ITEM_ETH_DST, 468 ITEM_ETH_SRC, 469 ITEM_ETH_TYPE, 470 ITEM_NEXT, 471 ZERO, 472 }; 473 474 static const enum index item_vlan[] = { 475 ITEM_VLAN_TPID, 476 ITEM_VLAN_TCI, 477 ITEM_VLAN_PCP, 478 ITEM_VLAN_DEI, 479 ITEM_VLAN_VID, 480 ITEM_NEXT, 481 ZERO, 482 }; 483 484 static const enum index item_ipv4[] = { 485 ITEM_IPV4_TOS, 486 ITEM_IPV4_TTL, 487 ITEM_IPV4_PROTO, 488 ITEM_IPV4_SRC, 489 ITEM_IPV4_DST, 490 ITEM_NEXT, 491 ZERO, 492 }; 493 494 static const enum index item_ipv6[] = { 495 ITEM_IPV6_TC, 496 ITEM_IPV6_FLOW, 497 ITEM_IPV6_PROTO, 498 ITEM_IPV6_HOP, 499 ITEM_IPV6_SRC, 500 ITEM_IPV6_DST, 501 ITEM_NEXT, 502 ZERO, 503 }; 504 505 static const enum index item_icmp[] = { 506 ITEM_ICMP_TYPE, 507 ITEM_ICMP_CODE, 508 ITEM_NEXT, 509 ZERO, 510 }; 511 512 static const enum index item_udp[] = { 513 ITEM_UDP_SRC, 514 ITEM_UDP_DST, 515 ITEM_NEXT, 516 ZERO, 517 }; 518 519 static const enum index item_tcp[] = { 520 ITEM_TCP_SRC, 521 ITEM_TCP_DST, 522 ITEM_NEXT, 523 ZERO, 524 }; 525 526 static const enum index item_sctp[] = { 527 ITEM_SCTP_SRC, 528 ITEM_SCTP_DST, 529 ITEM_SCTP_TAG, 530 ITEM_SCTP_CKSUM, 531 ITEM_NEXT, 532 ZERO, 533 }; 534 535 static const enum index item_vxlan[] = { 536 ITEM_VXLAN_VNI, 537 ITEM_NEXT, 538 ZERO, 539 }; 540 541 static const enum index next_action[] = { 542 ACTION_END, 543 ACTION_VOID, 544 ACTION_PASSTHRU, 545 ACTION_MARK, 546 ACTION_FLAG, 547 ACTION_QUEUE, 548 ACTION_DROP, 549 ACTION_COUNT, 550 ACTION_DUP, 551 ACTION_RSS, 552 ACTION_PF, 553 ACTION_VF, 554 ZERO, 555 }; 556 557 static const enum index action_mark[] = { 558 ACTION_MARK_ID, 559 ACTION_NEXT, 560 ZERO, 561 }; 562 563 static const enum index action_queue[] = { 564 ACTION_QUEUE_INDEX, 565 ACTION_NEXT, 566 ZERO, 567 }; 568 569 static const enum index action_dup[] = { 570 ACTION_DUP_INDEX, 571 ACTION_NEXT, 572 ZERO, 573 }; 574 575 static const enum index action_rss[] = { 576 ACTION_RSS_QUEUES, 577 ACTION_NEXT, 578 ZERO, 579 }; 580 581 static const enum index action_vf[] = { 582 ACTION_VF_ORIGINAL, 583 ACTION_VF_ID, 584 ACTION_NEXT, 585 ZERO, 586 }; 587 588 static int parse_init(struct context *, const struct token *, 589 const char *, unsigned int, 590 void *, unsigned int); 591 static int parse_vc(struct context *, const struct token *, 592 const char *, unsigned int, 593 void *, unsigned int); 594 static int parse_vc_spec(struct context *, const struct token *, 595 const char *, unsigned int, void *, unsigned int); 596 static int parse_vc_conf(struct context *, const struct token *, 597 const char *, unsigned int, void *, unsigned int); 598 static int parse_vc_action_rss_queue(struct context *, const struct token *, 599 const char *, unsigned int, void *, 600 unsigned int); 601 static int parse_destroy(struct context *, const struct token *, 602 const char *, unsigned int, 603 void *, unsigned int); 604 static int parse_flush(struct context *, const struct token *, 605 const char *, unsigned int, 606 void *, unsigned int); 607 static int parse_query(struct context *, const struct token *, 608 const char *, unsigned int, 609 void *, unsigned int); 610 static int parse_action(struct context *, const struct token *, 611 const char *, unsigned int, 612 void *, unsigned int); 613 static int parse_list(struct context *, const struct token *, 614 const char *, unsigned int, 615 void *, unsigned int); 616 static int parse_int(struct context *, const struct token *, 617 const char *, unsigned int, 618 void *, unsigned int); 619 static int parse_prefix(struct context *, const struct token *, 620 const char *, unsigned int, 621 void *, unsigned int); 622 static int parse_boolean(struct context *, const struct token *, 623 const char *, unsigned int, 624 void *, unsigned int); 625 static int parse_string(struct context *, const struct token *, 626 const char *, unsigned int, 627 void *, unsigned int); 628 static int parse_mac_addr(struct context *, const struct token *, 629 const char *, unsigned int, 630 void *, unsigned int); 631 static int parse_ipv4_addr(struct context *, const struct token *, 632 const char *, unsigned int, 633 void *, unsigned int); 634 static int parse_ipv6_addr(struct context *, const struct token *, 635 const char *, unsigned int, 636 void *, unsigned int); 637 static int parse_port(struct context *, const struct token *, 638 const char *, unsigned int, 639 void *, unsigned int); 640 static int comp_none(struct context *, const struct token *, 641 unsigned int, char *, unsigned int); 642 static int comp_boolean(struct context *, const struct token *, 643 unsigned int, char *, unsigned int); 644 static int comp_action(struct context *, const struct token *, 645 unsigned int, char *, unsigned int); 646 static int comp_port(struct context *, const struct token *, 647 unsigned int, char *, unsigned int); 648 static int comp_rule_id(struct context *, const struct token *, 649 unsigned int, char *, unsigned int); 650 static int comp_vc_action_rss_queue(struct context *, const struct token *, 651 unsigned int, char *, unsigned int); 652 653 /** Token definitions. */ 654 static const struct token token_list[] = { 655 /* Special tokens. */ 656 [ZERO] = { 657 .name = "ZERO", 658 .help = "null entry, abused as the entry point", 659 .next = NEXT(NEXT_ENTRY(FLOW)), 660 }, 661 [END] = { 662 .name = "", 663 .type = "RETURN", 664 .help = "command may end here", 665 }, 666 /* Common tokens. */ 667 [INTEGER] = { 668 .name = "{int}", 669 .type = "INTEGER", 670 .help = "integer value", 671 .call = parse_int, 672 .comp = comp_none, 673 }, 674 [UNSIGNED] = { 675 .name = "{unsigned}", 676 .type = "UNSIGNED", 677 .help = "unsigned integer value", 678 .call = parse_int, 679 .comp = comp_none, 680 }, 681 [PREFIX] = { 682 .name = "{prefix}", 683 .type = "PREFIX", 684 .help = "prefix length for bit-mask", 685 .call = parse_prefix, 686 .comp = comp_none, 687 }, 688 [BOOLEAN] = { 689 .name = "{boolean}", 690 .type = "BOOLEAN", 691 .help = "any boolean value", 692 .call = parse_boolean, 693 .comp = comp_boolean, 694 }, 695 [STRING] = { 696 .name = "{string}", 697 .type = "STRING", 698 .help = "fixed string", 699 .call = parse_string, 700 .comp = comp_none, 701 }, 702 [MAC_ADDR] = { 703 .name = "{MAC address}", 704 .type = "MAC-48", 705 .help = "standard MAC address notation", 706 .call = parse_mac_addr, 707 .comp = comp_none, 708 }, 709 [IPV4_ADDR] = { 710 .name = "{IPv4 address}", 711 .type = "IPV4 ADDRESS", 712 .help = "standard IPv4 address notation", 713 .call = parse_ipv4_addr, 714 .comp = comp_none, 715 }, 716 [IPV6_ADDR] = { 717 .name = "{IPv6 address}", 718 .type = "IPV6 ADDRESS", 719 .help = "standard IPv6 address notation", 720 .call = parse_ipv6_addr, 721 .comp = comp_none, 722 }, 723 [RULE_ID] = { 724 .name = "{rule id}", 725 .type = "RULE ID", 726 .help = "rule identifier", 727 .call = parse_int, 728 .comp = comp_rule_id, 729 }, 730 [PORT_ID] = { 731 .name = "{port_id}", 732 .type = "PORT ID", 733 .help = "port identifier", 734 .call = parse_port, 735 .comp = comp_port, 736 }, 737 [GROUP_ID] = { 738 .name = "{group_id}", 739 .type = "GROUP ID", 740 .help = "group identifier", 741 .call = parse_int, 742 .comp = comp_none, 743 }, 744 [PRIORITY_LEVEL] = { 745 .name = "{level}", 746 .type = "PRIORITY", 747 .help = "priority level", 748 .call = parse_int, 749 .comp = comp_none, 750 }, 751 /* Top-level command. */ 752 [FLOW] = { 753 .name = "flow", 754 .type = "{command} {port_id} [{arg} [...]]", 755 .help = "manage ingress/egress flow rules", 756 .next = NEXT(NEXT_ENTRY 757 (VALIDATE, 758 CREATE, 759 DESTROY, 760 FLUSH, 761 LIST, 762 QUERY)), 763 .call = parse_init, 764 }, 765 /* Sub-level commands. */ 766 [VALIDATE] = { 767 .name = "validate", 768 .help = "check whether a flow rule can be created", 769 .next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)), 770 .args = ARGS(ARGS_ENTRY(struct buffer, port)), 771 .call = parse_vc, 772 }, 773 [CREATE] = { 774 .name = "create", 775 .help = "create a flow rule", 776 .next = NEXT(next_vc_attr, NEXT_ENTRY(PORT_ID)), 777 .args = ARGS(ARGS_ENTRY(struct buffer, port)), 778 .call = parse_vc, 779 }, 780 [DESTROY] = { 781 .name = "destroy", 782 .help = "destroy specific flow rules", 783 .next = NEXT(NEXT_ENTRY(DESTROY_RULE), NEXT_ENTRY(PORT_ID)), 784 .args = ARGS(ARGS_ENTRY(struct buffer, port)), 785 .call = parse_destroy, 786 }, 787 [FLUSH] = { 788 .name = "flush", 789 .help = "destroy all flow rules", 790 .next = NEXT(NEXT_ENTRY(PORT_ID)), 791 .args = ARGS(ARGS_ENTRY(struct buffer, port)), 792 .call = parse_flush, 793 }, 794 [QUERY] = { 795 .name = "query", 796 .help = "query an existing flow rule", 797 .next = NEXT(NEXT_ENTRY(QUERY_ACTION), 798 NEXT_ENTRY(RULE_ID), 799 NEXT_ENTRY(PORT_ID)), 800 .args = ARGS(ARGS_ENTRY(struct buffer, args.query.action), 801 ARGS_ENTRY(struct buffer, args.query.rule), 802 ARGS_ENTRY(struct buffer, port)), 803 .call = parse_query, 804 }, 805 [LIST] = { 806 .name = "list", 807 .help = "list existing flow rules", 808 .next = NEXT(next_list_attr, NEXT_ENTRY(PORT_ID)), 809 .args = ARGS(ARGS_ENTRY(struct buffer, port)), 810 .call = parse_list, 811 }, 812 /* Destroy arguments. */ 813 [DESTROY_RULE] = { 814 .name = "rule", 815 .help = "specify a rule identifier", 816 .next = NEXT(next_destroy_attr, NEXT_ENTRY(RULE_ID)), 817 .args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.destroy.rule)), 818 .call = parse_destroy, 819 }, 820 /* Query arguments. */ 821 [QUERY_ACTION] = { 822 .name = "{action}", 823 .type = "ACTION", 824 .help = "action to query, must be part of the rule", 825 .call = parse_action, 826 .comp = comp_action, 827 }, 828 /* List arguments. */ 829 [LIST_GROUP] = { 830 .name = "group", 831 .help = "specify a group", 832 .next = NEXT(next_list_attr, NEXT_ENTRY(GROUP_ID)), 833 .args = ARGS(ARGS_ENTRY_PTR(struct buffer, args.list.group)), 834 .call = parse_list, 835 }, 836 /* Validate/create attributes. */ 837 [GROUP] = { 838 .name = "group", 839 .help = "specify a group", 840 .next = NEXT(next_vc_attr, NEXT_ENTRY(GROUP_ID)), 841 .args = ARGS(ARGS_ENTRY(struct rte_flow_attr, group)), 842 .call = parse_vc, 843 }, 844 [PRIORITY] = { 845 .name = "priority", 846 .help = "specify a priority level", 847 .next = NEXT(next_vc_attr, NEXT_ENTRY(PRIORITY_LEVEL)), 848 .args = ARGS(ARGS_ENTRY(struct rte_flow_attr, priority)), 849 .call = parse_vc, 850 }, 851 [INGRESS] = { 852 .name = "ingress", 853 .help = "affect rule to ingress", 854 .next = NEXT(next_vc_attr), 855 .call = parse_vc, 856 }, 857 [EGRESS] = { 858 .name = "egress", 859 .help = "affect rule to egress", 860 .next = NEXT(next_vc_attr), 861 .call = parse_vc, 862 }, 863 /* Validate/create pattern. */ 864 [PATTERN] = { 865 .name = "pattern", 866 .help = "submit a list of pattern items", 867 .next = NEXT(next_item), 868 .call = parse_vc, 869 }, 870 [ITEM_PARAM_IS] = { 871 .name = "is", 872 .help = "match value perfectly (with full bit-mask)", 873 .call = parse_vc_spec, 874 }, 875 [ITEM_PARAM_SPEC] = { 876 .name = "spec", 877 .help = "match value according to configured bit-mask", 878 .call = parse_vc_spec, 879 }, 880 [ITEM_PARAM_LAST] = { 881 .name = "last", 882 .help = "specify upper bound to establish a range", 883 .call = parse_vc_spec, 884 }, 885 [ITEM_PARAM_MASK] = { 886 .name = "mask", 887 .help = "specify bit-mask with relevant bits set to one", 888 .call = parse_vc_spec, 889 }, 890 [ITEM_PARAM_PREFIX] = { 891 .name = "prefix", 892 .help = "generate bit-mask from a prefix length", 893 .call = parse_vc_spec, 894 }, 895 [ITEM_NEXT] = { 896 .name = "/", 897 .help = "specify next pattern item", 898 .next = NEXT(next_item), 899 }, 900 [ITEM_END] = { 901 .name = "end", 902 .help = "end list of pattern items", 903 .priv = PRIV_ITEM(END, 0), 904 .next = NEXT(NEXT_ENTRY(ACTIONS)), 905 .call = parse_vc, 906 }, 907 [ITEM_VOID] = { 908 .name = "void", 909 .help = "no-op pattern item", 910 .priv = PRIV_ITEM(VOID, 0), 911 .next = NEXT(NEXT_ENTRY(ITEM_NEXT)), 912 .call = parse_vc, 913 }, 914 [ITEM_INVERT] = { 915 .name = "invert", 916 .help = "perform actions when pattern does not match", 917 .priv = PRIV_ITEM(INVERT, 0), 918 .next = NEXT(NEXT_ENTRY(ITEM_NEXT)), 919 .call = parse_vc, 920 }, 921 [ITEM_ANY] = { 922 .name = "any", 923 .help = "match any protocol for the current layer", 924 .priv = PRIV_ITEM(ANY, sizeof(struct rte_flow_item_any)), 925 .next = NEXT(item_any), 926 .call = parse_vc, 927 }, 928 [ITEM_ANY_NUM] = { 929 .name = "num", 930 .help = "number of layers covered", 931 .next = NEXT(item_any, NEXT_ENTRY(UNSIGNED), item_param), 932 .args = ARGS(ARGS_ENTRY(struct rte_flow_item_any, num)), 933 }, 934 [ITEM_PF] = { 935 .name = "pf", 936 .help = "match packets addressed to the physical function", 937 .priv = PRIV_ITEM(PF, 0), 938 .next = NEXT(NEXT_ENTRY(ITEM_NEXT)), 939 .call = parse_vc, 940 }, 941 [ITEM_VF] = { 942 .name = "vf", 943 .help = "match packets addressed to a virtual function ID", 944 .priv = PRIV_ITEM(VF, sizeof(struct rte_flow_item_vf)), 945 .next = NEXT(item_vf), 946 .call = parse_vc, 947 }, 948 [ITEM_VF_ID] = { 949 .name = "id", 950 .help = "destination VF ID", 951 .next = NEXT(item_vf, NEXT_ENTRY(UNSIGNED), item_param), 952 .args = ARGS(ARGS_ENTRY(struct rte_flow_item_vf, id)), 953 }, 954 [ITEM_PORT] = { 955 .name = "port", 956 .help = "device-specific physical port index to use", 957 .priv = PRIV_ITEM(PORT, sizeof(struct rte_flow_item_port)), 958 .next = NEXT(item_port), 959 .call = parse_vc, 960 }, 961 [ITEM_PORT_INDEX] = { 962 .name = "index", 963 .help = "physical port index", 964 .next = NEXT(item_port, NEXT_ENTRY(UNSIGNED), item_param), 965 .args = ARGS(ARGS_ENTRY(struct rte_flow_item_port, index)), 966 }, 967 [ITEM_RAW] = { 968 .name = "raw", 969 .help = "match an arbitrary byte string", 970 .priv = PRIV_ITEM(RAW, ITEM_RAW_SIZE), 971 .next = NEXT(item_raw), 972 .call = parse_vc, 973 }, 974 [ITEM_RAW_RELATIVE] = { 975 .name = "relative", 976 .help = "look for pattern after the previous item", 977 .next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param), 978 .args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw, 979 relative, 1)), 980 }, 981 [ITEM_RAW_SEARCH] = { 982 .name = "search", 983 .help = "search pattern from offset (see also limit)", 984 .next = NEXT(item_raw, NEXT_ENTRY(BOOLEAN), item_param), 985 .args = ARGS(ARGS_ENTRY_BF(struct rte_flow_item_raw, 986 search, 1)), 987 }, 988 [ITEM_RAW_OFFSET] = { 989 .name = "offset", 990 .help = "absolute or relative offset for pattern", 991 .next = NEXT(item_raw, NEXT_ENTRY(INTEGER), item_param), 992 .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, offset)), 993 }, 994 [ITEM_RAW_LIMIT] = { 995 .name = "limit", 996 .help = "search area limit for start of pattern", 997 .next = NEXT(item_raw, NEXT_ENTRY(UNSIGNED), item_param), 998 .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, limit)), 999 }, 1000 [ITEM_RAW_PATTERN] = { 1001 .name = "pattern", 1002 .help = "byte string to look for", 1003 .next = NEXT(item_raw, 1004 NEXT_ENTRY(STRING), 1005 NEXT_ENTRY(ITEM_PARAM_IS, 1006 ITEM_PARAM_SPEC, 1007 ITEM_PARAM_MASK)), 1008 .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, length), 1009 ARGS_ENTRY_USZ(struct rte_flow_item_raw, 1010 pattern, 1011 ITEM_RAW_PATTERN_SIZE)), 1012 }, 1013 [ITEM_ETH] = { 1014 .name = "eth", 1015 .help = "match Ethernet header", 1016 .priv = PRIV_ITEM(ETH, sizeof(struct rte_flow_item_eth)), 1017 .next = NEXT(item_eth), 1018 .call = parse_vc, 1019 }, 1020 [ITEM_ETH_DST] = { 1021 .name = "dst", 1022 .help = "destination MAC", 1023 .next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param), 1024 .args = ARGS(ARGS_ENTRY(struct rte_flow_item_eth, dst)), 1025 }, 1026 [ITEM_ETH_SRC] = { 1027 .name = "src", 1028 .help = "source MAC", 1029 .next = NEXT(item_eth, NEXT_ENTRY(MAC_ADDR), item_param), 1030 .args = ARGS(ARGS_ENTRY(struct rte_flow_item_eth, src)), 1031 }, 1032 [ITEM_ETH_TYPE] = { 1033 .name = "type", 1034 .help = "EtherType", 1035 .next = NEXT(item_eth, NEXT_ENTRY(UNSIGNED), item_param), 1036 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_eth, type)), 1037 }, 1038 [ITEM_VLAN] = { 1039 .name = "vlan", 1040 .help = "match 802.1Q/ad VLAN tag", 1041 .priv = PRIV_ITEM(VLAN, sizeof(struct rte_flow_item_vlan)), 1042 .next = NEXT(item_vlan), 1043 .call = parse_vc, 1044 }, 1045 [ITEM_VLAN_TPID] = { 1046 .name = "tpid", 1047 .help = "tag protocol identifier", 1048 .next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param), 1049 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tpid)), 1050 }, 1051 [ITEM_VLAN_TCI] = { 1052 .name = "tci", 1053 .help = "tag control information", 1054 .next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param), 1055 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vlan, tci)), 1056 }, 1057 [ITEM_VLAN_PCP] = { 1058 .name = "pcp", 1059 .help = "priority code point", 1060 .next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param), 1061 .args = ARGS(ARGS_ENTRY_MASK_HTON(struct rte_flow_item_vlan, 1062 tci, "\xe0\x00")), 1063 }, 1064 [ITEM_VLAN_DEI] = { 1065 .name = "dei", 1066 .help = "drop eligible indicator", 1067 .next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param), 1068 .args = ARGS(ARGS_ENTRY_MASK_HTON(struct rte_flow_item_vlan, 1069 tci, "\x10\x00")), 1070 }, 1071 [ITEM_VLAN_VID] = { 1072 .name = "vid", 1073 .help = "VLAN identifier", 1074 .next = NEXT(item_vlan, NEXT_ENTRY(UNSIGNED), item_param), 1075 .args = ARGS(ARGS_ENTRY_MASK_HTON(struct rte_flow_item_vlan, 1076 tci, "\x0f\xff")), 1077 }, 1078 [ITEM_IPV4] = { 1079 .name = "ipv4", 1080 .help = "match IPv4 header", 1081 .priv = PRIV_ITEM(IPV4, sizeof(struct rte_flow_item_ipv4)), 1082 .next = NEXT(item_ipv4), 1083 .call = parse_vc, 1084 }, 1085 [ITEM_IPV4_TOS] = { 1086 .name = "tos", 1087 .help = "type of service", 1088 .next = NEXT(item_ipv4, NEXT_ENTRY(UNSIGNED), item_param), 1089 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4, 1090 hdr.type_of_service)), 1091 }, 1092 [ITEM_IPV4_TTL] = { 1093 .name = "ttl", 1094 .help = "time to live", 1095 .next = NEXT(item_ipv4, NEXT_ENTRY(UNSIGNED), item_param), 1096 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4, 1097 hdr.time_to_live)), 1098 }, 1099 [ITEM_IPV4_PROTO] = { 1100 .name = "proto", 1101 .help = "next protocol ID", 1102 .next = NEXT(item_ipv4, NEXT_ENTRY(UNSIGNED), item_param), 1103 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4, 1104 hdr.next_proto_id)), 1105 }, 1106 [ITEM_IPV4_SRC] = { 1107 .name = "src", 1108 .help = "source address", 1109 .next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param), 1110 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4, 1111 hdr.src_addr)), 1112 }, 1113 [ITEM_IPV4_DST] = { 1114 .name = "dst", 1115 .help = "destination address", 1116 .next = NEXT(item_ipv4, NEXT_ENTRY(IPV4_ADDR), item_param), 1117 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv4, 1118 hdr.dst_addr)), 1119 }, 1120 [ITEM_IPV6] = { 1121 .name = "ipv6", 1122 .help = "match IPv6 header", 1123 .priv = PRIV_ITEM(IPV6, sizeof(struct rte_flow_item_ipv6)), 1124 .next = NEXT(item_ipv6), 1125 .call = parse_vc, 1126 }, 1127 [ITEM_IPV6_TC] = { 1128 .name = "tc", 1129 .help = "traffic class", 1130 .next = NEXT(item_ipv6, NEXT_ENTRY(UNSIGNED), item_param), 1131 .args = ARGS(ARGS_ENTRY_MASK_HTON(struct rte_flow_item_ipv6, 1132 hdr.vtc_flow, 1133 "\x0f\xf0\x00\x00")), 1134 }, 1135 [ITEM_IPV6_FLOW] = { 1136 .name = "flow", 1137 .help = "flow label", 1138 .next = NEXT(item_ipv6, NEXT_ENTRY(UNSIGNED), item_param), 1139 .args = ARGS(ARGS_ENTRY_MASK_HTON(struct rte_flow_item_ipv6, 1140 hdr.vtc_flow, 1141 "\x00\x0f\xff\xff")), 1142 }, 1143 [ITEM_IPV6_PROTO] = { 1144 .name = "proto", 1145 .help = "protocol (next header)", 1146 .next = NEXT(item_ipv6, NEXT_ENTRY(UNSIGNED), item_param), 1147 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6, 1148 hdr.proto)), 1149 }, 1150 [ITEM_IPV6_HOP] = { 1151 .name = "hop", 1152 .help = "hop limit", 1153 .next = NEXT(item_ipv6, NEXT_ENTRY(UNSIGNED), item_param), 1154 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6, 1155 hdr.hop_limits)), 1156 }, 1157 [ITEM_IPV6_SRC] = { 1158 .name = "src", 1159 .help = "source address", 1160 .next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param), 1161 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6, 1162 hdr.src_addr)), 1163 }, 1164 [ITEM_IPV6_DST] = { 1165 .name = "dst", 1166 .help = "destination address", 1167 .next = NEXT(item_ipv6, NEXT_ENTRY(IPV6_ADDR), item_param), 1168 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_ipv6, 1169 hdr.dst_addr)), 1170 }, 1171 [ITEM_ICMP] = { 1172 .name = "icmp", 1173 .help = "match ICMP header", 1174 .priv = PRIV_ITEM(ICMP, sizeof(struct rte_flow_item_icmp)), 1175 .next = NEXT(item_icmp), 1176 .call = parse_vc, 1177 }, 1178 [ITEM_ICMP_TYPE] = { 1179 .name = "type", 1180 .help = "ICMP packet type", 1181 .next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param), 1182 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_icmp, 1183 hdr.icmp_type)), 1184 }, 1185 [ITEM_ICMP_CODE] = { 1186 .name = "code", 1187 .help = "ICMP packet code", 1188 .next = NEXT(item_icmp, NEXT_ENTRY(UNSIGNED), item_param), 1189 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_icmp, 1190 hdr.icmp_code)), 1191 }, 1192 [ITEM_UDP] = { 1193 .name = "udp", 1194 .help = "match UDP header", 1195 .priv = PRIV_ITEM(UDP, sizeof(struct rte_flow_item_udp)), 1196 .next = NEXT(item_udp), 1197 .call = parse_vc, 1198 }, 1199 [ITEM_UDP_SRC] = { 1200 .name = "src", 1201 .help = "UDP source port", 1202 .next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param), 1203 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_udp, 1204 hdr.src_port)), 1205 }, 1206 [ITEM_UDP_DST] = { 1207 .name = "dst", 1208 .help = "UDP destination port", 1209 .next = NEXT(item_udp, NEXT_ENTRY(UNSIGNED), item_param), 1210 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_udp, 1211 hdr.dst_port)), 1212 }, 1213 [ITEM_TCP] = { 1214 .name = "tcp", 1215 .help = "match TCP header", 1216 .priv = PRIV_ITEM(TCP, sizeof(struct rte_flow_item_tcp)), 1217 .next = NEXT(item_tcp), 1218 .call = parse_vc, 1219 }, 1220 [ITEM_TCP_SRC] = { 1221 .name = "src", 1222 .help = "TCP source port", 1223 .next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param), 1224 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_tcp, 1225 hdr.src_port)), 1226 }, 1227 [ITEM_TCP_DST] = { 1228 .name = "dst", 1229 .help = "TCP destination port", 1230 .next = NEXT(item_tcp, NEXT_ENTRY(UNSIGNED), item_param), 1231 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_tcp, 1232 hdr.dst_port)), 1233 }, 1234 [ITEM_SCTP] = { 1235 .name = "sctp", 1236 .help = "match SCTP header", 1237 .priv = PRIV_ITEM(SCTP, sizeof(struct rte_flow_item_sctp)), 1238 .next = NEXT(item_sctp), 1239 .call = parse_vc, 1240 }, 1241 [ITEM_SCTP_SRC] = { 1242 .name = "src", 1243 .help = "SCTP source port", 1244 .next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param), 1245 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp, 1246 hdr.src_port)), 1247 }, 1248 [ITEM_SCTP_DST] = { 1249 .name = "dst", 1250 .help = "SCTP destination port", 1251 .next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param), 1252 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp, 1253 hdr.dst_port)), 1254 }, 1255 [ITEM_SCTP_TAG] = { 1256 .name = "tag", 1257 .help = "validation tag", 1258 .next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param), 1259 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp, 1260 hdr.tag)), 1261 }, 1262 [ITEM_SCTP_CKSUM] = { 1263 .name = "cksum", 1264 .help = "checksum", 1265 .next = NEXT(item_sctp, NEXT_ENTRY(UNSIGNED), item_param), 1266 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_sctp, 1267 hdr.cksum)), 1268 }, 1269 [ITEM_VXLAN] = { 1270 .name = "vxlan", 1271 .help = "match VXLAN header", 1272 .priv = PRIV_ITEM(VXLAN, sizeof(struct rte_flow_item_vxlan)), 1273 .next = NEXT(item_vxlan), 1274 .call = parse_vc, 1275 }, 1276 [ITEM_VXLAN_VNI] = { 1277 .name = "vni", 1278 .help = "VXLAN identifier", 1279 .next = NEXT(item_vxlan, NEXT_ENTRY(UNSIGNED), item_param), 1280 .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_vxlan, vni)), 1281 }, 1282 /* Validate/create actions. */ 1283 [ACTIONS] = { 1284 .name = "actions", 1285 .help = "submit a list of associated actions", 1286 .next = NEXT(next_action), 1287 .call = parse_vc, 1288 }, 1289 [ACTION_NEXT] = { 1290 .name = "/", 1291 .help = "specify next action", 1292 .next = NEXT(next_action), 1293 }, 1294 [ACTION_END] = { 1295 .name = "end", 1296 .help = "end list of actions", 1297 .priv = PRIV_ACTION(END, 0), 1298 .call = parse_vc, 1299 }, 1300 [ACTION_VOID] = { 1301 .name = "void", 1302 .help = "no-op action", 1303 .priv = PRIV_ACTION(VOID, 0), 1304 .next = NEXT(NEXT_ENTRY(ACTION_NEXT)), 1305 .call = parse_vc, 1306 }, 1307 [ACTION_PASSTHRU] = { 1308 .name = "passthru", 1309 .help = "let subsequent rule process matched packets", 1310 .priv = PRIV_ACTION(PASSTHRU, 0), 1311 .next = NEXT(NEXT_ENTRY(ACTION_NEXT)), 1312 .call = parse_vc, 1313 }, 1314 [ACTION_MARK] = { 1315 .name = "mark", 1316 .help = "attach 32 bit value to packets", 1317 .priv = PRIV_ACTION(MARK, sizeof(struct rte_flow_action_mark)), 1318 .next = NEXT(action_mark), 1319 .call = parse_vc, 1320 }, 1321 [ACTION_MARK_ID] = { 1322 .name = "id", 1323 .help = "32 bit value to return with packets", 1324 .next = NEXT(action_mark, NEXT_ENTRY(UNSIGNED)), 1325 .args = ARGS(ARGS_ENTRY(struct rte_flow_action_mark, id)), 1326 .call = parse_vc_conf, 1327 }, 1328 [ACTION_FLAG] = { 1329 .name = "flag", 1330 .help = "flag packets", 1331 .priv = PRIV_ACTION(FLAG, 0), 1332 .next = NEXT(NEXT_ENTRY(ACTION_NEXT)), 1333 .call = parse_vc, 1334 }, 1335 [ACTION_QUEUE] = { 1336 .name = "queue", 1337 .help = "assign packets to a given queue index", 1338 .priv = PRIV_ACTION(QUEUE, 1339 sizeof(struct rte_flow_action_queue)), 1340 .next = NEXT(action_queue), 1341 .call = parse_vc, 1342 }, 1343 [ACTION_QUEUE_INDEX] = { 1344 .name = "index", 1345 .help = "queue index to use", 1346 .next = NEXT(action_queue, NEXT_ENTRY(UNSIGNED)), 1347 .args = ARGS(ARGS_ENTRY(struct rte_flow_action_queue, index)), 1348 .call = parse_vc_conf, 1349 }, 1350 [ACTION_DROP] = { 1351 .name = "drop", 1352 .help = "drop packets (note: passthru has priority)", 1353 .priv = PRIV_ACTION(DROP, 0), 1354 .next = NEXT(NEXT_ENTRY(ACTION_NEXT)), 1355 .call = parse_vc, 1356 }, 1357 [ACTION_COUNT] = { 1358 .name = "count", 1359 .help = "enable counters for this rule", 1360 .priv = PRIV_ACTION(COUNT, 0), 1361 .next = NEXT(NEXT_ENTRY(ACTION_NEXT)), 1362 .call = parse_vc, 1363 }, 1364 [ACTION_DUP] = { 1365 .name = "dup", 1366 .help = "duplicate packets to a given queue index", 1367 .priv = PRIV_ACTION(DUP, sizeof(struct rte_flow_action_dup)), 1368 .next = NEXT(action_dup), 1369 .call = parse_vc, 1370 }, 1371 [ACTION_DUP_INDEX] = { 1372 .name = "index", 1373 .help = "queue index to duplicate packets to", 1374 .next = NEXT(action_dup, NEXT_ENTRY(UNSIGNED)), 1375 .args = ARGS(ARGS_ENTRY(struct rte_flow_action_dup, index)), 1376 .call = parse_vc_conf, 1377 }, 1378 [ACTION_RSS] = { 1379 .name = "rss", 1380 .help = "spread packets among several queues", 1381 .priv = PRIV_ACTION(RSS, ACTION_RSS_SIZE), 1382 .next = NEXT(action_rss), 1383 .call = parse_vc, 1384 }, 1385 [ACTION_RSS_QUEUES] = { 1386 .name = "queues", 1387 .help = "queue indices to use", 1388 .next = NEXT(action_rss, NEXT_ENTRY(ACTION_RSS_QUEUE)), 1389 .call = parse_vc_conf, 1390 }, 1391 [ACTION_RSS_QUEUE] = { 1392 .name = "{queue}", 1393 .help = "queue index", 1394 .call = parse_vc_action_rss_queue, 1395 .comp = comp_vc_action_rss_queue, 1396 }, 1397 [ACTION_PF] = { 1398 .name = "pf", 1399 .help = "redirect packets to physical device function", 1400 .priv = PRIV_ACTION(PF, 0), 1401 .next = NEXT(NEXT_ENTRY(ACTION_NEXT)), 1402 .call = parse_vc, 1403 }, 1404 [ACTION_VF] = { 1405 .name = "vf", 1406 .help = "redirect packets to virtual device function", 1407 .priv = PRIV_ACTION(VF, sizeof(struct rte_flow_action_vf)), 1408 .next = NEXT(action_vf), 1409 .call = parse_vc, 1410 }, 1411 [ACTION_VF_ORIGINAL] = { 1412 .name = "original", 1413 .help = "use original VF ID if possible", 1414 .next = NEXT(action_vf, NEXT_ENTRY(BOOLEAN)), 1415 .args = ARGS(ARGS_ENTRY_BF(struct rte_flow_action_vf, 1416 original, 1)), 1417 .call = parse_vc_conf, 1418 }, 1419 [ACTION_VF_ID] = { 1420 .name = "id", 1421 .help = "VF ID to redirect packets to", 1422 .next = NEXT(action_vf, NEXT_ENTRY(UNSIGNED)), 1423 .args = ARGS(ARGS_ENTRY(struct rte_flow_action_vf, id)), 1424 .call = parse_vc_conf, 1425 }, 1426 }; 1427 1428 /** Remove and return last entry from argument stack. */ 1429 static const struct arg * 1430 pop_args(struct context *ctx) 1431 { 1432 return ctx->args_num ? ctx->args[--ctx->args_num] : NULL; 1433 } 1434 1435 /** Add entry on top of the argument stack. */ 1436 static int 1437 push_args(struct context *ctx, const struct arg *arg) 1438 { 1439 if (ctx->args_num == CTX_STACK_SIZE) 1440 return -1; 1441 ctx->args[ctx->args_num++] = arg; 1442 return 0; 1443 } 1444 1445 /** Spread value into buffer according to bit-mask. */ 1446 static size_t 1447 arg_entry_bf_fill(void *dst, uintmax_t val, const struct arg *arg) 1448 { 1449 uint32_t i = arg->size; 1450 uint32_t end = 0; 1451 int sub = 1; 1452 int add = 0; 1453 size_t len = 0; 1454 1455 if (!arg->mask) 1456 return 0; 1457 #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN 1458 if (!arg->hton) { 1459 i = 0; 1460 end = arg->size; 1461 sub = 0; 1462 add = 1; 1463 } 1464 #endif 1465 while (i != end) { 1466 unsigned int shift = 0; 1467 uint8_t *buf = (uint8_t *)dst + arg->offset + (i -= sub); 1468 1469 for (shift = 0; arg->mask[i] >> shift; ++shift) { 1470 if (!(arg->mask[i] & (1 << shift))) 1471 continue; 1472 ++len; 1473 if (!dst) 1474 continue; 1475 *buf &= ~(1 << shift); 1476 *buf |= (val & 1) << shift; 1477 val >>= 1; 1478 } 1479 i += add; 1480 } 1481 return len; 1482 } 1483 1484 /** 1485 * Parse a prefix length and generate a bit-mask. 1486 * 1487 * Last argument (ctx->args) is retrieved to determine mask size, storage 1488 * location and whether the result must use network byte ordering. 1489 */ 1490 static int 1491 parse_prefix(struct context *ctx, const struct token *token, 1492 const char *str, unsigned int len, 1493 void *buf, unsigned int size) 1494 { 1495 const struct arg *arg = pop_args(ctx); 1496 static const uint8_t conv[] = "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe\xff"; 1497 char *end; 1498 uintmax_t u; 1499 unsigned int bytes; 1500 unsigned int extra; 1501 1502 (void)token; 1503 /* Argument is expected. */ 1504 if (!arg) 1505 return -1; 1506 errno = 0; 1507 u = strtoumax(str, &end, 0); 1508 if (errno || (size_t)(end - str) != len) 1509 goto error; 1510 if (arg->mask) { 1511 uintmax_t v = 0; 1512 1513 extra = arg_entry_bf_fill(NULL, 0, arg); 1514 if (u > extra) 1515 goto error; 1516 if (!ctx->object) 1517 return len; 1518 extra -= u; 1519 while (u--) 1520 (v <<= 1, v |= 1); 1521 v <<= extra; 1522 if (!arg_entry_bf_fill(ctx->object, v, arg) || 1523 !arg_entry_bf_fill(ctx->objmask, -1, arg)) 1524 goto error; 1525 return len; 1526 } 1527 bytes = u / 8; 1528 extra = u % 8; 1529 size = arg->size; 1530 if (bytes > size || bytes + !!extra > size) 1531 goto error; 1532 if (!ctx->object) 1533 return len; 1534 buf = (uint8_t *)ctx->object + arg->offset; 1535 #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN 1536 if (!arg->hton) { 1537 memset((uint8_t *)buf + size - bytes, 0xff, bytes); 1538 memset(buf, 0x00, size - bytes); 1539 if (extra) 1540 ((uint8_t *)buf)[size - bytes - 1] = conv[extra]; 1541 } else 1542 #endif 1543 { 1544 memset(buf, 0xff, bytes); 1545 memset((uint8_t *)buf + bytes, 0x00, size - bytes); 1546 if (extra) 1547 ((uint8_t *)buf)[bytes] = conv[extra]; 1548 } 1549 if (ctx->objmask) 1550 memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size); 1551 return len; 1552 error: 1553 push_args(ctx, arg); 1554 return -1; 1555 } 1556 1557 /** Default parsing function for token name matching. */ 1558 static int 1559 parse_default(struct context *ctx, const struct token *token, 1560 const char *str, unsigned int len, 1561 void *buf, unsigned int size) 1562 { 1563 (void)ctx; 1564 (void)buf; 1565 (void)size; 1566 if (strncmp(str, token->name, len)) 1567 return -1; 1568 return len; 1569 } 1570 1571 /** Parse flow command, initialize output buffer for subsequent tokens. */ 1572 static int 1573 parse_init(struct context *ctx, const struct token *token, 1574 const char *str, unsigned int len, 1575 void *buf, unsigned int size) 1576 { 1577 struct buffer *out = buf; 1578 1579 /* Token name must match. */ 1580 if (parse_default(ctx, token, str, len, NULL, 0) < 0) 1581 return -1; 1582 /* Nothing else to do if there is no buffer. */ 1583 if (!out) 1584 return len; 1585 /* Make sure buffer is large enough. */ 1586 if (size < sizeof(*out)) 1587 return -1; 1588 /* Initialize buffer. */ 1589 memset(out, 0x00, sizeof(*out)); 1590 memset((uint8_t *)out + sizeof(*out), 0x22, size - sizeof(*out)); 1591 ctx->objdata = 0; 1592 ctx->object = out; 1593 ctx->objmask = NULL; 1594 return len; 1595 } 1596 1597 /** Parse tokens for validate/create commands. */ 1598 static int 1599 parse_vc(struct context *ctx, const struct token *token, 1600 const char *str, unsigned int len, 1601 void *buf, unsigned int size) 1602 { 1603 struct buffer *out = buf; 1604 uint8_t *data; 1605 uint32_t data_size; 1606 1607 /* Token name must match. */ 1608 if (parse_default(ctx, token, str, len, NULL, 0) < 0) 1609 return -1; 1610 /* Nothing else to do if there is no buffer. */ 1611 if (!out) 1612 return len; 1613 if (!out->command) { 1614 if (ctx->curr != VALIDATE && ctx->curr != CREATE) 1615 return -1; 1616 if (sizeof(*out) > size) 1617 return -1; 1618 out->command = ctx->curr; 1619 ctx->objdata = 0; 1620 ctx->object = out; 1621 ctx->objmask = NULL; 1622 out->args.vc.data = (uint8_t *)out + size; 1623 return len; 1624 } 1625 ctx->objdata = 0; 1626 ctx->object = &out->args.vc.attr; 1627 ctx->objmask = NULL; 1628 switch (ctx->curr) { 1629 case GROUP: 1630 case PRIORITY: 1631 return len; 1632 case INGRESS: 1633 out->args.vc.attr.ingress = 1; 1634 return len; 1635 case EGRESS: 1636 out->args.vc.attr.egress = 1; 1637 return len; 1638 case PATTERN: 1639 out->args.vc.pattern = 1640 (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1), 1641 sizeof(double)); 1642 ctx->object = out->args.vc.pattern; 1643 ctx->objmask = NULL; 1644 return len; 1645 case ACTIONS: 1646 out->args.vc.actions = 1647 (void *)RTE_ALIGN_CEIL((uintptr_t) 1648 (out->args.vc.pattern + 1649 out->args.vc.pattern_n), 1650 sizeof(double)); 1651 ctx->object = out->args.vc.actions; 1652 ctx->objmask = NULL; 1653 return len; 1654 default: 1655 if (!token->priv) 1656 return -1; 1657 break; 1658 } 1659 if (!out->args.vc.actions) { 1660 const struct parse_item_priv *priv = token->priv; 1661 struct rte_flow_item *item = 1662 out->args.vc.pattern + out->args.vc.pattern_n; 1663 1664 data_size = priv->size * 3; /* spec, last, mask */ 1665 data = (void *)RTE_ALIGN_FLOOR((uintptr_t) 1666 (out->args.vc.data - data_size), 1667 sizeof(double)); 1668 if ((uint8_t *)item + sizeof(*item) > data) 1669 return -1; 1670 *item = (struct rte_flow_item){ 1671 .type = priv->type, 1672 }; 1673 ++out->args.vc.pattern_n; 1674 ctx->object = item; 1675 ctx->objmask = NULL; 1676 } else { 1677 const struct parse_action_priv *priv = token->priv; 1678 struct rte_flow_action *action = 1679 out->args.vc.actions + out->args.vc.actions_n; 1680 1681 data_size = priv->size; /* configuration */ 1682 data = (void *)RTE_ALIGN_FLOOR((uintptr_t) 1683 (out->args.vc.data - data_size), 1684 sizeof(double)); 1685 if ((uint8_t *)action + sizeof(*action) > data) 1686 return -1; 1687 *action = (struct rte_flow_action){ 1688 .type = priv->type, 1689 }; 1690 ++out->args.vc.actions_n; 1691 ctx->object = action; 1692 ctx->objmask = NULL; 1693 } 1694 memset(data, 0, data_size); 1695 out->args.vc.data = data; 1696 ctx->objdata = data_size; 1697 return len; 1698 } 1699 1700 /** Parse pattern item parameter type. */ 1701 static int 1702 parse_vc_spec(struct context *ctx, const struct token *token, 1703 const char *str, unsigned int len, 1704 void *buf, unsigned int size) 1705 { 1706 struct buffer *out = buf; 1707 struct rte_flow_item *item; 1708 uint32_t data_size; 1709 int index; 1710 int objmask = 0; 1711 1712 (void)size; 1713 /* Token name must match. */ 1714 if (parse_default(ctx, token, str, len, NULL, 0) < 0) 1715 return -1; 1716 /* Parse parameter types. */ 1717 switch (ctx->curr) { 1718 case ITEM_PARAM_IS: 1719 index = 0; 1720 objmask = 1; 1721 break; 1722 case ITEM_PARAM_SPEC: 1723 index = 0; 1724 break; 1725 case ITEM_PARAM_LAST: 1726 index = 1; 1727 break; 1728 case ITEM_PARAM_PREFIX: 1729 /* Modify next token to expect a prefix. */ 1730 if (ctx->next_num < 2) 1731 return -1; 1732 ctx->next[ctx->next_num - 2] = NEXT_ENTRY(PREFIX); 1733 /* Fall through. */ 1734 case ITEM_PARAM_MASK: 1735 index = 2; 1736 break; 1737 default: 1738 return -1; 1739 } 1740 /* Nothing else to do if there is no buffer. */ 1741 if (!out) 1742 return len; 1743 if (!out->args.vc.pattern_n) 1744 return -1; 1745 item = &out->args.vc.pattern[out->args.vc.pattern_n - 1]; 1746 data_size = ctx->objdata / 3; /* spec, last, mask */ 1747 /* Point to selected object. */ 1748 ctx->object = out->args.vc.data + (data_size * index); 1749 if (objmask) { 1750 ctx->objmask = out->args.vc.data + (data_size * 2); /* mask */ 1751 item->mask = ctx->objmask; 1752 } else 1753 ctx->objmask = NULL; 1754 /* Update relevant item pointer. */ 1755 *((const void **[]){ &item->spec, &item->last, &item->mask })[index] = 1756 ctx->object; 1757 return len; 1758 } 1759 1760 /** Parse action configuration field. */ 1761 static int 1762 parse_vc_conf(struct context *ctx, const struct token *token, 1763 const char *str, unsigned int len, 1764 void *buf, unsigned int size) 1765 { 1766 struct buffer *out = buf; 1767 struct rte_flow_action *action; 1768 1769 (void)size; 1770 /* Token name must match. */ 1771 if (parse_default(ctx, token, str, len, NULL, 0) < 0) 1772 return -1; 1773 /* Nothing else to do if there is no buffer. */ 1774 if (!out) 1775 return len; 1776 if (!out->args.vc.actions_n) 1777 return -1; 1778 action = &out->args.vc.actions[out->args.vc.actions_n - 1]; 1779 /* Point to selected object. */ 1780 ctx->object = out->args.vc.data; 1781 ctx->objmask = NULL; 1782 /* Update configuration pointer. */ 1783 action->conf = ctx->object; 1784 return len; 1785 } 1786 1787 /** 1788 * Parse queue field for RSS action. 1789 * 1790 * Valid tokens are queue indices and the "end" token. 1791 */ 1792 static int 1793 parse_vc_action_rss_queue(struct context *ctx, const struct token *token, 1794 const char *str, unsigned int len, 1795 void *buf, unsigned int size) 1796 { 1797 static const enum index next[] = NEXT_ENTRY(ACTION_RSS_QUEUE); 1798 int ret; 1799 int i; 1800 1801 (void)token; 1802 (void)buf; 1803 (void)size; 1804 if (ctx->curr != ACTION_RSS_QUEUE) 1805 return -1; 1806 i = ctx->objdata >> 16; 1807 if (!strncmp(str, "end", len)) { 1808 ctx->objdata &= 0xffff; 1809 return len; 1810 } 1811 if (i >= ACTION_RSS_NUM) 1812 return -1; 1813 if (push_args(ctx, ARGS_ENTRY(struct rte_flow_action_rss, queue[i]))) 1814 return -1; 1815 ret = parse_int(ctx, token, str, len, NULL, 0); 1816 if (ret < 0) { 1817 pop_args(ctx); 1818 return -1; 1819 } 1820 ++i; 1821 ctx->objdata = i << 16 | (ctx->objdata & 0xffff); 1822 /* Repeat token. */ 1823 if (ctx->next_num == RTE_DIM(ctx->next)) 1824 return -1; 1825 ctx->next[ctx->next_num++] = next; 1826 if (!ctx->object) 1827 return len; 1828 ((struct rte_flow_action_rss *)ctx->object)->num = i; 1829 return len; 1830 } 1831 1832 /** Parse tokens for destroy command. */ 1833 static int 1834 parse_destroy(struct context *ctx, const struct token *token, 1835 const char *str, unsigned int len, 1836 void *buf, unsigned int size) 1837 { 1838 struct buffer *out = buf; 1839 1840 /* Token name must match. */ 1841 if (parse_default(ctx, token, str, len, NULL, 0) < 0) 1842 return -1; 1843 /* Nothing else to do if there is no buffer. */ 1844 if (!out) 1845 return len; 1846 if (!out->command) { 1847 if (ctx->curr != DESTROY) 1848 return -1; 1849 if (sizeof(*out) > size) 1850 return -1; 1851 out->command = ctx->curr; 1852 ctx->objdata = 0; 1853 ctx->object = out; 1854 ctx->objmask = NULL; 1855 out->args.destroy.rule = 1856 (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1), 1857 sizeof(double)); 1858 return len; 1859 } 1860 if (((uint8_t *)(out->args.destroy.rule + out->args.destroy.rule_n) + 1861 sizeof(*out->args.destroy.rule)) > (uint8_t *)out + size) 1862 return -1; 1863 ctx->objdata = 0; 1864 ctx->object = out->args.destroy.rule + out->args.destroy.rule_n++; 1865 ctx->objmask = NULL; 1866 return len; 1867 } 1868 1869 /** Parse tokens for flush command. */ 1870 static int 1871 parse_flush(struct context *ctx, const struct token *token, 1872 const char *str, unsigned int len, 1873 void *buf, unsigned int size) 1874 { 1875 struct buffer *out = buf; 1876 1877 /* Token name must match. */ 1878 if (parse_default(ctx, token, str, len, NULL, 0) < 0) 1879 return -1; 1880 /* Nothing else to do if there is no buffer. */ 1881 if (!out) 1882 return len; 1883 if (!out->command) { 1884 if (ctx->curr != FLUSH) 1885 return -1; 1886 if (sizeof(*out) > size) 1887 return -1; 1888 out->command = ctx->curr; 1889 ctx->objdata = 0; 1890 ctx->object = out; 1891 ctx->objmask = NULL; 1892 } 1893 return len; 1894 } 1895 1896 /** Parse tokens for query command. */ 1897 static int 1898 parse_query(struct context *ctx, const struct token *token, 1899 const char *str, unsigned int len, 1900 void *buf, unsigned int size) 1901 { 1902 struct buffer *out = buf; 1903 1904 /* Token name must match. */ 1905 if (parse_default(ctx, token, str, len, NULL, 0) < 0) 1906 return -1; 1907 /* Nothing else to do if there is no buffer. */ 1908 if (!out) 1909 return len; 1910 if (!out->command) { 1911 if (ctx->curr != QUERY) 1912 return -1; 1913 if (sizeof(*out) > size) 1914 return -1; 1915 out->command = ctx->curr; 1916 ctx->objdata = 0; 1917 ctx->object = out; 1918 ctx->objmask = NULL; 1919 } 1920 return len; 1921 } 1922 1923 /** Parse action names. */ 1924 static int 1925 parse_action(struct context *ctx, const struct token *token, 1926 const char *str, unsigned int len, 1927 void *buf, unsigned int size) 1928 { 1929 struct buffer *out = buf; 1930 const struct arg *arg = pop_args(ctx); 1931 unsigned int i; 1932 1933 (void)size; 1934 /* Argument is expected. */ 1935 if (!arg) 1936 return -1; 1937 /* Parse action name. */ 1938 for (i = 0; next_action[i]; ++i) { 1939 const struct parse_action_priv *priv; 1940 1941 token = &token_list[next_action[i]]; 1942 if (strncmp(token->name, str, len)) 1943 continue; 1944 priv = token->priv; 1945 if (!priv) 1946 goto error; 1947 if (out) 1948 memcpy((uint8_t *)ctx->object + arg->offset, 1949 &priv->type, 1950 arg->size); 1951 return len; 1952 } 1953 error: 1954 push_args(ctx, arg); 1955 return -1; 1956 } 1957 1958 /** Parse tokens for list command. */ 1959 static int 1960 parse_list(struct context *ctx, const struct token *token, 1961 const char *str, unsigned int len, 1962 void *buf, unsigned int size) 1963 { 1964 struct buffer *out = buf; 1965 1966 /* Token name must match. */ 1967 if (parse_default(ctx, token, str, len, NULL, 0) < 0) 1968 return -1; 1969 /* Nothing else to do if there is no buffer. */ 1970 if (!out) 1971 return len; 1972 if (!out->command) { 1973 if (ctx->curr != LIST) 1974 return -1; 1975 if (sizeof(*out) > size) 1976 return -1; 1977 out->command = ctx->curr; 1978 ctx->objdata = 0; 1979 ctx->object = out; 1980 ctx->objmask = NULL; 1981 out->args.list.group = 1982 (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1), 1983 sizeof(double)); 1984 return len; 1985 } 1986 if (((uint8_t *)(out->args.list.group + out->args.list.group_n) + 1987 sizeof(*out->args.list.group)) > (uint8_t *)out + size) 1988 return -1; 1989 ctx->objdata = 0; 1990 ctx->object = out->args.list.group + out->args.list.group_n++; 1991 ctx->objmask = NULL; 1992 return len; 1993 } 1994 1995 /** 1996 * Parse signed/unsigned integers 8 to 64-bit long. 1997 * 1998 * Last argument (ctx->args) is retrieved to determine integer type and 1999 * storage location. 2000 */ 2001 static int 2002 parse_int(struct context *ctx, const struct token *token, 2003 const char *str, unsigned int len, 2004 void *buf, unsigned int size) 2005 { 2006 const struct arg *arg = pop_args(ctx); 2007 uintmax_t u; 2008 char *end; 2009 2010 (void)token; 2011 /* Argument is expected. */ 2012 if (!arg) 2013 return -1; 2014 errno = 0; 2015 u = arg->sign ? 2016 (uintmax_t)strtoimax(str, &end, 0) : 2017 strtoumax(str, &end, 0); 2018 if (errno || (size_t)(end - str) != len) 2019 goto error; 2020 if (!ctx->object) 2021 return len; 2022 if (arg->mask) { 2023 if (!arg_entry_bf_fill(ctx->object, u, arg) || 2024 !arg_entry_bf_fill(ctx->objmask, -1, arg)) 2025 goto error; 2026 return len; 2027 } 2028 buf = (uint8_t *)ctx->object + arg->offset; 2029 size = arg->size; 2030 objmask: 2031 switch (size) { 2032 case sizeof(uint8_t): 2033 *(uint8_t *)buf = u; 2034 break; 2035 case sizeof(uint16_t): 2036 *(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u; 2037 break; 2038 case sizeof(uint8_t [3]): 2039 #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN 2040 if (!arg->hton) { 2041 ((uint8_t *)buf)[0] = u; 2042 ((uint8_t *)buf)[1] = u >> 8; 2043 ((uint8_t *)buf)[2] = u >> 16; 2044 break; 2045 } 2046 #endif 2047 ((uint8_t *)buf)[0] = u >> 16; 2048 ((uint8_t *)buf)[1] = u >> 8; 2049 ((uint8_t *)buf)[2] = u; 2050 break; 2051 case sizeof(uint32_t): 2052 *(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u; 2053 break; 2054 case sizeof(uint64_t): 2055 *(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u; 2056 break; 2057 default: 2058 goto error; 2059 } 2060 if (ctx->objmask && buf != (uint8_t *)ctx->objmask + arg->offset) { 2061 u = -1; 2062 buf = (uint8_t *)ctx->objmask + arg->offset; 2063 goto objmask; 2064 } 2065 return len; 2066 error: 2067 push_args(ctx, arg); 2068 return -1; 2069 } 2070 2071 /** 2072 * Parse a string. 2073 * 2074 * Two arguments (ctx->args) are retrieved from the stack to store data and 2075 * its length (in that order). 2076 */ 2077 static int 2078 parse_string(struct context *ctx, const struct token *token, 2079 const char *str, unsigned int len, 2080 void *buf, unsigned int size) 2081 { 2082 const struct arg *arg_data = pop_args(ctx); 2083 const struct arg *arg_len = pop_args(ctx); 2084 char tmp[16]; /* Ought to be enough. */ 2085 int ret; 2086 2087 /* Arguments are expected. */ 2088 if (!arg_data) 2089 return -1; 2090 if (!arg_len) { 2091 push_args(ctx, arg_data); 2092 return -1; 2093 } 2094 size = arg_data->size; 2095 /* Bit-mask fill is not supported. */ 2096 if (arg_data->mask || size < len) 2097 goto error; 2098 if (!ctx->object) 2099 return len; 2100 /* Let parse_int() fill length information first. */ 2101 ret = snprintf(tmp, sizeof(tmp), "%u", len); 2102 if (ret < 0) 2103 goto error; 2104 push_args(ctx, arg_len); 2105 ret = parse_int(ctx, token, tmp, ret, NULL, 0); 2106 if (ret < 0) { 2107 pop_args(ctx); 2108 goto error; 2109 } 2110 buf = (uint8_t *)ctx->object + arg_data->offset; 2111 /* Output buffer is not necessarily NUL-terminated. */ 2112 memcpy(buf, str, len); 2113 memset((uint8_t *)buf + len, 0x55, size - len); 2114 if (ctx->objmask) 2115 memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff, len); 2116 return len; 2117 error: 2118 push_args(ctx, arg_len); 2119 push_args(ctx, arg_data); 2120 return -1; 2121 } 2122 2123 /** 2124 * Parse a MAC address. 2125 * 2126 * Last argument (ctx->args) is retrieved to determine storage size and 2127 * location. 2128 */ 2129 static int 2130 parse_mac_addr(struct context *ctx, const struct token *token, 2131 const char *str, unsigned int len, 2132 void *buf, unsigned int size) 2133 { 2134 const struct arg *arg = pop_args(ctx); 2135 struct ether_addr tmp; 2136 int ret; 2137 2138 (void)token; 2139 /* Argument is expected. */ 2140 if (!arg) 2141 return -1; 2142 size = arg->size; 2143 /* Bit-mask fill is not supported. */ 2144 if (arg->mask || size != sizeof(tmp)) 2145 goto error; 2146 ret = cmdline_parse_etheraddr(NULL, str, &tmp, size); 2147 if (ret < 0 || (unsigned int)ret != len) 2148 goto error; 2149 if (!ctx->object) 2150 return len; 2151 buf = (uint8_t *)ctx->object + arg->offset; 2152 memcpy(buf, &tmp, size); 2153 if (ctx->objmask) 2154 memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size); 2155 return len; 2156 error: 2157 push_args(ctx, arg); 2158 return -1; 2159 } 2160 2161 /** 2162 * Parse an IPv4 address. 2163 * 2164 * Last argument (ctx->args) is retrieved to determine storage size and 2165 * location. 2166 */ 2167 static int 2168 parse_ipv4_addr(struct context *ctx, const struct token *token, 2169 const char *str, unsigned int len, 2170 void *buf, unsigned int size) 2171 { 2172 const struct arg *arg = pop_args(ctx); 2173 char str2[len + 1]; 2174 struct in_addr tmp; 2175 int ret; 2176 2177 /* Argument is expected. */ 2178 if (!arg) 2179 return -1; 2180 size = arg->size; 2181 /* Bit-mask fill is not supported. */ 2182 if (arg->mask || size != sizeof(tmp)) 2183 goto error; 2184 /* Only network endian is supported. */ 2185 if (!arg->hton) 2186 goto error; 2187 memcpy(str2, str, len); 2188 str2[len] = '\0'; 2189 ret = inet_pton(AF_INET, str2, &tmp); 2190 if (ret != 1) { 2191 /* Attempt integer parsing. */ 2192 push_args(ctx, arg); 2193 return parse_int(ctx, token, str, len, buf, size); 2194 } 2195 if (!ctx->object) 2196 return len; 2197 buf = (uint8_t *)ctx->object + arg->offset; 2198 memcpy(buf, &tmp, size); 2199 if (ctx->objmask) 2200 memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size); 2201 return len; 2202 error: 2203 push_args(ctx, arg); 2204 return -1; 2205 } 2206 2207 /** 2208 * Parse an IPv6 address. 2209 * 2210 * Last argument (ctx->args) is retrieved to determine storage size and 2211 * location. 2212 */ 2213 static int 2214 parse_ipv6_addr(struct context *ctx, const struct token *token, 2215 const char *str, unsigned int len, 2216 void *buf, unsigned int size) 2217 { 2218 const struct arg *arg = pop_args(ctx); 2219 char str2[len + 1]; 2220 struct in6_addr tmp; 2221 int ret; 2222 2223 (void)token; 2224 /* Argument is expected. */ 2225 if (!arg) 2226 return -1; 2227 size = arg->size; 2228 /* Bit-mask fill is not supported. */ 2229 if (arg->mask || size != sizeof(tmp)) 2230 goto error; 2231 /* Only network endian is supported. */ 2232 if (!arg->hton) 2233 goto error; 2234 memcpy(str2, str, len); 2235 str2[len] = '\0'; 2236 ret = inet_pton(AF_INET6, str2, &tmp); 2237 if (ret != 1) 2238 goto error; 2239 if (!ctx->object) 2240 return len; 2241 buf = (uint8_t *)ctx->object + arg->offset; 2242 memcpy(buf, &tmp, size); 2243 if (ctx->objmask) 2244 memset((uint8_t *)ctx->objmask + arg->offset, 0xff, size); 2245 return len; 2246 error: 2247 push_args(ctx, arg); 2248 return -1; 2249 } 2250 2251 /** Boolean values (even indices stand for false). */ 2252 static const char *const boolean_name[] = { 2253 "0", "1", 2254 "false", "true", 2255 "no", "yes", 2256 "N", "Y", 2257 NULL, 2258 }; 2259 2260 /** 2261 * Parse a boolean value. 2262 * 2263 * Last argument (ctx->args) is retrieved to determine storage size and 2264 * location. 2265 */ 2266 static int 2267 parse_boolean(struct context *ctx, const struct token *token, 2268 const char *str, unsigned int len, 2269 void *buf, unsigned int size) 2270 { 2271 const struct arg *arg = pop_args(ctx); 2272 unsigned int i; 2273 int ret; 2274 2275 /* Argument is expected. */ 2276 if (!arg) 2277 return -1; 2278 for (i = 0; boolean_name[i]; ++i) 2279 if (!strncmp(str, boolean_name[i], len)) 2280 break; 2281 /* Process token as integer. */ 2282 if (boolean_name[i]) 2283 str = i & 1 ? "1" : "0"; 2284 push_args(ctx, arg); 2285 ret = parse_int(ctx, token, str, strlen(str), buf, size); 2286 return ret > 0 ? (int)len : ret; 2287 } 2288 2289 /** Parse port and update context. */ 2290 static int 2291 parse_port(struct context *ctx, const struct token *token, 2292 const char *str, unsigned int len, 2293 void *buf, unsigned int size) 2294 { 2295 struct buffer *out = &(struct buffer){ .port = 0 }; 2296 int ret; 2297 2298 if (buf) 2299 out = buf; 2300 else { 2301 ctx->objdata = 0; 2302 ctx->object = out; 2303 ctx->objmask = NULL; 2304 size = sizeof(*out); 2305 } 2306 ret = parse_int(ctx, token, str, len, out, size); 2307 if (ret >= 0) 2308 ctx->port = out->port; 2309 if (!buf) 2310 ctx->object = NULL; 2311 return ret; 2312 } 2313 2314 /** No completion. */ 2315 static int 2316 comp_none(struct context *ctx, const struct token *token, 2317 unsigned int ent, char *buf, unsigned int size) 2318 { 2319 (void)ctx; 2320 (void)token; 2321 (void)ent; 2322 (void)buf; 2323 (void)size; 2324 return 0; 2325 } 2326 2327 /** Complete boolean values. */ 2328 static int 2329 comp_boolean(struct context *ctx, const struct token *token, 2330 unsigned int ent, char *buf, unsigned int size) 2331 { 2332 unsigned int i; 2333 2334 (void)ctx; 2335 (void)token; 2336 for (i = 0; boolean_name[i]; ++i) 2337 if (buf && i == ent) 2338 return snprintf(buf, size, "%s", boolean_name[i]); 2339 if (buf) 2340 return -1; 2341 return i; 2342 } 2343 2344 /** Complete action names. */ 2345 static int 2346 comp_action(struct context *ctx, const struct token *token, 2347 unsigned int ent, char *buf, unsigned int size) 2348 { 2349 unsigned int i; 2350 2351 (void)ctx; 2352 (void)token; 2353 for (i = 0; next_action[i]; ++i) 2354 if (buf && i == ent) 2355 return snprintf(buf, size, "%s", 2356 token_list[next_action[i]].name); 2357 if (buf) 2358 return -1; 2359 return i; 2360 } 2361 2362 /** Complete available ports. */ 2363 static int 2364 comp_port(struct context *ctx, const struct token *token, 2365 unsigned int ent, char *buf, unsigned int size) 2366 { 2367 unsigned int i = 0; 2368 portid_t p; 2369 2370 (void)ctx; 2371 (void)token; 2372 FOREACH_PORT(p, ports) { 2373 if (buf && i == ent) 2374 return snprintf(buf, size, "%u", p); 2375 ++i; 2376 } 2377 if (buf) 2378 return -1; 2379 return i; 2380 } 2381 2382 /** Complete available rule IDs. */ 2383 static int 2384 comp_rule_id(struct context *ctx, const struct token *token, 2385 unsigned int ent, char *buf, unsigned int size) 2386 { 2387 unsigned int i = 0; 2388 struct rte_port *port; 2389 struct port_flow *pf; 2390 2391 (void)token; 2392 if (port_id_is_invalid(ctx->port, DISABLED_WARN) || 2393 ctx->port == (uint16_t)RTE_PORT_ALL) 2394 return -1; 2395 port = &ports[ctx->port]; 2396 for (pf = port->flow_list; pf != NULL; pf = pf->next) { 2397 if (buf && i == ent) 2398 return snprintf(buf, size, "%u", pf->id); 2399 ++i; 2400 } 2401 if (buf) 2402 return -1; 2403 return i; 2404 } 2405 2406 /** Complete queue field for RSS action. */ 2407 static int 2408 comp_vc_action_rss_queue(struct context *ctx, const struct token *token, 2409 unsigned int ent, char *buf, unsigned int size) 2410 { 2411 static const char *const str[] = { "", "end", NULL }; 2412 unsigned int i; 2413 2414 (void)ctx; 2415 (void)token; 2416 for (i = 0; str[i] != NULL; ++i) 2417 if (buf && i == ent) 2418 return snprintf(buf, size, "%s", str[i]); 2419 if (buf) 2420 return -1; 2421 return i; 2422 } 2423 2424 /** Internal context. */ 2425 static struct context cmd_flow_context; 2426 2427 /** Global parser instance (cmdline API). */ 2428 cmdline_parse_inst_t cmd_flow; 2429 2430 /** Initialize context. */ 2431 static void 2432 cmd_flow_context_init(struct context *ctx) 2433 { 2434 /* A full memset() is not necessary. */ 2435 ctx->curr = ZERO; 2436 ctx->prev = ZERO; 2437 ctx->next_num = 0; 2438 ctx->args_num = 0; 2439 ctx->reparse = 0; 2440 ctx->eol = 0; 2441 ctx->last = 0; 2442 ctx->port = 0; 2443 ctx->objdata = 0; 2444 ctx->object = NULL; 2445 ctx->objmask = NULL; 2446 } 2447 2448 /** Parse a token (cmdline API). */ 2449 static int 2450 cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result, 2451 unsigned int size) 2452 { 2453 struct context *ctx = &cmd_flow_context; 2454 const struct token *token; 2455 const enum index *list; 2456 int len; 2457 int i; 2458 2459 (void)hdr; 2460 /* Restart as requested. */ 2461 if (ctx->reparse) 2462 cmd_flow_context_init(ctx); 2463 token = &token_list[ctx->curr]; 2464 /* Check argument length. */ 2465 ctx->eol = 0; 2466 ctx->last = 1; 2467 for (len = 0; src[len]; ++len) 2468 if (src[len] == '#' || isspace(src[len])) 2469 break; 2470 if (!len) 2471 return -1; 2472 /* Last argument and EOL detection. */ 2473 for (i = len; src[i]; ++i) 2474 if (src[i] == '#' || src[i] == '\r' || src[i] == '\n') 2475 break; 2476 else if (!isspace(src[i])) { 2477 ctx->last = 0; 2478 break; 2479 } 2480 for (; src[i]; ++i) 2481 if (src[i] == '\r' || src[i] == '\n') { 2482 ctx->eol = 1; 2483 break; 2484 } 2485 /* Initialize context if necessary. */ 2486 if (!ctx->next_num) { 2487 if (!token->next) 2488 return 0; 2489 ctx->next[ctx->next_num++] = token->next[0]; 2490 } 2491 /* Process argument through candidates. */ 2492 ctx->prev = ctx->curr; 2493 list = ctx->next[ctx->next_num - 1]; 2494 for (i = 0; list[i]; ++i) { 2495 const struct token *next = &token_list[list[i]]; 2496 int tmp; 2497 2498 ctx->curr = list[i]; 2499 if (next->call) 2500 tmp = next->call(ctx, next, src, len, result, size); 2501 else 2502 tmp = parse_default(ctx, next, src, len, result, size); 2503 if (tmp == -1 || tmp != len) 2504 continue; 2505 token = next; 2506 break; 2507 } 2508 if (!list[i]) 2509 return -1; 2510 --ctx->next_num; 2511 /* Push subsequent tokens if any. */ 2512 if (token->next) 2513 for (i = 0; token->next[i]; ++i) { 2514 if (ctx->next_num == RTE_DIM(ctx->next)) 2515 return -1; 2516 ctx->next[ctx->next_num++] = token->next[i]; 2517 } 2518 /* Push arguments if any. */ 2519 if (token->args) 2520 for (i = 0; token->args[i]; ++i) { 2521 if (ctx->args_num == RTE_DIM(ctx->args)) 2522 return -1; 2523 ctx->args[ctx->args_num++] = token->args[i]; 2524 } 2525 return len; 2526 } 2527 2528 /** Return number of completion entries (cmdline API). */ 2529 static int 2530 cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr) 2531 { 2532 struct context *ctx = &cmd_flow_context; 2533 const struct token *token = &token_list[ctx->curr]; 2534 const enum index *list; 2535 int i; 2536 2537 (void)hdr; 2538 /* Tell cmd_flow_parse() that context must be reinitialized. */ 2539 ctx->reparse = 1; 2540 /* Count number of tokens in current list. */ 2541 if (ctx->next_num) 2542 list = ctx->next[ctx->next_num - 1]; 2543 else 2544 list = token->next[0]; 2545 for (i = 0; list[i]; ++i) 2546 ; 2547 if (!i) 2548 return 0; 2549 /* 2550 * If there is a single token, use its completion callback, otherwise 2551 * return the number of entries. 2552 */ 2553 token = &token_list[list[0]]; 2554 if (i == 1 && token->comp) { 2555 /* Save index for cmd_flow_get_help(). */ 2556 ctx->prev = list[0]; 2557 return token->comp(ctx, token, 0, NULL, 0); 2558 } 2559 return i; 2560 } 2561 2562 /** Return a completion entry (cmdline API). */ 2563 static int 2564 cmd_flow_complete_get_elt(cmdline_parse_token_hdr_t *hdr, int index, 2565 char *dst, unsigned int size) 2566 { 2567 struct context *ctx = &cmd_flow_context; 2568 const struct token *token = &token_list[ctx->curr]; 2569 const enum index *list; 2570 int i; 2571 2572 (void)hdr; 2573 /* Tell cmd_flow_parse() that context must be reinitialized. */ 2574 ctx->reparse = 1; 2575 /* Count number of tokens in current list. */ 2576 if (ctx->next_num) 2577 list = ctx->next[ctx->next_num - 1]; 2578 else 2579 list = token->next[0]; 2580 for (i = 0; list[i]; ++i) 2581 ; 2582 if (!i) 2583 return -1; 2584 /* If there is a single token, use its completion callback. */ 2585 token = &token_list[list[0]]; 2586 if (i == 1 && token->comp) { 2587 /* Save index for cmd_flow_get_help(). */ 2588 ctx->prev = list[0]; 2589 return token->comp(ctx, token, index, dst, size) < 0 ? -1 : 0; 2590 } 2591 /* Otherwise make sure the index is valid and use defaults. */ 2592 if (index >= i) 2593 return -1; 2594 token = &token_list[list[index]]; 2595 snprintf(dst, size, "%s", token->name); 2596 /* Save index for cmd_flow_get_help(). */ 2597 ctx->prev = list[index]; 2598 return 0; 2599 } 2600 2601 /** Populate help strings for current token (cmdline API). */ 2602 static int 2603 cmd_flow_get_help(cmdline_parse_token_hdr_t *hdr, char *dst, unsigned int size) 2604 { 2605 struct context *ctx = &cmd_flow_context; 2606 const struct token *token = &token_list[ctx->prev]; 2607 2608 (void)hdr; 2609 /* Tell cmd_flow_parse() that context must be reinitialized. */ 2610 ctx->reparse = 1; 2611 if (!size) 2612 return -1; 2613 /* Set token type and update global help with details. */ 2614 snprintf(dst, size, "%s", (token->type ? token->type : "TOKEN")); 2615 if (token->help) 2616 cmd_flow.help_str = token->help; 2617 else 2618 cmd_flow.help_str = token->name; 2619 return 0; 2620 } 2621 2622 /** Token definition template (cmdline API). */ 2623 static struct cmdline_token_hdr cmd_flow_token_hdr = { 2624 .ops = &(struct cmdline_token_ops){ 2625 .parse = cmd_flow_parse, 2626 .complete_get_nb = cmd_flow_complete_get_nb, 2627 .complete_get_elt = cmd_flow_complete_get_elt, 2628 .get_help = cmd_flow_get_help, 2629 }, 2630 .offset = 0, 2631 }; 2632 2633 /** Populate the next dynamic token. */ 2634 static void 2635 cmd_flow_tok(cmdline_parse_token_hdr_t **hdr, 2636 cmdline_parse_token_hdr_t *(*hdrs)[]) 2637 { 2638 struct context *ctx = &cmd_flow_context; 2639 2640 /* Always reinitialize context before requesting the first token. */ 2641 if (!(hdr - *hdrs)) 2642 cmd_flow_context_init(ctx); 2643 /* Return NULL when no more tokens are expected. */ 2644 if (!ctx->next_num && ctx->curr) { 2645 *hdr = NULL; 2646 return; 2647 } 2648 /* Determine if command should end here. */ 2649 if (ctx->eol && ctx->last && ctx->next_num) { 2650 const enum index *list = ctx->next[ctx->next_num - 1]; 2651 int i; 2652 2653 for (i = 0; list[i]; ++i) { 2654 if (list[i] != END) 2655 continue; 2656 *hdr = NULL; 2657 return; 2658 } 2659 } 2660 *hdr = &cmd_flow_token_hdr; 2661 } 2662 2663 /** Dispatch parsed buffer to function calls. */ 2664 static void 2665 cmd_flow_parsed(const struct buffer *in) 2666 { 2667 switch (in->command) { 2668 case VALIDATE: 2669 port_flow_validate(in->port, &in->args.vc.attr, 2670 in->args.vc.pattern, in->args.vc.actions); 2671 break; 2672 case CREATE: 2673 port_flow_create(in->port, &in->args.vc.attr, 2674 in->args.vc.pattern, in->args.vc.actions); 2675 break; 2676 case DESTROY: 2677 port_flow_destroy(in->port, in->args.destroy.rule_n, 2678 in->args.destroy.rule); 2679 break; 2680 case FLUSH: 2681 port_flow_flush(in->port); 2682 break; 2683 case QUERY: 2684 port_flow_query(in->port, in->args.query.rule, 2685 in->args.query.action); 2686 break; 2687 case LIST: 2688 port_flow_list(in->port, in->args.list.group_n, 2689 in->args.list.group); 2690 break; 2691 default: 2692 break; 2693 } 2694 } 2695 2696 /** Token generator and output processing callback (cmdline API). */ 2697 static void 2698 cmd_flow_cb(void *arg0, struct cmdline *cl, void *arg2) 2699 { 2700 if (cl == NULL) 2701 cmd_flow_tok(arg0, arg2); 2702 else 2703 cmd_flow_parsed(arg0); 2704 } 2705 2706 /** Global parser instance (cmdline API). */ 2707 cmdline_parse_inst_t cmd_flow = { 2708 .f = cmd_flow_cb, 2709 .data = NULL, /**< Unused. */ 2710 .help_str = NULL, /**< Updated by cmd_flow_get_help(). */ 2711 .tokens = { 2712 NULL, 2713 }, /**< Tokens are returned by cmd_flow_tok(). */ 2714 }; 2715