From: Dmitry Eremin-Solenikov <dmitry.ereminsoleni...@linaro.org> Implement SA database and SA handling.
- only IPv4 is supported for now - no support for time-based limits Signed-off-by: Dmitry Eremin-Solenikov <dmitry.ereminsoleni...@linaro.org> --- /** Email created from pull request 81 (lumag:ipsec-packet-impl-2) ** https://github.com/Linaro/odp/pull/81 ** Patch: https://github.com/Linaro/odp/pull/81.patch ** Base sha: c16f1363303cd5fc11324acbc4dfebe0a9680a41 ** Merge commit sha: 38d17798a2db32c26fbd413245d59dde8e3011d9 **/ platform/linux-generic/Makefile.am | 1 + platform/linux-generic/include/odp_internal.h | 4 + .../linux-generic/include/odp_ipsec_internal.h | 109 +++++ platform/linux-generic/odp_init.c | 13 + platform/linux-generic/odp_ipsec.c | 46 -- platform/linux-generic/odp_ipsec_sad.c | 507 +++++++++++++++++++++ 6 files changed, 634 insertions(+), 46 deletions(-) create mode 100644 platform/linux-generic/odp_ipsec_sad.c diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am index 42da7191..a3ea4f6d 100644 --- a/platform/linux-generic/Makefile.am +++ b/platform/linux-generic/Makefile.am @@ -249,6 +249,7 @@ __LIB__libodp_linux_la_SOURCES = \ odp_impl.c \ odp_ipsec.c \ odp_ipsec_events.c \ + odp_ipsec_sad.c \ odp_name_table.c \ odp_packet.c \ odp_packet_flags.c \ diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h index f916631b..8725dec8 100644 --- a/platform/linux-generic/include/odp_internal.h +++ b/platform/linux-generic/include/odp_internal.h @@ -75,6 +75,7 @@ enum init_stage { TRAFFIC_MNGR_INIT, NAME_TABLE_INIT, IPSEC_EVENTS_INIT, + IPSEC_SAD_INIT, MODULES_INIT, ALL_INIT /* All init stages completed */ }; @@ -134,6 +135,9 @@ int _odp_ishm_init_local(void); int _odp_ishm_term_global(void); int _odp_ishm_term_local(void); +int _odp_ipsec_sad_init_global(void); +int _odp_ipsec_sad_term_global(void); + int _odp_ipsec_events_init_global(void); int _odp_ipsec_events_term_global(void); diff --git a/platform/linux-generic/include/odp_ipsec_internal.h b/platform/linux-generic/include/odp_ipsec_internal.h index b31f048f..64ef8ab7 100644 --- a/platform/linux-generic/include/odp_ipsec_internal.h +++ b/platform/linux-generic/include/odp_ipsec_internal.h @@ -20,7 +20,9 @@ extern "C" { #include <odp/api/std_types.h> #include <odp/api/plat/strong_types.h> +#include <odp/api/byteorder.h> #include <odp/api/ipsec.h> +#include <odp/api/ticketlock.h> /** @ingroup odp_ipsec * @{ @@ -31,6 +33,8 @@ typedef ODP_HANDLE_T(ipsec_status_t); #define ODP_IPSEC_STATUS_INVALID \ _odp_cast_scalar(ipsec_status_t, 0xffffffff) +typedef struct ipsec_sa_s ipsec_sa_t; + /** * @internal Get ipsec_status handle from event * @@ -73,6 +77,111 @@ int _odp_ipsec_status_send(odp_queue_t queue, int result, odp_ipsec_warn_t warn); +#define IPSEC_MAX_IV_LEN 32 /**< Maximum IV length in bytes */ + +#define IPSEC_MAX_SALT_LEN 4 /**< Maximum salt length in bytes */ + +/** + * Maximum number of available SAs + */ +#define ODP_CONFIG_IPSEC_SAS 8 + +struct ipsec_sa_s { + odp_atomic_u32_t state ODP_ALIGNED_CACHE; + + uint32_t ipsec_sa_idx; + odp_ipsec_sa_t ipsec_sa_hdl; + + odp_ipsec_protocol_t proto; + uint32_t spi; + + odp_ipsec_mode_t mode; + + /* Limits */ + uint64_t soft_limit_bytes; + uint64_t soft_limit_packets; + uint64_t hard_limit_bytes; + uint64_t hard_limit_packets; + + /* Statistics for soft/hard expiration */ + odp_atomic_u64_t bytes; + odp_atomic_u64_t packets; + + odp_crypto_session_t session; + void *context; + odp_queue_t queue; + + uint32_t icv_len; + uint32_t esp_iv_len; + uint32_t esp_block_len; + + uint8_t salt[IPSEC_MAX_SALT_LEN]; + uint32_t salt_length; + + unsigned dec_ttl : 1; + unsigned copy_dscp : 1; + unsigned copy_df : 1; + + union { + struct { + odp_ipsec_lookup_mode_t lookup_mode; + odp_u32be_t lookup_dst_ip; + } in; + + struct { + odp_u32be_t tun_src_ip; + odp_u32be_t tun_dst_ip; + + /* 32-bit from which low 16 are used */ + odp_atomic_u32_t tun_hdr_id; + odp_atomic_u32_t seq; + + uint8_t tun_ttl; + uint8_t tun_dscp; + uint8_t tun_df; + } out; + }; +}; + +/** + * IPSEC Security Association (SA) lookup parameters + */ +typedef struct odp_ipsec_sa_lookup_s { + /** IPSEC protocol: ESP or AH */ + odp_ipsec_protocol_t proto; + + /** SPI value */ + uint32_t spi; + + /* FIXME: IPv4 vs IPv6 */ + + /** IP destination address (NETWORK ENDIAN) */ + void *dst_addr; +} ipsec_sa_lookup_t; + +/** + * Obtain SA reference + */ +ipsec_sa_t *_odp_ipsec_sa_use(odp_ipsec_sa_t sa); + +/** + * Release SA reference + */ +void _odp_ipsec_sa_unuse(ipsec_sa_t *ipsec_sa); + +/** + * Lookup SA corresponding to inbound packet pkt + */ +ipsec_sa_t *_odp_ipsec_sa_lookup(const ipsec_sa_lookup_t *lookup); + +/** + * Update SA usage statistics, filling respective status for the packet. + * + * @retval <0 if hard limits were breached + */ +int _odp_ipsec_sa_update_stats(ipsec_sa_t *ipsec_sa, uint32_t len, + odp_ipsec_op_status_t *status); + /** * @} */ diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c index 22bcbdc8..6e57c6e3 100644 --- a/platform/linux-generic/odp_init.c +++ b/platform/linux-generic/odp_init.c @@ -226,6 +226,12 @@ int odp_init_global(odp_instance_t *instance, } stage = IPSEC_EVENTS_INIT; + if (_odp_ipsec_sad_init_global()) { + ODP_ERR("ODP IPsec SAD init failed.\n"); + goto init_failed; + } + stage = IPSEC_SAD_INIT; + if (_odp_modules_init_global()) { ODP_ERR("ODP modules init failed\n"); goto init_failed; @@ -256,6 +262,13 @@ int _odp_term_global(enum init_stage stage) switch (stage) { case ALL_INIT: case MODULES_INIT: + case IPSEC_SAD_INIT: + if (_odp_ipsec_sad_term_global()) { + ODP_ERR("ODP IPsec SAD term failed.\n"); + rc = -1; + } + /* Fall through */ + case IPSEC_EVENTS_INIT: if (_odp_ipsec_events_term_global()) { ODP_ERR("ODP IPsec events term failed.\n"); diff --git a/platform/linux-generic/odp_ipsec.c b/platform/linux-generic/odp_ipsec.c index f2757628..d0ca027c 100644 --- a/platform/linux-generic/odp_ipsec.c +++ b/platform/linux-generic/odp_ipsec.c @@ -49,32 +49,6 @@ int odp_ipsec_config(const odp_ipsec_config_t *config) return -1; } -void odp_ipsec_sa_param_init(odp_ipsec_sa_param_t *param) -{ - memset(param, 0, sizeof(odp_ipsec_sa_param_t)); -} - -odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param) -{ - (void)param; - - return ODP_IPSEC_SA_INVALID; -} - -int odp_ipsec_sa_disable(odp_ipsec_sa_t sa) -{ - (void)sa; - - return -1; -} - -int odp_ipsec_sa_destroy(odp_ipsec_sa_t sa) -{ - (void)sa; - - return -1; -} - int odp_ipsec_in(const odp_packet_t pkt_in[], int num_in, odp_packet_t pkt_out[], int *num_out, const odp_ipsec_in_param_t *param) @@ -141,21 +115,6 @@ int odp_ipsec_result(odp_ipsec_packet_result_t *result, odp_packet_t packet) return -1; } -int odp_ipsec_mtu_update(odp_ipsec_sa_t sa, uint32_t mtu) -{ - (void)sa; - (void)mtu; - - return -1; -} - -void *odp_ipsec_sa_context(odp_ipsec_sa_t sa) -{ - (void)sa; - - return NULL; -} - odp_packet_t odp_ipsec_packet_from_event(odp_event_t ev) { (void)ev; @@ -169,8 +128,3 @@ odp_event_t odp_ipsec_packet_to_event(odp_packet_t pkt) return ODP_EVENT_INVALID; } - -uint64_t odp_ipsec_sa_to_u64(odp_ipsec_sa_t sa) -{ - return _odp_pri(sa); -} diff --git a/platform/linux-generic/odp_ipsec_sad.c b/platform/linux-generic/odp_ipsec_sad.c new file mode 100644 index 00000000..34da960e --- /dev/null +++ b/platform/linux-generic/odp_ipsec_sad.c @@ -0,0 +1,507 @@ +/* Copyright (c) 2017, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp/api/atomic.h> +#include <odp/api/ipsec.h> +#include <odp/api/random.h> +#include <odp/api/shared_memory.h> + +#include <odp_debug_internal.h> +#include <odp_ipsec_internal.h> + +#include <string.h> + +#define IPSEC_SA_STATE_DISABLE 0x40000000 +#define IPSEC_SA_STATE_FREE 0xc0000000 /* This includes disable !!! */ + +typedef struct ipsec_sa_table_t { + ipsec_sa_t ipsec_sa[ODP_CONFIG_IPSEC_SAS]; + odp_shm_t shm; +} ipsec_sa_table_t; + +static ipsec_sa_table_t *ipsec_sa_tbl; + +static inline +ipsec_sa_t *ipsec_sa_entry(uint32_t ipsec_sa_idx) +{ + return &ipsec_sa_tbl->ipsec_sa[ipsec_sa_idx]; +} + +static inline +ipsec_sa_t *ipsec_sa_entry_from_hdl(odp_ipsec_sa_t ipsec_sa_hdl) +{ + return ipsec_sa_entry(_odp_typeval(ipsec_sa_hdl)); +} + +static inline +odp_ipsec_sa_t ipsec_sa_index_to_handle(uint32_t ipsec_sa_idx) +{ + return _odp_cast_scalar(odp_ipsec_sa_t, ipsec_sa_idx); +} + +int _odp_ipsec_sad_init_global(void) +{ + odp_shm_t shm; + unsigned i; + + shm = odp_shm_reserve("ipsec_sa_table", + sizeof(ipsec_sa_table_t), + ODP_CACHE_LINE_SIZE, 0); + + ipsec_sa_tbl = odp_shm_addr(shm); + if (ipsec_sa_tbl == NULL) + return -1; + + memset(ipsec_sa_tbl, 0, sizeof(ipsec_sa_table_t)); + ipsec_sa_tbl->shm = shm; + + for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) { + ipsec_sa_t *ipsec_sa = ipsec_sa_entry(i); + + ipsec_sa->ipsec_sa_hdl = ipsec_sa_index_to_handle(i); + ipsec_sa->ipsec_sa_idx = i; + odp_atomic_init_u32(&ipsec_sa->state, IPSEC_SA_STATE_FREE); + odp_atomic_init_u64(&ipsec_sa->bytes, 0); + odp_atomic_init_u64(&ipsec_sa->packets, 0); + } + + return 0; +} + +int _odp_ipsec_sad_term_global(void) +{ + int i; + ipsec_sa_t *ipsec_sa; + int ret = 0; + int rc = 0; + + for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) { + ipsec_sa = ipsec_sa_entry(i); + + if (odp_atomic_load_u32(&ipsec_sa->state) != + IPSEC_SA_STATE_FREE) { + ODP_ERR("Not destroyed ipsec_sa: %u\n", + ipsec_sa->ipsec_sa_idx); + rc = -1; + } + odp_atomic_store_u32(&ipsec_sa->state, IPSEC_SA_STATE_FREE); + } + + ret = odp_shm_free(ipsec_sa_tbl->shm); + if (ret < 0) { + ODP_ERR("shm free failed"); + rc = -1; + } + + return rc; +} + +static +ipsec_sa_t *ipsec_sa_reserve(void) +{ + int i; + ipsec_sa_t *ipsec_sa; + + for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) { + uint32_t state = IPSEC_SA_STATE_FREE; + + ipsec_sa = ipsec_sa_entry(i); + + if (odp_atomic_cas_acq_u32(&ipsec_sa->state, &state, 0)) + return ipsec_sa; + } + + return NULL; +} + +static +void ipsec_sa_release(ipsec_sa_t *ipsec_sa) +{ + odp_atomic_store_rel_u32(&ipsec_sa->state, IPSEC_SA_STATE_FREE); +} + +static +int ipsec_sa_lock(ipsec_sa_t *ipsec_sa) +{ + int cas = 0; + uint32_t state = odp_atomic_load_u32(&ipsec_sa->state); + + while (0 == cas) { + /* + * This can be called from lookup path, so we really need this + * check + */ + if (state & IPSEC_SA_STATE_DISABLE) + return -1; + + cas = odp_atomic_cas_acq_u32(&ipsec_sa->state, &state, + state + 1); + } + + return 0; +} + +/* Do not call directly, use _odp_ipsec_sa_unuse */ +static +odp_bool_t ipsec_sa_unlock(ipsec_sa_t *ipsec_sa) +{ + int cas = 0; + uint32_t state = odp_atomic_load_u32(&ipsec_sa->state); + + while (0 == cas) + cas = odp_atomic_cas_rel_u32(&ipsec_sa->state, &state, + state - 1); + + return state == IPSEC_SA_STATE_DISABLE; +} + +ipsec_sa_t *_odp_ipsec_sa_use(odp_ipsec_sa_t sa) +{ + ipsec_sa_t *ipsec_sa; + + ODP_ASSERT(ODP_IPSEC_SA_INVALID != sa); + + ipsec_sa = ipsec_sa_entry_from_hdl(sa); + + if (ipsec_sa_lock(ipsec_sa) < 0) + return NULL; + + return ipsec_sa; +} + +void _odp_ipsec_sa_unuse(ipsec_sa_t *ipsec_sa) +{ + odp_queue_t queue; + odp_ipsec_sa_t sa; + odp_ipsec_warn_t warn = { .all = 0 }; + + ODP_ASSERT(NULL != ipsec_sa); + + queue = ipsec_sa->queue; + sa = ipsec_sa->ipsec_sa_hdl; + + if (ipsec_sa_unlock(ipsec_sa) && ODP_QUEUE_INVALID != queue) + _odp_ipsec_status_send(queue, + ODP_IPSEC_STATUS_SA_DISABLE, + sa, 0, warn); +} + +void odp_ipsec_sa_param_init(odp_ipsec_sa_param_t *param) +{ + memset(param, 0, sizeof(odp_ipsec_sa_param_t)); + param->dest_queue = ODP_QUEUE_INVALID; +} + +odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param) +{ + ipsec_sa_t *ipsec_sa; + odp_crypto_session_param_t crypto_param; + odp_crypto_ses_create_err_t ses_create_rc; + + ipsec_sa = ipsec_sa_reserve(); + if (NULL == ipsec_sa) { + ODP_ERR("No more free SA\n"); + return ODP_IPSEC_SA_INVALID; + } + + ipsec_sa->proto = param->proto; + ipsec_sa->spi = param->spi; + ipsec_sa->context = param->context; + ipsec_sa->queue = param->dest_queue; + ipsec_sa->mode = param->mode; + if (ODP_IPSEC_DIR_INBOUND == param->dir) { + ipsec_sa->in.lookup_mode = param->inbound.lookup_mode; + if (ODP_IPSEC_LOOKUP_DSTADDR_SPI == ipsec_sa->in.lookup_mode) + memcpy(&ipsec_sa->in.lookup_dst_ip, + param->inbound.lookup_param.dst_addr, + sizeof(ipsec_sa->in.lookup_dst_ip)); + + } else { + odp_atomic_store_u32(&ipsec_sa->out.seq, 1); + } + ipsec_sa->dec_ttl = param->opt.dec_ttl; + ipsec_sa->copy_dscp = param->opt.copy_dscp; + ipsec_sa->copy_df = param->opt.copy_df; + + odp_atomic_store_u64(&ipsec_sa->bytes, 0); + odp_atomic_store_u64(&ipsec_sa->packets, 0); + ipsec_sa->soft_limit_bytes = param->lifetime.soft_limit.bytes; + ipsec_sa->soft_limit_packets = param->lifetime.soft_limit.packets; + ipsec_sa->hard_limit_bytes = param->lifetime.hard_limit.bytes; + ipsec_sa->hard_limit_packets = param->lifetime.hard_limit.packets; + + if (ODP_IPSEC_MODE_TUNNEL == ipsec_sa->mode && + ODP_IPSEC_DIR_OUTBOUND == param->dir) { + if (param->outbound.tunnel.type != ODP_IPSEC_TUNNEL_IPV4) { + ipsec_sa_release(ipsec_sa); + + return ODP_IPSEC_SA_INVALID; + } + memcpy(&ipsec_sa->out.tun_src_ip, + param->outbound.tunnel.ipv4.src_addr, + sizeof(ipsec_sa->out.tun_src_ip)); + memcpy(&ipsec_sa->out.tun_dst_ip, + param->outbound.tunnel.ipv4.dst_addr, + sizeof(ipsec_sa->out.tun_dst_ip)); + odp_atomic_init_u32(&ipsec_sa->out.tun_hdr_id, 0); + ipsec_sa->out.tun_ttl = param->outbound.tunnel.ipv4.ttl; + ipsec_sa->out.tun_dscp = param->outbound.tunnel.ipv4.dscp; + ipsec_sa->out.tun_df = param->outbound.tunnel.ipv4.df; + } + + odp_crypto_session_param_init(&crypto_param); + + /* Setup parameters and call crypto library to create session */ + crypto_param.op = (ODP_IPSEC_DIR_INBOUND == param->dir) ? + ODP_CRYPTO_OP_DECODE : + ODP_CRYPTO_OP_ENCODE; + crypto_param.auth_cipher_text = 1; + + crypto_param.op_mode = ODP_CRYPTO_SYNC; + crypto_param.compl_queue = ODP_QUEUE_INVALID; + crypto_param.output_pool = ODP_POOL_INVALID; + + crypto_param.cipher_alg = param->crypto.cipher_alg; + crypto_param.cipher_key = param->crypto.cipher_key; + crypto_param.auth_alg = param->crypto.auth_alg; + crypto_param.auth_key = param->crypto.auth_key; + + switch (crypto_param.auth_alg) { + case ODP_AUTH_ALG_NULL: + ipsec_sa->icv_len = 0; + break; +#if ODP_DEPRECATED_API + case ODP_AUTH_ALG_MD5_96: +#endif + case ODP_AUTH_ALG_MD5_HMAC: + ipsec_sa->icv_len = 12; + break; + case ODP_AUTH_ALG_SHA1_HMAC: + ipsec_sa->icv_len = 12; + break; +#if ODP_DEPRECATED_API + case ODP_AUTH_ALG_SHA256_128: +#endif + case ODP_AUTH_ALG_SHA256_HMAC: + ipsec_sa->icv_len = 16; + break; + case ODP_AUTH_ALG_SHA512_HMAC: + ipsec_sa->icv_len = 32; + break; +#if ODP_DEPRECATED_API + case ODP_AUTH_ALG_AES128_GCM: +#endif + case ODP_AUTH_ALG_AES_GCM: + ipsec_sa->icv_len = 16; + break; + default: + return ODP_IPSEC_SA_INVALID; + } + + switch (crypto_param.cipher_alg) { + case ODP_CIPHER_ALG_NULL: + ipsec_sa->esp_iv_len = 0; + ipsec_sa->esp_block_len = 1; + break; + case ODP_CIPHER_ALG_DES: + case ODP_CIPHER_ALG_3DES_CBC: + ipsec_sa->esp_iv_len = 8; + ipsec_sa->esp_block_len = 8; + break; +#if ODP_DEPRECATED_API + case ODP_CIPHER_ALG_AES128_CBC: +#endif + case ODP_CIPHER_ALG_AES_CBC: + ipsec_sa->esp_iv_len = 16; + ipsec_sa->esp_block_len = 16; + break; +#if ODP_DEPRECATED_API + case ODP_CIPHER_ALG_AES128_GCM: +#endif + case ODP_CIPHER_ALG_AES_GCM: + ipsec_sa->esp_iv_len = 8; + ipsec_sa->esp_block_len = 16; + crypto_param.iv.length = 12; + break; + default: + return ODP_IPSEC_SA_INVALID; + } + + crypto_param.auth_digest_len = ipsec_sa->icv_len; + + if (param->crypto.cipher_key_extra.length) { + if (param->crypto.cipher_key_extra.length > + IPSEC_MAX_SALT_LEN) + goto error; + + ipsec_sa->salt_length = param->crypto.cipher_key_extra.length; + memcpy(ipsec_sa->salt, + param->crypto.cipher_key_extra.data, + param->crypto.cipher_key_extra.length); + } else { + ipsec_sa->salt_length = 0; + } + + if (odp_crypto_session_create(&crypto_param, &ipsec_sa->session, + &ses_create_rc)) + goto error; + + return ipsec_sa->ipsec_sa_hdl; + +error: + ipsec_sa_release(ipsec_sa); + + return ODP_IPSEC_SA_INVALID; +} + +int odp_ipsec_sa_disable(odp_ipsec_sa_t sa) +{ + ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa); + uint32_t state; + int cas = 0; + + /* This is a custom rwlock implementation. It is not possible to use + * original rwlock, because there is no way to test if current code is + * the last reader when disable operation is pending. */ + state = odp_atomic_load_u32(&ipsec_sa->state); + + while (0 == cas) { + if (state & IPSEC_SA_STATE_DISABLE) + return -1; + + cas = odp_atomic_cas_acq_u32(&ipsec_sa->state, &state, + state | IPSEC_SA_STATE_DISABLE); + } + + if (ODP_QUEUE_INVALID != ipsec_sa->queue) { + odp_ipsec_warn_t warn = { .all = 0 }; + + /* + * If there were not active state when we disabled SA, + * send the event. + */ + if (0 == state) + _odp_ipsec_status_send(ipsec_sa->queue, + ODP_IPSEC_STATUS_SA_DISABLE, + ipsec_sa->ipsec_sa_hdl, + 0, warn); + + return 0; + } + + while (IPSEC_SA_STATE_DISABLE != state) { + odp_cpu_pause(); + state = odp_atomic_load_u32(&ipsec_sa->state); + } + + return 0; +} + +int odp_ipsec_sa_destroy(odp_ipsec_sa_t sa) +{ + ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa); + int rc = 0; + uint32_t state = odp_atomic_load_u32(&ipsec_sa->state); + + if (IPSEC_SA_STATE_DISABLE != state) { + ODP_ERR("Distroying not disabled ipsec_sa: %u\n", + ipsec_sa->ipsec_sa_idx); + return -1; + } + + if (odp_crypto_session_destroy(ipsec_sa->session) < 0) { + ODP_ERR("Error destroying crypto session for ipsec_sa: %u\n", + ipsec_sa->ipsec_sa_idx); + rc = -1; + } + + ipsec_sa_release(ipsec_sa); + + return rc; +} + +void *odp_ipsec_sa_context(odp_ipsec_sa_t sa) +{ + ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa); + + return ipsec_sa->context; +} + +uint64_t odp_ipsec_sa_to_u64(odp_ipsec_sa_t sa) +{ + return _odp_pri(sa); +} + +int odp_ipsec_mtu_update(odp_ipsec_sa_t sa, uint32_t mtu) +{ + (void)sa; + (void)mtu; + + return -1; +} + +ipsec_sa_t *_odp_ipsec_sa_lookup(const ipsec_sa_lookup_t *lookup) +{ + (void)lookup; + + int i; + ipsec_sa_t *ipsec_sa; + ipsec_sa_t *best = NULL; + + for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) { + ipsec_sa = ipsec_sa_entry(i); + + if (ipsec_sa_lock(ipsec_sa) < 0) + continue; + + if (ODP_IPSEC_LOOKUP_DSTADDR_SPI == ipsec_sa->in.lookup_mode && + lookup->proto == ipsec_sa->proto && + lookup->spi == ipsec_sa->spi && + !memcmp(lookup->dst_addr, &ipsec_sa->in.lookup_dst_ip, + sizeof(ipsec_sa->in.lookup_dst_ip))) { + if (NULL != best) + _odp_ipsec_sa_unuse(best); + return ipsec_sa; + } else if (ODP_IPSEC_LOOKUP_SPI == ipsec_sa->in.lookup_mode && + lookup->proto == ipsec_sa->proto && + lookup->spi == ipsec_sa->spi) { + best = ipsec_sa; + } else { + _odp_ipsec_sa_unuse(ipsec_sa); + } + } + + return best; +} + +int _odp_ipsec_sa_update_stats(ipsec_sa_t *ipsec_sa, uint32_t len, + odp_ipsec_op_status_t *status) +{ + uint64_t bytes = odp_atomic_fetch_add_u64(&ipsec_sa->bytes, len) + len; + uint64_t packets = odp_atomic_fetch_add_u64(&ipsec_sa->packets, 1) + 1; + int rc = 0; + + if (ipsec_sa->soft_limit_bytes > 0 && + bytes > ipsec_sa->soft_limit_bytes) + status->warn.soft_exp_bytes = 1; + + if (ipsec_sa->soft_limit_packets > 0 && + packets > ipsec_sa->soft_limit_packets) + status->warn.soft_exp_packets = 1; + + if (ipsec_sa->hard_limit_bytes > 0 && + bytes > ipsec_sa->hard_limit_bytes) { + status->error.hard_exp_bytes = 1; + rc = -1; + } + if (ipsec_sa->hard_limit_packets > 0 && + packets > ipsec_sa->hard_limit_packets) { + status->error.hard_exp_packets = 1; + rc = -1; + } + + return rc; +}