test/bonding: fix RSS test when disable RSS
[dpdk.git] / app / test-pmd / cmdline_flow.c
index 7b56b1b..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,
@@ -323,13 +425,30 @@ enum index {
        ITEM_FLEX_ITEM_HANDLE,
        ITEM_FLEX_PATTERN_HANDLE,
        ITEM_L2TPV2,
-       ITEM_L2TPV2_COMMON,
-       ITEM_L2TPV2_COMMON_TYPE,
-       ITEM_L2TPV2_COMMON_TYPE_DATA_L,
-       ITEM_L2TPV2_COMMON_TYPE_CTRL,
+       ITEM_L2TPV2_TYPE,
+       ITEM_L2TPV2_TYPE_DATA,
+       ITEM_L2TPV2_TYPE_DATA_L,
+       ITEM_L2TPV2_TYPE_DATA_S,
+       ITEM_L2TPV2_TYPE_DATA_O,
+       ITEM_L2TPV2_TYPE_DATA_L_S,
+       ITEM_L2TPV2_TYPE_CTRL,
+       ITEM_L2TPV2_MSG_DATA_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_DATA_SESSION_ID,
        ITEM_L2TPV2_MSG_DATA_L_LENGTH,
        ITEM_L2TPV2_MSG_DATA_L_TUNNEL_ID,
        ITEM_L2TPV2_MSG_DATA_L_SESSION_ID,
+       ITEM_L2TPV2_MSG_DATA_S_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_DATA_S_SESSION_ID,
+       ITEM_L2TPV2_MSG_DATA_S_NS,
+       ITEM_L2TPV2_MSG_DATA_S_NR,
+       ITEM_L2TPV2_MSG_DATA_O_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_DATA_O_SESSION_ID,
+       ITEM_L2TPV2_MSG_DATA_O_OFFSET,
+       ITEM_L2TPV2_MSG_DATA_L_S_LENGTH,
+       ITEM_L2TPV2_MSG_DATA_L_S_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_DATA_L_S_SESSION_ID,
+       ITEM_L2TPV2_MSG_DATA_L_S_NS,
+       ITEM_L2TPV2_MSG_DATA_L_S_NR,
        ITEM_L2TPV2_MSG_CTRL_LENGTH,
        ITEM_L2TPV2_MSG_CTRL_TUNNEL_ID,
        ITEM_L2TPV2_MSG_CTRL_SESSION_ID,
@@ -676,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. */
@@ -846,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;
@@ -855,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;
@@ -928,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,
@@ -1043,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,
@@ -1234,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,
@@ -1451,19 +1729,70 @@ static const enum index item_flex[] = {
 };
 
 static const enum index item_l2tpv2[] = {
-       ITEM_L2TPV2_COMMON,
+       ITEM_L2TPV2_TYPE,
+       ITEM_NEXT,
+       ZERO,
+};
+
+static const enum index item_l2tpv2_type[] = {
+       ITEM_L2TPV2_TYPE_DATA,
+       ITEM_L2TPV2_TYPE_DATA_L,
+       ITEM_L2TPV2_TYPE_DATA_S,
+       ITEM_L2TPV2_TYPE_DATA_O,
+       ITEM_L2TPV2_TYPE_DATA_L_S,
+       ITEM_L2TPV2_TYPE_CTRL,
+       ZERO,
+};
+
+static const enum index item_l2tpv2_type_data[] = {
+       ITEM_L2TPV2_MSG_DATA_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_DATA_SESSION_ID,
+       ITEM_NEXT,
+       ZERO,
+};
+
+static const enum index item_l2tpv2_type_data_l[] = {
+       ITEM_L2TPV2_MSG_DATA_L_LENGTH,
+       ITEM_L2TPV2_MSG_DATA_L_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_DATA_L_SESSION_ID,
+       ITEM_NEXT,
+       ZERO,
+};
+
+static const enum index item_l2tpv2_type_data_s[] = {
+       ITEM_L2TPV2_MSG_DATA_S_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_DATA_S_SESSION_ID,
+       ITEM_L2TPV2_MSG_DATA_S_NS,
+       ITEM_L2TPV2_MSG_DATA_S_NR,
+       ITEM_NEXT,
+       ZERO,
+};
+
+static const enum index item_l2tpv2_type_data_o[] = {
+       ITEM_L2TPV2_MSG_DATA_O_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_DATA_O_SESSION_ID,
+       ITEM_L2TPV2_MSG_DATA_O_OFFSET,
        ITEM_NEXT,
        ZERO,
 };
 
-static const enum index item_l2tpv2_common[] = {
-       ITEM_L2TPV2_COMMON_TYPE,
+static const enum index item_l2tpv2_type_data_l_s[] = {
+       ITEM_L2TPV2_MSG_DATA_L_S_LENGTH,
+       ITEM_L2TPV2_MSG_DATA_L_S_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_DATA_L_S_SESSION_ID,
+       ITEM_L2TPV2_MSG_DATA_L_S_NS,
+       ITEM_L2TPV2_MSG_DATA_L_S_NR,
+       ITEM_NEXT,
        ZERO,
 };
 
-static const enum index item_l2tpv2_common_type[] = {
-       ITEM_L2TPV2_COMMON_TYPE_DATA_L,
-       ITEM_L2TPV2_COMMON_TYPE_CTRL,
+static const enum index item_l2tpv2_type_ctrl[] = {
+       ITEM_L2TPV2_MSG_CTRL_LENGTH,
+       ITEM_L2TPV2_MSG_CTRL_TUNNEL_ID,
+       ITEM_L2TPV2_MSG_CTRL_SESSION_ID,
+       ITEM_L2TPV2_MSG_CTRL_NS,
+       ITEM_L2TPV2_MSG_CTRL_NR,
+       ITEM_NEXT,
        ZERO,
 };
 
@@ -1964,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);
@@ -2033,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[] = {
@@ -2183,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,
@@ -2200,127 +2602,628 @@ static const struct token token_list[] = {
                              QUERY,
                              ISOLATE,
                              TUNNEL,
-                             FLEX)),
+                             FLEX,
+                             QUEUE,
+                             PUSH,
+                             PULL)),
                .call = parse_init,
        },
        /* Top-level command. */
-       [INDIRECT_ACTION] = {
-               .name = "indirect_action",
+       [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 indirect actions",
-               .next = NEXT(next_ia_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+               .help = "manage pattern templates",
+               .next = NEXT(next_pt_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
                .args = ARGS(ARGS_ENTRY(struct buffer, port)),
-               .call = parse_ia,
+               .call = parse_template,
        },
        /* Sub-level commands. */
-       [INDIRECT_ACTION_CREATE] = {
+       [PATTERN_TEMPLATE_CREATE] = {
                .name = "create",
-               .help = "create indirect action",
-               .next = NEXT(next_ia_create_attr),
-               .call = parse_ia,
+               .help = "create pattern template",
+               .next = NEXT(next_pt_attr),
+               .call = parse_template,
        },
-       [INDIRECT_ACTION_UPDATE] = {
-               .name = "update",
-               .help = "update indirect action",
-               .next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_SPEC),
-                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
-               .call = parse_ia,
-       },
-       [INDIRECT_ACTION_DESTROY] = {
+       [PATTERN_TEMPLATE_DESTROY] = {
                .name = "destroy",
-               .help = "destroy indirect action",
-               .next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_DESTROY_ID)),
+               .help = "destroy pattern template",
+               .next = NEXT(NEXT_ENTRY(PATTERN_TEMPLATE_DESTROY_ID)),
                .args = ARGS(ARGS_ENTRY(struct buffer, port)),
-               .call = parse_ia_destroy,
+               .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,
        },
-       [INDIRECT_ACTION_QUERY] = {
-               .name = "query",
-               .help = "query indirect action",
-               .next = NEXT(NEXT_ENTRY(END),
-                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
-               .args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
-               .call = parse_ia,
+       [PATTERN_TEMPLATE_EGRESS] = {
+               .name = "egress",
+               .help = "attribute pattern to egress",
+               .next = NEXT(next_pt_attr),
+               .call = parse_template,
        },
-       [VALIDATE] = {
-               .name = "validate",
-               .help = "check whether a flow rule can be created",
-               .next = NEXT(next_vc_attr, NEXT_ENTRY(COMMON_PORT_ID)),
+       [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_vc,
+               .call = parse_template,
        },
-       [CREATE] = {
+       /* Sub-level commands. */
+       [ACTIONS_TEMPLATE_CREATE] = {
                .name = "create",
-               .help = "create a flow rule",
-               .next = NEXT(next_vc_attr, NEXT_ENTRY(COMMON_PORT_ID)),
-               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
-               .call = parse_vc,
+               .help = "create actions template",
+               .next = NEXT(next_at_attr),
+               .call = parse_template,
        },
-       [DESTROY] = {
+       [ACTIONS_TEMPLATE_DESTROY] = {
                .name = "destroy",
-               .help = "destroy specific flow rules",
-               .next = NEXT(NEXT_ENTRY(DESTROY_RULE),
-                            NEXT_ENTRY(COMMON_PORT_ID)),
+               .help = "destroy actions template",
+               .next = NEXT(NEXT_ENTRY(ACTIONS_TEMPLATE_DESTROY_ID)),
                .args = ARGS(ARGS_ENTRY(struct buffer, port)),
-               .call = parse_destroy,
+               .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,
        },
-       [FLUSH] = {
-               .name = "flush",
-               .help = "destroy all flow rules",
-               .next = NEXT(NEXT_ENTRY(COMMON_PORT_ID)),
-               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
-               .call = parse_flush,
+       [ACTIONS_TEMPLATE_INGRESS] = {
+               .name = "ingress",
+               .help = "attribute actions to ingress",
+               .next = NEXT(next_at_attr),
+               .call = parse_template,
        },
-       [DUMP] = {
-               .name = "dump",
-               .help = "dump single/all flow rules to file",
-               .next = NEXT(next_dump_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
-               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
-               .call = parse_dump,
+       [ACTIONS_TEMPLATE_EGRESS] = {
+               .name = "egress",
+               .help = "attribute actions to egress",
+               .next = NEXT(next_at_attr),
+               .call = parse_template,
        },
-       [QUERY] = {
-               .name = "query",
-               .help = "query an existing flow rule",
-               .next = NEXT(NEXT_ENTRY(QUERY_ACTION),
-                            NEXT_ENTRY(COMMON_RULE_ID),
-                            NEXT_ENTRY(COMMON_PORT_ID)),
-               .args = ARGS(ARGS_ENTRY(struct buffer, args.query.action.type),
-                            ARGS_ENTRY(struct buffer, args.query.rule),
-                            ARGS_ENTRY(struct buffer, port)),
-               .call = parse_query,
+       [ACTIONS_TEMPLATE_TRANSFER] = {
+               .name = "transfer",
+               .help = "attribute actions to transfer",
+               .next = NEXT(next_at_attr),
+               .call = parse_template,
        },
-       [LIST] = {
-               .name = "list",
-               .help = "list existing flow rules",
-               .next = NEXT(next_list_attr, NEXT_ENTRY(COMMON_PORT_ID)),
+       [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_list,
+               .call = parse_table,
        },
-       [AGED] = {
-               .name = "aged",
-               .help = "list and destroy aged flows",
-               .next = NEXT(next_aged_attr, NEXT_ENTRY(COMMON_PORT_ID)),
+       /* 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_aged,
+               .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,
        },
-       [ISOLATE] = {
-               .name = "isolate",
-               .help = "restrict ingress traffic to the defined flow rules",
-               .next = NEXT(NEXT_ENTRY(COMMON_BOOLEAN),
-                            NEXT_ENTRY(COMMON_PORT_ID)),
-               .args = ARGS(ARGS_ENTRY(struct buffer, args.isolate.set),
-                            ARGS_ENTRY(struct buffer, port)),
-               .call = parse_isolate,
+       [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)),
        },
-       [FLEX] = {
-               .name = "flex_item",
-               .help = "flex item API",
-               .next = NEXT(next_flex_item),
-               .call = parse_flex,
+       [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)),
        },
-       [FLEX_ITEM_INIT] = {
-               .name = "init",
-               .help = "flex item init",
+       [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} [...]]",
+               .help = "manage indirect actions",
+               .next = NEXT(next_ia_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_ia,
+       },
+       /* Sub-level commands. */
+       [INDIRECT_ACTION_CREATE] = {
+               .name = "create",
+               .help = "create indirect action",
+               .next = NEXT(next_ia_create_attr),
+               .call = parse_ia,
+       },
+       [INDIRECT_ACTION_UPDATE] = {
+               .name = "update",
+               .help = "update indirect action",
+               .next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_SPEC),
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+               .call = parse_ia,
+       },
+       [INDIRECT_ACTION_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy indirect action",
+               .next = NEXT(NEXT_ENTRY(INDIRECT_ACTION_DESTROY_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_ia_destroy,
+       },
+       [INDIRECT_ACTION_QUERY] = {
+               .name = "query",
+               .help = "query indirect action",
+               .next = NEXT(NEXT_ENTRY(END),
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.ia.action_id)),
+               .call = parse_ia,
+       },
+       [VALIDATE] = {
+               .name = "validate",
+               .help = "check whether a flow rule can be created",
+               .next = NEXT(next_vc_attr, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_vc,
+       },
+       [CREATE] = {
+               .name = "create",
+               .help = "create a flow rule",
+               .next = NEXT(next_vc_attr, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_vc,
+       },
+       [DESTROY] = {
+               .name = "destroy",
+               .help = "destroy specific flow rules",
+               .next = NEXT(NEXT_ENTRY(DESTROY_RULE),
+                            NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_destroy,
+       },
+       [FLUSH] = {
+               .name = "flush",
+               .help = "destroy all flow rules",
+               .next = NEXT(NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_flush,
+       },
+       [DUMP] = {
+               .name = "dump",
+               .help = "dump single/all flow rules to file",
+               .next = NEXT(next_dump_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_dump,
+       },
+       [QUERY] = {
+               .name = "query",
+               .help = "query an existing flow rule",
+               .next = NEXT(NEXT_ENTRY(QUERY_ACTION),
+                            NEXT_ENTRY(COMMON_RULE_ID),
+                            NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.query.action.type),
+                            ARGS_ENTRY(struct buffer, args.query.rule),
+                            ARGS_ENTRY(struct buffer, port)),
+               .call = parse_query,
+       },
+       [LIST] = {
+               .name = "list",
+               .help = "list existing flow rules",
+               .next = NEXT(next_list_attr, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_list,
+       },
+       [AGED] = {
+               .name = "aged",
+               .help = "list and destroy aged flows",
+               .next = NEXT(next_aged_attr, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_aged,
+       },
+       [ISOLATE] = {
+               .name = "isolate",
+               .help = "restrict ingress traffic to the defined flow rules",
+               .next = NEXT(NEXT_ENTRY(COMMON_BOOLEAN),
+                            NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.isolate.set),
+                            ARGS_ENTRY(struct buffer, port)),
+               .call = parse_isolate,
+       },
+       [FLEX] = {
+               .name = "flex_item",
+               .help = "flex item API",
+               .next = NEXT(next_flex_item),
+               .call = parse_flex,
+       },
+       [FLEX_ITEM_INIT] = {
+               .name = "init",
+               .help = "flex item init",
                .args = ARGS(ARGS_ENTRY(struct buffer, args.flex.token),
                             ARGS_ENTRY(struct buffer, port)),
                .next = NEXT(NEXT_ENTRY(COMMON_FLEX_TOKEN),
@@ -2526,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] = {
@@ -3494,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",
@@ -3858,31 +4793,46 @@ static const struct token token_list[] = {
                .next = NEXT(item_l2tpv2),
                .call = parse_vc,
        },
-       [ITEM_L2TPV2_COMMON] = {
-               .name = "common",
-               .help = "L2TPv2 common header",
-               .next = NEXT(item_l2tpv2_common),
-       },
-       [ITEM_L2TPV2_COMMON_TYPE] = {
+       [ITEM_L2TPV2_TYPE] = {
                .name = "type",
-               .help = "type of common header",
-               .next = NEXT(item_l2tpv2_common_type),
+               .help = "type of l2tpv2",
+               .next = NEXT(item_l2tpv2_type),
                .args = ARGS(ARG_ENTRY_HTON(struct rte_flow_item_l2tpv2)),
        },
-       [ITEM_L2TPV2_COMMON_TYPE_DATA_L] = {
+       [ITEM_L2TPV2_TYPE_DATA] = {
+               .name = "data",
+               .help = "Type #7: data message without any options",
+               .next = NEXT(item_l2tpv2_type_data),
+               .call = parse_vc_item_l2tpv2_type,
+       },
+       [ITEM_L2TPV2_MSG_DATA_TUNNEL_ID] = {
+               .name = "tunnel_id",
+               .help = "tunnel identifier",
+               .next = NEXT(item_l2tpv2_type_data,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type7.tunnel_id)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_SESSION_ID] = {
+               .name = "session_id",
+               .help = "session identifier",
+               .next = NEXT(item_l2tpv2_type_data,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type7.session_id)),
+       },
+       [ITEM_L2TPV2_TYPE_DATA_L] = {
                .name = "data_l",
                .help = "Type #6: data message with length option",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_DATA_L_LENGTH,
-                                       ITEM_L2TPV2_MSG_DATA_L_TUNNEL_ID,
-                                       ITEM_L2TPV2_MSG_DATA_L_SESSION_ID,
-                                       ITEM_NEXT)),
+               .next = NEXT(item_l2tpv2_type_data_l),
                .call = parse_vc_item_l2tpv2_type,
        },
        [ITEM_L2TPV2_MSG_DATA_L_LENGTH] = {
                .name = "length",
                .help = "message length",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_DATA_L_LENGTH,
-                                       ITEM_L2TPV2_COMMON, ITEM_NEXT),
+               .next = NEXT(item_l2tpv2_type_data_l,
                             NEXT_ENTRY(COMMON_UNSIGNED),
                             item_param),
                .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
@@ -3891,8 +4841,7 @@ static const struct token token_list[] = {
        [ITEM_L2TPV2_MSG_DATA_L_TUNNEL_ID] = {
                .name = "tunnel_id",
                .help = "tunnel identifier",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_DATA_L_TUNNEL_ID,
-                                       ITEM_L2TPV2_COMMON, ITEM_NEXT),
+               .next = NEXT(item_l2tpv2_type_data_l,
                             NEXT_ENTRY(COMMON_UNSIGNED),
                             item_param),
                .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
@@ -3901,84 +4850,201 @@ static const struct token token_list[] = {
        [ITEM_L2TPV2_MSG_DATA_L_SESSION_ID] = {
                .name = "session_id",
                .help = "session identifier",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_DATA_L_SESSION_ID,
-                                       ITEM_L2TPV2_COMMON, ITEM_NEXT),
+               .next = NEXT(item_l2tpv2_type_data_l,
                             NEXT_ENTRY(COMMON_UNSIGNED),
                             item_param),
                .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
                                             hdr.type6.session_id)),
        },
-       [ITEM_L2TPV2_COMMON_TYPE_CTRL] = {
-               .name = "control",
-               .help = "Type #3: conrtol message contains length, ns, nr options",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_CTRL_LENGTH,
-                                       ITEM_L2TPV2_MSG_CTRL_TUNNEL_ID,
-                                       ITEM_L2TPV2_MSG_CTRL_SESSION_ID,
-                                       ITEM_L2TPV2_MSG_CTRL_NS,
-                                       ITEM_L2TPV2_MSG_CTRL_NR,
-                                       ITEM_NEXT)),
+       [ITEM_L2TPV2_TYPE_DATA_S] = {
+               .name = "data_s",
+               .help = "Type #5: data message with ns, nr option",
+               .next = NEXT(item_l2tpv2_type_data_s),
                .call = parse_vc_item_l2tpv2_type,
        },
-       [ITEM_L2TPV2_MSG_CTRL_LENGTH] = {
-               .name = "length",
-               .help = "message length",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_CTRL_LENGTH,
-                                       ITEM_L2TPV2_COMMON, ITEM_NEXT),
-                            NEXT_ENTRY(COMMON_UNSIGNED),
-                            item_param),
-               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
-                                            hdr.type3.length)),
-       },
-       [ITEM_L2TPV2_MSG_CTRL_TUNNEL_ID] = {
+       [ITEM_L2TPV2_MSG_DATA_S_TUNNEL_ID] = {
                .name = "tunnel_id",
                .help = "tunnel identifier",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_CTRL_TUNNEL_ID,
-                                       ITEM_L2TPV2_COMMON, ITEM_NEXT),
+               .next = NEXT(item_l2tpv2_type_data_s,
                             NEXT_ENTRY(COMMON_UNSIGNED),
                             item_param),
                .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
-                                            hdr.type3.tunnel_id)),
+                                            hdr.type5.tunnel_id)),
        },
-       [ITEM_L2TPV2_MSG_CTRL_SESSION_ID] = {
+       [ITEM_L2TPV2_MSG_DATA_S_SESSION_ID] = {
                .name = "session_id",
                .help = "session identifier",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_CTRL_SESSION_ID,
-                                       ITEM_L2TPV2_COMMON, ITEM_NEXT),
+               .next = NEXT(item_l2tpv2_type_data_s,
                             NEXT_ENTRY(COMMON_UNSIGNED),
                             item_param),
                .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
-                                            hdr.type3.session_id)),
+                                            hdr.type5.session_id)),
        },
-       [ITEM_L2TPV2_MSG_CTRL_NS] = {
+       [ITEM_L2TPV2_MSG_DATA_S_NS] = {
                .name = "ns",
                .help = "sequence number for message",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_CTRL_NS,
-                                       ITEM_L2TPV2_COMMON, ITEM_NEXT),
+               .next = NEXT(item_l2tpv2_type_data_s,
                             NEXT_ENTRY(COMMON_UNSIGNED),
                             item_param),
                .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
-                                            hdr.type3.ns)),
+                                            hdr.type5.ns)),
        },
-       [ITEM_L2TPV2_MSG_CTRL_NR] = {
+       [ITEM_L2TPV2_MSG_DATA_S_NR] = {
                .name = "nr",
                .help = "sequence number for next receive message",
-               .next = NEXT(NEXT_ENTRY(ITEM_L2TPV2_MSG_CTRL_NS,
-                                       ITEM_L2TPV2_COMMON, ITEM_NEXT),
+               .next = NEXT(item_l2tpv2_type_data_s,
                             NEXT_ENTRY(COMMON_UNSIGNED),
                             item_param),
                .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
-                                            hdr.type3.nr)),
+                                            hdr.type5.nr)),
        },
-       [ITEM_PPP] = {
-               .name = "ppp",
-               .help = "match PPP header",
-               .priv = PRIV_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
-               .next = NEXT(item_ppp),
-               .call = parse_vc,
+       [ITEM_L2TPV2_TYPE_DATA_O] = {
+               .name = "data_o",
+               .help = "Type #4: data message with offset option",
+               .next = NEXT(item_l2tpv2_type_data_o),
+               .call = parse_vc_item_l2tpv2_type,
        },
-       [ITEM_PPP_ADDR] = {
-               .name = "addr",
-               .help = "PPP address",
+       [ITEM_L2TPV2_MSG_DATA_O_TUNNEL_ID] = {
+               .name = "tunnel_id",
+               .help = "tunnel identifier",
+               .next = NEXT(item_l2tpv2_type_data_o,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type4.tunnel_id)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_O_SESSION_ID] = {
+               .name = "session_id",
+               .help = "session identifier",
+               .next = NEXT(item_l2tpv2_type_data_o,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type5.session_id)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_O_OFFSET] = {
+               .name = "offset_size",
+               .help = "the size of offset padding",
+               .next = NEXT(item_l2tpv2_type_data_o,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type4.offset_size)),
+       },
+       [ITEM_L2TPV2_TYPE_DATA_L_S] = {
+               .name = "data_l_s",
+               .help = "Type #3: data message contains length, ns, nr "
+                       "options",
+               .next = NEXT(item_l2tpv2_type_data_l_s),
+               .call = parse_vc_item_l2tpv2_type,
+       },
+       [ITEM_L2TPV2_MSG_DATA_L_S_LENGTH] = {
+               .name = "length",
+               .help = "message length",
+               .next = NEXT(item_l2tpv2_type_data_l_s,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.length)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_L_S_TUNNEL_ID] = {
+               .name = "tunnel_id",
+               .help = "tunnel identifier",
+               .next = NEXT(item_l2tpv2_type_data_l_s,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.tunnel_id)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_L_S_SESSION_ID] = {
+               .name = "session_id",
+               .help = "session identifier",
+               .next = NEXT(item_l2tpv2_type_data_l_s,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.session_id)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_L_S_NS] = {
+               .name = "ns",
+               .help = "sequence number for message",
+               .next = NEXT(item_l2tpv2_type_data_l_s,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.ns)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_L_S_NR] = {
+               .name = "nr",
+               .help = "sequence number for next receive message",
+               .next = NEXT(item_l2tpv2_type_data_l_s,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.nr)),
+       },
+       [ITEM_L2TPV2_TYPE_CTRL] = {
+               .name = "control",
+               .help = "Type #3: conrtol message contains length, ns, nr "
+                       "options",
+               .next = NEXT(item_l2tpv2_type_ctrl),
+               .call = parse_vc_item_l2tpv2_type,
+       },
+       [ITEM_L2TPV2_MSG_CTRL_LENGTH] = {
+               .name = "length",
+               .help = "message length",
+               .next = NEXT(item_l2tpv2_type_ctrl,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.length)),
+       },
+       [ITEM_L2TPV2_MSG_CTRL_TUNNEL_ID] = {
+               .name = "tunnel_id",
+               .help = "tunnel identifier",
+               .next = NEXT(item_l2tpv2_type_ctrl,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.tunnel_id)),
+       },
+       [ITEM_L2TPV2_MSG_CTRL_SESSION_ID] = {
+               .name = "session_id",
+               .help = "session identifier",
+               .next = NEXT(item_l2tpv2_type_ctrl,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.session_id)),
+       },
+       [ITEM_L2TPV2_MSG_CTRL_NS] = {
+               .name = "ns",
+               .help = "sequence number for message",
+               .next = NEXT(item_l2tpv2_type_ctrl,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.ns)),
+       },
+       [ITEM_L2TPV2_MSG_CTRL_NR] = {
+               .name = "nr",
+               .help = "sequence number for next receive message",
+               .next = NEXT(item_l2tpv2_type_ctrl,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type3.nr)),
+       },
+       [ITEM_PPP] = {
+               .name = "ppp",
+               .help = "match PPP header",
+               .priv = PRIV_ITEM(PPP, sizeof(struct rte_flow_item_ppp)),
+               .next = NEXT(item_ppp),
+               .call = parse_vc,
+       },
+       [ITEM_PPP_ADDR] = {
+               .name = "addr",
+               .help = "PPP address",
                .next = NEXT(item_ppp, NEXT_ENTRY(COMMON_UNSIGNED),
                             item_param),
                .args = ARGS(ARGS_ENTRY(struct rte_flow_item_ppp, hdr.addr)),
@@ -5585,6 +6651,110 @@ parse_ia_destroy(struct context *ctx, const struct token *token,
        return len;
 }
 
+/** 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,
@@ -5643,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;
@@ -5693,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)
@@ -5904,11 +7084,23 @@ parse_vc_item_l2tpv2_type(struct context *ctx, const struct token *token,
        if (parse_default(ctx, token, str, len, NULL, 0) < 0)
                return -1;
        switch (ctx->curr) {
-       case ITEM_L2TPV2_COMMON_TYPE_DATA_L:
-               msg_type |= 0x4000;
+       case ITEM_L2TPV2_TYPE_DATA:
+               msg_type |= RTE_L2TPV2_MSG_TYPE_DATA;
+               break;
+       case ITEM_L2TPV2_TYPE_DATA_L:
+               msg_type |= RTE_L2TPV2_MSG_TYPE_DATA_L;
+               break;
+       case ITEM_L2TPV2_TYPE_DATA_S:
+               msg_type |= RTE_L2TPV2_MSG_TYPE_DATA_S;
                break;
-       case ITEM_L2TPV2_COMMON_TYPE_CTRL:
-               msg_type |= 0xC800;
+       case ITEM_L2TPV2_TYPE_DATA_O:
+               msg_type |= RTE_L2TPV2_MSG_TYPE_DATA_O;
+               break;
+       case ITEM_L2TPV2_TYPE_DATA_L_S:
+               msg_type |= RTE_L2TPV2_MSG_TYPE_DATA_L_S;
+               break;
+       case ITEM_L2TPV2_TYPE_CTRL:
+               msg_type |= RTE_L2TPV2_MSG_TYPE_CONTROL;
                break;
        default:
                return -1;
@@ -7286,13 +8478,361 @@ parse_flush(struct context *ctx, const struct token *token,
        return len;
 }
 
-/** Parse tokens for dump command. */
+/** Parse tokens for dump command. */
+static int
+parse_dump(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 != DUMP)
+                       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 DUMP_ALL:
+       case DUMP_ONE:
+               out->args.dump.mode = (ctx->curr == DUMP_ALL) ? true : false;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       default:
+               return -1;
+       }
+}
+
+/** Parse tokens for query command. */
+static int
+parse_query(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 != QUERY)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+       }
+       return len;
+}
+
+/** Parse action names. */
+static int
+parse_action(struct context *ctx, const struct token *token,
+            const char *str, unsigned int len,
+            void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+       const struct arg *arg = pop_args(ctx);
+       unsigned int i;
+
+       (void)size;
+       /* Argument is expected. */
+       if (!arg)
+               return -1;
+       /* Parse action name. */
+       for (i = 0; next_action[i]; ++i) {
+               const struct parse_action_priv *priv;
+
+               token = &token_list[next_action[i]];
+               if (strcmp_partial(token->name, str, len))
+                       continue;
+               priv = token->priv;
+               if (!priv)
+                       goto error;
+               if (out)
+                       memcpy((uint8_t *)ctx->object + arg->offset,
+                              &priv->type,
+                              arg->size);
+               return len;
+       }
+error:
+       push_args(ctx, arg);
+       return -1;
+}
+
+/** Parse tokens for list command. */
+static int
+parse_list(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 != LIST)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.list.group =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               return len;
+       }
+       if (((uint8_t *)(out->args.list.group + out->args.list.group_n) +
+            sizeof(*out->args.list.group)) > (uint8_t *)out + size)
+               return -1;
+       ctx->objdata = 0;
+       ctx->object = out->args.list.group + out->args.list.group_n++;
+       ctx->objmask = NULL;
+       return len;
+}
+
+/** Parse tokens for list all aged flows command. */
+static int
+parse_aged(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 != AGED)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+       }
+       if (ctx->curr == AGED_DESTROY)
+               out->args.aged.destroy = 1;
+       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 tokens for info/configure command. */
+static int
+parse_configure(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 != INFO && ctx->curr != CONFIGURE)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+       }
+       return len;
+}
+
+/** Parse tokens for template create command. */
+static int
+parse_template(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 != PATTERN_TEMPLATE &&
+                   ctx->curr != ACTIONS_TEMPLATE)
+                       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 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;
+       }
+}
+
+/** Parse tokens for template destroy command. */
+static int
+parse_template_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 *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 ||
+               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;
+       }
+       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;
+       ctx->objdata = 0;
+       ctx->object = template_id;
+       ctx->objmask = NULL;
+       return len;
+}
+
+/** Parse tokens for table create command. */
 static int
-parse_dump(struct context *ctx, const struct token *token,
+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)
@@ -7301,7 +8841,7 @@ parse_dump(struct context *ctx, const struct token *token,
        if (!out)
                return len;
        if (!out->command) {
-               if (ctx->curr != DUMP)
+               if (ctx->curr != TABLE)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7312,26 +8852,61 @@ parse_dump(struct context *ctx, const struct token *token,
                return len;
        }
        switch (ctx->curr) {
-       case DUMP_ALL:
-       case DUMP_ONE:
-               out->args.dump.mode = (ctx->curr == DUMP_ALL) ? true : false;
+       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 query command. */
+/** Parse tokens for table destroy command. */
 static int
-parse_query(struct context *ctx, const struct token *token,
-           const char *str, unsigned int len,
-           void *buf, unsigned int size)
+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)
@@ -7339,8 +8914,8 @@ parse_query(struct context *ctx, const struct token *token,
        /* Nothing else to do if there is no buffer. */
        if (!out)
                return len;
-       if (!out->command) {
-               if (ctx->curr != QUERY)
+       if (!out->command || out->command == TABLE) {
+               if (ctx->curr != TABLE_DESTROY)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7348,52 +8923,88 @@ parse_query(struct context *ctx, const struct token *token,
                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 action names. */
+/** Parse tokens for queue create commands. */
 static int
-parse_action(struct context *ctx, const struct token *token,
-            const char *str, unsigned int len,
-            void *buf, unsigned int size)
+parse_qo(struct context *ctx, const struct token *token,
+        const char *str, unsigned int len,
+        void *buf, unsigned int size)
 {
        struct buffer *out = buf;
-       const struct arg *arg = pop_args(ctx);
-       unsigned int i;
 
-       (void)size;
-       /* Argument is expected. */
-       if (!arg)
+       /* Token name must match. */
+       if (parse_default(ctx, token, str, len, NULL, 0) < 0)
                return -1;
-       /* Parse action name. */
-       for (i = 0; next_action[i]; ++i) {
-               const struct parse_action_priv *priv;
-
-               token = &token_list[next_action[i]];
-               if (strcmp_partial(token->name, str, len))
-                       continue;
-               priv = token->priv;
-               if (!priv)
-                       goto error;
-               if (out)
-                       memcpy((uint8_t *)ctx->object + arg->offset,
-                              &priv->type,
-                              arg->size);
+       /* 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;
        }
-error:
-       push_args(ctx, arg);
-       return -1;
+       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 list command. */
+/** Parse tokens for queue destroy command. */
 static int
-parse_list(struct context *ctx, const struct token *token,
-          const char *str, unsigned int len,
-          void *buf, unsigned int size)
+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)
@@ -7401,8 +9012,8 @@ parse_list(struct context *ctx, const struct token *token,
        /* Nothing else to do if there is no buffer. */
        if (!out)
                return len;
-       if (!out->command) {
-               if (ctx->curr != LIST)
+       if (!out->command || out->command == QUEUE) {
+               if (ctx->curr != QUEUE_DESTROY)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7410,23 +9021,31 @@ parse_list(struct context *ctx, const struct token *token,
                ctx->objdata = 0;
                ctx->object = out;
                ctx->objmask = NULL;
-               out->args.list.group =
+               out->args.destroy.rule =
                        (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
                                               sizeof(double));
                return len;
        }
-       if (((uint8_t *)(out->args.list.group + out->args.list.group_n) +
-            sizeof(*out->args.list.group)) > (uint8_t *)out + size)
+       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;
-       ctx->objdata = 0;
-       ctx->object = out->args.list.group + out->args.list.group_n++;
-       ctx->objmask = NULL;
-       return len;
+       }
 }
 
-/** Parse tokens for list all aged flows command. */
+/** Parse tokens for push queue command. */
 static int
-parse_aged(struct context *ctx, const struct token *token,
+parse_push(struct context *ctx, const struct token *token,
           const char *str, unsigned int len,
           void *buf, unsigned int size)
 {
@@ -7439,7 +9058,7 @@ parse_aged(struct context *ctx, const struct token *token,
        if (!out)
                return len;
        if (!out->command) {
-               if (ctx->curr != AGED)
+               if (ctx->curr != PUSH)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7447,17 +9066,16 @@ parse_aged(struct context *ctx, const struct token *token,
                ctx->objdata = 0;
                ctx->object = out;
                ctx->objmask = NULL;
+               out->args.vc.data = (uint8_t *)out + size;
        }
-       if (ctx->curr == AGED_DESTROY)
-               out->args.aged.destroy = 1;
        return len;
 }
 
-/** Parse tokens for isolate command. */
+/** Parse tokens for pull command. */
 static int
-parse_isolate(struct context *ctx, const struct token *token,
-             const char *str, unsigned int len,
-             void *buf, unsigned int size)
+parse_pull(struct context *ctx, const struct token *token,
+          const char *str, unsigned int len,
+          void *buf, unsigned int size)
 {
        struct buffer *out = buf;
 
@@ -7468,7 +9086,7 @@ parse_isolate(struct context *ctx, const struct token *token,
        if (!out)
                return len;
        if (!out->command) {
-               if (ctx->curr != ISOLATE)
+               if (ctx->curr != PULL)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7476,6 +9094,7 @@ parse_isolate(struct context *ctx, const struct token *token,
                ctx->objdata = 0;
                ctx->object = out;
                ctx->objmask = NULL;
+               out->args.vc.data = (uint8_t *)out + size;
        }
        return len;
 }
@@ -7720,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;
@@ -8449,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;
 
@@ -8708,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,
@@ -9170,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);
@@ -9252,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;
@@ -9400,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 = {