[PATCH v1 14/21] net/i40e: reimplement FDIR parser

Anatoly Burakov <[email protected]>
Newsgroups org.dpdk.dev
Message-ID <63ffa8dd67ae5714f2efe1557844e21d8a15b62d.1787233988.git.anatoly.burakov@intel.com>
Use the new flow graph API and the common parsing framework to implement
flow parser for flow director.

As a result of transitioning to more formalized validation, some checks
have become more stringent. In particular, some protocols (such as SCTP)
were previously accepting non-zero or invalid masks and either ignoring
them or misinterpreting them to mean "fully masked". This has now been
corrected.

The FDIR engine in i40e has also relied on a custom memory allocation
scheme for fdir flows - also migrated to the new infrastructure.

Signed-off-by: Anatoly Burakov <[email protected]>
---
 drivers/net/intel/i40e/i40e_ethdev.c    |   48 -
 drivers/net/intel/i40e/i40e_ethdev.h    |   36 +-
 drivers/net/intel/i40e/i40e_fdir.c      |   78 +-
 drivers/net/intel/i40e/i40e_flow.c      | 1957 +----------------------
 drivers/net/intel/i40e/i40e_flow.h      |    5 +
 drivers/net/intel/i40e/i40e_flow_fdir.c | 1834 +++++++++++++++++++++
 drivers/net/intel/i40e/meson.build      |    1 +
 drivers/net/intel/i40e/rte_pmd_i40e.c   |    3 +-
 8 files changed, 1873 insertions(+), 2089 deletions(-)
 create mode 100644 drivers/net/intel/i40e/i40e_flow_fdir.c

diff --git a/drivers/net/intel/i40e/i40e_ethdev.c b/drivers/net/intel/i40e/i40e_ethdev.c
index a9f6a5680b..476b828211 100644
--- a/drivers/net/intel/i40e/i40e_ethdev.c
+++ b/drivers/net/intel/i40e/i40e_ethdev.c
@@ -1103,10 +1103,6 @@ i40e_init_fdir_filter_list(struct rte_eth_dev *dev)
 	uint32_t alloc = hw->func_caps.fd_filters_guaranteed;
 	uint32_t best = hw->func_caps.fd_filters_best_effort;
 	enum i40e_filter_pctype pctype;
-	struct rte_bitmap *bmp = NULL;
-	uint32_t bmp_size;
-	void *mem = NULL;
-	uint32_t i = 0;
 	int ret;
 
 	struct rte_hash_parameters fdir_hash_params = {
@@ -1163,50 +1159,8 @@ i40e_init_fdir_filter_list(struct rte_eth_dev *dev)
 
 	PMD_DRV_LOG(INFO, "FDIR guarantee space: %u, best_effort space %u.", alloc, best);
 
-	fdir_info->fdir_flow_pool.pool =
-			rte_zmalloc("i40e_fdir_entry",
-				sizeof(struct i40e_fdir_entry) *
-				fdir_info->fdir_space_size,
-				0);
-
-	if (!fdir_info->fdir_flow_pool.pool) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for bitmap flow!");
-		ret = -ENOMEM;
-		goto err_fdir_bitmap_flow_alloc;
-	}
-
-	for (i = 0; i < fdir_info->fdir_space_size; i++)
-		fdir_info->fdir_flow_pool.pool[i].idx = i;
-
-	bmp_size =
-		rte_bitmap_get_memory_footprint(fdir_info->fdir_space_size);
-	mem = rte_zmalloc("fdir_bmap", bmp_size, RTE_CACHE_LINE_SIZE);
-	if (mem == NULL) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to allocate memory for fdir bitmap!");
-		ret = -ENOMEM;
-		goto err_fdir_mem_alloc;
-	}
-	bmp = rte_bitmap_init(fdir_info->fdir_space_size, mem, bmp_size);
-	if (bmp == NULL) {
-		PMD_INIT_LOG(ERR,
-			     "Failed to initialization fdir bitmap!");
-		ret = -ENOMEM;
-		goto err_fdir_bmp_alloc;
-	}
-	for (i = 0; i < fdir_info->fdir_space_size; i++)
-		rte_bitmap_set(bmp, i);
-
-	fdir_info->fdir_flow_pool.bitmap = bmp;
-
 	return 0;
 
-err_fdir_bmp_alloc:
-	rte_free(mem);
-err_fdir_mem_alloc:
-	rte_free(fdir_info->fdir_flow_pool.pool);
-err_fdir_bitmap_flow_alloc:
 	rte_free(fdir_info->fdir_filter_array);
 err_fdir_filter_array_alloc:
 	rte_free(fdir_info->hash_map);
@@ -1940,8 +1894,6 @@ i40e_fdir_memory_cleanup(struct i40e_pf *pf)
 	/* flow director memory cleanup */
 	rte_free(fdir_info->hash_map);
 	rte_hash_free(fdir_info->hash_table);
-	rte_free(fdir_info->fdir_flow_pool.bitmap);
-	rte_free(fdir_info->fdir_flow_pool.pool);
 	rte_free(fdir_info->fdir_filter_array);
 }
 
diff --git a/drivers/net/intel/i40e/i40e_ethdev.h b/drivers/net/intel/i40e/i40e_ethdev.h
index db04b7de22..fb255b5c11 100644
--- a/drivers/net/intel/i40e/i40e_ethdev.h
+++ b/drivers/net/intel/i40e/i40e_ethdev.h
@@ -522,6 +522,9 @@ struct i40e_vmdq_info {
 #define I40E_WORD(hi, lo) (uint16_t)((((hi) << 8) & 0xFF00) | ((lo) & 0xFF))
 #define I40E_FLEX_WORD_MASK(off) (0x80 >> (off))
 #define I40E_FDIR_IPv6_TC_OFFSET	20
+#define I40E_IPV6_TC_MASK	(0xFF << I40E_FDIR_IPv6_TC_OFFSET)
+#define I40E_IPV6_FRAG_HEADER	44
+#define I40E_VLAN_TCI_MASK	(RTE_VLAN_PRI_MASK | RTE_VLAN_DEI_MASK | RTE_VLAN_ID_MASK)
 
 /* A structure used to define the input for GTP flow */
 struct i40e_gtp_flow {
@@ -718,28 +721,12 @@ struct i40e_fdir_filter {
 	struct i40e_fdir_filter_conf fdir;
 };
 
-/* fdir memory pool entry */
-struct i40e_fdir_entry {
-	struct rte_flow flow;
-	uint32_t idx;
-};
-
-/* pre-allocated fdir memory pool */
-struct i40e_fdir_flow_pool {
-	/* a bitmap to manage the fdir pool */
-	struct rte_bitmap *bitmap;
-	/* the size the pool is pf->fdir->fdir_space_size */
-	struct i40e_fdir_entry *pool;
-};
-
-#define FLOW_TO_FLOW_BITMAP(f) \
-	container_of((f), struct i40e_fdir_entry, flow)
-
 TAILQ_HEAD(i40e_fdir_filter_list, i40e_fdir_filter);
 /*
  *  A structure used to define fields of a FDIR related info.
  */
 struct i40e_fdir_info {
+	uint64_t num_fdir_flows;
 	struct i40e_vsi *fdir_vsi;     /* pointer to fdir VSI structure */
 	uint16_t match_counter_index;  /* Statistic counter index used for fdir*/
 	struct ci_tx_queue *txq;
@@ -790,8 +777,6 @@ struct i40e_fdir_info {
 	uint32_t fdir_guarantee_free_space;
 	/* the fdir total guaranteed space */
 	uint32_t fdir_guarantee_total_space;
-	/* the pre-allocated pool of the rte_flow */
-	struct i40e_fdir_flow_pool fdir_flow_pool;
 
 	/* Mark if flex pit and mask is set */
 	bool flex_pit_flag[I40E_MAX_FLXPLD_LAYER];
@@ -1325,7 +1310,6 @@ extern const struct rte_flow_ops i40e_flow_ops;
 
 struct i40e_filter_ctx {
 	union {
-		struct i40e_fdir_filter_conf fdir_filter;
 		struct i40e_tunnel_filter_conf consistent_tunnel_filter;
 		struct i40e_rte_flow_rss_conf rss_conf;
 	};
@@ -1374,8 +1358,8 @@ void i40e_vsi_enable_queues_intr(struct i40e_vsi *vsi);
 const struct rte_memzone *i40e_memzone_reserve(const char *name,
 					uint32_t len,
 					int socket_id);
-int i40e_fdir_configure(struct rte_eth_dev *dev);
-void i40e_fdir_rx_proc_enable(struct rte_eth_dev *dev, bool on);
+int i40e_fdir_configure(struct i40e_pf *pf);
+void i40e_fdir_rx_proc_enable(struct rte_eth_dev_data *dev_data, bool on);
 void i40e_fdir_teardown(struct i40e_pf *pf);
 enum i40e_filter_pctype
 	i40e_flowtype_to_pctype(const struct i40e_adapter *adapter,
@@ -1422,11 +1406,7 @@ uint64_t i40e_get_default_input_set(uint16_t pctype);
 int i40e_ethertype_filter_set(struct i40e_pf *pf,
 			      struct rte_eth_ethertype_filter *filter,
 			      bool add);
-struct rte_flow *
-i40e_fdir_entry_pool_get(struct i40e_fdir_info *fdir_info);
-void i40e_fdir_entry_pool_put(struct i40e_fdir_info *fdir_info,
-		struct rte_flow *flow);
-int i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
+int i40e_flow_add_del_fdir_filter(struct i40e_pf *pf,
 			      const struct i40e_fdir_filter_conf *filter,
 			      bool add);
 int i40e_dev_tunnel_filter_set(struct i40e_pf *pf,
@@ -1435,7 +1415,7 @@ int i40e_dev_tunnel_filter_set(struct i40e_pf *pf,
 int i40e_dev_consistent_tunnel_filter_set(struct i40e_pf *pf,
 				  struct i40e_tunnel_filter_conf *tunnel_filter,
 				  uint8_t add);
-int i40e_fdir_flush(struct rte_eth_dev *dev);
+int i40e_fdir_flush(struct i40e_pf *pf);
 int i40e_find_all_vlan_for_mac(struct i40e_vsi *vsi,
 			       struct i40e_macvlan_filter *mv_f,
 			       int num, struct rte_ether_addr *addr);
diff --git a/drivers/net/intel/i40e/i40e_fdir.c b/drivers/net/intel/i40e/i40e_fdir.c
index 8a233f8a97..696a492467 100644
--- a/drivers/net/intel/i40e/i40e_fdir.c
+++ b/drivers/net/intel/i40e/i40e_fdir.c
@@ -377,12 +377,12 @@ i40e_init_flx_pld(struct i40e_pf *pf)
  * Enable/disable flow director RX processing in vector routines.
  */
 void
-i40e_fdir_rx_proc_enable(struct rte_eth_dev *dev, bool on)
+i40e_fdir_rx_proc_enable(struct rte_eth_dev_data *dev_data, bool on)
 {
 	int32_t i;
 
-	for (i = 0; i < dev->data->nb_rx_queues; i++) {
-		struct ci_rx_queue *rxq = dev->data->rx_queues[i];
+	for (i = 0; i < dev_data->nb_rx_queues; i++) {
+		struct ci_rx_queue *rxq = dev_data->rx_queues[i];
 		if (!rxq)
 			continue;
 		rxq->fdir_enabled = on;
@@ -394,10 +394,9 @@ i40e_fdir_rx_proc_enable(struct rte_eth_dev *dev, bool on)
  * Configure flow director related setting
  */
 int
-i40e_fdir_configure(struct rte_eth_dev *dev)
+i40e_fdir_configure(struct i40e_pf *pf)
 {
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	uint32_t val;
 	int ret = 0;
 
@@ -407,7 +406,7 @@ i40e_fdir_configure(struct rte_eth_dev *dev)
 	* If filters exist, flush them.
 	*/
 	if (i40e_fdir_empty(hw) < 0) {
-		ret = i40e_fdir_flush(dev);
+		ret = i40e_fdir_flush(pf);
 		if (ret) {
 			PMD_DRV_LOG(ERR, "failed to flush fdir table.");
 			return ret;
@@ -422,7 +421,7 @@ i40e_fdir_configure(struct rte_eth_dev *dev)
 	i40e_init_flx_pld(pf); /* set flex config to default value */
 
 	/* Enable FDIR processing in RX routines */
-	i40e_fdir_rx_proc_enable(dev, 1);
+	i40e_fdir_rx_proc_enable(pf->dev_data, 1);
 
 	return ret;
 }
@@ -1093,53 +1092,6 @@ i40e_sw_fdir_filter_del(struct i40e_pf *pf, struct i40e_fdir_input *input)
 	return 0;
 }
 
-struct rte_flow *
-i40e_fdir_entry_pool_get(struct i40e_fdir_info *fdir_info)
-{
-	struct rte_flow *flow = NULL;
-	uint64_t slab = 0;
-	uint32_t pos = 0;
-	uint32_t i = 0;
-	int ret;
-
-	if (fdir_info->fdir_actual_cnt >=
-			fdir_info->fdir_space_size) {
-		PMD_DRV_LOG(ERR, "Fdir space full");
-		return NULL;
-	}
-
-	ret = rte_bitmap_scan(fdir_info->fdir_flow_pool.bitmap, &pos,
-			&slab);
-
-	/* normally this won't happen as the fdir_actual_cnt should be
-	 * same with the number of the set bits in fdir_flow_pool,
-	 * but anyway handle this error condition here for safe
-	 */
-	if (ret == 0) {
-		PMD_DRV_LOG(ERR, "fdir_actual_cnt out of sync");
-		return NULL;
-	}
-
-	i = rte_bsf64(slab);
-	pos += i;
-	rte_bitmap_clear(fdir_info->fdir_flow_pool.bitmap, pos);
-	flow = &fdir_info->fdir_flow_pool.pool[pos].flow;
-
-	memset(flow, 0, sizeof(struct rte_flow));
-
-	return flow;
-}
-
-void
-i40e_fdir_entry_pool_put(struct i40e_fdir_info *fdir_info,
-		struct rte_flow *flow)
-{
-	struct i40e_fdir_entry *f;
-
-	f = FLOW_TO_FLOW_BITMAP(flow);
-	rte_bitmap_set(fdir_info->fdir_flow_pool.bitmap, f->idx);
-}
-
 static int
 i40e_flow_store_flex_pit(struct i40e_pf *pf,
 			 struct i40e_fdir_flex_pit *flex_pit,
@@ -1365,9 +1317,8 @@ i40e_flow_set_fdir_inset(struct i40e_pf *pf,
 }
 
 static inline unsigned char *
-i40e_find_available_buffer(struct rte_eth_dev *dev)
+i40e_find_available_buffer(struct i40e_pf *pf)
 {
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_fdir_info *fdir_info = &pf->fdir;
 	struct ci_tx_queue *txq = pf->fdir.txq;
 
@@ -1410,12 +1361,11 @@ i40e_find_available_buffer(struct rte_eth_dev *dev)
  * @add: 0 - delete, 1 - add
  */
 int
-i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
+i40e_flow_add_del_fdir_filter(struct i40e_pf *pf,
 			      const struct i40e_fdir_filter_conf *filter,
 			      bool add)
 {
-	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	enum i40e_flxpld_layer_idx layer_idx = I40E_FLXPLD_L2_IDX;
 	struct i40e_fdir_info *fdir_info = &pf->fdir;
 	uint8_t flex_mask[I40E_FDIR_MAX_FLEX_LEN];
@@ -1554,7 +1504,7 @@ i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
 	}
 
 	/* find a buffer to store the pkt */
-	pkt = i40e_find_available_buffer(dev);
+	pkt = i40e_find_available_buffer(pf);
 	if (pkt == NULL)
 		goto error_op;
 
@@ -1765,9 +1715,8 @@ i40e_flow_fdir_filter_programming(struct i40e_pf *pf,
  * @pf: board private structure
  */
 int
-i40e_fdir_flush(struct rte_eth_dev *dev)
+i40e_fdir_flush(struct i40e_pf *pf)
 {
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
 	uint32_t reg;
 	uint16_t guarant_cnt, best_cnt;
@@ -1945,7 +1894,6 @@ i40e_fdir_stats_get(struct rte_eth_dev *dev, struct rte_eth_fdir_stats *stat)
 void
 i40e_fdir_filter_restore(struct i40e_pf *pf)
 {
-	struct rte_eth_dev *dev = I40E_VSI_TO_ETH_DEV(pf->main_vsi);
 	struct i40e_fdir_filter_list *fdir_list = &pf->fdir.fdir_list;
 	struct i40e_fdir_filter *f;
 	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
@@ -1954,7 +1902,7 @@ i40e_fdir_filter_restore(struct i40e_pf *pf)
 	uint32_t best_cnt;     /**< Number of filters in best effort spaces. */
 
 	TAILQ_FOREACH(f, fdir_list, rules)
-		i40e_flow_add_del_fdir_filter(dev, &f->fdir, TRUE);
+		i40e_flow_add_del_fdir_filter(pf, &f->fdir, TRUE);
 
 	fdstat = I40E_READ_REG(hw, I40E_PFQF_FDSTAT);
 	guarant_cnt =
diff --git a/drivers/net/intel/i40e/i40e_flow.c b/drivers/net/intel/i40e/i40e_flow.c
index 9d52abf6d8..a3b82f9c62 100644
--- a/drivers/net/intel/i40e/i40e_flow.c
+++ b/drivers/net/intel/i40e/i40e_flow.c
@@ -33,14 +33,10 @@
 const struct ci_flow_engine_list i40e_flow_engine_list = {
 	{
 		&i40e_flow_engine_ethertype,
+		&i40e_flow_engine_fdir,
 	}
 };
 
-#define I40E_IPV6_TC_MASK	(0xFF << I40E_FDIR_IPv6_TC_OFFSET)
-#define I40E_IPV6_FRAG_HEADER	44
-#define I40E_TENANT_ARRAY_NUM	3
-#define I40E_VLAN_TCI_MASK	0xFFFF
-
 static int i40e_flow_validate(struct rte_eth_dev *dev,
 			      const struct rte_flow_attr *attr,
 			      const struct rte_flow_item pattern[],
@@ -64,23 +60,10 @@ static int i40e_flow_dev_dump(struct rte_eth_dev *dev,
 			      struct rte_flow *flow,
 			      FILE *file,
 			      struct rte_flow_error *error);
-static int i40e_flow_parse_fdir_pattern(struct rte_eth_dev *dev,
-					const struct rte_flow_item *pattern,
-					struct rte_flow_error *error,
-					struct i40e_fdir_filter_conf *filter);
-static int i40e_flow_parse_fdir_action(struct rte_eth_dev *dev,
-				       const struct rte_flow_action *actions,
-				       struct rte_flow_error *error,
-				       struct i40e_fdir_filter_conf *filter);
 static int i40e_flow_parse_tunnel_action(struct rte_eth_dev *dev,
 				 const struct rte_flow_action *actions,
 				 struct rte_flow_error *error,
 				 struct i40e_tunnel_filter_conf *filter);
-static int i40e_flow_parse_fdir_filter(struct rte_eth_dev *dev,
-				       const struct rte_flow_item pattern[],
-				       const struct rte_flow_action actions[],
-				       struct rte_flow_error *error,
-				       struct i40e_filter_ctx *filter);
 static int i40e_flow_parse_vxlan_filter(struct rte_eth_dev *dev,
 					const struct rte_flow_item pattern[],
 					const struct rte_flow_action actions[],
@@ -103,7 +86,6 @@ static int i40e_flow_parse_gtp_filter(struct rte_eth_dev *dev,
 				      struct i40e_filter_ctx *filter);
 static int i40e_flow_destroy_tunnel_filter(struct i40e_pf *pf,
 					   struct i40e_tunnel_filter *filter);
-static int i40e_flow_flush_fdir_filter(struct i40e_pf *pf);
 static int i40e_flow_flush_tunnel_filter(struct i40e_pf *pf);
 static int
 i40e_flow_parse_qinq_filter(struct rte_eth_dev *dev,
@@ -131,19 +113,6 @@ const struct rte_flow_ops i40e_flow_ops = {
 	.dev_dump = i40e_flow_dev_dump,
 };
 
-/* Pattern matched ethertype filter */
-static enum rte_flow_item_type pattern_ethertype[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-/* Pattern matched flow director filter */
-static enum rte_flow_item_type pattern_fdir_ipv4[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 static enum rte_flow_item_type pattern_fdir_ipv4_udp[] = {
 	RTE_FLOW_ITEM_TYPE_ETH,
 	RTE_FLOW_ITEM_TYPE_IPV4,
@@ -181,30 +150,6 @@ static enum rte_flow_item_type pattern_fdir_ipv4_gtpu[] = {
 	RTE_FLOW_ITEM_TYPE_END,
 };
 
-static enum rte_flow_item_type pattern_fdir_ipv4_gtpu_ipv4[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_GTPU,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_gtpu_ipv6[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_GTPU,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 static enum rte_flow_item_type pattern_fdir_ipv6_udp[] = {
 	RTE_FLOW_ITEM_TYPE_ETH,
 	RTE_FLOW_ITEM_TYPE_IPV6,
@@ -242,581 +187,6 @@ static enum rte_flow_item_type pattern_fdir_ipv6_gtpu[] = {
 	RTE_FLOW_ITEM_TYPE_END,
 };
 
-static enum rte_flow_item_type pattern_fdir_ipv6_gtpu_ipv4[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_GTPU,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_gtpu_ipv6[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_GTPU,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ethertype_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ethertype_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ethertype_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_udp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_udp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_udp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_tcp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_tcp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_tcp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_sctp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_sctp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_sctp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_udp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_udp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_udp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_tcp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_tcp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_tcp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_sctp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_sctp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_sctp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ethertype_vlan[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_udp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_tcp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_sctp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_udp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_tcp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_sctp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ethertype_vlan_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ethertype_vlan_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ethertype_vlan_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_udp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_udp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_udp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_tcp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_tcp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_tcp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_sctp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_sctp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv4_sctp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_udp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_udp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_udp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_tcp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_tcp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_tcp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_TCP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_sctp_raw_1[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_sctp_raw_2[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_vlan_ipv6_sctp_raw_3[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_VLAN,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_SCTP,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_RAW,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 /* Pattern matched tunnel filter */
 static enum rte_flow_item_type pattern_vxlan_1[] = {
 	RTE_FLOW_ITEM_TYPE_ETH,
@@ -929,138 +299,7 @@ static enum rte_flow_item_type pattern_qinq_1[] = {
 	RTE_FLOW_ITEM_TYPE_END,
 };
 
-static enum rte_flow_item_type pattern_fdir_ipv4_l2tpv3oip[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_L2TPV3OIP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_l2tpv3oip[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_L2TPV3OIP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_esp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_ESP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_esp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_ESP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv4_udp_esp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV4,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_ESP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
-static enum rte_flow_item_type pattern_fdir_ipv6_udp_esp[] = {
-	RTE_FLOW_ITEM_TYPE_ETH,
-	RTE_FLOW_ITEM_TYPE_IPV6,
-	RTE_FLOW_ITEM_TYPE_UDP,
-	RTE_FLOW_ITEM_TYPE_ESP,
-	RTE_FLOW_ITEM_TYPE_END,
-};
-
 static struct i40e_valid_pattern i40e_supported_patterns[] = {
-	/* FDIR - support default flow type without flexible payload*/
-	{ pattern_ethertype, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_udp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_tcp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_sctp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_gtpc, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_gtpu, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_gtpu_ipv4, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_gtpu_ipv6, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_esp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_udp_esp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_udp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_tcp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_sctp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_gtpc, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_gtpu, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_gtpu_ipv4, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_gtpu_ipv6, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_esp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_udp_esp, i40e_flow_parse_fdir_filter },
-	/* FDIR - support default flow type with flexible payload */
-	{ pattern_fdir_ethertype_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ethertype_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ethertype_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_udp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_udp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_udp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_tcp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_tcp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_tcp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_sctp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_sctp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv4_sctp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_udp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_udp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_udp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_tcp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_tcp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_tcp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_sctp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_sctp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_sctp_raw_3, i40e_flow_parse_fdir_filter },
-	/* FDIR - support single vlan input set */
-	{ pattern_fdir_ethertype_vlan, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_udp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_tcp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_sctp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_udp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_tcp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_sctp, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ethertype_vlan_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ethertype_vlan_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ethertype_vlan_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_udp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_udp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_udp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_tcp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_tcp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_tcp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_sctp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_sctp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv4_sctp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_udp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_udp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_udp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_tcp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_tcp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_tcp_raw_3, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_sctp_raw_1, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_sctp_raw_2, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_vlan_ipv6_sctp_raw_3, i40e_flow_parse_fdir_filter },
 	/* VXLAN */
 	{ pattern_vxlan_1, i40e_flow_parse_vxlan_filter },
 	{ pattern_vxlan_2, i40e_flow_parse_vxlan_filter },
@@ -1083,9 +322,6 @@ static struct i40e_valid_pattern i40e_supported_patterns[] = {
 	{ pattern_fdir_ipv6_gtpu, i40e_flow_parse_gtp_filter },
 	/* QINQ */
 	{ pattern_qinq_1, i40e_flow_parse_qinq_filter },
-	/* L2TPv3 over IP */
-	{ pattern_fdir_ipv4_l2tpv3oip, i40e_flow_parse_fdir_filter },
-	{ pattern_fdir_ipv6_l2tpv3oip, i40e_flow_parse_fdir_filter },
 	/* L4 over port */
 	{ pattern_fdir_ipv4_udp, i40e_flow_parse_l4_cloud_filter },
 	{ pattern_fdir_ipv4_tcp, i40e_flow_parse_l4_cloud_filter },
@@ -1336,47 +572,7 @@ i40e_get_outer_vlan(struct i40e_pf *pf, uint16_t *tpid)
 	return 0;
 }
 
-static int
-i40e_flow_check_raw_item(const struct rte_flow_item *item,
-			 const struct rte_flow_item_raw *raw_spec,
-			 struct rte_flow_error *error)
-{
-	if (!raw_spec->relative) {
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ITEM,
-				   item,
-				   "Relative should be 1.");
-		return -rte_errno;
-	}
-
-	if (raw_spec->offset % sizeof(uint16_t)) {
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ITEM,
-				   item,
-				   "Offset should be even.");
-		return -rte_errno;
-	}
-
-	if (raw_spec->search || raw_spec->limit) {
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ITEM,
-				   item,
-				   "search or limit is not supported.");
-		return -rte_errno;
-	}
-
-	if (raw_spec->offset < 0) {
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ITEM,
-				   item,
-				   "Offset should be non-negative.");
-		return -rte_errno;
-	}
-	return 0;
-}
-
-
-static uint8_t
+uint8_t
 i40e_flow_fdir_get_pctype_value(struct i40e_pf *pf,
 				enum rte_flow_item_type item_type,
 				struct i40e_fdir_filter_conf *filter)
@@ -1443,1013 +639,6 @@ i40e_flow_fdir_get_pctype_value(struct i40e_pf *pf,
 	return I40E_FILTER_PCTYPE_INVALID;
 }
 
-static void
-i40e_flow_set_filter_spi(struct i40e_fdir_filter_conf *filter,
-	const struct rte_flow_item_esp *esp_spec)
-{
-	if (filter->input.flow_ext.oip_type ==
-		I40E_FDIR_IPTYPE_IPV4) {
-		if (filter->input.flow_ext.is_udp)
-			filter->input.flow.esp_ipv4_udp_flow.spi =
-				esp_spec->hdr.spi;
-		else
-			filter->input.flow.esp_ipv4_flow.spi =
-				esp_spec->hdr.spi;
-	}
-	if (filter->input.flow_ext.oip_type ==
-		I40E_FDIR_IPTYPE_IPV6) {
-		if (filter->input.flow_ext.is_udp)
-			filter->input.flow.esp_ipv6_udp_flow.spi =
-				esp_spec->hdr.spi;
-		else
-			filter->input.flow.esp_ipv6_flow.spi =
-				esp_spec->hdr.spi;
-	}
-}
-
-/* 1. Last in item should be NULL as range is not supported.
- * 2. Supported patterns: refer to array i40e_supported_patterns.
- * 3. Default supported flow type and input set: refer to array
- *    valid_fdir_inset_table in i40e_ethdev.c.
- * 4. Mask of fields which need to be matched should be
- *    filled with 1.
- * 5. Mask of fields which needn't to be matched should be
- *    filled with 0.
- * 6. GTP profile supports GTPv1 only.
- * 7. GTP-C response message ('source_port' = 2123) is not supported.
- */
-static int
-i40e_flow_parse_fdir_pattern(struct rte_eth_dev *dev,
-			     const struct rte_flow_item *pattern,
-			     struct rte_flow_error *error,
-			     struct i40e_fdir_filter_conf *filter)
-{
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-	const struct rte_flow_item *item = pattern;
-	const struct rte_flow_item_eth *eth_spec, *eth_mask;
-	const struct rte_flow_item_vlan *vlan_spec, *vlan_mask;
-	const struct rte_flow_item_ipv4 *ipv4_spec, *ipv4_last, *ipv4_mask;
-	const struct rte_flow_item_ipv6 *ipv6_spec, *ipv6_mask;
-	const struct rte_flow_item_tcp *tcp_spec, *tcp_mask;
-	const struct rte_flow_item_udp *udp_spec, *udp_mask;
-	const struct rte_flow_item_sctp *sctp_spec, *sctp_mask;
-	const struct rte_flow_item_gtp *gtp_spec, *gtp_mask;
-	const struct rte_flow_item_esp *esp_spec, *esp_mask;
-	const struct rte_flow_item_raw *raw_spec, *raw_mask;
-	const struct rte_flow_item_l2tpv3oip *l2tpv3oip_spec, *l2tpv3oip_mask;
-
-	uint8_t pctype = 0;
-	uint64_t input_set = I40E_INSET_NONE;
-	enum rte_flow_item_type item_type;
-	enum rte_flow_item_type next_type;
-	enum rte_flow_item_type l3 = RTE_FLOW_ITEM_TYPE_END;
-	enum rte_flow_item_type cus_proto = RTE_FLOW_ITEM_TYPE_END;
-	uint32_t i, j;
-	uint8_t  ipv6_addr_mask[16] = {
-		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-	enum i40e_flxpld_layer_idx layer_idx = I40E_FLXPLD_L2_IDX;
-	uint8_t raw_id = 0;
-	int32_t off_arr[I40E_MAX_FLXPLD_FIED];
-	uint16_t len_arr[I40E_MAX_FLXPLD_FIED];
-	struct i40e_fdir_flex_pit flex_pit;
-	uint8_t next_dst_off = 0;
-	uint16_t flex_size;
-	uint16_t ether_type;
-	uint32_t vtc_flow_cpu;
-	bool outer_ip = true;
-	uint8_t field_idx;
-	int ret;
-	uint16_t tpid;
-
-	memset(off_arr, 0, sizeof(off_arr));
-	memset(len_arr, 0, sizeof(len_arr));
-	filter->input.flow_ext.customized_pctype = false;
-	for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
-		if (item->last && item->type != RTE_FLOW_ITEM_TYPE_IPV4) {
-			rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Not support range");
-			return -rte_errno;
-		}
-		item_type = item->type;
-		switch (item_type) {
-		case RTE_FLOW_ITEM_TYPE_ETH:
-			eth_spec = item->spec;
-			eth_mask = item->mask;
-			next_type = (item + 1)->type;
-
-			if (next_type == RTE_FLOW_ITEM_TYPE_END &&
-						(!eth_spec || !eth_mask)) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "NULL eth spec/mask.");
-				return -rte_errno;
-			}
-
-			if (eth_spec && eth_mask) {
-				if (rte_is_broadcast_ether_addr(&eth_mask->hdr.dst_addr) &&
-					rte_is_zero_ether_addr(&eth_mask->hdr.src_addr)) {
-					filter->input.flow.l2_flow.dst =
-						eth_spec->hdr.dst_addr;
-					input_set |= I40E_INSET_DMAC;
-				} else if (rte_is_zero_ether_addr(&eth_mask->hdr.dst_addr) &&
-					rte_is_broadcast_ether_addr(&eth_mask->hdr.src_addr)) {
-					filter->input.flow.l2_flow.src =
-						eth_spec->hdr.src_addr;
-					input_set |= I40E_INSET_SMAC;
-				} else if (rte_is_broadcast_ether_addr(&eth_mask->hdr.dst_addr) &&
-					rte_is_broadcast_ether_addr(&eth_mask->hdr.src_addr)) {
-					filter->input.flow.l2_flow.dst =
-						eth_spec->hdr.dst_addr;
-					filter->input.flow.l2_flow.src =
-						eth_spec->hdr.src_addr;
-					input_set |= (I40E_INSET_DMAC | I40E_INSET_SMAC);
-				} else if (!rte_is_zero_ether_addr(&eth_mask->hdr.src_addr) ||
-					   !rte_is_zero_ether_addr(&eth_mask->hdr.dst_addr)) {
-					rte_flow_error_set(error, EINVAL,
-						      RTE_FLOW_ERROR_TYPE_ITEM,
-						      item,
-						      "Invalid MAC_addr mask.");
-					return -rte_errno;
-				}
-			}
-			if (eth_spec && eth_mask &&
-			next_type == RTE_FLOW_ITEM_TYPE_END) {
-				if (eth_mask->hdr.ether_type != RTE_BE16(0xffff)) {
-					rte_flow_error_set(error, EINVAL,
-						      RTE_FLOW_ERROR_TYPE_ITEM,
-						      item,
-						      "Invalid type mask.");
-					return -rte_errno;
-				}
-
-				ether_type = rte_be_to_cpu_16(eth_spec->hdr.ether_type);
-
-				if (ether_type == RTE_ETHER_TYPE_IPV4 ||
-				    ether_type == RTE_ETHER_TYPE_IPV6) {
-					rte_flow_error_set(error, EINVAL,
-						     RTE_FLOW_ERROR_TYPE_ITEM,
-						     item,
-						     "Unsupported ether_type.");
-					return -rte_errno;
-				}
-				ret = i40e_get_outer_vlan(pf, &tpid);
-				if (ret != 0) {
-					rte_flow_error_set(error, EIO,
-							RTE_FLOW_ERROR_TYPE_ITEM,
-							item,
-							"Can not get the Ethertype identifying the L2 tag");
-					return -rte_errno;
-				}
-				if (ether_type == tpid) {
-					rte_flow_error_set(error, EINVAL,
-						     RTE_FLOW_ERROR_TYPE_ITEM,
-						     item,
-						     "Unsupported ether_type.");
-					return -rte_errno;
-				}
-
-				input_set |= I40E_INSET_LAST_ETHER_TYPE;
-				filter->input.flow.l2_flow.ether_type =
-					eth_spec->hdr.ether_type;
-			}
-
-			pctype = I40E_FILTER_PCTYPE_L2_PAYLOAD;
-			layer_idx = I40E_FLXPLD_L2_IDX;
-
-			break;
-		case RTE_FLOW_ITEM_TYPE_VLAN:
-			vlan_spec = item->spec;
-			vlan_mask = item->mask;
-
-			RTE_ASSERT(!(input_set & I40E_INSET_LAST_ETHER_TYPE));
-			if (vlan_spec && vlan_mask) {
-				if (vlan_mask->hdr.vlan_tci != 0 &&
-				    vlan_mask->hdr.vlan_tci !=
-				    rte_cpu_to_be_16(I40E_VLAN_TCI_MASK)) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Unsupported TCI mask.");
-					return -rte_errno;
-				}
-				if (vlan_mask->hdr.vlan_tci != 0) {
-					input_set |= I40E_INSET_VLAN_INNER;
-					filter->input.flow_ext.vlan_tci = vlan_spec->hdr.vlan_tci;
-				}
-			}
-			if (vlan_spec && vlan_mask && vlan_mask->hdr.eth_proto) {
-				if (vlan_mask->hdr.eth_proto != RTE_BE16(0xffff)) {
-					rte_flow_error_set(error, EINVAL,
-						      RTE_FLOW_ERROR_TYPE_ITEM,
-						      item,
-						      "Invalid inner_type"
-						      " mask.");
-					return -rte_errno;
-				}
-
-				ether_type =
-					rte_be_to_cpu_16(vlan_spec->hdr.eth_proto);
-
-				if (ether_type == RTE_ETHER_TYPE_IPV4 ||
-				    ether_type == RTE_ETHER_TYPE_IPV6) {
-					rte_flow_error_set(error, EINVAL,
-						     RTE_FLOW_ERROR_TYPE_ITEM,
-						     item,
-						     "Unsupported inner_type.");
-					return -rte_errno;
-				}
-				ret = i40e_get_outer_vlan(pf, &tpid);
-				if (ret != 0) {
-					rte_flow_error_set(error, EIO,
-							RTE_FLOW_ERROR_TYPE_ITEM,
-							item,
-							"Can not get the Ethertype identifying the L2 tag");
-					return -rte_errno;
-				}
-				if (ether_type == tpid) {
-					rte_flow_error_set(error, EINVAL,
-						     RTE_FLOW_ERROR_TYPE_ITEM,
-						     item,
-						     "Unsupported ether_type.");
-					return -rte_errno;
-				}
-
-				input_set |= I40E_INSET_LAST_ETHER_TYPE;
-				filter->input.flow.l2_flow.ether_type =
-					vlan_spec->hdr.eth_proto;
-			}
-
-			pctype = I40E_FILTER_PCTYPE_L2_PAYLOAD;
-			layer_idx = I40E_FLXPLD_L2_IDX;
-
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV4:
-			l3 = RTE_FLOW_ITEM_TYPE_IPV4;
-			ipv4_spec = item->spec;
-			ipv4_mask = item->mask;
-			ipv4_last = item->last;
-			pctype = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
-			layer_idx = I40E_FLXPLD_L3_IDX;
-
-			if (ipv4_last) {
-				if (!ipv4_spec || !ipv4_mask || !outer_ip) {
-					rte_flow_error_set(error, EINVAL,
-						RTE_FLOW_ERROR_TYPE_ITEM,
-						item,
-						"Not support range");
-					return -rte_errno;
-				}
-				/* Only fragment_offset supports range */
-				if (ipv4_last->hdr.version_ihl ||
-				    ipv4_last->hdr.type_of_service ||
-				    ipv4_last->hdr.total_length ||
-				    ipv4_last->hdr.packet_id ||
-				    ipv4_last->hdr.time_to_live ||
-				    ipv4_last->hdr.next_proto_id ||
-				    ipv4_last->hdr.hdr_checksum ||
-				    ipv4_last->hdr.src_addr ||
-				    ipv4_last->hdr.dst_addr) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Not support range");
-					return -rte_errno;
-				}
-			}
-			if (ipv4_spec && ipv4_mask && outer_ip) {
-				/* Check IPv4 mask and update input set */
-				if (ipv4_mask->hdr.version_ihl ||
-				    ipv4_mask->hdr.total_length ||
-				    ipv4_mask->hdr.packet_id ||
-				    ipv4_mask->hdr.hdr_checksum) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv4 mask.");
-					return -rte_errno;
-				}
-
-				if (ipv4_mask->hdr.src_addr == UINT32_MAX)
-					input_set |= I40E_INSET_IPV4_SRC;
-				if (ipv4_mask->hdr.dst_addr == UINT32_MAX)
-					input_set |= I40E_INSET_IPV4_DST;
-				if (ipv4_mask->hdr.type_of_service == UINT8_MAX)
-					input_set |= I40E_INSET_IPV4_TOS;
-				if (ipv4_mask->hdr.time_to_live == UINT8_MAX)
-					input_set |= I40E_INSET_IPV4_TTL;
-				if (ipv4_mask->hdr.next_proto_id == UINT8_MAX)
-					input_set |= I40E_INSET_IPV4_PROTO;
-
-				/* Check if it is fragment. */
-				uint16_t frag_mask =
-					ipv4_mask->hdr.fragment_offset;
-				uint16_t frag_spec =
-					ipv4_spec->hdr.fragment_offset;
-				uint16_t frag_last = 0;
-				if (ipv4_last)
-					frag_last =
-					ipv4_last->hdr.fragment_offset;
-				if (frag_mask) {
-					frag_mask = rte_be_to_cpu_16(frag_mask);
-					frag_spec = rte_be_to_cpu_16(frag_spec);
-					frag_last = rte_be_to_cpu_16(frag_last);
-					/* frag_off mask has to be 0x3fff */
-					if (frag_mask !=
-					    (RTE_IPV4_HDR_OFFSET_MASK |
-					    RTE_IPV4_HDR_MF_FLAG)) {
-						rte_flow_error_set(error,
-						   EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv4 fragment_offset mask");
-						return -rte_errno;
-					}
-					/*
-					 * non-frag rule:
-					 * mask=0x3fff,spec=0
-					 * frag rule:
-					 * mask=0x3fff,spec=0x8,last=0x2000
-					 */
-					if (frag_spec ==
-					    (1 << RTE_IPV4_HDR_FO_SHIFT) &&
-					    frag_last == RTE_IPV4_HDR_MF_FLAG) {
-						pctype =
-						  I40E_FILTER_PCTYPE_FRAG_IPV4;
-					} else if (frag_spec || frag_last) {
-						rte_flow_error_set(error,
-						   EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv4 fragment_offset rule");
-						return -rte_errno;
-					}
-				} else if (frag_spec || frag_last) {
-					rte_flow_error_set(error,
-						EINVAL,
-						RTE_FLOW_ERROR_TYPE_ITEM,
-						item,
-						"Invalid fragment_offset");
-					return -rte_errno;
-				}
-
-				if (input_set & (I40E_INSET_DMAC | I40E_INSET_SMAC)) {
-					if (input_set & (I40E_INSET_IPV4_SRC |
-						I40E_INSET_IPV4_DST | I40E_INSET_IPV4_TOS |
-						I40E_INSET_IPV4_TTL | I40E_INSET_IPV4_PROTO)) {
-						rte_flow_error_set(error, EINVAL,
-							RTE_FLOW_ERROR_TYPE_ITEM,
-							item,
-							"L2 and L3 input set are exclusive.");
-						return -rte_errno;
-					}
-				} else {
-					/* Get the filter info */
-					filter->input.flow.ip4_flow.proto =
-						ipv4_spec->hdr.next_proto_id;
-					filter->input.flow.ip4_flow.tos =
-						ipv4_spec->hdr.type_of_service;
-					filter->input.flow.ip4_flow.ttl =
-						ipv4_spec->hdr.time_to_live;
-					filter->input.flow.ip4_flow.src_ip =
-						ipv4_spec->hdr.src_addr;
-					filter->input.flow.ip4_flow.dst_ip =
-						ipv4_spec->hdr.dst_addr;
-
-					filter->input.flow_ext.inner_ip = false;
-					filter->input.flow_ext.oip_type =
-						I40E_FDIR_IPTYPE_IPV4;
-				}
-			} else if (!ipv4_spec && !ipv4_mask && !outer_ip) {
-				filter->input.flow_ext.inner_ip = true;
-				filter->input.flow_ext.iip_type =
-					I40E_FDIR_IPTYPE_IPV4;
-			} else if (!ipv4_spec && !ipv4_mask && outer_ip) {
-				filter->input.flow_ext.inner_ip = false;
-				filter->input.flow_ext.oip_type =
-					I40E_FDIR_IPTYPE_IPV4;
-			} else if ((ipv4_spec || ipv4_mask) && !outer_ip) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid inner IPv4 mask.");
-				return -rte_errno;
-			}
-
-			if (outer_ip)
-				outer_ip = false;
-
-			break;
-		case RTE_FLOW_ITEM_TYPE_IPV6:
-			l3 = RTE_FLOW_ITEM_TYPE_IPV6;
-			ipv6_spec = item->spec;
-			ipv6_mask = item->mask;
-			pctype = I40E_FILTER_PCTYPE_NONF_IPV6_OTHER;
-			layer_idx = I40E_FLXPLD_L3_IDX;
-
-			if (ipv6_spec && ipv6_mask && outer_ip) {
-				/* Check IPv6 mask and update input set */
-				if (ipv6_mask->hdr.payload_len) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid IPv6 mask");
-					return -rte_errno;
-				}
-
-				if (!memcmp(&ipv6_mask->hdr.src_addr,
-					    ipv6_addr_mask,
-					    sizeof(ipv6_mask->hdr.src_addr)))
-					input_set |= I40E_INSET_IPV6_SRC;
-				if (!memcmp(&ipv6_mask->hdr.dst_addr,
-					    ipv6_addr_mask,
-					    sizeof(ipv6_mask->hdr.dst_addr)))
-					input_set |= I40E_INSET_IPV6_DST;
-
-				if ((ipv6_mask->hdr.vtc_flow &
-				     rte_cpu_to_be_32(I40E_IPV6_TC_MASK))
-				    == rte_cpu_to_be_32(I40E_IPV6_TC_MASK))
-					input_set |= I40E_INSET_IPV6_TC;
-				if (ipv6_mask->hdr.proto == UINT8_MAX)
-					input_set |= I40E_INSET_IPV6_NEXT_HDR;
-				if (ipv6_mask->hdr.hop_limits == UINT8_MAX)
-					input_set |= I40E_INSET_IPV6_HOP_LIMIT;
-
-				/* Get filter info */
-				vtc_flow_cpu =
-				      rte_be_to_cpu_32(ipv6_spec->hdr.vtc_flow);
-				filter->input.flow.ipv6_flow.tc =
-					(uint8_t)(vtc_flow_cpu >>
-						  I40E_FDIR_IPv6_TC_OFFSET);
-				filter->input.flow.ipv6_flow.proto =
-					ipv6_spec->hdr.proto;
-				filter->input.flow.ipv6_flow.hop_limits =
-					ipv6_spec->hdr.hop_limits;
-
-				filter->input.flow_ext.inner_ip = false;
-				filter->input.flow_ext.oip_type =
-					I40E_FDIR_IPTYPE_IPV6;
-
-				memcpy(filter->input.flow.ipv6_flow.src_ip,
-					   &ipv6_spec->hdr.src_addr, 16);
-				memcpy(filter->input.flow.ipv6_flow.dst_ip,
-					   &ipv6_spec->hdr.dst_addr, 16);
-
-				/* Check if it is fragment. */
-				if (ipv6_spec->hdr.proto ==
-				    I40E_IPV6_FRAG_HEADER)
-					pctype = I40E_FILTER_PCTYPE_FRAG_IPV6;
-			} else if (!ipv6_spec && !ipv6_mask && !outer_ip) {
-				filter->input.flow_ext.inner_ip = true;
-				filter->input.flow_ext.iip_type =
-					I40E_FDIR_IPTYPE_IPV6;
-			} else if (!ipv6_spec && !ipv6_mask && outer_ip) {
-				filter->input.flow_ext.inner_ip = false;
-				filter->input.flow_ext.oip_type =
-					I40E_FDIR_IPTYPE_IPV6;
-			} else if ((ipv6_spec || ipv6_mask) && !outer_ip) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid inner IPv6 mask");
-				return -rte_errno;
-			}
-
-			if (outer_ip)
-				outer_ip = false;
-			break;
-		case RTE_FLOW_ITEM_TYPE_TCP:
-			tcp_spec = item->spec;
-			tcp_mask = item->mask;
-
-			if (l3 == RTE_FLOW_ITEM_TYPE_IPV4)
-				pctype =
-					I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
-			else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6)
-				pctype =
-					I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
-			if (tcp_spec && tcp_mask) {
-				/* Check TCP mask and update input set */
-				if (tcp_mask->hdr.sent_seq ||
-				    tcp_mask->hdr.recv_ack ||
-				    tcp_mask->hdr.data_off ||
-				    tcp_mask->hdr.tcp_flags ||
-				    tcp_mask->hdr.rx_win ||
-				    tcp_mask->hdr.cksum ||
-				    tcp_mask->hdr.tcp_urp) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid TCP mask");
-					return -rte_errno;
-				}
-
-				if (tcp_mask->hdr.src_port == UINT16_MAX)
-					input_set |= I40E_INSET_SRC_PORT;
-				if (tcp_mask->hdr.dst_port == UINT16_MAX)
-					input_set |= I40E_INSET_DST_PORT;
-
-				if (input_set & (I40E_INSET_DMAC | I40E_INSET_SMAC)) {
-					if (input_set &
-						(I40E_INSET_SRC_PORT | I40E_INSET_DST_PORT)) {
-						rte_flow_error_set(error, EINVAL,
-							RTE_FLOW_ERROR_TYPE_ITEM,
-							item,
-							"L2 and L4 input set are exclusive.");
-						return -rte_errno;
-					}
-				} else {
-					/* Get filter info */
-					if (l3 == RTE_FLOW_ITEM_TYPE_IPV4) {
-						filter->input.flow.tcp4_flow.src_port =
-							tcp_spec->hdr.src_port;
-						filter->input.flow.tcp4_flow.dst_port =
-							tcp_spec->hdr.dst_port;
-					} else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6) {
-						filter->input.flow.tcp6_flow.src_port =
-							tcp_spec->hdr.src_port;
-						filter->input.flow.tcp6_flow.dst_port =
-							tcp_spec->hdr.dst_port;
-					}
-				}
-			}
-
-			layer_idx = I40E_FLXPLD_L4_IDX;
-
-			break;
-		case RTE_FLOW_ITEM_TYPE_UDP:
-			udp_spec = item->spec;
-			udp_mask = item->mask;
-
-			if (l3 == RTE_FLOW_ITEM_TYPE_IPV4)
-				pctype =
-					I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
-			else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6)
-				pctype =
-					I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
-
-			if (udp_spec && udp_mask) {
-				/* Check UDP mask and update input set*/
-				if (udp_mask->hdr.dgram_len ||
-				    udp_mask->hdr.dgram_cksum) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid UDP mask");
-					return -rte_errno;
-				}
-
-				if (udp_mask->hdr.src_port == UINT16_MAX)
-					input_set |= I40E_INSET_SRC_PORT;
-				if (udp_mask->hdr.dst_port == UINT16_MAX)
-					input_set |= I40E_INSET_DST_PORT;
-
-				if (input_set & (I40E_INSET_DMAC | I40E_INSET_SMAC)) {
-					if (input_set &
-						(I40E_INSET_SRC_PORT | I40E_INSET_DST_PORT)) {
-						rte_flow_error_set(error, EINVAL,
-							RTE_FLOW_ERROR_TYPE_ITEM,
-							item,
-							"L2 and L4 input set are exclusive.");
-						return -rte_errno;
-					}
-				} else {
-					/* Get filter info */
-					if (l3 == RTE_FLOW_ITEM_TYPE_IPV4) {
-						filter->input.flow.udp4_flow.src_port =
-							udp_spec->hdr.src_port;
-						filter->input.flow.udp4_flow.dst_port =
-							udp_spec->hdr.dst_port;
-					} else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6) {
-						filter->input.flow.udp6_flow.src_port =
-							udp_spec->hdr.src_port;
-						filter->input.flow.udp6_flow.dst_port =
-							udp_spec->hdr.dst_port;
-					}
-				}
-			}
-			filter->input.flow_ext.is_udp = true;
-			layer_idx = I40E_FLXPLD_L4_IDX;
-
-			break;
-		case RTE_FLOW_ITEM_TYPE_GTPC:
-		case RTE_FLOW_ITEM_TYPE_GTPU:
-			if (!pf->gtp_support) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Unsupported protocol");
-				return -rte_errno;
-			}
-
-			gtp_spec = item->spec;
-			gtp_mask = item->mask;
-
-			if (gtp_spec && gtp_mask) {
-				if (gtp_mask->hdr.gtp_hdr_info ||
-				    gtp_mask->hdr.msg_type ||
-				    gtp_mask->hdr.plen ||
-				    gtp_mask->hdr.teid != UINT32_MAX) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid GTP mask");
-					return -rte_errno;
-				}
-
-				filter->input.flow.gtp_flow.teid =
-					gtp_spec->hdr.teid;
-				filter->input.flow_ext.customized_pctype = true;
-				cus_proto = item_type;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_ESP:
-			if (!pf->esp_support) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Unsupported ESP protocol");
-				return -rte_errno;
-			}
-
-			esp_spec = item->spec;
-			esp_mask = item->mask;
-
-			if (!esp_spec || !esp_mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid ESP item");
-				return -rte_errno;
-			}
-
-			if (esp_spec && esp_mask) {
-				if (esp_mask->hdr.spi != UINT32_MAX) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid ESP mask");
-					return -rte_errno;
-				}
-				i40e_flow_set_filter_spi(filter, esp_spec);
-				filter->input.flow_ext.customized_pctype = true;
-				cus_proto = item_type;
-			}
-			break;
-		case RTE_FLOW_ITEM_TYPE_SCTP:
-			sctp_spec = item->spec;
-			sctp_mask = item->mask;
-
-			if (l3 == RTE_FLOW_ITEM_TYPE_IPV4)
-				pctype =
-					I40E_FILTER_PCTYPE_NONF_IPV4_SCTP;
-			else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6)
-				pctype =
-					I40E_FILTER_PCTYPE_NONF_IPV6_SCTP;
-
-			if (sctp_spec && sctp_mask) {
-				/* Check SCTP mask and update input set */
-				if (sctp_mask->hdr.cksum) {
-					rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Invalid UDP mask");
-					return -rte_errno;
-				}
-
-				if (sctp_mask->hdr.src_port == UINT16_MAX)
-					input_set |= I40E_INSET_SRC_PORT;
-				if (sctp_mask->hdr.dst_port == UINT16_MAX)
-					input_set |= I40E_INSET_DST_PORT;
-				if (sctp_mask->hdr.tag == UINT32_MAX)
-					input_set |= I40E_INSET_SCTP_VT;
-
-				/* Get filter info */
-				if (l3 == RTE_FLOW_ITEM_TYPE_IPV4) {
-					filter->input.flow.sctp4_flow.src_port =
-						sctp_spec->hdr.src_port;
-					filter->input.flow.sctp4_flow.dst_port =
-						sctp_spec->hdr.dst_port;
-					filter->input.flow.sctp4_flow.verify_tag
-						= sctp_spec->hdr.tag;
-				} else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6) {
-					filter->input.flow.sctp6_flow.src_port =
-						sctp_spec->hdr.src_port;
-					filter->input.flow.sctp6_flow.dst_port =
-						sctp_spec->hdr.dst_port;
-					filter->input.flow.sctp6_flow.verify_tag
-						= sctp_spec->hdr.tag;
-				}
-			}
-
-			layer_idx = I40E_FLXPLD_L4_IDX;
-
-			break;
-		case RTE_FLOW_ITEM_TYPE_RAW:
-			raw_spec = item->spec;
-			raw_mask = item->mask;
-
-			if (!raw_spec || !raw_mask) {
-				rte_flow_error_set(error, EINVAL,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "NULL RAW spec/mask");
-				return -rte_errno;
-			}
-
-			if (pf->support_multi_driver) {
-				rte_flow_error_set(error, ENOTSUP,
-						   RTE_FLOW_ERROR_TYPE_ITEM,
-						   item,
-						   "Unsupported flexible payload.");
-				return -rte_errno;
-			}
-
-			ret = i40e_flow_check_raw_item(item, raw_spec, error);
-			if (ret < 0)
-				return ret;
-
-			off_arr[raw_id] = raw_spec->offset;
-			len_arr[raw_id] = raw_spec->length;
-
-			flex_size = 0;
-			memset(&flex_pit, 0, sizeof(struct i40e_fdir_flex_pit));
-			field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + raw_id;
-			flex_pit.size =
-				raw_spec->length / sizeof(uint16_t);
-			flex_pit.dst_offset =
-				next_dst_off / sizeof(uint16_t);
-
-			for (i = 0; i <= raw_id; i++) {
-				if (i == raw_id)
-					flex_pit.src_offset +=
-						raw_spec->offset /
-						sizeof(uint16_t);
-				else
-					flex_pit.src_offset +=
-						(off_arr[i] + len_arr[i]) /
-						sizeof(uint16_t);
-				flex_size += len_arr[i];
-			}
-			if (((flex_pit.src_offset + flex_pit.size) >=
-			     I40E_MAX_FLX_SOURCE_OFF / sizeof(uint16_t)) ||
-				flex_size > I40E_FDIR_MAX_FLEXLEN) {
-				rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Exceeds maximal payload limit.");
-				return -rte_errno;
-			}
-
-			if (raw_spec->length != 0) {
-				if (raw_spec->pattern == NULL) {
-					rte_flow_error_set(error, EINVAL,
-							   RTE_FLOW_ERROR_TYPE_ITEM,
-							   item,
-							   "NULL RAW spec pattern");
-					return -rte_errno;
-				}
-				if (raw_mask->pattern == NULL) {
-					rte_flow_error_set(error, EINVAL,
-							   RTE_FLOW_ERROR_TYPE_ITEM,
-							   item,
-							   "NULL RAW mask pattern");
-					return -rte_errno;
-				}
-			}
-
-			for (i = 0; i < raw_spec->length; i++) {
-				j = i + next_dst_off;
-				if (j >= RTE_ETH_FDIR_MAX_FLEXLEN ||
-						j >= I40E_FDIR_MAX_FLEX_LEN)
-					break;
-				filter->input.flow_ext.flexbytes[j] =
-					raw_spec->pattern[i];
-				filter->input.flow_ext.flex_mask[j] =
-					raw_mask->pattern[i];
-			}
-
-			next_dst_off += raw_spec->length;
-			raw_id++;
-
-			filter->input.flow_ext.flex_pit[field_idx] = flex_pit;
-			filter->input.flow_ext.layer_idx = layer_idx;
-			filter->input.flow_ext.raw_id = raw_id;
-			filter->input.flow_ext.is_flex_flow = true;
-			break;
-		case RTE_FLOW_ITEM_TYPE_L2TPV3OIP:
-			l2tpv3oip_spec = item->spec;
-			l2tpv3oip_mask = item->mask;
-
-			if (!l2tpv3oip_spec || !l2tpv3oip_mask)
-				break;
-
-			if (l2tpv3oip_mask->session_id != UINT32_MAX) {
-				rte_flow_error_set(error, EINVAL,
-					RTE_FLOW_ERROR_TYPE_ITEM,
-					item,
-					"Invalid L2TPv3 mask");
-				return -rte_errno;
-			}
-
-			if (l3 == RTE_FLOW_ITEM_TYPE_IPV4) {
-				filter->input.flow.ip4_l2tpv3oip_flow.session_id =
-					l2tpv3oip_spec->session_id;
-				filter->input.flow_ext.oip_type =
-					I40E_FDIR_IPTYPE_IPV4;
-			} else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6) {
-				filter->input.flow.ip6_l2tpv3oip_flow.session_id =
-					l2tpv3oip_spec->session_id;
-				filter->input.flow_ext.oip_type =
-					I40E_FDIR_IPTYPE_IPV6;
-			}
-
-			filter->input.flow_ext.customized_pctype = true;
-			cus_proto = item_type;
-			break;
-		default:
-			break;
-		}
-	}
-
-	/* Get customized pctype value */
-	if (filter->input.flow_ext.customized_pctype) {
-		pctype = i40e_flow_fdir_get_pctype_value(pf, cus_proto, filter);
-		if (pctype == I40E_FILTER_PCTYPE_INVALID) {
-			rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Unsupported pctype");
-			return -rte_errno;
-		}
-	}
-
-	/* If customized pctype is not used, set fdir configuration.*/
-	if (!filter->input.flow_ext.customized_pctype) {
-		/* Check if the input set is valid */
-		if (i40e_validate_input_set(pctype, RTE_ETH_FILTER_FDIR,
-						input_set) != 0) {
-			rte_flow_error_set(error, EINVAL,
-					   RTE_FLOW_ERROR_TYPE_ITEM,
-					   item,
-					   "Invalid input set");
-			return -rte_errno;
-		}
-
-		filter->input.flow_ext.input_set = input_set;
-	}
-
-	filter->input.pctype = pctype;
-
-	return 0;
-}
-
-/* Parse to get the action info of a FDIR filter.
- * FDIR action supports QUEUE or (QUEUE + MARK).
- */
-static int
-i40e_flow_parse_fdir_action(struct rte_eth_dev *dev,
-			    const struct rte_flow_action *actions,
-			    struct rte_flow_error *error,
-			    struct i40e_fdir_filter_conf *filter)
-{
-	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-	struct ci_flow_actions parsed_actions = {0};
-	struct ci_flow_actions_check_param ac_param = {
-		.allowed_types = (enum rte_flow_action_type[]) {
-			RTE_FLOW_ACTION_TYPE_QUEUE,
-			RTE_FLOW_ACTION_TYPE_DROP,
-			RTE_FLOW_ACTION_TYPE_PASSTHRU,
-			RTE_FLOW_ACTION_TYPE_MARK,
-			RTE_FLOW_ACTION_TYPE_FLAG,
-			RTE_FLOW_ACTION_TYPE_RSS,
-			RTE_FLOW_ACTION_TYPE_END
-		},
-		.max_actions = 2,
-	};
-	const struct rte_flow_action *first, *second;
-	int ret;
-
-	ret = ci_flow_check_actions(actions, &ac_param, &parsed_actions, error);
-	if (ret)
-		return ret;
-	first = parsed_actions.actions[0];
-	/* can be NULL */
-	second = parsed_actions.actions[1];
-
-	switch (first->type) {
-	case RTE_FLOW_ACTION_TYPE_QUEUE:
-	{
-		const struct rte_flow_action_queue *act_q = first->conf;
-		/* check against PF constraints */
-		if (!filter->input.flow_ext.is_vf && act_q->index >= pf->dev_data->nb_rx_queues) {
-			return rte_flow_error_set(error, EINVAL,
-					RTE_FLOW_ERROR_TYPE_ACTION, first,
-					"Invalid queue ID for FDIR");
-		}
-		/* check against VF constraints */
-		if (filter->input.flow_ext.is_vf && act_q->index >= pf->vf_nb_qps) {
-			return rte_flow_error_set(error, EINVAL,
-					RTE_FLOW_ERROR_TYPE_ACTION, first,
-					"Invalid queue ID for FDIR");
-		}
-		filter->action.rx_queue = act_q->index;
-		filter->action.behavior = I40E_FDIR_ACCEPT;
-		break;
-	}
-	case RTE_FLOW_ACTION_TYPE_DROP:
-		filter->action.behavior = I40E_FDIR_REJECT;
-		break;
-	case RTE_FLOW_ACTION_TYPE_PASSTHRU:
-		filter->action.behavior = I40E_FDIR_PASSTHRU;
-		break;
-	case RTE_FLOW_ACTION_TYPE_MARK:
-	{
-		const struct rte_flow_action_mark *act_m = first->conf;
-		filter->action.behavior = I40E_FDIR_PASSTHRU;
-		filter->action.report_status = I40E_FDIR_REPORT_ID;
-		filter->soft_id = act_m->id;
-		break;
-	}
-	default:
-		return rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ACTION, first,
-				"Invalid first action for FDIR");
-	}
-
-	/* do we have another? */
-	if (second == NULL)
-		return 0;
-
-	switch (second->type) {
-	case RTE_FLOW_ACTION_TYPE_MARK:
-	{
-		const struct rte_flow_action_mark *act_m = second->conf;
-		/* only one mark action can be specified */
-		if (first->type == RTE_FLOW_ACTION_TYPE_MARK) {
-			return rte_flow_error_set(error, EINVAL,
-						  RTE_FLOW_ERROR_TYPE_ACTION, second,
-						  "Invalid second action for FDIR");
-		}
-		filter->action.report_status = I40E_FDIR_REPORT_ID;
-		filter->soft_id = act_m->id;
-		break;
-	}
-	case RTE_FLOW_ACTION_TYPE_FLAG:
-	{
-		/* mark + flag is unsupported */
-		if (first->type == RTE_FLOW_ACTION_TYPE_MARK) {
-			return rte_flow_error_set(error, EINVAL,
-					RTE_FLOW_ERROR_TYPE_ACTION, second,
-					"Invalid second action for FDIR");
-		}
-		filter->action.report_status = I40E_FDIR_NO_REPORT_STATUS;
-		break;
-	}
-	case RTE_FLOW_ACTION_TYPE_RSS:
-		/* RSS filter only can be after passthru or mark */
-		if (first->type != RTE_FLOW_ACTION_TYPE_PASSTHRU &&
-				first->type != RTE_FLOW_ACTION_TYPE_MARK) {
-			return rte_flow_error_set(error, EINVAL,
-					RTE_FLOW_ERROR_TYPE_ACTION, second,
-					"Invalid second action for FDIR");
-		}
-		break;
-	default:
-		return rte_flow_error_set(error, EINVAL,
-					  RTE_FLOW_ERROR_TYPE_ACTION, second,
-					  "Invalid second action for FDIR");
-	}
-
-	return 0;
-}
-
-static int
-i40e_flow_parse_fdir_filter(struct rte_eth_dev *dev,
-			    const struct rte_flow_item pattern[],
-			    const struct rte_flow_action actions[],
-			    struct rte_flow_error *error,
-			    struct i40e_filter_ctx *filter)
-{
-	struct i40e_fdir_filter_conf *fdir_filter = &filter->fdir_filter;
-	int ret;
-
-	ret = i40e_flow_parse_fdir_pattern(dev, pattern, error, fdir_filter);
-	if (ret)
-		return ret;
-
-	ret = i40e_flow_parse_fdir_action(dev, actions, error, fdir_filter);
-	if (ret)
-		return ret;
-
-	filter->type = RTE_ETH_FILTER_FDIR;
-
-	return 0;
-}
-
 /* Parse to get the action info of a tunnel filter
  * Tunnel action only supports PF, VF and QUEUE.
  */
@@ -3747,7 +1936,6 @@ i40e_flow_create(struct rte_eth_dev *dev,
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_filter_ctx filter_ctx = {0};
 	struct rte_flow *flow = NULL;
-	struct i40e_fdir_info *fdir_info = &pf->fdir;
 	int ret;
 
 	/* try the new engine first */
@@ -3759,55 +1947,15 @@ i40e_flow_create(struct rte_eth_dev *dev,
 	if (ret < 0)
 		return NULL;
 
-	if (filter_ctx.type == RTE_ETH_FILTER_FDIR) {
-		/* if this is the first time we're creating an fdir flow */
-		if (pf->fdir.fdir_vsi == NULL) {
-			ret = i40e_fdir_setup(pf);
-			if (ret != I40E_SUCCESS) {
-				rte_flow_error_set(error, ENOTSUP,
-						RTE_FLOW_ERROR_TYPE_HANDLE,
-						NULL, "Failed to setup fdir.");
-				return NULL;
-			}
-			ret = i40e_fdir_configure(dev);
-			if (ret < 0) {
-				rte_flow_error_set(error, ENOTSUP,
-						RTE_FLOW_ERROR_TYPE_HANDLE,
-						NULL, "Failed to configure fdir.");
-				i40e_fdir_teardown(pf);
-				return NULL;
-			}
-		}
-		/* If create the first fdir rule, enable fdir check for rx queues */
-		if (TAILQ_EMPTY(&pf->fdir.fdir_list))
-			i40e_fdir_rx_proc_enable(dev, 1);
-
-		flow = i40e_fdir_entry_pool_get(fdir_info);
-		if (flow == NULL) {
-			rte_flow_error_set(error, ENOBUFS,
-			   RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
-			   "Fdir space full");
-
-			return flow;
-		}
-	} else {
-		flow = rte_zmalloc("i40e_flow", sizeof(struct rte_flow), 0);
-		if (!flow) {
-			rte_flow_error_set(error, ENOMEM,
-					   RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
-					   "Failed to allocate memory");
-			return flow;
-		}
+	flow = rte_zmalloc("i40e_flow", sizeof(struct rte_flow), 0);
+	if (!flow) {
+		rte_flow_error_set(error, ENOMEM,
+					RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+					"Failed to allocate memory");
+		return flow;
 	}
 
 	switch (filter_ctx.type) {
-	case RTE_ETH_FILTER_FDIR:
-		ret = i40e_flow_add_del_fdir_filter(dev, &filter_ctx.fdir_filter, 1);
-		if (ret)
-			goto free_flow;
-		flow->rule = TAILQ_LAST(&pf->fdir.fdir_list,
-					i40e_fdir_filter_list);
-		break;
 	case RTE_ETH_FILTER_TUNNEL:
 		ret = i40e_dev_consistent_tunnel_filter_set(pf,
 				&filter_ctx.consistent_tunnel_filter, 1);
@@ -3836,10 +1984,7 @@ i40e_flow_create(struct rte_eth_dev *dev,
 			   RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
 			   "Failed to create flow.");
 
-	if (filter_ctx.type != RTE_ETH_FILTER_FDIR)
-		rte_free(flow);
-	else
-		i40e_fdir_entry_pool_put(fdir_info, flow);
+	rte_free(flow);
 
 	return NULL;
 }
@@ -3851,7 +1996,6 @@ i40e_flow_destroy(struct rte_eth_dev *dev,
 {
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	enum rte_filter_type filter_type = flow->filter_type;
-	struct i40e_fdir_info *fdir_info = &pf->fdir;
 	int ret = 0;
 
 	/* try the new engine first */
@@ -3864,16 +2008,6 @@ i40e_flow_destroy(struct rte_eth_dev *dev,
 		ret = i40e_flow_destroy_tunnel_filter(pf,
 			      (struct i40e_tunnel_filter *)flow->rule);
 		break;
-	case RTE_ETH_FILTER_FDIR:
-		ret = i40e_flow_add_del_fdir_filter(dev,
-				&((struct i40e_fdir_filter *)flow->rule)->fdir,
-				0);
-
-		/* If the last flow is destroyed, disable fdir. */
-		if (!ret && TAILQ_EMPTY(&pf->fdir.fdir_list)) {
-			i40e_fdir_rx_proc_enable(dev, 0);
-		}
-		break;
 	case RTE_ETH_FILTER_HASH:
 		ret = i40e_hash_filter_destroy(pf, flow->rule);
 		break;
@@ -3886,10 +2020,7 @@ i40e_flow_destroy(struct rte_eth_dev *dev,
 
 	if (!ret) {
 		TAILQ_REMOVE(&pf->flow_list, flow, node);
-		if (filter_type == RTE_ETH_FILTER_FDIR)
-			i40e_fdir_entry_pool_put(fdir_info, flow);
-		else
-			rte_free(flow);
+		rte_free(flow);
 
 	} else
 		rte_flow_error_set(error, -ret,
@@ -3969,14 +2100,6 @@ i40e_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 	if (ret != 0)
 		return ret;
 
-	ret = i40e_flow_flush_fdir_filter(pf);
-	if (ret) {
-		rte_flow_error_set(error, -ret,
-				   RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
-				   "Failed to flush FDIR flows.");
-		return -rte_errno;
-	}
-
 	ret = i40e_flow_flush_tunnel_filter(pf);
 	if (ret) {
 		rte_flow_error_set(error, -ret,
@@ -3993,66 +2116,6 @@ i40e_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 	return ret;
 }
 
-static int
-i40e_flow_flush_fdir_filter(struct i40e_pf *pf)
-{
-	struct rte_eth_dev *dev = &rte_eth_devices[pf->dev_data->port_id];
-	struct i40e_fdir_info *fdir_info = &pf->fdir;
-	struct i40e_fdir_filter *fdir_filter;
-	enum i40e_filter_pctype pctype;
-	struct rte_flow *flow;
-	void *temp;
-	int ret;
-	uint32_t i = 0;
-
-	ret = i40e_fdir_flush(dev);
-	if (!ret) {
-		/* Delete FDIR filters in FDIR list. */
-		while ((fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list))) {
-			ret = i40e_sw_fdir_filter_del(pf,
-						      &fdir_filter->fdir.input);
-			if (ret < 0)
-				return ret;
-		}
-
-		/* Delete FDIR flows in flow list. */
-		RTE_TAILQ_FOREACH_SAFE(flow, &pf->flow_list, node, temp) {
-			if (flow->filter_type == RTE_ETH_FILTER_FDIR) {
-				TAILQ_REMOVE(&pf->flow_list, flow, node);
-			}
-		}
-
-		/* reset bitmap */
-		rte_bitmap_reset(fdir_info->fdir_flow_pool.bitmap);
-		for (i = 0; i < fdir_info->fdir_space_size; i++) {
-			fdir_info->fdir_flow_pool.pool[i].idx = i;
-			rte_bitmap_set(fdir_info->fdir_flow_pool.bitmap, i);
-		}
-
-		fdir_info->fdir_actual_cnt = 0;
-		fdir_info->fdir_guarantee_free_space =
-			fdir_info->fdir_guarantee_total_space;
-		memset(fdir_info->fdir_filter_array,
-			0,
-			sizeof(struct i40e_fdir_filter) *
-			I40E_MAX_FDIR_FILTER_NUM);
-
-		for (pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
-		     pctype <= I40E_FILTER_PCTYPE_L2_PAYLOAD; pctype++) {
-			pf->fdir.flow_count[pctype] = 0;
-			pf->fdir.flex_mask_flag[pctype] = 0;
-		}
-
-		for (i = 0; i < I40E_MAX_FLXPLD_LAYER; i++)
-			pf->fdir.flex_pit_flag[i] = 0;
-
-		/* Disable FDIR processing as all FDIR rules are now flushed */
-		i40e_fdir_rx_proc_enable(dev, 0);
-	}
-
-	return ret;
-}
-
 /* Flush all tunnel filters */
 static int
 i40e_flow_flush_tunnel_filter(struct i40e_pf *pf)
diff --git a/drivers/net/intel/i40e/i40e_flow.h b/drivers/net/intel/i40e/i40e_flow.h
index 11d13a76fe..df5e133a5e 100644
--- a/drivers/net/intel/i40e/i40e_flow.h
+++ b/drivers/net/intel/i40e/i40e_flow.h
@@ -8,9 +8,14 @@
 #include "../common/flow_engine.h"
 
 int i40e_get_outer_vlan(struct i40e_pf *pf, uint16_t *tpid);
+uint8_t
+i40e_flow_fdir_get_pctype_value(struct i40e_pf *pf,
+		enum rte_flow_item_type item_type,
+		struct i40e_fdir_filter_conf *filter);
 
 extern const struct ci_flow_engine_list i40e_flow_engine_list;
 
 extern const struct ci_flow_engine i40e_flow_engine_ethertype;
+extern const struct ci_flow_engine i40e_flow_engine_fdir;
 
 #endif /* _I40E_FLOW_H_ */
diff --git a/drivers/net/intel/i40e/i40e_flow_fdir.c b/drivers/net/intel/i40e/i40e_flow_fdir.c
new file mode 100644
index 0000000000..5538e160b3
--- /dev/null
+++ b/drivers/net/intel/i40e/i40e_flow_fdir.c
@@ -0,0 +1,1834 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#include "i40e_ethdev.h"
+#include "i40e_flow.h"
+
+#include <rte_bitmap.h>
+#include <rte_malloc.h>
+
+#include "../common/flow_engine.h"
+#include "../common/flow_check.h"
+#include "../common/flow_util.h"
+
+struct i40e_fdir_ctx {
+	struct ci_flow_engine_ctx base;
+	struct i40e_fdir_filter_conf fdir_filter;
+	enum rte_flow_item_type custom_pctype;
+	struct flex_item {
+		size_t size;
+		size_t offset;
+	} flex_data[I40E_MAX_FLXPLD_FIED];
+};
+
+struct i40e_flow_engine_fdir_flow {
+	struct rte_flow base;
+	struct i40e_fdir_filter_conf fdir_filter;
+};
+
+struct i40e_fdir_flow_pool_entry {
+	struct i40e_flow_engine_fdir_flow flow;
+	uint32_t idx;
+};
+
+struct i40e_fdir_engine_priv {
+	struct rte_bitmap *bmp;
+	struct i40e_fdir_flow_pool_entry *pool;
+};
+
+#define I40E_FDIR_FLOW_ENTRY(flow_ptr) \
+	container_of((flow_ptr), struct i40e_fdir_flow_pool_entry, flow)
+
+/**
+ * FDIR graph implementation (non-tunnel)
+ * Pattern: START -> ETH -> [VLAN] -> (IPv4 | IPv6) -> [TCP | UDP | SCTP | ESP | L2TPv3 | GTP] -> END
+ * With RAW flexible payload support:
+ *   - L2: ETH/VLAN -> RAW -> RAW -> RAW -> END
+ *   - L3: IPv4/IPv6 -> RAW -> RAW -> RAW -> END
+ *   - L4: TCP/UDP/SCTP -> RAW -> RAW -> RAW -> END
+ * GTP tunnel support:
+ *   - IPv4/IPv6 -> UDP -> GTP -> END (GTP-C, GTP-U outer)
+ *   - IPv4/IPv6 -> UDP -> GTP -> IPv4/IPv6 -> END (GTP-U with inner IP)
+ */
+
+enum i40e_fdir_node_id {
+	I40E_FDIR_NODE_START = RTE_FLOW_NODE_FIRST,
+	I40E_FDIR_NODE_ETH,
+	I40E_FDIR_NODE_VLAN,
+	I40E_FDIR_NODE_IPV4,
+	I40E_FDIR_NODE_IPV6,
+	I40E_FDIR_NODE_TCP,
+	I40E_FDIR_NODE_UDP,
+	I40E_FDIR_NODE_SCTP,
+	I40E_FDIR_NODE_ESP,
+	I40E_FDIR_NODE_L2TPV3OIP,
+	I40E_FDIR_NODE_GTPC,
+	I40E_FDIR_NODE_GTPU,
+	I40E_FDIR_NODE_INNER_IPV4,
+	I40E_FDIR_NODE_INNER_IPV6,
+	I40E_FDIR_NODE_RAW,
+	I40E_FDIR_NODE_END,
+	I40E_FDIR_NODE_MAX,
+};
+
+static int
+i40e_fdir_node_eth_validate(const void *ctx __rte_unused, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_eth *eth_spec = item->spec;
+	const struct rte_flow_item_eth *eth_mask = item->mask;
+	bool no_src_mac, no_dst_mac, src_mac, dst_mac;
+
+	/* may be empty */
+	if (eth_spec == NULL && eth_mask == NULL)
+		return 0;
+
+	/* source and destination masks may be all zero or all one */
+	no_src_mac = CI_FIELD_IS_ZERO(&eth_mask->hdr.src_addr);
+	no_dst_mac = CI_FIELD_IS_ZERO(&eth_mask->hdr.dst_addr);
+	src_mac = CI_FIELD_IS_MASKED(&eth_mask->hdr.src_addr);
+	dst_mac = CI_FIELD_IS_MASKED(&eth_mask->hdr.dst_addr);
+
+	/* can't be all zero */
+	if (no_src_mac && no_dst_mac) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item, "Invalid eth mask");
+	}
+	/* can't be neither zero nor ones */
+	if ((!no_src_mac && !src_mac) ||
+	    (!no_dst_mac && !dst_mac)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item, "Invalid eth mask");
+	}
+
+	/* ethertype can either be unmasked or fully masked */
+	if (CI_FIELD_IS_ZERO(&eth_mask->hdr.ether_type))
+		return 0;
+
+	if (!CI_FIELD_IS_MASKED(&eth_mask->hdr.ether_type)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item, "Invalid ethertype mask");
+	}
+
+	/* Check for valid ethertype (not IPv4/IPv6) */
+	uint16_t ether_type = rte_be_to_cpu_16(eth_spec->hdr.ether_type);
+	if (ether_type == RTE_ETHER_TYPE_IPV4 ||
+	    ether_type == RTE_ETHER_TYPE_IPV6) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"IPv4/IPv6 not supported by ethertype filter");
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_eth_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *fdir_conf = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_eth *eth_spec = item->spec;
+	const struct rte_flow_item_eth *eth_mask = item->mask;
+	uint16_t tpid, ether_type;
+	uint64_t input_set = 0;
+	int ret;
+
+	/* Set layer index for L2 flexible payload (after ETH/VLAN) */
+	fdir_conf->input.flow_ext.layer_idx = I40E_FLXPLD_L2_IDX;
+
+	/* set packet type */
+	fdir_conf->input.pctype = I40E_FILTER_PCTYPE_L2_PAYLOAD;
+
+	/* do we need to set up MAC addresses? */
+	if (eth_spec == NULL && eth_mask == NULL)
+		return 0;
+
+	/* do we care for source address? */
+	if (CI_FIELD_IS_MASKED(&eth_mask->hdr.src_addr)) {
+		fdir_conf->input.flow.l2_flow.src = eth_spec->hdr.src_addr;
+		input_set |= I40E_INSET_SMAC;
+	}
+	/* do we care for destination address? */
+	if (CI_FIELD_IS_MASKED(&eth_mask->hdr.dst_addr)) {
+		fdir_conf->input.flow.l2_flow.dst = eth_spec->hdr.dst_addr;
+		input_set |= I40E_INSET_DMAC;
+	}
+
+	/* do we care for ethertype? */
+	if (eth_mask->hdr.ether_type) {
+		struct i40e_pf *pf =
+				I40E_DEV_PRIVATE_TO_PF(fdir_ctx->base.dev_data->dev_private);
+
+		ether_type = rte_be_to_cpu_16(eth_spec->hdr.ether_type);
+		ret = i40e_get_outer_vlan(pf, &tpid);
+		if (ret != 0) {
+			return rte_flow_error_set(error, EIO,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Can not get the Ethertype identifying the L2 tag");
+		}
+		if (ether_type == tpid) {
+			return rte_flow_error_set(error, EINVAL,
+						RTE_FLOW_ERROR_TYPE_ITEM, item,
+						"Unsupported ether_type in control packet filter.");
+		}
+		fdir_conf->input.flow.l2_flow.ether_type = eth_spec->hdr.ether_type;
+		input_set |= I40E_INSET_LAST_ETHER_TYPE;
+	}
+
+	fdir_conf->input.flow_ext.input_set = input_set;
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_vlan_validate(const void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_vlan *vlan_spec = item->spec;
+	const struct rte_flow_item_vlan *vlan_mask = item->mask;
+	const struct i40e_fdir_ctx *fdir_ctx = ctx;
+	const struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	uint16_t ether_type;
+
+	if (vlan_spec == NULL && vlan_mask == NULL)
+		return 0;
+
+	/* TCI mask can be either fully disabled or fully enabled. */
+	if (vlan_mask->hdr.vlan_tci != 0 &&
+	    vlan_mask->hdr.vlan_tci != rte_cpu_to_be_16(I40E_VLAN_TCI_MASK)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Unsupported TCI mask");
+	}
+	if (CI_FIELD_IS_ZERO(&vlan_mask->hdr.eth_proto))
+		return 0;
+
+	if (!CI_FIELD_IS_MASKED(&vlan_mask->hdr.eth_proto)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid VLAN header mask");
+	}
+
+	/* can't match on eth_proto as we're already matching on ethertype */
+	if (filter->input.flow_ext.input_set & I40E_INSET_LAST_ETHER_TYPE) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Cannot set two ethertype filters");
+	}
+
+	ether_type = rte_be_to_cpu_16(vlan_spec->hdr.eth_proto);
+	if (ether_type == RTE_ETHER_TYPE_IPV4 ||
+	    ether_type == RTE_ETHER_TYPE_IPV6) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"IPv4/IPv6 not supported by VLAN protocol filter");
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_vlan_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_vlan *vlan_spec = item->spec;
+	const struct rte_flow_item_vlan *vlan_mask = item->mask;
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+
+	/* Set layer index for L2 flexible payload (after ETH/VLAN) */
+	filter->input.flow_ext.layer_idx = I40E_FLXPLD_L2_IDX;
+
+	/* set packet type */
+	filter->input.pctype = I40E_FILTER_PCTYPE_L2_PAYLOAD;
+
+	if (vlan_spec == NULL && vlan_mask == NULL)
+		return 0;
+
+	/* Store TCI value if requested */
+	if (vlan_mask->hdr.vlan_tci) {
+		filter->input.flow_ext.vlan_tci = vlan_spec->hdr.vlan_tci;
+		filter->input.flow_ext.input_set |= I40E_INSET_VLAN_INNER;
+	}
+
+	/* if ethertype specified, store it */
+	if (vlan_mask->hdr.eth_proto) {
+		struct i40e_pf *pf =
+				I40E_DEV_PRIVATE_TO_PF(fdir_ctx->base.dev_data->dev_private);
+		uint16_t tpid, ether_type;
+		int ret;
+
+		ether_type = rte_be_to_cpu_16(vlan_spec->hdr.eth_proto);
+
+		ret = i40e_get_outer_vlan(pf, &tpid);
+		if (ret != 0) {
+			return rte_flow_error_set(error, EIO,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Can not get the Ethertype identifying the L2 tag");
+		}
+		if (ether_type == tpid) {
+			return rte_flow_error_set(error, EINVAL,
+						RTE_FLOW_ERROR_TYPE_ITEM, item,
+						"Unsupported ether_type in control packet filter.");
+		}
+		filter->input.flow.l2_flow.ether_type = vlan_spec->hdr.eth_proto;
+		filter->input.flow_ext.input_set |= I40E_INSET_LAST_ETHER_TYPE;
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_ipv4_validate(const void *ctx __rte_unused, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_ipv4 *ipv4_spec = item->spec;
+	const struct rte_flow_item_ipv4 *ipv4_mask = item->mask;
+	const struct rte_flow_item_ipv4 *ipv4_last = item->last;
+
+	if (ipv4_mask == NULL && ipv4_spec == NULL)
+		return 0;
+
+	/* Validate mask fields */
+	if (ipv4_mask->hdr.version_ihl ||
+			ipv4_mask->hdr.total_length ||
+			ipv4_mask->hdr.packet_id ||
+			ipv4_mask->hdr.hdr_checksum) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid IPv4 header mask");
+	}
+	if (!CI_FIELD_IS_ZERO_OR_MASKED(&ipv4_mask->hdr.src_addr) ||
+			!CI_FIELD_IS_ZERO_OR_MASKED(&ipv4_mask->hdr.dst_addr) ||
+			!CI_FIELD_IS_ZERO_OR_MASKED(&ipv4_mask->hdr.type_of_service) ||
+			!CI_FIELD_IS_ZERO_OR_MASKED(&ipv4_mask->hdr.time_to_live) ||
+			!CI_FIELD_IS_ZERO_OR_MASKED(&ipv4_mask->hdr.next_proto_id)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid IPv4 header mask");
+	}
+
+	if (ipv4_last == NULL)
+		return 0;
+
+	/* Only fragment_offset supports range */
+	if (ipv4_last->hdr.version_ihl ||
+	    ipv4_last->hdr.type_of_service ||
+	    ipv4_last->hdr.total_length ||
+	    ipv4_last->hdr.packet_id ||
+	    ipv4_last->hdr.time_to_live ||
+	    ipv4_last->hdr.next_proto_id ||
+	    ipv4_last->hdr.hdr_checksum ||
+	    ipv4_last->hdr.src_addr ||
+	    ipv4_last->hdr.dst_addr) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"IPv4 range only supported for fragment_offset");
+	}
+
+	/* Validate fragment_offset range values */
+	uint16_t frag_mask = rte_be_to_cpu_16(ipv4_mask->hdr.fragment_offset);
+	uint16_t frag_spec = rte_be_to_cpu_16(ipv4_spec->hdr.fragment_offset);
+	uint16_t frag_last = rte_be_to_cpu_16(ipv4_last->hdr.fragment_offset);
+
+	/* Mask must be 0x3fff (fragment offset + MF flag) */
+	if (frag_mask != (RTE_IPV4_HDR_OFFSET_MASK | RTE_IPV4_HDR_MF_FLAG)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid IPv4 fragment_offset mask");
+	}
+
+	/* Only allow: frag rule (spec=0x8, last=0x2000) or non-frag (spec=0, last=0) */
+	if (frag_spec == (1 << RTE_IPV4_HDR_FO_SHIFT) &&
+	    frag_last == RTE_IPV4_HDR_MF_FLAG)
+		return 0; /* Fragment rule */
+
+	if (frag_spec == 0 && frag_last == 0)
+		return 0; /* Non-fragment rule */
+
+	return rte_flow_error_set(error, EINVAL,
+			RTE_FLOW_ERROR_TYPE_ITEM, item,
+			"Invalid IPv4 fragment_offset rule");
+}
+
+static int
+i40e_fdir_node_ipv4_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error __rte_unused)
+{
+	const struct rte_flow_item_ipv4 *ipv4_spec = item->spec;
+	const struct rte_flow_item_ipv4 *ipv4_mask = item->mask;
+	const struct rte_flow_item_ipv4 *ipv4_last = item->last;
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	uint16_t frag_spec, frag_last;
+
+	/* Set layer index for L2 flexible payload (after ETH/VLAN) */
+	filter->input.flow_ext.layer_idx = I40E_FLXPLD_L3_IDX;
+
+	/* set packet type */
+	filter->input.pctype = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
+
+	/* set up flow type */
+	filter->input.flow_ext.inner_ip = false;
+	filter->input.flow_ext.oip_type = I40E_FDIR_IPTYPE_IPV4;
+
+	if (ipv4_mask == NULL && ipv4_spec == NULL)
+		return 0;
+
+	/* Mark that IPv4 fields are used */
+	if (!CI_FIELD_IS_ZERO(&ipv4_mask->hdr.next_proto_id)) {
+		filter->input.flow.ip4_flow.proto = ipv4_spec->hdr.next_proto_id;
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV4_PROTO;
+	}
+	if (!CI_FIELD_IS_ZERO(&ipv4_mask->hdr.type_of_service)) {
+		filter->input.flow.ip4_flow.tos = ipv4_spec->hdr.type_of_service;
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV4_TOS;
+	}
+	if (!CI_FIELD_IS_ZERO(&ipv4_mask->hdr.time_to_live)) {
+		filter->input.flow.ip4_flow.ttl = ipv4_spec->hdr.time_to_live;
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV4_TTL;
+	}
+	if (!CI_FIELD_IS_ZERO(&ipv4_mask->hdr.src_addr)) {
+		filter->input.flow.ip4_flow.src_ip = ipv4_spec->hdr.src_addr;
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV4_SRC;
+	}
+	if (!CI_FIELD_IS_ZERO(&ipv4_mask->hdr.dst_addr)) {
+		filter->input.flow.ip4_flow.dst_ip = ipv4_spec->hdr.dst_addr;
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV4_DST;
+	}
+
+	/* do we have range? */
+	if (ipv4_last == NULL)
+		return 0;
+
+	/* frag mask is already known to be non-zero */
+	frag_spec = rte_be_to_cpu_16(ipv4_spec->hdr.fragment_offset);
+	frag_last = rte_be_to_cpu_16(ipv4_last->hdr.fragment_offset);
+	/* frag spec and last are already known to be either 0 or valid */
+
+	/* is range specified for fragment_offset? */
+	if (frag_spec != 0 && frag_last != 0)
+		filter->input.pctype = I40E_FILTER_PCTYPE_FRAG_IPV4;
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_ipv6_validate(const void *ctx __rte_unused, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct rte_flow_item_ipv6 *ipv6_spec = item->spec;
+	const struct rte_flow_item_ipv6 *ipv6_mask = item->mask;
+	if (ipv6_mask == NULL && ipv6_spec == NULL)
+		return 0;
+
+	/* payload len isn't supported */
+	if (ipv6_mask->hdr.payload_len) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid IPv6 header mask");
+	}
+	/* source and destination mask can either be all zeroes or all ones */
+	if (!CI_FIELD_IS_ZERO_OR_MASKED(&ipv6_mask->hdr.src_addr)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid IPv6 source address mask");
+	}
+	if (!CI_FIELD_IS_ZERO_OR_MASKED(&ipv6_mask->hdr.dst_addr)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid IPv6 destination address mask");
+	}
+
+	/* check other supported fields */
+	if (!ci_is_zero_or_masked(ipv6_mask->hdr.vtc_flow, rte_cpu_to_be_32(I40E_IPV6_TC_MASK)) ||
+	    !CI_FIELD_IS_ZERO_OR_MASKED(&ipv6_mask->hdr.proto) ||
+	    !CI_FIELD_IS_ZERO_OR_MASKED(&ipv6_mask->hdr.hop_limits)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid IPv6 header mask");
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_ipv6_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error __rte_unused)
+{
+	const struct rte_flow_item_ipv6 *ipv6_spec = item->spec;
+	const struct rte_flow_item_ipv6 *ipv6_mask = item->mask;
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+
+	/* Set layer index for L2 flexible payload (after ETH/VLAN) */
+	filter->input.flow_ext.layer_idx = I40E_FLXPLD_L3_IDX;
+
+	/* set packet type */
+	filter->input.pctype = I40E_FILTER_PCTYPE_NONF_IPV6_OTHER;
+
+	/* set up flow type */
+	filter->input.flow_ext.inner_ip = false;
+	filter->input.flow_ext.oip_type = I40E_FDIR_IPTYPE_IPV6;
+
+	if (ipv6_mask == NULL && ipv6_spec == NULL)
+		return 0;
+	if (CI_FIELD_IS_MASKED(&ipv6_mask->hdr.src_addr)) {
+		memcpy(&filter->input.flow.ipv6_flow.src_ip, &ipv6_spec->hdr.src_addr, sizeof(ipv6_spec->hdr.src_addr));
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV6_SRC;
+	}
+	if (CI_FIELD_IS_MASKED(&ipv6_mask->hdr.dst_addr)) {
+		memcpy(&filter->input.flow.ipv6_flow.dst_ip, &ipv6_spec->hdr.dst_addr, sizeof(ipv6_spec->hdr.dst_addr));
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV6_DST;
+	}
+
+	if (!CI_FIELD_IS_ZERO(&ipv6_mask->hdr.vtc_flow)) {
+		rte_be32_t vtc_flow = rte_be_to_cpu_32(ipv6_spec->hdr.vtc_flow);
+		uint8_t tc = (uint8_t)((vtc_flow & I40E_IPV6_TC_MASK) >> I40E_FDIR_IPv6_TC_OFFSET);
+		filter->input.flow.ipv6_flow.tc = tc;
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV6_TC;
+	}
+	if (!CI_FIELD_IS_ZERO(&ipv6_mask->hdr.proto)) {
+		filter->input.flow.ipv6_flow.proto = ipv6_spec->hdr.proto;
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV6_NEXT_HDR;
+	}
+	if (!CI_FIELD_IS_ZERO(&ipv6_mask->hdr.hop_limits)) {
+		filter->input.flow.ipv6_flow.hop_limits = ipv6_spec->hdr.hop_limits;
+		filter->input.flow_ext.input_set |= I40E_INSET_IPV6_HOP_LIMIT;
+	}
+	/* mark as fragment traffic if necessary */
+	if (ipv6_spec->hdr.proto == I40E_IPV6_FRAG_HEADER) {
+		filter->input.pctype = I40E_FILTER_PCTYPE_FRAG_IPV6;
+	}
+
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_tcp_validate(const void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct i40e_fdir_ctx *fdir_ctx = ctx;
+	const struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_tcp *tcp_spec = item->spec;
+	const struct rte_flow_item_tcp *tcp_mask = item->mask;
+
+	/* cannot match both fragmented and TCP */
+	if (filter->input.pctype == I40E_FILTER_PCTYPE_FRAG_IPV4 ||
+	    filter->input.pctype == I40E_FILTER_PCTYPE_FRAG_IPV6) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Cannot combine fragmented IP and TCP match");
+	}
+
+	if (tcp_spec == NULL && tcp_mask == NULL)
+		return 0;
+
+	if (tcp_mask->hdr.sent_seq ||
+	    tcp_mask->hdr.recv_ack ||
+	    tcp_mask->hdr.data_off ||
+	    tcp_mask->hdr.tcp_flags ||
+	    tcp_mask->hdr.rx_win ||
+	    tcp_mask->hdr.cksum ||
+	    tcp_mask->hdr.tcp_urp) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid TCP header mask");
+	}
+	if (!CI_FIELD_IS_ZERO_OR_MASKED(&tcp_mask->hdr.src_port) ||
+	    !CI_FIELD_IS_ZERO_OR_MASKED(&tcp_mask->hdr.dst_port)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid TCP header mask");
+	}
+	return 0;
+}
+
+static int
+i40e_fdir_node_tcp_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error __rte_unused)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_tcp *tcp_spec = item->spec;
+	const struct rte_flow_item_tcp *tcp_mask = item->mask;
+	rte_be16_t src_spec, dst_spec, src_mask, dst_mask;
+	bool is_ipv4;
+
+	/* Set layer index for L4 flexible payload */
+	filter->input.flow_ext.layer_idx = I40E_FLXPLD_L4_IDX;
+
+	/* set packet type depending on L3 type */
+	if (filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV4) {
+		filter->input.pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
+	} else if (filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV6) {
+		filter->input.pctype = I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
+	}
+
+	if (tcp_spec == NULL && tcp_mask == NULL)
+		return 0;
+
+	src_spec = tcp_spec->hdr.src_port;
+	dst_spec = tcp_spec->hdr.dst_port;
+	src_mask = tcp_mask->hdr.src_port;
+	dst_mask = tcp_mask->hdr.dst_port;
+	is_ipv4 = filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV4;
+
+	if (is_ipv4) {
+		if (src_mask != 0) {
+			filter->input.flow_ext.input_set |= I40E_INSET_SRC_PORT;
+			filter->input.flow.tcp4_flow.src_port = src_spec;
+		}
+		if (dst_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_DST_PORT;
+			filter->input.flow.tcp4_flow.dst_port = dst_spec;
+		}
+	} else {
+		if (src_mask != 0) {
+			filter->input.flow_ext.input_set |= I40E_INSET_SRC_PORT;
+			filter->input.flow.tcp6_flow.src_port = src_spec;
+		}
+		if (dst_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_DST_PORT;
+			filter->input.flow.tcp6_flow.dst_port = dst_spec;
+		}
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_udp_validate(const void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct i40e_fdir_ctx *fdir_ctx = ctx;
+	const struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_udp *udp_spec = item->spec;
+	const struct rte_flow_item_udp *udp_mask = item->mask;
+
+	/* cannot match both fragmented and TCP */
+	if (filter->input.pctype == I40E_FILTER_PCTYPE_FRAG_IPV4 ||
+	    filter->input.pctype == I40E_FILTER_PCTYPE_FRAG_IPV6) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Cannot combine fragmented IP and UDP match");
+	}
+
+	if (udp_spec == NULL && udp_mask == NULL)
+		return 0;
+
+	if (udp_mask->hdr.dgram_len || udp_mask->hdr.dgram_cksum) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid UDP header mask");
+	}
+	if (!CI_FIELD_IS_ZERO_OR_MASKED(&udp_mask->hdr.src_port) ||
+	    !CI_FIELD_IS_ZERO_OR_MASKED(&udp_mask->hdr.dst_port)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid UDP header mask");
+	}
+	return 0;
+}
+
+static int
+i40e_fdir_node_udp_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error __rte_unused)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_udp *udp_spec = item->spec;
+	const struct rte_flow_item_udp *udp_mask = item->mask;
+	rte_be16_t src_spec, dst_spec, src_mask, dst_mask;
+	bool is_ipv4;
+
+	/* Set layer index for L4 flexible payload */
+	filter->input.flow_ext.layer_idx = I40E_FLXPLD_L4_IDX;
+
+	/* set packet type depending on L3 type */
+	if (filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV4) {
+		filter->input.pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
+	} else if (filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV6) {
+		filter->input.pctype = I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
+	}
+
+	/* set UDP */
+	filter->input.flow_ext.is_udp = true;
+
+	if (udp_spec == NULL && udp_mask == NULL)
+		return 0;
+
+	src_spec = udp_spec->hdr.src_port;
+	dst_spec = udp_spec->hdr.dst_port;
+	src_mask = udp_mask->hdr.src_port;
+	dst_mask = udp_mask->hdr.dst_port;
+	is_ipv4 = filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV4;
+
+	if (is_ipv4) {
+		if (src_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_SRC_PORT;
+			filter->input.flow.udp4_flow.src_port = src_spec;
+		}
+		if (dst_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_DST_PORT;
+			filter->input.flow.udp4_flow.dst_port = dst_spec;
+		}
+	} else {
+		if (src_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_SRC_PORT;
+			filter->input.flow.udp6_flow.src_port = src_spec;
+		}
+		if (dst_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_DST_PORT;
+			filter->input.flow.udp6_flow.dst_port = dst_spec;
+		}
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_sctp_validate(const void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct i40e_fdir_ctx *fdir_ctx = ctx;
+	const struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_sctp *sctp_spec = item->spec;
+	const struct rte_flow_item_sctp *sctp_mask = item->mask;
+
+	/* cannot match both fragmented and TCP */
+	if (filter->input.pctype == I40E_FILTER_PCTYPE_FRAG_IPV4 ||
+	    filter->input.pctype == I40E_FILTER_PCTYPE_FRAG_IPV6) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Cannot combine fragmented IP and SCTP match");
+	}
+
+	if (sctp_spec == NULL && sctp_mask == NULL)
+		return 0;
+
+	if (sctp_mask->hdr.cksum) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid SCTP header mask");
+	}
+	if (!CI_FIELD_IS_ZERO_OR_MASKED(&sctp_mask->hdr.src_port) ||
+	    !CI_FIELD_IS_ZERO_OR_MASKED(&sctp_mask->hdr.dst_port) ||
+	    !CI_FIELD_IS_ZERO_OR_MASKED(&sctp_mask->hdr.tag)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid SCTP header mask");
+	}
+	return 0;
+}
+
+static int
+i40e_fdir_node_sctp_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error __rte_unused)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_sctp *sctp_spec = item->spec;
+	const struct rte_flow_item_sctp *sctp_mask = item->mask;
+	rte_be16_t src_spec, dst_spec, src_mask, dst_mask, tag_spec, tag_mask;
+	bool is_ipv4;
+
+	/* Set layer index for L4 flexible payload */
+	filter->input.flow_ext.layer_idx = I40E_FLXPLD_L4_IDX;
+
+	/* set packet type depending on L3 type */
+	if (filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV4) {
+		filter->input.pctype = I40E_FILTER_PCTYPE_NONF_IPV4_SCTP;
+	} else if (filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV6) {
+		filter->input.pctype = I40E_FILTER_PCTYPE_NONF_IPV6_SCTP;
+	}
+
+	if (sctp_spec == NULL && sctp_mask == NULL)
+		return 0;
+
+	if (!CI_FIELD_IS_ZERO(&sctp_mask->hdr.tag)) {
+		filter->input.flow_ext.input_set |= I40E_INSET_SCTP_VT;
+	}
+
+	src_spec = sctp_spec->hdr.src_port;
+	dst_spec = sctp_spec->hdr.dst_port;
+	src_mask = sctp_mask->hdr.src_port;
+	dst_mask = sctp_mask->hdr.dst_port;
+	tag_spec = sctp_spec->hdr.tag;
+	tag_mask = sctp_mask->hdr.tag;
+	is_ipv4 = filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV4;
+
+	if (is_ipv4) {
+		if (src_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_SRC_PORT;
+			filter->input.flow.sctp4_flow.src_port = src_spec;
+		}
+		if (dst_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_DST_PORT;
+			filter->input.flow.sctp4_flow.dst_port = dst_spec;
+		}
+		if (tag_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_SCTP_VT;
+			filter->input.flow.sctp4_flow.verify_tag = tag_spec;
+		}
+	} else {
+		if (src_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_SRC_PORT;
+			filter->input.flow.sctp6_flow.src_port = src_spec;
+		}
+		if (dst_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_DST_PORT;
+			filter->input.flow.sctp6_flow.dst_port = dst_spec;
+		}
+		if (tag_mask) {
+			filter->input.flow_ext.input_set |= I40E_INSET_SCTP_VT;
+			filter->input.flow.sctp6_flow.verify_tag = tag_spec;
+		}
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_raw_validate(const void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct i40e_fdir_ctx *fdir_ctx = ctx;
+	const struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(fdir_ctx->base.dev_data->dev_private);
+	const struct rte_flow_item_raw *raw_spec = item->spec;
+	const struct rte_flow_item_raw *raw_mask = item->mask;
+	enum i40e_flxpld_layer_idx raw_id = filter->input.flow_ext.raw_id;
+	size_t spec_size, spec_offset;
+	size_t total_size, i;
+	size_t new_src_offset;
+
+	/* we shouldn't write to global registers on some hardware */
+	if (pf->support_multi_driver) {
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Unsupported flexible payload.");
+	}
+
+	/* Check max RAW items limit */
+	RTE_BUILD_BUG_ON(I40E_MAX_FLXPLD_LAYER != I40E_MAX_FLXPLD_FIED);
+	if (raw_id >= I40E_MAX_FLXPLD_LAYER) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Maximum 3 RAW items allowed per layer");
+	}
+
+	if (raw_spec->pattern == NULL) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW spec pattern must not be NULL");
+	}
+
+	if (raw_mask->pattern == NULL) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW mask pattern must not be NULL");
+	}
+
+	if (raw_mask->length != raw_spec->length &&
+	    raw_mask->length != 0xffff) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW mask length must match spec length or be 0xffff");
+	}
+
+	if (raw_mask->relative || raw_mask->search ||
+	    raw_mask->reserved || raw_mask->offset || raw_mask->limit) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW mask control fields are not supported");
+	}
+
+	/* Relative offset is mandatory */
+	if (!raw_spec->relative) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW relative must be 1");
+	}
+
+	/* Offset must be 16-bit aligned */
+	if (raw_spec->offset % sizeof(uint16_t)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW offset must be even");
+	}
+
+	/* Search and limit not supported */
+	if (raw_spec->search || raw_spec->limit) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW search/limit not supported");
+	}
+
+	if (raw_spec->reserved) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW reserved field must be zero");
+	}
+
+	/* Offset must be non-negative */
+	if (raw_spec->offset < 0) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW offset must be non-negative");
+	}
+
+	/* flex size/offset for current item (in bytes) */
+	spec_size = raw_spec->length;
+	spec_offset = raw_spec->offset;
+
+	/*
+	 * RAW node can be triggered multiple times, each time we will be copying more data to the
+	 * flexbyte buffer. we need to validate total size/offset against max allowed because we
+	 * cannot overflow our flexbyte buffer.
+	 */
+
+	/* accumulate previous raw items' size/offset */
+	total_size = 0;
+	new_src_offset = 0;
+	for (i = 0; i < raw_id; i++) {
+		const struct flex_item *fi = &fdir_ctx->flex_data[i];
+		total_size += fi->size;
+		/* offset is relative to end of previous item */
+		new_src_offset += fi->offset + fi->size;
+	}
+	/* add current item to totals */
+	total_size += spec_size;
+	new_src_offset += spec_offset;
+
+	/* validate against max offset/size */
+	if (spec_size + new_src_offset >= I40E_MAX_FLX_SOURCE_OFF) {
+		return rte_flow_error_set(error, EINVAL,
+						RTE_FLOW_ERROR_TYPE_ITEM, item,
+						"RAW total offset exceeds maximum");
+	}
+	if (total_size > I40E_FDIR_MAX_FLEXLEN) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"RAW total size exceeds maximum");
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_raw_process(void *ctx,
+			    const struct rte_flow_item *item,
+			    struct rte_flow_error *error __rte_unused)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_raw *raw_spec = item->spec;
+	const struct rte_flow_item_raw *raw_mask = item->mask;
+	enum i40e_flxpld_layer_idx raw_id = filter->input.flow_ext.raw_id;
+	enum i40e_flxpld_layer_idx layer_idx = filter->input.flow_ext.layer_idx;
+	size_t flex_pit_field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + raw_id;
+	struct i40e_fdir_flex_pit *flex_pit;
+	size_t spec_size, spec_offset, i;
+	size_t total_size, new_src_offset;
+
+	/* flex size for current item */
+	spec_size = raw_spec->length;
+	spec_offset = raw_spec->offset;
+
+	/* accumulate previous raw items' size/offset */
+	total_size = 0;
+	new_src_offset = 0;
+	for (i = 0; i < raw_id; i++) {
+		const struct flex_item *fi = &fdir_ctx->flex_data[i];
+		total_size += fi->size;
+		/* offset is relative to end of previous item */
+		new_src_offset += fi->offset + fi->size;
+	}
+	/* src offset must also include current offset */
+	new_src_offset += spec_offset;
+
+	/* store the current data */
+	fdir_ctx->flex_data[raw_id].size = spec_size;
+	fdir_ctx->flex_data[raw_id].offset = spec_offset;
+
+	/* copy bytes from current spec into the flex pit buffer */
+	for (i = 0; i < spec_size; i++) {
+		const size_t j = total_size + i;
+		filter->input.flow_ext.flexbytes[j] = raw_spec->pattern[i];
+		filter->input.flow_ext.flex_mask[j] = raw_mask->pattern[i];
+	}
+
+	/*
+	 * all metadata in the flex pit is stored in units of 2 bytes (words),
+	 * but all the limits are in bytes, so we need to convert sizes/offsets
+	 * accordingly.
+	 */
+
+	/* pick our flex pit */
+	flex_pit = &filter->input.flow_ext.flex_pit[flex_pit_field_idx];
+	/* convert to words (2-byte units) */
+	flex_pit->src_offset = (uint16_t)new_src_offset / sizeof(uint16_t);
+	flex_pit->dst_offset = (uint16_t)total_size / sizeof(uint16_t);
+	flex_pit->size = (uint16_t)spec_size / sizeof(uint16_t);
+
+	/* increment raw item index */
+	filter->input.flow_ext.raw_id++;
+
+	/* mark as flex flow */
+	filter->input.flow_ext.is_flex_flow = true;
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_esp_validate(const void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct i40e_fdir_ctx *fdir_ctx = ctx;
+	const struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(fdir_ctx->base.dev_data->dev_private);
+	const struct rte_flow_item_esp *esp_mask = item->mask;
+
+	if (!pf->esp_support) {
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Protocol not supported");
+	}
+
+	/* SPI must be fully masked */
+	if (!CI_FIELD_IS_MASKED(&esp_mask->hdr.spi)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid ESP header mask");
+	}
+	return 0;
+}
+
+static int
+i40e_fdir_node_esp_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error __rte_unused)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_esp *esp_spec = item->spec;
+	bool is_ipv4 = filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV4;
+	bool is_udp = filter->input.flow_ext.is_udp;
+
+	/* ESP uses customized pctype */
+	filter->input.flow_ext.customized_pctype = true;
+	fdir_ctx->custom_pctype = item->type;
+
+	if (is_ipv4) {
+		if (is_udp)
+			filter->input.flow.esp_ipv4_udp_flow.spi = esp_spec->hdr.spi;
+		else {
+			filter->input.flow.esp_ipv4_flow.spi = esp_spec->hdr.spi;
+		}
+	} else {
+		if (is_udp)
+			filter->input.flow.esp_ipv6_udp_flow.spi = esp_spec->hdr.spi;
+		else {
+			filter->input.flow.esp_ipv6_flow.spi = esp_spec->hdr.spi;
+		}
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_l2tpv3oip_validate(const void *ctx __rte_unused,
+				   const struct rte_flow_item *item,
+				   struct rte_flow_error *error)
+{
+	const struct rte_flow_item_l2tpv3oip *l2tp_mask = item->mask;
+
+	if (!CI_FIELD_IS_MASKED(&l2tp_mask->session_id)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid L2TPv3oIP header mask");
+	}
+	return 0;
+
+}
+
+static int
+i40e_fdir_node_l2tpv3oip_process(void *ctx,
+				  const struct rte_flow_item *item,
+				  struct rte_flow_error *error __rte_unused)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_l2tpv3oip *l2tp_spec = item->spec;
+
+	/* L2TPv3 uses customized pctype */
+	filter->input.flow_ext.customized_pctype = true;
+	fdir_ctx->custom_pctype = item->type;
+
+	/* Store session_id in appropriate flow union member based on IP version */
+	if (filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV4) {
+		filter->input.flow.ip4_l2tpv3oip_flow.session_id = l2tp_spec->session_id;
+	} else if (filter->input.flow_ext.oip_type == I40E_FDIR_IPTYPE_IPV6) {
+		filter->input.flow.ip6_l2tpv3oip_flow.session_id = l2tp_spec->session_id;
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_gtp_validate(const void *ctx __rte_unused, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct i40e_fdir_ctx *fdir_ctx = ctx;
+	const struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(fdir_ctx->base.dev_data->dev_private);
+	const struct rte_flow_item_gtp *gtp_mask = item->mask;
+
+	/* DDP may not support this packet type */
+	if (!pf->gtp_support) {
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Protocol not supported");
+	}
+
+	if (gtp_mask->hdr.gtp_hdr_info ||
+	    gtp_mask->hdr.msg_type ||
+	    gtp_mask->hdr.plen) {
+		return rte_flow_error_set(error, EINVAL,
+					  RTE_FLOW_ERROR_TYPE_ITEM, item,
+					  "Invalid GTP header mask");
+	}
+	/* if GTP is specified, TEID must be masked */
+	if (!CI_FIELD_IS_MASKED(&gtp_mask->hdr.teid)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Invalid GTP header mask");
+	}
+	return 0;
+}
+
+static int
+i40e_fdir_node_gtp_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error __rte_unused)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	const struct rte_flow_item_gtp *gtp_spec = item->spec;
+
+	/* Mark as GTP tunnel with customized pctype */
+	filter->input.flow_ext.customized_pctype = true;
+	fdir_ctx->custom_pctype = item->type;
+
+	filter->input.flow.gtp_flow.teid = gtp_spec->teid;
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_inner_ipv4_process(void *ctx, const struct rte_flow_item *item __rte_unused,
+		struct rte_flow_error *error __rte_unused)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+
+	/* Mark as inner IP */
+	filter->input.flow_ext.inner_ip = true;
+	filter->input.flow_ext.iip_type = I40E_FDIR_IPTYPE_IPV4;
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_inner_ipv6_process(void *ctx, const struct rte_flow_item *item __rte_unused,
+		struct rte_flow_error *error __rte_unused)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+
+	/* Mark as inner IP */
+	filter->input.flow_ext.inner_ip = true;
+	filter->input.flow_ext.iip_type = I40E_FDIR_IPTYPE_IPV6;
+
+	return 0;
+}
+
+/* END node validation for FDIR - performs pctype determination and input_set validation */
+static int
+i40e_fdir_node_end_validate(const void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	const struct i40e_fdir_ctx *fdir_ctx = ctx;
+	const struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	uint64_t input_set = filter->input.flow_ext.input_set;
+	enum i40e_filter_pctype pctype = filter->input.pctype;
+
+	/*
+	 * Before sending the configuration down to hardware, we need to make
+	 * sure that the configuration makes sense - more specifically, that the
+	 * input set is a valid one that is actually supported by the hardware.
+	 * This is validated for built-in ptypes, however for customized ptypes,
+	 * the validation is skipped, and we have no way of validating the input
+	 * set because we do not have that information at our disposal - the
+	 * input set for customized packet type is not available through DDP
+	 * queries.
+	 *
+	 * However, we do know that some things are unsupported by the hardware no matter the
+	 * configuration. We can check for them here.
+	 */
+	const uint64_t i40e_l2_input_set = I40E_INSET_DMAC | I40E_INSET_SMAC;
+	const uint64_t i40e_l3_input_set = (I40E_INSET_IPV4_SRC | I40E_INSET_IPV4_DST |
+					    I40E_INSET_IPV4_TOS | I40E_INSET_IPV4_TTL |
+					    I40E_INSET_IPV4_PROTO);
+	const uint64_t i40e_l4_input_set = (I40E_INSET_SRC_PORT | I40E_INSET_DST_PORT);
+	const bool l2_in_set = (input_set & i40e_l2_input_set) != 0;
+	const bool l3_in_set = (input_set & i40e_l3_input_set) != 0;
+	const bool l4_in_set = (input_set & i40e_l4_input_set) != 0;
+
+	/* if we're matching ethertype, we may be matching L2 only, and cannot have RAW patterns */
+	if ((input_set & I40E_INSET_LAST_ETHER_TYPE) != 0 &&
+	     (pctype != I40E_FILTER_PCTYPE_L2_PAYLOAD ||
+	      filter->input.flow_ext.is_flex_flow)) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Cannot match ethertype with L3/L4 or RAW patterns");
+	}
+
+	/* L2 and L3 input sets are exclusive */
+	if (l2_in_set && l3_in_set) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Matching both L2 and L3 is not supported");
+	}
+	/* L2 and L4 input sets are exclusive */
+	if (l2_in_set && l4_in_set) {
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ITEM, item,
+				"Matching both L2 and L4 is not supported");
+	}
+
+	/* if we are using one of the builtin packet types, validate it */
+	if (!filter->input.flow_ext.customized_pctype) {
+		/* validate the input set for the built-in pctype */
+		if (i40e_validate_input_set(pctype, RTE_ETH_FILTER_FDIR, input_set) != 0) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Invalid input set");
+		}
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_node_end_process(void *ctx, const struct rte_flow_item *item,
+		struct rte_flow_error *error)
+{
+	struct i40e_fdir_ctx *fdir_ctx = ctx;
+	struct i40e_fdir_filter_conf *filter = &fdir_ctx->fdir_filter;
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(fdir_ctx->base.dev_data->dev_private);
+
+	/* Get customized pctype value */
+	if (filter->input.flow_ext.customized_pctype) {
+		enum i40e_filter_pctype pctype = i40e_flow_fdir_get_pctype_value(pf,
+				fdir_ctx->custom_pctype, filter);
+		if (pctype == I40E_FILTER_PCTYPE_INVALID) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM, item,
+					"Unsupported packet type");
+			return -rte_errno;
+		}
+		/* update FDIR packet type */
+		filter->input.pctype = pctype;
+	}
+
+	return 0;
+}
+
+static const struct rte_flow_graph i40e_fdir_graph = {
+	.nodes = (struct rte_flow_graph_node[]) {
+		[I40E_FDIR_NODE_START] = { .name = "START" },
+		[I40E_FDIR_NODE_ETH] = {
+			.name = "ETH",
+			.type = RTE_FLOW_ITEM_TYPE_ETH,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY |
+				       RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_eth_validate,
+			.process = i40e_fdir_node_eth_process,
+		},
+		[I40E_FDIR_NODE_VLAN] = {
+			.name = "VLAN",
+			.type = RTE_FLOW_ITEM_TYPE_VLAN,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY |
+				       RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_vlan_validate,
+			.process = i40e_fdir_node_vlan_process,
+		},
+		[I40E_FDIR_NODE_IPV4] = {
+			.name = "IPv4",
+			.type = RTE_FLOW_ITEM_TYPE_IPV4,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY |
+				       RTE_FLOW_NODE_EXPECT_SPEC_MASK |
+				       RTE_FLOW_NODE_EXPECT_RANGE,
+			.validate = i40e_fdir_node_ipv4_validate,
+			.process = i40e_fdir_node_ipv4_process,
+		},
+		[I40E_FDIR_NODE_IPV6] = {
+			.name = "IPv6",
+			.type = RTE_FLOW_ITEM_TYPE_IPV6,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY |
+				       RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_ipv6_validate,
+			.process = i40e_fdir_node_ipv6_process,
+		},
+		[I40E_FDIR_NODE_TCP] = {
+			.name = "TCP",
+			.type = RTE_FLOW_ITEM_TYPE_TCP,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY |
+				       RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_tcp_validate,
+			.process = i40e_fdir_node_tcp_process,
+		},
+		[I40E_FDIR_NODE_UDP] = {
+			.name = "UDP",
+			.type = RTE_FLOW_ITEM_TYPE_UDP,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY |
+				       RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_udp_validate,
+			.process = i40e_fdir_node_udp_process,
+		},
+		[I40E_FDIR_NODE_SCTP] = {
+			.name = "SCTP",
+			.type = RTE_FLOW_ITEM_TYPE_SCTP,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY |
+				       RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_sctp_validate,
+			.process = i40e_fdir_node_sctp_process,
+		},
+		[I40E_FDIR_NODE_ESP] = {
+			.name = "ESP",
+			.type = RTE_FLOW_ITEM_TYPE_ESP,
+			.constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_esp_validate,
+			.process = i40e_fdir_node_esp_process,
+		},
+		[I40E_FDIR_NODE_L2TPV3OIP] = {
+			.name = "L2TPV3OIP",
+			.type = RTE_FLOW_ITEM_TYPE_L2TPV3OIP,
+			.constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_l2tpv3oip_validate,
+			.process = i40e_fdir_node_l2tpv3oip_process,
+		},
+		[I40E_FDIR_NODE_GTPC] = {
+			.name = "GTPC",
+			.type = RTE_FLOW_ITEM_TYPE_GTPC,
+			.constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_gtp_validate,
+			.process = i40e_fdir_node_gtp_process,
+		},
+		[I40E_FDIR_NODE_GTPU] = {
+			.name = "GTPU",
+			.type = RTE_FLOW_ITEM_TYPE_GTPU,
+			.constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_gtp_validate,
+			.process = i40e_fdir_node_gtp_process,
+		},
+		[I40E_FDIR_NODE_INNER_IPV4] = {
+			.name = "INNER_IPv4",
+			.type = RTE_FLOW_ITEM_TYPE_IPV4,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY,
+			.validate = i40e_fdir_node_ipv4_validate,
+			.process = i40e_fdir_node_inner_ipv4_process,
+		},
+		[I40E_FDIR_NODE_INNER_IPV6] = {
+			.name = "INNER_IPv6",
+			.type = RTE_FLOW_ITEM_TYPE_IPV6,
+			.constraints = RTE_FLOW_NODE_EXPECT_EMPTY,
+			.validate = i40e_fdir_node_ipv6_validate,
+			.process = i40e_fdir_node_inner_ipv6_process,
+		},
+		[I40E_FDIR_NODE_RAW] = {
+			.name = "RAW",
+			.type = RTE_FLOW_ITEM_TYPE_RAW,
+			.constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK,
+			.validate = i40e_fdir_node_raw_validate,
+			.process = i40e_fdir_node_raw_process,
+		},
+		[I40E_FDIR_NODE_END] = {
+			.name = "END",
+			.type = RTE_FLOW_ITEM_TYPE_END,
+			.validate = i40e_fdir_node_end_validate,
+			.process = i40e_fdir_node_end_process
+		},
+	},
+	.edges = (struct rte_flow_graph_edge[]) {
+		[I40E_FDIR_NODE_START] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_ETH,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_ETH] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_VLAN,
+				I40E_FDIR_NODE_IPV4,
+				I40E_FDIR_NODE_IPV6,
+				I40E_FDIR_NODE_RAW,
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_VLAN] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_IPV4,
+				I40E_FDIR_NODE_IPV6,
+				I40E_FDIR_NODE_RAW,
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_IPV4] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_TCP,
+				I40E_FDIR_NODE_UDP,
+				I40E_FDIR_NODE_SCTP,
+				I40E_FDIR_NODE_ESP,
+				I40E_FDIR_NODE_L2TPV3OIP,
+				I40E_FDIR_NODE_RAW,
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_IPV6] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_TCP,
+				I40E_FDIR_NODE_UDP,
+				I40E_FDIR_NODE_SCTP,
+				I40E_FDIR_NODE_ESP,
+				I40E_FDIR_NODE_L2TPV3OIP,
+				I40E_FDIR_NODE_RAW,
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_TCP] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_RAW,
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_UDP] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_GTPC,
+				I40E_FDIR_NODE_GTPU,
+				I40E_FDIR_NODE_ESP,
+				I40E_FDIR_NODE_RAW,
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_SCTP] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_RAW,
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_ESP] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_L2TPV3OIP] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_GTPC] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_GTPU] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_INNER_IPV4,
+				I40E_FDIR_NODE_INNER_IPV6,
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_INNER_IPV4] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_INNER_IPV6] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+		[I40E_FDIR_NODE_RAW] = {
+			.next = (const size_t[]) {
+				I40E_FDIR_NODE_RAW,
+				I40E_FDIR_NODE_END,
+				RTE_FLOW_NODE_EDGE_END
+			}
+		},
+	},
+};
+
+static int
+i40e_fdir_action_check(const struct ci_flow_actions *actions,
+		const struct ci_flow_actions_check_param *param,
+		struct rte_flow_error *error)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(param->driver_ctx);
+	const struct rte_flow_action *first, *second;
+
+	first = actions->actions[0];
+	/* can be NULL */
+	second = actions->actions[1];
+
+	switch (first->type) {
+	case RTE_FLOW_ACTION_TYPE_QUEUE:
+	{
+		const struct rte_flow_action_queue *act_q = first->conf;
+		/* check against PF constraints */
+		if (act_q->index >= pf->dev_data->nb_rx_queues) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, first,
+					"Invalid queue ID for FDIR");
+		}
+		break;
+	}
+	case RTE_FLOW_ACTION_TYPE_DROP:
+	case RTE_FLOW_ACTION_TYPE_PASSTHRU:
+	case RTE_FLOW_ACTION_TYPE_MARK:
+		break;
+	default:
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, first,
+				"Invalid first action for FDIR");
+	}
+
+	/* do we have another? */
+	if (second == NULL)
+		return 0;
+
+	switch (second->type) {
+	case RTE_FLOW_ACTION_TYPE_MARK:
+	{
+		/* only one mark action can be specified */
+		if (first->type == RTE_FLOW_ACTION_TYPE_MARK) {
+			return rte_flow_error_set(error, EINVAL,
+						  RTE_FLOW_ERROR_TYPE_ACTION, second,
+						  "Invalid second action for FDIR");
+		}
+		break;
+	}
+	case RTE_FLOW_ACTION_TYPE_FLAG:
+	{
+		/* mark + flag is unsupported */
+		if (first->type == RTE_FLOW_ACTION_TYPE_MARK) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, second,
+					"Invalid second action for FDIR");
+		}
+		break;
+	}
+	case RTE_FLOW_ACTION_TYPE_RSS:
+		/* RSS filter only can be after passthru or mark */
+		if (first->type != RTE_FLOW_ACTION_TYPE_PASSTHRU &&
+				first->type != RTE_FLOW_ACTION_TYPE_MARK) {
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, second,
+					"Invalid second action for FDIR");
+		}
+		break;
+	default:
+		return rte_flow_error_set(error, EINVAL,
+					  RTE_FLOW_ERROR_TYPE_ACTION, second,
+					  "Invalid second action for FDIR");
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_ctx_parse(const struct rte_flow_action *actions,
+		const struct rte_flow_attr *attr,
+		struct ci_flow_engine_ctx *ctx,
+		struct rte_flow_error *error)
+{
+	struct i40e_adapter *adapter = I40E_DEV_PRIVATE_TO_ADAPTER(ctx->dev_data->dev_private);
+	struct i40e_fdir_ctx *fdir_ctx = (struct i40e_fdir_ctx *)ctx;
+	struct ci_flow_actions parsed_actions = {0};
+	struct ci_flow_actions_check_param ac_param = {
+		.allowed_types = (enum rte_flow_action_type[]) {
+			RTE_FLOW_ACTION_TYPE_QUEUE,
+			RTE_FLOW_ACTION_TYPE_DROP,
+			RTE_FLOW_ACTION_TYPE_PASSTHRU,
+			RTE_FLOW_ACTION_TYPE_MARK,
+			RTE_FLOW_ACTION_TYPE_FLAG,
+			RTE_FLOW_ACTION_TYPE_RSS,
+			RTE_FLOW_ACTION_TYPE_END
+		},
+		.max_actions = 2,
+		.driver_ctx = adapter,
+		.check = i40e_fdir_action_check,
+	};
+	int ret;
+	const struct rte_flow_action *first, *second;
+
+	ret = ci_flow_check_attr(attr, NULL, error);
+	if (ret) {
+		return ret;
+	}
+
+	ret = ci_flow_check_actions(actions, &ac_param, &parsed_actions, error);
+	if (ret) {
+		return ret;
+	}
+
+	first = parsed_actions.actions[0];
+	/* can be NULL */
+	second = parsed_actions.actions[1];
+
+	if (first->type == RTE_FLOW_ACTION_TYPE_QUEUE) {
+		const struct rte_flow_action_queue *act_q = first->conf;
+		fdir_ctx->fdir_filter.action.rx_queue = act_q->index;
+		fdir_ctx->fdir_filter.action.behavior = I40E_FDIR_ACCEPT;
+	} else if (first->type == RTE_FLOW_ACTION_TYPE_DROP) {
+		fdir_ctx->fdir_filter.action.behavior = I40E_FDIR_REJECT;
+	} else if (first->type == RTE_FLOW_ACTION_TYPE_PASSTHRU) {
+		fdir_ctx->fdir_filter.action.behavior = I40E_FDIR_PASSTHRU;
+	} else if (first->type == RTE_FLOW_ACTION_TYPE_MARK) {
+		const struct rte_flow_action_mark *act_m = first->conf;
+		fdir_ctx->fdir_filter.action.behavior = I40E_FDIR_PASSTHRU;
+		fdir_ctx->fdir_filter.action.report_status = I40E_FDIR_REPORT_ID;
+		fdir_ctx->fdir_filter.soft_id = act_m->id;
+	}
+
+	if (second != NULL) {
+		if (second->type == RTE_FLOW_ACTION_TYPE_MARK) {
+			const struct rte_flow_action_mark *act_m = second->conf;
+			fdir_ctx->fdir_filter.action.report_status = I40E_FDIR_REPORT_ID;
+			fdir_ctx->fdir_filter.soft_id = act_m->id;
+		} else if (second->type == RTE_FLOW_ACTION_TYPE_FLAG) {
+			fdir_ctx->fdir_filter.action.report_status = I40E_FDIR_NO_REPORT_STATUS;
+		}
+		/* RSS action does nothing */
+	}
+	return 0;
+}
+
+static int
+i40e_fdir_flow_install(struct ci_flow *flow, struct rte_flow_error *error)
+{
+	struct i40e_flow_engine_fdir_flow *fdir_flow = (struct i40e_flow_engine_fdir_flow *)flow;
+	struct rte_eth_dev_data *dev_data = flow->dev_data;
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev_data->dev_private);
+	bool need_teardown = false;
+	bool need_rx_proc_disable = false;
+	int ret;
+
+	/* if fdir is not configured, configure it */
+	if (pf->fdir.fdir_vsi == NULL) {
+		ret = i40e_fdir_setup(pf);
+		if (ret != I40E_SUCCESS) {
+			ret = rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_HANDLE,
+					NULL, "Failed to setup fdir.");
+			goto err;
+		}
+		/* if something failed down the line, teardown is needed */
+		need_teardown = true;
+		ret = i40e_fdir_configure(pf);
+		if (ret < 0) {
+			ret = rte_flow_error_set(error, ENOTSUP,
+					RTE_FLOW_ERROR_TYPE_HANDLE,
+					NULL, "Failed to configure fdir.");
+			goto err;
+		}
+	}
+
+	/* if this is first flow, enable fdir check for rx queues */
+	if (pf->fdir.num_fdir_flows == 0) {
+		i40e_fdir_rx_proc_enable(dev_data, 1);
+		/* if something failed down the line, we need to disable fdir check for rx queues */
+		need_rx_proc_disable = true;
+	}
+
+	ret = i40e_flow_add_del_fdir_filter(pf, &fdir_flow->fdir_filter, 1);
+	if (ret != 0) {
+		ret = rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failed to add fdir filter.");
+		goto err;
+	}
+
+	/* we got flows now */
+	pf->fdir.num_fdir_flows++;
+
+	return 0;
+err:
+	if (need_rx_proc_disable)
+		i40e_fdir_rx_proc_enable(dev_data, 0);
+	if (need_teardown)
+		i40e_fdir_teardown(pf);
+	return ret;
+}
+
+static int
+i40e_fdir_flow_uninstall(struct ci_flow *flow, struct rte_flow_error *error __rte_unused)
+{
+	struct rte_eth_dev_data *dev_data = flow->dev_data;
+	struct i40e_flow_engine_fdir_flow *fdir_flow = (struct i40e_flow_engine_fdir_flow *)flow;
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev_data->dev_private);
+	int ret;
+
+	ret = i40e_flow_add_del_fdir_filter(pf, &fdir_flow->fdir_filter, 0);
+	if (ret != 0) {
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_HANDLE,
+				NULL, "Failed to delete fdir filter.");
+	}
+
+	/* we are removing a flow */
+	if (pf->fdir.num_fdir_flows > 0)
+		pf->fdir.num_fdir_flows--;
+
+	/* if there are no more flows, disable fdir check for rx queues and teardown fdir */
+	if (pf->fdir.num_fdir_flows == 0) {
+		i40e_fdir_rx_proc_enable(dev_data, 0);
+		i40e_fdir_teardown(pf);
+	}
+
+	return 0;
+}
+
+static int
+i40e_fdir_flow_engine_init(const struct ci_flow_engine *engine,
+		struct rte_eth_dev_data *dev_data,
+		void *priv_data)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev_data->dev_private);
+	struct i40e_fdir_info *fdir_info = &pf->fdir;
+	struct i40e_fdir_engine_priv *priv = priv_data;
+	struct i40e_fdir_flow_pool_entry *pool;
+	struct rte_bitmap *bmp;
+	uint32_t bmp_size;
+	void *bmp_mem;
+	uint32_t i;
+
+	pool = rte_zmalloc(engine->name,
+			fdir_info->fdir_space_size * sizeof(*pool), 0);
+	if (pool == NULL)
+		return -ENOMEM;
+
+	bmp_size = rte_bitmap_get_memory_footprint(fdir_info->fdir_space_size);
+	bmp_mem = rte_zmalloc("fdir_bmap", bmp_size, RTE_CACHE_LINE_SIZE);
+	if (bmp_mem == NULL) {
+		rte_free(pool);
+		return -ENOMEM;
+	}
+
+	bmp = rte_bitmap_init(fdir_info->fdir_space_size, bmp_mem, bmp_size);
+	if (bmp == NULL) {
+		rte_free(bmp_mem);
+		rte_free(pool);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < fdir_info->fdir_space_size; i++) {
+		pool[i].idx = i;
+		rte_bitmap_set(bmp, i);
+	}
+
+	priv->pool = pool;
+	priv->bmp = bmp;
+
+	return 0;
+}
+
+static void
+i40e_fdir_flow_engine_uninit(const struct ci_flow_engine *engine __rte_unused,
+		void *priv_data)
+{
+	struct i40e_fdir_engine_priv *priv = priv_data;
+
+	rte_free(priv->bmp);
+	rte_free(priv->pool);
+}
+
+static struct ci_flow *
+i40e_fdir_flow_alloc(const struct ci_flow_engine *engine __rte_unused,
+		struct rte_eth_dev_data *dev_data,
+		void *priv_data)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev_data->dev_private);
+	struct i40e_fdir_info *fdir_info = &pf->fdir;
+	struct i40e_fdir_engine_priv *priv = priv_data;
+	struct i40e_flow_engine_fdir_flow *flow;
+	uint64_t slab = 0;
+	uint32_t pos = 0;
+	uint32_t bit;
+	size_t mem_sz;
+	int ret;
+
+	if (fdir_info->fdir_actual_cnt >= fdir_info->fdir_space_size)
+		return NULL;
+
+	ret = rte_bitmap_scan(priv->bmp, &pos, &slab);
+	if (ret == 0)
+		return NULL;
+
+	bit = rte_bsf64(slab);
+	pos += bit;
+	rte_bitmap_clear(priv->bmp, pos);
+
+	flow = &priv->pool[pos].flow;
+	/* do not touch ci_flow members, they are initialized by the caller */
+	mem_sz = sizeof(*flow) - sizeof(struct ci_flow);
+
+	memset(RTE_PTR_ADD(flow, sizeof(struct ci_flow)), 0, mem_sz);
+	return (struct ci_flow *)flow;
+}
+
+static void
+i40e_fdir_flow_free(struct ci_flow *flow,
+		struct rte_eth_dev_data *dev_data __rte_unused,
+		void *priv_data)
+{
+	struct i40e_fdir_engine_priv *priv = priv_data;
+	struct i40e_fdir_flow_pool_entry *entry;
+
+	entry = I40E_FDIR_FLOW_ENTRY((struct i40e_flow_engine_fdir_flow *)flow);
+	/* idx is not set at alloc, it's set at init */
+	rte_bitmap_set(priv->bmp, entry->idx);
+}
+
+static const struct ci_flow_engine_ops i40e_flow_engine_fdir_ops = {
+	.engine_init = i40e_fdir_flow_engine_init,
+	.engine_uninit = i40e_fdir_flow_engine_uninit,
+	.flow_alloc = i40e_fdir_flow_alloc,
+	.flow_free = i40e_fdir_flow_free,
+	.ctx_parse = i40e_fdir_ctx_parse,
+	.flow_install = i40e_fdir_flow_install,
+	.flow_uninstall = i40e_fdir_flow_uninstall,
+};
+
+const struct ci_flow_engine i40e_flow_engine_fdir = {
+	.name = "fdir",
+	.ops = &i40e_flow_engine_fdir_ops,
+	.ctx_size = sizeof(struct i40e_fdir_ctx),
+	.flow_size = sizeof(struct i40e_flow_engine_fdir_flow),
+	.priv_size = sizeof(struct i40e_fdir_engine_priv),
+	.graph = &i40e_fdir_graph,
+};
diff --git a/drivers/net/intel/i40e/meson.build b/drivers/net/intel/i40e/meson.build
index bff0518fc9..0638f873dd 100644
--- a/drivers/net/intel/i40e/meson.build
+++ b/drivers/net/intel/i40e/meson.build
@@ -26,6 +26,7 @@ sources += files(
         'i40e_fdir.c',
         'i40e_flow.c',
         'i40e_flow_ethertype.c',
+        'i40e_flow_fdir.c',
         'i40e_tm.c',
         'i40e_hash.c',
         'i40e_vf_representor.c',
diff --git a/drivers/net/intel/i40e/rte_pmd_i40e.c b/drivers/net/intel/i40e/rte_pmd_i40e.c
index 2e7943ef8b..6adf343bd2 100644
--- a/drivers/net/intel/i40e/rte_pmd_i40e.c
+++ b/drivers/net/intel/i40e/rte_pmd_i40e.c
@@ -3044,6 +3044,7 @@ int rte_pmd_i40e_flow_add_del_packet_template(
 			uint8_t add)
 {
 	struct rte_eth_dev *dev = &rte_eth_devices[port];
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	struct i40e_fdir_filter_conf filter_conf;
 
 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
@@ -3068,7 +3069,7 @@ int rte_pmd_i40e_flow_add_del_packet_template(
 		(enum i40e_fdir_status)conf->action.report_status;
 	filter_conf.action.flex_off = conf->action.flex_off;
 
-	return i40e_flow_add_del_fdir_filter(dev, &filter_conf, add);
+	return i40e_flow_add_del_fdir_filter(pf, &filter_conf, add);
 }
 
 RTE_EXPORT_SYMBOL(rte_pmd_i40e_inset_get)
-- 
2.52.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.