diff --git a/drivers/net/ixgbe/ixgbe_ipsec.c b/drivers/net/ixgbe/ixgbe_ipsec.c
index 48f5082..94daf92 100644
--- a/drivers/net/ixgbe/ixgbe_ipsec.c
+++ b/drivers/net/ixgbe/ixgbe_ipsec.c
@@ -9,6 +9,7 @@
 #include <rte_security_driver.h>
 #include <rte_cryptodev.h>
 #include <rte_flow.h>
+#include <rte_hash_crc.h>
 
 #include "base/ixgbe_type.h"
 #include "base/ixgbe_api.h"
@@ -94,6 +95,7 @@ ixgbe_crypto_add_sa(struct ixgbe_crypto_session *ic_session)
 			dev->data->dev_private);
 	uint32_t reg_val;
 	int sa_index = -1;
+	struct ixgbe_crypto_sess_htable_key tbl_key = {0};
 
 	if (ic_session->op == IXGBE_OP_AUTHENTICATED_DECRYPTION) {
 		int i, ip_index = -1;
@@ -158,9 +160,14 @@ ixgbe_crypto_add_sa(struct ixgbe_crypto_session *ic_session)
 		if (ic_session->dst_ip.type == IPv6) {
 			priv->rx_sa_tbl[sa_index].mode |= IPSRXMOD_IPV6;
 			priv->rx_ip_tbl[ip_index].ip.type = IPv6;
-		} else if (ic_session->dst_ip.type == IPv4)
+			memcpy(&tbl_key.ip.ipv6, &ic_session->dst_ip.ipv6,
+					sizeof(ic_session->dst_ip.ipv6));
+		} else if (ic_session->dst_ip.type == IPv4) {
 			priv->rx_ip_tbl[ip_index].ip.type = IPv4;
+			tbl_key.ip.ipv4 = ic_session->dst_ip.ipv4;
+		}
 
+		tbl_key.spi = priv->rx_sa_tbl[sa_index].spi;
 		priv->rx_sa_tbl[sa_index].used = 1;
 
 		/* write IP table entry*/
@@ -238,6 +245,7 @@ ixgbe_crypto_add_sa(struct ixgbe_crypto_session *ic_session)
 
 		priv->tx_sa_tbl[sa_index].spi =
 			rte_cpu_to_be_32(ic_session->spi);
+		tbl_key.spi = priv->tx_sa_tbl[sa_index].spi;
 		priv->tx_sa_tbl[i].used = 1;
 		ic_session->sa_index = sa_index;
 
@@ -264,6 +272,7 @@ ixgbe_crypto_add_sa(struct ixgbe_crypto_session *ic_session)
 		free(key);
 	}
 
+	rte_hash_add_key_data(priv->session_tbl, &tbl_key, ic_session);
 	return 0;
 }
 
@@ -276,6 +285,7 @@ ixgbe_crypto_remove_sa(struct rte_eth_dev *dev,
 			IXGBE_DEV_PRIVATE_TO_IPSEC(dev->data->dev_private);
 	uint32_t reg_val;
 	int sa_index = -1;
+	struct ixgbe_crypto_sess_htable_key tbl_key = {0};
 
 	if (ic_session->op == IXGBE_OP_AUTHENTICATED_DECRYPTION) {
 		int i, ip_index = -1;
@@ -324,6 +334,13 @@ ixgbe_crypto_remove_sa(struct rte_eth_dev *dev,
 		IXGBE_WRITE_REG(hw, IXGBE_IPSRXMOD, 0);
 		IXGBE_WAIT_RWRITE;
 		priv->rx_sa_tbl[sa_index].used = 0;
+		if (priv->rx_sa_tbl[sa_index].mode & IPSRXMOD_IPV6) {
+			memcpy(&tbl_key.ip.ipv6, &ic_session->dst_ip.ipv6,
+					sizeof(ic_session->dst_ip.ipv6));
+		} else {
+			tbl_key.ip.ipv4 = ic_session->dst_ip.ipv4;
+		}
+		tbl_key.spi = priv->rx_sa_tbl[sa_index].spi;
 
 		/* If last used then clear the IP table entry*/
 		priv->rx_ip_tbl[ip_index].ref_count--;
@@ -361,8 +378,10 @@ ixgbe_crypto_remove_sa(struct rte_eth_dev *dev,
 		IXGBE_WAIT_TWRITE;
 
 		priv->tx_sa_tbl[sa_index].used = 0;
+		tbl_key.spi = priv->tx_sa_tbl[sa_index].spi;
 	}
 
+	rte_hash_del_key(priv->session_tbl, &tbl_key);
 	return 0;
 }
 
@@ -376,6 +395,8 @@ ixgbe_crypto_create_session(void *device,
 	struct ixgbe_crypto_session *ic_session = NULL;
 	struct rte_crypto_aead_xform *aead_xform;
 	struct rte_eth_conf *dev_conf = &eth_dev->data->dev_conf;
+	struct ixgbe_ipsec *priv =
+			IXGBE_DEV_PRIVATE_TO_IPSEC(eth_dev->data->dev_private);
 
 	if (rte_mempool_get(mempool, (void **)&ic_session)) {
 		PMD_DRV_LOG(ERR, "Cannot get object from ic_session mempool");
@@ -426,6 +447,40 @@ ixgbe_crypto_create_session(void *device,
 		}
 	}
 
+	if (conf->ipsec.options.stats) {
+		ic_session->stats_enabled = 1;
+		priv->per_session_stats_active++;
+	}
+
+	return 0;
+}
+
+static int
+ixgbe_crypto_stats_get(__rte_unused void *device,
+		struct rte_security_session *sess,
+		struct rte_security_stats *stats)
+{
+	struct rte_eth_dev *eth_dev = device;
+	struct ixgbe_ipsec *priv =
+			IXGBE_DEV_PRIVATE_TO_IPSEC(eth_dev->data->dev_private);
+	volatile struct rte_security_ipsec_stats *ixgbe_stats;
+
+	if (sess) {
+		struct ixgbe_crypto_session *ic_session =
+			(struct ixgbe_crypto_session *)
+			get_sec_session_private_data(sess);
+		ixgbe_stats = &ic_session->stats;
+	} else {
+		ixgbe_stats = &priv->stats;
+	}
+
+	stats->ipsec.ipackets = ixgbe_stats->ipackets;
+	stats->ipsec.opackets = ixgbe_stats->opackets;
+	stats->ipsec.ibytes = ixgbe_stats->ibytes;
+	stats->ipsec.obytes = ixgbe_stats->obytes;
+	stats->ipsec.ierrors = ixgbe_stats->ierrors;
+	stats->ipsec.oerrors = ixgbe_stats->oerrors;
+
 	return 0;
 }
 
@@ -444,6 +499,8 @@ ixgbe_crypto_remove_session(void *device,
 		(struct ixgbe_crypto_session *)
 		get_sec_session_private_data(session);
 	struct rte_mempool *mempool = rte_mempool_from_obj(ic_session);
+	struct ixgbe_ipsec *priv =
+			IXGBE_DEV_PRIVATE_TO_IPSEC(eth_dev->data->dev_private);
 
 	if (eth_dev != ic_session->dev) {
 		PMD_DRV_LOG(ERR, "Session not bound to this device\n");
@@ -455,11 +512,44 @@ ixgbe_crypto_remove_session(void *device,
 		return -EFAULT;
 	}
 
+	if (ic_session->stats_enabled && priv->per_session_stats_active > 0)
+		priv->per_session_stats_active--;
+
 	rte_mempool_put(mempool, (void *)ic_session);
 
 	return 0;
 }
 
+static int
+ixgbe_crypto_update_session(void *device,
+		struct rte_security_session *session,
+		struct rte_security_session_conf *conf)
+{
+	struct rte_eth_dev *eth_dev = device;
+	struct ixgbe_crypto_session *ic_session =
+		(struct ixgbe_crypto_session *)
+		get_sec_session_private_data(session);
+	struct ixgbe_ipsec *priv =
+			IXGBE_DEV_PRIVATE_TO_IPSEC(eth_dev->data->dev_private);
+
+	if (eth_dev != ic_session->dev) {
+		PMD_DRV_LOG(ERR, "Session not bound to this device\n");
+		return -ENODEV;
+	}
+
+	/* Enable/disable per session stats */
+	if (ic_session->stats_enabled && !conf->ipsec.options.stats) {
+		ic_session->stats_enabled = 0;
+		if (priv->per_session_stats_active > 0)
+			priv->per_session_stats_active--;
+	} else if (!ic_session->stats_enabled && conf->ipsec.options.stats) {
+		ic_session->stats_enabled = 1;
+		priv->per_session_stats_active++;
+	}
+
+	return 0;
+}
+
 static inline uint8_t
 ixgbe_crypto_compute_pad_len(struct rte_mbuf *m)
 {
@@ -624,6 +714,8 @@ int
 ixgbe_crypto_enable_ipsec(struct rte_eth_dev *dev)
 {
 	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_ipsec *priv =
+			IXGBE_DEV_PRIVATE_TO_IPSEC(dev->data->dev_private);
 	uint32_t reg;
 	uint64_t rx_offloads;
 	uint64_t tx_offloads;
@@ -676,6 +768,23 @@ ixgbe_crypto_enable_ipsec(struct rte_eth_dev *dev)
 
 	ixgbe_crypto_clear_ipsec_tables(dev);
 
+	if (priv->session_tbl == NULL) {
+		char session_tbl_hash_name[RTE_HASH_NAMESIZE];
+		struct rte_hash_parameters params = {
+			.name = session_tbl_hash_name,
+			.entries = IPSEC_MAX_SA_COUNT * 2,
+			.key_len = sizeof(struct ixgbe_crypto_sess_htable_key),
+			.hash_func = rte_hash_crc,
+			.socket_id = rte_socket_id(),
+		};
+		snprintf(session_tbl_hash_name, RTE_HASH_NAMESIZE,
+			 "session_tbl_hash_%s", dev->device->name);
+
+		priv->session_tbl = rte_hash_create(&params);
+	} else {
+		rte_hash_reset(priv->session_tbl);
+	}
+
 	return 0;
 }
 
@@ -709,11 +818,140 @@ ixgbe_crypto_add_ingress_sa_from_flow(const void *sess,
 	return 0;
 }
 
+void
+ixgbe_crypto_update_rx_stats(struct ixgbe_ipsec *ixgbe_ipsec,
+			     struct rte_mbuf **mbufs,
+			     uint16_t count)
+{
+	uint16_t i;
+	uint32_t ipackets = 0, ibytes = 0, ierrors = 0;
+
+	if (ixgbe_ipsec->per_session_stats_active) {
+		struct ip *ip;
+		struct rte_esp_hdr *esp;
+		struct ixgbe_crypto_session *sess;
+		struct ixgbe_crypto_sess_htable_key tbl_key = {0};
+
+		for (i = 0; i < count; i++) {
+			if ((mbufs[i]->ol_flags & PKT_RX_SEC_OFFLOAD) == 0)
+				continue;
+			ip = rte_pktmbuf_mtod_offset(mbufs[i],
+					struct ip *,
+					sizeof(struct rte_ether_hdr));
+			if (ip->ip_v == IPVERSION) {
+				tbl_key.ip.ipv4 = ip->ip_dst.s_addr;
+				esp = rte_pktmbuf_mtod_offset(mbufs[i],
+						struct rte_esp_hdr *,
+						sizeof(struct rte_ether_hdr) +
+						ip->ip_hl * 4);
+			} else {
+				struct ip6_hdr *ip6 = (struct ip6_hdr *)ip;
+				memcpy(&tbl_key.ip.ipv6, &ip6->ip6_dst, 16);
+				esp = rte_pktmbuf_mtod_offset(mbufs[i],
+						struct rte_esp_hdr *,
+						sizeof(struct rte_ether_hdr) +
+						sizeof(struct ip6_hdr));
+			}
+			tbl_key.spi = esp->spi;
+			if (rte_hash_lookup_data(ixgbe_ipsec->session_tbl,
+					&tbl_key, (void **)&sess) < 0)
+				sess = NULL;
+			if (mbufs[i]->ol_flags & PKT_RX_SEC_OFFLOAD_FAILED) {
+				if (sess && sess->stats_enabled)
+					sess->stats.ierrors++;
+				ierrors++;
+			} else {
+				if (sess && sess->stats_enabled) {
+					sess->stats.ipackets++;
+					sess->stats.ibytes +=
+						rte_pktmbuf_pkt_len(mbufs[i]);
+				}
+				ipackets++;
+				ibytes += rte_pktmbuf_pkt_len(mbufs[i]);
+			}
+		}
+	} else { /* Global stats only */
+		for (i = 0; i < count; i++) {
+			if (mbufs[i]->ol_flags & PKT_RX_SEC_OFFLOAD) {
+				if (mbufs[i]->ol_flags &
+						PKT_RX_SEC_OFFLOAD_FAILED) {
+					ierrors++;
+				} else {
+					ipackets++;
+					ibytes += rte_pktmbuf_pkt_len(mbufs[i]);
+				}
+			}
+		}
+	}
+
+	/* Update global stats */
+	ixgbe_ipsec->stats.ipackets += ipackets;
+	ixgbe_ipsec->stats.ibytes += ibytes;
+	ixgbe_ipsec->stats.ierrors += ierrors;
+}
+
+void
+ixgbe_crypto_update_tx_stats(struct ixgbe_ipsec *ixgbe_ipsec,
+			     struct rte_mbuf **mbufs,
+			     uint16_t count)
+{
+	uint16_t i;
+	uint32_t opackets = 0, obytes = 0;
+
+	if (ixgbe_ipsec->per_session_stats_active) {
+		struct ip *ip;
+		struct rte_esp_hdr *esp;
+		struct ixgbe_crypto_session *sess;
+		struct ixgbe_crypto_sess_htable_key tbl_key = {0};
+
+		for (i = 0; i < count; i++) {
+			if ((mbufs[i]->ol_flags & PKT_TX_SEC_OFFLOAD) == 0)
+				continue;
+			ip = rte_pktmbuf_mtod_offset(mbufs[i],
+					struct ip *,
+					sizeof(struct rte_ether_hdr));
+			if (ip->ip_v == IPVERSION) {
+				esp = rte_pktmbuf_mtod_offset(mbufs[i],
+						struct rte_esp_hdr *,
+						sizeof(struct rte_ether_hdr) +
+						ip->ip_hl * 4);
+			} else {
+				esp = rte_pktmbuf_mtod_offset(mbufs[i],
+						struct rte_esp_hdr *,
+						sizeof(struct rte_ether_hdr) +
+						sizeof(struct ip6_hdr));
+			}
+			tbl_key.spi = esp->spi;
+			if (rte_hash_lookup_data(ixgbe_ipsec->session_tbl,
+					&tbl_key, (void **)&sess) < 0)
+				sess = NULL;
+			if (sess && sess->stats_enabled) {
+				sess->stats.opackets++;
+				sess->stats.obytes +=
+					rte_pktmbuf_pkt_len(mbufs[i]);
+			}
+			opackets++;
+			obytes += rte_pktmbuf_pkt_len(mbufs[i]);
+		}
+	} else { /* Global stats only */
+		for (i = 0; i < count; i++) {
+			if (mbufs[i]->ol_flags & PKT_RX_SEC_OFFLOAD) {
+				opackets++;
+				obytes += rte_pktmbuf_pkt_len(mbufs[i]);
+			}
+		}
+	}
+
+	/* Update global stats */
+	ixgbe_ipsec->stats.opackets += opackets;
+	ixgbe_ipsec->stats.obytes += obytes;
+}
+
 static struct rte_security_ops ixgbe_security_ops = {
 	.session_create = ixgbe_crypto_create_session,
-	.session_update = NULL,
+	.session_update = ixgbe_crypto_update_session,
 	.session_get_size = ixgbe_crypto_session_get_size,
-	.session_stats_get = NULL,
+	.session_stats_get = ixgbe_crypto_stats_get,
 	.session_destroy = ixgbe_crypto_remove_session,
 	.set_pkt_metadata = ixgbe_crypto_update_mb,
 	.capabilities_get = ixgbe_crypto_capabilities_get
diff --git a/drivers/net/ixgbe/ixgbe_ipsec.h b/drivers/net/ixgbe/ixgbe_ipsec.h
index e218c0a..a4ff8b6 100644
--- a/drivers/net/ixgbe/ixgbe_ipsec.h
+++ b/drivers/net/ixgbe/ixgbe_ipsec.h
@@ -70,6 +70,8 @@ struct ixgbe_crypto_session {
 	struct ipaddr src_ip;
 	struct ipaddr dst_ip;
 	struct rte_eth_dev *dev;
+	volatile struct rte_security_ipsec_stats stats;
+	uint8_t stats_enabled;
 } __rte_cache_aligned;
 
 struct ixgbe_crypto_rx_ip_table {
@@ -100,10 +102,18 @@ union ixgbe_crypto_tx_desc_md {
 	};
 };
 
+struct ixgbe_crypto_sess_htable_key {
+	uint32_t spi;
+	struct ipaddr ip;
+};
+
 struct ixgbe_ipsec {
 	struct ixgbe_crypto_rx_ip_table rx_ip_tbl[IPSEC_MAX_RX_IP_COUNT];
 	struct ixgbe_crypto_rx_sa_table rx_sa_tbl[IPSEC_MAX_SA_COUNT];
 	struct ixgbe_crypto_tx_sa_table tx_sa_tbl[IPSEC_MAX_SA_COUNT];
+	volatile struct rte_security_ipsec_stats stats;
+	volatile uint16_t per_session_stats_active;
+	struct rte_hash *session_tbl;
 };
 
 
@@ -112,7 +122,12 @@ int ixgbe_crypto_enable_ipsec(struct rte_eth_dev *dev);
 int ixgbe_crypto_add_ingress_sa_from_flow(const void *sess,
 					  const void *ip_spec,
 					  uint8_t is_ipv6);
-
+void ixgbe_crypto_update_rx_stats(struct ixgbe_ipsec *ixgbe_ipsec,
+				  struct rte_mbuf **mbufs,
+				  uint16_t count);
+void ixgbe_crypto_update_tx_stats(struct ixgbe_ipsec *ixgbe_ipsec,
+				  struct rte_mbuf **mbufs,
+				  uint16_t count);
 
 
 #endif /*IXGBE_IPSEC_H_*/
diff --git a/drivers/net/ixgbe/ixgbe_rxtx.c b/drivers/net/ixgbe/ixgbe_rxtx.c
index edcfa60..cde012f 100644
--- a/drivers/net/ixgbe/ixgbe_rxtx.c
+++ b/drivers/net/ixgbe/ixgbe_rxtx.c
@@ -956,6 +956,12 @@ ixgbe_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
 	IXGBE_PCI_REG_WRITE_RELAXED(txq->tdt_reg_addr, tx_id);
 	txq->tx_tail = tx_id;
 
+#ifdef RTE_LIBRTE_SECURITY
+		if (unlikely(txq->using_ipsec))
+			ixgbe_crypto_update_tx_stats(txq->ixgbe_ipsec,
+					&tx_pkts[-nb_tx], nb_tx);
+#endif
+
 	return nb_tx;
 }
 
@@ -1643,6 +1649,12 @@ ixgbe_rx_fill_from_stage(struct ixgbe_rx_queue *rxq, struct rte_mbuf **rx_pkts,
 	rxq->rx_nb_avail = (uint16_t)(rxq->rx_nb_avail - nb_pkts);
 	rxq->rx_next_avail = (uint16_t)(rxq->rx_next_avail + nb_pkts);
 
+#ifdef RTE_LIBRTE_SECURITY
+	if (unlikely(rxq->using_ipsec))
+		ixgbe_crypto_update_rx_stats(rxq->ixgbe_ipsec,
+				rx_pkts, nb_pkts);
+#endif
+
 	return nb_pkts;
 }
 
@@ -2618,6 +2630,9 @@ ixgbe_dev_tx_queue_setup(struct rte_eth_dev *dev,
 #ifdef RTE_LIBRTE_SECURITY
 	txq->using_ipsec = !!(dev->data->dev_conf.txmode.offloads &
 			DEV_TX_OFFLOAD_SECURITY);
+	if (txq->using_ipsec)
+		txq->ixgbe_ipsec =
+			IXGBE_DEV_PRIVATE_TO_IPSEC(dev->data->dev_private);
 #endif
 
 	/*
@@ -4730,6 +4745,9 @@ ixgbe_set_rx_function(struct rte_eth_dev *dev)
 #ifdef RTE_LIBRTE_SECURITY
 		rxq->using_ipsec = !!(dev->data->dev_conf.rxmode.offloads &
 				DEV_RX_OFFLOAD_SECURITY);
+		if (rxq->using_ipsec)
+			rxq->ixgbe_ipsec =
+			IXGBE_DEV_PRIVATE_TO_IPSEC(dev->data->dev_private);
 #endif
 	}
 }
diff --git a/drivers/net/ixgbe/ixgbe_rxtx.h b/drivers/net/ixgbe/ixgbe_rxtx.h
index 505d344..b5f130d 100644
--- a/drivers/net/ixgbe/ixgbe_rxtx.h
+++ b/drivers/net/ixgbe/ixgbe_rxtx.h
@@ -114,6 +114,8 @@ struct ixgbe_rx_queue {
 #ifdef RTE_LIBRTE_SECURITY
 	uint8_t            using_ipsec;
 	/**< indicates that IPsec RX feature is in use */
+	struct ixgbe_ipsec *ixgbe_ipsec;
+	/**< IXGBE IPsec internals */
 #endif
 #ifdef RTE_IXGBE_INC_VECTOR
 	uint16_t            rxrearm_nb;     /**< number of remaining to be re-armed */
@@ -231,6 +233,8 @@ struct ixgbe_tx_queue {
 #ifdef RTE_LIBRTE_SECURITY
 	uint8_t		    using_ipsec;
 	/**< indicates that IPsec TX feature is in use */
+	struct ixgbe_ipsec *ixgbe_ipsec;
+	/**< IXGBE IPsec internals */
 #endif
 };
 
diff --git a/drivers/net/ixgbe/ixgbe_rxtx_vec_sse.c b/drivers/net/ixgbe/ixgbe_rxtx_vec_sse.c
index 599ba30..b92d1a9 100644
--- a/drivers/net/ixgbe/ixgbe_rxtx_vec_sse.c
+++ b/drivers/net/ixgbe/ixgbe_rxtx_vec_sse.c
@@ -553,6 +553,12 @@ _recv_raw_pkts_vec(struct ixgbe_rx_queue *rxq, struct rte_mbuf **rx_pkts,
 	rxq->rx_tail = (uint16_t)(rxq->rx_tail & (rxq->nb_rx_desc - 1));
 	rxq->rxrearm_nb = (uint16_t)(rxq->rxrearm_nb + nb_pkts_recd);
 
+#ifdef RTE_LIBRTE_SECURITY
+		if (unlikely(use_ipsec))
+			ixgbe_crypto_update_rx_stats(rxq->ixgbe_ipsec,
+					rx_pkts, nb_pkts_recd);
+#endif
+
 	return nb_pkts_recd;
 }
 
