diff --git a/examples/ipsec-secgw/esp.c b/examples/ipsec-secgw/esp.c
index e77afa0..c6b48d4 100644
--- a/examples/ipsec-secgw/esp.c
+++ b/examples/ipsec-secgw/esp.c
@@ -60,8 +60,11 @@
 
 	RTE_ASSERT(m != NULL);
 	RTE_ASSERT(sa != NULL);
-	RTE_ASSERT(cop != NULL);
 
+	if (OFFLOADED_SA(sa))
+		return 0;
+
+	RTE_ASSERT(cop != NULL);
 	ip4 = rte_pktmbuf_mtod(m, struct ip *);
 	if (likely(ip4->ip_v == IPVERSION))
 		ip_hdr_len = ip4->ip_hl * 4;
@@ -159,12 +162,22 @@
 	uint8_t *nexthdr, *pad_len;
 	uint8_t *padding;
 	uint16_t i;
+	uint8_t decrypt_fail;
 
 	RTE_ASSERT(m != NULL);
 	RTE_ASSERT(sa != NULL);
-	RTE_ASSERT(cop != NULL);
 
-	if (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+	if (OFFLOADED_SA(sa)) {
+		if (m->ol_flags & PKT_RX_IPSEC_CRYPTO)
+			decrypt_fail = !!(m->ol_flags & PKT_RX_IPSEC_CRYPTO_FAILED);
+		else
+			decrypt_fail = 1;
+	} else {
+		RTE_ASSERT(cop != NULL);
+		decrypt_fail = (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS);
+	}
+
+	if (decrypt_fail) {
 		RTE_LOG(ERR, IPSEC_ESP, "failed crypto op\n");
 		return -1;
 	}
@@ -222,7 +235,6 @@
 
 	RTE_ASSERT(m != NULL);
 	RTE_ASSERT(sa != NULL);
-	RTE_ASSERT(cop != NULL);
 
 	ip_hdr_len = 0;
 
@@ -250,7 +262,6 @@
 	/* Padded payload length */
 	pad_payload_len = RTE_ALIGN_CEIL(rte_pktmbuf_pkt_len(m) -
 			ip_hdr_len + 2, sa->block_size);
-	pad_len = pad_payload_len + ip_hdr_len - rte_pktmbuf_pkt_len(m);
 
 	RTE_ASSERT(sa->flags == IP4_TUNNEL || sa->flags == IP6_TUNNEL ||
 			sa->flags == TRANSPORT);
@@ -272,12 +283,18 @@
 		return -EINVAL;
 	}
 
-	padding = (uint8_t *)rte_pktmbuf_append(m, pad_len + sa->digest_len);
-	if (unlikely(padding == NULL)) {
-		RTE_LOG(ERR, IPSEC_ESP, "not enough mbuf trailing space\n");
-		return -ENOSPC;
+	if (!OFFLOADED_SA(sa)) {
+		pad_len = pad_payload_len + ip_hdr_len - rte_pktmbuf_pkt_len(m);
+
+		padding = (uint8_t *)rte_pktmbuf_append(m, pad_len +
+							sa->digest_len);
+		if (unlikely(padding == NULL)) {
+			RTE_LOG(ERR, IPSEC_ESP,
+					"not enough mbuf trailing space\n");
+			return -ENOSPC;
+		}
+		rte_prefetch0(padding);
 	}
-	rte_prefetch0(padding);
 
 	switch (sa->flags) {
 	case IP4_TUNNEL:
@@ -311,20 +328,39 @@
 	esp->seq = rte_cpu_to_be_32((uint32_t)sa->seq);
 
 	uint64_t *iv = (uint64_t *)(esp + 1);
+	switch (sa->cipher_algo) {
+	case RTE_CRYPTO_CIPHER_NULL:
+	case RTE_CRYPTO_CIPHER_AES_CBC:
+		memset(iv, 0, sa->iv_len);
+		break;
+	case RTE_CRYPTO_CIPHER_AES_CTR:
+	case RTE_CRYPTO_CIPHER_AES_GCM:
+		*iv = sa->seq;
+		break;
+	default:
+		RTE_LOG(ERR, IPSEC_ESP, "unsupported cipher algorithm %u\n",
+				sa->cipher_algo);
+		return -EINVAL;
+	}
 
+	if (OFFLOADED_SA(sa)) {
+		m->inner_esp_next_proto = nlp;
+		m->ol_flags |= PKT_TX_IPSEC_CRYPTO_HW_TRAILER;
+		goto done;
+	}
+
+	RTE_ASSERT(cop != NULL);
 	sym_cop = get_sym_cop(cop);
 	sym_cop->m_src = m;
 	switch (sa->cipher_algo) {
 	case RTE_CRYPTO_CIPHER_NULL:
 	case RTE_CRYPTO_CIPHER_AES_CBC:
-		memset(iv, 0, sa->iv_len);
 		sym_cop->cipher.data.offset = ip_hdr_len +
 			sizeof(struct esp_hdr);
 		sym_cop->cipher.data.length = pad_payload_len + sa->iv_len;
 		break;
 	case RTE_CRYPTO_CIPHER_AES_CTR:
 	case RTE_CRYPTO_CIPHER_AES_GCM:
-		*iv = sa->seq;
 		sym_cop->cipher.data.offset = ip_hdr_len +
 			sizeof(struct esp_hdr) + sa->iv_len;
 		sym_cop->cipher.data.length = pad_payload_len;
@@ -380,18 +416,22 @@
 			rte_pktmbuf_pkt_len(m) - sa->digest_len);
 	sym_cop->auth.digest.length = sa->digest_len;
 
+done:
 	return 0;
 }
 
 int
 esp_outbound_post(struct rte_mbuf *m __rte_unused,
-		struct ipsec_sa *sa __rte_unused,
+		struct ipsec_sa *sa,
 		struct rte_crypto_op *cop)
 {
 	RTE_ASSERT(m != NULL);
 	RTE_ASSERT(sa != NULL);
-	RTE_ASSERT(cop != NULL);
 
+	if (OFFLOADED_SA(sa))
+		return 0;
+
+	RTE_ASSERT(cop != NULL);
 	if (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
 		RTE_LOG(ERR, IPSEC_ESP, "Failed crypto op\n");
 		return -1;
diff --git a/examples/ipsec-secgw/esp.h b/examples/ipsec-secgw/esp.h
index fa5cc8a..e93b2aa 100644
--- a/examples/ipsec-secgw/esp.h
+++ b/examples/ipsec-secgw/esp.h
@@ -33,18 +33,9 @@
 #ifndef __RTE_IPSEC_XFORM_ESP_H__
 #define __RTE_IPSEC_XFORM_ESP_H__
 
-struct mbuf;
+#include <rte_esp.h>
 
-/* RFC4303 */
-struct esp_hdr {
-	uint32_t spi;
-	uint32_t seq;
-	/* Payload */
-	/* Padding */
-	/* Pad Length */
-	/* Next Header */
-	/* Integrity Check Value - ICV */
-};
+struct mbuf;
 
 int
 esp_inbound(struct rte_mbuf *m, struct ipsec_sa *sa,
diff --git a/examples/ipsec-secgw/ipsec.c b/examples/ipsec-secgw/ipsec.c
index edca5f0..8728f16 100644
--- a/examples/ipsec-secgw/ipsec.c
+++ b/examples/ipsec-secgw/ipsec.c
@@ -40,12 +40,32 @@
 #include <rte_cryptodev.h>
 #include <rte_mbuf.h>
 #include <rte_hash.h>
+#include <rte_flow.h>
 
 #include "ipsec.h"
 #include "esp.h"
 
+
+static inline int
+create_session_inline(struct ipsec_ctx *ipsec_ctx __rte_unused,
+		      struct ipsec_sa *sa)
+{
+	struct rte_flow_error err;
+
+	sa->flow = rte_flow_create(sa->portid, &sa->attr, sa->pattern,
+			sa->action, &err);
+	if (sa->flow == NULL) {
+		RTE_LOG(ERR, IPSEC, "Failed to create ipsec flow message: %s\n",
+				err.message);
+		return -1;
+	}
+
+	return 0;
+}
+
 static inline int
-create_session(struct ipsec_ctx *ipsec_ctx __rte_unused, struct ipsec_sa *sa)
+create_session_cryptodev(struct ipsec_ctx *ipsec_ctx __rte_unused,
+			 struct ipsec_sa *sa)
 {
 	struct rte_cryptodev_info cdev_info;
 	unsigned long cdev_id_qp = 0;
@@ -91,6 +111,15 @@
 	return 0;
 }
 
+int
+create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
+{
+	if (sa->portid != -1)
+		return create_session_inline(ipsec_ctx, sa);
+	else
+		return create_session_cryptodev(ipsec_ctx, sa);
+}
+
 static inline void
 enqueue_cop(struct cdev_qp *cqp, struct rte_crypto_op *cop)
 {
@@ -117,7 +146,8 @@
 static inline void
 ipsec_enqueue(ipsec_xform_fn xform_func, struct ipsec_ctx *ipsec_ctx,
 		struct rte_mbuf *pkts[], struct ipsec_sa *sas[],
-		uint16_t nb_pkts)
+		uint16_t nb_pkts,
+		uint8_t inflight_pkt_types[], uint16_t *nb_offloaded)
 {
 	int32_t ret = 0, i;
 	struct ipsec_mbuf_metadata *priv;
@@ -142,37 +172,89 @@
 		rte_prefetch0(&priv->sym_cop);
 		priv->cop.sym = &priv->sym_cop;
 
-		if ((unlikely(sa->crypto_session == NULL)) &&
+		if ((unlikely(sa->crypto_session == NULL && sa->flow == NULL)) &&
 				create_session(ipsec_ctx, sa)) {
 			rte_pktmbuf_free(pkts[i]);
 			continue;
 		}
 
-		rte_crypto_op_attach_sym_session(&priv->cop,
-				sa->crypto_session);
-
 		ret = xform_func(pkts[i], sa, &priv->cop);
 		if (unlikely(ret)) {
 			rte_pktmbuf_free(pkts[i]);
 			continue;
 		}
 
-		RTE_ASSERT(sa->cdev_id_qp < ipsec_ctx->nb_qps);
-		enqueue_cop(&ipsec_ctx->tbl[sa->cdev_id_qp], &priv->cop);
+		if (!OFFLOADED_SA(sa)) {
+			inflight_pkt_types[i] = IPSEC_INFLIGHT_PKT_CRYPTODEV;
+			rte_crypto_op_attach_sym_session(&priv->cop,
+							 sa->crypto_session);
+
+			RTE_ASSERT(sa->cdev_id_qp < ipsec_ctx->nb_qps);
+			enqueue_cop(&ipsec_ctx->tbl[sa->cdev_id_qp],
+				    &priv->cop);
+		} else {
+			inflight_pkt_types[i] = IPSEC_INFLIGHT_PKT_OFFLOADED;
+			(*nb_offloaded)++;
+		}
+	}
+}
+
+static int32_t next_offloaded_pkt_idx(uint8_t inflight_pkt_types[],
+		int32_t curr_idx, uint16_t max_pkts)
+{
+	int32_t i = 0;
+
+	for (i = curr_idx; i < max_pkts; ++i) {
+		if (inflight_pkt_types[i] == IPSEC_INFLIGHT_PKT_OFFLOADED)
+			break;
 	}
+	return i;
+}
+
+static int32_t next_cryptodev_pkt_idx(uint8_t inflight_pkt_types[],
+		int32_t curr_idx, uint16_t max_pkts)
+{
+	int32_t i = 0;
+
+	for (i = curr_idx; i < max_pkts; ++i) {
+		if (inflight_pkt_types[i] == IPSEC_INFLIGHT_PKT_CRYPTODEV)
+			break;
+	}
+	return i;
 }
 
 static inline int
 ipsec_dequeue(ipsec_xform_fn xform_func, struct ipsec_ctx *ipsec_ctx,
-		struct rte_mbuf *pkts[], uint16_t max_pkts)
+		struct rte_mbuf *pkts[], uint16_t max_pkts,
+		uint8_t inflight_pkt_types[], uint16_t nb_offloaded)
 {
-	int32_t nb_pkts = 0, ret = 0, i, j, nb_cops;
+	int32_t nb_pkts = 0, ret = 0, i, j, idx, nb_cops;
 	struct ipsec_mbuf_metadata *priv;
 	struct rte_crypto_op *cops[max_pkts];
 	struct ipsec_sa *sa;
 	struct rte_mbuf *pkt;
 
-	for (i = 0; i < ipsec_ctx->nb_qps && nb_pkts < max_pkts; i++) {
+	/* all offloaded pkts are in place already */
+	for (i = 0, idx = 0; i < nb_offloaded; ++i) {
+		idx = next_offloaded_pkt_idx(inflight_pkt_types, idx, max_pkts);
+
+		pkt = pkts[idx];
+		rte_prefetch0(pkt);
+		priv = get_priv(pkt);
+		sa = priv->sa;
+
+		RTE_ASSERT(sa != NULL);
+
+		ret = xform_func(pkt, sa, NULL);
+		if (unlikely(ret)) {
+			rte_pktmbuf_free(pkt);
+			pkt = NULL;
+		}
+		pkts[idx++] = pkt;
+	}
+	nb_pkts += nb_offloaded;
+
+	for (i = 0, idx = 0; i < ipsec_ctx->nb_qps && nb_pkts < max_pkts; i++) {
 		struct cdev_qp *cqp;
 
 		cqp = &ipsec_ctx->tbl[ipsec_ctx->last_qp++];
@@ -197,13 +279,29 @@
 			RTE_ASSERT(sa != NULL);
 
 			ret = xform_func(pkt, sa, cops[j]);
-			if (unlikely(ret))
+			if (unlikely(ret)) {
 				rte_pktmbuf_free(pkt);
-			else
-				pkts[nb_pkts++] = pkt;
+				pkt = NULL;
+			}
+
+			idx = next_cryptodev_pkt_idx(inflight_pkt_types, idx,
+						     max_pkts);
+			pkts[idx++] = pkt;
+			nb_pkts++;
 		}
 	}
 
+	for (i = 0; i < max_pkts; ++i)
+		if (!pkts[i])
+			goto err;
+
+	goto done;
+err:
+	for (; i < max_pkts; ++i) {
+		rte_pktmbuf_free(pkts[i]);
+		--nb_pkts;
+	}
+done:
 	/* return packets */
 	return nb_pkts;
 }
@@ -213,12 +311,16 @@
 		uint16_t nb_pkts, uint16_t len)
 {
 	struct ipsec_sa *sas[nb_pkts];
+	uint8_t inflight_pkt_types[nb_pkts];
+	uint16_t nb_offloaded;
 
 	inbound_sa_lookup(ctx->sa_ctx, pkts, sas, nb_pkts);
 
-	ipsec_enqueue(esp_inbound, ctx, pkts, sas, nb_pkts);
+	ipsec_enqueue(esp_inbound, ctx, pkts, sas, nb_pkts,
+			inflight_pkt_types, &nb_offloaded);
 
-	return ipsec_dequeue(esp_inbound_post, ctx, pkts, len);
+	return ipsec_dequeue(esp_inbound_post, ctx, pkts, len,
+			inflight_pkt_types, nb_offloaded);
 }
 
 uint16_t
@@ -226,10 +328,14 @@
 		uint32_t sa_idx[], uint16_t nb_pkts, uint16_t len)
 {
 	struct ipsec_sa *sas[nb_pkts];
+	uint8_t inflight_pkt_types[nb_pkts];
+	uint16_t nb_offloaded;
 
 	outbound_sa_lookup(ctx->sa_ctx, sa_idx, sas, nb_pkts);
 
-	ipsec_enqueue(esp_outbound, ctx, pkts, sas, nb_pkts);
+	ipsec_enqueue(esp_outbound, ctx, pkts, sas, nb_pkts,
+			inflight_pkt_types, &nb_offloaded);
 
-	return ipsec_dequeue(esp_outbound_post, ctx, pkts, len);
+	return ipsec_dequeue(esp_outbound_post, ctx, pkts, len,
+			inflight_pkt_types, nb_offloaded);
 }
diff --git a/examples/ipsec-secgw/ipsec.h b/examples/ipsec-secgw/ipsec.h
index fe42661..fb2413a 100644
--- a/examples/ipsec-secgw/ipsec.h
+++ b/examples/ipsec-secgw/ipsec.h
@@ -38,6 +38,7 @@
 
 #include <rte_byteorder.h>
 #include <rte_crypto.h>
+#include <rte_flow.h>
 
 #define RTE_LOGTYPE_IPSEC       RTE_LOGTYPE_USER1
 #define RTE_LOGTYPE_IPSEC_ESP   RTE_LOGTYPE_USER2
@@ -97,6 +98,22 @@ struct ipsec_sa {
 	uint32_t cdev_id_qp;
 	uint64_t seq;
 	uint32_t salt;
+	int32_t portid;
+	struct rte_flow_attr attr;
+#define MAX_RTE_FLOW_PATTERN (4)
+	// ETH + IP + ESP + END
+	union {
+		struct rte_flow_item_ipv4 ipv4;
+		struct rte_flow_item_ipv6 ipv6;
+	} ip_spec;
+	struct rte_flow_item_esp esp_spec;
+	struct rte_flow_item pattern[MAX_RTE_FLOW_PATTERN];
+#define MAX_RTE_FLOW_ACTIONS (2)
+	// IPsec + END
+	struct rte_flow_action_crypto crypto_action;
+	struct rte_flow_action action[MAX_RTE_FLOW_ACTIONS];
+	struct rte_flow *flow;
+#define OFFLOADED_SA(sa)     ((sa)->flow)
 	struct rte_cryptodev_sym_session *crypto_session;
 	enum rte_crypto_cipher_algorithm cipher_algo;
 	enum rte_crypto_auth_algorithm auth_algo;
@@ -117,6 +134,9 @@ struct ipsec_sa {
 	struct rte_crypto_sym_xform *xforms;
 } __rte_cache_aligned;
 
+#define IPSEC_INFLIGHT_PKT_OFFLOADED (1 << 0)
+#define IPSEC_INFLIGHT_PKT_CRYPTODEV (1 << 1)
+
 struct ipsec_mbuf_metadata {
 	uint8_t buf[32];
 	struct ipsec_sa *sa;
@@ -132,6 +152,14 @@ struct cdev_qp {
 	struct rte_crypto_op *buf[MAX_PKT_BURST] __rte_aligned(sizeof(void *));
 };
 
+struct sa_ctx {
+	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
+	struct {
+		struct rte_crypto_sym_xform a;
+		struct rte_crypto_sym_xform b;
+	} xf[IPSEC_SA_MAX_ENTRIES];
+};
+
 struct ipsec_ctx {
 	struct rte_hash *cdev_map;
 	struct sp_ctx *sp4_ctx;
@@ -231,4 +259,6 @@ struct cnt_blk {
 void
 rt_init(struct socket_ctx *ctx, int32_t socket_id);
 
+int
+create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa);
 #endif /* __IPSEC_H__ */
diff --git a/examples/ipsec-secgw/sa.c b/examples/ipsec-secgw/sa.c
index 39624c4..bb17cd5 100644
--- a/examples/ipsec-secgw/sa.c
+++ b/examples/ipsec-secgw/sa.c
@@ -46,6 +46,8 @@
 #include <rte_errno.h>
 #include <rte_ip.h>
 #include <rte_random.h>
+#include <rte_flow.h>
+#include <rte_ethdev.h>
 
 #include "ipsec.h"
 #include "esp.h"
@@ -213,6 +215,7 @@ struct supported_auth_algo {
 	uint32_t src_p = 0;
 	uint32_t dst_p = 0;
 	uint32_t mode_p = 0;
+	uint32_t portid_p = 0;
 
 	if (strcmp(tokens[0], "in") == 0) {
 		ri = &nb_sa_in;
@@ -407,6 +410,8 @@ struct supported_auth_algo {
 					return;
 				rule->src.ip.ip4 = rte_bswap32(
 					(uint32_t)ip.s_addr);
+				rule->src.ip.ip4 = rte_cpu_to_be_32(
+						rule->src.ip.ip4);
 			} else if (rule->flags == IP6_TUNNEL) {
 				struct in6_addr ip;
 
@@ -450,6 +455,8 @@ struct supported_auth_algo {
 					return;
 				rule->dst.ip.ip4 = rte_bswap32(
 					(uint32_t)ip.s_addr);
+				rule->dst.ip.ip4 = rte_cpu_to_be_32(
+						rule->dst.ip.ip4);
 			} else if (rule->flags == IP6_TUNNEL) {
 				struct in6_addr ip;
 
@@ -471,6 +478,23 @@ struct supported_auth_algo {
 			continue;
 		}
 
+		if (strcmp(tokens[ti], "inline_port") == 0) {
+			APP_CHECK_PRESENCE(portid_p, tokens[ti], status);
+			if (status->status < 0)
+				return;
+
+			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
+			if (status->status < 0)
+				return;
+
+			rule->portid = atoi(tokens[ti]);
+
+			if (status->status < 0)
+				return;
+			portid_p = 1;
+			continue;
+		}
+
 		/* unrecognizeable input */
 		APP_CHECK(0, status, "unrecognized input \"%s\"",
 			tokens[ti]);
@@ -489,6 +513,10 @@ struct supported_auth_algo {
 	if (status->status < 0)
 		return;
 
+	/* This SA isn't offload */
+	if (!portid_p)
+		rule->portid = -1;
+
 	*ri = *ri + 1;
 }
 
@@ -547,14 +575,6 @@ struct supported_auth_algo {
 	printf("\n");
 }
 
-struct sa_ctx {
-	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
-	struct {
-		struct rte_crypto_sym_xform a;
-		struct rte_crypto_sym_xform b;
-	} xf[IPSEC_SA_MAX_ENTRIES];
-};
-
 static struct sa_ctx *
 sa_create(const char *name, int32_t socket_id)
 {
@@ -588,11 +608,13 @@ struct sa_ctx {
 		uint32_t nb_entries, uint32_t inbound)
 {
 	struct ipsec_sa *sa;
-	uint32_t i, idx;
+	uint32_t i, idx, j;
+	struct rte_eth_dev_info dev_info;
 
 	for (i = 0; i < nb_entries; i++) {
 		idx = SPI2IDX(entries[i].spi);
 		sa = &sa_ctx->sa[idx];
+		j = 0;
 		if (sa->spi != 0) {
 			printf("Index %u already in use by SPI %u\n",
 					idx, sa->spi);
@@ -601,12 +623,75 @@ struct sa_ctx {
 		*sa = entries[i];
 		sa->seq = 0;
 
+		if (sa->portid == -1)
+			goto not_offloaded;
+
+		rte_eth_dev_info_get(sa->portid, &dev_info);
+
+		if (inbound) {
+			if ((dev_info.rx_offload_capa &
+			     DEV_RX_OFFLOAD_IPSEC_CRYPTO) == 0) {
+				RTE_LOG(WARNING, PORT,
+					"hardware RX IPSec offload is not supported\n");
+				return -EINVAL;
+			}
+		} else { /* outbound */
+			if ((dev_info.tx_offload_capa &
+			     DEV_TX_OFFLOAD_IPSEC_CRYPTO_NEED_METADATA) == 0)
+				goto inline_with_metadata;
+			if ((dev_info.tx_offload_capa &
+			     DEV_TX_OFFLOAD_IPSEC_CRYPTO_HW_TRAILER) == 0) {
+				RTE_LOG(WARNING, PORT,
+					"hardware TX IPSec offload is not supported\n");
+				return -EINVAL;
+			}
+		}
+
+		sa->pattern[j++].type = RTE_FLOW_ITEM_TYPE_ETH;
 		switch (sa->flags) {
 		case IP4_TUNNEL:
-			sa->src.ip.ip4 = rte_cpu_to_be_32(sa->src.ip.ip4);
-			sa->dst.ip.ip4 = rte_cpu_to_be_32(sa->dst.ip.ip4);
+			sa->pattern[j].type = RTE_FLOW_ITEM_TYPE_IPV4;
+			sa->pattern[j].spec = &sa->ip_spec.ipv4;
+			sa->pattern[j++].mask = &rte_flow_item_ipv4_mask;
+			sa->ip_spec.ipv4.hdr.src_addr = sa->src.ip.ip4 =
+				rte_cpu_to_be_32(sa->src.ip.ip4);
+			sa->ip_spec.ipv4.hdr.dst_addr = sa->dst.ip.ip4 =
+				rte_cpu_to_be_32(sa->dst.ip.ip4);
+			break;
+		case IP6_TUNNEL:
+			sa->pattern[j].type = RTE_FLOW_ITEM_TYPE_IPV6;
+			sa->pattern[j].spec = &sa->ip_spec.ipv6;
+			sa->pattern[j++].mask = &rte_flow_item_ipv6_mask;
+			memcpy(sa->ip_spec.ipv6.hdr.src_addr,
+					sa->src.ip.ip6.ip6_b, 16);
+			memcpy(sa->ip_spec.ipv6.hdr.dst_addr,
+					sa->dst.ip.ip6.ip6_b, 16);
+			break;
+		case TRANSPORT:
+			rte_exit(EXIT_FAILURE,
+				 "Error creating offload SA with TRANSPORT, currently not supported\n");
 		}
-
+		sa->pattern[j].type = RTE_FLOW_ITEM_TYPE_ESP;
+		sa->pattern[j].spec = &sa->esp_spec;
+		sa->pattern[j++].mask = &rte_flow_item_esp_mask;
+		sa->esp_spec.hdr.spi = entries[i].spi;
+
+		sa->pattern[j++].type = RTE_FLOW_ITEM_TYPE_END;
+
+		memset(&sa->attr, 0, sizeof(struct rte_flow_attr));
+		j = 0;
+		sa->action[j].type = RTE_FLOW_ACTION_TYPE_CRYPTO;
+		sa->action[j++].conf = &sa->crypto_action;
+		sa->crypto_action.xform.type = RTE_CRYPTO_SYM_XFORM_IPSEC;
+		sa->crypto_action.xform.ipsec.algo = RTE_CRYPTO_CIPHER_AES_GCM;
+		sa->crypto_action.xform.ipsec.key.data = sa->cipher_key;
+		sa->crypto_action.xform.ipsec.key.length = sa->cipher_key_len;
+		sa->crypto_action.xform.ipsec.salt = sa->salt;
+
+		sa->action[j].type = RTE_FLOW_ITEM_TYPE_END;
+inline_with_metadata:
+		/* Implement TX ipsec inline crypto offload with metadata here! */
+not_offloaded:
 		if (inbound) {
 			sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
 			sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
@@ -628,7 +713,11 @@ struct sa_ctx {
 				sa->digest_len;
 			sa_ctx->xf[idx].a.auth.op =
 				RTE_CRYPTO_AUTH_OP_VERIFY;
-
+			if (sa->portid != -1) {
+				sa->attr.ingress = 1;
+				sa->crypto_action.xform.ipsec.op =
+						RTE_CRYPTO_CIPHER_OP_DECRYPT;
+			}
 		} else { /* outbound */
 			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
 			sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
@@ -650,6 +739,11 @@ struct sa_ctx {
 				sa->digest_len;
 			sa_ctx->xf[idx].b.auth.op =
 				RTE_CRYPTO_AUTH_OP_GENERATE;
+			if (sa->portid != -1) {
+				sa->attr.egress = 1;
+				sa->crypto_action.xform.ipsec.op =
+						RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+			}
 		}
 
 		sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
