test/bonding: fix RSS test when disable RSS
[dpdk.git] / app / test-pmd / cmdline_flow.c
index e0c79d5..cfd55c5 100644 (file)
@@ -56,6 +56,10 @@ enum index {
        COMMON_POLICY_ID,
        COMMON_FLEX_HANDLE,
        COMMON_FLEX_TOKEN,
+       COMMON_PATTERN_TEMPLATE_ID,
+       COMMON_ACTIONS_TEMPLATE_ID,
+       COMMON_TABLE_ID,
+       COMMON_QUEUE_ID,
 
        /* TOP-level command. */
        ADD,
@@ -72,6 +76,11 @@ enum index {
        /* Top-level command. */
        FLOW,
        /* Sub-level commands. */
+       INFO,
+       CONFIGURE,
+       PATTERN_TEMPLATE,
+       ACTIONS_TEMPLATE,
+       TABLE,
        INDIRECT_ACTION,
        VALIDATE,
        CREATE,
@@ -84,12 +93,94 @@ enum index {
        ISOLATE,
        TUNNEL,
        FLEX,
+       QUEUE,
+       PUSH,
+       PULL,
 
        /* Flex arguments */
        FLEX_ITEM_INIT,
        FLEX_ITEM_CREATE,
        FLEX_ITEM_DESTROY,
 
+       /* Pattern template arguments. */
+       PATTERN_TEMPLATE_CREATE,
+       PATTERN_TEMPLATE_DESTROY,
+       PATTERN_TEMPLATE_CREATE_ID,
+       PATTERN_TEMPLATE_DESTROY_ID,
+       PATTERN_TEMPLATE_RELAXED_MATCHING,
+       PATTERN_TEMPLATE_INGRESS,
+       PATTERN_TEMPLATE_EGRESS,
+       PATTERN_TEMPLATE_TRANSFER,
+       PATTERN_TEMPLATE_SPEC,
+
+       /* Actions template arguments. */
+       ACTIONS_TEMPLATE_CREATE,
+       ACTIONS_TEMPLATE_DESTROY,
+       ACTIONS_TEMPLATE_CREATE_ID,
+       ACTIONS_TEMPLATE_DESTROY_ID,
+       ACTIONS_TEMPLATE_INGRESS,
+       ACTIONS_TEMPLATE_EGRESS,
+       ACTIONS_TEMPLATE_TRANSFER,
+       ACTIONS_TEMPLATE_SPEC,
+       ACTIONS_TEMPLATE_MASK,
+
+       /* Queue arguments. */
+       QUEUE_CREATE,
+       QUEUE_DESTROY,
+       QUEUE_INDIRECT_ACTION,
+
+       /* Queue create arguments. */
+       QUEUE_CREATE_ID,
+       QUEUE_CREATE_POSTPONE,
+       QUEUE_TEMPLATE_TABLE,
+       QUEUE_PATTERN_TEMPLATE,
+       QUEUE_ACTIONS_TEMPLATE,
+       QUEUE_SPEC,
+
+       /* Queue destroy arguments. */
+       QUEUE_DESTROY_ID,
+       QUEUE_DESTROY_POSTPONE,
+
+       /* Queue indirect action arguments */
+       QUEUE_INDIRECT_ACTION_CREATE,
+       QUEUE_INDIRECT_ACTION_UPDATE,
+       QUEUE_INDIRECT_ACTION_DESTROY,
+
+       /* Queue indirect action create arguments */
+       QUEUE_INDIRECT_ACTION_CREATE_ID,
+       QUEUE_INDIRECT_ACTION_INGRESS,
+       QUEUE_INDIRECT_ACTION_EGRESS,
+       QUEUE_INDIRECT_ACTION_TRANSFER,
+       QUEUE_INDIRECT_ACTION_CREATE_POSTPONE,
+       QUEUE_INDIRECT_ACTION_SPEC,
+
+       /* Queue indirect action update arguments */
+       QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+
+       /* Queue indirect action destroy arguments */
+       QUEUE_INDIRECT_ACTION_DESTROY_ID,
+       QUEUE_INDIRECT_ACTION_DESTROY_POSTPONE,
+
+       /* Push arguments. */
+       PUSH_QUEUE,
+
+       /* Pull arguments. */
+       PULL_QUEUE,
+
+       /* Table arguments. */
+       TABLE_CREATE,
+       TABLE_DESTROY,
+       TABLE_CREATE_ID,
+       TABLE_DESTROY_ID,
+       TABLE_GROUP,
+       TABLE_PRIORITY,
+       TABLE_INGRESS,
+       TABLE_EGRESS,
+       TABLE_TRANSFER,
+       TABLE_RULES_NUMBER,
+       TABLE_PATTERN_TEMPLATE,
+       TABLE_ACTIONS_TEMPLATE,
+
        /* Tunnel arguments. */
        TUNNEL_CREATE,
        TUNNEL_CREATE_TYPE,
@@ -122,6 +213,13 @@ enum index {
        DUMP_ALL,
        DUMP_ONE,
 
+       /* Configure arguments */
+       CONFIG_QUEUES_NUMBER,
+       CONFIG_QUEUES_SIZE,
+       CONFIG_COUNTERS_NUMBER,
+       CONFIG_AGING_OBJECTS_NUMBER,
+       CONFIG_METERS_NUMBER,
+
        /* Indirect action arguments */
        INDIRECT_ACTION_CREATE,
        INDIRECT_ACTION_UPDATE,
@@ -272,6 +370,10 @@ enum index {
        ITEM_META_DATA,
        ITEM_GRE_KEY,
        ITEM_GRE_KEY_VALUE,
+       ITEM_GRE_OPTION,
+       ITEM_GRE_OPTION_CHECKSUM,
+       ITEM_GRE_OPTION_KEY,
+       ITEM_GRE_OPTION_SEQUENCE,
        ITEM_GTP_PSC,
        ITEM_GTP_PSC_QFI,
        ITEM_GTP_PSC_PDU_T,
@@ -693,7 +795,8 @@ static const char *const modify_field_ids[] = {
        "tcp_seq_num", "tcp_ack_num", "tcp_flags",
        "udp_port_src", "udp_port_dst",
        "vxlan_vni", "geneve_vni", "gtp_teid",
-       "tag", "mark", "meta", "pointer", "value", NULL
+       "tag", "mark", "meta", "pointer", "value",
+       "ipv4_ecn", "ipv6_ecn", NULL
 };
 
 /** Maximum number of subsequent tokens and arguments on the stack. */
@@ -863,7 +966,30 @@ struct token {
 struct buffer {
        enum index command; /**< Flow command. */
        portid_t port; /**< Affected port ID. */
+       queueid_t queue; /** Async queue ID. */
+       bool postpone; /** Postpone async operation */
        union {
+               struct {
+                       struct rte_flow_port_attr port_attr;
+                       uint32_t nb_queue;
+                       struct rte_flow_queue_attr queue_attr;
+               } configure; /**< Configuration arguments. */
+               struct {
+                       uint32_t *template_id;
+                       uint32_t template_id_n;
+               } templ_destroy; /**< Template destroy arguments. */
+               struct {
+                       uint32_t id;
+                       struct rte_flow_template_table_attr attr;
+                       uint32_t *pat_templ_id;
+                       uint32_t pat_templ_id_n;
+                       uint32_t *act_templ_id;
+                       uint32_t act_templ_id_n;
+               } table; /**< Table arguments. */
+               struct {
+                       uint32_t *table_id;
+                       uint32_t table_id_n;
+               } table_destroy; /**< Template destroy arguments. */
                struct {
                        uint32_t *action_id;
                        uint32_t action_id_n;
@@ -872,10 +998,14 @@ struct buffer {
                        uint32_t action_id;
                } ia; /* Indirect action query arguments */
                struct {
+                       uint32_t table_id;
+                       uint32_t pat_templ_id;
+                       uint32_t act_templ_id;
                        struct rte_flow_attr attr;
                        struct tunnel_ops tunnel_ops;
                        struct rte_flow_item *pattern;
                        struct rte_flow_action *actions;
+                       struct rte_flow_action *masks;
                        uint32_t pattern_n;
                        uint32_t actions_n;
                        uint8_t *data;
@@ -945,6 +1075,128 @@ static const enum index next_flex_item[] = {
        ZERO,
 };
 
+static const enum index next_config_attr[] = {
+       CONFIG_QUEUES_NUMBER,
+       CONFIG_QUEUES_SIZE,
+       CONFIG_COUNTERS_NUMBER,
+       CONFIG_AGING_OBJECTS_NUMBER,
+       CONFIG_METERS_NUMBER,
+       END,
+       ZERO,
+};
+
+static const enum index next_pt_subcmd[] = {
+       PATTERN_TEMPLATE_CREATE,
+       PATTERN_TEMPLATE_DESTROY,
+       ZERO,
+};
+
+static const enum index next_pt_attr[] = {
+       PATTERN_TEMPLATE_CREATE_ID,
+       PATTERN_TEMPLATE_RELAXED_MATCHING,
+       PATTERN_TEMPLATE_INGRESS,
+       PATTERN_TEMPLATE_EGRESS,
+       PATTERN_TEMPLATE_TRANSFER,
+       PATTERN_TEMPLATE_SPEC,
+       ZERO,
+};
+
+static const enum index next_pt_destroy_attr[] = {
+       PATTERN_TEMPLATE_DESTROY_ID,
+       END,
+       ZERO,
+};
+
+static const enum index next_at_subcmd[] = {
+       ACTIONS_TEMPLATE_CREATE,
+       ACTIONS_TEMPLATE_DESTROY,
+       ZERO,
+};
+
+static const enum index next_at_attr[] = {
+       ACTIONS_TEMPLATE_CREATE_ID,
+       ACTIONS_TEMPLATE_INGRESS,
+       ACTIONS_TEMPLATE_EGRESS,
+       ACTIONS_TEMPLATE_TRANSFER,
+       ACTIONS_TEMPLATE_SPEC,
+       ZERO,
+};
+
+static const enum index next_at_destroy_attr[] = {
+       ACTIONS_TEMPLATE_DESTROY_ID,
+       END,
+       ZERO,
+};
+
+static const enum index next_table_subcmd[] = {
+       TABLE_CREATE,
+       TABLE_DESTROY,
+       ZERO,
+};
+
+static const enum index next_table_attr[] = {
+       TABLE_CREATE_ID,
+       TABLE_GROUP,
+       TABLE_PRIORITY,
+       TABLE_INGRESS,
+       TABLE_EGRESS,
+       TABLE_TRANSFER,
+       TABLE_RULES_NUMBER,
+       TABLE_PATTERN_TEMPLATE,
+       TABLE_ACTIONS_TEMPLATE,
+       END,
+       ZERO,
+};
+
+static const enum index next_table_destroy_attr[] = {
+       TABLE_DESTROY_ID,
+       END,
+       ZERO,
+};
+
+static const enum index next_queue_subcmd[] = {
+       QUEUE_CREATE,
+       QUEUE_DESTROY,
+       QUEUE_INDIRECT_ACTION,
+       ZERO,
+};
+
+static const enum index next_queue_destroy_attr[] = {
+       QUEUE_DESTROY_ID,
+       END,
+       ZERO,
+};
+
+static const enum index next_qia_subcmd[] = {
+       QUEUE_INDIRECT_ACTION_CREATE,
+       QUEUE_INDIRECT_ACTION_UPDATE,
+       QUEUE_INDIRECT_ACTION_DESTROY,
+       ZERO,
+};
+
+static const enum index next_qia_create_attr[] = {
+       QUEUE_INDIRECT_ACTION_CREATE_ID,
+       QUEUE_INDIRECT_ACTION_INGRESS,
+       QUEUE_INDIRECT_ACTION_EGRESS,
+       QUEUE_INDIRECT_ACTION_TRANSFER,
+       QUEUE_INDIRECT_ACTION_CREATE_POSTPONE,
+       QUEUE_INDIRECT_ACTION_SPEC,
+       ZERO,
+};
+
+static const enum index next_qia_update_attr[] = {
+       QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE,
+       QUEUE_INDIRECT_ACTION_SPEC,
+       ZERO,
+};
+
+static const enum index next_qia_destroy_attr[] = {
+       QUEUE_INDIRECT_ACTION_DESTROY_POSTPONE,
+       QUEUE_INDIRECT_ACTION_DESTROY_ID,
+       END,
+       ZERO,
+};
+
 static const enum index next_ia_create_attr[] = {
        INDIRECT_ACTION_CREATE_ID,
        INDIRECT_ACTION_INGRESS,
@@ -1060,6 +1312,7 @@ static const enum index next_item[] = {
        ITEM_ICMP6_ND_OPT_TLA_ETH,
        ITEM_META,
        ITEM_GRE_KEY,
+       ITEM_GRE_OPTION,
        ITEM_GTP_PSC,
        ITEM_PPPOES,
        ITEM_PPPOED,
@@ -1251,6 +1504,14 @@ static const enum index item_gre_key[] = {
        ZERO,
 };
 
+static const enum index item_gre_option[] = {
+       ITEM_GRE_OPTION_CHECKSUM,
+       ITEM_GRE_OPTION_KEY,
+       ITEM_GRE_OPTION_SEQUENCE,
+       ITEM_NEXT,
+       ZERO,
+};
+
 static const enum index item_gtp[] = {
        ITEM_GTP_FLAGS,
        ITEM_GTP_MSG_TYPE,
@@ -2032,6 +2293,38 @@ static int parse_aged(struct context *, const struct token *,
 static int parse_isolate(struct context *, const struct token *,
                         const char *, unsigned int,
                         void *, unsigned int);
+static int parse_configure(struct context *, const struct token *,
+                          const char *, unsigned int,
+                          void *, unsigned int);
+static int parse_template(struct context *, const struct token *,
+                         const char *, unsigned int,
+                         void *, unsigned int);
+static int parse_template_destroy(struct context *, const struct token *,
+                                 const char *, unsigned int,
+                                 void *, unsigned int);
+static int parse_table(struct context *, const struct token *,
+                      const char *, unsigned int, void *, unsigned int);
+static int parse_table_destroy(struct context *, const struct token *,
+                              const char *, unsigned int,
+                              void *, unsigned int);
+static int parse_qo(struct context *, const struct token *,
+                   const char *, unsigned int,
+                   void *, unsigned int);
+static int parse_qo_destroy(struct context *, const struct token *,
+                           const char *, unsigned int,
+                           void *, unsigned int);
+static int parse_qia(struct context *, const struct token *,
+                    const char *, unsigned int,
+                    void *, unsigned int);
+static int parse_qia_destroy(struct context *, const struct token *,
+                            const char *, unsigned int,
+                            void *, unsigned int);
+static int parse_push(struct context *, const struct token *,
+                     const char *, unsigned int,
+                     void *, unsigned int);
+static int parse_pull(struct context *, const struct token *,
+                     const char *, unsigned int,
+                     void *, unsigned int);
 static int parse_tunnel(struct context *, const struct token *,
                        const char *, unsigned int,
                        void *, unsigned int);
@@ -2101,6 +2394,14 @@ static int comp_set_modify_field_op(struct context *, const struct token *,
                              unsigned int, char *, unsigned int);
 static int comp_set_modify_field_id(struct context *, const struct token *,
                              unsigned int, char *, unsigned int);
+static int comp_pattern_template_id(struct context *, const struct token *,
+                                   unsigned int, char *, unsigned int);
+static int comp_actions_template_id(struct context *, const struct token *,
+                                   unsigned int, char *, unsigned int);
+static int comp_table_id(struct context *, const struct token *,
+                        unsigned int, char *, unsigned int);
+static int comp_queue_id(struct context *, const struct token *,
+                        unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -2251,13 +2552,46 @@ static const struct token token_list[] = {
                .call = parse_flex_handle,
                .comp = comp_none,
        },
+       [COMMON_PATTERN_TEMPLATE_ID] = {
+               .name = "{pattern_template_id}",
+               .type = "PATTERN_TEMPLATE_ID",
+               .help = "pattern template id",
+               .call = parse_int,
+               .comp = comp_pattern_template_id,
+       },
+       [COMMON_ACTIONS_TEMPLATE_ID] = {
+               .name = "{actions_template_id}",
+               .type = "ACTIONS_TEMPLATE_ID",
+               .help = "actions template id",
+               .call = parse_int,
+               .comp = comp_actions_template_id,
+       },
+       [COMMON_TABLE_ID] = {
+               .name = "{table_id}",
+               .type = "TABLE_ID",
+               .help = "table id",
+               .call = parse_int,
+               .comp = comp_table_id,
+       },
+       [COMMON_QUEUE_ID] = {
+               .name = "{queue_id}",
+               .type = "QUEUE_ID",
+               .help = "queue id",
+               .call = parse_int,
+               .comp = comp_queue_id,
+       },
        /* Top-level command. */
        [FLOW] = {
                .name = "flow",
                .type = "{command} {port_id} [{arg} [...]]",
                .help = "manage ingress/egress flow rules",
                .next = NEXT(NEXT_ENTRY
-                            (INDIRECT_ACTION,
+                            (INFO,
+                             CONFIGURE,
+                             PATTERN_TEMPLATE,
+                             ACTIONS_TEMPLATE,
+                             TABLE,
+                             INDIRECT_ACTION,
                              VALIDATE,
                              CREATE,
                              DESTROY,
@@ -2268,10 +2602,511 @@ static const struct token token_list[] = {
                              QUERY,
                              ISOLATE,
                              TUNNEL,
-                             FLEX)),
+                             FLEX,
+                             QUEUE,
+                             PUSH,
+                             PULL)),
                .call = parse_init,
        },
        /* Top-level command. */
+       [INFO] = {
+               .name = "info",
+               .help = "get information about flow engine",
+               .next = NEXT(NEXT_ENTRY(END),
+                            NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_configure,
+       },
+       /* Top-level command. */
+       [CONFIGURE] = {
+               .name = "configure",
+               .help = "configure flow engine",
+               .next = NEXT(next_config_attr,
+                            NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_configure,
+       },
+       /* Configure arguments. */
+       [CONFIG_QUEUES_NUMBER] = {
+               .name = "queues_number",
+               .help = "number of queues",
+               .next = NEXT(next_config_attr,
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.configure.nb_queue)),
+       },
+       [CONFIG_QUEUES_SIZE] = {
+               .name = "queues_size",
+               .help = "number of elements in queues",
+               .next = NEXT(next_config_attr,
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.configure.queue_attr.size)),
+       },
+       [CONFIG_COUNTERS_NUMBER] = {
+               .name = "counters_number",
+               .help = "number of counters",
+               .next = NEXT(next_config_attr,
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.configure.port_attr.nb_counters)),
+       },
+       [CONFIG_AGING_OBJECTS_NUMBER] = {
+               .name = "aging_counters_number",
+               .help = "number of aging objects",
+               .next = NEXT(next_config_attr,
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.configure.port_attr.nb_aging_objects)),
+       },
+       [CONFIG_METERS_NUMBER] = {
+               .name = "meters_number",
+               .help = "number of meters",
+               .next = NEXT(next_config_attr,
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.configure.port_attr.nb_meters)),
+       },
+       /* Top-level command. */
+       [PATTERN_TEMPLATE] = {
+               .name = "pattern_template",
+               .type = "{command} {port_id} [{arg} [...]]",
+               .help = "manage pattern templates",
+               .next = NEXT(next_pt_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_template,
+       },
+       /* Sub-level commands. */
+       [PATTERN_TEMPLATE_CREATE] = {
+               .name = "create",
+               .help = "create pattern template",
+               .next = NEXT(next_pt_attr),
+               .call = parse_template,
+       },
+       [PATTERN_TEMPLATE_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy pattern template",
+               .next = NEXT(NEXT_ENTRY(PATTERN_TEMPLATE_DESTROY_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_template_destroy,
+       },
+       /* Pattern template arguments. */
+       [PATTERN_TEMPLATE_CREATE_ID] = {
+               .name = "pattern_template_id",
+               .help = "specify a pattern template id to create",
+               .next = NEXT(next_pt_attr,
+                            NEXT_ENTRY(COMMON_PATTERN_TEMPLATE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.pat_templ_id)),
+       },
+       [PATTERN_TEMPLATE_DESTROY_ID] = {
+               .name = "pattern_template",
+               .help = "specify a pattern template id to destroy",
+               .next = NEXT(next_pt_destroy_attr,
+                            NEXT_ENTRY(COMMON_PATTERN_TEMPLATE_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.templ_destroy.template_id)),
+               .call = parse_template_destroy,
+       },
+       [PATTERN_TEMPLATE_RELAXED_MATCHING] = {
+               .name = "relaxed",
+               .help = "is matching relaxed",
+               .next = NEXT(next_pt_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY_BF(struct buffer,
+                            args.vc.attr.reserved, 1)),
+       },
+       [PATTERN_TEMPLATE_INGRESS] = {
+               .name = "ingress",
+               .help = "attribute pattern to ingress",
+               .next = NEXT(next_pt_attr),
+               .call = parse_template,
+       },
+       [PATTERN_TEMPLATE_EGRESS] = {
+               .name = "egress",
+               .help = "attribute pattern to egress",
+               .next = NEXT(next_pt_attr),
+               .call = parse_template,
+       },
+       [PATTERN_TEMPLATE_TRANSFER] = {
+               .name = "transfer",
+               .help = "attribute pattern to transfer",
+               .next = NEXT(next_pt_attr),
+               .call = parse_template,
+       },
+       [PATTERN_TEMPLATE_SPEC] = {
+               .name = "template",
+               .help = "specify item to create pattern template",
+               .next = NEXT(next_item),
+       },
+       /* Top-level command. */
+       [ACTIONS_TEMPLATE] = {
+               .name = "actions_template",
+               .type = "{command} {port_id} [{arg} [...]]",
+               .help = "manage actions templates",
+               .next = NEXT(next_at_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_template,
+       },
+       /* Sub-level commands. */
+       [ACTIONS_TEMPLATE_CREATE] = {
+               .name = "create",
+               .help = "create actions template",
+               .next = NEXT(next_at_attr),
+               .call = parse_template,
+       },
+       [ACTIONS_TEMPLATE_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy actions template",
+               .next = NEXT(NEXT_ENTRY(ACTIONS_TEMPLATE_DESTROY_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_template_destroy,
+       },
+       /* Actions template arguments. */
+       [ACTIONS_TEMPLATE_CREATE_ID] = {
+               .name = "actions_template_id",
+               .help = "specify an actions template id to create",
+               .next = NEXT(NEXT_ENTRY(ACTIONS_TEMPLATE_MASK),
+                            NEXT_ENTRY(ACTIONS_TEMPLATE_SPEC),
+                            NEXT_ENTRY(COMMON_ACTIONS_TEMPLATE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.act_templ_id)),
+       },
+       [ACTIONS_TEMPLATE_DESTROY_ID] = {
+               .name = "actions_template",
+               .help = "specify an actions template id to destroy",
+               .next = NEXT(next_at_destroy_attr,
+                            NEXT_ENTRY(COMMON_ACTIONS_TEMPLATE_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.templ_destroy.template_id)),
+               .call = parse_template_destroy,
+       },
+       [ACTIONS_TEMPLATE_INGRESS] = {
+               .name = "ingress",
+               .help = "attribute actions to ingress",
+               .next = NEXT(next_at_attr),
+               .call = parse_template,
+       },
+       [ACTIONS_TEMPLATE_EGRESS] = {
+               .name = "egress",
+               .help = "attribute actions to egress",
+               .next = NEXT(next_at_attr),
+               .call = parse_template,
+       },
+       [ACTIONS_TEMPLATE_TRANSFER] = {
+               .name = "transfer",
+               .help = "attribute actions to transfer",
+               .next = NEXT(next_at_attr),
+               .call = parse_template,
+       },
+       [ACTIONS_TEMPLATE_SPEC] = {
+               .name = "template",
+               .help = "specify action to create actions template",
+               .next = NEXT(next_action),
+               .call = parse_template,
+       },
+       [ACTIONS_TEMPLATE_MASK] = {
+               .name = "mask",
+               .help = "specify action mask to create actions template",
+               .next = NEXT(next_action),
+               .call = parse_template,
+       },
+       /* Top-level command. */
+       [TABLE] = {
+               .name = "template_table",
+               .type = "{command} {port_id} [{arg} [...]]",
+               .help = "manage template tables",
+               .next = NEXT(next_table_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_table,
+       },
+       /* Sub-level commands. */
+       [TABLE_CREATE] = {
+               .name = "create",
+               .help = "create template table",
+               .next = NEXT(next_table_attr),
+               .call = parse_table,
+       },
+       [TABLE_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy template table",
+               .next = NEXT(NEXT_ENTRY(TABLE_DESTROY_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_table_destroy,
+       },
+       /* Table  arguments. */
+       [TABLE_CREATE_ID] = {
+               .name = "table_id",
+               .help = "specify table id to create",
+               .next = NEXT(next_table_attr,
+                            NEXT_ENTRY(COMMON_TABLE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.table.id)),
+       },
+       [TABLE_DESTROY_ID] = {
+               .name = "table",
+               .help = "specify table id to destroy",
+               .next = NEXT(next_table_destroy_attr,
+                            NEXT_ENTRY(COMMON_TABLE_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.table_destroy.table_id)),
+               .call = parse_table_destroy,
+       },
+       [TABLE_GROUP] = {
+               .name = "group",
+               .help = "specify a group",
+               .next = NEXT(next_table_attr, NEXT_ENTRY(COMMON_GROUP_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.table.attr.flow_attr.group)),
+       },
+       [TABLE_PRIORITY] = {
+               .name = "priority",
+               .help = "specify a priority level",
+               .next = NEXT(next_table_attr, NEXT_ENTRY(COMMON_PRIORITY_LEVEL)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.table.attr.flow_attr.priority)),
+       },
+       [TABLE_EGRESS] = {
+               .name = "egress",
+               .help = "affect rule to egress",
+               .next = NEXT(next_table_attr),
+               .call = parse_table,
+       },
+       [TABLE_INGRESS] = {
+               .name = "ingress",
+               .help = "affect rule to ingress",
+               .next = NEXT(next_table_attr),
+               .call = parse_table,
+       },
+       [TABLE_TRANSFER] = {
+               .name = "transfer",
+               .help = "affect rule to transfer",
+               .next = NEXT(next_table_attr),
+               .call = parse_table,
+       },
+       [TABLE_RULES_NUMBER] = {
+               .name = "rules_number",
+               .help = "number of rules in table",
+               .next = NEXT(next_table_attr,
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.table.attr.nb_flows)),
+       },
+       [TABLE_PATTERN_TEMPLATE] = {
+               .name = "pattern_template",
+               .help = "specify pattern template id",
+               .next = NEXT(next_table_attr,
+                            NEXT_ENTRY(COMMON_PATTERN_TEMPLATE_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.table.pat_templ_id)),
+               .call = parse_table,
+       },
+       [TABLE_ACTIONS_TEMPLATE] = {
+               .name = "actions_template",
+               .help = "specify actions template id",
+               .next = NEXT(next_table_attr,
+                            NEXT_ENTRY(COMMON_ACTIONS_TEMPLATE_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.table.act_templ_id)),
+               .call = parse_table,
+       },
+       /* Top-level command. */
+       [QUEUE] = {
+               .name = "queue",
+               .help = "queue a flow rule operation",
+               .next = NEXT(next_queue_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_qo,
+       },
+       /* Sub-level commands. */
+       [QUEUE_CREATE] = {
+               .name = "create",
+               .help = "create a flow rule",
+               .next = NEXT(NEXT_ENTRY(QUEUE_TEMPLATE_TABLE),
+                            NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+               .call = parse_qo,
+       },
+       [QUEUE_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy a flow rule",
+               .next = NEXT(NEXT_ENTRY(QUEUE_DESTROY_ID),
+                            NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+               .call = parse_qo_destroy,
+       },
+       [QUEUE_INDIRECT_ACTION] = {
+               .name = "indirect_action",
+               .help = "queue indirect actions",
+               .next = NEXT(next_qia_subcmd, NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+               .call = parse_qia,
+       },
+       /* Queue  arguments. */
+       [QUEUE_TEMPLATE_TABLE] = {
+               .name = "template table",
+               .help = "specify table id",
+               .next = NEXT(NEXT_ENTRY(QUEUE_PATTERN_TEMPLATE),
+                            NEXT_ENTRY(COMMON_TABLE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.vc.table_id)),
+               .call = parse_qo,
+       },
+       [QUEUE_PATTERN_TEMPLATE] = {
+               .name = "pattern_template",
+               .help = "specify pattern template index",
+               .next = NEXT(NEXT_ENTRY(QUEUE_ACTIONS_TEMPLATE),
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.vc.pat_templ_id)),
+               .call = parse_qo,
+       },
+       [QUEUE_ACTIONS_TEMPLATE] = {
+               .name = "actions_template",
+               .help = "specify actions template index",
+               .next = NEXT(NEXT_ENTRY(QUEUE_CREATE_POSTPONE),
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.vc.act_templ_id)),
+               .call = parse_qo,
+       },
+       [QUEUE_CREATE_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone create operation",
+               .next = NEXT(NEXT_ENTRY(ITEM_PATTERN),
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+               .call = parse_qo,
+       },
+       [QUEUE_DESTROY_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone destroy operation",
+               .next = NEXT(NEXT_ENTRY(QUEUE_DESTROY_ID),
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+               .call = parse_qo_destroy,
+       },
+       [QUEUE_DESTROY_ID] = {
+               .name = "rule",
+               .help = "specify rule id to destroy",
+               .next = NEXT(next_queue_destroy_attr,
+                       NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.destroy.rule)),
+               .call = parse_qo_destroy,
+       },
+       /* Queue indirect action arguments */
+       [QUEUE_INDIRECT_ACTION_CREATE] = {
+               .name = "create",
+               .help = "create indirect action",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_UPDATE] = {
+               .name = "update",
+               .help = "update indirect action",
+               .next = NEXT(next_qia_update_attr,
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy indirect action",
+               .next = NEXT(next_qia_destroy_attr),
+               .call = parse_qia_destroy,
+       },
+       /* Indirect action destroy arguments. */
+       [QUEUE_INDIRECT_ACTION_DESTROY_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone destroy operation",
+               .next = NEXT(next_qia_destroy_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+       },
+       [QUEUE_INDIRECT_ACTION_DESTROY_ID] = {
+               .name = "action_id",
+               .help = "specify a indirect action id to destroy",
+               .next = NEXT(next_qia_destroy_attr,
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.ia_destroy.action_id)),
+               .call = parse_qia_destroy,
+       },
+       /* Indirect action update arguments. */
+       [QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone update operation",
+               .next = NEXT(next_qia_update_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+       },
+       /* Indirect action create arguments. */
+       [QUEUE_INDIRECT_ACTION_CREATE_ID] = {
+               .name = "action_id",
+               .help = "specify a indirect action id to create",
+               .next = NEXT(next_qia_create_attr,
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+       },
+       [QUEUE_INDIRECT_ACTION_INGRESS] = {
+               .name = "ingress",
+               .help = "affect rule to ingress",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_EGRESS] = {
+               .name = "egress",
+               .help = "affect rule to egress",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_TRANSFER] = {
+               .name = "transfer",
+               .help = "affect rule to transfer",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_CREATE_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone create operation",
+               .next = NEXT(next_qia_create_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+       },
+       [QUEUE_INDIRECT_ACTION_SPEC] = {
+               .name = "action",
+               .help = "specify action to create indirect handle",
+               .next = NEXT(next_action),
+       },
+       /* Top-level command. */
+       [PUSH] = {
+               .name = "push",
+               .help = "push enqueued operations",
+               .next = NEXT(NEXT_ENTRY(PUSH_QUEUE), NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_push,
+       },
+       /* Sub-level commands. */
+       [PUSH_QUEUE] = {
+               .name = "queue",
+               .help = "specify queue id",
+               .next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+       },
+       /* Top-level command. */
+       [PULL] = {
+               .name = "pull",
+               .help = "pull flow operations results",
+               .next = NEXT(NEXT_ENTRY(PULL_QUEUE), NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_pull,
+       },
+       /* Sub-level commands. */
+       [PULL_QUEUE] = {
+               .name = "queue",
+               .help = "specify queue id",
+               .next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+       },
+       /* Top-level command. */
        [INDIRECT_ACTION] = {
                .name = "indirect_action",
                .type = "{command} {port_id} [{arg} [...]]",
@@ -2594,7 +3429,7 @@ static const struct token token_list[] = {
                .name = "end",
                .help = "end list of pattern items",
                .priv = PRIV_ITEM(END, 0),
-               .next = NEXT(NEXT_ENTRY(ACTIONS)),
+               .next = NEXT(NEXT_ENTRY(ACTIONS, END)),
                .call = parse_vc,
        },
        [ITEM_VOID] = {
@@ -3562,6 +4397,38 @@ static const struct token token_list[] = {
                             item_param),
                .args = ARGS(ARG_ENTRY_HTON(rte_be32_t)),
        },
+       [ITEM_GRE_OPTION] = {
+               .name = "gre_option",
+               .help = "match GRE optional fields",
+               .priv = PRIV_ITEM(GRE_OPTION,
+                                 sizeof(struct rte_flow_item_gre_opt)),
+               .next = NEXT(item_gre_option),
+               .call = parse_vc,
+       },
+       [ITEM_GRE_OPTION_CHECKSUM] = {
+               .name = "checksum",
+               .help = "match GRE checksum",
+               .next = NEXT(item_gre_option, NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_gre_opt,
+                                            checksum_rsvd.checksum)),
+       },
+       [ITEM_GRE_OPTION_KEY] = {
+               .name = "key",
+               .help = "match GRE key",
+               .next = NEXT(item_gre_option, NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_gre_opt,
+                                            key.key)),
+       },
+       [ITEM_GRE_OPTION_SEQUENCE] = {
+               .name = "sequence",
+               .help = "match GRE sequence",
+               .next = NEXT(item_gre_option, NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_gre_opt,
+                                            sequence.sequence)),
+       },
        [ITEM_GTP_PSC] = {
                .name = "gtp_psc",
                .help = "match GTP extension header with type 0x85",
@@ -5784,7 +6651,111 @@ parse_ia_destroy(struct context *ctx, const struct token *token,
        return len;
 }
 
-/** Parse tokens for meter policy action commands. */
+/** Parse tokens for indirect action commands. */
+static int
+parse_qia(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 != QUEUE)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->args.vc.data = (uint8_t *)out + size;
+               return len;
+       }
+       switch (ctx->curr) {
+       case QUEUE_INDIRECT_ACTION:
+               return len;
+       case QUEUE_INDIRECT_ACTION_CREATE:
+       case QUEUE_INDIRECT_ACTION_UPDATE:
+               out->args.vc.actions =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               out->args.vc.attr.group = UINT32_MAX;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       case QUEUE_INDIRECT_ACTION_EGRESS:
+               out->args.vc.attr.egress = 1;
+               return len;
+       case QUEUE_INDIRECT_ACTION_INGRESS:
+               out->args.vc.attr.ingress = 1;
+               return len;
+       case QUEUE_INDIRECT_ACTION_TRANSFER:
+               out->args.vc.attr.transfer = 1;
+               return len;
+       case QUEUE_INDIRECT_ACTION_CREATE_POSTPONE:
+               return len;
+       default:
+               return -1;
+       }
+}
+
+/** Parse tokens for indirect action destroy command. */
+static int
+parse_qia_destroy(struct context *ctx, const struct token *token,
+                 const char *str, unsigned int len,
+                 void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+       uint32_t *action_id;
+
+       /* 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 || out->command == QUEUE) {
+               if (ctx->curr != QUEUE_INDIRECT_ACTION_DESTROY)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.ia_destroy.action_id =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               return len;
+       }
+       switch (ctx->curr) {
+       case QUEUE_INDIRECT_ACTION:
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       case QUEUE_INDIRECT_ACTION_DESTROY_ID:
+               action_id = out->args.ia_destroy.action_id
+                               + out->args.ia_destroy.action_id_n++;
+               if ((uint8_t *)action_id > (uint8_t *)out + size)
+                       return -1;
+               ctx->objdata = 0;
+               ctx->object = action_id;
+               ctx->objmask = NULL;
+               return len;
+       case QUEUE_INDIRECT_ACTION_DESTROY_POSTPONE:
+               return len;
+       default:
+               return -1;
+       }
+}
+
+/** Parse tokens for meter policy action commands. */
 static int
 parse_mp(struct context *ctx, const struct token *token,
        const char *str, unsigned int len,
@@ -5842,7 +6813,9 @@ parse_vc(struct context *ctx, const struct token *token,
        if (!out)
                return len;
        if (!out->command) {
-               if (ctx->curr != VALIDATE && ctx->curr != CREATE)
+               if (ctx->curr != VALIDATE && ctx->curr != CREATE &&
+                   ctx->curr != PATTERN_TEMPLATE_CREATE &&
+                   ctx->curr != ACTIONS_TEMPLATE_CREATE)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -5892,6 +6865,14 @@ parse_vc(struct context *ctx, const struct token *token,
                ctx->object = out->args.vc.pattern;
                ctx->objmask = NULL;
                return len;
+       case ITEM_END:
+               if ((out->command == VALIDATE || out->command == CREATE) &&
+                   ctx->last)
+                       return -1;
+               if (out->command == PATTERN_TEMPLATE_CREATE &&
+                   !ctx->last)
+                       return -1;
+               break;
        case ACTIONS:
                out->args.vc.actions =
                        (void *)RTE_ALIGN_CEIL((uintptr_t)
@@ -7691,10 +8672,11 @@ parse_isolate(struct context *ctx, const struct token *token,
        return len;
 }
 
+/** Parse tokens for info/configure command. */
 static int
-parse_flex(struct context *ctx, const struct token *token,
-            const char *str, unsigned int len,
-            void *buf, unsigned int size)
+parse_configure(struct context *ctx, const struct token *token,
+               const char *str, unsigned int len,
+               void *buf, unsigned int size)
 {
        struct buffer *out = buf;
 
@@ -7704,8 +8686,8 @@ parse_flex(struct context *ctx, const struct token *token,
        /* Nothing else to do if there is no buffer. */
        if (!out)
                return len;
-       if (out->command == ZERO) {
-               if (ctx->curr != FLEX)
+       if (!out->command) {
+               if (ctx->curr != INFO && ctx->curr != CONFIGURE)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7713,25 +8695,15 @@ parse_flex(struct context *ctx, const struct token *token,
                ctx->objdata = 0;
                ctx->object = out;
                ctx->objmask = NULL;
-       } else {
-               switch (ctx->curr) {
-               default:
-                       break;
-               case FLEX_ITEM_INIT:
-               case FLEX_ITEM_CREATE:
-               case FLEX_ITEM_DESTROY:
-                       out->command = ctx->curr;
-                       break;
-               }
        }
-
        return len;
 }
 
+/** Parse tokens for template create command. */
 static int
-parse_tunnel(struct context *ctx, const struct token *token,
-            const char *str, unsigned int len,
-            void *buf, unsigned int size)
+parse_template(struct context *ctx, const struct token *token,
+              const char *str, unsigned int len,
+              void *buf, unsigned int size)
 {
        struct buffer *out = buf;
 
@@ -7742,7 +8714,8 @@ parse_tunnel(struct context *ctx, const struct token *token,
        if (!out)
                return len;
        if (!out->command) {
-               if (ctx->curr != TUNNEL)
+               if (ctx->curr != PATTERN_TEMPLATE &&
+                   ctx->curr != ACTIONS_TEMPLATE)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7750,104 +8723,539 @@ parse_tunnel(struct context *ctx, const struct token *token,
                ctx->objdata = 0;
                ctx->object = out;
                ctx->objmask = NULL;
-       } else {
-               switch (ctx->curr) {
-               default:
-                       break;
-               case TUNNEL_CREATE:
-               case TUNNEL_DESTROY:
-               case TUNNEL_LIST:
-                       out->command = ctx->curr;
-                       break;
-               case TUNNEL_CREATE_TYPE:
-               case TUNNEL_DESTROY_ID:
-                       ctx->object = &out->args.vc.tunnel_ops;
-                       break;
-               }
+               out->args.vc.data = (uint8_t *)out + size;
+               return len;
+       }
+       switch (ctx->curr) {
+       case PATTERN_TEMPLATE_CREATE:
+               out->args.vc.pattern =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               out->args.vc.pat_templ_id = UINT32_MAX;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       case PATTERN_TEMPLATE_EGRESS:
+               out->args.vc.attr.egress = 1;
+               return len;
+       case PATTERN_TEMPLATE_INGRESS:
+               out->args.vc.attr.ingress = 1;
+               return len;
+       case PATTERN_TEMPLATE_TRANSFER:
+               out->args.vc.attr.transfer = 1;
+               return len;
+       case ACTIONS_TEMPLATE_CREATE:
+               out->args.vc.act_templ_id = UINT32_MAX;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       case ACTIONS_TEMPLATE_SPEC:
+               out->args.vc.actions =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               ctx->object = out->args.vc.actions;
+               ctx->objmask = NULL;
+               return len;
+       case ACTIONS_TEMPLATE_MASK:
+               out->args.vc.masks =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)
+                                              (out->args.vc.actions +
+                                               out->args.vc.actions_n),
+                                              sizeof(double));
+               ctx->object = out->args.vc.masks;
+               ctx->objmask = NULL;
+               return len;
+       case ACTIONS_TEMPLATE_EGRESS:
+               out->args.vc.attr.egress = 1;
+               return len;
+       case ACTIONS_TEMPLATE_INGRESS:
+               out->args.vc.attr.ingress = 1;
+               return len;
+       case ACTIONS_TEMPLATE_TRANSFER:
+               out->args.vc.attr.transfer = 1;
+               return len;
+       default:
+               return -1;
        }
-
-       return len;
 }
 
-/**
- * Parse signed/unsigned integers 8 to 64-bit long.
- *
- * Last argument (ctx->args) is retrieved to determine integer type and
- * storage location.
- */
+/** Parse tokens for template destroy command. */
 static int
-parse_int(struct context *ctx, const struct token *token,
-         const char *str, unsigned int len,
-         void *buf, unsigned int size)
+parse_template_destroy(struct context *ctx, const struct token *token,
+                      const char *str, unsigned int len,
+                      void *buf, unsigned int size)
 {
-       const struct arg *arg = pop_args(ctx);
-       uintmax_t u;
-       char *end;
+       struct buffer *out = buf;
+       uint32_t *template_id;
 
-       (void)token;
-       /* Argument is expected. */
-       if (!arg)
+       /* Token name must match. */
+       if (parse_default(ctx, token, str, len, NULL, 0) < 0)
                return -1;
-       errno = 0;
-       u = arg->sign ?
-               (uintmax_t)strtoimax(str, &end, 0) :
-               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)
+       /* Nothing else to do if there is no buffer. */
+       if (!out)
                return len;
-       if (arg->mask) {
-               if (!arg_entry_bf_fill(ctx->object, u, arg) ||
-                   !arg_entry_bf_fill(ctx->objmask, -1, arg))
-                       goto error;
+       if (!out->command ||
+               out->command == PATTERN_TEMPLATE ||
+               out->command == ACTIONS_TEMPLATE) {
+               if (ctx->curr != PATTERN_TEMPLATE_DESTROY &&
+                       ctx->curr != ACTIONS_TEMPLATE_DESTROY)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.templ_destroy.template_id =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
                return len;
        }
-       buf = (uint8_t *)ctx->object + arg->offset;
-       size = arg->size;
-       if (u > RTE_LEN2MASK(size * CHAR_BIT, uint64_t))
+       template_id = out->args.templ_destroy.template_id
+                   + out->args.templ_destroy.template_id_n++;
+       if ((uint8_t *)template_id > (uint8_t *)out + size)
                return -1;
-objmask:
-       switch (size) {
-       case sizeof(uint8_t):
-               *(uint8_t *)buf = u;
-               break;
-       case sizeof(uint16_t):
-               *(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
-               break;
-       case sizeof(uint8_t [3]):
-#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
-               if (!arg->hton) {
-                       ((uint8_t *)buf)[0] = u;
-                       ((uint8_t *)buf)[1] = u >> 8;
-                       ((uint8_t *)buf)[2] = u >> 16;
-                       break;
-               }
-#endif
-               ((uint8_t *)buf)[0] = u >> 16;
-               ((uint8_t *)buf)[1] = u >> 8;
-               ((uint8_t *)buf)[2] = u;
-               break;
-       case sizeof(uint32_t):
-               *(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
-               break;
-       case sizeof(uint64_t):
-               *(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u;
-               break;
-       default:
-               goto error;
-       }
-       if (ctx->objmask && buf != (uint8_t *)ctx->objmask + arg->offset) {
-               u = -1;
-               buf = (uint8_t *)ctx->objmask + arg->offset;
-               goto objmask;
-       }
+       ctx->objdata = 0;
+       ctx->object = template_id;
+       ctx->objmask = NULL;
        return len;
-error:
+}
+
+/** Parse tokens for table create command. */
+static int
+parse_table(struct context *ctx, const struct token *token,
+           const char *str, unsigned int len,
+           void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+       uint32_t *template_id;
+
+       /* 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 != TABLE)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       }
+       switch (ctx->curr) {
+       case TABLE_CREATE:
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.table.id = UINT32_MAX;
+               return len;
+       case TABLE_PATTERN_TEMPLATE:
+               out->args.table.pat_templ_id =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               template_id = out->args.table.pat_templ_id
+                               + out->args.table.pat_templ_id_n++;
+               if ((uint8_t *)template_id > (uint8_t *)out + size)
+                       return -1;
+               ctx->objdata = 0;
+               ctx->object = template_id;
+               ctx->objmask = NULL;
+               return len;
+       case TABLE_ACTIONS_TEMPLATE:
+               out->args.table.act_templ_id =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)
+                                              (out->args.table.pat_templ_id +
+                                               out->args.table.pat_templ_id_n),
+                                              sizeof(double));
+               template_id = out->args.table.act_templ_id
+                               + out->args.table.act_templ_id_n++;
+               if ((uint8_t *)template_id > (uint8_t *)out + size)
+                       return -1;
+               ctx->objdata = 0;
+               ctx->object = template_id;
+               ctx->objmask = NULL;
+               return len;
+       case TABLE_INGRESS:
+               out->args.table.attr.flow_attr.ingress = 1;
+               return len;
+       case TABLE_EGRESS:
+               out->args.table.attr.flow_attr.egress = 1;
+               return len;
+       case TABLE_TRANSFER:
+               out->args.table.attr.flow_attr.transfer = 1;
+               return len;
+       default:
+               return -1;
+       }
+}
+
+/** Parse tokens for table destroy command. */
+static int
+parse_table_destroy(struct context *ctx, const struct token *token,
+                   const char *str, unsigned int len,
+                   void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+       uint32_t *table_id;
+
+       /* 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 || out->command == TABLE) {
+               if (ctx->curr != TABLE_DESTROY)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.table_destroy.table_id =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               return len;
+       }
+       table_id = out->args.table_destroy.table_id
+                   + out->args.table_destroy.table_id_n++;
+       if ((uint8_t *)table_id > (uint8_t *)out + size)
+               return -1;
+       ctx->objdata = 0;
+       ctx->object = table_id;
+       ctx->objmask = NULL;
+       return len;
+}
+
+/** Parse tokens for queue create commands. */
+static int
+parse_qo(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 != QUEUE)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.vc.data = (uint8_t *)out + size;
+               return len;
+       }
+       switch (ctx->curr) {
+       case QUEUE_CREATE:
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       case QUEUE_TEMPLATE_TABLE:
+       case QUEUE_PATTERN_TEMPLATE:
+       case QUEUE_ACTIONS_TEMPLATE:
+       case QUEUE_CREATE_POSTPONE:
+               return len;
+       case ITEM_PATTERN:
+               out->args.vc.pattern =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               ctx->object = out->args.vc.pattern;
+               ctx->objmask = NULL;
+               return len;
+       case ACTIONS:
+               out->args.vc.actions =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)
+                                              (out->args.vc.pattern +
+                                               out->args.vc.pattern_n),
+                                              sizeof(double));
+               ctx->object = out->args.vc.actions;
+               ctx->objmask = NULL;
+               return len;
+       default:
+               return -1;
+       }
+}
+
+/** Parse tokens for queue destroy command. */
+static int
+parse_qo_destroy(struct context *ctx, const struct token *token,
+                const char *str, unsigned int len,
+                void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+       uint32_t *flow_id;
+
+       /* 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 || out->command == QUEUE) {
+               if (ctx->curr != QUEUE_DESTROY)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.destroy.rule =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               return len;
+       }
+       switch (ctx->curr) {
+       case QUEUE_DESTROY_ID:
+               flow_id = out->args.destroy.rule
+                               + out->args.destroy.rule_n++;
+               if ((uint8_t *)flow_id > (uint8_t *)out + size)
+                       return -1;
+               ctx->objdata = 0;
+               ctx->object = flow_id;
+               ctx->objmask = NULL;
+               return len;
+       case QUEUE_DESTROY_POSTPONE:
+               return len;
+       default:
+               return -1;
+       }
+}
+
+/** Parse tokens for push queue command. */
+static int
+parse_push(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 != PUSH)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.vc.data = (uint8_t *)out + size;
+       }
+       return len;
+}
+
+/** Parse tokens for pull command. */
+static int
+parse_pull(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 != PULL)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.vc.data = (uint8_t *)out + size;
+       }
+       return len;
+}
+
+static int
+parse_flex(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 == ZERO) {
+               if (ctx->curr != FLEX)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+       } else {
+               switch (ctx->curr) {
+               default:
+                       break;
+               case FLEX_ITEM_INIT:
+               case FLEX_ITEM_CREATE:
+               case FLEX_ITEM_DESTROY:
+                       out->command = ctx->curr;
+                       break;
+               }
+       }
+
+       return len;
+}
+
+static int
+parse_tunnel(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 != TUNNEL)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+       } else {
+               switch (ctx->curr) {
+               default:
+                       break;
+               case TUNNEL_CREATE:
+               case TUNNEL_DESTROY:
+               case TUNNEL_LIST:
+                       out->command = ctx->curr;
+                       break;
+               case TUNNEL_CREATE_TYPE:
+               case TUNNEL_DESTROY_ID:
+                       ctx->object = &out->args.vc.tunnel_ops;
+                       break;
+               }
+       }
+
+       return len;
+}
+
+/**
+ * Parse signed/unsigned integers 8 to 64-bit long.
+ *
+ * Last argument (ctx->args) is retrieved to determine integer type and
+ * storage location.
+ */
+static int
+parse_int(struct context *ctx, const struct token *token,
+         const char *str, unsigned int len,
+         void *buf, unsigned int size)
+{
+       const struct arg *arg = pop_args(ctx);
+       uintmax_t u;
+       char *end;
+
+       (void)token;
+       /* Argument is expected. */
+       if (!arg)
+               return -1;
+       errno = 0;
+       u = arg->sign ?
+               (uintmax_t)strtoimax(str, &end, 0) :
+               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) {
+               if (!arg_entry_bf_fill(ctx->object, u, arg) ||
+                   !arg_entry_bf_fill(ctx->objmask, -1, arg))
+                       goto error;
+               return len;
+       }
+       buf = (uint8_t *)ctx->object + arg->offset;
+       size = arg->size;
+       if (u > RTE_LEN2MASK(size * CHAR_BIT, uint64_t))
+               return -1;
+objmask:
+       switch (size) {
+       case sizeof(uint8_t):
+               *(uint8_t *)buf = u;
+               break;
+       case sizeof(uint16_t):
+               *(uint16_t *)buf = arg->hton ? rte_cpu_to_be_16(u) : u;
+               break;
+       case sizeof(uint8_t [3]):
+#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
+               if (!arg->hton) {
+                       ((uint8_t *)buf)[0] = u;
+                       ((uint8_t *)buf)[1] = u >> 8;
+                       ((uint8_t *)buf)[2] = u >> 16;
+                       break;
+               }
+#endif
+               ((uint8_t *)buf)[0] = u >> 16;
+               ((uint8_t *)buf)[1] = u >> 8;
+               ((uint8_t *)buf)[2] = u;
+               break;
+       case sizeof(uint32_t):
+               *(uint32_t *)buf = arg->hton ? rte_cpu_to_be_32(u) : u;
+               break;
+       case sizeof(uint64_t):
+               *(uint64_t *)buf = arg->hton ? rte_cpu_to_be_64(u) : u;
+               break;
+       default:
+               goto error;
+       }
+       if (ctx->objmask && buf != (uint8_t *)ctx->objmask + arg->offset) {
+               u = -1;
+               buf = (uint8_t *)ctx->objmask + arg->offset;
+               goto objmask;
+       }
+       return len;
+error:
        push_args(ctx, arg);
        return -1;
 }
@@ -7931,11 +9339,7 @@ parse_hex_string(const char *src, uint8_t *dst, uint32_t *size)
        const uint8_t *head = dst;
        uint32_t left;
 
-       /* Check input parameters */
-       if ((src == NULL) ||
-               (dst == NULL) ||
-               (size == NULL) ||
-               (*size == 0))
+       if (*size == 0)
                return -1;
 
        left = *size;
@@ -8660,6 +10064,100 @@ comp_set_modify_field_id(struct context *ctx, const struct token *token,
        return -1;
 }
 
+/** Complete available pattern template IDs. */
+static int
+comp_pattern_template_id(struct context *ctx, const struct token *token,
+                        unsigned int ent, char *buf, unsigned int size)
+{
+       unsigned int i = 0;
+       struct rte_port *port;
+       struct port_template *pt;
+
+       (void)token;
+       if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
+           ctx->port == (portid_t)RTE_PORT_ALL)
+               return -1;
+       port = &ports[ctx->port];
+       for (pt = port->pattern_templ_list; pt != NULL; pt = pt->next) {
+               if (buf && i == ent)
+                       return snprintf(buf, size, "%u", pt->id);
+               ++i;
+       }
+       if (buf)
+               return -1;
+       return i;
+}
+
+/** Complete available actions template IDs. */
+static int
+comp_actions_template_id(struct context *ctx, const struct token *token,
+                        unsigned int ent, char *buf, unsigned int size)
+{
+       unsigned int i = 0;
+       struct rte_port *port;
+       struct port_template *pt;
+
+       (void)token;
+       if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
+           ctx->port == (portid_t)RTE_PORT_ALL)
+               return -1;
+       port = &ports[ctx->port];
+       for (pt = port->actions_templ_list; pt != NULL; pt = pt->next) {
+               if (buf && i == ent)
+                       return snprintf(buf, size, "%u", pt->id);
+               ++i;
+       }
+       if (buf)
+               return -1;
+       return i;
+}
+
+/** Complete available table IDs. */
+static int
+comp_table_id(struct context *ctx, const struct token *token,
+             unsigned int ent, char *buf, unsigned int size)
+{
+       unsigned int i = 0;
+       struct rte_port *port;
+       struct port_table *pt;
+
+       (void)token;
+       if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
+           ctx->port == (portid_t)RTE_PORT_ALL)
+               return -1;
+       port = &ports[ctx->port];
+       for (pt = port->table_list; pt != NULL; pt = pt->next) {
+               if (buf && i == ent)
+                       return snprintf(buf, size, "%u", pt->id);
+               ++i;
+       }
+       if (buf)
+               return -1;
+       return i;
+}
+
+/** Complete available queue IDs. */
+static int
+comp_queue_id(struct context *ctx, const struct token *token,
+             unsigned int ent, char *buf, unsigned int size)
+{
+       unsigned int i = 0;
+       struct rte_port *port;
+
+       (void)token;
+       if (port_id_is_invalid(ctx->port, DISABLED_WARN) ||
+           ctx->port == (portid_t)RTE_PORT_ALL)
+               return -1;
+       port = &ports[ctx->port];
+       for (i = 0; i < port->queue_nb; i++) {
+               if (buf && i == ent)
+                       return snprintf(buf, size, "%u", i);
+       }
+       if (buf)
+               return -1;
+       return i;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
@@ -8919,6 +10417,98 @@ static void
 cmd_flow_parsed(const struct buffer *in)
 {
        switch (in->command) {
+       case INFO:
+               port_flow_get_info(in->port);
+               break;
+       case CONFIGURE:
+               port_flow_configure(in->port,
+                                   &in->args.configure.port_attr,
+                                   in->args.configure.nb_queue,
+                                   &in->args.configure.queue_attr);
+               break;
+       case PATTERN_TEMPLATE_CREATE:
+               port_flow_pattern_template_create(in->port,
+                               in->args.vc.pat_templ_id,
+                               &((const struct rte_flow_pattern_template_attr) {
+                                       .relaxed_matching = in->args.vc.attr.reserved,
+                                       .ingress = in->args.vc.attr.ingress,
+                                       .egress = in->args.vc.attr.egress,
+                                       .transfer = in->args.vc.attr.transfer,
+                               }),
+                               in->args.vc.pattern);
+               break;
+       case PATTERN_TEMPLATE_DESTROY:
+               port_flow_pattern_template_destroy(in->port,
+                               in->args.templ_destroy.template_id_n,
+                               in->args.templ_destroy.template_id);
+               break;
+       case ACTIONS_TEMPLATE_CREATE:
+               port_flow_actions_template_create(in->port,
+                               in->args.vc.act_templ_id,
+                               &((const struct rte_flow_actions_template_attr) {
+                                       .ingress = in->args.vc.attr.ingress,
+                                       .egress = in->args.vc.attr.egress,
+                                       .transfer = in->args.vc.attr.transfer,
+                               }),
+                               in->args.vc.actions,
+                               in->args.vc.masks);
+               break;
+       case ACTIONS_TEMPLATE_DESTROY:
+               port_flow_actions_template_destroy(in->port,
+                               in->args.templ_destroy.template_id_n,
+                               in->args.templ_destroy.template_id);
+               break;
+       case TABLE_CREATE:
+               port_flow_template_table_create(in->port, in->args.table.id,
+                       &in->args.table.attr, in->args.table.pat_templ_id_n,
+                       in->args.table.pat_templ_id, in->args.table.act_templ_id_n,
+                       in->args.table.act_templ_id);
+               break;
+       case TABLE_DESTROY:
+               port_flow_template_table_destroy(in->port,
+                                       in->args.table_destroy.table_id_n,
+                                       in->args.table_destroy.table_id);
+               break;
+       case QUEUE_CREATE:
+               port_queue_flow_create(in->port, in->queue, in->postpone,
+                                      in->args.vc.table_id, in->args.vc.pat_templ_id,
+                                      in->args.vc.act_templ_id, in->args.vc.pattern,
+                                      in->args.vc.actions);
+               break;
+       case QUEUE_DESTROY:
+               port_queue_flow_destroy(in->port, in->queue, in->postpone,
+                                       in->args.destroy.rule_n,
+                                       in->args.destroy.rule);
+               break;
+       case PUSH:
+               port_queue_flow_push(in->port, in->queue);
+               break;
+       case PULL:
+               port_queue_flow_pull(in->port, in->queue);
+               break;
+       case QUEUE_INDIRECT_ACTION_CREATE:
+               port_queue_action_handle_create(
+                               in->port, in->queue, in->postpone,
+                               in->args.vc.attr.group,
+                               &((const struct rte_flow_indir_action_conf) {
+                                       .ingress = in->args.vc.attr.ingress,
+                                       .egress = in->args.vc.attr.egress,
+                                       .transfer = in->args.vc.attr.transfer,
+                               }),
+                               in->args.vc.actions);
+               break;
+       case QUEUE_INDIRECT_ACTION_DESTROY:
+               port_queue_action_handle_destroy(in->port,
+                                          in->queue, in->postpone,
+                                          in->args.ia_destroy.action_id_n,
+                                          in->args.ia_destroy.action_id);
+               break;
+       case QUEUE_INDIRECT_ACTION_UPDATE:
+               port_queue_action_handle_update(in->port,
+                                               in->queue, in->postpone,
+                                               in->args.vc.attr.group,
+                                               in->args.vc.actions);
+               break;
        case INDIRECT_ACTION_CREATE:
                port_action_handle_create(
                                in->port, in->args.vc.attr.group,
@@ -9381,7 +10971,8 @@ cmd_set_raw_parsed(const struct buffer *in)
                case RTE_FLOW_ITEM_TYPE_GENEVE_OPT:
                        opt = (const struct rte_flow_item_geneve_opt *)
                                                                item->spec;
-                       size = offsetof(struct rte_flow_item_geneve_opt, data);
+                       size = offsetof(struct rte_flow_item_geneve_opt,
+                                       option_len) + sizeof(uint8_t);
                        if (opt->option_len && opt->data) {
                                *total_size += opt->option_len *
                                               sizeof(uint32_t);
@@ -9463,6 +11054,33 @@ cmd_set_raw_parsed(const struct buffer *in)
                                ((const struct rte_flow_item_flex *)
                                item->spec)->length : 0;
                        break;
+               case RTE_FLOW_ITEM_TYPE_GRE_OPTION:
+                       size = 0;
+                       if (item->spec) {
+                               const struct rte_flow_item_gre_opt
+                                       *opt = item->spec;
+                               if (opt->checksum_rsvd.checksum) {
+                                       *total_size +=
+                                               sizeof(opt->checksum_rsvd);
+                                       rte_memcpy(data_tail - (*total_size),
+                                                  &opt->checksum_rsvd,
+                                                  sizeof(opt->checksum_rsvd));
+                               }
+                               if (opt->key.key) {
+                                       *total_size += sizeof(opt->key.key);
+                                       rte_memcpy(data_tail - (*total_size),
+                                                  &opt->key.key,
+                                                  sizeof(opt->key.key));
+                               }
+                               if (opt->sequence.sequence) {
+                                       *total_size += sizeof(opt->sequence.sequence);
+                                       rte_memcpy(data_tail - (*total_size),
+                                                  &opt->sequence.sequence,
+                                                  sizeof(opt->sequence.sequence));
+                               }
+                       }
+                       proto = 0x2F;
+                       break;
                default:
                        fprintf(stderr, "Error - Not supported item\n");
                        goto error;
@@ -9611,16 +11229,16 @@ cmd_show_set_raw_parsed(void *parsed_result, struct cmdline *cl, void *data)
        } while (all && ++index < RAW_ENCAP_CONFS_MAX_NUM);
 }
 
-cmdline_parse_token_string_t cmd_show_set_raw_cmd_show =
+static cmdline_parse_token_string_t cmd_show_set_raw_cmd_show =
        TOKEN_STRING_INITIALIZER(struct cmd_show_set_raw_result,
                        cmd_show, "show");
-cmdline_parse_token_string_t cmd_show_set_raw_cmd_what =
+static cmdline_parse_token_string_t cmd_show_set_raw_cmd_what =
        TOKEN_STRING_INITIALIZER(struct cmd_show_set_raw_result,
                        cmd_what, "raw_encap#raw_decap");
-cmdline_parse_token_num_t cmd_show_set_raw_cmd_index =
+static cmdline_parse_token_num_t cmd_show_set_raw_cmd_index =
        TOKEN_NUM_INITIALIZER(struct cmd_show_set_raw_result,
                        cmd_index, RTE_UINT16);
-cmdline_parse_token_string_t cmd_show_set_raw_cmd_all =
+static cmdline_parse_token_string_t cmd_show_set_raw_cmd_all =
        TOKEN_STRING_INITIALIZER(struct cmd_show_set_raw_result,
                        cmd_all, "all");
 cmdline_parse_inst_t cmd_show_set_raw = {