numam-dpdk/examples/ipsec-secgw/esp.c
Anoob Joseph 0ccfd14bc1 examples/ipsec-secgw: support inline protocol
Adding support for inline protocol processing

In ingress side, application will receive regular IP packets, without
any IPsec related info. Application will do a selector check (SP-SA
check) by making use of the metadata from the packet. The
device-specific metadata in mbuf would aid in determing the security
session which processed the packet.

In egress side, the plain packet would be submitted to the driver. The
packet will have optional metadata, which could be used to identify the
security session associated with the packet.

Signed-off-by: Anoob Joseph <anoob.joseph@caviumnetworks.com>
Reviewed-by: Akhil Goyal <akhil.goyal@nxp.com>
2018-01-20 16:10:20 +01:00

462 lines
12 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2016-2017 Intel Corporation
*/
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <fcntl.h>
#include <unistd.h>
#include <rte_common.h>
#include <rte_crypto.h>
#include <rte_cryptodev.h>
#include <rte_random.h>
#include "ipsec.h"
#include "esp.h"
#include "ipip.h"
int
esp_inbound(struct rte_mbuf *m, struct ipsec_sa *sa,
struct rte_crypto_op *cop)
{
struct ip *ip4;
struct rte_crypto_sym_op *sym_cop;
int32_t payload_len, ip_hdr_len;
RTE_ASSERT(sa != NULL);
if (sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO)
return 0;
RTE_ASSERT(m != NULL);
RTE_ASSERT(cop != NULL);
ip4 = rte_pktmbuf_mtod(m, struct ip *);
if (likely(ip4->ip_v == IPVERSION))
ip_hdr_len = ip4->ip_hl * 4;
else if (ip4->ip_v == IP6_VERSION)
/* XXX No option headers supported */
ip_hdr_len = sizeof(struct ip6_hdr);
else {
RTE_LOG(ERR, IPSEC_ESP, "invalid IP packet type %d\n",
ip4->ip_v);
return -EINVAL;
}
payload_len = rte_pktmbuf_pkt_len(m) - ip_hdr_len -
sizeof(struct esp_hdr) - sa->iv_len - sa->digest_len;
if ((payload_len & (sa->block_size - 1)) || (payload_len <= 0)) {
RTE_LOG_DP(DEBUG, IPSEC_ESP, "payload %d not multiple of %u\n",
payload_len, sa->block_size);
return -EINVAL;
}
sym_cop = get_sym_cop(cop);
sym_cop->m_src = m;
if (sa->aead_algo == RTE_CRYPTO_AEAD_AES_GCM) {
sym_cop->aead.data.offset = ip_hdr_len + sizeof(struct esp_hdr) +
sa->iv_len;
sym_cop->aead.data.length = payload_len;
struct cnt_blk *icb;
uint8_t *aad;
uint8_t *iv = RTE_PTR_ADD(ip4, ip_hdr_len + sizeof(struct esp_hdr));
icb = get_cnt_blk(m);
icb->salt = sa->salt;
memcpy(&icb->iv, iv, 8);
icb->cnt = rte_cpu_to_be_32(1);
aad = get_aad(m);
memcpy(aad, iv - sizeof(struct esp_hdr), 8);
sym_cop->aead.aad.data = aad;
sym_cop->aead.aad.phys_addr = rte_pktmbuf_iova_offset(m,
aad - rte_pktmbuf_mtod(m, uint8_t *));
sym_cop->aead.digest.data = rte_pktmbuf_mtod_offset(m, void*,
rte_pktmbuf_pkt_len(m) - sa->digest_len);
sym_cop->aead.digest.phys_addr = rte_pktmbuf_iova_offset(m,
rte_pktmbuf_pkt_len(m) - sa->digest_len);
} else {
sym_cop->cipher.data.offset = ip_hdr_len + sizeof(struct esp_hdr) +
sa->iv_len;
sym_cop->cipher.data.length = payload_len;
struct cnt_blk *icb;
uint8_t *iv = RTE_PTR_ADD(ip4, ip_hdr_len + sizeof(struct esp_hdr));
uint8_t *iv_ptr = rte_crypto_op_ctod_offset(cop,
uint8_t *, IV_OFFSET);
switch (sa->cipher_algo) {
case RTE_CRYPTO_CIPHER_NULL:
case RTE_CRYPTO_CIPHER_AES_CBC:
/* Copy IV at the end of crypto operation */
rte_memcpy(iv_ptr, iv, sa->iv_len);
break;
case RTE_CRYPTO_CIPHER_AES_CTR:
icb = get_cnt_blk(m);
icb->salt = sa->salt;
memcpy(&icb->iv, iv, 8);
icb->cnt = rte_cpu_to_be_32(1);
break;
default:
RTE_LOG(ERR, IPSEC_ESP, "unsupported cipher algorithm %u\n",
sa->cipher_algo);
return -EINVAL;
}
switch (sa->auth_algo) {
case RTE_CRYPTO_AUTH_NULL:
case RTE_CRYPTO_AUTH_SHA1_HMAC:
case RTE_CRYPTO_AUTH_SHA256_HMAC:
sym_cop->auth.data.offset = ip_hdr_len;
sym_cop->auth.data.length = sizeof(struct esp_hdr) +
sa->iv_len + payload_len;
break;
default:
RTE_LOG(ERR, IPSEC_ESP, "unsupported auth algorithm %u\n",
sa->auth_algo);
return -EINVAL;
}
sym_cop->auth.digest.data = rte_pktmbuf_mtod_offset(m, void*,
rte_pktmbuf_pkt_len(m) - sa->digest_len);
sym_cop->auth.digest.phys_addr = rte_pktmbuf_iova_offset(m,
rte_pktmbuf_pkt_len(m) - sa->digest_len);
}
return 0;
}
int
esp_inbound_post(struct rte_mbuf *m, struct ipsec_sa *sa,
struct rte_crypto_op *cop)
{
struct ip *ip4, *ip;
struct ip6_hdr *ip6;
uint8_t *nexthdr, *pad_len;
uint8_t *padding;
uint16_t i;
RTE_ASSERT(m != NULL);
RTE_ASSERT(sa != NULL);
RTE_ASSERT(cop != NULL);
if ((sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL) ||
(sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO)) {
if (m->ol_flags & PKT_RX_SEC_OFFLOAD) {
if (m->ol_flags & PKT_RX_SEC_OFFLOAD_FAILED)
cop->status = RTE_CRYPTO_OP_STATUS_ERROR;
else
cop->status = RTE_CRYPTO_OP_STATUS_SUCCESS;
} else
cop->status = RTE_CRYPTO_OP_STATUS_NOT_PROCESSED;
}
if (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
RTE_LOG(ERR, IPSEC_ESP, "failed crypto op\n");
return -1;
}
if (sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO &&
sa->ol_flags & RTE_SECURITY_RX_HW_TRAILER_OFFLOAD) {
nexthdr = &m->inner_esp_next_proto;
} else {
nexthdr = rte_pktmbuf_mtod_offset(m, uint8_t*,
rte_pktmbuf_pkt_len(m) - sa->digest_len - 1);
pad_len = nexthdr - 1;
padding = pad_len - *pad_len;
for (i = 0; i < *pad_len; i++) {
if (padding[i] != i + 1) {
RTE_LOG(ERR, IPSEC_ESP, "invalid padding\n");
return -EINVAL;
}
}
if (rte_pktmbuf_trim(m, *pad_len + 2 + sa->digest_len)) {
RTE_LOG(ERR, IPSEC_ESP,
"failed to remove pad_len + digest\n");
return -EINVAL;
}
}
if (unlikely(sa->flags == TRANSPORT)) {
ip = rte_pktmbuf_mtod(m, struct ip *);
ip4 = (struct ip *)rte_pktmbuf_adj(m,
sizeof(struct esp_hdr) + sa->iv_len);
if (likely(ip->ip_v == IPVERSION)) {
memmove(ip4, ip, ip->ip_hl * 4);
ip4->ip_p = *nexthdr;
ip4->ip_len = htons(rte_pktmbuf_data_len(m));
} else {
ip6 = (struct ip6_hdr *)ip4;
/* XXX No option headers supported */
memmove(ip6, ip, sizeof(struct ip6_hdr));
ip6->ip6_nxt = *nexthdr;
ip6->ip6_plen = htons(rte_pktmbuf_data_len(m) -
sizeof(struct ip6_hdr));
}
} else
ipip_inbound(m, sizeof(struct esp_hdr) + sa->iv_len);
return 0;
}
int
esp_outbound(struct rte_mbuf *m, struct ipsec_sa *sa,
struct rte_crypto_op *cop)
{
struct ip *ip4;
struct ip6_hdr *ip6;
struct esp_hdr *esp = NULL;
uint8_t *padding = NULL, *new_ip, nlp;
struct rte_crypto_sym_op *sym_cop;
int32_t i;
uint16_t pad_payload_len, pad_len, ip_hdr_len;
RTE_ASSERT(m != NULL);
RTE_ASSERT(sa != NULL);
ip_hdr_len = 0;
ip4 = rte_pktmbuf_mtod(m, struct ip *);
if (likely(ip4->ip_v == IPVERSION)) {
if (unlikely(sa->flags == TRANSPORT)) {
ip_hdr_len = ip4->ip_hl * 4;
nlp = ip4->ip_p;
} else
nlp = IPPROTO_IPIP;
} else if (ip4->ip_v == IP6_VERSION) {
if (unlikely(sa->flags == TRANSPORT)) {
/* XXX No option headers supported */
ip_hdr_len = sizeof(struct ip6_hdr);
ip6 = (struct ip6_hdr *)ip4;
nlp = ip6->ip6_nxt;
} else
nlp = IPPROTO_IPV6;
} else {
RTE_LOG(ERR, IPSEC_ESP, "invalid IP packet type %d\n",
ip4->ip_v);
return -EINVAL;
}
/* Padded payload length */
pad_payload_len = RTE_ALIGN_CEIL(rte_pktmbuf_pkt_len(m) -
ip_hdr_len + 2, sa->block_size);
pad_len = pad_payload_len + ip_hdr_len - rte_pktmbuf_pkt_len(m);
RTE_ASSERT(sa->flags == IP4_TUNNEL || sa->flags == IP6_TUNNEL ||
sa->flags == TRANSPORT);
if (likely(sa->flags == IP4_TUNNEL))
ip_hdr_len = sizeof(struct ip);
else if (sa->flags == IP6_TUNNEL)
ip_hdr_len = sizeof(struct ip6_hdr);
else if (sa->flags != TRANSPORT) {
RTE_LOG(ERR, IPSEC_ESP, "Unsupported SA flags: 0x%x\n",
sa->flags);
return -EINVAL;
}
/* Check maximum packet size */
if (unlikely(ip_hdr_len + sizeof(struct esp_hdr) + sa->iv_len +
pad_payload_len + sa->digest_len > IP_MAXPACKET)) {
RTE_LOG(ERR, IPSEC_ESP, "ipsec packet is too big\n");
return -EINVAL;
}
/* Add trailer padding if it is not constructed by HW */
if (sa->type != RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO ||
(sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO &&
!(sa->ol_flags & RTE_SECURITY_TX_HW_TRAILER_OFFLOAD))) {
padding = (uint8_t *)rte_pktmbuf_append(m, pad_len +
sa->digest_len);
if (unlikely(padding == NULL)) {
RTE_LOG(ERR, IPSEC_ESP,
"not enough mbuf trailing space\n");
return -ENOSPC;
}
rte_prefetch0(padding);
}
switch (sa->flags) {
case IP4_TUNNEL:
ip4 = ip4ip_outbound(m, sizeof(struct esp_hdr) + sa->iv_len,
&sa->src, &sa->dst);
esp = (struct esp_hdr *)(ip4 + 1);
break;
case IP6_TUNNEL:
ip6 = ip6ip_outbound(m, sizeof(struct esp_hdr) + sa->iv_len,
&sa->src, &sa->dst);
esp = (struct esp_hdr *)(ip6 + 1);
break;
case TRANSPORT:
new_ip = (uint8_t *)rte_pktmbuf_prepend(m,
sizeof(struct esp_hdr) + sa->iv_len);
memmove(new_ip, ip4, ip_hdr_len);
esp = (struct esp_hdr *)(new_ip + ip_hdr_len);
ip4 = (struct ip *)new_ip;
if (likely(ip4->ip_v == IPVERSION)) {
ip4->ip_p = IPPROTO_ESP;
ip4->ip_len = htons(rte_pktmbuf_data_len(m));
} else {
ip6 = (struct ip6_hdr *)new_ip;
ip6->ip6_nxt = IPPROTO_ESP;
ip6->ip6_plen = htons(rte_pktmbuf_data_len(m) -
sizeof(struct ip6_hdr));
}
}
sa->seq++;
esp->spi = rte_cpu_to_be_32(sa->spi);
esp->seq = rte_cpu_to_be_32((uint32_t)sa->seq);
/* set iv */
uint64_t *iv = (uint64_t *)(esp + 1);
if (sa->aead_algo == RTE_CRYPTO_AEAD_AES_GCM) {
*iv = rte_cpu_to_be_64(sa->seq);
} else {
switch (sa->cipher_algo) {
case RTE_CRYPTO_CIPHER_NULL:
case RTE_CRYPTO_CIPHER_AES_CBC:
memset(iv, 0, sa->iv_len);
break;
case RTE_CRYPTO_CIPHER_AES_CTR:
*iv = rte_cpu_to_be_64(sa->seq);
break;
default:
RTE_LOG(ERR, IPSEC_ESP,
"unsupported cipher algorithm %u\n",
sa->cipher_algo);
return -EINVAL;
}
}
if (sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) {
if (sa->ol_flags & RTE_SECURITY_TX_HW_TRAILER_OFFLOAD) {
/* Set the inner esp next protocol for HW trailer */
m->inner_esp_next_proto = nlp;
m->packet_type |= RTE_PTYPE_TUNNEL_ESP;
} else {
padding[pad_len - 2] = pad_len - 2;
padding[pad_len - 1] = nlp;
}
goto done;
}
RTE_ASSERT(cop != NULL);
sym_cop = get_sym_cop(cop);
sym_cop->m_src = m;
if (sa->aead_algo == RTE_CRYPTO_AEAD_AES_GCM) {
uint8_t *aad;
sym_cop->aead.data.offset = ip_hdr_len +
sizeof(struct esp_hdr) + sa->iv_len;
sym_cop->aead.data.length = pad_payload_len;
/* Fill pad_len using default sequential scheme */
for (i = 0; i < pad_len - 2; i++)
padding[i] = i + 1;
padding[pad_len - 2] = pad_len - 2;
padding[pad_len - 1] = nlp;
struct cnt_blk *icb = get_cnt_blk(m);
icb->salt = sa->salt;
icb->iv = rte_cpu_to_be_64(sa->seq);
icb->cnt = rte_cpu_to_be_32(1);
aad = get_aad(m);
memcpy(aad, esp, 8);
sym_cop->aead.aad.data = aad;
sym_cop->aead.aad.phys_addr = rte_pktmbuf_iova_offset(m,
aad - rte_pktmbuf_mtod(m, uint8_t *));
sym_cop->aead.digest.data = rte_pktmbuf_mtod_offset(m, uint8_t *,
rte_pktmbuf_pkt_len(m) - sa->digest_len);
sym_cop->aead.digest.phys_addr = rte_pktmbuf_iova_offset(m,
rte_pktmbuf_pkt_len(m) - sa->digest_len);
} else {
switch (sa->cipher_algo) {
case RTE_CRYPTO_CIPHER_NULL:
case RTE_CRYPTO_CIPHER_AES_CBC:
sym_cop->cipher.data.offset = ip_hdr_len +
sizeof(struct esp_hdr);
sym_cop->cipher.data.length = pad_payload_len + sa->iv_len;
break;
case RTE_CRYPTO_CIPHER_AES_CTR:
sym_cop->cipher.data.offset = ip_hdr_len +
sizeof(struct esp_hdr) + sa->iv_len;
sym_cop->cipher.data.length = pad_payload_len;
break;
default:
RTE_LOG(ERR, IPSEC_ESP, "unsupported cipher algorithm %u\n",
sa->cipher_algo);
return -EINVAL;
}
/* Fill pad_len using default sequential scheme */
for (i = 0; i < pad_len - 2; i++)
padding[i] = i + 1;
padding[pad_len - 2] = pad_len - 2;
padding[pad_len - 1] = nlp;
struct cnt_blk *icb = get_cnt_blk(m);
icb->salt = sa->salt;
icb->iv = rte_cpu_to_be_64(sa->seq);
icb->cnt = rte_cpu_to_be_32(1);
switch (sa->auth_algo) {
case RTE_CRYPTO_AUTH_NULL:
case RTE_CRYPTO_AUTH_SHA1_HMAC:
case RTE_CRYPTO_AUTH_SHA256_HMAC:
sym_cop->auth.data.offset = ip_hdr_len;
sym_cop->auth.data.length = sizeof(struct esp_hdr) +
sa->iv_len + pad_payload_len;
break;
default:
RTE_LOG(ERR, IPSEC_ESP, "unsupported auth algorithm %u\n",
sa->auth_algo);
return -EINVAL;
}
sym_cop->auth.digest.data = rte_pktmbuf_mtod_offset(m, uint8_t *,
rte_pktmbuf_pkt_len(m) - sa->digest_len);
sym_cop->auth.digest.phys_addr = rte_pktmbuf_iova_offset(m,
rte_pktmbuf_pkt_len(m) - sa->digest_len);
}
done:
return 0;
}
int
esp_outbound_post(struct rte_mbuf *m,
struct ipsec_sa *sa,
struct rte_crypto_op *cop)
{
RTE_ASSERT(m != NULL);
RTE_ASSERT(sa != NULL);
if ((sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL) ||
(sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO)) {
m->ol_flags |= PKT_TX_SEC_OFFLOAD;
} else {
RTE_ASSERT(cop != NULL);
if (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
RTE_LOG(ERR, IPSEC_ESP, "Failed crypto op\n");
return -1;
}
}
return 0;
}