diff --git a/config/common_base b/config/common_base
index 8ef75c203..9c876fdbe 100644
--- a/config/common_base
+++ b/config/common_base
@@ -324,6 +324,7 @@ CONFIG_RTE_LIBRTE_ICE_DEBUG_TX=n
 CONFIG_RTE_LIBRTE_ICE_DEBUG_TX_FREE=n
 CONFIG_RTE_LIBRTE_ICE_RX_ALLOW_BULK_ALLOC=y
 CONFIG_RTE_LIBRTE_ICE_16BYTE_RX_DESC=n
+CONFIG_RTE_LIBRTE_ICE_PROTO_XTR=y
 
 # Compile burst-oriented IAVF PMD driver
 #
diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index 03819d29f..185601f21 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -46,6 +46,12 @@ Please note that enabling debugging options may affect system performance.
 
   Toggle to use a 16-byte RX descriptor, by default the RX descriptor is 32 byte.
 
+- ``CONFIG_RTE_LIBRTE_ICE_PROTO_XTR`` (default ``y``)
+
+  Toggle to enable ``Protocol extraction for per queue``, it can't be enabled
+  with ``CONFIG_RTE_LIBRTE_ICE_16BYTE_RX_DESC`` at the same time. And it will
+  modify the ``rte_mbuf::udata64``.
+
 Runtime Config Options
 ~~~~~~~~~~~~~~~~~~~~~~
 
@@ -61,6 +67,107 @@ Runtime Config Options
   NOTE: In Safe mode, only very limited features are available, features like RSS,
   checksum, fdir, tunneling ... are all disabled.
 
+- ``Protocol extraction for per queue``
+
+  Configure the RX queues to do protocol extraction into ``rte_mbuf::udata64``
+  for protocol handling acceleration, like checking the TCP SYN packets quickly.
+
+  The argument format is::
+
+      -w 18:00.0,proto_xtr=<queues:protocol>[<queues:protocol>...]
+      -w 18:00.0,proto_xtr=<protocol>
+
+  Queues are grouped by ``(`` and ``)`` within the group. The ``-`` character
+  is used as a range separator and ``,`` is used as a single number separator.
+  The grouping ``()`` can be omitted for single element group. If no queues are
+  specified, PMD will use this protocol extraction type for all queues.
+
+  Protocol is : ``vlan, ipv4, ipv6, ipv6_flow, tcp``.
+
+  .. code-block:: console
+
+    testpmd -w 18:00.0,proto_xtr='[(1,2-3,8-9):tcp,10-13:vlan]'
+
+  This setting means queues 1, 2-3, 8-9 are TCP extraction, queues 10-13 are
+  VLAN extraction, other queues run with no protocol extraction.
+
+  .. code-block:: console
+
+    testpmd -w 18:00.0,proto_xtr=vlan,proto_xtr='[(1,2-3,8-9):tcp,10-23:ipv6]'
+
+  This setting means queues 1, 2-3, 8-9 are TCP extraction, queues 10-23 are
+  IPv6 extraction, other queues use the default VLAN extraction.
+
+  The extraction will be copied into the lower 32 bit of ``rte_mbuf::udata64``.
+
+  .. table:: Protocol extraction : ``vlan``
+
+   +----------------------------+----------------------------+
+   |           VLAN2            |           VLAN1            |
+   +======+===+=================+======+===+=================+
+   |  PCP | D |       VID       |  PCP | D |       VID       |
+   +------+---+-----------------+------+---+-----------------+
+
+  VLAN1 - single or EVLAN (first for QinQ).
+
+  VLAN2 - C-VLAN (second for QinQ).
+
+  .. table:: Protocol extraction : ``ipv4``
+
+   +----------------------------+----------------------------+
+   |           IPHDR2           |           IPHDR1           |
+   +======+=======+=============+==============+=============+
+   |  Ver |Hdr Len|    ToS      |      TTL     |  Protocol   |
+   +------+-------+-------------+--------------+-------------+
+
+  IPHDR1 - IPv4 header word 4, "TTL" and "Protocol" fields.
+
+  IPHDR2 - IPv4 header word 0, "Ver", "Hdr Len" and "Type of Service" fields.
+
+  .. table:: Protocol extraction : ``ipv6``
+
+   +----------------------------+----------------------------+
+   |           IPHDR2           |           IPHDR1           |
+   +=====+=============+========+=============+==============+
+   | Ver |Traffic class|  Flow  | Next Header |   Hop Limit  |
+   +-----+-------------+--------+-------------+--------------+
+
+  IPHDR1 - IPv6 header word 3, "Next Header" and "Hop Limit" fields.
+
+  IPHDR2 - IPv6 header word 0, "Ver", "Traffic class" and high 4 bits of
+  "Flow Label" fields.
+
+  .. table:: Protocol extraction : ``ipv6_flow``
+
+   +----------------------------+----------------------------+
+   |           IPHDR2           |           IPHDR1           |
+   +=====+=============+========+============================+
+   | Ver |Traffic class|            Flow Label               |
+   +-----+-------------+-------------------------------------+
+
+  IPHDR1 - IPv6 header word 1, 16 low bits of the "Flow Label" field.
+
+  IPHDR2 - IPv6 header word 0, "Ver", "Traffic class" and high 4 bits of
+  "Flow Label" fields.
+
+  .. table:: Protocol extraction : ``tcp``
+
+   +----------------------------+----------------------------+
+   |           TCPHDR2          |           TCPHDR1          |
+   +============================+======+======+==============+
+   |          Reserved          |Offset|  RSV |     Flags    |
+   +----------------------------+------+------+--------------+
+
+  TCPHDR1 - TCP header word 6, "Data Offset" and "Flags" fields.
+
+  TCPHDR2 - Reserved
+
+  Use ``get_proto_xtr_flds(struct rte_mbuf *mb)`` to access the protocol
+  extraction, do not use ``rte_mbuf::udata64`` directly.
+
+  The ``dump_proto_xtr_flds(struct rte_mbuf *mb)`` routine shows how to
+  access the protocol extraction result in ``struct rte_mbuf``.
+
 Driver compilation and testing
 ------------------------------
 
diff --git a/drivers/net/ice/Makefile b/drivers/net/ice/Makefile
index ae53c2646..4a279f196 100644
--- a/drivers/net/ice/Makefile
+++ b/drivers/net/ice/Makefile
@@ -82,4 +82,7 @@ ifeq ($(CC_AVX2_SUPPORT), 1)
 endif
 SRCS-$(CONFIG_RTE_LIBRTE_ICE_PMD) += ice_generic_flow.c
 
+# install this header file
+SYMLINK-$(CONFIG_RTE_LIBRTE_ICE_PMD)-include := rte_pmd_ice.h
+
 include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/net/ice/ice_ethdev.c b/drivers/net/ice/ice_ethdev.c
index 63997fdfb..68ab5a2bf 100644
--- a/drivers/net/ice/ice_ethdev.c
+++ b/drivers/net/ice/ice_ethdev.c
@@ -19,9 +19,13 @@
 
 /* devargs */
 #define ICE_SAFE_MODE_SUPPORT_ARG "safe-mode-support"
+#define ICE_PROTO_XTR_ARG         "proto_xtr"
 
 static const char * const ice_valid_args[] = {
 	ICE_SAFE_MODE_SUPPORT_ARG,
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+	ICE_PROTO_XTR_ARG,
+#endif
 	NULL
 };
 
@@ -257,6 +261,307 @@ ice_init_controlq_parameter(struct ice_hw *hw)
 	hw->mailboxq.sq_buf_size = ICE_MAILBOXQ_BUF_SZ;
 }
 
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+
+static int
+lookup_proto_xtr_type(const char *xtr_name)
+{
+	static struct {
+		const char *name;
+		enum proto_xtr_type type;
+	} xtr_type_map[] = {
+		{ "vlan",      PROTO_XTR_VLAN      },
+		{ "ipv4",      PROTO_XTR_IPV4      },
+		{ "ipv6",      PROTO_XTR_IPV6      },
+		{ "ipv6_flow", PROTO_XTR_IPV6_FLOW },
+		{ "tcp",       PROTO_XTR_TCP       },
+	};
+	uint32_t i;
+
+	for (i = 0; i < RTE_DIM(xtr_type_map); i++) {
+		if (strcmp(xtr_name, xtr_type_map[i].name) == 0)
+			return xtr_type_map[i].type;
+	}
+
+	return -1;
+}
+
+/*
+ * Parse elem, the elem could be single number/range or '(' ')' group
+ * 1) A single number elem, it's just a simple digit. e.g. 9
+ * 2) A single range elem, two digits with a '-' between. e.g. 2-6
+ * 3) A group elem, combines multiple 1) or 2) with '( )'. e.g (0,2-4,6)
+ *    Within group elem, '-' used for a range separator;
+ *                       ',' used for a single number.
+ */
+static int
+parse_queue_set(const char *input, int xtr_type, struct ice_pf *pf)
+{
+	const char *str = input;
+	char *end = NULL;
+	uint32_t min, max;
+	uint32_t idx;
+
+	while (isblank(*str))
+		str++;
+
+	if ((!isdigit(*str) && *str != '(') || (*str == '\0'))
+		return -1;
+
+	/* process single number or single range of number */
+	if (*str != '(') {
+		errno = 0;
+		idx = strtoul(str, &end, 10);
+		if (errno || end == NULL || idx >= pf->lan_nb_qps)
+			return -1;
+
+		while (isblank(*end))
+			end++;
+
+		min = idx;
+		max = idx;
+
+		/* process single <number>-<number> */
+		if (*end == '-') {
+			end++;
+			while (isblank(*end))
+				end++;
+			if (!isdigit(*end))
+				return -1;
+
+			errno = 0;
+			idx = strtoul(end, &end, 10);
+			if (errno || end == NULL || idx >= pf->lan_nb_qps)
+				return -1;
+
+			max = idx;
+			while (isblank(*end))
+				end++;
+		}
+
+		if (*end != ':')
+			return -1;
+
+		for (idx = RTE_MIN(min, max);
+		     idx <= RTE_MAX(min, max); idx++)
+			pf->proto_xtr_table[idx] = xtr_type;
+
+		return 0;
+	}
+
+	/* process set within bracket */
+	str++;
+	while (isblank(*str))
+		str++;
+	if (*str == '\0')
+		return -1;
+
+	min = pf->lan_nb_qps;
+	do {
+		/* go ahead to the first digit */
+		while (isblank(*str))
+			str++;
+		if (!isdigit(*str))
+			return -1;
+
+		/* get the digit value */
+		errno = 0;
+		idx = strtoul(str, &end, 10);
+		if (errno || end == NULL || idx >= pf->lan_nb_qps)
+			return -1;
+
+		/* go ahead to separator '-',',' and ')' */
+		while (isblank(*end))
+			end++;
+		if (*end == '-') {
+			if (min == pf->lan_nb_qps)
+				min = idx;
+			else /* avoid continuous '-' */
+				return -1;
+		} else if (*end == ',' || *end == ')') {
+			max = idx;
+			if (min == pf->lan_nb_qps)
+				min = idx;
+
+			for (idx = RTE_MIN(min, max);
+			     idx <= RTE_MAX(min, max); idx++)
+				pf->proto_xtr_table[idx] = xtr_type;
+
+			min = pf->lan_nb_qps;
+		} else {
+			return -1;
+		}
+
+		str = end + 1;
+	} while (*end != ')' && *end != '\0');
+
+	return 0;
+}
+
+static int
+parse_queue_proto_xtr(const char *queues, struct ice_pf *pf)
+{
+	const char *queue_start;
+	uint32_t idx;
+	int xtr_type;
+	char xtr_name[32];
+
+	while (isblank(*queues))
+		queues++;
+
+	if (*queues != '[') {
+		xtr_type = lookup_proto_xtr_type(queues);
+		if (xtr_type < 0)
+			return -1;
+
+		pf->proto_xtr = xtr_type;
+		return 0;
+	}
+
+	if (pf->proto_xtr_table == NULL) {
+		PMD_DRV_LOG(ERR,
+			    "Can't support protocol extraction table for no memory");
+		return -1;
+	}
+
+	queues++;
+	do {
+		while (isblank(*queues))
+			queues++;
+		if (*queues == '\0')
+			return -1;
+
+		queue_start = queues;
+
+		/* go across a complete bracket */
+		if (*queue_start == '(') {
+			queues += strcspn(queues, ")");
+			if (*queues != ')')
+				return -1;
+		}
+
+		/* scan the separator ':' */
+		queues += strcspn(queues, ":");
+		if (*queues++ != ':')
+			return -1;
+		while (isblank(*queues))
+			queues++;
+
+		for (idx = 0; ; idx++) {
+			if (isblank(queues[idx]) ||
+			    queues[idx] == ',' ||
+			    queues[idx] == ']' ||
+			    queues[idx] == '\0')
+				break;
+
+			if (idx > sizeof(xtr_name) - 2)
+				return -1;
+
+			xtr_name[idx] = queues[idx];
+		}
+		xtr_name[idx] = '\0';
+		xtr_type = lookup_proto_xtr_type(xtr_name);
+		if (xtr_type < 0)
+			return -1;
+
+		queues += idx;
+
+		while (isblank(*queues) || *queues == ',' || *queues == ']')
+			queues++;
+
+		if (parse_queue_set(queue_start, xtr_type, pf) < 0)
+			return -1;
+	} while (*queues != '\0');
+
+	return 0;
+}
+
+static int
+handle_proto_xtr_arg(__rte_unused const char *key, const char *value,
+		     void *extra_args)
+{
+	struct ice_pf *pf = extra_args;
+
+	if (value == NULL || extra_args == NULL)
+		return -EINVAL;
+
+	if (parse_queue_proto_xtr(value, pf) < 0) {
+		PMD_DRV_LOG(ERR,
+			    "The protocol extraction parameter is wrong : '%s'",
+			    value);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+ice_parse_proto_xtr_devarg(struct rte_devargs *devargs, struct ice_pf *pf)
+{
+	struct rte_kvargs *kvlist;
+
+	pf->proto_xtr = PROTO_XTR_NONE;
+	if (devargs == NULL)
+		return;
+
+	kvlist = rte_kvargs_parse(devargs->args, NULL);
+	if (kvlist == NULL)
+		return;
+
+	rte_kvargs_process(kvlist, ICE_PROTO_XTR_ARG,
+			   handle_proto_xtr_arg, pf);
+
+	rte_kvargs_free(kvlist);
+}
+
+static bool
+ice_proto_xtr_support(struct ice_hw *hw)
+{
+#define FLX_REG(val, fld, idx) \
+	(((val) & GLFLXP_RXDID_FLX_WRD_##idx##_##fld##_M) >> \
+	 GLFLXP_RXDID_FLX_WRD_##idx##_##fld##_S)
+	static struct {
+		uint32_t rxdid;
+		uint16_t protid_0;
+		uint16_t protid_1;
+	} xtr_sets[] = {
+		{ ICE_RXDID_COMMS_AUX_VLAN, ICE_PROT_EVLAN_O, ICE_PROT_VLAN_O },
+		{ ICE_RXDID_COMMS_AUX_IPV4, ICE_PROT_IPV4_OF_OR_S,
+		  ICE_PROT_IPV4_OF_OR_S },
+		{ ICE_RXDID_COMMS_AUX_IPV6, ICE_PROT_IPV6_OF_OR_S,
+		  ICE_PROT_IPV6_OF_OR_S },
+		{ ICE_RXDID_COMMS_AUX_IPV6_FLOW, ICE_PROT_IPV6_OF_OR_S,
+		  ICE_PROT_IPV6_OF_OR_S },
+		{ ICE_RXDID_COMMS_AUX_TCP, ICE_PROT_TCP_IL, ICE_PROT_ID_INVAL },
+	};
+	uint32_t i;
+
+	for (i = 0; i < RTE_DIM(xtr_sets); i++) {
+		uint32_t rxdid = xtr_sets[i].rxdid;
+		uint32_t v;
+
+		if (xtr_sets[i].protid_0 != ICE_PROT_ID_INVAL) {
+			v = ICE_READ_REG(hw, GLFLXP_RXDID_FLX_WRD_4(rxdid));
+
+			if (FLX_REG(v, PROT_MDID, 4) != xtr_sets[i].protid_0 ||
+			    FLX_REG(v, RXDID_OPCODE, 4) != ICE_RX_OPC_EXTRACT)
+				return false;
+		}
+
+		if (xtr_sets[i].protid_1 != ICE_PROT_ID_INVAL) {
+			v = ICE_READ_REG(hw, GLFLXP_RXDID_FLX_WRD_5(rxdid));
+
+			if (FLX_REG(v, PROT_MDID, 5) != xtr_sets[i].protid_1 ||
+			    FLX_REG(v, RXDID_OPCODE, 5) != ICE_RX_OPC_EXTRACT)
+				return false;
+		}
+	}
+
+	return true;
+}
+
+#endif
+
 static int
 ice_res_pool_init(struct ice_res_pool_info *pool, uint32_t base,
 		  uint32_t num)
@@ -1088,6 +1393,21 @@ ice_pf_sw_init(struct rte_eth_dev *dev)
 
 	pf->lan_nb_qps = pf->lan_nb_qp_max;
 
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+	if (ice_proto_xtr_support(hw)) {
+		pf->proto_xtr_table = rte_zmalloc(NULL, pf->lan_nb_qps, 0);
+		if (pf->proto_xtr_table == NULL)
+			PMD_DRV_LOG(ERR, "No memory for protocol extraction table");
+
+		ice_parse_proto_xtr_devarg(dev->device->devargs, pf);
+	} else {
+		PMD_DRV_LOG(NOTICE, "Protocol extraction is disabled");
+
+		pf->proto_xtr = PROTO_XTR_NONE;
+		pf->proto_xtr_table = NULL;
+	}
+#endif
+
 	return 0;
 }
 
@@ -1556,6 +1876,9 @@ ice_dev_init(struct rte_eth_dev *dev)
 	ice_sched_cleanup_all(hw);
 	rte_free(hw->port_info);
 	ice_shutdown_all_ctrlq(hw);
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+	rte_free(pf->proto_xtr_table);
+#endif
 
 	return ret;
 }
@@ -1681,6 +2004,10 @@ ice_dev_close(struct rte_eth_dev *dev)
 	rte_free(hw->port_info);
 	hw->port_info = NULL;
 	ice_shutdown_all_ctrlq(hw);
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+	rte_free(pf->proto_xtr_table);
+	pf->proto_xtr_table = NULL;
+#endif
 }
 
 static int
@@ -3807,6 +4134,9 @@ RTE_PMD_REGISTER_PCI(net_ice, rte_ice_pmd);
 RTE_PMD_REGISTER_PCI_TABLE(net_ice, pci_id_ice_map);
 RTE_PMD_REGISTER_KMOD_DEP(net_ice, "* igb_uio | uio_pci_generic | vfio-pci");
 RTE_PMD_REGISTER_PARAM_STRING(net_ice,
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+			      ICE_PROTO_XTR_ARG "=[queue:]<vlan|ipv4|ipv6|ipv6_flow|tcp>"
+#endif
 			      ICE_SAFE_MODE_SUPPORT_ARG "=<0|1>");
 
 RTE_INIT(ice_init_log)
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index c4444f903..19fe277e3 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -263,6 +263,10 @@ struct ice_pf {
 	uint16_t lan_nb_qp_max;
 	uint16_t lan_nb_qps; /* The number of queue pairs of LAN */
 	uint16_t base_queue; /* The base queue pairs index  in the device */
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+	uint8_t proto_xtr; /* Protocol extraction from flexible descriptor */
+	uint8_t *proto_xtr_table; /* Per queue for lan_nb_qps size */
+#endif
 	struct ice_hw_port_stats stats_offset;
 	struct ice_hw_port_stats stats;
 	/* internal packet statistics, it should be excluded from the total */
diff --git a/drivers/net/ice/ice_rxtx.c b/drivers/net/ice/ice_rxtx.c
index 3b4ccf151..69913b545 100644
--- a/drivers/net/ice/ice_rxtx.c
+++ b/drivers/net/ice/ice_rxtx.c
@@ -13,6 +13,40 @@
 		PKT_TX_TCP_SEG |		 \
 		PKT_TX_OUTER_IP_CKSUM)
 
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+
+static inline uint8_t
+ice_rxdid_to_proto_xtr_type(uint8_t rxdid)
+{
+	static uint8_t xtr_map[] = {
+		[ICE_RXDID_COMMS_AUX_VLAN]      = PROTO_XTR_VLAN,
+		[ICE_RXDID_COMMS_AUX_IPV4]      = PROTO_XTR_IPV4,
+		[ICE_RXDID_COMMS_AUX_IPV6]      = PROTO_XTR_IPV6,
+		[ICE_RXDID_COMMS_AUX_IPV6_FLOW] = PROTO_XTR_IPV6_FLOW,
+		[ICE_RXDID_COMMS_AUX_TCP]       = PROTO_XTR_TCP,
+	};
+
+	return rxdid < RTE_DIM(xtr_map) ? xtr_map[rxdid] : PROTO_XTR_NONE;
+}
+
+static inline uint8_t
+ice_proto_xtr_type_to_rxdid(uint8_t xtr_tpye)
+{
+	static uint8_t rxdid_map[] = {
+		[PROTO_XTR_VLAN]      = ICE_RXDID_COMMS_AUX_VLAN,
+		[PROTO_XTR_IPV4]      = ICE_RXDID_COMMS_AUX_IPV4,
+		[PROTO_XTR_IPV6]      = ICE_RXDID_COMMS_AUX_IPV6,
+		[PROTO_XTR_IPV6_FLOW] = ICE_RXDID_COMMS_AUX_IPV6_FLOW,
+		[PROTO_XTR_TCP]       = ICE_RXDID_COMMS_AUX_TCP,
+	};
+	uint8_t rxdid;
+
+	rxdid = xtr_tpye < RTE_DIM(rxdid_map) ? rxdid_map[xtr_tpye] : 0;
+
+	return rxdid != 0 ? rxdid : ICE_RXDID_COMMS_GENERIC;
+}
+
+#endif
 
 static enum ice_status
 ice_program_hw_rx_queue(struct ice_rx_queue *rxq)
@@ -84,6 +118,13 @@ ice_program_hw_rx_queue(struct ice_rx_queue *rxq)
 	rx_ctx.showiv = 0;
 	rx_ctx.crcstrip = (rxq->crc_len == 0) ? 1 : 0;
 
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+	rxdid = ice_proto_xtr_type_to_rxdid(rxq->proto_xtr);
+
+	PMD_DRV_LOG(DEBUG, "Port (%u) - Rx queue (%u) is set with RXDID : %u",
+		    rxq->port_id, rxq->queue_id, rxdid);
+#endif
+
 	/* Enable Flexible Descriptors in the queue context which
 	 * allows this driver to select a specific receive descriptor format
 	 */
@@ -639,6 +680,13 @@ ice_rx_queue_setup(struct rte_eth_dev *dev,
 	rxq->vsi = vsi;
 	rxq->rx_deferred_start = rx_conf->rx_deferred_start;
 
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+	rxq->proto_xtr = pf->proto_xtr_table != NULL ?
+			 pf->proto_xtr_table[queue_idx] : PROTO_XTR_NONE;
+	if (rxq->proto_xtr == PROTO_XTR_NONE)
+		rxq->proto_xtr = pf->proto_xtr;
+#endif
+
 	/* Allocate the maximun number of RX ring hardware descriptor. */
 	len = ICE_MAX_RING_DESC;
 
@@ -1059,6 +1107,10 @@ ice_rxd_to_vlan_tci(struct rte_mbuf *mb, volatile union ice_rx_flex_desc *rxdp)
 		   mb->vlan_tci, mb->vlan_tci_outer);
 }
 
+#define ICE_RX_PROTO_XTR_VALID \
+	((1 << ICE_RX_FLEX_DESC_STATUS1_XTRMD4_VALID_S) | \
+	 (1 << ICE_RX_FLEX_DESC_STATUS1_XTRMD5_VALID_S))
+
 static inline void
 ice_rxd_to_pkt_fields(struct rte_mbuf *mb,
 		      volatile union ice_rx_flex_desc *rxdp)
@@ -1072,6 +1124,29 @@ ice_rxd_to_pkt_fields(struct rte_mbuf *mb,
 		mb->ol_flags |= PKT_RX_RSS_HASH;
 		mb->hash.rss = rte_le_to_cpu_32(desc->rss_hash);
 	}
+
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+#ifdef RTE_LIBRTE_ICE_16BYTE_RX_DESC
+#error "RTE_LIBRTE_ICE_16BYTE_RX_DESC must be disabled"
+#endif
+	init_proto_xtr_flds(mb);
+
+	stat_err = rte_le_to_cpu_16(desc->status_error1);
+	if (stat_err & ICE_RX_PROTO_XTR_VALID) {
+		struct proto_xtr_flds *xtr = get_proto_xtr_flds(mb);
+
+		if (stat_err & (1 << ICE_RX_FLEX_DESC_STATUS1_XTRMD4_VALID_S))
+			xtr->u.raw.data0 =
+				rte_le_to_cpu_16(desc->flex_ts.flex.aux0);
+
+		if (stat_err & (1 << ICE_RX_FLEX_DESC_STATUS1_XTRMD5_VALID_S))
+			xtr->u.raw.data1 =
+				rte_le_to_cpu_16(desc->flex_ts.flex.aux1);
+
+		xtr->type = ice_rxdid_to_proto_xtr_type(desc->rxdid);
+		xtr->magic = PROTO_XTR_MAGIC_ID;
+	}
+#endif
 }
 
 #ifdef RTE_LIBRTE_ICE_RX_ALLOW_BULK_ALLOC
diff --git a/drivers/net/ice/ice_rxtx.h b/drivers/net/ice/ice_rxtx.h
index 64e891875..6f4726854 100644
--- a/drivers/net/ice/ice_rxtx.h
+++ b/drivers/net/ice/ice_rxtx.h
@@ -5,6 +5,7 @@
 #ifndef _ICE_RXTX_H_
 #define _ICE_RXTX_H_
 
+#include "rte_pmd_ice.h"
 #include "ice_ethdev.h"
 
 #define ICE_ALIGN_RING_DESC  32
@@ -78,6 +79,9 @@ struct ice_rx_queue {
 	uint16_t max_pkt_len; /* Maximum packet length */
 	bool q_set; /* indicate if rx queue has been configured */
 	bool rx_deferred_start; /* don't start this queue in dev start */
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+	uint8_t proto_xtr; /* Protocol extraction from flexible descriptor */
+#endif
 	ice_rx_release_mbufs_t rx_rel_mbufs;
 };
 
diff --git a/drivers/net/ice/ice_rxtx_vec_common.h b/drivers/net/ice/ice_rxtx_vec_common.h
index c5f0d564f..ebdd714f8 100644
--- a/drivers/net/ice/ice_rxtx_vec_common.h
+++ b/drivers/net/ice/ice_rxtx_vec_common.h
@@ -234,6 +234,11 @@ ice_rx_vec_queue_default(struct ice_rx_queue *rxq)
 	if (rxq->nb_rx_desc % rxq->rx_free_thresh)
 		return -1;
 
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+	if (rxq->proto_xtr != PROTO_XTR_NONE)
+		return -1;
+#endif
+
 	return 0;
 }
 
diff --git a/drivers/net/ice/meson.build b/drivers/net/ice/meson.build
index 36b4b3c85..6828170a9 100644
--- a/drivers/net/ice/meson.build
+++ b/drivers/net/ice/meson.build
@@ -34,3 +34,5 @@ if arch_subdir == 'x86'
 		objs += ice_avx2_lib.extract_objects('ice_rxtx_vec_avx2.c')
 	endif
 endif
+
+install_headers('rte_pmd_ice.h')
diff --git a/drivers/net/ice/rte_pmd_ice.h b/drivers/net/ice/rte_pmd_ice.h
new file mode 100644
index 000000000..2bd73dd3d
--- /dev/null
+++ b/drivers/net/ice/rte_pmd_ice.h
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#ifndef _RTE_PMD_ICE_H_
+#define _RTE_PMD_ICE_H_
+
+#include <stdio.h>
+#include <rte_mbuf.h>
+#include <rte_ethdev.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum proto_xtr_type {
+	PROTO_XTR_NONE,
+	PROTO_XTR_VLAN,
+	PROTO_XTR_IPV4,
+	PROTO_XTR_IPV6,
+	PROTO_XTR_IPV6_FLOW,
+	PROTO_XTR_TCP,
+};
+
+struct proto_xtr_flds {
+	union {
+		struct {
+			uint16_t data0;
+			uint16_t data1;
+		} raw;
+		struct {
+			uint16_t stag_vid:12,
+				 stag_dei:1,
+				 stag_pcp:3;
+			uint16_t ctag_vid:12,
+				 ctag_dei:1,
+				 ctag_pcp:3;
+		} vlan;
+		struct {
+			uint16_t protocol:8,
+				 ttl:8;
+			uint16_t tos:8,
+				 ihl:4,
+				 version:4;
+		} ipv4;
+		struct {
+			uint16_t hoplimit:8,
+				 nexthdr:8;
+			uint16_t flowhi4:4,
+				 tc:8,
+				 version:4;
+		} ipv6;
+		struct {
+			uint16_t flowlo16;
+			uint16_t flowhi4:4,
+				 tc:8,
+				 version:4;
+		} ipv6_flow;
+		struct {
+			uint16_t fin:1,
+				 syn:1,
+				 rst:1,
+				 psh:1,
+				 ack:1,
+				 urg:1,
+				 ece:1,
+				 cwr:1,
+				 res1:4,
+				 doff:4;
+			uint16_t rsvd;
+		} tcp;
+	} u;
+
+	uint16_t rsvd;
+
+	uint8_t type;
+
+#define PROTO_XTR_MAGIC_ID	0xCE
+	uint8_t magic;
+};
+
+#ifdef RTE_LIBRTE_ICE_PROTO_XTR
+
+static inline void
+init_proto_xtr_flds(struct rte_mbuf *mb)
+{
+	mb->udata64 = 0;
+}
+
+static inline struct proto_xtr_flds *
+get_proto_xtr_flds(struct rte_mbuf *mb)
+{
+	RTE_BUILD_BUG_ON(sizeof(struct proto_xtr_flds) > sizeof(mb->udata64));
+
+	return (struct proto_xtr_flds *)&mb->udata64;
+}
+
+static inline void
+dump_proto_xtr_flds(struct rte_mbuf *mb)
+{
+	struct proto_xtr_flds *xtr = get_proto_xtr_flds(mb);
+
+	if (xtr->magic != PROTO_XTR_MAGIC_ID || xtr->type == PROTO_XTR_NONE)
+		return;
+
+	printf(" - Protocol Extraction:[0x%04x:0x%04x],",
+	       xtr->u.raw.data0, xtr->u.raw.data1);
+
+	if (xtr->type == PROTO_XTR_VLAN)
+		printf("vlan,stag=%u:%u:%u,ctag=%u:%u:%u ",
+		       xtr->u.vlan.stag_pcp,
+		       xtr->u.vlan.stag_dei,
+		       xtr->u.vlan.stag_vid,
+		       xtr->u.vlan.ctag_pcp,
+		       xtr->u.vlan.ctag_dei,
+		       xtr->u.vlan.ctag_vid);
+	else if (xtr->type == PROTO_XTR_IPV4)
+		printf("ipv4,ver=%u,hdrlen=%u,tos=%u,ttl=%u,proto=%u ",
+		       xtr->u.ipv4.version,
+		       xtr->u.ipv4.ihl,
+		       xtr->u.ipv4.tos,
+		       xtr->u.ipv4.ttl,
+		       xtr->u.ipv4.protocol);
+	else if (xtr->type == PROTO_XTR_IPV6)
+		printf("ipv6,ver=%u,tc=%u,flow_hi4=0x%x,nexthdr=%u,hoplimit=%u ",
+		       xtr->u.ipv6.version,
+		       xtr->u.ipv6.tc,
+		       xtr->u.ipv6.flowhi4,
+		       xtr->u.ipv6.nexthdr,
+		       xtr->u.ipv6.hoplimit);
+	else if (xtr->type == PROTO_XTR_IPV6_FLOW)
+		printf("ipv6_flow,ver=%u,tc=%u,flow=0x%x%04x ",
+		       xtr->u.ipv6_flow.version,
+		       xtr->u.ipv6_flow.tc,
+		       xtr->u.ipv6_flow.flowhi4,
+		       xtr->u.ipv6_flow.flowlo16);
+	else if (xtr->type == PROTO_XTR_TCP)
+		printf("tcp,doff=%u,flags=%s%s%s%s%s%s%s%s ",
+		       xtr->u.tcp.doff,
+		       xtr->u.tcp.cwr ? "C" : "",
+		       xtr->u.tcp.ece ? "E" : "",
+		       xtr->u.tcp.urg ? "U" : "",
+		       xtr->u.tcp.ack ? "A" : "",
+		       xtr->u.tcp.psh ? "P" : "",
+		       xtr->u.tcp.rst ? "R" : "",
+		       xtr->u.tcp.syn ? "S" : "",
+		       xtr->u.tcp.fin ? "F" : "");
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_PMD_ICE_H_ */
