diff --git a/doc/guides/rel_notes/release_16_04.rst b/doc/guides/rel_notes/release_16_04.rst
index 73494f9..c3ee8b1 100644
--- a/doc/guides/rel_notes/release_16_04.rst
+++ b/doc/guides/rel_notes/release_16_04.rst
@@ -49,6 +49,8 @@ This section should contain new features added in this release. Sample format:
 
   * Free multiple mbufs at a time to reduce freeing mbuf cycles.
 
+* **Added fm10k FTAG based forwarding support.**
+
 * **Added new new X550EM_a devices.**
 
   Added new X550EM_a devices and their mac types, X550EM_a and X550EM_a_vf.
diff --git a/drivers/net/fm10k/fm10k.h b/drivers/net/fm10k/fm10k.h
index 770d6ba..05aa1a2 100644
--- a/drivers/net/fm10k/fm10k.h
+++ b/drivers/net/fm10k/fm10k.h
@@ -204,6 +204,7 @@ struct fm10k_rx_queue {
 	uint8_t port_id;
 	uint8_t drop_en;
 	uint8_t rx_deferred_start; /* don't start this queue in dev start. */
+	uint16_t rx_ftag_en; /* indicates FTAG RX supported */
 };
 
 /*
@@ -240,6 +241,7 @@ struct fm10k_tx_queue {
 	uint8_t port_id;
 	uint8_t tx_deferred_start; /** don't start this queue in dev start. */
 	uint16_t queue_id;
+	uint16_t tx_ftag_en; /* indicates FTAG TX supported */
 };
 
 struct fm10k_txq_ops {
diff --git a/drivers/net/fm10k/fm10k_ethdev.c b/drivers/net/fm10k/fm10k_ethdev.c
index 3c1e1d6..ad6cad9 100644
--- a/drivers/net/fm10k/fm10k_ethdev.c
+++ b/drivers/net/fm10k/fm10k_ethdev.c
@@ -37,6 +37,7 @@
 #include <rte_string_fns.h>
 #include <rte_dev.h>
 #include <rte_spinlock.h>
+#include <rte_kvargs.h>
 
 #include "fm10k.h"
 #include "base/fm10k_api.h"
@@ -79,6 +80,7 @@ static void fm10k_tx_queue_release(void *queue);
 static void fm10k_rx_queue_release(void *queue);
 static void fm10k_set_rx_function(struct rte_eth_dev *dev);
 static void fm10k_set_tx_function(struct rte_eth_dev *dev);
+static int fm10k_check_ftag(struct rte_devargs *devargs);
 
 struct fm10k_xstats_name_off {
 	char name[RTE_ETH_XSTATS_NAME_SIZE];
@@ -668,6 +670,19 @@ fm10k_dev_tx_init(struct rte_eth_dev *dev)
 			PMD_INIT_LOG(ERR, "failed to disable queue %d", i);
 			return -1;
 		}
+		/* Enable use of FTAG bit in TX descriptor, PFVTCTL
+		 * register is read-only for VF.
+		 */
+		if (fm10k_check_ftag(dev->pci_dev->devargs)) {
+			if (hw->mac.type == fm10k_mac_pf) {
+				FM10K_WRITE_REG(hw, FM10K_PFVTCTL(i),
+						FM10K_PFVTCTL_FTAG_DESC_ENABLE);
+				PMD_INIT_LOG(DEBUG, "FTAG mode is enabled");
+			} else {
+				PMD_INIT_LOG(ERR, "VF FTAG is not supported.");
+				return -ENOTSUP;
+			}
+		}
 
 		/* set location and size for descriptor ring */
 		FM10K_WRITE_REG(hw, FM10K_TDBAL(i),
@@ -2597,15 +2612,57 @@ static const struct eth_dev_ops fm10k_eth_dev_ops = {
 	.rss_hash_conf_get	= fm10k_rss_hash_conf_get,
 };
 
+static int ftag_check_handler(__rte_unused const char *key,
+		const char *value, __rte_unused void *opaque)
+{
+	if (strcmp(value, "1"))
+		return -1;
+
+	return 0;
+}
+
+static int
+fm10k_check_ftag(struct rte_devargs *devargs)
+{
+	struct rte_kvargs *kvlist;
+	const char *ftag_key = "enable_ftag";
+
+	if (devargs == NULL)
+		return 0;
+
+	kvlist = rte_kvargs_parse(devargs->args, NULL);
+	if (kvlist == NULL)
+		return 0;
+
+	if (!rte_kvargs_count(kvlist, ftag_key)) {
+		rte_kvargs_free(kvlist);
+		return 0;
+	}
+	/* FTAG is enabled when there's key-value pair: enable_ftag=1 */
+	if (rte_kvargs_process(kvlist, ftag_key,
+				ftag_check_handler, NULL) < 0) {
+		rte_kvargs_free(kvlist);
+		return 0;
+	}
+	rte_kvargs_free(kvlist);
+
+	return 1;
+}
+
 static void __attribute__((cold))
 fm10k_set_tx_function(struct rte_eth_dev *dev)
 {
 	struct fm10k_tx_queue *txq;
 	int i;
 	int use_sse = 1;
+	uint16_t tx_ftag_en = 0;
+
+	if (fm10k_check_ftag(dev->pci_dev->devargs))
+		tx_ftag_en = 1;
 
 	for (i = 0; i < dev->data->nb_tx_queues; i++) {
 		txq = dev->data->tx_queues[i];
+		txq->tx_ftag_en = tx_ftag_en;
 		/* Check if Vector Tx is satisfied */
 		if (fm10k_tx_vec_condition_check(txq)) {
 			use_sse = 0;
@@ -2631,11 +2688,16 @@ fm10k_set_rx_function(struct rte_eth_dev *dev)
 {
 	struct fm10k_dev_info *dev_info = FM10K_DEV_PRIVATE_TO_INFO(dev);
 	uint16_t i, rx_using_sse;
+	uint16_t rx_ftag_en = 0;
+
+	if (fm10k_check_ftag(dev->pci_dev->devargs))
+		rx_ftag_en = 1;
 
 	/* In order to allow Vector Rx there are a few configuration
 	 * conditions to be met.
 	 */
-	if (!fm10k_rx_vec_condition_check(dev) && dev_info->rx_vec_allowed) {
+	if (!fm10k_rx_vec_condition_check(dev) &&
+			dev_info->rx_vec_allowed && !rx_ftag_en) {
 		if (dev->data->scattered_rx)
 			dev->rx_pkt_burst = fm10k_recv_scattered_pkts_vec;
 		else
@@ -2658,6 +2720,7 @@ fm10k_set_rx_function(struct rte_eth_dev *dev)
 		struct fm10k_rx_queue *rxq = dev->data->rx_queues[i];
 
 		rxq->rx_using_sse = rx_using_sse;
+		rxq->rx_ftag_en = rx_ftag_en;
 	}
 }
 
diff --git a/drivers/net/fm10k/fm10k_rxtx.c b/drivers/net/fm10k/fm10k_rxtx.c
index 9f832c1..66db5b6 100644
--- a/drivers/net/fm10k/fm10k_rxtx.c
+++ b/drivers/net/fm10k/fm10k_rxtx.c
@@ -152,6 +152,12 @@ fm10k_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
 		 */
 		mbuf->ol_flags |= PKT_RX_VLAN_PKT;
 		mbuf->vlan_tci = desc.w.vlan;
+		/**
+		 * mbuf->vlan_tci_outer is an idle field in fm10k driver,
+		 * so it can be selected to store sglort value.
+		 */
+		if (q->rx_ftag_en)
+			mbuf->vlan_tci_outer = rte_le_to_cpu_16(desc.w.sglort);
 
 		rx_pkts[count] = mbuf;
 		if (++next_dd == q->nb_desc) {
@@ -307,6 +313,13 @@ fm10k_recv_scattered_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
 		 */
 		first_seg->ol_flags |= PKT_RX_VLAN_PKT;
 		first_seg->vlan_tci = desc.w.vlan;
+		/**
+		 * mbuf->vlan_tci_outer is an idle field in fm10k driver,
+		 * so it can be selected to store sglort value.
+		 */
+		if (q->rx_ftag_en)
+			first_seg->vlan_tci_outer =
+				rte_le_to_cpu_16(desc.w.sglort);
 
 		/* Prefetch data of first segment, if configured to do so. */
 		rte_packet_prefetch((char *)first_seg->buf_addr +
@@ -498,6 +511,8 @@ static inline void tx_xmit_pkt(struct fm10k_tx_queue *q, struct rte_mbuf *mb)
 	q->nb_free -= mb->nb_segs;
 
 	q->hw_ring[q->next_free].flags = 0;
+	if (q->tx_ftag_en)
+		q->hw_ring[q->next_free].flags |= FM10K_TXD_FLAG_FTAG;
 	/* set checksum flags on first descriptor of packet. SCTP checksum
 	 * offload is not supported, but we do not explicitly check for this
 	 * case in favor of greatly simplified processing. */
diff --git a/drivers/net/fm10k/fm10k_rxtx_vec.c b/drivers/net/fm10k/fm10k_rxtx_vec.c
index 9f178db..1c78725 100644
--- a/drivers/net/fm10k/fm10k_rxtx_vec.c
+++ b/drivers/net/fm10k/fm10k_rxtx_vec.c
@@ -688,6 +688,9 @@ fm10k_tx_vec_condition_check(struct fm10k_tx_queue *txq)
 	if ((txq->txq_flags & FM10K_SIMPLE_TX_FLAG) != FM10K_SIMPLE_TX_FLAG)
 		return -1;
 
+	if (txq->tx_ftag_en)
+		return -1;
+
 	return 0;
 }
 
