diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 3264810..1eb752b 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -32,6 +32,10 @@
 #include <stdlib.h>
 #include <sys/time.h>
 
+#include <rte_config.h>
+#include <rte_cryptodev.h>
+#include <rte_mbuf.h>
+
 #include "byte-order.h"
 #include "csum.h"
 #include "dp-packet.h"
@@ -175,6 +179,39 @@ netdev_tnl_push_ip_header(struct dp_packet *packet,
     }
 }
 
+/* Pushes the 'size' bytes of 'header' into the headroom of 'packet',
+ * reallocating the packet if necessary.  'header' should contain an Ethernet
+ * header, followed by an IPv4 header (without options), and an L4 header.
+ *
+ * This function sets the IP header's ip_tot_len field (which should be zeroed
+ * as part of 'header') and puts its value into '*ip_tot_size' as well.  Also
+ * updates IP header checksum, as well as the l3 and l4 offsets in 'packet'.
+ *
+ * Return pointer to the L4 header added to 'packet'. */
+void *
+netdev_tnl_push_esp_header(struct dp_packet *packet,
+                const void *header, int size, uint32_t spi, uint64_t seq,
+                uint32_t iv_len, int *ip_tot_size)
+{
+    struct esp_header *esp;
+    uint8_t *iv;
+
+    esp = netdev_tnl_push_ip_header(packet, header, size, ip_tot_size);
+
+    /* set SPI and sequence number for the esp */
+    esp->spi = htonl(spi);
+    esp->seq_no = htonl(seq);
+
+    /* Adjust size to account for ESP and IV */
+    *ip_tot_size -= ESP_HEADER_LEN;
+    *ip_tot_size -= iv_len;
+    packet->l4_ofs = dp_packet_size(packet) - *ip_tot_size;
+
+    iv = (uint8_t *)(esp + 1);
+
+    return iv + iv_len;
+}
+
 static void *
 udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
                    unsigned int *hlen)
@@ -212,7 +249,6 @@ udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
     return udp + 1;
 }
 
-
 void
 netdev_tnl_push_udp_header(struct dp_packet *packet,
                            const struct ovs_action_push_tnl *data)
@@ -243,6 +279,179 @@ netdev_tnl_push_udp_header(struct dp_packet *packet,
     }
 }
 
+static inline void *
+get_sym_cop(struct rte_crypto_op *cop)
+{
+       return (cop + 1);
+}
+
+static void
+netdev_ipsec_outbound(struct dp_packet *packet, struct ipsec_options *ops,
+                        int ip_tot_size)
+{
+    struct rte_crypto_op *cop;
+    struct rte_crypto_op **cop_p;
+    struct rte_crypto_sym_op *sym_cop;
+    struct rte_mbuf *m = (struct rte_mbuf *)packet;
+    uint16_t pad_payload_len, pad_len, l3_hdr_len, p_len, iv_len, digest_len;
+    uint8_t *padding;
+    struct rte_cryptodev_sym_session *c_session;
+    unsigned ret = 0;
+    int32_t i;
+
+    iv_len = ops->en_ops.iv.length;
+    digest_len = ops->en_ops.digest_size;
+
+    /* l3_hdr_len includes eth, ipv4, esp header and IV */
+    l3_hdr_len = dp_packet_size(packet) - ip_tot_size;
+
+    /* Compute required padding, must be a multiple of the block size */
+    pad_payload_len = RTE_ALIGN_CEIL(ip_tot_size + ESP_TRAILER_LEN,
+                        ops->en_ops.block_size);
+
+    pad_len = pad_payload_len + l3_hdr_len - dp_packet_size(packet);
+
+    /* Check maximum packet size */
+    if (OVS_UNLIKELY(l3_hdr_len + iv_len + pad_payload_len + digest_len >
+                        IP_MAXPACKET)) {
+        VLOG_ERR("IPsec packet is too big");
+        return;
+    }
+
+    padding = (uint8_t *)dp_packet_put_uninit(packet, pad_len + digest_len);
+    if (OVS_UNLIKELY(padding == NULL)) {
+        VLOG_ERR("Not enough packet trailing space\n");
+        return;
+    }
+
+    /* Fill pad_len using default sequential scheme */
+    for (i = 0; i < pad_len - ESP_TRAILER_LEN; i++) {
+        padding[i] = i + 1;
+    }
+
+    /* Populate the ESP trailer */
+    padding[pad_len - ESP_TRAILER_LEN] = pad_len - ESP_TRAILER_LEN;
+    padding[pad_len - 1] = IPPROTO_UDP;
+
+    cop = rte_crypto_op_alloc(ops->crypto_op_pool,
+                                RTE_CRYPTO_OP_TYPE_SYMMETRIC);
+
+    if (cop == NULL) {
+        rte_pktmbuf_free(m);
+        rte_crypto_op_free(cop);
+        VLOG_ERR("Could not allocate crypto op.");
+        return;
+    }
+    cop->type = RTE_CRYPTO_OP_TYPE_SYMMETRIC;
+    cop->status = RTE_CRYPTO_OP_STATUS_NOT_PROCESSED;
+    sym_cop = get_sym_cop(cop);
+    sym_cop->m_src = m;
+
+    /* Adjust l3_hedr_len to give starting point for crypto cipher */
+    l3_hdr_len -= iv_len;
+
+    /* Set cipher data, address and length */
+    sym_cop->cipher.data.offset = l3_hdr_len;
+    sym_cop->cipher.data.length = pad_payload_len + iv_len;
+
+    /* Set IV data, address and length */
+    sym_cop->cipher.iv.data = ops->en_ops.iv.data;
+    sym_cop->cipher.iv.length = iv_len;
+
+    /* Set auth data, address, digest */
+    sym_cop->auth.data.offset = l3_hdr_len - ESP_HEADER_LEN;
+    sym_cop->auth.data.length = ESP_HEADER_LEN + iv_len + pad_payload_len;
+
+    p_len = dp_packet_size(packet);
+    sym_cop->auth.digest.data = rte_pktmbuf_mtod_offset(m, uint8_t *,
+                                p_len - digest_len);
+    sym_cop->auth.digest.phys_addr = rte_pktmbuf_mtophys_offset(m,
+                                        p_len - digest_len);
+    sym_cop->auth.digest.length = digest_len;
+
+    /* Set and attach sym encrypt session */
+    c_session = ops->en_ops.session;
+    ret = rte_crypto_op_attach_sym_session(cop,c_session);
+
+    if (ret != 0) {
+        VLOG_ERR("Could not attach crypto session.");
+    }
+
+    /* Add packet for crypto enqueue and dequeue */
+    cop_p = &cop;
+    ret = rte_cryptodev_enqueue_burst(ops->cdev_id,0, cop_p , 1);
+
+    if (ret < 1) {
+        rte_pktmbuf_free(cop->sym->m_src);
+        rte_crypto_op_free(cop);
+        VLOG_ERR("Could not enqueue for crypto op.");
+        return;
+    }
+
+    ret = rte_cryptodev_dequeue_burst( ops->cdev_id, 0, cop_p, 1);
+    if (ret < 1) {
+        rte_pktmbuf_free(cop->sym->m_src);
+        rte_crypto_op_free(cop);
+        VLOG_INFO("Could not dequeue crypto op.");
+        return;
+    }
+
+    if (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+        VLOG_ERR("Error occurred during outbound IPsec crypto operation.");
+    }
+
+    /* Packet has been modified, free the crypto op */
+    rte_crypto_op_free(cop);
+
+    return;
+}
+
+
+void
+netdev_tnl_push_ipsec_header(struct dp_packet *packet,
+                           const struct ovs_action_push_tnl *data)
+{
+    struct udp_header *udp;
+    int ip_tot_size;
+    struct netdev_vport *dev = data->dev;
+    struct ipsec_options *ops = dev->ipsec_ops;
+    uint16_t iv_len = ops->en_ops.iv.length;
+
+    /* XXX To do: should handle if packet is not DPDK source first */
+
+    udp = netdev_tnl_push_esp_header(packet, data->header, data->header_len,
+                                        ops->spi, ops->seq, iv_len,
+                                        &ip_tot_size);
+
+    /* Increment the associated ESP sequence number */
+    ops->seq++;
+
+    /* set udp src port */
+    udp->udp_src = netdev_tnl_get_src_port(packet);
+    udp->udp_len = htons(ip_tot_size);
+
+    if (udp->udp_csum) {
+        uint32_t csum;
+        if (netdev_tnl_is_header_ipv6(dp_packet_data(packet))) {
+            csum = packet_csum_pseudoheader6(netdev_tnl_ipv6_hdr(
+                                                dp_packet_data(packet)));
+        } else {
+            csum = packet_csum_pseudoheader(netdev_tnl_ip_hdr
+                                            (dp_packet_data(packet)));
+        }
+
+        csum = csum_continue(csum, udp, ip_tot_size);
+        udp->udp_csum = csum_finish(csum);
+
+        if (!udp->udp_csum) {
+            udp->udp_csum = htons(0xffff);
+        }
+    }
+
+    /* Conduct IPsec outbound crypto operations */
+    netdev_ipsec_outbound(packet, ops, ip_tot_size);
+}
+
 static void *
 eth_build_header(struct ovs_action_push_tnl *data,
                  const struct netdev_tnl_build_header_params *params)
@@ -326,6 +535,46 @@ udp_build_header(struct netdev_tunnel_config *tnl_cfg,
     return udp + 1;
 }
 
+static void *
+esp_build_header(struct netdev_vport *dev,
+                struct ovs_action_push_tnl *data,
+                const struct netdev_tnl_build_header_params *params)
+{
+    struct esp_header *esp;
+    uint8_t *iv;
+    uint8_t iv_len = dev->ipsec_ops->en_ops.iv.length;
+
+    esp = netdev_tnl_ip_build_header(data, params, IPPROTO_ESP);
+
+    iv = (uint8_t *)(esp + 1);
+    memset(iv, 0, iv_len);
+    data->header_len += sizeof *esp;
+    data->header_len += iv_len;
+    return iv + iv_len;
+}
+
+static void *
+vxlanipsec_udp_build_header(struct netdev_vport *dev,
+                            struct netdev_tunnel_config *tnl_cfg,
+                            struct ovs_action_push_tnl *data,
+                            const struct netdev_tnl_build_header_params
+                            *params)
+{
+    struct udp_header *udp;
+
+    udp = esp_build_header(dev, data, params);
+    udp->udp_dst = tnl_cfg->dst_port;
+
+    if (params->is_ipv6 || params->flow->tunnel.flags & FLOW_TNL_F_CSUM) {
+        /* Write a value in now to mark that we should compute the checksum
+         * later. 0xffff is handy because it is transparent to the
+         * calculation. */
+        udp->udp_csum = htons(0xffff);
+    }
+    data->header_len += sizeof *udp;
+    return udp + 1;
+}
+
 static int
 gre_header_len(ovs_be16 flags)
 {
@@ -564,6 +813,229 @@ err:
     return NULL;
 }
 
+/*
+ * The SPI of an IPsec packet is used as an index value of the spi map.
+ * This returns the associated vport netdev and from this we can access the
+ * associated crypto options for decryption.
+ */
+static struct netdev_vport *
+get_vport_from_spi(struct dp_packet *packet) {
+    uint8_t *nh = (uint8_t *)dp_packet_data(packet);
+    uint16_t ip_hdr_len = ETH_HEADER_LEN + IP_HEADER_LEN;
+    struct netdev_vport *dev = NULL;
+    struct esp_header *esp = (struct esp_header *)(nh + ip_hdr_len);
+
+    uint32_t spi = ntohl(esp->spi);
+    dev = spi_map[spi]->vp;
+    if (!dev) {
+        VLOG_ERR("Could not get associated vport for spi value: %u", spi);
+    }
+
+    return dev;
+}
+
+static int
+netdev_ipsec_inbound(struct dp_packet *packet, uint16_t *iv_len,
+                        uint16_t *trim_len)
+{
+    struct netdev_vport *dev = NULL;
+    struct ipsec_options *ops = NULL;
+    struct rte_crypto_op *cop;
+    struct rte_crypto_sym_op *sym_cop;
+    struct rte_mbuf *m = (struct rte_mbuf *)packet;
+    struct rte_cryptodev_sym_session *c_session;
+    struct rte_crypto_op **cop_p;
+    uint8_t *pad_len;
+    uint16_t ip_hdr_len, l3_hdr_len, payload_len, digest_len;
+    unsigned ret = 0;
+
+    dev = get_vport_from_spi(packet);
+
+    if (!dev) {
+        return -1;
+    }
+
+    ops = dev->ipsec_ops;
+    digest_len = ops->de_ops.digest_size;
+    *iv_len = ops->de_ops.iv.length;
+
+    /* Compute expected header lengths. Note this assumes IPv4 */
+    /* XXX To Do: compute both IPv4 andIPv6 header lengths */
+    ip_hdr_len = ETH_HEADER_LEN + IP_HEADER_LEN;
+    l3_hdr_len = ip_hdr_len + ESP_HEADER_LEN;
+    payload_len = dp_packet_size(packet) - (l3_hdr_len + *iv_len + digest_len);
+
+    cop = rte_crypto_op_alloc(ops->crypto_op_pool,
+                                RTE_CRYPTO_OP_TYPE_SYMMETRIC);
+
+    if (cop == NULL) {
+        rte_crypto_op_free(cop);
+        VLOG_INFO("Could not allocate crypto op");
+        return -1;
+    }
+
+    cop->type = RTE_CRYPTO_OP_TYPE_SYMMETRIC;
+    cop->status = RTE_CRYPTO_OP_STATUS_NOT_PROCESSED;
+    sym_cop = get_sym_cop(cop);
+    sym_cop->m_src = m;
+
+    /* Set cipher offset and length */
+    sym_cop->cipher.data.offset = l3_hdr_len + *iv_len;
+    sym_cop->cipher.data.length = payload_len;
+
+    /* Set IV offset, length */
+    uint8_t *iv = (uint8_t *) dp_packet_data(packet) + l3_hdr_len;
+    sym_cop->cipher.iv.data = iv;
+    sym_cop->cipher.iv.length = *iv_len;
+
+    /* Set authentication data offset and length */
+    sym_cop->auth.data.offset = ip_hdr_len;
+    sym_cop->auth.data.length = ESP_HEADER_LEN + *iv_len + payload_len;
+
+    /* Set authentication digest length, data and adress */
+    sym_cop->auth.digest.data = rte_pktmbuf_mtod_offset(m, void *,
+                                                        rte_pktmbuf_pkt_len(m)
+                                                        - digest_len);
+    sym_cop->auth.digest.phys_addr = rte_pktmbuf_mtophys_offset(m,
+                                                        rte_pktmbuf_pkt_len(m)
+                                                        - digest_len);
+    sym_cop->auth.digest.length = digest_len;
+
+    /* Set and attach sym encrypt session */
+    c_session = ops->de_ops.session;
+    ret = rte_crypto_op_attach_sym_session(cop,c_session);
+    if (ret != 0) {
+        VLOG_ERR(" Could not attach de crypto session");
+        rte_pktmbuf_free(cop->sym->m_src);
+        return -1;
+    }
+
+    /* Add packet for crypto enqueue and dequeue */
+    cop_p  = &cop;
+    ret = rte_cryptodev_enqueue_burst(ops->cdev_id,0, cop_p , 1);
+
+    if (ret < 1) {
+        rte_crypto_op_free(cop);
+        VLOG_INFO("Could not enqueue for crypto op");
+        return -1;
+    }
+
+    ret = rte_cryptodev_dequeue_burst(ops->cdev_id, 0, cop_p, 1);
+
+    if (ret < 1) {
+        rte_crypto_op_free(cop);
+        VLOG_INFO("Could not dequeue crypto op");
+        return -1;
+    }
+
+    if (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
+        VLOG_ERR("Could not verify/decrypt crypto operation");
+    }
+
+    /* Packet has been modified, free the crypto op */
+    rte_crypto_op_free(cop);
+
+    /* Set the correct l4 offset for the packet now that it has been
+     * verified and decrypted by taking the IV length into account
+     */
+    packet->l4_ofs = l3_hdr_len + *iv_len;
+
+    /* Now that the packet is decrypted we can compute the trailer length
+     * that will be stripped from the end of the packet using the padding
+     * value in the esp trailer */
+    pad_len = rte_pktmbuf_mtod_offset(m, uint8_t *,
+                                        rte_pktmbuf_pkt_len(m) - digest_len
+                                        - ESP_TRAILER_LEN);
+    *trim_len = digest_len + *pad_len + ESP_TRAILER_LEN;
+
+    return 0;
+}
+
+struct dp_packet *
+netdev_vxlanipsec_pop_header(struct dp_packet *packet)
+{
+    struct pkt_metadata *md = &packet->md;
+    struct flow_tnl *tnl = &md->tunnel;
+    struct vxlanhdr *vxh;
+    unsigned int hlen;
+    ovs_be32 vx_flags;
+    enum packet_type next_pt = PT_ETH;
+    int ret = 0;
+    uint16_t iv_len, trim_len;
+    struct rte_mbuf *m = (struct rte_mbuf *)packet;
+
+    /* XXX To do: should handle if packet is not DPDK source first */
+
+    /* Need to verify and decrypt the packet before performing further
+     * operations.
+     */
+    ret = netdev_ipsec_inbound(packet, &iv_len, &trim_len);
+
+    if (ret) {
+        /* Error occurred during verification/decryption */
+        goto err;
+    }
+
+    pkt_metadata_init_tnl(md);
+    if (VXLAN_HLEN > dp_packet_l4_size(packet)) {
+        goto err;
+    }
+
+    vxh = udp_extract_tnl_md(packet, tnl, &hlen);
+    if (!vxh) {
+        goto err;
+    }
+
+    vx_flags = get_16aligned_be32(&vxh->vx_flags);
+    if (vx_flags & htonl(VXLAN_HF_GPE)) {
+        vx_flags &= htonl(~VXLAN_GPE_USED_BITS);
+        /* Drop the OAM packets */
+        if (vxh->vx_gpe.flags & VXLAN_GPE_FLAGS_O) {
+            goto err;
+        }
+        switch (vxh->vx_gpe.next_protocol) {
+        case VXLAN_GPE_NP_IPV4:
+            next_pt = PT_IPV4;
+            break;
+        case VXLAN_GPE_NP_IPV6:
+            next_pt = PT_IPV6;
+            break;
+        case VXLAN_GPE_NP_NSH:
+            next_pt = PT_NSH;
+            break;
+        case VXLAN_GPE_NP_ETHERNET:
+            next_pt = PT_ETH;
+            break;
+        default:
+            goto err;
+        }
+    }
+
+    if (vx_flags != htonl(VXLAN_FLAGS) ||
+       (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) {
+        VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n",
+                     ntohl(vx_flags),
+                     ntohl(get_16aligned_be32(&vxh->vx_vni)));
+        goto err;
+    }
+    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
+    tnl->flags |= FLOW_TNL_F_KEY;
+
+    packet->packet_type = htonl(next_pt);
+    dp_packet_reset_packet(packet, hlen + ESP_HEADER_LEN + iv_len +
+                            VXLAN_HLEN);
+    ret = rte_pktmbuf_trim(m, trim_len);
+
+    if (next_pt != PT_ETH) {
+        packet->l3_ofs = 0;
+    }
+
+    return packet;
+err:
+    dp_packet_delete(packet);
+    return NULL;
+}
+
 int
 netdev_vxlan_build_header(const struct netdev *netdev,
                           struct ovs_action_push_tnl *data,
@@ -621,6 +1093,64 @@ drop:
     return 1;
 }
 
+int
+netdev_vxlanipsec_build_header(const struct netdev *netdev,
+                          struct ovs_action_push_tnl *data,
+                          const struct netdev_tnl_build_header_params *params)
+{
+    struct netdev_vport *dev = netdev_vport_cast(netdev);
+    struct netdev_tunnel_config *tnl_cfg;
+    struct vxlanhdr *vxh;
+
+    /* XXX: RCUfy tnl_cfg. */
+    ovs_mutex_lock(&dev->mutex);
+    tnl_cfg = &dev->tnl_cfg;
+
+    data->dev = netdev_vport_cast(netdev);
+
+    vxh = vxlanipsec_udp_build_header(dev, tnl_cfg, data, params);
+
+    if (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
+        put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS | VXLAN_HF_GPE));
+        put_16aligned_be32(&vxh->vx_vni,
+                           htonl(ntohll(params->flow->tunnel.tun_id) << 8));
+        if (params->flow->packet_type == htonl(PT_ETH)) {
+            vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_ETHERNET;
+        } else if (pt_ns(params->flow->packet_type) == OFPHTN_ETHERTYPE) {
+            switch (pt_ns_type(params->flow->packet_type)) {
+            case ETH_TYPE_IP:
+                vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_IPV4;
+                break;
+            case ETH_TYPE_IPV6:
+                vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_IPV6;
+                break;
+            case ETH_TYPE_TEB:
+                vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_ETHERNET;
+                break;
+            default:
+                goto drop;
+            }
+        } else {
+            goto drop;
+        }
+    } else {
+        put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS));
+        put_16aligned_be32(&vxh->vx_vni,
+                           htonl(ntohll(params->flow->tunnel.tun_id) << 8));
+    }
+
+    ovs_mutex_unlock(&dev->mutex);
+    data->header_len += sizeof *vxh;
+    data->tnl_type = OVS_VPORT_TYPE_VXLAN;
+    return 0;
+
+drop:
+    ovs_mutex_unlock(&dev->mutex);
+    return 1;
+}
+
+
+
 struct dp_packet *
 netdev_geneve_pop_header(struct dp_packet *packet)
 {
diff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h
index a912ce9..21ffdbf 100644
--- a/lib/netdev-native-tnl.h
+++ b/lib/netdev-native-tnl.h
@@ -42,6 +42,9 @@ netdev_gre_pop_header(struct dp_packet *packet);
 void
 netdev_tnl_push_udp_header(struct dp_packet *packet,
                            const struct ovs_action_push_tnl *data);
+void
+netdev_tnl_push_ipsec_header(struct dp_packet *packet,
+                                const struct ovs_action_push_tnl *data);
 int
 netdev_geneve_build_header(const struct netdev *netdev,
                            struct ovs_action_push_tnl *data,
@@ -55,9 +58,28 @@ netdev_vxlan_build_header(const struct netdev *netdev,
                           struct ovs_action_push_tnl *data,
                           const struct netdev_tnl_build_header_params *params);
 
+/* Prototype for vxlanipsec build header functionality */
+int
+netdev_vxlanipsec_build_header(const struct netdev *netdev,
+                          struct ovs_action_push_tnl *data,
+                          const struct netdev_tnl_build_header_params *params);
+
 struct dp_packet *
 netdev_vxlan_pop_header(struct dp_packet *packet);
 
+/* Prototype for vxlanipsec pop functionality */
+struct dp_packet *
+netdev_vxlanipsec_pop_header(struct dp_packet *packet);
+
+/* Prototype of ipsec_inbound functionality */
+/*int
+netdev_ipsec_inbound(struct dp_packet *packet);*/
+
+/* Prototype of ipsec_inbound functionality */
+/*void
+netdev_ipsec_outbound(struct dp_packet *packet, struct ipsec_options *ops,
+                        int ip_tot_size);*/
+
 static inline bool
 netdev_tnl_is_header_ipv6(const void *header)
 {
@@ -103,6 +125,10 @@ netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
 void *
 netdev_tnl_push_ip_header(struct dp_packet *packet,
                           const void *header, int size, int *ip_tot_size);
+void *
+netdev_tnl_push_esp_header(struct dp_packet *packet,
+                           const void *header, int size, uint32_t spi,
+                           uint64_t seq, uint32_t iv_len, int *ip_tot_size);
 void
 netdev_tnl_egress_port_range(struct unixctl_conn *conn, int argc,
                              const char *argv[], void *aux OVS_UNUSED);
diff --git a/lib/netdev-vport-private.h b/lib/netdev-vport-private.h
index d89a28c..2c72a7e 100644
--- a/lib/netdev-vport-private.h
+++ b/lib/netdev-vport-private.h
@@ -24,6 +24,36 @@
 #include "netdev-provider.h"
 #include "ovs-thread.h"
 
+#define MAX_KEY_SIZE 128
+
+struct cryptofwd_key {
+    uint8_t *data;
+    uint32_t length;
+    phys_addr_t phys_addr;
+};
+
+struct crypto_options {
+    struct rte_crypto_sym_xform cipher_xform;
+    struct cryptofwd_key iv;
+    struct rte_crypto_sym_xform auth_xform;
+    int digest_size;
+    uint16_t block_size;
+    struct rte_cryptodev_sym_session *session;
+};
+
+struct ipsec_options {
+    uint8_t cdev_id;
+    uint32_t spi;
+    uint64_t seq;
+    struct crypto_options en_ops;
+    struct crypto_options de_ops;
+    struct rte_mempool *crypto_pkt_buf_pool;
+    struct rte_mempool *crypto_op_pool;
+    struct rte_cryptodev_qp_conf qp_conf;
+    struct rte_cryptodev_config conf;
+    struct spi_map_entry spi_entry;
+};
+
 struct netdev_vport {
     struct netdev up;
 
@@ -40,6 +70,9 @@ struct netdev_vport {
 
     /* Patch Ports. */
     char *peer;
+
+    /* IPsec cryptodev specifics */
+    struct ipsec_options *ipsec_ops;
 };
 
 int netdev_vport_construct(struct netdev *);
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index d11c5cc..8575c5c 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -26,6 +26,13 @@
 #include <netinet/in.h>
 #include <netinet/ip6.h>
 #include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <rte_config.h>
+#include <rte_cryptodev.h>
+#include <rte_errno.h>
+#include <rte_malloc.h>
+#include <rte_mbuf.h>
 
 #include "byte-order.h"
 #include "daemon.h"
@@ -59,6 +66,8 @@ VLOG_DEFINE_THIS_MODULE(netdev_vport);
 
 #define DEFAULT_TTL 64
 
+#define SPI_VAL 1
+
 /* Last read of the route-table's change number. */
 static uint64_t rt_change_seqno;
 
@@ -106,7 +115,8 @@ netdev_vport_needs_dst_port(const struct netdev *dev)
 
     return (class->get_config == get_tunnel_config &&
             (!strcmp("geneve", type) || !strcmp("vxlan", type) ||
-             !strcmp("lisp", type) || !strcmp("stt", type)) );
+             !strcmp("lisp", type) || !strcmp("stt", type) ||
+             !strcmp("vxlanipsec", type)) );
 }
 
 const char *
@@ -180,11 +190,299 @@ netdev_vport_alloc(void)
     return &netdev->up;
 }
 
+static void
+netdev_vport_reserve_crypto_key_memory(struct crypto_options *options)
+{
+    options->cipher_xform.cipher.key.data = rte_malloc("crypto key",
+                                            MAX_KEY_SIZE, 0);
+
+    if (options->cipher_xform.cipher.key.data == NULL) {
+        VLOG_ERR("Failed to allocate memory for cipher key");
+    }
+
+    options->auth_xform.auth.key.data = rte_malloc("auth key",
+                                        MAX_KEY_SIZE, 0);
+
+    if (options->auth_xform.auth.key.data == NULL) {
+        VLOG_ERR("Failed to allocate memory for auth key");
+    }
+
+    options->iv.data = rte_malloc("iv", MAX_KEY_SIZE, 0);
+
+    if (options->iv.data == NULL) {
+        VLOG_ERR("Failed to allocate memory for IV");
+    }
+
+    options->iv.phys_addr = rte_malloc_virt2phy(options->iv.data);
+}
+
+/*
+ * Setup the cryptodev transforms for the encryption action. Note here
+ * the transforms are hard coded. In the future these arguments will be
+ * provided by a user, parsed and setup accordingly.
+ *
+ * The current transform is setup to encrypt first, then generate a hash
+ * digest.
+ *
+ * The cipher is 128 AES_CBC (16 byte key and IV)
+ * The authentication is SHA1_HMAC 96 (20 byte key length, 12 byte digest)
+ */
+static void
+netdev_vport_setup_en_crypto_xforms(struct crypto_options *options)
+{
+    /* XXX To do:Read transform values from user provided arguments. */
+    /* Cipher Data */
+    options->cipher_xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+    options->cipher_xform.next = NULL;
+    options->cipher_xform.cipher.key.length = 16;
+    options->iv.length = 16;
+    options->cipher_xform.cipher.algo = RTE_CRYPTO_CIPHER_AES_CBC;
+    options->cipher_xform.cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+
+    options->block_size = 16;
+
+    /* Authentication Data */
+    options->auth_xform.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+    options->auth_xform.next = NULL;
+    options->auth_xform.auth.key.length = 20;
+    options->digest_size = 12;
+    options->auth_xform.auth.algo = RTE_CRYPTO_AUTH_SHA1_HMAC;
+    options->auth_xform.auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+}
+
+/*
+ * Setup the cryptodev transforms for the decryption action. Note here
+ * the transforms are hard coded. In the future these arguments will be
+ * provided by a user, parsed and setup accordingly.
+ *
+ * The current transform is setup to generate a hash digest first then decrypt.
+ *
+ * The cipher is 128 AES_CBC (16 byte key and IV)
+ * The authentication is SHA1_HMAC 96 (20 byte key length, 12 byte digest)
+ */
+static void
+netdev_vport_setup_de_crypto_xforms(struct crypto_options *options)
+{
+    /* Cipher Data */
+    options->cipher_xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+    options->cipher_xform.next = NULL;
+    options->cipher_xform.cipher.key.length = 16;
+    options->iv.length = 16;
+    options->cipher_xform.cipher.algo = RTE_CRYPTO_CIPHER_AES_CBC;
+    options->cipher_xform.cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+    options->block_size = 16;
+
+    /* Authentication Data */
+    options->auth_xform.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+    options->auth_xform.next = NULL;
+    options->auth_xform.auth.key.length = 20;
+    options->digest_size = 12;
+    options->auth_xform.auth.algo = RTE_CRYPTO_AUTH_SHA1_HMAC;
+    options->auth_xform.auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+}
+
+static int
+netdev_vport_allocate_crypto_ops_pool(struct netdev_vport *dev) {
+
+    int socket_id = rte_lcore_to_socket_id(rte_get_master_lcore());
+    int err = 0;
+
+    dev->ipsec_ops->crypto_op_pool =
+    rte_crypto_op_pool_create("crypto_op_pool",
+                                RTE_CRYPTO_OP_TYPE_SYMMETRIC, 8192, 128, 0,
+                                socket_id);
+    if (dev->ipsec_ops->crypto_op_pool == NULL) {
+        VLOG_ERR("Could not allocate mempool for crypto ops, error %d",
+                    rte_errno);
+        return rte_errno;
+    } else {
+       VLOG_INFO("Success: Allocated crypto ops mempool for port %s",
+                   dev->up.name);
+    }
+
+    return err;
+}
+
+
+static struct ipsec_options *
+netdev_vport_construct_ipsec_options(void) {
+    struct ipsec_options *ipsec = NULL ;
+    ipsec = xmalloc(sizeof *ipsec);
+
+    return ipsec;
+}
+
+static int
+netdev_vport_initialize_crypto_sessions(struct ipsec_options *options,
+                                        uint8_t cdev_id)
+{
+    struct rte_crypto_sym_xform *encrypt_xform;
+    struct rte_crypto_sym_xform *decrypt_xform;
+
+    encrypt_xform = &options->en_ops.cipher_xform;
+    encrypt_xform->next = &options->en_ops.auth_xform;
+
+    /* Setup encrypt cipher/auth parameters */
+    options->en_ops.session = rte_cryptodev_sym_session_create(cdev_id,
+                                                                encrypt_xform);
+
+    if (options->en_ops.session == NULL) {
+        VLOG_ERR("Could not create encrypt crypto session");
+        return -1;
+    }
+
+    decrypt_xform = &options->de_ops.auth_xform;
+    decrypt_xform->next = &options->de_ops.cipher_xform;
+
+    /* Setup decrypt cipher/auth parameters */
+    options->de_ops.session = rte_cryptodev_sym_session_create(cdev_id,
+                                                                decrypt_xform);
+    if (options->de_ops.session == NULL) {
+            VLOG_ERR("Could not create encrypt crypto session");
+            return -1;
+       }
+
+       return 0;
+}
+
+/* Generate random key */
+static void
+generate_random_key(uint8_t *key, unsigned length)
+{
+    int fd;
+    int ret;
+
+    fd = open("/dev/urandom", O_RDONLY);
+    if (fd < 0) {
+        VLOG_ERR("Failed to generate random key");
+    }
+
+    ret = read(fd, key, length);
+    close(fd);
+
+    if (ret != (signed)length) {
+        VLOG_ERR("Failed to generate random key");
+    }
+}
+
+/* Set key of given length */
+static void
+set_crypto_key(uint8_t *key, unsigned length)
+{
+    int i;
+
+    /* XXX To do: Read keys provided by users and set them accordingly */
+    /* Note the approach below is meant for functional testing only, setting a
+     * key in this manner is terrible in terms of security but future revisions
+     * will introduce the ability to read keys from the command line.
+     */
+    for (i = 0; i < length; i++) {
+        key[i] = (uint8_t)i;
+    }
+
+}
+
+static int
+netdev_vport_construct_crypto(struct netdev_vport *dev)
+{
+    int err = 0;
+    /* Allocate the IPsec pointer */
+    dev->ipsec_ops = netdev_vport_construct_ipsec_options();
+
+    if (dev->ipsec_ops == NULL) {
+        VLOG_ERR("Failure: could not allocate ipsec struct for port %s",
+                    dev->up.name);
+        }
+
+    /* Allocate the crypto op pool */
+    netdev_vport_allocate_crypto_ops_pool(dev);
+
+    /* Reserve key memory */
+    netdev_vport_reserve_crypto_key_memory(&dev->ipsec_ops->en_ops);
+    netdev_vport_reserve_crypto_key_memory(&dev->ipsec_ops->de_ops);
+
+    /* Initialize spi and sequence numbers */
+    dev->ipsec_ops->spi = SPI_VAL;
+    dev->ipsec_ops->seq = 0;
+
+    /* Initialize the encrypt crypto xform */
+    netdev_vport_setup_en_crypto_xforms(&dev->ipsec_ops->en_ops);
+
+    /* Initialize the decrypt  crypto xform */
+    netdev_vport_setup_de_crypto_xforms(&dev->ipsec_ops->de_ops);
+
+    set_crypto_key(dev->ipsec_ops->en_ops.cipher_xform.cipher.key.data,
+                    dev->ipsec_ops->en_ops.cipher_xform.cipher.key.length);
+
+
+    set_crypto_key(dev->ipsec_ops->en_ops.auth_xform.auth.key.data,
+                    dev->ipsec_ops->en_ops.cipher_xform.auth.key.length);
+
+    generate_random_key(dev->ipsec_ops->en_ops.iv.data,
+                        dev->ipsec_ops->en_ops.iv.length);
+
+    /* Copy the cipher, auth and IV keys to decryption options*/
+    memcpy(dev->ipsec_ops->de_ops.cipher_xform.cipher.key.data,
+            dev->ipsec_ops->en_ops.cipher_xform.cipher.key.data,
+            dev->ipsec_ops->en_ops.cipher_xform.cipher.key.length);
+    memcpy(dev->ipsec_ops->de_ops.auth_xform.auth.key.data,
+            dev->ipsec_ops->en_ops.auth_xform.auth.key.data,
+            dev->ipsec_ops->en_ops.auth_xform.auth.key.length);
+    memcpy(dev->ipsec_ops->de_ops.iv.data,
+            dev->ipsec_ops->en_ops.iv.data,
+            dev->ipsec_ops->en_ops.iv.length);
+
+    dev->ipsec_ops->spi_entry.vp = dev;
+    dev->ipsec_ops->spi_entry.spi_val = SPI_VAL;
+    spi_map[SPI_VAL] = &dev->ipsec_ops->spi_entry;
+
+    /* create the vdev for the cryptodev pmd */
+    rte_vdev_init("cryptodev_aesni_mb_pmd",
+                    "max_nb_queue_pairs=2,max_nb_sessions=1024,socket_id=0");
+    dev->ipsec_ops->cdev_id = rte_cryptodev_count()-1;
+
+    /* Set the cryptodev conf */
+    dev->ipsec_ops->conf.nb_queue_pairs = 1;
+    dev->ipsec_ops->conf.socket_id = 0;
+    dev->ipsec_ops->conf.session_mp.nb_objs = 2048;
+    dev->ipsec_ops->conf.session_mp.cache_size = 64;
+    err = rte_cryptodev_configure(dev->ipsec_ops->cdev_id,
+                                    &dev->ipsec_ops->conf);
+    if (err < 0) {
+        VLOG_ERR("Failed to configure cryptodev %u", dev->ipsec_ops->cdev_id);
+    }
+
+    /* set the queue configuration for the cryptodev device */
+    dev->ipsec_ops->qp_conf.nb_descriptors = 2048;
+    err = rte_cryptodev_queue_pair_setup(dev->ipsec_ops->cdev_id, 0,
+                                            &dev->ipsec_ops->qp_conf,0);
+    if (err < 0) {
+        VLOG_ERR("Failed to setup queue pair %u on cryptodev %u",0,
+                    dev->ipsec_ops->cdev_id);
+    }
+
+    err = rte_cryptodev_start(dev->ipsec_ops->cdev_id);
+    if (err < 0) {
+        VLOG_ERR("Failed to start device %u: error %d\n",
+                    dev->ipsec_ops->cdev_id, err);
+    }
+
+    err = netdev_vport_initialize_crypto_sessions(dev->ipsec_ops,
+                                                    dev->ipsec_ops->cdev_id);
+    if (err) {
+        VLOG_ERR("Error occurred initializing crypto session");
+    }
+
+    return err;
+}
+
 int
 netdev_vport_construct(struct netdev *netdev_)
 {
     struct netdev_vport *dev = netdev_vport_cast(netdev_);
     const char *type = netdev_get_type(netdev_);
+    const char *name = netdev_->name;
+    int err = 0;
 
     ovs_mutex_init(&dev->mutex);
     eth_addr_random(&dev->etheraddr);
@@ -198,18 +496,41 @@ netdev_vport_construct(struct netdev *netdev_)
         dev->tnl_cfg.dst_port = htons(LISP_DST_PORT);
     } else if (!strcmp(type, "stt")) {
         dev->tnl_cfg.dst_port = htons(STT_DST_PORT);
+    } else if (!strcmp(type, "vxlanipsec")) {
+        if (strcmp(name, "vxlan_sys_4789")) {
+            dev->tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
+            err = netdev_vport_construct_crypto(dev);
+        }
     }
 
     dev->tnl_cfg.dont_fragment = true;
     dev->tnl_cfg.ttl = DEFAULT_TTL;
-    return 0;
+    return err;
 }
 
 static void
 netdev_vport_destruct(struct netdev *netdev_)
 {
     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
-
+    const char *type = netdev_get_type(netdev_);
+    const char *name = netdev_->name;
+    struct ipsec_options *ops = netdev->ipsec_ops;
+
+    if (!strcmp(type,"vxlanipsec")) {
+        if (strcmp(name,"vxlan_sys_4789")) {
+            rte_cryptodev_stop(netdev->ipsec_ops->cdev_id);
+            rte_cryptodev_close(netdev->ipsec_ops->cdev_id);
+
+            rte_free(ops->en_ops.cipher_xform.cipher.key.data);
+            rte_free(ops->en_ops.auth_xform.auth.key.data);
+            rte_free(ops->en_ops.iv.data);
+            rte_free(ops->de_ops.cipher_xform.cipher.key.data);
+            rte_free(ops->de_ops.auth_xform.auth.key.data);
+            rte_free(ops->de_ops.iv.data);
+            rte_mempool_free(netdev->ipsec_ops->crypto_op_pool);
+            free(netdev->ipsec_ops);
+           }
+        }
     free(netdev->peer);
     ovs_mutex_destroy(&netdev->mutex);
 }
@@ -410,6 +731,9 @@ tunnel_supported_layers(const char *type,
     } else if (!strcmp(type, "vxlan")
                && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
         return TNL_L2 | TNL_L3;
+    } else if (!strcmp(type, "vxlanipsec")
+                && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
+        return TNL_L2 | TNL_L3;
     } else {
         return TNL_L2;
     }
@@ -454,6 +778,11 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
         tnl_cfg.dst_port = htons(STT_DST_PORT);
     }
 
+    if (!strcmp(type, "vxlanipsec")) {
+         /* Use the VXLAN destination for for vlan ipsec */
+         tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
+    }
+
     needs_dst_port = netdev_vport_needs_dst_port(dev_);
     tnl_cfg.dont_fragment = true;
 
@@ -535,6 +864,28 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
             }
 
             free(str);
+        } else if (!strcmp(node->key, "exts") && !strcmp(type, "vxlanipsec")) {
+            char *str = xstrdup(node->value);
+            char *ext, *save_ptr = NULL;
+
+            tnl_cfg.exts = 0;
+
+            ext = strtok_r(str, ",", &save_ptr);
+            while (ext) {
+                if (!strcmp(type, "vxlanipsec") && !strcmp(ext, "gbp")) {
+                    tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP);
+                } else if (!strcmp(type, "vxlanipsec") &&
+                            !strcmp(ext, "gpe")) {
+                    tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE);
+                } else {
+                    ds_put_format(&errors, "%s: unknown extension '%s'\n",
+                                   name, ext);
+                }
+
+                ext = strtok_r(NULL, ",", &save_ptr);
+            }
+
+            free(str);
         } else if (!strcmp(node->key, "egress_pkt_mark")) {
             tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10);
             tnl_cfg.set_egress_pkt_mark = true;
@@ -548,6 +899,10 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
     const char *full_type = (strcmp(type, "vxlan") ? type
                              : (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GPE)
                                 ? "VXLAN-GPE" : "VXLAN (without GPE"));
+
+    /* XXX todo vxlan ipsec here for GPE */
+
+
     const char *packet_type = smap_get(args, "packet_type");
     if (!packet_type) {
         tnl_cfg.pt_mode = default_pt_mode(layers);
@@ -698,6 +1053,7 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
 
         if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
             (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
+            (!strcmp("vxlanipsec", type) && dst_port != VXLAN_DST_PORT) ||
             (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
             (!strcmp("stt", type) && dst_port != STT_DST_PORT)) {
             smap_add_format(args, "dst_port", "%d", dst_port);
@@ -975,6 +1331,10 @@ netdev_vport_tunnel_register(void)
                                            netdev_tnl_push_udp_header,
                                            netdev_vxlan_pop_header,
                                            NETDEV_VPORT_GET_IFINDEX),
+        TUNNEL_CLASS("vxlanipsec", "vxlan_sys", netdev_vxlanipsec_build_header,
+                                                netdev_tnl_push_ipsec_header,
+                                                netdev_vxlanipsec_pop_header,
+                                                NETDEV_VPORT_GET_IFINDEX),
         TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL, NULL),
         TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL, NULL),
     };
diff --git a/lib/netdev-vport.h b/lib/netdev-vport.h
index 9d756a2..1caecef 100644
--- a/lib/netdev-vport.h
+++ b/lib/netdev-vport.h
@@ -27,6 +27,18 @@ struct netdev;
 struct netdev_class;
 struct netdev_stats;
 
+struct spi_map_entry {
+    struct netdev_vport *vp;
+    int spi_val;
+};
+
+/* XXX To Do: For simple testing we define the array as having 5 entries below,
+ * but this really should be set to the maximum number for SPIs supported which
+ * could be the number of ipsec tunnels supported if there is a 1:1
+ * mapping of SPIs to ports.
+ */
+struct spi_map_entry *spi_map[5];
+
 void netdev_vport_tunnel_register(void);
 void netdev_vport_patch_register(void);
 
diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
index 777ed4d..1416ebb 100644
--- a/lib/tnl-ports.c
+++ b/lib/tnl-ports.c
@@ -127,9 +127,10 @@ map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
          /* XXX: No fragments support. */
         match.wc.masks.nw_frag = FLOW_NW_FRAG_MASK;
 
-        /* 'tp_port' is zero for GRE tunnels. In this case it
+        /* 'tp_port' is zero for GRE tunnels and will not be
+         * accessible in an encrypted IPsec packet. In this case it
          * doesn't make sense to match on UDP port numbers. */
-        if (tp_port) {
+        if (tp_port && (nw_proto != IPPROTO_ESP)) {
             match.wc.masks.tp_dst = OVS_BE16_MAX;
         }
         if (IN6_IS_ADDR_V4MAPPED(addr)) {
@@ -177,6 +178,9 @@ tnl_type_to_nw_proto(const char type[])
     if (!strcmp(type, "vxlan")) {
         return IPPROTO_UDP;
     }
+    if (!strcmp(type, "vxlanipsec")) {
+        return IPPROTO_ESP;
+    }
     return 0;
 }
 
