test/bonding: fix RSS test when disable RSS
[dpdk.git] / app / test-pmd / cmdline_flow.c
index d897d0d..cfd55c5 100644 (file)
@@ -54,6 +54,12 @@ enum index {
        COMMON_PRIORITY_LEVEL,
        COMMON_INDIRECT_ACTION_ID,
        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,
@@ -70,6 +76,11 @@ enum index {
        /* Top-level command. */
        FLOW,
        /* Sub-level commands. */
+       INFO,
+       CONFIGURE,
+       PATTERN_TEMPLATE,
+       ACTIONS_TEMPLATE,
+       TABLE,
        INDIRECT_ACTION,
        VALIDATE,
        CREATE,
@@ -81,6 +92,94 @@ enum index {
        AGED,
        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,
@@ -114,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,
@@ -158,6 +264,7 @@ enum index {
        ITEM_RAW_OFFSET,
        ITEM_RAW_LIMIT,
        ITEM_RAW_PATTERN,
+       ITEM_RAW_PATTERN_HEX,
        ITEM_ETH,
        ITEM_ETH_DST,
        ITEM_ETH_SRC,
@@ -263,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,
@@ -310,6 +421,43 @@ enum index {
        ITEM_PORT_REPRESENTOR_PORT_ID,
        ITEM_REPRESENTED_PORT,
        ITEM_REPRESENTED_PORT_ETHDEV_PORT_ID,
+       ITEM_FLEX,
+       ITEM_FLEX_ITEM_HANDLE,
+       ITEM_FLEX_PATTERN_HANDLE,
+       ITEM_L2TPV2,
+       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,
+       ITEM_L2TPV2_MSG_CTRL_NS,
+       ITEM_L2TPV2_MSG_CTRL_NR,
+       ITEM_PPP,
+       ITEM_PPP_ADDR,
+       ITEM_PPP_CTRL,
+       ITEM_PPP_PROTO_ID,
 
        /* Validate/create actions. */
        ACTIONS,
@@ -451,6 +599,7 @@ enum index {
        ACTION_MODIFY_FIELD_SRC_LEVEL,
        ACTION_MODIFY_FIELD_SRC_OFFSET,
        ACTION_MODIFY_FIELD_SRC_VALUE,
+       ACTION_MODIFY_FIELD_SRC_POINTER,
        ACTION_MODIFY_FIELD_WIDTH,
        ACTION_CONNTRACK,
        ACTION_CONNTRACK_UPDATE,
@@ -466,7 +615,7 @@ enum index {
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
-#define ITEM_RAW_PATTERN_SIZE 40
+#define ITEM_RAW_PATTERN_SIZE 512
 
 /** Maximum size for GENEVE option data pattern in bytes. */
 #define ITEM_GENEVE_OPT_DATA_SIZE 124
@@ -475,6 +624,14 @@ enum index {
 #define ITEM_RAW_SIZE \
        (sizeof(struct rte_flow_item_raw) + ITEM_RAW_PATTERN_SIZE)
 
+/** Maximum size for external pattern in struct rte_flow_action_modify_data. */
+#define ACTION_MODIFY_PATTERN_SIZE 32
+
+/** Storage size for struct rte_flow_action_modify_field including pattern. */
+#define ACTION_MODIFY_SIZE \
+       (sizeof(struct rte_flow_action_modify_field) + \
+       ACTION_MODIFY_PATTERN_SIZE)
+
 /** Maximum number of queue indices in struct rte_flow_action_rss. */
 #define ACTION_RSS_QUEUE_NUM 128
 
@@ -638,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. */
@@ -808,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;
@@ -817,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;
@@ -851,6 +1036,11 @@ struct buffer {
                struct {
                        uint32_t policy_id;
                } policy;/**< Policy arguments. */
+               struct {
+                       uint16_t token;
+                       uintptr_t uintptr;
+                       char filename[128];
+               } flex; /**< Flex arguments*/
        } args; /**< Command arguments. */
 };
 
@@ -878,6 +1068,135 @@ struct parse_action_priv {
                .size = s, \
        })
 
+static const enum index next_flex_item[] = {
+       FLEX_ITEM_INIT,
+       FLEX_ITEM_CREATE,
+       FLEX_ITEM_DESTROY,
+       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,
@@ -993,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,
@@ -1009,6 +1329,9 @@ static const enum index next_item[] = {
        ITEM_CONNTRACK,
        ITEM_PORT_REPRESENTOR,
        ITEM_REPRESENTED_PORT,
+       ITEM_FLEX,
+       ITEM_L2TPV2,
+       ITEM_PPP,
        END_SET,
        ZERO,
 };
@@ -1055,6 +1378,7 @@ static const enum index item_raw[] = {
        ITEM_RAW_OFFSET,
        ITEM_RAW_LIMIT,
        ITEM_RAW_PATTERN,
+       ITEM_RAW_PATTERN_HEX,
        ITEM_NEXT,
        ZERO,
 };
@@ -1180,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,
@@ -1389,6 +1721,89 @@ static const enum index item_represented_port[] = {
        ZERO,
 };
 
+static const enum index item_flex[] = {
+       ITEM_FLEX_PATTERN_HANDLE,
+       ITEM_FLEX_ITEM_HANDLE,
+       ITEM_NEXT,
+       ZERO,
+};
+
+static const enum index item_l2tpv2[] = {
+       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_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_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,
+};
+
+static const enum index item_ppp[] = {
+       ITEM_PPP_ADDR,
+       ITEM_PPP_CTRL,
+       ITEM_PPP_PROTO_ID,
+       ITEM_NEXT,
+       ZERO,
+};
+
 static const enum index next_action[] = {
        ACTION_END,
        ACTION_VOID,
@@ -1726,6 +2141,7 @@ static const enum index action_modify_field_src[] = {
        ACTION_MODIFY_FIELD_SRC_LEVEL,
        ACTION_MODIFY_FIELD_SRC_OFFSET,
        ACTION_MODIFY_FIELD_SRC_VALUE,
+       ACTION_MODIFY_FIELD_SRC_POINTER,
        ACTION_MODIFY_FIELD_WIDTH,
        ZERO,
 };
@@ -1758,6 +2174,9 @@ static int parse_set_sample_action(struct context *, const struct token *,
 static int parse_set_init(struct context *, const struct token *,
                          const char *, unsigned int,
                          void *, unsigned int);
+static int
+parse_flex_handle(struct context *, const struct token *,
+                 const char *, unsigned int, void *, unsigned int);
 static int parse_init(struct context *, const struct token *,
                      const char *, unsigned int,
                      void *, unsigned int);
@@ -1771,6 +2190,9 @@ static int parse_vc_conf(struct context *, const struct token *,
 static int parse_vc_item_ecpri_type(struct context *, const struct token *,
                                    const char *, unsigned int,
                                    void *, unsigned int);
+static int parse_vc_item_l2tpv2_type(struct context *, const struct token *,
+                                   const char *, unsigned int,
+                                   void *, unsigned int);
 static int parse_vc_action_meter_color_type(struct context *,
                                        const struct token *,
                                        const char *, unsigned int, void *,
@@ -1871,9 +2293,43 @@ 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);
+static int parse_flex(struct context *, const struct token *,
+                     const char *, unsigned int, void *, unsigned int);
 static int parse_int(struct context *, const struct token *,
                     const char *, unsigned int,
                     void *, unsigned int);
@@ -1938,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[] = {
@@ -2069,18 +2533,65 @@ static const struct token token_list[] = {
        },
        [COMMON_POLICY_ID] = {
                .name = "{policy_id}",
-               .type = "POLCIY_ID",
+               .type = "POLICY_ID",
                .help = "policy id",
                .call = parse_int,
                .comp = comp_none,
        },
+       [COMMON_FLEX_TOKEN] = {
+               .name = "{flex token}",
+               .type = "flex token",
+               .help = "flex token",
+               .call = parse_int,
+               .comp = comp_none,
+       },
+       [COMMON_FLEX_HANDLE] = {
+               .name = "{flex handle}",
+               .type = "FLEX HANDLE",
+               .help = "fill flex item data",
+               .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,
@@ -2090,111 +2601,613 @@ static const struct token token_list[] = {
                              AGED,
                              QUERY,
                              ISOLATE,
-                             TUNNEL)),
+                             TUNNEL,
+                             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,
-       },
-       [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,
+               .help = "create pattern template",
+               .next = NEXT(next_pt_attr),
+               .call = parse_template,
        },
-       [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)),
-               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
-               .call = parse_list,
+       [ACTIONS_TEMPLATE_SPEC] = {
+               .name = "template",
+               .help = "specify action to create actions template",
+               .next = NEXT(next_action),
+               .call = parse_template,
        },
-       [AGED] = {
-               .name = "aged",
-               .help = "list and destroy aged flows",
-               .next = NEXT(next_aged_attr, NEXT_ENTRY(COMMON_PORT_ID)),
+       [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_aged,
+               .call = parse_table,
        },
-       [ISOLATE] = {
-               .name = "isolate",
+       /* Sub-level commands. */
+       [TABLE_CREATE] = {
+               .name = "create",
+               .help = "create template table",
+               .next = NEXT(next_table_attr),
+               .call = parse_table,
+       },
+       [TABLE_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy template table",
+               .next = NEXT(NEXT_ENTRY(TABLE_DESTROY_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_table_destroy,
+       },
+       /* Table  arguments. */
+       [TABLE_CREATE_ID] = {
+               .name = "table_id",
+               .help = "specify table id to create",
+               .next = NEXT(next_table_attr,
+                            NEXT_ENTRY(COMMON_TABLE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.table.id)),
+       },
+       [TABLE_DESTROY_ID] = {
+               .name = "table",
+               .help = "specify table id to destroy",
+               .next = NEXT(next_table_destroy_attr,
+                            NEXT_ENTRY(COMMON_TABLE_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.table_destroy.table_id)),
+               .call = parse_table_destroy,
+       },
+       [TABLE_GROUP] = {
+               .name = "group",
+               .help = "specify a group",
+               .next = NEXT(next_table_attr, NEXT_ENTRY(COMMON_GROUP_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.table.attr.flow_attr.group)),
+       },
+       [TABLE_PRIORITY] = {
+               .name = "priority",
+               .help = "specify a priority level",
+               .next = NEXT(next_table_attr, NEXT_ENTRY(COMMON_PRIORITY_LEVEL)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.table.attr.flow_attr.priority)),
+       },
+       [TABLE_EGRESS] = {
+               .name = "egress",
+               .help = "affect rule to egress",
+               .next = NEXT(next_table_attr),
+               .call = parse_table,
+       },
+       [TABLE_INGRESS] = {
+               .name = "ingress",
+               .help = "affect rule to ingress",
+               .next = NEXT(next_table_attr),
+               .call = parse_table,
+       },
+       [TABLE_TRANSFER] = {
+               .name = "transfer",
+               .help = "affect rule to transfer",
+               .next = NEXT(next_table_attr),
+               .call = parse_table,
+       },
+       [TABLE_RULES_NUMBER] = {
+               .name = "rules_number",
+               .help = "number of rules in table",
+               .next = NEXT(next_table_attr,
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.table.attr.nb_flows)),
+       },
+       [TABLE_PATTERN_TEMPLATE] = {
+               .name = "pattern_template",
+               .help = "specify pattern template id",
+               .next = NEXT(next_table_attr,
+                            NEXT_ENTRY(COMMON_PATTERN_TEMPLATE_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.table.pat_templ_id)),
+               .call = parse_table,
+       },
+       [TABLE_ACTIONS_TEMPLATE] = {
+               .name = "actions_template",
+               .help = "specify actions template id",
+               .next = NEXT(next_table_attr,
+                            NEXT_ENTRY(COMMON_ACTIONS_TEMPLATE_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.table.act_templ_id)),
+               .call = parse_table,
+       },
+       /* Top-level command. */
+       [QUEUE] = {
+               .name = "queue",
+               .help = "queue a flow rule operation",
+               .next = NEXT(next_queue_subcmd, NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_qo,
+       },
+       /* Sub-level commands. */
+       [QUEUE_CREATE] = {
+               .name = "create",
+               .help = "create a flow rule",
+               .next = NEXT(NEXT_ENTRY(QUEUE_TEMPLATE_TABLE),
+                            NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+               .call = parse_qo,
+       },
+       [QUEUE_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy a flow rule",
+               .next = NEXT(NEXT_ENTRY(QUEUE_DESTROY_ID),
+                            NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+               .call = parse_qo_destroy,
+       },
+       [QUEUE_INDIRECT_ACTION] = {
+               .name = "indirect_action",
+               .help = "queue indirect actions",
+               .next = NEXT(next_qia_subcmd, NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+               .call = parse_qia,
+       },
+       /* Queue  arguments. */
+       [QUEUE_TEMPLATE_TABLE] = {
+               .name = "template table",
+               .help = "specify table id",
+               .next = NEXT(NEXT_ENTRY(QUEUE_PATTERN_TEMPLATE),
+                            NEXT_ENTRY(COMMON_TABLE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.vc.table_id)),
+               .call = parse_qo,
+       },
+       [QUEUE_PATTERN_TEMPLATE] = {
+               .name = "pattern_template",
+               .help = "specify pattern template index",
+               .next = NEXT(NEXT_ENTRY(QUEUE_ACTIONS_TEMPLATE),
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.vc.pat_templ_id)),
+               .call = parse_qo,
+       },
+       [QUEUE_ACTIONS_TEMPLATE] = {
+               .name = "actions_template",
+               .help = "specify actions template index",
+               .next = NEXT(NEXT_ENTRY(QUEUE_CREATE_POSTPONE),
+                            NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY(struct buffer,
+                                       args.vc.act_templ_id)),
+               .call = parse_qo,
+       },
+       [QUEUE_CREATE_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone create operation",
+               .next = NEXT(NEXT_ENTRY(ITEM_PATTERN),
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+               .call = parse_qo,
+       },
+       [QUEUE_DESTROY_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone destroy operation",
+               .next = NEXT(NEXT_ENTRY(QUEUE_DESTROY_ID),
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+               .call = parse_qo_destroy,
+       },
+       [QUEUE_DESTROY_ID] = {
+               .name = "rule",
+               .help = "specify rule id to destroy",
+               .next = NEXT(next_queue_destroy_attr,
+                       NEXT_ENTRY(COMMON_UNSIGNED)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.destroy.rule)),
+               .call = parse_qo_destroy,
+       },
+       /* Queue indirect action arguments */
+       [QUEUE_INDIRECT_ACTION_CREATE] = {
+               .name = "create",
+               .help = "create indirect action",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_UPDATE] = {
+               .name = "update",
+               .help = "update indirect action",
+               .next = NEXT(next_qia_update_attr,
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_DESTROY] = {
+               .name = "destroy",
+               .help = "destroy indirect action",
+               .next = NEXT(next_qia_destroy_attr),
+               .call = parse_qia_destroy,
+       },
+       /* Indirect action destroy arguments. */
+       [QUEUE_INDIRECT_ACTION_DESTROY_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone destroy operation",
+               .next = NEXT(next_qia_destroy_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+       },
+       [QUEUE_INDIRECT_ACTION_DESTROY_ID] = {
+               .name = "action_id",
+               .help = "specify a indirect action id to destroy",
+               .next = NEXT(next_qia_destroy_attr,
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY_PTR(struct buffer,
+                                           args.ia_destroy.action_id)),
+               .call = parse_qia_destroy,
+       },
+       /* Indirect action update arguments. */
+       [QUEUE_INDIRECT_ACTION_UPDATE_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone update operation",
+               .next = NEXT(next_qia_update_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+       },
+       /* Indirect action create arguments. */
+       [QUEUE_INDIRECT_ACTION_CREATE_ID] = {
+               .name = "action_id",
+               .help = "specify a indirect action id to create",
+               .next = NEXT(next_qia_create_attr,
+                            NEXT_ENTRY(COMMON_INDIRECT_ACTION_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),
+       },
+       [QUEUE_INDIRECT_ACTION_INGRESS] = {
+               .name = "ingress",
+               .help = "affect rule to ingress",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_EGRESS] = {
+               .name = "egress",
+               .help = "affect rule to egress",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_TRANSFER] = {
+               .name = "transfer",
+               .help = "affect rule to transfer",
+               .next = NEXT(next_qia_create_attr),
+               .call = parse_qia,
+       },
+       [QUEUE_INDIRECT_ACTION_CREATE_POSTPONE] = {
+               .name = "postpone",
+               .help = "postpone create operation",
+               .next = NEXT(next_qia_create_attr,
+                            NEXT_ENTRY(COMMON_BOOLEAN)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, postpone)),
+       },
+       [QUEUE_INDIRECT_ACTION_SPEC] = {
+               .name = "action",
+               .help = "specify action to create indirect handle",
+               .next = NEXT(next_action),
+       },
+       /* Top-level command. */
+       [PUSH] = {
+               .name = "push",
+               .help = "push enqueued operations",
+               .next = NEXT(NEXT_ENTRY(PUSH_QUEUE), NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_push,
+       },
+       /* Sub-level commands. */
+       [PUSH_QUEUE] = {
+               .name = "queue",
+               .help = "specify queue id",
+               .next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+       },
+       /* Top-level command. */
+       [PULL] = {
+               .name = "pull",
+               .help = "pull flow operations results",
+               .next = NEXT(NEXT_ENTRY(PULL_QUEUE), NEXT_ENTRY(COMMON_PORT_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, port)),
+               .call = parse_pull,
+       },
+       /* Sub-level commands. */
+       [PULL_QUEUE] = {
+               .name = "queue",
+               .help = "specify queue id",
+               .next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(COMMON_QUEUE_ID)),
+               .args = ARGS(ARGS_ENTRY(struct buffer, queue)),
+       },
+       /* Top-level command. */
+       [INDIRECT_ACTION] = {
+               .name = "indirect_action",
+               .type = "{command} {port_id} [{arg} [...]]",
+               .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)),
@@ -2202,6 +3215,41 @@ static const struct token token_list[] = {
                             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),
+                            NEXT_ENTRY(COMMON_PORT_ID)),
+               .call = parse_flex
+       },
+       [FLEX_ITEM_CREATE] = {
+               .name = "create",
+               .help = "flex item create",
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.flex.filename),
+                            ARGS_ENTRY(struct buffer, args.flex.token),
+                            ARGS_ENTRY(struct buffer, port)),
+               .next = NEXT(NEXT_ENTRY(COMMON_FILE_PATH),
+                            NEXT_ENTRY(COMMON_FLEX_TOKEN),
+                            NEXT_ENTRY(COMMON_PORT_ID)),
+               .call = parse_flex
+       },
+       [FLEX_ITEM_DESTROY] = {
+               .name = "destroy",
+               .help = "flex item destroy",
+               .args = ARGS(ARGS_ENTRY(struct buffer, args.flex.token),
+                            ARGS_ENTRY(struct buffer, port)),
+               .next = NEXT(NEXT_ENTRY(COMMON_FLEX_TOKEN),
+                            NEXT_ENTRY(COMMON_PORT_ID)),
+               .call = parse_flex
+       },
        [TUNNEL] = {
                .name = "tunnel",
                .help = "new tunnel API",
@@ -2227,7 +3275,7 @@ static const struct token token_list[] = {
        },
        [TUNNEL_DESTROY] = {
                .name = "destroy",
-               .help = "destroy tunel",
+               .help = "destroy tunnel",
                .next = NEXT(NEXT_ENTRY(TUNNEL_DESTROY_ID),
                             NEXT_ENTRY(COMMON_PORT_ID)),
                .args = ARGS(ARGS_ENTRY(struct buffer, port)),
@@ -2235,7 +3283,7 @@ static const struct token token_list[] = {
        },
        [TUNNEL_DESTROY_ID] = {
                .name = "id",
-               .help = "tunnel identifier to testroy",
+               .help = "tunnel identifier to destroy",
                .next = NEXT(NEXT_ENTRY(COMMON_UNSIGNED)),
                .args = ARGS(ARGS_ENTRY(struct tunnel_ops, id)),
                .call = parse_tunnel,
@@ -2381,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] = {
@@ -2521,6 +3569,19 @@ static const struct token token_list[] = {
                             ARGS_ENTRY_ARB(sizeof(struct rte_flow_item_raw),
                                            ITEM_RAW_PATTERN_SIZE)),
        },
+       [ITEM_RAW_PATTERN_HEX] = {
+               .name = "pattern_hex",
+               .help = "hex string to look for",
+               .next = NEXT(item_raw,
+                            NEXT_ENTRY(COMMON_HEX),
+                            NEXT_ENTRY(ITEM_PARAM_IS,
+                                       ITEM_PARAM_SPEC,
+                                       ITEM_PARAM_MASK)),
+               .args = ARGS(ARGS_ENTRY(struct rte_flow_item_raw, pattern),
+                            ARGS_ENTRY(struct rte_flow_item_raw, length),
+                            ARGS_ENTRY_ARB(sizeof(struct rte_flow_item_raw),
+                                           ITEM_RAW_PATTERN_SIZE)),
+       },
        [ITEM_ETH] = {
                .name = "eth",
                .help = "match Ethernet header",
@@ -3336,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",
@@ -3672,37 +4765,336 @@ static const struct token token_list[] = {
                             item_param),
                .args = ARGS(ARGS_ENTRY(struct rte_flow_item_ethdev, port_id)),
        },
-       /* Validate/create actions. */
-       [ACTIONS] = {
-               .name = "actions",
-               .help = "submit a list of associated actions",
-               .next = NEXT(next_action),
+       [ITEM_FLEX] = {
+               .name = "flex",
+               .help = "match flex header",
+               .priv = PRIV_ITEM(FLEX, sizeof(struct rte_flow_item_flex)),
+               .next = NEXT(item_flex),
                .call = parse_vc,
        },
-       [ACTION_NEXT] = {
-               .name = "/",
-               .help = "specify next action",
-               .next = NEXT(next_action),
+       [ITEM_FLEX_ITEM_HANDLE] = {
+               .name = "item",
+               .help = "flex item handle",
+               .next = NEXT(item_flex, NEXT_ENTRY(COMMON_FLEX_HANDLE),
+                            NEXT_ENTRY(ITEM_PARAM_IS)),
+               .args = ARGS(ARGS_ENTRY(struct rte_flow_item_flex, handle)),
        },
-       [ACTION_END] = {
-               .name = "end",
-               .help = "end list of actions",
-               .priv = PRIV_ACTION(END, 0),
-               .call = parse_vc,
+       [ITEM_FLEX_PATTERN_HANDLE] = {
+               .name = "pattern",
+               .help = "flex pattern handle",
+               .next = NEXT(item_flex, NEXT_ENTRY(COMMON_FLEX_HANDLE),
+                            NEXT_ENTRY(ITEM_PARAM_IS)),
+               .args = ARGS(ARGS_ENTRY(struct rte_flow_item_flex, pattern)),
        },
-       [ACTION_VOID] = {
-               .name = "void",
-               .help = "no-op action",
-               .priv = PRIV_ACTION(VOID, 0),
-               .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+       [ITEM_L2TPV2] = {
+               .name = "l2tpv2",
+               .help = "match L2TPv2 header",
+               .priv = PRIV_ITEM(L2TPV2, sizeof(struct rte_flow_item_l2tpv2)),
+               .next = NEXT(item_l2tpv2),
                .call = parse_vc,
        },
-       [ACTION_PASSTHRU] = {
-               .name = "passthru",
-               .help = "let subsequent rule process matched packets",
-               .priv = PRIV_ACTION(PASSTHRU, 0),
-               .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
-               .call = parse_vc,
+       [ITEM_L2TPV2_TYPE] = {
+               .name = "type",
+               .help = "type of l2tpv2",
+               .next = NEXT(item_l2tpv2_type),
+               .args = ARGS(ARG_ENTRY_HTON(struct rte_flow_item_l2tpv2)),
+       },
+       [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(item_l2tpv2_type_data_l),
+               .call = parse_vc_item_l2tpv2_type,
+       },
+       [ITEM_L2TPV2_MSG_DATA_L_LENGTH] = {
+               .name = "length",
+               .help = "message length",
+               .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.length)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_L_TUNNEL_ID] = {
+               .name = "tunnel_id",
+               .help = "tunnel identifier",
+               .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.tunnel_id)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_L_SESSION_ID] = {
+               .name = "session_id",
+               .help = "session identifier",
+               .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_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_DATA_S_TUNNEL_ID] = {
+               .name = "tunnel_id",
+               .help = "tunnel identifier",
+               .next = NEXT(item_l2tpv2_type_data_s,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type5.tunnel_id)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_S_SESSION_ID] = {
+               .name = "session_id",
+               .help = "session identifier",
+               .next = NEXT(item_l2tpv2_type_data_s,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type5.session_id)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_S_NS] = {
+               .name = "ns",
+               .help = "sequence number for message",
+               .next = NEXT(item_l2tpv2_type_data_s,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type5.ns)),
+       },
+       [ITEM_L2TPV2_MSG_DATA_S_NR] = {
+               .name = "nr",
+               .help = "sequence number for next receive message",
+               .next = NEXT(item_l2tpv2_type_data_s,
+                            NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY_HTON(struct rte_flow_item_l2tpv2,
+                                            hdr.type5.nr)),
+       },
+       [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_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)),
+       },
+       [ITEM_PPP_CTRL] = {
+               .name = "ctrl",
+               .help = "PPP control",
+               .next = NEXT(item_ppp, NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY(struct rte_flow_item_ppp, hdr.ctrl)),
+       },
+       [ITEM_PPP_PROTO_ID] = {
+               .name = "proto_id",
+               .help = "PPP protocol identifier",
+               .next = NEXT(item_ppp, NEXT_ENTRY(COMMON_UNSIGNED),
+                            item_param),
+               .args = ARGS(ARGS_ENTRY(struct rte_flow_item_ppp,
+                                       hdr.proto_id)),
+       },
+       /* Validate/create actions. */
+       [ACTIONS] = {
+               .name = "actions",
+               .help = "submit a list of associated actions",
+               .next = NEXT(next_action),
+               .call = parse_vc,
+       },
+       [ACTION_NEXT] = {
+               .name = "/",
+               .help = "specify next action",
+               .next = NEXT(next_action),
+       },
+       [ACTION_END] = {
+               .name = "end",
+               .help = "end list of actions",
+               .priv = PRIV_ACTION(END, 0),
+               .call = parse_vc,
+       },
+       [ACTION_VOID] = {
+               .name = "void",
+               .help = "no-op action",
+               .priv = PRIV_ACTION(VOID, 0),
+               .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+               .call = parse_vc,
+       },
+       [ACTION_PASSTHRU] = {
+               .name = "passthru",
+               .help = "let subsequent rule process matched packets",
+               .priv = PRIV_ACTION(PASSTHRU, 0),
+               .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+               .call = parse_vc,
        },
        [ACTION_JUMP] = {
                .name = "jump",
@@ -4511,8 +5903,7 @@ static const struct token token_list[] = {
        [ACTION_MODIFY_FIELD] = {
                .name = "modify_field",
                .help = "modify destination field with data from source field",
-               .priv = PRIV_ACTION(MODIFY_FIELD,
-                       sizeof(struct rte_flow_action_modify_field)),
+               .priv = PRIV_ACTION(MODIFY_FIELD, ACTION_MODIFY_SIZE),
                .next = NEXT(NEXT_ENTRY(ACTION_MODIFY_FIELD_OP)),
                .call = parse_vc,
        },
@@ -4595,11 +5986,26 @@ static const struct token token_list[] = {
                .name = "src_value",
                .help = "source immediate value",
                .next = NEXT(NEXT_ENTRY(ACTION_MODIFY_FIELD_WIDTH),
-                       NEXT_ENTRY(COMMON_UNSIGNED)),
-               .args = ARGS(ARGS_ENTRY(struct rte_flow_action_modify_field,
+                            NEXT_ENTRY(COMMON_HEX)),
+               .args = ARGS(ARGS_ENTRY_ARB(0, 0),
+                            ARGS_ENTRY_ARB(0, 0),
+                            ARGS_ENTRY(struct rte_flow_action_modify_field,
                                        src.value)),
                .call = parse_vc_conf,
        },
+       [ACTION_MODIFY_FIELD_SRC_POINTER] = {
+               .name = "src_ptr",
+               .help = "pointer to source immediate value",
+               .next = NEXT(NEXT_ENTRY(ACTION_MODIFY_FIELD_WIDTH),
+                            NEXT_ENTRY(COMMON_HEX)),
+               .args = ARGS(ARGS_ENTRY(struct rte_flow_action_modify_field,
+                                       src.pvalue),
+                            ARGS_ENTRY_ARB(0, 0),
+                            ARGS_ENTRY_ARB
+                               (sizeof(struct rte_flow_action_modify_field),
+                                ACTION_MODIFY_PATTERN_SIZE)),
+               .call = parse_vc_conf,
+       },
        [ACTION_MODIFY_FIELD_WIDTH] = {
                .name = "width",
                .help = "number of bits to copy",
@@ -5245,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,
@@ -5303,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;
@@ -5353,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)
@@ -5545,6 +7065,69 @@ parse_vc_item_ecpri_type(struct context *ctx, const struct token *token,
        return len;
 }
 
+/** Parse L2TPv2 common header type field. */
+static int
+parse_vc_item_l2tpv2_type(struct context *ctx, const struct token *token,
+                        const char *str, unsigned int len,
+                        void *buf, unsigned int size)
+{
+       struct rte_flow_item_l2tpv2 *l2tpv2;
+       struct rte_flow_item_l2tpv2 *l2tpv2_mask;
+       struct rte_flow_item *item;
+       uint32_t data_size;
+       uint16_t msg_type = 0;
+       struct buffer *out = buf;
+       const struct arg *arg;
+
+       (void)size;
+       /* Token name must match. */
+       if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+               return -1;
+       switch (ctx->curr) {
+       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_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;
+       }
+       if (!ctx->object)
+               return len;
+       arg = pop_args(ctx);
+       if (!arg)
+               return -1;
+       l2tpv2 = (struct rte_flow_item_l2tpv2 *)out->args.vc.data;
+       l2tpv2->hdr.common.flags_version |= msg_type;
+       data_size = ctx->objdata / 3; /* spec, last, mask */
+       l2tpv2_mask = (struct rte_flow_item_l2tpv2 *)(out->args.vc.data +
+                                                   (data_size * 2));
+       l2tpv2_mask->hdr.common.flags_version = 0xFFFF;
+       if (arg->hton) {
+               l2tpv2->hdr.common.flags_version =
+                       rte_cpu_to_be_16(l2tpv2->hdr.common.flags_version);
+               l2tpv2_mask->hdr.common.flags_version =
+                   rte_cpu_to_be_16(l2tpv2_mask->hdr.common.flags_version);
+       }
+       item = &out->args.vc.pattern[out->args.vc.pattern_n - 1];
+       item->spec = l2tpv2;
+       item->mask = l2tpv2_mask;
+       return len;
+}
+
 /** Parse meter color action type. */
 static int
 parse_vc_action_meter_color_type(struct context *ctx, const struct token *token,
@@ -6934,13 +8517,494 @@ parse_dump(struct context *ctx, const struct token *token,
        }
 }
 
-/** Parse tokens for query command. */
+/** 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_table(struct context *ctx, const struct token *token,
+           const char *str, unsigned int len,
+           void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+       uint32_t *template_id;
+
+       /* Token name must match. */
+       if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+               return -1;
+       /* Nothing else to do if there is no buffer. */
+       if (!out)
+               return len;
+       if (!out->command) {
+               if (ctx->curr != TABLE)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       }
+       switch (ctx->curr) {
+       case TABLE_CREATE:
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.table.id = UINT32_MAX;
+               return len;
+       case TABLE_PATTERN_TEMPLATE:
+               out->args.table.pat_templ_id =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               template_id = out->args.table.pat_templ_id
+                               + out->args.table.pat_templ_id_n++;
+               if ((uint8_t *)template_id > (uint8_t *)out + size)
+                       return -1;
+               ctx->objdata = 0;
+               ctx->object = template_id;
+               ctx->objmask = NULL;
+               return len;
+       case TABLE_ACTIONS_TEMPLATE:
+               out->args.table.act_templ_id =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)
+                                              (out->args.table.pat_templ_id +
+                                               out->args.table.pat_templ_id_n),
+                                              sizeof(double));
+               template_id = out->args.table.act_templ_id
+                               + out->args.table.act_templ_id_n++;
+               if ((uint8_t *)template_id > (uint8_t *)out + size)
+                       return -1;
+               ctx->objdata = 0;
+               ctx->object = template_id;
+               ctx->objmask = NULL;
+               return len;
+       case TABLE_INGRESS:
+               out->args.table.attr.flow_attr.ingress = 1;
+               return len;
+       case TABLE_EGRESS:
+               out->args.table.attr.flow_attr.egress = 1;
+               return len;
+       case TABLE_TRANSFER:
+               out->args.table.attr.flow_attr.transfer = 1;
+               return len;
+       default:
+               return -1;
+       }
+}
+
+/** Parse tokens for table destroy command. */
+static int
+parse_table_destroy(struct context *ctx, const struct token *token,
+                   const char *str, unsigned int len,
+                   void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+       uint32_t *table_id;
+
+       /* Token name must match. */
+       if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+               return -1;
+       /* Nothing else to do if there is no buffer. */
+       if (!out)
+               return len;
+       if (!out->command || out->command == TABLE) {
+               if (ctx->curr != TABLE_DESTROY)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.table_destroy.table_id =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               return len;
+       }
+       table_id = out->args.table_destroy.table_id
+                   + out->args.table_destroy.table_id_n++;
+       if ((uint8_t *)table_id > (uint8_t *)out + size)
+               return -1;
+       ctx->objdata = 0;
+       ctx->object = table_id;
+       ctx->objmask = NULL;
+       return len;
+}
+
+/** Parse tokens for queue create commands. */
+static int
+parse_qo(struct context *ctx, const struct token *token,
+        const char *str, unsigned int len,
+        void *buf, unsigned int size)
+{
+       struct buffer *out = buf;
+
+       /* Token name must match. */
+       if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+               return -1;
+       /* Nothing else to do if there is no buffer. */
+       if (!out)
+               return len;
+       if (!out->command) {
+               if (ctx->curr != QUEUE)
+                       return -1;
+               if (sizeof(*out) > size)
+                       return -1;
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               out->args.vc.data = (uint8_t *)out + size;
+               return len;
+       }
+       switch (ctx->curr) {
+       case QUEUE_CREATE:
+               out->command = ctx->curr;
+               ctx->objdata = 0;
+               ctx->object = out;
+               ctx->objmask = NULL;
+               return len;
+       case QUEUE_TEMPLATE_TABLE:
+       case QUEUE_PATTERN_TEMPLATE:
+       case QUEUE_ACTIONS_TEMPLATE:
+       case QUEUE_CREATE_POSTPONE:
+               return len;
+       case ITEM_PATTERN:
+               out->args.vc.pattern =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               ctx->object = out->args.vc.pattern;
+               ctx->objmask = NULL;
+               return len;
+       case ACTIONS:
+               out->args.vc.actions =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)
+                                              (out->args.vc.pattern +
+                                               out->args.vc.pattern_n),
+                                              sizeof(double));
+               ctx->object = out->args.vc.actions;
+               ctx->objmask = NULL;
+               return len;
+       default:
+               return -1;
+       }
+}
+
+/** Parse tokens for queue destroy command. */
 static int
-parse_query(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)
@@ -6948,8 +9012,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 == QUEUE) {
+               if (ctx->curr != QUEUE_DESTROY)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -6957,48 +9021,31 @@ parse_query(struct context *ctx, const struct token *token,
                ctx->objdata = 0;
                ctx->object = out;
                ctx->objmask = NULL;
+               out->args.destroy.rule =
+                       (void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),
+                                              sizeof(double));
+               return len;
        }
-       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);
+       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;
        }
-error:
-       push_args(ctx, arg);
-       return -1;
 }
 
-/** Parse tokens for list command. */
+/** Parse tokens for push queue command. */
 static int
-parse_list(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)
 {
@@ -7011,7 +9058,7 @@ parse_list(struct context *ctx, const struct token *token,
        if (!out)
                return len;
        if (!out->command) {
-               if (ctx->curr != LIST)
+               if (ctx->curr != PUSH)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7019,23 +9066,14 @@ parse_list(struct context *ctx, const struct token *token,
                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;
+               out->args.vc.data = (uint8_t *)out + size;
        }
-       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. */
+/** Parse tokens for pull command. */
 static int
-parse_aged(struct context *ctx, const struct token *token,
+parse_pull(struct context *ctx, const struct token *token,
           const char *str, unsigned int len,
           void *buf, unsigned int size)
 {
@@ -7048,7 +9086,7 @@ parse_aged(struct context *ctx, const struct token *token,
        if (!out)
                return len;
        if (!out->command) {
-               if (ctx->curr != AGED)
+               if (ctx->curr != PULL)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7056,17 +9094,15 @@ 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. */
 static int
-parse_isolate(struct context *ctx, const struct token *token,
-             const char *str, unsigned int len,
-             void *buf, unsigned int size)
+parse_flex(struct context *ctx, const struct token *token,
+            const char *str, unsigned int len,
+            void *buf, unsigned int size)
 {
        struct buffer *out = buf;
 
@@ -7076,8 +9112,8 @@ parse_isolate(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 != ISOLATE)
+       if (out->command == ZERO) {
+               if (ctx->curr != FLEX)
                        return -1;
                if (sizeof(*out) > size)
                        return -1;
@@ -7085,7 +9121,18 @@ parse_isolate(struct context *ctx, const struct token *token,
                ctx->objdata = 0;
                ctx->object = out;
                ctx->objmask = NULL;
+       } else {
+               switch (ctx->curr) {
+               default:
+                       break;
+               case FLEX_ITEM_INIT:
+               case FLEX_ITEM_CREATE:
+               case FLEX_ITEM_DESTROY:
+                       out->command = ctx->curr;
+                       break;
+               }
        }
+
        return len;
 }
 
@@ -7289,31 +9336,32 @@ error:
 static int
 parse_hex_string(const char *src, uint8_t *dst, uint32_t *size)
 {
-       char *c = NULL;
-       uint32_t i, len;
-       char tmp[3];
+       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;
+
        /* Convert chars to bytes */
-       for (i = 0, len = 0; i < *size; i += 2) {
-               snprintf(tmp, 3, "%s", src + i);
-               dst[len++] = strtoul(tmp, &c, 16);
-               if (*c != 0) {
-                       len--;
-                       dst[len] = 0;
-                       *size = len;
+       while (left) {
+               char tmp[3], *end = tmp;
+               uint32_t read_lim = left & 1 ? 1 : 2;
+
+               snprintf(tmp, read_lim + 1, "%s", src);
+               *dst = strtoul(tmp, &end, 16);
+               if (*end) {
+                       *dst = 0;
+                       *size = (uint32_t)(dst - head);
                        return -1;
                }
+               left -= read_lim;
+               src += read_lim;
+               dst++;
        }
-       dst[len] = 0;
-       *size = len;
-
+       *dst = 0;
+       *size = (uint32_t)(dst - head);
        return 0;
 }
 
@@ -7357,10 +9405,13 @@ parse_hex(struct context *ctx, const struct token *token,
                hexlen -= 2;
        }
        if (hexlen > length)
-               return -1;
+               goto error;
        ret = parse_hex_string(str, hex_tmp, &hexlen);
        if (ret < 0)
                goto error;
+       /* Check the converted binary fits into data buffer. */
+       if (hexlen > size)
+               goto error;
        /* Let parse_int() fill length information first. */
        ret = snprintf(tmp, sizeof(tmp), "%u", hexlen);
        if (ret < 0)
@@ -7751,6 +9802,71 @@ parse_set_init(struct context *ctx, const struct token *token,
        return len;
 }
 
+/*
+ * Replace testpmd handles in a flex flow item with real values.
+ */
+static int
+parse_flex_handle(struct context *ctx, const struct token *token,
+                 const char *str, unsigned int len,
+                 void *buf, unsigned int size)
+{
+       struct rte_flow_item_flex *spec, *mask;
+       const struct rte_flow_item_flex *src_spec, *src_mask;
+       const struct arg *arg = pop_args(ctx);
+       uint32_t offset;
+       uint16_t handle;
+       int ret;
+
+       if (!arg) {
+               printf("Bad environment\n");
+               return -1;
+       }
+       offset = arg->offset;
+       push_args(ctx, arg);
+       ret = parse_int(ctx, token, str, len, buf, size);
+       if (ret <= 0 || !ctx->object)
+               return ret;
+       if (ctx->port >= RTE_MAX_ETHPORTS) {
+               printf("Bad port\n");
+               return -1;
+       }
+       if (offset == offsetof(struct rte_flow_item_flex, handle)) {
+               const struct flex_item *fp;
+               struct rte_flow_item_flex *item_flex = ctx->object;
+               handle = (uint16_t)(uintptr_t)item_flex->handle;
+               if (handle >= FLEX_MAX_PARSERS_NUM) {
+                       printf("Bad flex item handle\n");
+                       return -1;
+               }
+               fp = flex_items[ctx->port][handle];
+               if (!fp) {
+                       printf("Bad flex item handle\n");
+                       return -1;
+               }
+               item_flex->handle = fp->flex_handle;
+       } else if (offset == offsetof(struct rte_flow_item_flex, pattern)) {
+               handle = (uint16_t)(uintptr_t)
+                       ((struct rte_flow_item_flex *)ctx->object)->pattern;
+               if (handle >= FLEX_MAX_PATTERNS_NUM) {
+                       printf("Bad pattern handle\n");
+                       return -1;
+               }
+               src_spec = &flex_patterns[handle].spec;
+               src_mask = &flex_patterns[handle].mask;
+               spec = ctx->object;
+               mask = spec + 2; /* spec, last, mask */
+               /* fill flow rule spec and mask parameters */
+               spec->length = src_spec->length;
+               spec->pattern = src_spec->pattern;
+               mask->length = src_mask->length;
+               mask->pattern = src_mask->pattern;
+       } else {
+               printf("Bad arguments - unknown flex item offset\n");
+               return -1;
+       }
+       return ret;
+}
+
 /** No completion. */
 static int
 comp_none(struct context *ctx, const struct token *token,
@@ -7920,15 +10036,11 @@ static int
 comp_set_modify_field_op(struct context *ctx, const struct token *token,
                   unsigned int ent, char *buf, unsigned int size)
 {
-       uint16_t idx = 0;
-
        RTE_SET_USED(ctx);
        RTE_SET_USED(token);
-       for (idx = 0; modify_field_ops[idx]; ++idx)
-               ;
        if (!buf)
-               return idx + 1;
-       if (ent < idx)
+               return RTE_DIM(modify_field_ops);
+       if (ent < RTE_DIM(modify_field_ops) - 1)
                return strlcpy(buf, modify_field_ops[ent], size);
        return -1;
 }
@@ -7938,19 +10050,114 @@ static int
 comp_set_modify_field_id(struct context *ctx, const struct token *token,
                   unsigned int ent, char *buf, unsigned int size)
 {
-       uint16_t idx = 0;
+       const char *name;
 
-       RTE_SET_USED(ctx);
        RTE_SET_USED(token);
-       for (idx = 0; modify_field_ids[idx]; ++idx)
-               ;
        if (!buf)
-               return idx + 1;
-       if (ent < idx)
-               return strlcpy(buf, modify_field_ids[ent], size);
+               return RTE_DIM(modify_field_ids);
+       if (ent >= RTE_DIM(modify_field_ids) - 1)
+               return -1;
+       name = modify_field_ids[ent];
+       if (ctx->curr == ACTION_MODIFY_FIELD_SRC_TYPE ||
+           (strcmp(name, "pointer") && strcmp(name, "value")))
+               return strlcpy(buf, name, size);
        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;
 
@@ -8052,6 +10259,30 @@ cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
        return len;
 }
 
+int
+flow_parse(const char *src, void *result, unsigned int size,
+          struct rte_flow_attr **attr,
+          struct rte_flow_item **pattern, struct rte_flow_action **actions)
+{
+       int ret;
+       struct context saved_flow_ctx = cmd_flow_context;
+
+       cmd_flow_context_init(&cmd_flow_context);
+       do {
+               ret = cmd_flow_parse(NULL, src, result, size);
+               if (ret > 0) {
+                       src += ret;
+                       while (isspace(*src))
+                               src++;
+               }
+       } while (ret > 0 && strlen(src));
+       cmd_flow_context = saved_flow_ctx;
+       *attr = &((struct buffer *)result)->args.vc.attr;
+       *pattern = ((struct buffer *)result)->args.vc.pattern;
+       *actions = ((struct buffer *)result)->args.vc.actions;
+       return (ret >= 0 && !strlen(src)) ? 0 : -1;
+}
+
 /** Return number of completion entries (cmdline API). */
 static int
 cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr)
@@ -8186,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,
@@ -8257,6 +10580,13 @@ cmd_flow_parsed(const struct buffer *in)
                port_meter_policy_add(in->port, in->args.policy.policy_id,
                                        in->args.vc.actions);
                break;
+       case FLEX_ITEM_CREATE:
+               flex_item_create(in->port, in->args.flex.token,
+                                in->args.flex.filename);
+               break;
+       case FLEX_ITEM_DESTROY:
+               flex_item_destroy(in->port, in->args.flex.token);
+               break;
        default:
                break;
        }
@@ -8437,6 +10767,12 @@ flow_item_default_mask(const struct rte_flow_item *item)
        case RTE_FLOW_ITEM_TYPE_REPRESENTED_PORT:
                mask = &rte_flow_item_ethdev_mask;
                break;
+       case RTE_FLOW_ITEM_TYPE_L2TPV2:
+               mask = &rte_flow_item_l2tpv2_mask;
+               break;
+       case RTE_FLOW_ITEM_TYPE_PPP:
+               mask = &rte_flow_item_ppp_mask;
+               break;
        default:
                break;
        }
@@ -8635,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);
@@ -8698,7 +11035,7 @@ cmd_set_raw_parsed(const struct buffer *in)
                                        uint8_t qfi:6;
                                        uint8_t next;
                                } psc;
-                               psc.len = sizeof(psc);
+                               psc.len = sizeof(psc) / 4;
                                psc.pdu_type = opt->hdr.type;
                                psc.qfi = opt->hdr.qfi;
                                psc.next = 0;
@@ -8712,6 +11049,38 @@ cmd_set_raw_parsed(const struct buffer *in)
                case RTE_FLOW_ITEM_TYPE_PFCP:
                        size = sizeof(struct rte_flow_item_pfcp);
                        break;
+               case RTE_FLOW_ITEM_TYPE_FLEX:
+                       size = item->spec ?
+                               ((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;
@@ -8860,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 = {