diff mbox

[ovs-dev,RFC,v2,06/10] vxlanipsec: Add userspace support for vxlan ipsec.

Message ID 1503679232-11135-7-git-send-email-ian.stokes@intel.com
State Changes Requested
Headers show

Commit Message

Stokes, Ian Aug. 25, 2017, 4:40 p.m. UTC
This patch introduces a new tunnel port type 'vxlanipsec'. This port
combines vxlan tunnelling with IPsec operating in transport mode.

Ciphering and authentication actions ares provided by a DPDK cryptodev.
The cryptodev operates as a vdev and is associated with the vxlan tunnel
port. Upon tunnel encapsulation packets are encrypted and a hash digest
attached to the packet as per RFC4303. Upon decapsulation a packet is
first verified via the hash and then decrypted.

The cipher algorithm used is 128 AES-CBC and the authentication algorithm
is HMAC-SHA1-96. Note this work is in progress and is not meant for
upstream. It's purpose is to solicit feedback on the approach and known
issues flagged in the accompanying cover letter to the patch series.

Signed-off-by: Ian Stokes <ian.stokes@intel.com>
---
 lib/netdev-native-tnl.c    |  532 +++++++++++++++++++++++++++++++++++++++++++-
 lib/netdev-native-tnl.h    |   26 +++
 lib/netdev-vport-private.h |   33 +++
 lib/netdev-vport.c         |  366 ++++++++++++++++++++++++++++++-
 lib/netdev-vport.h         |   12 +
 lib/tnl-ports.c            |    8 +-
 6 files changed, 971 insertions(+), 6 deletions(-)

Comments

Chandran, Sugesh Sept. 1, 2017, 10:15 p.m. UTC | #1
At very high level, this patch is very long.
Is it possible to split them based on the functionality?
More comments are below,

Regards
_Sugesh

> -----Original Message-----
> From: ovs-dev-bounces@openvswitch.org [mailto:ovs-dev-
> bounces@openvswitch.org] On Behalf Of Ian Stokes
> Sent: Friday, August 25, 2017 5:40 PM
> To: dev@openvswitch.org
> Subject: [ovs-dev] [RFC PATCH v2 06/10] vxlanipsec: Add userspace support
> for vxlan ipsec.
> 
> This patch introduces a new tunnel port type 'vxlanipsec'. This port combines
> vxlan tunnelling with IPsec operating in transport mode.
> 
> Ciphering and authentication actions ares provided by a DPDK cryptodev.
> The cryptodev operates as a vdev and is associated with the vxlan tunnel
> port. Upon tunnel encapsulation packets are encrypted and a hash digest
> attached to the packet as per RFC4303. Upon decapsulation a packet is first
> verified via the hash and then decrypted.
> 
> The cipher algorithm used is 128 AES-CBC and the authentication algorithm is
> HMAC-SHA1-96. Note this work is in progress and is not meant for upstream.
> It's purpose is to solicit feedback on the approach and known issues flagged
> in the accompanying cover letter to the patch series.
> 
> Signed-off-by: Ian Stokes <ian.stokes@intel.com>
> ---

[snip]
>  }
> 
> +
> +    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;
> +    }
> +
[Sugesh] Why should this need to be filled with numbers here. May be I am missing something?
> +    /* 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.");
[Sugesh] This shoule be VLOG_ERR_RL.
Since the tunnel combine take care of packet clone, I assume there is no issues of freeing the packet here. However I would suggest to test and make sure
it doesn't break any post tunnel actions and handling of patch ports with tunnel.
> +        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;
[Sugesh] Is it possible to define the sym_cop only once and reuse it for all the packets than initializing for every packet,?
> +

[snip]
> +    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.");
[Sugesh] Should we free the packet here, when the attach failed?
> +    }
> +
> +    /* 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.");
[Sugesh] again VLOG_ERR_RL , Use it for all the packet processing path to avoid so many messages.
> +        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.");
[Sugesh] this case also the packets must be released. Otherwise the packets get forwarded plain.
> +    }
> +
> +    /* 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++;
[Sugesh] Should it be get roll over after some point in time??
I see that the seq is initialized at the time of vport creation.
Is it OK ?

> +
> +    /* 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);
> +

[snip]
>  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;
[Sugesh] this has to be a hash map , perhaps it has to point to the ipsec
context instead of vport ?
> +    if (!dev) {
> +        VLOG_ERR("Could not get associated vport for spi value: %u", spi);
> +    }
> +
> +    return dev;
> +}
> +
[Sugesh] May be a comment that says inbound is for decrypt 
Or handling of packets at the decap side?
> +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);

[Snip]
> +
> +    /* 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);
> +
[Sugesh] I don't see free up the packets on any of following error cases.
Any reason for that?
Also for the free case above , what would be the impact if we have more actions
Something like
PKT-IN --> decap -->VM
               -->  sample -->controller
This case the same packet is cloned for sample, will it handle properly here?
> +    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
> 
>  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
[Sugesh] In general I am kind of inclined towards having a new file(netdev-ipsec-vport) to keep all the ipsec crypto specific functions.
There two advantages of having it that way 1) code is modular and doesn't impact existing vport implementation 2) Doesn't need to put
a lot of #if, #else for DPDK based code in the current vport code. Again its just my opinion. 
Same for the vport.h file .
> @@ -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;
> 

[Snip]
> 
> --
> 1.7.0.7
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Ben Pfaff Oct. 31, 2017, 10 p.m. UTC | #2
On Fri, Aug 25, 2017 at 05:40:28PM +0100, Ian Stokes wrote:
> This patch introduces a new tunnel port type 'vxlanipsec'. This port
> combines vxlan tunnelling with IPsec operating in transport mode.
> 
> Ciphering and authentication actions ares provided by a DPDK cryptodev.
> The cryptodev operates as a vdev and is associated with the vxlan tunnel
> port. Upon tunnel encapsulation packets are encrypted and a hash digest
> attached to the packet as per RFC4303. Upon decapsulation a packet is
> first verified via the hash and then decrypted.
> 
> The cipher algorithm used is 128 AES-CBC and the authentication algorithm
> is HMAC-SHA1-96. Note this work is in progress and is not meant for
> upstream. It's purpose is to solicit feedback on the approach and known
> issues flagged in the accompanying cover letter to the patch series.
> 
> Signed-off-by: Ian Stokes <ian.stokes@intel.com>

Thanks a lot for working on this feature!

When I compile without dpdk enabled, I now get:

    ../lib/netdev-vport.c:31:10: fatal error: 'rte_config.h' file not found
    ../lib/netdev-native-tnl.c:35:10: fatal error: 'rte_config.h' file not found
"sparse" complains:

../lib/netdev-vport.h:40:22: warning: symbol 'spi_map' was not declared. Should it be static?

There is obviously a lot of code here to review, but I have not started
on that yet.
Stokes, Ian Nov. 1, 2017, 5:01 p.m. UTC | #3
> On Fri, Aug 25, 2017 at 05:40:28PM +0100, Ian Stokes wrote:
> > This patch introduces a new tunnel port type 'vxlanipsec'. This port
> > combines vxlan tunnelling with IPsec operating in transport mode.
> >
> > Ciphering and authentication actions ares provided by a DPDK cryptodev.
> > The cryptodev operates as a vdev and is associated with the vxlan
> > tunnel port. Upon tunnel encapsulation packets are encrypted and a
> > hash digest attached to the packet as per RFC4303. Upon decapsulation
> > a packet is first verified via the hash and then decrypted.
> >
> > The cipher algorithm used is 128 AES-CBC and the authentication
> > algorithm is HMAC-SHA1-96. Note this work is in progress and is not
> > meant for upstream. It's purpose is to solicit feedback on the
> > approach and known issues flagged in the accompanying cover letter to
> the patch series.
> >
> > Signed-off-by: Ian Stokes <ian.stokes@intel.com>
> 
> Thanks a lot for working on this feature!
> 
> When I compile without dpdk enabled, I now get:
> 
>     ../lib/netdev-vport.c:31:10: fatal error: 'rte_config.h' file not
> found
>     ../lib/netdev-native-tnl.c:35:10: fatal error: 'rte_config.h' file not
> found "sparse" complains:
> 
> ../lib/netdev-vport.h:40:22: warning: symbol 'spi_map' was not declared.
> Should it be static?

Hi Ben, thanks for looking at this, I flagged that compilation fails without DPDK enabled in the cover letter (I know, a big no no, I didn't expect this code to be upstreamed in its current form so I thought flagging it as known in the cover and keeping it as RFC would be ok. 

For the purpose of this RFC my aim was to give people something to functionally test with, and hopefully gather opinions on issues such as the acinclude build steps, dependency on external libraries etc. as well as the overall design.

Any feedback you have as regards design or changes is more than welcome as I expect a few more RFC revisions before nailing something concrete down.

Ian
> 
> There is obviously a lot of code here to review, but I have not started on
> that yet.
Ben Pfaff Nov. 1, 2017, 5:36 p.m. UTC | #4
On Wed, Nov 01, 2017 at 05:01:42PM +0000, Stokes, Ian wrote:
> > On Fri, Aug 25, 2017 at 05:40:28PM +0100, Ian Stokes wrote:
> > > This patch introduces a new tunnel port type 'vxlanipsec'. This port
> > > combines vxlan tunnelling with IPsec operating in transport mode.
> > >
> > > Ciphering and authentication actions ares provided by a DPDK cryptodev.
> > > The cryptodev operates as a vdev and is associated with the vxlan
> > > tunnel port. Upon tunnel encapsulation packets are encrypted and a
> > > hash digest attached to the packet as per RFC4303. Upon decapsulation
> > > a packet is first verified via the hash and then decrypted.
> > >
> > > The cipher algorithm used is 128 AES-CBC and the authentication
> > > algorithm is HMAC-SHA1-96. Note this work is in progress and is not
> > > meant for upstream. It's purpose is to solicit feedback on the
> > > approach and known issues flagged in the accompanying cover letter to
> > the patch series.
> > >
> > > Signed-off-by: Ian Stokes <ian.stokes@intel.com>
> > 
> > Thanks a lot for working on this feature!
> > 
> > When I compile without dpdk enabled, I now get:
> > 
> >     ../lib/netdev-vport.c:31:10: fatal error: 'rte_config.h' file not
> > found
> >     ../lib/netdev-native-tnl.c:35:10: fatal error: 'rte_config.h' file not
> > found "sparse" complains:
> > 
> > ../lib/netdev-vport.h:40:22: warning: symbol 'spi_map' was not declared.
> > Should it be static?
> 
> Hi Ben, thanks for looking at this, I flagged that compilation fails without DPDK enabled in the cover letter (I know, a big no no, I didn't expect this code to be upstreamed in its current form so I thought flagging it as known in the cover and keeping it as RFC would be ok. 
> 
> For the purpose of this RFC my aim was to give people something to functionally test with, and hopefully gather opinions on issues such as the acinclude build steps, dependency on external libraries etc. as well as the overall design.
> 
> Any feedback you have as regards design or changes is more than welcome as I expect a few more RFC revisions before nailing something concrete down.

OK, I understand now.
diff mbox

Patch

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;
 }