app/testpmd: add command for queue setup
[dpdk.git] / app / test-pmd / cmdline_flow.c
index 3e5803a..f0b4b7b 100644 (file)
@@ -1,34 +1,6 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright 2016 6WIND S.A.
- *   Copyright 2016 Mellanox.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of 6WIND S.A. nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2016 6WIND S.A.
+ * Copyright 2016 Mellanox Technologies, Ltd
  */
 
 #include <stddef.h>
@@ -80,6 +52,7 @@ enum index {
        FLUSH,
        QUERY,
        LIST,
+       ISOLATE,
 
        /* Destroy arguments. */
        DESTROY_RULE,
@@ -168,6 +141,15 @@ enum index {
        ITEM_MPLS_LABEL,
        ITEM_GRE,
        ITEM_GRE_PROTO,
+       ITEM_FUZZY,
+       ITEM_FUZZY_THRESH,
+       ITEM_GTP,
+       ITEM_GTP_TEID,
+       ITEM_GTPC,
+       ITEM_GTPU,
+       ITEM_GENEVE,
+       ITEM_GENEVE_VNI,
+       ITEM_GENEVE_PROTO,
 
        /* Validate/create actions. */
        ACTIONS,
@@ -185,12 +167,18 @@ enum index {
        ACTION_DUP,
        ACTION_DUP_INDEX,
        ACTION_RSS,
+       ACTION_RSS_TYPES,
+       ACTION_RSS_TYPE,
+       ACTION_RSS_KEY,
+       ACTION_RSS_KEY_LEN,
        ACTION_RSS_QUEUES,
        ACTION_RSS_QUEUE,
        ACTION_PF,
        ACTION_VF,
        ACTION_VF_ORIGINAL,
        ACTION_VF_ID,
+       ACTION_METER,
+       ACTION_METER_ID,
 };
 
 /** Size of pattern[] field in struct rte_flow_item_raw. */
@@ -200,13 +188,19 @@ enum index {
 #define ITEM_RAW_SIZE \
        (offsetof(struct rte_flow_item_raw, pattern) + ITEM_RAW_PATTERN_SIZE)
 
-/** Number of queue[] entries in struct rte_flow_action_rss. */
-#define ACTION_RSS_NUM 32
-
-/** Storage size for struct rte_flow_action_rss including queues. */
-#define ACTION_RSS_SIZE \
-       (offsetof(struct rte_flow_action_rss, queue) + \
-        sizeof(*((struct rte_flow_action_rss *)0)->queue) * ACTION_RSS_NUM)
+/** Maximum number of queue indices in struct rte_flow_action_rss. */
+#define ACTION_RSS_QUEUE_NUM 32
+
+/** Storage for struct rte_flow_action_rss including external data. */
+union action_rss_data {
+       struct rte_flow_action_rss conf;
+       struct {
+               uint8_t conf_data[offsetof(struct rte_flow_action_rss, queue)];
+               uint16_t queue[ACTION_RSS_QUEUE_NUM];
+               struct rte_eth_rss_conf rss_conf;
+               uint8_t rss_key[RSS_HASH_KEY_LENGTH];
+       } s;
+};
 
 /** Maximum number of subsequent tokens and arguments on the stack. */
 #define CTX_STACK_SIZE 16
@@ -221,10 +215,9 @@ struct context {
        enum index prev; /**< Index of the last token seen. */
        int next_num; /**< Number of entries in next[]. */
        int args_num; /**< Number of entries in args[]. */
-       uint32_t reparse:1; /**< Start over from the beginning. */
        uint32_t eol:1; /**< EOL has been detected. */
        uint32_t last:1; /**< No more arguments. */
-       uint16_t port; /**< Current port ID (for completions). */
+       portid_t port; /**< Current port ID (for completions). */
        uint32_t objdata; /**< Object-specific data. */
        void *object; /**< Address of current object for relative offsets. */
        void *objmask; /**< Object a full mask must be written to. */
@@ -234,6 +227,9 @@ struct context {
 struct arg {
        uint32_t hton:1; /**< Use network byte ordering. */
        uint32_t sign:1; /**< Value is signed. */
+       uint32_t bounded:1; /**< Value is bounded. */
+       uintmax_t min; /**< Minimum value if bounded. */
+       uintmax_t max; /**< Maximum value if bounded. */
        uint32_t offset; /**< Relative offset from ctx->object. */
        uint32_t size; /**< Field size. */
        const uint8_t *mask; /**< Bit-mask to use instead of offset/size. */
@@ -333,6 +329,23 @@ struct token {
                .size = (sz), \
        })
 
+/** Static initializer for ARGS() with arbitrary offset and size. */
+#define ARGS_ENTRY_ARB(o, s) \
+       (&(const struct arg){ \
+               .offset = (o), \
+               .size = (s), \
+       })
+
+/** Same as ARGS_ENTRY_ARB() with bounded values. */
+#define ARGS_ENTRY_ARB_BOUNDED(o, s, i, a) \
+       (&(const struct arg){ \
+               .bounded = 1, \
+               .min = (i), \
+               .max = (a), \
+               .offset = (o), \
+               .size = (s), \
+       })
+
 /** Same as ARGS_ENTRY() using network byte ordering. */
 #define ARGS_ENTRY_HTON(s, f) \
        (&(const struct arg){ \
@@ -344,7 +357,7 @@ struct token {
 /** Parser output buffer layout expected by cmd_flow_parsed(). */
 struct buffer {
        enum index command; /**< Flow command. */
-       uint16_t port; /**< Affected port ID. */
+       portid_t port; /**< Affected port ID. */
        union {
                struct {
                        struct rte_flow_attr attr;
@@ -366,6 +379,9 @@ struct buffer {
                        uint32_t *group;
                        uint32_t group_n;
                } list; /**< List arguments. */
+               struct {
+                       int set;
+               } isolate; /**< Isolated mode arguments. */
        } args; /**< Command arguments. */
 };
 
@@ -445,6 +461,17 @@ static const enum index next_item[] = {
        ITEM_NVGRE,
        ITEM_MPLS,
        ITEM_GRE,
+       ITEM_FUZZY,
+       ITEM_GTP,
+       ITEM_GTPC,
+       ITEM_GTPU,
+       ITEM_GENEVE,
+       ZERO,
+};
+
+static const enum index item_fuzzy[] = {
+       ITEM_FUZZY_THRESH,
+       ITEM_NEXT,
        ZERO,
 };
 
@@ -576,6 +603,19 @@ static const enum index item_gre[] = {
        ZERO,
 };
 
+static const enum index item_gtp[] = {
+       ITEM_GTP_TEID,
+       ITEM_NEXT,
+       ZERO,
+};
+
+static const enum index item_geneve[] = {
+       ITEM_GENEVE_VNI,
+       ITEM_GENEVE_PROTO,
+       ITEM_NEXT,
+       ZERO,
+};
+
 static const enum index next_action[] = {
        ACTION_END,
        ACTION_VOID,
@@ -589,6 +629,7 @@ static const enum index next_action[] = {
        ACTION_RSS,
        ACTION_PF,
        ACTION_VF,
+       ACTION_METER,
        ZERO,
 };
 
@@ -611,6 +652,9 @@ static const enum index action_dup[] = {
 };
 
 static const enum index action_rss[] = {
+       ACTION_RSS_TYPES,
+       ACTION_RSS_KEY,
+       ACTION_RSS_KEY_LEN,
        ACTION_RSS_QUEUES,
        ACTION_NEXT,
        ZERO,
@@ -623,6 +667,12 @@ static const enum index action_vf[] = {
        ZERO,
 };
 
+static const enum index action_meter[] = {
+       ACTION_METER_ID,
+       ACTION_NEXT,
+       ZERO,
+};
+
 static int parse_init(struct context *, const struct token *,
                      const char *, unsigned int,
                      void *, unsigned int);
@@ -633,6 +683,12 @@ static int parse_vc_spec(struct context *, const struct token *,
                         const char *, unsigned int, void *, unsigned int);
 static int parse_vc_conf(struct context *, const struct token *,
                         const char *, unsigned int, void *, unsigned int);
+static int parse_vc_action_rss(struct context *, const struct token *,
+                              const char *, unsigned int, void *,
+                              unsigned int);
+static int parse_vc_action_rss_type(struct context *, const struct token *,
+                                   const char *, unsigned int, void *,
+                                   unsigned int);
 static int parse_vc_action_rss_queue(struct context *, const struct token *,
                                     const char *, unsigned int, void *,
                                     unsigned int);
@@ -651,6 +707,9 @@ static int parse_action(struct context *, const struct token *,
 static int parse_list(struct context *, const struct token *,
                      const char *, unsigned int,
                      void *, unsigned int);
+static int parse_isolate(struct context *, const struct token *,
+                        const char *, unsigned int,
+                        void *, unsigned int);
 static int parse_int(struct context *, const struct token *,
                     const char *, unsigned int,
                     void *, unsigned int);
@@ -685,6 +744,8 @@ static int comp_port(struct context *, const struct token *,
                     unsigned int, char *, unsigned int);
 static int comp_rule_id(struct context *, const struct token *,
                        unsigned int, char *, unsigned int);
+static int comp_vc_action_rss_type(struct context *, const struct token *,
+                                  unsigned int, char *, unsigned int);
 static int comp_vc_action_rss_queue(struct context *, const struct token *,
                                    unsigned int, char *, unsigned int);
 
@@ -797,7 +858,8 @@ static const struct token token_list[] = {
                              DESTROY,
                              FLUSH,
                              LIST,
-                             QUERY)),
+                             QUERY,
+                             ISOLATE)),
                .call = parse_init,
        },
        /* Sub-level commands. */
@@ -847,6 +909,15 @@ static const struct token token_list[] = {
                .args = ARGS(ARGS_ENTRY(struct buffer, port)),
                .call = parse_list,
        },
+       [ISOLATE] = {
+               .name = "isolate",
+               .help = "restrict ingress traffic to the defined flow rules",
+               .next = NEXT(NEXT_ENTRY(BOOLEAN),
+                            NEXT_ENTRY(PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.isolate.set),
+                            ARGS_ENTRY(struct buffer, port)),
+               .call = parse_isolate,
+       },
        /* Destroy arguments. */
        [DESTROY_RULE] = {
                .name = "rule",
@@ -1381,6 +1452,69 @@ static const struct token token_list[] = {
                .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_gre,
                                             protocol)),
        },
+       [ITEM_FUZZY] = {
+               .name = "fuzzy",
+               .help = "fuzzy pattern match, expect faster than default",
+               .priv = PRIV_ITEM(FUZZY,
+                               sizeof(struct rte_flow_item_fuzzy)),
+               .next = NEXT(item_fuzzy),
+               .call = parse_vc,
+       },
+       [ITEM_FUZZY_THRESH] = {
+               .name = "thresh",
+               .help = "match accuracy threshold",
+               .next = NEXT(item_fuzzy, NEXT_ENTRY(UNSIGNED), item_param),
+               .args = ARGS(ARGS_ENTRY(struct rte_flow_item_fuzzy,
+                                       thresh)),
+       },
+       [ITEM_GTP] = {
+               .name = "gtp",
+               .help = "match GTP header",
+               .priv = PRIV_ITEM(GTP, sizeof(struct rte_flow_item_gtp)),
+               .next = NEXT(item_gtp),
+               .call = parse_vc,
+       },
+       [ITEM_GTP_TEID] = {
+               .name = "teid",
+               .help = "tunnel endpoint identifier",
+               .next = NEXT(item_gtp, NEXT_ENTRY(UNSIGNED), item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_gtp, teid)),
+       },
+       [ITEM_GTPC] = {
+               .name = "gtpc",
+               .help = "match GTP header",
+               .priv = PRIV_ITEM(GTPC, sizeof(struct rte_flow_item_gtp)),
+               .next = NEXT(item_gtp),
+               .call = parse_vc,
+       },
+       [ITEM_GTPU] = {
+               .name = "gtpu",
+               .help = "match GTP header",
+               .priv = PRIV_ITEM(GTPU, sizeof(struct rte_flow_item_gtp)),
+               .next = NEXT(item_gtp),
+               .call = parse_vc,
+       },
+       [ITEM_GENEVE] = {
+               .name = "geneve",
+               .help = "match GENEVE header",
+               .priv = PRIV_ITEM(GENEVE, sizeof(struct rte_flow_item_geneve)),
+               .next = NEXT(item_geneve),
+               .call = parse_vc,
+       },
+       [ITEM_GENEVE_VNI] = {
+               .name = "vni",
+               .help = "virtual network identifier",
+               .next = NEXT(item_geneve, NEXT_ENTRY(UNSIGNED), item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_geneve, vni)),
+       },
+       [ITEM_GENEVE_PROTO] = {
+               .name = "protocol",
+               .help = "GENEVE protocol type",
+               .next = NEXT(item_geneve, NEXT_ENTRY(UNSIGNED), item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_geneve,
+                                            protocol)),
+       },
+
        /* Validate/create actions. */
        [ACTIONS] = {
                .name = "actions",
@@ -1480,9 +1614,46 @@ static const struct token token_list[] = {
        [ACTION_RSS] = {
                .name = "rss",
                .help = "spread packets among several queues",
-               .priv = PRIV_ACTION(RSS, ACTION_RSS_SIZE),
+               .priv = PRIV_ACTION(RSS, sizeof(union action_rss_data)),
                .next = NEXT(action_rss),
-               .call = parse_vc,
+               .call = parse_vc_action_rss,
+       },
+       [ACTION_RSS_TYPES] = {
+               .name = "types",
+               .help = "RSS hash types",
+               .next = NEXT(action_rss, NEXT_ENTRY(ACTION_RSS_TYPE)),
+       },
+       [ACTION_RSS_TYPE] = {
+               .name = "{type}",
+               .help = "RSS hash type",
+               .call = parse_vc_action_rss_type,
+               .comp = comp_vc_action_rss_type,
+       },
+       [ACTION_RSS_KEY] = {
+               .name = "key",
+               .help = "RSS hash key",
+               .next = NEXT(action_rss, NEXT_ENTRY(STRING)),
+               .args = ARGS(ARGS_ENTRY_ARB
+                            (((uintptr_t)&((union action_rss_data *)0)->
+                              s.rss_conf.rss_key_len),
+                             sizeof(((struct rte_eth_rss_conf *)0)->
+                                    rss_key_len)),
+                            ARGS_ENTRY_ARB
+                            (((uintptr_t)((union action_rss_data *)0)->
+                              s.rss_key),
+                             RSS_HASH_KEY_LENGTH)),
+       },
+       [ACTION_RSS_KEY_LEN] = {
+               .name = "key_len",
+               .help = "RSS hash key length in bytes",
+               .next = NEXT(action_rss, NEXT_ENTRY(UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY_ARB_BOUNDED
+                            (((uintptr_t)&((union action_rss_data *)0)->
+                              s.rss_conf.rss_key_len),
+                             sizeof(((struct rte_eth_rss_conf *)0)->
+                                    rss_key_len),
+                             0,
+                             RSS_HASH_KEY_LENGTH)),
        },
        [ACTION_RSS_QUEUES] = {
                .name = "queues",
@@ -1525,6 +1696,21 @@ static const struct token token_list[] = {
                .args = ARGS(ARGS_ENTRY(struct rte_flow_action_vf, id)),
                .call = parse_vc_conf,
        },
+       [ACTION_METER] = {
+               .name = "meter",
+               .help = "meter the directed packets at given id",
+               .priv = PRIV_ACTION(METER,
+                                   sizeof(struct rte_flow_action_meter)),
+               .next = NEXT(action_meter),
+               .call = parse_vc,
+       },
+       [ACTION_METER_ID] = {
+               .name = "mtr_id",
+               .help = "meter id to use",
+               .next = NEXT(action_meter, NEXT_ENTRY(UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct rte_flow_action_meter, mtr_id)),
+               .call = parse_vc_conf,
+       },
 };
 
 /** Remove and return last entry from argument stack. */
@@ -1583,6 +1769,19 @@ arg_entry_bf_fill(void *dst, uintmax_t val, const struct arg *arg)
        return len;
 }
 
+/** Compare a string with a partial one of a given length. */
+static int
+strcmp_partial(const char *full, const char *partial, size_t partial_len)
+{
+       int r = strncmp(full, partial, partial_len);
+
+       if (r)
+               return r;
+       if (strlen(full) <= partial_len)
+               return 0;
+       return full[partial_len];
+}
+
 /**
  * Parse a prefix length and generate a bit-mask.
  *
@@ -1665,7 +1864,7 @@ parse_default(struct context *ctx, const struct token *token,
        (void)ctx;
        (void)buf;
        (void)size;
-       if (strncmp(str, token->name, len))
+       if (strcmp_partial(token->name, str, len))
                return -1;
        return len;
 }
@@ -1788,6 +1987,7 @@ parse_vc(struct context *ctx, const struct token *token,
                        return -1;
                *action = (struct rte_flow_action){
                        .type = priv->type,
+                       .conf = data_size ? data : NULL,
                };
                ++out->args.vc.actions_n;
                ctx->object = action;
@@ -1868,7 +2068,6 @@ parse_vc_conf(struct context *ctx, const struct token *token,
              void *buf, unsigned int size)
 {
        struct buffer *out = buf;
-       struct rte_flow_action *action;
 
        (void)size;
        /* Token name must match. */
@@ -1877,14 +2076,108 @@ parse_vc_conf(struct context *ctx, const struct token *token,
        /* Nothing else to do if there is no buffer. */
        if (!out)
                return len;
+       /* Point to selected object. */
+       ctx->object = out->args.vc.data;
+       ctx->objmask = NULL;
+       return len;
+}
+
+/** Parse RSS action. */
+static int
+parse_vc_action_rss(struct context *ctx, const struct token *token,
+                   const char *str, unsigned int len,
+                   void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+       struct rte_flow_action *action;
+       union action_rss_data *action_rss_data;
+       unsigned int i;
+       int ret;
+
+       ret = parse_vc(ctx, token, str, len, buf, size);
+       if (ret < 0)
+               return ret;
+       /* Nothing else to do if there is no buffer. */
+       if (!out)
+               return ret;
        if (!out->args.vc.actions_n)
                return -1;
        action = &out->args.vc.actions[out->args.vc.actions_n - 1];
        /* Point to selected object. */
        ctx->object = out->args.vc.data;
        ctx->objmask = NULL;
-       /* Update configuration pointer. */
-       action->conf = ctx->object;
+       /* Set up default configuration. */
+       action_rss_data = ctx->object;
+       *action_rss_data = (union action_rss_data){
+               .conf = (struct rte_flow_action_rss){
+                       .rss_conf = &action_rss_data->s.rss_conf,
+                       .num = RTE_MIN(nb_rxq, ACTION_RSS_QUEUE_NUM),
+               },
+       };
+       action_rss_data->s.rss_conf = (struct rte_eth_rss_conf){
+               .rss_key = action_rss_data->s.rss_key,
+               .rss_key_len = sizeof(action_rss_data->s.rss_key),
+               .rss_hf = rss_hf,
+       };
+       strncpy((void *)action_rss_data->s.rss_key,
+               "testpmd's default RSS hash key",
+               sizeof(action_rss_data->s.rss_key));
+       for (i = 0; i < action_rss_data->conf.num; ++i)
+               action_rss_data->conf.queue[i] = i;
+       if (!port_id_is_invalid(ctx->port, DISABLED_WARN) &&
+           ctx->port != (portid_t)RTE_PORT_ALL) {
+               struct rte_eth_dev_info info;
+
+               rte_eth_dev_info_get(ctx->port, &info);
+               action_rss_data->s.rss_conf.rss_key_len =
+                       RTE_MIN(sizeof(action_rss_data->s.rss_key),
+                               info.hash_key_size);
+       }
+       action->conf = &action_rss_data->conf;
+       return ret;
+}
+
+/**
+ * Parse type field for RSS action.
+ *
+ * Valid tokens are type field names and the "end" token.
+ */
+static int
+parse_vc_action_rss_type(struct context *ctx, const struct token *token,
+                         const char *str, unsigned int len,
+                         void *buf, unsigned int size)
+{
+       static const enum index next[] = NEXT_ENTRY(ACTION_RSS_TYPE);
+       union action_rss_data *action_rss_data;
+       unsigned int i;
+
+       (void)token;
+       (void)buf;
+       (void)size;
+       if (ctx->curr != ACTION_RSS_TYPE)
+               return -1;
+       if (!(ctx->objdata >> 16) && ctx->object) {
+               action_rss_data = ctx->object;
+               action_rss_data->s.rss_conf.rss_hf = 0;
+       }
+       if (!strcmp_partial("end", str, len)) {
+               ctx->objdata &= 0xffff;
+               return len;
+       }
+       for (i = 0; rss_type_table[i].str; ++i)
+               if (!strcmp_partial(rss_type_table[i].str, str, len))
+                       break;
+       if (!rss_type_table[i].str)
+               return -1;
+       ctx->objdata = 1 << 16 | (ctx->objdata & 0xffff);
+       /* Repeat token. */
+       if (ctx->next_num == RTE_DIM(ctx->next))
+               return -1;
+       ctx->next[ctx->next_num++] = next;
+       if (!ctx->object)
+               return len;
+       action_rss_data = ctx->object;
+       action_rss_data->s.rss_conf.rss_hf |= rss_type_table[i].rss_type;
        return len;
 }
 
@@ -1899,6 +2192,7 @@ parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
                          void *buf, unsigned int size)
 {
        static const enum index next[] = NEXT_ENTRY(ACTION_RSS_QUEUE);
+       union action_rss_data *action_rss_data;
        int ret;
        int i;
 
@@ -1908,13 +2202,17 @@ parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
        if (ctx->curr != ACTION_RSS_QUEUE)
                return -1;
        i = ctx->objdata >> 16;
-       if (!strncmp(str, "end", len)) {
+       if (!strcmp_partial("end", str, len)) {
                ctx->objdata &= 0xffff;
                return len;
        }
-       if (i >= ACTION_RSS_NUM)
+       if (i >= ACTION_RSS_QUEUE_NUM)
                return -1;
-       if (push_args(ctx, ARGS_ENTRY(struct rte_flow_action_rss, queue[i])))
+       if (push_args(ctx,
+                     ARGS_ENTRY_ARB(offsetof(struct rte_flow_action_rss,
+                                             queue) +
+                                    i * sizeof(action_rss_data->s.queue[i]),
+                                    sizeof(action_rss_data->s.queue[i]))))
                return -1;
        ret = parse_int(ctx, token, str, len, NULL, 0);
        if (ret < 0) {
@@ -1929,7 +2227,8 @@ parse_vc_action_rss_queue(struct context *ctx, const struct token *token,
        ctx->next[ctx->next_num++] = next;
        if (!ctx->object)
                return len;
-       ((struct rte_flow_action_rss *)ctx->object)->num = i;
+       action_rss_data = ctx->object;
+       action_rss_data->conf.num = i;
        return len;
 }
 
@@ -2043,7 +2342,7 @@ parse_action(struct context *ctx, const struct token *token,
                const struct parse_action_priv *priv;
 
                token = &token_list[next_action[i]];
-               if (strncmp(token->name, str, len))
+               if (strcmp_partial(token->name, str, len))
                        continue;
                priv = token->priv;
                if (!priv)
@@ -2096,6 +2395,33 @@ parse_list(struct context *ctx, const struct token *token,
        return len;
 }
 
+/** Parse tokens for isolate command. */
+static int
+parse_isolate(struct context *ctx, const struct token *token,
+             const char *str, unsigned int len,
+             void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+
+       /* Token name must match. */
+       if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+               return -1;
+       /* Nothing else to do if there is no buffer. */
+       if (!out)
+               return len;
+       if (!out->command) {
+               if (ctx->curr != ISOLATE)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+       }
+       return len;
+}
+
 /**
  * Parse signed/unsigned integers 8 to 64-bit long.
  *
@@ -2121,6 +2447,11 @@ parse_int(struct context *ctx, const struct token *token,
                strtoumax(str, &end, 0);
        if (errno || (size_t)(end - str) != len)
                goto error;
+       if (arg->bounded &&
+           ((arg->sign && ((intmax_t)u < (intmax_t)arg->min ||
+                           (intmax_t)u > (intmax_t)arg->max)) ||
+            (!arg->sign && (u < arg->min || u > arg->max))))
+               goto error;
        if (!ctx->object)
                return len;
        if (arg->mask) {
@@ -2214,7 +2545,7 @@ parse_string(struct context *ctx, const struct token *token,
        buf = (uint8_t *)ctx->object + arg_data->offset;
        /* Output buffer is not necessarily NUL-terminated. */
        memcpy(buf, str, len);
-       memset((uint8_t *)buf + len, 0x55, size - len);
+       memset((uint8_t *)buf + len, 0x00, size - len);
        if (ctx->objmask)
                memset((uint8_t *)ctx->objmask + arg_data->offset, 0xff, len);
        return len;
@@ -2361,6 +2692,7 @@ static const char *const boolean_name[] = {
        "false", "true",
        "no", "yes",
        "N", "Y",
+       "off", "on",
        NULL,
 };
 
@@ -2383,7 +2715,7 @@ parse_boolean(struct context *ctx, const struct token *token,
        if (!arg)
                return -1;
        for (i = 0; boolean_name[i]; ++i)
-               if (!strncmp(str, boolean_name[i], len))
+               if (!strcmp_partial(boolean_name[i], str, len))
                        break;
        /* Process token as integer. */
        if (boolean_name[i])
@@ -2497,7 +2829,7 @@ comp_rule_id(struct context *ctx, const struct token *token,
 
        (void)token;
        if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
-           ctx->port == (uint16_t)RTE_PORT_ALL)
+           ctx->port == (portid_t)RTE_PORT_ALL)
                return -1;
        port = &ports[ctx->port];
        for (pf = port->flow_list; pf != NULL; pf = pf->next) {
@@ -2510,22 +2842,40 @@ comp_rule_id(struct context *ctx, const struct token *token,
        return i;
 }
 
+/** Complete type field for RSS action. */
+static int
+comp_vc_action_rss_type(struct context *ctx, const struct token *token,
+                       unsigned int ent, char *buf, unsigned int size)
+{
+       unsigned int i;
+
+       (void)ctx;
+       (void)token;
+       for (i = 0; rss_type_table[i].str; ++i)
+               ;
+       if (!buf)
+               return i + 1;
+       if (ent < i)
+               return snprintf(buf, size, "%s", rss_type_table[ent].str);
+       if (ent == i)
+               return snprintf(buf, size, "end");
+       return -1;
+}
+
 /** Complete queue field for RSS action. */
 static int
 comp_vc_action_rss_queue(struct context *ctx, const struct token *token,
                         unsigned int ent, char *buf, unsigned int size)
 {
-       static const char *const str[] = { "", "end", NULL };
-       unsigned int i;
-
        (void)ctx;
        (void)token;
-       for (i = 0; str[i] != NULL; ++i)
-               if (buf && i == ent)
-                       return snprintf(buf, size, "%s", str[i]);
-       if (buf)
-               return -1;
-       return i;
+       if (!buf)
+               return nb_rxq + 1;
+       if (ent < nb_rxq)
+               return snprintf(buf, size, "%u", ent);
+       if (ent == nb_rxq)
+               return snprintf(buf, size, "end");
+       return -1;
 }
 
 /** Internal context. */
@@ -2543,7 +2893,6 @@ cmd_flow_context_init(struct context *ctx)
        ctx->prev = ZERO;
        ctx->next_num = 0;
        ctx->args_num = 0;
-       ctx->reparse = 0;
        ctx->eol = 0;
        ctx->last = 0;
        ctx->port = 0;
@@ -2564,9 +2913,6 @@ cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
        int i;
 
        (void)hdr;
-       /* Restart as requested. */
-       if (ctx->reparse)
-               cmd_flow_context_init(ctx);
        token = &token_list[ctx->curr];
        /* Check argument length. */
        ctx->eol = 0;
@@ -2642,8 +2988,6 @@ cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr)
        int i;
 
        (void)hdr;
-       /* Tell cmd_flow_parse() that context must be reinitialized. */
-       ctx->reparse = 1;
        /* Count number of tokens in current list. */
        if (ctx->next_num)
                list = ctx->next[ctx->next_num - 1];
@@ -2677,8 +3021,6 @@ cmd_flow_complete_get_elt(cmdline_parse_token_hdr_t *hdr, int index,
        int i;
 
        (void)hdr;
-       /* Tell cmd_flow_parse() that context must be reinitialized. */
-       ctx->reparse = 1;
        /* Count number of tokens in current list. */
        if (ctx->next_num)
                list = ctx->next[ctx->next_num - 1];
@@ -2713,8 +3055,6 @@ cmd_flow_get_help(cmdline_parse_token_hdr_t *hdr, char *dst, unsigned int size)
        const struct token *token = &token_list[ctx->prev];
 
        (void)hdr;
-       /* Tell cmd_flow_parse() that context must be reinitialized. */
-       ctx->reparse = 1;
        if (!size)
                return -1;
        /* Set token type and update global help with details. */
@@ -2740,12 +3080,12 @@ static struct cmdline_token_hdr cmd_flow_token_hdr = {
 /** Populate the next dynamic token. */
 static void
 cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
-            cmdline_parse_token_hdr_t *(*hdrs)[])
+            cmdline_parse_token_hdr_t **hdr_inst)
 {
        struct context *ctx = &cmd_flow_context;
 
        /* Always reinitialize context before requesting the first token. */
-       if (!(hdr - *hdrs))
+       if (!(hdr_inst - cmd_flow.tokens))
                cmd_flow_context_init(ctx);
        /* Return NULL when no more tokens are expected. */
        if (!ctx->next_num && ctx->curr) {
@@ -2795,6 +3135,9 @@ cmd_flow_parsed(const struct buffer *in)
                port_flow_list(in->port, in->args.list.group_n,
                               in->args.list.group);
                break;
+       case ISOLATE:
+               port_flow_isolate(in->port, in->args.isolate.set);
+               break;
        default:
                break;
        }