diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 3bdef8e..1ffafa0 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -10720,6 +10720,270 @@ i40e_parse_vxlan_tunnel_filter(const struct rte_flow_attr *attr,
 	return i40e_parse_tunnel_act_attr(attr, actions, filter, error);
 }
 
+/* whether it is NVGRE tunnel rule */
+static int
+i40e_parse_nvgre_tunnel_filter(const struct rte_flow_attr *attr,
+			       const struct rte_flow_item *pattern,
+			       const struct rte_flow_action *actions,
+			       struct rte_eth_tunnel_filter_conf *filter,
+			       struct rte_flow_error *error)
+{
+	const struct rte_flow_item *item;
+	const struct rte_flow_item_eth *o_eth_spec = NULL;
+	const struct rte_flow_item_eth *o_eth_mask = NULL;
+	const struct rte_flow_item_nvgre *nvgre_spec = NULL;
+	const struct rte_flow_item_nvgre *nvgre_mask = NULL;
+	const struct rte_flow_item_eth *i_eth_spec, *i_eth_mask;
+	const struct rte_flow_item_vlan *vlan_spec = NULL;
+	const struct rte_flow_item_vlan *vlan_mask = NULL;
+	struct ether_addr macaddr_unmasked = {
+		.addr_bytes = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
+	};
+	struct ether_addr macaddr_masked = {
+		.addr_bytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
+	};
+	bool is_tni_masked = 0;
+	uint32_t i;
+
+	/* parse pattern */
+	i = 0;
+
+	/* The first not void item should be ETH or IP or UDP or VXLAN. */
+	PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+			  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	if (item->type != RTE_FLOW_ITEM_TYPE_ETH &&
+	    item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
+	    item->type != RTE_FLOW_ITEM_TYPE_IPV6 &&
+	    item->type != RTE_FLOW_ITEM_TYPE_NVGRE) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
+		o_eth_spec = (const struct rte_flow_item_eth *)item->spec;
+		o_eth_mask = (const struct rte_flow_item_eth *)item->mask;
+
+		if ((!o_eth_spec && o_eth_mask) ||
+		    (o_eth_spec && !o_eth_mask)) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		if (o_eth_spec)
+			rte_memcpy(&filter->outer_mac, &o_eth_spec->dst,
+				   ETHER_ADDR_LEN);
+
+		if (o_eth_mask) {
+			/**
+			 * DST MAC address shouldn't be masked.
+			 * SRC MAC address should be masked.
+			 * Ethertype should be masked.
+			 */
+			if (!is_same_ether_addr(&o_eth_mask->dst,
+						&macaddr_unmasked) ||
+			    !is_same_ether_addr(&o_eth_mask->src,
+						&macaddr_masked) ||
+			    o_eth_mask->type) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		}
+
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
+		    item->type != RTE_FLOW_ITEM_TYPE_IPV6) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_IPV4 ||
+	    item->type == RTE_FLOW_ITEM_TYPE_IPV6) {
+		/**
+		 * If the item is IP, the content should be NULL.
+		 * Only used to describe the protocol stack.
+		 */
+		if (item->spec || item->mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* Check if the next not void item is UDP. */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_NVGRE) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (item->type != RTE_FLOW_ITEM_TYPE_NVGRE) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	nvgre_spec = (const struct rte_flow_item_nvgre *)item->spec;
+	nvgre_mask = (const struct rte_flow_item_nvgre *)item->mask;
+
+	/**
+	 * Check if NVGRE item is used to describe the protocol stack.
+	 * If yes, both nvgre_spec and nvgre_mask should be NULL.
+	 * If no, either nvgre_spec or nvgre_mask shouldn't be NULL.
+	 */
+	if ((!nvgre_spec && nvgre_mask) ||
+	    (nvgre_spec && !nvgre_mask)) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	/* Check if TNI isn't masked */
+	if (nvgre_mask) {
+		for (uint32_t j = 0; j < RTE_DIM(nvgre_mask->tni); j++) {
+			if (nvgre_mask->tni[j] == 0xFF) {
+				if (j > 0 &&
+				    (nvgre_mask->tni[j] !=
+				     nvgre_mask->tni[j - 1])) {
+					error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+					return -EINVAL;
+				}
+				is_tni_masked = 0;
+			} else if (nvgre_mask->tni[j] == 0) {
+				if (j > 0 &&
+				    (nvgre_mask->tni[j] !=
+				     nvgre_mask->tni[j - 1])) {
+					error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+					return -EINVAL;
+				}
+				is_tni_masked = 1;
+			} else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		}
+	}
+
+	/* check if the next not void item is ETH*/
+	i++;
+	PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+			  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	if (item->type != RTE_FLOW_ITEM_TYPE_ETH) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	if (item->type ==  RTE_FLOW_ITEM_TYPE_ETH) {
+		i_eth_spec = (const struct rte_flow_item_eth *)item->spec;
+		i_eth_mask = (const struct rte_flow_item_eth *)item->mask;
+
+		if (!i_eth_spec || !i_eth_mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		rte_memcpy(&filter->inner_mac, &i_eth_spec->dst,
+			   ETHER_ADDR_LEN);
+
+		/**
+		 * DST address of inner MAC shouldn't be masked.
+		 * SRC address of Inner MAC should be masked.
+		 */
+		if (!is_same_ether_addr(&i_eth_mask->dst, &macaddr_unmasked) ||
+		    !is_same_ether_addr(&i_eth_mask->src, &macaddr_masked) ||
+		    i_eth_mask->type) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* Check if the next not void item is VLAN or END. */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_VLAN &&
+		    item->type != RTE_FLOW_ITEM_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
+		vlan_spec = (const struct rte_flow_item_vlan *)item->spec;
+		vlan_mask = (const struct rte_flow_item_vlan *)item->mask;
+
+		if (!(vlan_spec && vlan_mask)) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* check if the next not void item is END */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (vlan_spec && vlan_mask &&
+	    (vlan_mask->tci == rte_cpu_to_be_16(0x0FFF))) {
+		filter->inner_vlan = rte_be_to_cpu_16(vlan_spec->tci) & 0xFFF;
+		if (nvgre_spec && nvgre_mask && !is_tni_masked) {
+			rte_memcpy(&filter->tenant_id, nvgre_spec->tni,
+				   RTE_DIM(nvgre_spec->tni));
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_IMAC_IVLAN_TENID;
+			else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else if (!nvgre_spec && !nvgre_mask) {
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_IMAC_IVLAN;
+			else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	} else if ((!vlan_spec && !vlan_mask) ||
+		   (vlan_spec && vlan_mask && vlan_mask->tci == 0x0)) {
+		if (nvgre_spec && nvgre_mask && !is_tni_masked) {
+			rte_memcpy(&filter->tenant_id, nvgre_spec->tni,
+				   RTE_DIM(nvgre_spec->tni));
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_IMAC_TENID;
+			else
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_OMAC_TENID_IMAC;
+		} else if (!nvgre_spec && !nvgre_mask) {
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type = ETH_TUNNEL_FILTER_IMAC;
+			else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	} else {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	filter->tunnel_type = RTE_TUNNEL_TYPE_NVGRE;
+
+	return i40e_parse_tunnel_act_attr(attr, actions, filter, error);
+}
+
 static int
 i40e_parse_tunnel_filter(const struct rte_flow_attr *attr,
 			 const struct rte_flow_item *pattern,
@@ -10734,6 +10998,11 @@ i40e_parse_tunnel_filter(const struct rte_flow_attr *attr,
 	if (!ret)
 		return 0;
 
+	ret = i40e_parse_nvgre_tunnel_filter(attr, pattern,
+					     actions, rule, error);
+	if (!ret)
+		return 0;
+
 	return ret;
 }
 
diff --git a/lib/librte_ether/rte_flow.h b/lib/librte_ether/rte_flow.h
index 211f307..6bdbba1 100644
--- a/lib/librte_ether/rte_flow.h
+++ b/lib/librte_ether/rte_flow.h
@@ -270,6 +270,13 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_vxlan.
 	 */
 	RTE_FLOW_ITEM_TYPE_VXLAN,
+
+	/**
+	 * Matches a NVGRE header.
+	 *
+	 * See struct rte_flow_item_nvgre.
+	 */
+	RTE_FLOW_ITEM_TYPE_NVGRE,
 };
 
 /**
@@ -461,6 +468,22 @@ struct rte_flow_item_vxlan {
 };
 
 /**
+ * RTE_FLOW_ITEM_TYPE_NVGRE.
+ *
+ * Matches a NVGRE header.
+ */
+struct rte_flow_item_nvgre {
+	uint32_t flags0:1; /**< 0 */
+	uint32_t rsvd1:1; /**< 1 bit not defined */
+	uint32_t flags1:2; /**< 2 bits, 1 0 */
+	uint32_t rsvd0:9; /**< Reserved0 */
+	uint32_t ver:3; /**< version */
+	uint32_t protocol:16; /**< protocol type, 0x6558 */
+	uint8_t tni[3]; /**< tenant network ID or virtual subnet ID */
+	uint8_t flow_id; /**< flow ID or Reserved */
+};
+
+/**
  * Matching pattern item definition.
  *
  * A pattern is formed by stacking items starting from the lowest protocol
