diff --git a/drivers/net/nfp/flower/nfp_flower.c b/drivers/net/nfp/flower/nfp_flower.c
index 168bf0c..d2a8f9b 100644
--- a/drivers/net/nfp/flower/nfp_flower.c
+++ b/drivers/net/nfp/flower/nfp_flower.c
@@ -1073,6 +1073,8 @@
 nfp_init_app_fw_flower(struct nfp_pf_dev *pf_dev)
 {
 	int ret;
+	int err;
+	uint64_t ext_features;
 	unsigned int numa_node;
 	struct nfp_net_hw *pf_hw;
 	struct nfp_net_hw *ctrl_hw;
@@ -1114,6 +1116,18 @@
 		goto vnic_cleanup;
 	}
 
+	/* Read the extra features */
+	ext_features = nfp_rtsym_read_le(pf_dev->sym_tbl, "_abi_flower_extra_features",
+			&err);
+	if (err != 0) {
+		PMD_INIT_LOG(ERR, "Couldn't read extra features from fw");
+		ret = -EIO;
+		goto pf_cpp_area_cleanup;
+	}
+
+	/* Store the extra features */
+	app_fw_flower->ext_features = ext_features;
+
 	/* Fill in the PF vNIC and populate app struct */
 	app_fw_flower->pf_hw = pf_hw;
 	pf_hw->ctrl_bar = pf_dev->ctrl_bar;
diff --git a/drivers/net/nfp/flower/nfp_flower.h b/drivers/net/nfp/flower/nfp_flower.h
index b90391c..dc0eb36 100644
--- a/drivers/net/nfp/flower/nfp_flower.h
+++ b/drivers/net/nfp/flower/nfp_flower.h
@@ -6,6 +6,21 @@
 #ifndef _NFP_FLOWER_H_
 #define _NFP_FLOWER_H_
 
+/* Extra features bitmap. */
+#define NFP_FL_FEATS_GENEVE             RTE_BIT64(0)
+#define NFP_FL_NBI_MTU_SETTING          RTE_BIT64(1)
+#define NFP_FL_FEATS_GENEVE_OPT         RTE_BIT64(2)
+#define NFP_FL_FEATS_VLAN_PCP           RTE_BIT64(3)
+#define NFP_FL_FEATS_VF_RLIM            RTE_BIT64(4)
+#define NFP_FL_FEATS_FLOW_MOD           RTE_BIT64(5)
+#define NFP_FL_FEATS_PRE_TUN_RULES      RTE_BIT64(6)
+#define NFP_FL_FEATS_IPV6_TUN           RTE_BIT64(7)
+#define NFP_FL_FEATS_VLAN_QINQ          RTE_BIT64(8)
+#define NFP_FL_FEATS_QOS_PPS            RTE_BIT64(9)
+#define NFP_FL_FEATS_QOS_METER          RTE_BIT64(10)
+#define NFP_FL_FEATS_DECAP_V2           RTE_BIT64(11)
+#define NFP_FL_FEATS_HOST_ACK           RTE_BIT64(31)
+
 /*
  * Flower fallback and ctrl path always adds and removes
  * 8 bytes of prepended data. Tx descriptors must point
@@ -52,9 +67,18 @@ struct nfp_app_fw_flower {
 	/* PF representor */
 	struct nfp_flower_representor *pf_repr;
 
+	/* Flower extra features */
+	uint64_t ext_features;
+
 	struct nfp_flow_priv *flow_priv;
 };
 
+static inline bool
+nfp_flower_support_decap_v2(const struct nfp_app_fw_flower *app_fw_flower)
+{
+	return app_fw_flower->ext_features & NFP_FL_FEATS_DECAP_V2;
+}
+
 int nfp_init_app_fw_flower(struct nfp_pf_dev *pf_dev);
 int nfp_secondary_init_app_fw_flower(struct nfp_cpp *cpp);
 uint16_t nfp_flower_pf_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
diff --git a/drivers/net/nfp/flower/nfp_flower_cmsg.c b/drivers/net/nfp/flower/nfp_flower_cmsg.c
index 76815cf..babdd8e 100644
--- a/drivers/net/nfp/flower/nfp_flower_cmsg.c
+++ b/drivers/net/nfp/flower/nfp_flower_cmsg.c
@@ -263,6 +263,8 @@
 	}
 
 	msg_len = sizeof(struct nfp_flower_cmsg_tun_neigh_v4);
+	if (!nfp_flower_support_decap_v2(app_fw_flower))
+		msg_len -= sizeof(struct nfp_flower_tun_neigh_ext);
 	msg = nfp_flower_cmsg_init(mbuf, NFP_FLOWER_CMSG_TYPE_TUN_NEIGH, msg_len);
 	memcpy(msg, payload, msg_len);
 
@@ -292,6 +294,8 @@
 	}
 
 	msg_len = sizeof(struct nfp_flower_cmsg_tun_neigh_v6);
+	if (!nfp_flower_support_decap_v2(app_fw_flower))
+		msg_len -= sizeof(struct nfp_flower_tun_neigh_ext);
 	msg = nfp_flower_cmsg_init(mbuf, NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6, msg_len);
 	memcpy(msg, payload, msg_len);
 
diff --git a/drivers/net/nfp/flower/nfp_flower_cmsg.h b/drivers/net/nfp/flower/nfp_flower_cmsg.h
index a48da67..04601cb 100644
--- a/drivers/net/nfp/flower/nfp_flower_cmsg.h
+++ b/drivers/net/nfp/flower/nfp_flower_cmsg.h
@@ -136,6 +136,21 @@ struct nfp_flower_tun_neigh {
 };
 
 /*
+ *    Bit    3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
+ *    -----\ 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ *          +---------------------------------------------------------------+
+ *        0 |         VLAN_TPID             |            VLAN_ID            |
+ *          +---------------------------------------------------------------+
+ *        1 |                           HOST_CTX                            |
+ *          +---------------------------------------------------------------+
+ */
+struct nfp_flower_tun_neigh_ext {
+	rte_be16_t vlan_tpid;
+	rte_be16_t vlan_tci;
+	rte_be32_t host_ctx;
+};
+
+/*
  * NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V4
  *    Bit    3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
  *    -----\ 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
@@ -157,6 +172,7 @@ struct nfp_flower_cmsg_tun_neigh_v4 {
 	rte_be32_t dst_ipv4;
 	rte_be32_t src_ipv4;
 	struct nfp_flower_tun_neigh common;
+	struct nfp_flower_tun_neigh_ext ext;
 };
 
 /*
@@ -193,6 +209,7 @@ struct nfp_flower_cmsg_tun_neigh_v6 {
 	uint8_t dst_ipv6[16];
 	uint8_t src_ipv6[16];
 	struct nfp_flower_tun_neigh common;
+	struct nfp_flower_tun_neigh_ext ext;
 };
 
 #define NFP_TUN_PRE_TUN_RULE_DEL    (1 << 0)
diff --git a/drivers/net/nfp/nfp_flow.c b/drivers/net/nfp/nfp_flow.c
index 1ccc6ef..ac60e90 100644
--- a/drivers/net/nfp/nfp_flow.c
+++ b/drivers/net/nfp/nfp_flow.c
@@ -2402,8 +2402,10 @@ struct nfp_pre_tun_entry {
 
 static int
 nfp_flower_add_tun_neigh_v4_decap(struct nfp_app_fw_flower *app_fw_flower,
+		struct nfp_fl_rule_metadata *nfp_flow_meta,
 		struct rte_flow *nfp_flow)
 {
+	bool exists = false;
 	struct nfp_fl_tun *tmp;
 	struct nfp_fl_tun *tun;
 	struct nfp_flow_priv *priv;
@@ -2437,11 +2439,17 @@ struct nfp_pre_tun_entry {
 	LIST_FOREACH(tmp, &priv->nn_list, next) {
 		if (memcmp(&tmp->payload, &tun->payload, sizeof(struct nfp_fl_tun_entry)) == 0) {
 			tmp->ref_cnt++;
-			return 0;
+			exists = true;
+			break;
 		}
 	}
 
-	LIST_INSERT_HEAD(&priv->nn_list, tun, next);
+	if (exists) {
+		if (!nfp_flower_support_decap_v2(app_fw_flower))
+			return 0;
+	} else {
+		LIST_INSERT_HEAD(&priv->nn_list, tun, next);
+	}
 
 	memset(&payload, 0, sizeof(struct nfp_flower_cmsg_tun_neigh_v4));
 	payload.dst_ipv4 = ipv4->ipv4_src;
@@ -2450,6 +2458,17 @@ struct nfp_pre_tun_entry {
 	memcpy(payload.common.src_mac, eth->mac_dst, RTE_ETHER_ADDR_LEN);
 	payload.common.port_id = port->in_port;
 
+	if (nfp_flower_support_decap_v2(app_fw_flower)) {
+		if (meta_tci->tci != 0) {
+			payload.ext.vlan_tci = meta_tci->tci;
+			payload.ext.vlan_tpid = 0x88a8;
+		} else {
+			payload.ext.vlan_tci = 0xffff;
+			payload.ext.vlan_tpid = 0xffff;
+		}
+		payload.ext.host_ctx = nfp_flow_meta->host_ctx_id;
+	}
+
 	return nfp_flower_cmsg_tun_neigh_v4_rule(app_fw_flower, &payload);
 }
 
@@ -2510,8 +2529,10 @@ struct nfp_pre_tun_entry {
 
 static int
 nfp_flower_add_tun_neigh_v6_decap(struct nfp_app_fw_flower *app_fw_flower,
+		struct nfp_fl_rule_metadata *nfp_flow_meta,
 		struct rte_flow *nfp_flow)
 {
+	bool exists = false;
 	struct nfp_fl_tun *tmp;
 	struct nfp_fl_tun *tun;
 	struct nfp_flow_priv *priv;
@@ -2545,11 +2566,17 @@ struct nfp_pre_tun_entry {
 	LIST_FOREACH(tmp, &priv->nn_list, next) {
 		if (memcmp(&tmp->payload, &tun->payload, sizeof(struct nfp_fl_tun_entry)) == 0) {
 			tmp->ref_cnt++;
-			return 0;
+			exists = true;
+			break;
 		}
 	}
 
-	LIST_INSERT_HEAD(&priv->nn_list, tun, next);
+	if (exists) {
+		if (!nfp_flower_support_decap_v2(app_fw_flower))
+			return 0;
+	} else {
+		LIST_INSERT_HEAD(&priv->nn_list, tun, next);
+	}
 
 	memset(&payload, 0, sizeof(struct nfp_flower_cmsg_tun_neigh_v6));
 	memcpy(payload.dst_ipv6, ipv6->ipv6_src, sizeof(payload.dst_ipv6));
@@ -2558,6 +2585,17 @@ struct nfp_pre_tun_entry {
 	memcpy(payload.common.src_mac, eth->mac_dst, RTE_ETHER_ADDR_LEN);
 	payload.common.port_id = port->in_port;
 
+	if (nfp_flower_support_decap_v2(app_fw_flower)) {
+		if (meta_tci->tci != 0) {
+			payload.ext.vlan_tci = meta_tci->tci;
+			payload.ext.vlan_tpid = 0x88a8;
+		} else {
+			payload.ext.vlan_tci = 0xffff;
+			payload.ext.vlan_tpid = 0xffff;
+		}
+		payload.ext.host_ctx = nfp_flow_meta->host_ctx_id;
+	}
+
 	return nfp_flower_cmsg_tun_neigh_v6_rule(app_fw_flower, &payload);
 }
 
@@ -2575,12 +2613,14 @@ struct nfp_pre_tun_entry {
 
 static int
 nfp_flower_del_tun_neigh(struct nfp_app_fw_flower *app_fw_flower,
-		struct rte_flow *nfp_flow)
+		struct rte_flow *nfp_flow,
+		bool decap_flag)
 {
 	int ret;
 	bool flag = false;
 	struct nfp_fl_tun *tmp;
 	struct nfp_fl_tun *tun;
+	struct nfp_flower_in_port *port;
 
 	tun = &nfp_flow->tun;
 	LIST_FOREACH(tmp, &app_fw_flower->flow_priv->nn_list, next) {
@@ -2608,6 +2648,40 @@ struct nfp_pre_tun_entry {
 		}
 	}
 
+	if (!decap_flag)
+		return 0;
+
+	port = (struct nfp_flower_in_port *)(nfp_flow->payload.unmasked_data +
+			sizeof(struct nfp_fl_rule_metadata) +
+			sizeof(struct nfp_flower_meta_tci));
+
+	if (tmp->payload.v6_flag != 0) {
+		struct nfp_flower_cmsg_tun_neigh_v6 nn_v6;
+		memset(&nn_v6, 0, sizeof(struct nfp_flower_cmsg_tun_neigh_v6));
+		memcpy(nn_v6.dst_ipv6, tmp->payload.dst.dst_ipv6, sizeof(nn_v6.dst_ipv6));
+		memcpy(nn_v6.src_ipv6, tmp->payload.src.src_ipv6, sizeof(nn_v6.src_ipv6));
+		memcpy(nn_v6.common.dst_mac, tmp->payload.dst_addr, RTE_ETHER_ADDR_LEN);
+		memcpy(nn_v6.common.src_mac, tmp->payload.src_addr, RTE_ETHER_ADDR_LEN);
+		nn_v6.common.port_id = port->in_port;
+
+		ret = nfp_flower_cmsg_tun_neigh_v6_rule(app_fw_flower, &nn_v6);
+	} else {
+		struct nfp_flower_cmsg_tun_neigh_v4 nn_v4;
+		memset(&nn_v4, 0, sizeof(struct nfp_flower_cmsg_tun_neigh_v4));
+		nn_v4.dst_ipv4 = tmp->payload.dst.dst_ipv4;
+		nn_v4.src_ipv4 = tmp->payload.src.src_ipv4;
+		memcpy(nn_v4.common.dst_mac, tmp->payload.dst_addr, RTE_ETHER_ADDR_LEN);
+		memcpy(nn_v4.common.src_mac, tmp->payload.src_addr, RTE_ETHER_ADDR_LEN);
+		nn_v4.common.port_id = port->in_port;
+
+		ret = nfp_flower_cmsg_tun_neigh_v4_rule(app_fw_flower, &nn_v4);
+	}
+
+	if (ret != 0) {
+		PMD_DRV_LOG(DEBUG, "Failed to send the nn entry");
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -2895,12 +2969,14 @@ struct nfp_pre_tun_entry {
 		goto free_entry;
 	}
 
-	ret = nfp_flower_cmsg_pre_tunnel_rule(repr->app_fw_flower, nfp_flow_meta,
-			nfp_mac_idx, true);
-	if (ret != 0) {
-		PMD_DRV_LOG(ERR, "Send pre tunnel rule failed");
-		ret = -EINVAL;
-		goto free_entry;
+	if (!nfp_flower_support_decap_v2(repr->app_fw_flower)) {
+		ret = nfp_flower_cmsg_pre_tunnel_rule(repr->app_fw_flower, nfp_flow_meta,
+				nfp_mac_idx, true);
+		if (ret != 0) {
+			PMD_DRV_LOG(ERR, "Send pre tunnel rule failed");
+			ret = -EINVAL;
+			goto free_entry;
+		}
 	}
 
 	find_entry->ref_cnt = 1U;
@@ -2951,18 +3027,20 @@ struct nfp_pre_tun_entry {
 		return -EINVAL;
 	}
 
-	ret = nfp_flower_cmsg_pre_tunnel_rule(app_fw_flower, nfp_flow_meta,
-			nfp_mac_idx, false);
-	if (ret != 0) {
-		PMD_DRV_LOG(ERR, "Send pre tunnel rule failed");
-		return -EINVAL;
+	if (!nfp_flower_support_decap_v2(app_fw_flower)) {
+		ret = nfp_flower_cmsg_pre_tunnel_rule(app_fw_flower, nfp_flow_meta,
+				nfp_mac_idx, false);
+		if (ret != 0) {
+			PMD_DRV_LOG(ERR, "Send pre tunnel rule failed");
+			return -EINVAL;
+		}
 	}
 
 	meta_tci = (struct nfp_flower_meta_tci *)nfp_flow->payload.unmasked_data;
 	if (meta_tci->nfp_flow_key_layer & NFP_FLOWER_LAYER_IPV4)
-		return nfp_flower_add_tun_neigh_v4_decap(app_fw_flower, nfp_flow);
+		return nfp_flower_add_tun_neigh_v4_decap(app_fw_flower, nfp_flow_meta, nfp_flow);
 	else
-		return nfp_flower_add_tun_neigh_v6_decap(app_fw_flower, nfp_flow);
+		return nfp_flower_add_tun_neigh_v6_decap(app_fw_flower, nfp_flow_meta, nfp_flow);
 }
 
 static int
@@ -3662,11 +3740,11 @@ struct nfp_pre_tun_entry {
 		break;
 	case NFP_FLOW_ENCAP:
 		/* Delete the entry from nn table */
-		ret = nfp_flower_del_tun_neigh(app_fw_flower, nfp_flow);
+		ret = nfp_flower_del_tun_neigh(app_fw_flower, nfp_flow, false);
 		break;
 	case NFP_FLOW_DECAP:
 		/* Delete the entry from nn table */
-		ret = nfp_flower_del_tun_neigh(app_fw_flower, nfp_flow);
+		ret = nfp_flower_del_tun_neigh(app_fw_flower, nfp_flow, true);
 		if (ret != 0)
 			goto exit;
 
