agent-enviroments/builder/libs/seastar/dpdk/lib/pipeline/rte_swx_ipsec.c
2024-09-10 17:06:08 +03:00

1826 lines
42 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2022 Intel Corporation
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
#include <rte_common.h>
#include <rte_ip.h>
#include <rte_tailq.h>
#include <rte_eal_memconfig.h>
#include <rte_ring.h>
#include <rte_mbuf.h>
#include <rte_cryptodev.h>
#include <rte_ipsec.h>
#include "rte_swx_ipsec.h"
#ifndef RTE_SWX_IPSEC_HUGE_PAGES_DISABLE
#include <rte_malloc.h>
static void *
env_calloc(size_t size, size_t alignment, int numa_node)
{
return rte_zmalloc_socket(NULL, size, alignment, numa_node);
}
static void
env_free(void *start, size_t size __rte_unused)
{
rte_free(start);
}
#else
#include <numa.h>
static void *
env_calloc(size_t size, size_t alignment __rte_unused, int numa_node)
{
void *start;
if (numa_available() == -1)
return NULL;
start = numa_alloc_onnode(size, numa_node);
if (!start)
return NULL;
memset(start, 0, size);
return start;
}
static void
env_free(void *start, size_t size)
{
if ((numa_available() == -1) || !start)
return;
numa_free(start, size);
}
#endif
#ifndef RTE_SWX_IPSEC_POOL_CACHE_SIZE
#define RTE_SWX_IPSEC_POOL_CACHE_SIZE 256
#endif
/* The two crypto device mempools have their size set to the number of SAs. The mempool API requires
* the mempool size to be at least 1.5 times the size of the mempool cache.
*/
#define N_SA_MIN (RTE_SWX_IPSEC_POOL_CACHE_SIZE * 1.5)
struct ipsec_sa {
struct rte_ipsec_session s;
int valid;
};
struct ipsec_pkts_in {
struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
struct ipsec_sa *sa[RTE_SWX_IPSEC_BURST_SIZE_MAX];
struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
struct rte_crypto_op *group_cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
uint32_t n_cops;
};
struct ipsec_pkts_out {
struct rte_crypto_op *cops[RTE_SWX_IPSEC_BURST_SIZE_MAX];
struct rte_mbuf *group_pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
struct rte_ipsec_group groups[RTE_SWX_IPSEC_BURST_SIZE_MAX];
struct rte_mbuf *pkts[RTE_SWX_IPSEC_BURST_SIZE_MAX];
uint32_t n_pkts;
};
struct rte_swx_ipsec {
/*
* Parameters.
*/
/* IPsec instance name. */
char name[RTE_SWX_IPSEC_NAME_SIZE];
/* Input packet queue. */
struct rte_ring *ring_in;
/* Output packet queue. */
struct rte_ring *ring_out;
/* Crypto device ID. */
uint8_t dev_id;
/* Crypto device queue pair ID. */
uint16_t qp_id;
/* Burst sizes. */
struct rte_swx_ipsec_burst_size bsz;
/* SA table size. */
size_t n_sa_max;
/*
* Internals.
*/
/* Crypto device buffer pool for sessions. */
struct rte_mempool *mp_session;
/* Pre-crypto packets. */
struct ipsec_pkts_in in;
/* Post-crypto packets. */
struct ipsec_pkts_out out;
/* Crypto device enqueue threshold. */
uint32_t crypto_wr_threshold;
/* Packets currently under crypto device processing. */
uint32_t n_pkts_crypto;
/* List of free SADB positions. */
uint32_t *sa_free_id;
/* Number of elements in the SADB list of free positions. */
size_t n_sa_free_id;
/* Allocated memory total size in bytes. */
size_t total_size;
/* Flag for registration to the global list of instances. */
int registered;
/*
* Table memory.
*/
uint8_t memory[] __rte_cache_aligned;
};
static inline struct ipsec_sa *
ipsec_sa_get(struct rte_swx_ipsec *ipsec, uint32_t sa_id)
{
struct ipsec_sa *sadb = (struct ipsec_sa *)ipsec->memory;
return &sadb[sa_id & (ipsec->n_sa_max - 1)];
}
/* Global list of instances. */
TAILQ_HEAD(rte_swx_ipsec_list, rte_tailq_entry);
static struct rte_tailq_elem rte_swx_ipsec_tailq = {
.name = "RTE_SWX_IPSEC",
};
EAL_REGISTER_TAILQ(rte_swx_ipsec_tailq)
struct rte_swx_ipsec *
rte_swx_ipsec_find(const char *name)
{
struct rte_swx_ipsec_list *ipsec_list;
struct rte_tailq_entry *te = NULL;
if (!name ||
!name[0] ||
(strnlen(name, RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE))
return NULL;
ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
rte_mcfg_tailq_read_lock();
TAILQ_FOREACH(te, ipsec_list, next) {
struct rte_swx_ipsec *ipsec = (struct rte_swx_ipsec *)te->data;
if (!strncmp(name, ipsec->name, sizeof(ipsec->name))) {
rte_mcfg_tailq_read_unlock();
return ipsec;
}
}
rte_mcfg_tailq_read_unlock();
return NULL;
}
static int
ipsec_register(struct rte_swx_ipsec *ipsec)
{
struct rte_swx_ipsec_list *ipsec_list;
struct rte_tailq_entry *te = NULL;
ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
rte_mcfg_tailq_write_lock();
TAILQ_FOREACH(te, ipsec_list, next) {
struct rte_swx_ipsec *elem = (struct rte_swx_ipsec *)te->data;
if (!strncmp(ipsec->name, elem->name, sizeof(ipsec->name))) {
rte_mcfg_tailq_write_unlock();
return -EEXIST;
}
}
te = calloc(1, sizeof(struct rte_tailq_entry));
if (!te) {
rte_mcfg_tailq_write_unlock();
return -ENOMEM;
}
te->data = (void *)ipsec;
TAILQ_INSERT_TAIL(ipsec_list, te, next);
rte_mcfg_tailq_write_unlock();
return 0;
}
static void
ipsec_unregister(struct rte_swx_ipsec *ipsec)
{
struct rte_swx_ipsec_list *ipsec_list;
struct rte_tailq_entry *te = NULL;
ipsec_list = RTE_TAILQ_CAST(rte_swx_ipsec_tailq.head, rte_swx_ipsec_list);
rte_mcfg_tailq_write_lock();
TAILQ_FOREACH(te, ipsec_list, next) {
if (te->data == (void *)ipsec) {
TAILQ_REMOVE(ipsec_list, te, next);
rte_mcfg_tailq_write_unlock();
free(te);
return;
}
}
rte_mcfg_tailq_write_unlock();
}
static void
ipsec_session_free(struct rte_swx_ipsec *ipsec, struct rte_ipsec_session *s);
void
rte_swx_ipsec_free(struct rte_swx_ipsec *ipsec)
{
size_t i;
if (!ipsec)
return;
/* Remove the current instance from the global list. */
if (ipsec->registered)
ipsec_unregister(ipsec);
/* SADB. */
for (i = 0; i < ipsec->n_sa_max; i++) {
struct ipsec_sa *sa = ipsec_sa_get(ipsec, i);
if (!sa->valid)
continue;
/* SA session. */
ipsec_session_free(ipsec, &sa->s);
}
/* Crypto device buffer pools. */
rte_mempool_free(ipsec->mp_session);
/* IPsec object memory. */
env_free(ipsec, ipsec->total_size);
}
int
rte_swx_ipsec_create(struct rte_swx_ipsec **ipsec_out,
const char *name,
struct rte_swx_ipsec_params *params,
int numa_node)
{
char resource_name[RTE_SWX_IPSEC_NAME_SIZE];
struct rte_swx_ipsec *ipsec = NULL;
struct rte_ring *ring_in, *ring_out;
struct rte_cryptodev_info dev_info;
size_t n_sa_max, sadb_offset, sadb_size, sa_free_id_offset, sa_free_id_size, total_size, i;
uint32_t dev_session_size;
int dev_id, status = 0;
/* Check input parameters. */
if (!ipsec_out ||
!name ||
!name[0] ||
(strnlen((name), RTE_SWX_IPSEC_NAME_SIZE) == RTE_SWX_IPSEC_NAME_SIZE) ||
!params ||
(params->bsz.ring_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
(params->bsz.ring_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
(params->bsz.crypto_wr > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
(params->bsz.crypto_rd > RTE_SWX_IPSEC_BURST_SIZE_MAX) ||
!params->n_sa_max) {
status = -EINVAL;
goto error;
}
ring_in = rte_ring_lookup(params->ring_in_name);
if (!ring_in) {
status = -EINVAL;
goto error;
}
ring_out = rte_ring_lookup(params->ring_out_name);
if (!ring_out) {
status = -EINVAL;
goto error;
}
dev_id = rte_cryptodev_get_dev_id(params->crypto_dev_name);
if (dev_id == -1) {
status = -EINVAL;
goto error;
}
rte_cryptodev_info_get(dev_id, &dev_info);
if (params->crypto_dev_queue_pair_id >= dev_info.max_nb_queue_pairs) {
status = -EINVAL;
goto error;
}
/* Memory allocation. */
n_sa_max = rte_align64pow2(RTE_MAX(params->n_sa_max, N_SA_MIN));
sadb_offset = sizeof(struct rte_swx_ipsec);
sadb_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(struct ipsec_sa));
sa_free_id_offset = sadb_offset + sadb_size;
sa_free_id_size = RTE_CACHE_LINE_ROUNDUP(n_sa_max * sizeof(uint32_t));
total_size = sa_free_id_offset + sa_free_id_size;
ipsec = env_calloc(total_size, RTE_CACHE_LINE_SIZE, numa_node);
if (!ipsec) {
status = -ENOMEM;
goto error;
}
/* Initialization. */
strcpy(ipsec->name, name);
ipsec->ring_in = ring_in;
ipsec->ring_out = ring_out;
ipsec->dev_id = (uint8_t)dev_id;
ipsec->qp_id = params->crypto_dev_queue_pair_id;
memcpy(&ipsec->bsz, &params->bsz, sizeof(struct rte_swx_ipsec_burst_size));
ipsec->n_sa_max = n_sa_max;
ipsec->crypto_wr_threshold = params->bsz.crypto_wr * 3 / 4;
ipsec->sa_free_id = (uint32_t *)&ipsec->memory[sa_free_id_offset];
for (i = 0; i < n_sa_max; i++)
ipsec->sa_free_id[i] = n_sa_max - 1 - i;
ipsec->n_sa_free_id = n_sa_max;
ipsec->total_size = total_size;
/* Crypto device memory pools. */
dev_session_size = rte_cryptodev_sym_get_private_session_size((uint8_t)dev_id);
snprintf(resource_name, sizeof(resource_name), "%s_mp", name);
ipsec->mp_session = rte_cryptodev_sym_session_pool_create(resource_name,
n_sa_max, /* number of pool elements */
dev_session_size, /* pool element size */
RTE_SWX_IPSEC_POOL_CACHE_SIZE, /* pool cache size */
0, /* pool element private data size */
numa_node);
if (!ipsec->mp_session) {
status = -ENOMEM;
goto error;
}
/* Add the current instance to the global list. */
status = ipsec_register(ipsec);
if (status)
goto error;
ipsec->registered = 1;
*ipsec_out = ipsec;
return 0;
error:
rte_swx_ipsec_free(ipsec);
return status;
}
static inline int
ipsec_sa_group(struct rte_swx_ipsec *ipsec, int n_pkts)
{
struct ipsec_sa *sa;
struct rte_ipsec_group *g;
int n_groups, n_pkts_in_group, i;
sa = ipsec->in.sa[0];
g = &ipsec->in.groups[0];
g->id.ptr = sa;
g->m = &ipsec->in.pkts[0];
n_pkts_in_group = 1;
n_groups = 1;
for (i = 1; i < n_pkts; i++) {
struct ipsec_sa *sa_new = ipsec->in.sa[i];
/* Same SA => Add the current pkt to the same group. */
if (sa_new == sa) {
n_pkts_in_group++;
continue;
}
/* Different SA => Close the current group & add the current pkt to a new group. */
g->cnt = n_pkts_in_group;
sa = sa_new;
g++;
g->id.ptr = sa;
g->m = &ipsec->in.pkts[i];
n_pkts_in_group = 1;
n_groups++;
}
/* Close the last group. */
g->cnt = n_pkts_in_group;
return n_groups;
}
static inline void
ipsec_crypto_enqueue(struct rte_swx_ipsec *ipsec, uint16_t n_cops)
{
struct rte_crypto_op **dst0 = ipsec->in.cops, **dst;
struct rte_crypto_op **src = ipsec->in.group_cops;
uint32_t n_pkts_crypto = ipsec->n_pkts_crypto;
uint32_t n_dst = ipsec->in.n_cops;
uint32_t n_dst_max = ipsec->bsz.crypto_wr;
uint32_t n_dst_avail = n_dst_max - n_dst;
uint32_t n_src = n_cops;
uint32_t i;
dst = &dst0[n_dst];
/* Shortcut: If no elements in DST and enough elements in SRC, then simply use SRC directly
* instead of moving the SRC to DST first and then using DST.
*/
if (!n_dst && n_src >= ipsec->crypto_wr_threshold) {
uint16_t n_ok;
n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, src, n_src);
ipsec->n_pkts_crypto = n_pkts_crypto + n_ok;
for (i = n_ok; i < n_src; i++) {
struct rte_crypto_op *cop = src[i];
struct rte_mbuf *m = cop->sym->m_src;
rte_pktmbuf_free(m);
}
return;
}
/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
for ( ; n_src >= n_dst_avail; ) {
uint32_t n_ok;
/* Move from SRC to DST. */
for (i = 0; i < n_dst_avail; i++)
*dst++ = *src++;
n_src -= n_dst_avail;
/* DST full: send burst from DST. */
n_ok = rte_cryptodev_enqueue_burst(ipsec->dev_id, ipsec->qp_id, dst0, n_dst_max);
n_pkts_crypto += n_ok;
for (i = n_ok ; i < n_dst_max; i++) {
struct rte_crypto_op *cop = dst0[i];
struct rte_mbuf *m = cop->sym->m_src;
rte_pktmbuf_free(m);
}
/* Next iteration. */
dst = dst0;
n_dst = 0;
n_dst_avail = n_dst_max;
}
ipsec->n_pkts_crypto = n_pkts_crypto;
/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
for (i = 0; i < n_src; i++)
*dst++ = *src++;
n_dst += n_src;
ipsec->in.n_cops = n_dst;
}
/**
* Packet buffer anatomy:
*
* +----------+---------+--------------------------------------------------------------------------+
* | Offset | Size | Description |
* | (Byte #) | (Bytes) | |
* +==========+=========+==========================================================================+
* | 0 | 128 | Meta-data: struct rte_mbuf. |
* | | | The buf_addr field points to the start of the packet section. |
* +----------+---------+--------------------------------------------------------------------------+
* | 128 | 128 | Meta-data: struct ipsec_mbuf (see below). |
* +----------+---------+--------------------------------------------------------------------------+
* | 256 | | Packet section. |
* | | | The first packet byte is placed at the offset indicated by the struct |
* | | | rte_mbuf::data_off field relative to the start of the packet section. |
* +----------+---------+--------------------------------------------------------------------------+
*/
struct ipsec_mbuf {
struct ipsec_sa *sa;
struct rte_crypto_op cop;
struct rte_crypto_sym_op sym_cop;
uint8_t buffer[32]; /* The crypto IV is placed here. */
};
/* Offset from the start of the struct ipsec_mbuf::cop where the crypto IV will be placed. */
#define IV_OFFSET (sizeof(struct rte_crypto_op) + sizeof(struct rte_crypto_sym_op))
#define META_LENGTH sizeof(struct rte_swx_ipsec_input_packet_metadata)
static inline void
rte_swx_ipsec_pre_crypto(struct rte_swx_ipsec *ipsec)
{
int n_pkts, n_groups, i;
/* Read packets from the input ring. */
n_pkts = rte_ring_sc_dequeue_burst(ipsec->ring_in,
(void **)ipsec->in.pkts,
ipsec->bsz.ring_rd,
NULL);
if (!n_pkts)
return;
/* Get the SA for each packet. */
for (i = 0; i < n_pkts; i++) {
struct rte_mbuf *m = ipsec->in.pkts[i];
struct rte_swx_ipsec_input_packet_metadata *meta;
struct rte_ipv4_hdr *ipv4_hdr;
uint32_t sa_id;
meta = rte_pktmbuf_mtod(m, struct rte_swx_ipsec_input_packet_metadata *);
ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, META_LENGTH);
/* Read the SA ID from the IPsec meta-data placed at the front of the IP packet. */
sa_id = ntohl(meta->sa_id);
/* Consume the IPsec meta-data. */
m->data_off += META_LENGTH;
m->data_len -= META_LENGTH;
m->pkt_len -= META_LENGTH;
/* Set the fields required by the IPsec library. */
m->l2_len = 0;
m->l3_len = (ipv4_hdr->version_ihl >> 4 == 4) ?
sizeof(struct rte_ipv4_hdr) :
sizeof(struct rte_ipv6_hdr);
/* Get the SA. */
ipsec->in.sa[i] = ipsec_sa_get(ipsec, sa_id);
}
/* Group packets that share the same SA. */
n_groups = ipsec_sa_group(ipsec, n_pkts);
/* Write each group of packets sharing the same SA to the crypto device. */
for (i = 0; i < n_groups; i++) {
struct rte_ipsec_group *g = &ipsec->in.groups[i];
struct ipsec_sa *sa = g->id.ptr;
struct rte_ipsec_session *s = &sa->s;
uint32_t j;
uint16_t n_pkts_ok;
/* Prepare the crypto ops for the current group. */
for (j = 0; j < g->cnt; j++) {
struct rte_mbuf *m = g->m[j];
struct ipsec_mbuf *priv = rte_mbuf_to_priv(m);
priv->sa = sa;
ipsec->in.group_cops[j] = &priv->cop;
}
n_pkts_ok = rte_ipsec_pkt_crypto_prepare(s, g->m, ipsec->in.group_cops, g->cnt);
for (j = n_pkts_ok; j < g->cnt; j++) {
struct rte_mbuf *m = g->m[j];
rte_pktmbuf_free(m);
}
/* Write the crypto ops of the current group to the crypto device. */
ipsec_crypto_enqueue(ipsec, n_pkts_ok);
}
}
static inline void
ipsec_ring_enqueue(struct rte_swx_ipsec *ipsec, struct rte_ipsec_group *g, uint32_t n_pkts)
{
struct rte_mbuf **dst0 = ipsec->out.pkts, **dst;
struct rte_mbuf **src = g->m;
uint32_t n_dst = ipsec->out.n_pkts;
uint32_t n_dst_max = ipsec->bsz.ring_wr;
uint32_t n_dst_avail = n_dst_max - n_dst;
uint32_t n_src = n_pkts;
uint32_t i;
dst = &dst0[n_dst];
/* Move from SRC to DST. Every time DST gets full, send burst from DST. */
for ( ; n_src >= n_dst_avail; ) {
uint32_t n_ok;
/* Move from SRC to DST. */
for (i = 0; i < n_dst_avail; i++)
*dst++ = *src++;
n_src -= n_dst_avail;
/* DST full: send burst from DST. */
n_ok = rte_ring_sp_enqueue_burst(ipsec->ring_out, (void **)dst0, n_dst_max, NULL);
for (i = n_ok ; i < n_dst_max; i++) {
struct rte_mbuf *m = dst[i];
rte_pktmbuf_free(m);
}
/* Next iteration. */
dst = dst0;
n_dst = 0;
n_dst_avail = n_dst_max;
}
/* Move from SRC to DST. Not enough elements in SRC to get DST full. */
for (i = 0; i < n_src; i++)
*dst++ = *src++;
n_dst += n_src;
ipsec->out.n_pkts = n_dst;
}
static inline void
rte_swx_ipsec_post_crypto(struct rte_swx_ipsec *ipsec)
{
uint32_t n_pkts_crypto = ipsec->n_pkts_crypto, n_pkts, ng, i;
/* Read the crypto ops from the crypto device. */
if (!n_pkts_crypto)
return;
n_pkts = rte_cryptodev_dequeue_burst(ipsec->dev_id,
ipsec->qp_id,
ipsec->out.cops,
ipsec->bsz.crypto_rd);
if (!n_pkts)
return;
ipsec->n_pkts_crypto = n_pkts_crypto - n_pkts;
/* Group packets that share the same SA. */
ng = rte_ipsec_pkt_crypto_group((const struct rte_crypto_op **)(uintptr_t)ipsec->out.cops,
ipsec->out.group_pkts,
ipsec->out.groups,
n_pkts);
/* Perform post-crypto IPsec processing for each group of packets that share the same SA.
* Write each group of packets to the output ring.
*/
for (i = 0, n_pkts = 0; i < ng; i++) {
struct rte_ipsec_group *g = &ipsec->out.groups[i];
struct rte_ipsec_session *s = g->id.ptr;
uint32_t n_pkts_ok, j;
/* Perform post-crypto IPsec processing for the current group. */
n_pkts_ok = rte_ipsec_pkt_process(s, g->m, g->cnt);
for (j = n_pkts_ok; j < g->cnt; j++) {
struct rte_mbuf *m = g->m[j];
rte_pktmbuf_free(m);
}
/* Write the packets of the current group to the output ring. */
ipsec_ring_enqueue(ipsec, g, n_pkts_ok);
}
}
void
rte_swx_ipsec_run(struct rte_swx_ipsec *ipsec)
{
rte_swx_ipsec_pre_crypto(ipsec);
rte_swx_ipsec_post_crypto(ipsec);
}
/**
* IPsec Control Plane API
*/
struct cipher_alg {
const char *name;
enum rte_crypto_cipher_algorithm alg;
uint32_t iv_size;
uint32_t block_size;
uint32_t key_size;
};
struct auth_alg {
const char *name;
enum rte_crypto_auth_algorithm alg;
uint32_t iv_size;
uint32_t digest_size;
uint32_t key_size;
};
struct aead_alg {
const char *name;
enum rte_crypto_aead_algorithm alg;
uint32_t iv_size;
uint32_t block_size;
uint32_t digest_size;
uint32_t key_size;
uint32_t aad_size;
};
static struct cipher_alg cipher_algs[] = {
[0] = {
.name = "null",
.alg = RTE_CRYPTO_CIPHER_NULL,
.iv_size = 0,
.block_size = 4,
.key_size = 0,
},
[1] = {
.name = "aes-cbc-128",
.alg = RTE_CRYPTO_CIPHER_AES_CBC,
.iv_size = 16,
.block_size = 16,
.key_size = 16,
},
[2] = {
.name = "aes-cbc-192",
.alg = RTE_CRYPTO_CIPHER_AES_CBC,
.iv_size = 16,
.block_size = 16,
.key_size = 24,
},
[3] = {
.name = "aes-cbc-256",
.alg = RTE_CRYPTO_CIPHER_AES_CBC,
.iv_size = 16,
.block_size = 16,
.key_size = 32,
},
[4] = {
.name = "aes-ctr-128",
.alg = RTE_CRYPTO_CIPHER_AES_CTR,
.iv_size = 8,
.block_size = 4,
.key_size = 20,
},
[5] = {
.name = "aes-ctr-192",
.alg = RTE_CRYPTO_CIPHER_AES_CTR,
.iv_size = 16,
.block_size = 16,
.key_size = 28,
},
[6] = {
.name = "aes-ctr-256",
.alg = RTE_CRYPTO_CIPHER_AES_CTR,
.iv_size = 16,
.block_size = 16,
.key_size = 36,
},
[7] = {
.name = "3des-cbc",
.alg = RTE_CRYPTO_CIPHER_3DES_CBC,
.iv_size = 8,
.block_size = 8,
.key_size = 24,
},
[8] = {
.name = "des-cbc",
.alg = RTE_CRYPTO_CIPHER_DES_CBC,
.iv_size = 8,
.block_size = 8,
.key_size = 8,
},
};
static struct auth_alg auth_algs[] = {
[0] = {
.name = "null",
.alg = RTE_CRYPTO_AUTH_NULL,
.iv_size = 0,
.digest_size = 0,
.key_size = 0,
},
[1] = {
.name = "sha1-hmac",
.alg = RTE_CRYPTO_AUTH_SHA1_HMAC,
.iv_size = 0,
.digest_size = 12,
.key_size = 20,
},
[2] = {
.name = "sha256-hmac",
.alg = RTE_CRYPTO_AUTH_SHA256_HMAC,
.iv_size = 0,
.digest_size = 16,
.key_size = 32,
},
[3] = {
.name = "sha384-hmac",
.alg = RTE_CRYPTO_AUTH_SHA384_HMAC,
.iv_size = 0,
.digest_size = 24,
.key_size = 48,
},
[4] = {
.name = "sha512-hmac",
.alg = RTE_CRYPTO_AUTH_SHA512_HMAC,
.iv_size = 0,
.digest_size = 32,
.key_size = 64,
},
[5] = {
.name = "aes-gmac",
.alg = RTE_CRYPTO_AUTH_AES_GMAC,
.iv_size = 8,
.digest_size = 16,
.key_size = 20,
},
[6] = {
.name = "aes-xcbc-mac-96",
.alg = RTE_CRYPTO_AUTH_AES_XCBC_MAC,
.iv_size = 0,
.digest_size = 12,
.key_size = 16,
},
};
static struct aead_alg aead_algs[] = {
[0] = {
.name = "aes-gcm-128",
.alg = RTE_CRYPTO_AEAD_AES_GCM,
.iv_size = 8,
.block_size = 4,
.key_size = 20,
.digest_size = 16,
.aad_size = 8,
},
[1] = {
.name = "aes-gcm-192",
.alg = RTE_CRYPTO_AEAD_AES_GCM,
.iv_size = 8,
.block_size = 4,
.key_size = 28,
.digest_size = 16,
.aad_size = 8,
},
[2] = {
.name = "aes-gcm-256",
.alg = RTE_CRYPTO_AEAD_AES_GCM,
.iv_size = 8,
.block_size = 4,
.key_size = 36,
.digest_size = 16,
.aad_size = 8,
},
[3] = {
.name = "aes-ccm-128",
.alg = RTE_CRYPTO_AEAD_AES_CCM,
.iv_size = 8,
.block_size = 4,
.key_size = 20,
.digest_size = 16,
.aad_size = 8,
},
[4] = {
.name = "aes-ccm-192",
.alg = RTE_CRYPTO_AEAD_AES_CCM,
.iv_size = 8,
.block_size = 4,
.key_size = 28,
.digest_size = 16,
.aad_size = 8,
},
[5] = {
.name = "aes-ccm-256",
.alg = RTE_CRYPTO_AEAD_AES_CCM,
.iv_size = 8,
.block_size = 4,
.key_size = 36,
.digest_size = 16,
.aad_size = 8,
},
[6] = {
.name = "chacha20-poly1305",
.alg = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,
.iv_size = 12,
.block_size = 64,
.key_size = 36,
.digest_size = 16,
.aad_size = 8,
},
};
static struct cipher_alg *
cipher_alg_find(const char *name)
{
size_t i;
for (i = 0; i < RTE_DIM(cipher_algs); i++) {
struct cipher_alg *alg = &cipher_algs[i];
if (!strcmp(name, alg->name))
return alg;
}
return NULL;
}
static struct cipher_alg *
cipher_alg_find_by_id(enum rte_crypto_cipher_algorithm alg_id, uint32_t key_size)
{
size_t i;
for (i = 0; i < RTE_DIM(cipher_algs); i++) {
struct cipher_alg *alg = &cipher_algs[i];
if (alg->alg == alg_id && alg->key_size == key_size)
return alg;
}
return NULL;
}
static struct auth_alg *
auth_alg_find(const char *name)
{
size_t i;
for (i = 0; i < RTE_DIM(auth_algs); i++) {
struct auth_alg *alg = &auth_algs[i];
if (!strcmp(name, alg->name))
return alg;
}
return NULL;
}
static struct auth_alg *
auth_alg_find_by_id(enum rte_crypto_auth_algorithm alg_id, uint32_t key_size)
{
size_t i;
for (i = 0; i < RTE_DIM(auth_algs); i++) {
struct auth_alg *alg = &auth_algs[i];
if (alg->alg == alg_id && alg->key_size == key_size)
return alg;
}
return NULL;
}
static struct aead_alg *
aead_alg_find(const char *name)
{
size_t i;
for (i = 0; i < RTE_DIM(aead_algs); i++) {
struct aead_alg *alg = &aead_algs[i];
if (!strcmp(name, alg->name))
return alg;
}
return NULL;
}
static struct aead_alg *
aead_alg_find_by_id(enum rte_crypto_aead_algorithm alg_id, uint32_t key_size)
{
size_t i;
for (i = 0; i < RTE_DIM(aead_algs); i++) {
struct aead_alg *alg = &aead_algs[i];
if (alg->alg == alg_id && alg->key_size == key_size)
return alg;
}
return NULL;
}
static int
char_to_hex(char c, uint8_t *val)
{
if (c >= '0' && c <= '9') {
*val = c - '0';
return 0;
}
if (c >= 'A' && c <= 'F') {
*val = c - 'A' + 10;
return 0;
}
if (c >= 'a' && c <= 'f') {
*val = c - 'a' + 10;
return 0;
}
return -EINVAL;
}
static int
hex_string_parse(char *src, uint8_t *dst, uint32_t n_dst_bytes)
{
uint32_t i;
/* Check input arguments. */
if (!src || !src[0] || !dst || !n_dst_bytes)
return -EINVAL;
/* Skip any leading "0x" or "0X" in the src string. */
if ((src[0] == '0') && (src[1] == 'x' || src[1] == 'X'))
src += 2;
/* Convert each group of two hex characters in the src string to one byte in dst array. */
for (i = 0; i < n_dst_bytes; i++) {
uint8_t a, b;
int status;
status = char_to_hex(*src, &a);
if (status)
return status;
src++;
status = char_to_hex(*src, &b);
if (status)
return status;
src++;
dst[i] = a * 16 + b;
}
/* Check for the end of the src string. */
if (*src)
return -EINVAL;
return 0;
}
static int
token_is_comment(const char *token)
{
if ((token[0] == '#') ||
(token[0] == ';') ||
((token[0] == '/') && (token[1] == '/')))
return 1; /* TRUE. */
return 0; /* FALSE. */
}
#define MAX_TOKENS 64
#define CHECK(condition, msg) \
do { \
if (!(condition)) { \
if (errmsg) \
*errmsg = msg; \
goto error; \
} \
} while (0)
struct rte_swx_ipsec_sa_params *
rte_swx_ipsec_sa_read(struct rte_swx_ipsec *ipsec __rte_unused,
const char *string,
int *is_blank_or_comment,
const char **errmsg)
{
char *token_array[MAX_TOKENS], **t;
struct rte_swx_ipsec_sa_params *p = NULL;
char *s0 = NULL, *s;
uint32_t n_tokens = 0;
int blank_or_comment = 0;
/* Check input arguments. */
CHECK(string && string[0], "NULL input");
/* Memory allocation. */
s0 = strdup(string);
p = calloc(1, sizeof(struct rte_swx_ipsec_sa_params));
CHECK(s0 && p, "Not enough memory");
/* Parse the string into tokens. */
for (s = s0; ; ) {
char *token;
token = strtok_r(s, " \f\n\r\t\v", &s);
if (!token || token_is_comment(token))
break;
CHECK(n_tokens < RTE_DIM(token_array), "Too many tokens");
token_array[n_tokens] = token;
n_tokens++;
}
t = token_array;
if (!n_tokens) {
blank_or_comment = 1;
goto error;
}
/*
* Crypto operation.
*/
if (!strcmp(t[0], "encrypt"))
p->encrypt = 1;
else if (!strcmp(t[0], "decrypt"))
p->encrypt = 0;
else
CHECK(0, "Missing \"encrypt\"/\"decrypt\" keyword");
t++;
n_tokens--;
/*
* Crypto parameters.
*/
CHECK(n_tokens >= 2, "Not enough tokens");
if (!strcmp(t[0], "cipher")) {
struct cipher_alg *cipher_alg;
struct auth_alg *auth_alg;
uint32_t key_size;
p->crypto.is_aead = 0;
/* cipher. */
cipher_alg = cipher_alg_find(t[1]);
CHECK(cipher_alg, "Unsupported cipher algorithm");
key_size = cipher_alg->key_size;
p->crypto.cipher_auth.cipher.alg = cipher_alg->alg;
p->crypto.cipher_auth.cipher.key_size = key_size;
t += 2;
n_tokens -= 2;
if (key_size) {
int status;
CHECK(n_tokens >= 2, "Not enough tokens");
CHECK(!strcmp(t[0], "key"), "Missing cipher \"key\" keyword");
CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.cipher.key),
"Cipher algorithm key too big");
status = hex_string_parse(t[1], p->crypto.cipher_auth.cipher.key, key_size);
CHECK(!status, "Cipher key invalid format");
t += 2;
n_tokens -= 2;
}
/* authentication. */
CHECK(n_tokens >= 2, "Not enough tokens");
CHECK(!strcmp(t[0], "auth"), "Missing \"auth\" keyword");
auth_alg = auth_alg_find(t[1]);
CHECK(auth_alg, "Unsupported authentication algorithm");
key_size = auth_alg->key_size;
p->crypto.cipher_auth.auth.alg = auth_alg->alg;
p->crypto.cipher_auth.auth.key_size = key_size;
t += 2;
n_tokens -= 2;
if (key_size) {
int status;
CHECK(n_tokens >= 2, "Not enough tokens");
CHECK(!strcmp(t[0], "key"), "Missing authentication \"key\" keyword");
CHECK(key_size <= RTE_DIM(p->crypto.cipher_auth.auth.key),
"Authentication algorithm key too big");
status = hex_string_parse(t[1], p->crypto.cipher_auth.auth.key, key_size);
CHECK(!status, "Authentication key invalid format");
t += 2;
n_tokens -= 2;
}
} else if (!strcmp(t[0], "aead")) {
struct aead_alg *alg;
uint32_t key_size;
int status;
p->crypto.is_aead = 1;
CHECK(n_tokens >= 4, "Not enough tokens");
alg = aead_alg_find(t[1]);
CHECK(alg, "Unsupported AEAD algorithm");
key_size = alg->key_size;
p->crypto.aead.alg = alg->alg;
p->crypto.aead.key_size = key_size;
CHECK(!strcmp(t[2], "key"), "Missing AEAD \"key\" keyword");
CHECK(key_size <= RTE_DIM(p->crypto.aead.key),
"AEAD algorithm key too big");
status = hex_string_parse(t[3], p->crypto.aead.key, key_size);
CHECK(!status, "AEAD key invalid format");
t += 4;
n_tokens -= 4;
} else
CHECK(0, "Missing \"cipher\"/\"aead\" keyword");
/*
* Packet ecapsulation parameters.
*/
CHECK(n_tokens >= 4, "Not enough tokens");
CHECK(!strcmp(t[0], "esp"), "Missing \"esp\" keyword");
CHECK(!strcmp(t[1], "spi"), "Missing \"spi\" keyword");
p->encap.esp.spi = strtoul(t[2], &t[2], 0);
CHECK(!t[2][0], "ESP SPI field invalid format");
t += 3;
n_tokens -= 3;
if (!strcmp(t[0], "tunnel")) {
p->encap.tunnel_mode = 1;
CHECK(n_tokens >= 6, "Not enough tokens");
if (!strcmp(t[1], "ipv4")) {
uint32_t addr;
p->encap.tunnel_ipv4 = 1;
CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
addr = strtoul(t[3], &t[3], 0);
CHECK(!t[3][0], "Tunnel IPv4 source address invalid format");
p->encap.tunnel.ipv4.src_addr.s_addr = htonl(addr);
CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
addr = strtoul(t[5], &t[5], 0);
CHECK(!t[5][0], "Tunnel IPv4 destination address invalid format");
p->encap.tunnel.ipv4.dst_addr.s_addr = htonl(addr);
t += 6;
n_tokens -= 6;
} else if (!strcmp(t[1], "ipv6")) {
int status;
p->encap.tunnel_ipv4 = 0;
CHECK(!strcmp(t[2], "srcaddr"), "Missing \"srcaddr\" keyword");
status = hex_string_parse(t[3],
p->encap.tunnel.ipv6.src_addr.s6_addr,
16);
CHECK(!status, "Tunnel IPv6 source address invalid format");
CHECK(!strcmp(t[4], "dstaddr"), "Missing \"dstaddr\" keyword");
status = hex_string_parse(t[5],
p->encap.tunnel.ipv6.dst_addr.s6_addr,
16);
CHECK(!status, "Tunnel IPv6 destination address invalid format");
t += 6;
n_tokens -= 6;
} else
CHECK(0, "Missing \"ipv4\"/\"ipv6\" keyword");
} else if (!strcmp(t[0], "transport")) {
p->encap.tunnel_mode = 0;
t++;
n_tokens--;
} else
CHECK(0, "Missing \"tunnel\"/\"transport\" keyword");
/*
* Any other parameters.
*/
CHECK(!n_tokens, "Unexpected trailing tokens");
free(s0);
return p;
error:
free(p);
free(s0);
if (is_blank_or_comment)
*is_blank_or_comment = blank_or_comment;
return NULL;
}
static void
tunnel_ipv4_header_set(struct rte_ipv4_hdr *h, struct rte_swx_ipsec_sa_params *p)
{
struct rte_ipv4_hdr ipv4_hdr = {
.version_ihl = 0x45,
.type_of_service = 0,
.total_length = 0, /* Cannot be pre-computed. */
.packet_id = 0,
.fragment_offset = 0,
.time_to_live = 64,
.next_proto_id = IPPROTO_ESP,
.hdr_checksum = 0, /* Cannot be pre-computed. */
.src_addr = p->encap.tunnel.ipv4.src_addr.s_addr,
.dst_addr = p->encap.tunnel.ipv4.dst_addr.s_addr,
};
memcpy(h, &ipv4_hdr, sizeof(ipv4_hdr));
}
static void
tunnel_ipv6_header_set(struct rte_ipv6_hdr *h, struct rte_swx_ipsec_sa_params *p)
{
struct rte_ipv6_hdr ipv6_hdr = {
.vtc_flow = 0x60000000,
.payload_len = 0, /* Cannot be pre-computed. */
.proto = IPPROTO_ESP,
.hop_limits = 64,
.src_addr = {0},
.dst_addr = {0},
};
memcpy(h, &ipv6_hdr, sizeof(ipv6_hdr));
memcpy(h->src_addr, p->encap.tunnel.ipv6.src_addr.s6_addr, 16);
memcpy(h->dst_addr, p->encap.tunnel.ipv6.dst_addr.s6_addr, 16);
}
/* IPsec library SA parameters. */
static struct rte_crypto_sym_xform *
crypto_xform_get(struct rte_swx_ipsec_sa_params *p,
struct rte_crypto_sym_xform *xform,
uint32_t *salt_out)
{
if (p->crypto.is_aead) {
struct aead_alg *alg;
uint32_t key_size, salt, iv_length;
alg = aead_alg_find_by_id(p->crypto.aead.alg, p->crypto.aead.key_size);
if (!alg)
return NULL;
/* salt and salt-related key size adjustment. */
key_size = p->crypto.aead.key_size - 4;
memcpy(&salt, &p->crypto.aead.key[key_size], 4);
/* IV length. */
iv_length = 12;
if (p->crypto.aead.alg == RTE_CRYPTO_AEAD_AES_CCM)
iv_length = 11;
/* xform. */
xform[0].type = RTE_CRYPTO_SYM_XFORM_AEAD;
xform[0].aead.op = p->encrypt ?
RTE_CRYPTO_AEAD_OP_ENCRYPT :
RTE_CRYPTO_AEAD_OP_DECRYPT;
xform[0].aead.algo = p->crypto.aead.alg;
xform[0].aead.key.data = p->crypto.aead.key;
xform[0].aead.key.length = key_size;
xform[0].aead.iv.offset = IV_OFFSET;
xform[0].aead.iv.length = iv_length;
xform[0].aead.digest_length = alg->digest_size;
xform[0].aead.aad_length = alg->aad_size;
xform[0].next = NULL;
*salt_out = salt;
return &xform[0];
} else {
struct cipher_alg *cipher_alg;
struct auth_alg *auth_alg;
uint32_t cipher_key_size, auth_key_size, salt, auth_iv_length;
cipher_alg = cipher_alg_find_by_id(p->crypto.cipher_auth.cipher.alg,
p->crypto.cipher_auth.cipher.key_size);
if (!cipher_alg)
return NULL;
auth_alg = auth_alg_find_by_id(p->crypto.cipher_auth.auth.alg,
p->crypto.cipher_auth.auth.key_size);
if (!auth_alg)
return NULL;
/* salt and salt-related key size adjustment. */
cipher_key_size = p->crypto.cipher_auth.cipher.key_size;
auth_key_size = p->crypto.cipher_auth.auth.key_size;
switch (p->crypto.cipher_auth.cipher.alg) {
case RTE_CRYPTO_CIPHER_AES_CBC:
case RTE_CRYPTO_CIPHER_3DES_CBC:
salt = (uint32_t)rand();
break;
case RTE_CRYPTO_CIPHER_AES_CTR:
cipher_key_size -= 4;
memcpy(&salt, &p->crypto.cipher_auth.cipher.key[cipher_key_size], 4);
break;
default:
salt = 0;
}
if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
auth_key_size -= 4;
memcpy(&salt, &p->crypto.cipher_auth.auth.key[auth_key_size], 4);
}
/* IV length. */
auth_iv_length = cipher_alg->iv_size;
if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC)
auth_iv_length = 12;
/* xform. */
if (p->encrypt) {
xform[0].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
xform[0].cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
xform[0].cipher.algo = p->crypto.cipher_auth.cipher.alg;
xform[0].cipher.key.data = p->crypto.cipher_auth.cipher.key;
xform[0].cipher.key.length = cipher_key_size;
xform[0].cipher.iv.offset = IV_OFFSET;
xform[0].cipher.iv.length = cipher_alg->iv_size;
xform[0].cipher.dataunit_len = 0;
xform[0].next = &xform[1];
xform[1].type = RTE_CRYPTO_SYM_XFORM_AUTH;
xform[1].auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
xform[1].auth.algo = p->crypto.cipher_auth.auth.alg;
xform[1].auth.key.data = p->crypto.cipher_auth.auth.key;
xform[1].auth.key.length = auth_key_size;
xform[1].auth.iv.offset = IV_OFFSET;
xform[1].auth.iv.length = auth_iv_length;
xform[1].auth.digest_length = auth_alg->digest_size;
xform[1].next = NULL;
} else {
xform[0].type = RTE_CRYPTO_SYM_XFORM_AUTH;
xform[0].auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
xform[0].auth.algo = p->crypto.cipher_auth.auth.alg;
xform[0].auth.key.data = p->crypto.cipher_auth.auth.key;
xform[0].auth.key.length = auth_key_size;
xform[0].auth.iv.offset = IV_OFFSET;
xform[0].auth.iv.length = auth_iv_length;
xform[0].auth.digest_length = auth_alg->digest_size;
xform[0].next = &xform[1];
xform[1].type = RTE_CRYPTO_SYM_XFORM_CIPHER;
xform[1].cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
xform[1].cipher.algo = p->crypto.cipher_auth.cipher.alg;
xform[1].cipher.key.data = p->crypto.cipher_auth.cipher.key;
xform[1].cipher.key.length = cipher_key_size;
xform[1].cipher.iv.offset = IV_OFFSET;
xform[1].cipher.iv.length = cipher_alg->iv_size;
xform[1].cipher.dataunit_len = 0;
xform[1].next = NULL;
}
*salt_out = salt;
if (p->crypto.cipher_auth.auth.alg == RTE_CRYPTO_AUTH_AES_GMAC) {
if (p->encrypt)
return &xform[1];
xform[0].next = NULL;
return &xform[0];
}
return &xform[0];
}
}
static void
ipsec_xform_get(struct rte_swx_ipsec_sa_params *p,
struct rte_security_ipsec_xform *ipsec_xform,
uint32_t salt)
{
ipsec_xform->spi = p->encap.esp.spi;
ipsec_xform->salt = salt;
ipsec_xform->options.esn = 0;
ipsec_xform->options.udp_encap = 0;
ipsec_xform->options.copy_dscp = 1;
ipsec_xform->options.copy_flabel = 0;
ipsec_xform->options.copy_df = 0;
ipsec_xform->options.dec_ttl = 0;
ipsec_xform->options.ecn = 1;
ipsec_xform->options.stats = 0;
ipsec_xform->options.iv_gen_disable = 0;
ipsec_xform->options.tunnel_hdr_verify = 0;
ipsec_xform->options.udp_ports_verify = 0;
ipsec_xform->options.ip_csum_enable = 0;
ipsec_xform->options.l4_csum_enable = 0;
ipsec_xform->options.ip_reassembly_en = 0;
ipsec_xform->options.reserved_opts = 0;
ipsec_xform->direction = p->encrypt ?
RTE_SECURITY_IPSEC_SA_DIR_EGRESS :
RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
ipsec_xform->proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
ipsec_xform->mode = p->encap.tunnel_mode ?
RTE_SECURITY_IPSEC_SA_MODE_TUNNEL :
RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT;
ipsec_xform->tunnel.type = p->encap.tunnel_ipv4 ?
RTE_SECURITY_IPSEC_TUNNEL_IPV4 :
RTE_SECURITY_IPSEC_TUNNEL_IPV6;
if (p->encap.tunnel_mode) {
if (p->encap.tunnel_ipv4) {
ipsec_xform->tunnel.ipv4.src_ip = p->encap.tunnel.ipv4.src_addr;
ipsec_xform->tunnel.ipv4.dst_ip = p->encap.tunnel.ipv4.dst_addr;
ipsec_xform->tunnel.ipv4.dscp = 0;
ipsec_xform->tunnel.ipv4.df = 0;
ipsec_xform->tunnel.ipv4.ttl = 64;
} else {
ipsec_xform->tunnel.ipv6.src_addr = p->encap.tunnel.ipv6.src_addr;
ipsec_xform->tunnel.ipv6.dst_addr = p->encap.tunnel.ipv6.dst_addr;
ipsec_xform->tunnel.ipv6.dscp = 0;
ipsec_xform->tunnel.ipv6.flabel = 0;
ipsec_xform->tunnel.ipv6.hlimit = 64;
}
}
ipsec_xform->life.packets_soft_limit = 0;
ipsec_xform->life.bytes_soft_limit = 0;
ipsec_xform->life.packets_hard_limit = 0;
ipsec_xform->life.bytes_hard_limit = 0;
ipsec_xform->replay_win_sz = 0;
ipsec_xform->esn.value = 0;
ipsec_xform->udp.dport = 0;
ipsec_xform->udp.sport = 0;
}
static int
ipsec_sa_prm_get(struct rte_swx_ipsec_sa_params *p,
struct rte_ipsec_sa_prm *sa_prm,
struct rte_ipv4_hdr *ipv4_hdr,
struct rte_ipv6_hdr *ipv6_hdr,
struct rte_crypto_sym_xform *crypto_xform)
{
uint32_t salt;
memset(sa_prm, 0, sizeof(*sa_prm)); /* Better to be safe than sorry. */
sa_prm->userdata = 0; /* Not used. */
sa_prm->flags = 0; /* Flag RTE_IPSEC_SAFLAG_SQN_ATOM not enabled. */
/*
* crypto_xform.
*/
sa_prm->crypto_xform = crypto_xform_get(p, crypto_xform, &salt);
if (!sa_prm->crypto_xform)
return -EINVAL;
/*
* ipsec_xform.
*/
ipsec_xform_get(p, &sa_prm->ipsec_xform, salt);
/*
* tunnel / transport.
*
* Currently, the input IP packet type is assumed to be IPv4. To support both IPv4 and IPv6,
* the input packet type should be added to the SA configuration parameters.
*/
if (p->encap.tunnel_mode) {
if (p->encap.tunnel_ipv4) {
sa_prm->tun.hdr_len = sizeof(struct rte_ipv4_hdr);
sa_prm->tun.hdr_l3_off = 0;
sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
sa_prm->tun.hdr = ipv4_hdr;
} else {
sa_prm->tun.hdr_len = sizeof(struct rte_ipv6_hdr);
sa_prm->tun.hdr_l3_off = 0;
sa_prm->tun.next_proto = IPPROTO_IPIP; /* IPv4. */
sa_prm->tun.hdr = ipv6_hdr;
}
} else {
sa_prm->trs.proto = IPPROTO_IPIP; /* IPv4. */
}
return 0;
}
static int
ipsec_session_create(struct rte_swx_ipsec *ipsec,
struct rte_swx_ipsec_sa_params *p,
struct rte_ipsec_session *s)
{
struct rte_ipv4_hdr ipv4_hdr;
struct rte_ipv6_hdr ipv6_hdr;
struct rte_crypto_sym_xform crypto_xform[2];
struct rte_ipsec_sa_prm sa_prm;
struct rte_ipsec_sa *sa = NULL;
struct rte_cryptodev_sym_session *crypto_session = NULL;
int sa_size;
int sa_valid = 0, status = 0;
tunnel_ipv4_header_set(&ipv4_hdr, p);
tunnel_ipv6_header_set(&ipv6_hdr, p);
/* IPsec library SA setup. */
status = ipsec_sa_prm_get(p, &sa_prm, &ipv4_hdr, &ipv6_hdr, crypto_xform);
if (status)
goto error;
sa_size = rte_ipsec_sa_size(&sa_prm);
if (sa_size < 0) {
status = sa_size;
goto error;
}
if (!sa_size) {
status = -EINVAL;
goto error;
}
sa = calloc(1, sa_size);
if (!sa) {
status = -ENOMEM;
goto error;
}
sa_size = rte_ipsec_sa_init(sa, &sa_prm, sa_size);
if (sa_size < 0) {
status = sa_size;
goto error;
}
if (!sa_size) {
status = -EINVAL;
goto error;
}
sa_valid = 1;
/* Cryptodev library session setup. */
crypto_session = rte_cryptodev_sym_session_create(ipsec->dev_id,
sa_prm.crypto_xform,
ipsec->mp_session);
if (!crypto_session) {
status = -ENOMEM;
goto error;
}
/* IPsec library session setup. */
s->sa = sa;
s->type = RTE_SECURITY_ACTION_TYPE_NONE;
s->crypto.ses = crypto_session;
s->crypto.dev_id = ipsec->dev_id;
s->pkt_func.prepare.async = NULL;
s->pkt_func.process = NULL;
status = rte_ipsec_session_prepare(s);
if (status)
goto error;
return 0;
error:
/* sa. */
if (sa_valid)
rte_ipsec_sa_fini(sa);
free(sa);
/* crypto_session. */
if (crypto_session)
rte_cryptodev_sym_session_free(ipsec->dev_id, crypto_session);
/* s. */
memset(s, 0, sizeof(*s));
return status;
}
static void
ipsec_session_free(struct rte_swx_ipsec *ipsec,
struct rte_ipsec_session *s)
{
if (!s)
return;
/* IPsec library SA. */
if (s->sa)
rte_ipsec_sa_fini(s->sa);
free(s->sa);
/* Cryptodev library session. */
if (s->crypto.ses)
rte_cryptodev_sym_session_free(ipsec->dev_id, s->crypto.ses);
/* IPsec library session. */
memset(s, 0, sizeof(*s));
}
int
rte_swx_ipsec_sa_add(struct rte_swx_ipsec *ipsec,
struct rte_swx_ipsec_sa_params *sa_params,
uint32_t *id)
{
struct ipsec_sa *sa;
uint32_t sa_id;
int status;
/* Check the input parameters. */
if (!ipsec || !sa_params || !id)
return -EINVAL;
/* Allocate a free SADB entry. */
if (!ipsec->n_sa_free_id)
return -ENOSPC;
sa_id = ipsec->sa_free_id[ipsec->n_sa_free_id - 1];
ipsec->n_sa_free_id--;
/* Acquire the SA resources. */
sa = ipsec_sa_get(ipsec, sa_id);
status = ipsec_session_create(ipsec, sa_params, &sa->s);
if (status) {
/* Free the allocated SADB entry. */
ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
ipsec->n_sa_free_id++;
return status;
}
/* Validate the new SA. */
sa->valid = 1;
*id = sa_id;
return 0;
}
void
rte_swx_ipsec_sa_delete(struct rte_swx_ipsec *ipsec,
uint32_t sa_id)
{
struct ipsec_sa *sa;
/* Check the input parameters. */
if (!ipsec || (sa_id >= ipsec->n_sa_max))
return;
/* Release the SA resources. */
sa = ipsec_sa_get(ipsec, sa_id);
ipsec_session_free(ipsec, &sa->s);
/* Free the SADB entry. */
ipsec->sa_free_id[ipsec->n_sa_free_id] = sa_id;
ipsec->n_sa_free_id++;
/* Invalidate the SA. */
sa->valid = 0;
}