From patchwork Tue Apr 11 13:41:32 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Eremin-Solenikov X-Patchwork-Id: 97256 Delivered-To: patch@linaro.org Received: by 10.140.89.233 with SMTP id v96csp1814521qgd; Tue, 11 Apr 2017 06:41:44 -0700 (PDT) X-Received: by 10.36.2.205 with SMTP id 196mr10231481itu.63.1491918104314; Tue, 11 Apr 2017 06:41:44 -0700 (PDT) Return-Path: Received: from lists.linaro.org (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTP id n17si17548757ioo.93.2017.04.11.06.41.43; Tue, 11 Apr 2017 06:41:44 -0700 (PDT) Received-SPF: pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) client-ip=54.225.227.206; Authentication-Results: mx.google.com; spf=pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) smtp.mailfrom=lng-odp-bounces@lists.linaro.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id 710B6635C2; Tue, 11 Apr 2017 13:41:43 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by lists.linaro.org (Postfix) with ESMTP id 57B90635C2; Tue, 11 Apr 2017 13:41:41 +0000 (UTC) X-Original-To: lng-odp@lists.linaro.org Delivered-To: lng-odp@lists.linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id CCE60635C6; Tue, 11 Apr 2017 13:41:39 +0000 (UTC) Received: from mail-lf0-f50.google.com (mail-lf0-f50.google.com [209.85.215.50]) by lists.linaro.org (Postfix) with ESMTPS id 53E66635AB for ; Tue, 11 Apr 2017 13:41:37 +0000 (UTC) Received: by mail-lf0-f50.google.com with SMTP id 75so27501939lfs.2 for ; Tue, 11 Apr 2017 06:41:37 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id; bh=bVNI0/ui5y8KzseiyuZO/WxMRGKHyzlSd/Z4qN0WBnk=; b=po+6oZ8doXROtHAzj/3JDH2d1OUp+6+4QKXtgq2qA+Ib7QWxx1TrBQJzKMUkNfb5/5 LrFLfFTO8A6KGiO2KpebHU/i7pQoSQCn/joeiSNN66fN5XwCAKSM3HTx0eDfnUJ1KH2q AvBqdpt7t+SeuGxrjmYBuufmh/BUZO9AT4CtAdIy34lO94Mw5jfYfLyK1TM+9yBU9tbR +QExT1siB8opStFdE0Mtk9S6J4rXiHzJBP2gkeHJUf9WZ+Zmu5Ch3aDr2F5qpH2LWByl H/C6x7iek4LD95oFEskj2hXe0Tm00sxZwO6nxkw98uNCLqZ7tOxw2AQmu6zHBnve+CAG uXwg== X-Gm-Message-State: AFeK/H3vKnAIMA3aa60hy+/fFuxVrgc7m4akvPhMGOHUZvDT11dmvlIS63RVU9YEX0+fmnqVcTyfUIDD X-Received: by 10.46.84.22 with SMTP id i22mr16550240ljb.132.1491918095189; Tue, 11 Apr 2017 06:41:35 -0700 (PDT) Received: from forlindon.lumag.auriga.ru ([188.162.64.99]) by smtp.gmail.com with ESMTPSA id y21sm3480662lfj.12.2017.04.11.06.41.33 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 11 Apr 2017 06:41:34 -0700 (PDT) From: Dmitry Eremin-Solenikov To: lng-odp@lists.linaro.org Date: Tue, 11 Apr 2017 16:41:32 +0300 Message-Id: <20170411134132.9236-1-dmitry.ereminsolenikov@linaro.org> X-Mailer: git-send-email 2.11.0 Subject: [lng-odp] [API-NEXT][RFC][rebased] linux-gen: ipsec: draft IPsec implementation X-BeenThere: lng-odp@lists.linaro.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: "The OpenDataPlane \(ODP\) List" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: lng-odp-bounces@lists.linaro.org Sender: "lng-odp" For now it's only a preview with the following limitation: - No inline processing support - No SA lookups - Only IPv4 support - No tunnel support - No header modification according to RFCs Signed-off-by: Dmitry Eremin-Solenikov --- platform/linux-generic/include/odp_internal.h | 4 + .../linux-generic/include/odp_ipsec_internal.h | 71 ++ platform/linux-generic/include/protocols/ip.h | 52 + platform/linux-generic/odp_event.c | 5 + platform/linux-generic/odp_init.c | 13 + platform/linux-generic/odp_ipsec.c | 1172 +++++++++++++++++++- 6 files changed, 1287 insertions(+), 30 deletions(-) create mode 100644 platform/linux-generic/include/odp_ipsec_internal.h -- 2.11.0 diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h index 05c8a422..fd7848ac 100644 --- a/platform/linux-generic/include/odp_internal.h +++ b/platform/linux-generic/include/odp_internal.h @@ -71,6 +71,7 @@ enum init_stage { CLASSIFICATION_INIT, TRAFFIC_MNGR_INIT, NAME_TABLE_INIT, + IPSEC_INIT, MODULES_INIT, ALL_INIT /* All init stages completed */ }; @@ -130,6 +131,9 @@ int _odp_ishm_init_local(void); int _odp_ishm_term_global(void); int _odp_ishm_term_local(void); +int odp_ipsec_init_global(void); +int odp_ipsec_term_global(void); + int _odp_modules_init_global(void); int cpuinfo_parser(FILE *file, system_info_t *sysinfo); diff --git a/platform/linux-generic/include/odp_ipsec_internal.h b/platform/linux-generic/include/odp_ipsec_internal.h new file mode 100644 index 00000000..c7620b88 --- /dev/null +++ b/platform/linux-generic/include/odp_ipsec_internal.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2017, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * ODP internal IPsec routines + */ + +#ifndef ODP_IPSEC_INTERNAL_H_ +#define ODP_IPSEC_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** @ingroup odp_ipsec + * @{ + */ + +typedef ODP_HANDLE_T(odp_ipsec_op_result_event_t); + +#define ODP_IPSEC_OP_RESULT_EVENT_INVALID \ + _odp_cast_scalar(odp_ipsec_op_result_event_t, 0xffffffff) + +/** + * Get ipsec_op_result_event handle from event + * + * Converts an ODP_EVENT_IPSEC_RESULT_EVENT type event to an IPsec result event. + * + * @param ev Event handle + * + * @return IPsec result handle + * + * @see odp_event_type() + */ +odp_ipsec_op_result_event_t odp_ipsec_op_result_event_from_event(odp_event_t ev); + +/** + * Convert IPsec result event handle to event + * + * @param res IPsec result handle + * + * @return Event handle + */ +odp_event_t odp_ipsec_op_result_event_to_event(odp_ipsec_op_result_event_t res); + +/** + * Free IPsec result event + * + * Frees the ipsec_op_result_event into the ipsec_op_result_event pool it was allocated from. + * + * @param res IPsec result handle + */ +void odp_ipsec_op_result_event_free(odp_ipsec_op_result_event_t res); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/linux-generic/include/protocols/ip.h b/platform/linux-generic/include/protocols/ip.h index 2b34a753..9f3e1616 100644 --- a/platform/linux-generic/include/protocols/ip.h +++ b/platform/linux-generic/include/protocols/ip.h @@ -89,6 +89,58 @@ typedef struct ODP_PACKED { ODP_STATIC_ASSERT(sizeof(_odp_ipv4hdr_t) == _ODP_IPV4HDR_LEN, "_ODP_IPV4HDR_T__SIZE_ERROR"); +/** + * Checksum + * + * @param buffer calculate chksum for buffer + * @param len buffer length + * + * @return checksum value in host cpu order + */ +static inline odp_u16sum_t _odp_chksum(void *buffer, int len) +{ + uint16_t *buf = (uint16_t *)buffer; + uint32_t sum = 0; + uint16_t result; + + for (sum = 0; len > 1; len -= 2) + sum += *buf++; + + if (len == 1) + sum += *(unsigned char *)buf; + + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + result = ~sum; + + return (__odp_force odp_u16sum_t) result; +} + +/** + * Calculate and fill in IPv4 checksum + * + * @note when using this api to populate data destined for the wire + * odp_cpu_to_be_16() can be used to remove sparse warnings + * + * @param pkt ODP packet + * + * @return IPv4 checksum in host cpu order, or 0 on failure + */ +static inline odp_u16sum_t _odp_ipv4_csum_update(odp_packet_t pkt) +{ + uint16_t *w; + _odp_ipv4hdr_t *ip; + int nleft = sizeof(_odp_ipv4hdr_t); + + ip = (_odp_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + if (ip == NULL) + return 0; + + w = (uint16_t *)(void *)ip; + ip->chksum = _odp_chksum(w, nleft); + return ip->chksum; +} + /** IPv6 version */ #define _ODP_IPV6 6 diff --git a/platform/linux-generic/odp_event.c b/platform/linux-generic/odp_event.c index d71f4464..1a2cc0aa 100644 --- a/platform/linux-generic/odp_event.c +++ b/platform/linux-generic/odp_event.c @@ -7,10 +7,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -34,6 +36,9 @@ void odp_event_free(odp_event_t event) case ODP_EVENT_CRYPTO_COMPL: odp_crypto_compl_free(odp_crypto_compl_from_event(event)); break; + case ODP_EVENT_IPSEC_RESULT: + odp_ipsec_op_result_event_free(odp_ipsec_op_result_event_from_event(event)); + break; default: ODP_ABORT("Invalid event type: %d\n", odp_event_type(event)); } diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c index 685e02fa..bebcc62e 100644 --- a/platform/linux-generic/odp_init.c +++ b/platform/linux-generic/odp_init.c @@ -266,6 +266,12 @@ int odp_init_global(odp_instance_t *instance, } stage = NAME_TABLE_INIT; + if (odp_ipsec_init_global()) { + ODP_ERR("ODP IPsec init failed.\n"); + goto init_failed; + } + stage = IPSEC_INIT; + if (_odp_modules_init_global()) { ODP_ERR("ODP modules init failed\n"); goto init_failed; @@ -296,6 +302,13 @@ int _odp_term_global(enum init_stage stage) switch (stage) { case ALL_INIT: case MODULES_INIT: + case IPSEC_INIT: + if (odp_ipsec_term_global()) { + ODP_ERR("ODP IPsec term failed.\n"); + rc = -1; + } + /* Fall through */ + case NAME_TABLE_INIT: if (_odp_int_name_tbl_term_global()) { ODP_ERR("Name table term failed.\n"); diff --git a/platform/linux-generic/odp_ipsec.c b/platform/linux-generic/odp_ipsec.c index 10918dfb..2cc4c690 100644 --- a/platform/linux-generic/odp_ipsec.c +++ b/platform/linux-generic/odp_ipsec.c @@ -5,56 +5,392 @@ */ #include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include #include +#include + +#define ODP_CONFIG_IPSEC_SAS 8 + +#define MAX_IV_LEN 32 /**< Maximum IV length in bytes */ + +typedef struct ipsec_sa_t { + odp_ticketlock_t lock ODP_ALIGNED_CACHE; + int reserved; + odp_ipsec_sa_t ipsec_sa_hdl; + uint32_t ipsec_sa_idx; + + odp_crypto_session_t session; + odp_bool_t in_place; + void *context; + odp_queue_t queue; + + uint32_t ah_icv_len; + uint32_t esp_iv_len; + uint32_t esp_block_len; + uint32_t spi; + uint32_t seq; + uint8_t iv[MAX_IV_LEN]; /**< ESP IV storage */ +} ipsec_sa_t; + +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; + +typedef struct odp_ipsec_ctx_s odp_ipsec_ctx_t; + +typedef void (*odp_ipsecproc_t)(odp_packet_t pkt, odp_ipsec_ctx_t *ctx); + +/** + * Per packet IPsec processing context + */ +struct odp_ipsec_ctx_s { + odp_buffer_t buffer; /**< Buffer for context */ + odp_ipsec_ctx_t *next; /**< Next context in event */ + + uint8_t ip_tos; /**< Saved IP TOS value */ + uint16_t ip_frag_offset; /**< Saved IP flags value */ + uint8_t ip_ttl; /**< Saved IP TTL value */ + int hdr_len; /**< Length of IPsec headers */ + int trl_len; /**< Length of IPsec trailers */ + uint16_t tun_hdr_offset; /**< Offset of tunnel header from + buffer start */ + uint16_t ah_offset; /**< Offset of AH header from buffer start */ + uint16_t esp_offset; /**< Offset of ESP header from buffer start */ + + odp_ipsecproc_t postprocess; + odp_ipsec_sa_t sa; + odp_crypto_op_result_t crypto; + odp_ipsec_op_status_t status; + + /* Input only */ + uint32_t src_ip; /**< SA source IP address */ + uint32_t dst_ip; /**< SA dest IP address */ + + /* Output only */ + uint32_t *ah_seq; /**< AH sequence number location */ + uint32_t *esp_seq; /**< ESP sequence number location */ + uint16_t *tun_hdr_id; /**< Tunnel header ID > */ +}; + +typedef struct { + /* common buffer header */ + odp_buffer_hdr_t buf_hdr; + odp_ipsec_ctx_t *ctx; +} odp_ipsec_op_result_event_hdr_t; + +#define SHM_CTX_POOL_BUF_COUNT 1024 + +static odp_pool_t odp_odp_ipsec_ctx_pool = ODP_POOL_INVALID; +static odp_pool_t odp_ipsec_op_result_pool = ODP_POOL_INVALID; + +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_init_global(void) +{ + uint32_t i; + odp_shm_t shm; + odp_pool_param_t params; + + /* Create context buffer pool */ + params.buf.size = sizeof(odp_ipsec_ctx_t); + params.buf.align = 0; + params.buf.num = SHM_CTX_POOL_BUF_COUNT; + params.type = ODP_POOL_BUFFER; + + odp_odp_ipsec_ctx_pool = odp_pool_create("odp_odp_ipsec_ctx_pool", ¶ms); + if (ODP_POOL_INVALID == odp_odp_ipsec_ctx_pool) { + ODP_ERR("Error: context pool create failed.\n"); + return -1; + } + + params.buf.size = sizeof(odp_ipsec_op_result_event_hdr_t); + params.buf.align = 0; + params.buf.num = SHM_CTX_POOL_BUF_COUNT; + params.type = ODP_POOL_BUFFER; + + odp_ipsec_op_result_pool = odp_pool_create("odp_ipsec_op_result_pool", ¶ms); + if (ODP_POOL_INVALID == odp_ipsec_op_result_pool) { + ODP_ERR("Error: result pool create failed.\n"); + odp_pool_destroy(odp_odp_ipsec_ctx_pool); + return -1; + } + + shm = odp_shm_reserve("_odp_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) { + odp_pool_destroy(odp_ipsec_op_result_pool); + odp_pool_destroy(odp_odp_ipsec_ctx_pool); + 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); + + odp_ticketlock_init(&ipsec_sa->lock); + ipsec_sa->ipsec_sa_hdl = ipsec_sa_index_to_handle(i); + ipsec_sa->ipsec_sa_idx = i; + } + + return 0; +} + +int odp_ipsec_term_global(void) +{ + int i; + ipsec_sa_t *ipsec_sa; + int ret = 0; + int rc = 0; + + ret = odp_pool_destroy(odp_odp_ipsec_ctx_pool); + if (ret < 0) { + ODP_ERR("ctx pool destroy failed"); + rc = -1; + } + + for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) { + ipsec_sa = ipsec_sa_entry(i); + + odp_ticketlock_lock(&ipsec_sa->lock); + if (ipsec_sa->reserved) { + ODP_ERR("Not destroyed ipsec_sa: %u\n", ipsec_sa->ipsec_sa_idx); + rc = -1; + } + ipsec_sa->reserved = 1; + odp_ticketlock_unlock(&ipsec_sa->lock); + } + + ret = odp_shm_free(ipsec_sa_tbl->shm); + if (ret < 0) { + ODP_ERR("shm free failed"); + rc = -1; + } + + return rc; +} + +static ipsec_sa_t *reserve_ipsec_sa(void) +{ + int i; + ipsec_sa_t *ipsec_sa; + + for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) { + ipsec_sa = ipsec_sa_entry(i); + + odp_ticketlock_lock(&ipsec_sa->lock); + if (ipsec_sa->reserved == 0) { + ipsec_sa->reserved = 1; + odp_ticketlock_unlock(&ipsec_sa->lock); + + return ipsec_sa; + } + odp_ticketlock_unlock(&ipsec_sa->lock); + } + + return NULL; +} int odp_ipsec_capability(odp_ipsec_capability_t *capa) { + int rc; + odp_crypto_capability_t crypto_capa; + memset(capa, 0, sizeof(odp_ipsec_capability_t)); + rc = odp_crypto_capability(&crypto_capa); + if (rc < 0) + return rc; + + capa->max_num_sa = ODP_CONFIG_IPSEC_SAS; + capa->op_mode_sync = 2; + capa->ciphers = crypto_capa.ciphers; + capa->auths = crypto_capa.auths; + return 0; } int odp_ipsec_cipher_capability(odp_cipher_alg_t cipher, odp_crypto_cipher_capability_t capa[], int num) { - (void)cipher; - (void)capa; - (void)num; - - return -1; + return odp_crypto_cipher_capability(cipher, capa, num); } int odp_ipsec_auth_capability(odp_auth_alg_t auth, odp_crypto_auth_capability_t capa[], int num) { - (void)auth; - (void)capa; - (void)num; - - return -1; + return odp_crypto_auth_capability(auth, capa, num); } void odp_ipsec_config_init(odp_ipsec_config_t *config) { memset(config, 0, sizeof(odp_ipsec_config_t)); + config->inbound_mode = ODP_IPSEC_OP_MODE_SYNC; + config->outbound_mode = ODP_IPSEC_OP_MODE_SYNC; + config->max_num_sa = ODP_CONFIG_IPSEC_SAS; + config->inbound.default_queue = ODP_QUEUE_INVALID; + config->inbound.lookup.min_spi = 0; + config->inbound.lookup.max_spi = UINT32_MAX; + config->outbound.default_queue = ODP_QUEUE_INVALID; } +static odp_ipsec_config_t ipsec_config; + int odp_ipsec_config(const odp_ipsec_config_t *config) { - (void)config; + /* FIXME: unsupported for now */ + if (ODP_IPSEC_OP_MODE_INLINE == config->outbound_mode) + return -1; - return -1; + /* FIXME: unsupported for now */ + if (ODP_IPSEC_OP_MODE_INLINE == config->inbound_mode) + return -1; + + ipsec_config = *config; + + return 0; } 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) { - (void)param; + ipsec_sa_t *ipsec_sa; + odp_crypto_session_param_t crypto_param; + odp_crypto_ses_create_err_t ses_create_rc; + + ipsec_sa = reserve_ipsec_sa(); + if (NULL == ipsec_sa) { + ODP_ERR("No more free SA\n"); + return ODP_IPSEC_SA_INVALID; + } + +#if 1 + ipsec_sa->in_place = false; +#else + ipsec_sa->in_place = true; +#endif + ipsec_sa->spi = param->spi; + ipsec_sa->seq = param->seq; + ipsec_sa->context = param->context; + ipsec_sa->queue = param->dest_queue; + + 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; + + // FIXME: is it possible to use ASYNC crypto with ASYNC IPsec? + crypto_param.pref_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->ah_icv_len = 0; + break; + case ODP_AUTH_ALG_MD5_HMAC: + case ODP_AUTH_ALG_MD5_96: + ipsec_sa->ah_icv_len = 12; + break; + case ODP_AUTH_ALG_SHA1_HMAC: + ipsec_sa->ah_icv_len = 12; + break; + case ODP_AUTH_ALG_SHA256_HMAC: + case ODP_AUTH_ALG_SHA256_128: + ipsec_sa->ah_icv_len = 16; + break; + case ODP_AUTH_ALG_SHA512_HMAC: + ipsec_sa->ah_icv_len = 32; + 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 = 0; + 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; + case ODP_CIPHER_ALG_AES_CBC: + case ODP_CIPHER_ALG_AES128_CBC: + case ODP_CIPHER_ALG_AES_GCM: + case ODP_CIPHER_ALG_AES128_GCM: + ipsec_sa->esp_iv_len = 16; + ipsec_sa->esp_block_len = 16; + break; + } + + /* Generate an IV */ + if (ipsec_sa->esp_iv_len) { + crypto_param.iv.data = ipsec_sa->iv; + crypto_param.iv.length = odp_random_data(crypto_param.iv.data, ipsec_sa->esp_iv_len, 1); + if (crypto_param.iv.length != ipsec_sa->esp_iv_len) + goto error; + } + + if (odp_crypto_session_create(&crypto_param, &ipsec_sa->session, &ses_create_rc)) + goto error; + if (ODP_CRYPTO_SES_CREATE_ERR_NONE != ses_create_rc) + goto error; + + return ipsec_sa->ipsec_sa_hdl; + +error: + odp_ticketlock_lock(&ipsec_sa->lock); + ipsec_sa->reserved = 1; + odp_ticketlock_unlock(&ipsec_sa->lock); return ODP_IPSEC_SA_INVALID; } @@ -68,41 +404,790 @@ int odp_ipsec_sa_disable(odp_ipsec_sa_t sa) int odp_ipsec_sa_destroy(odp_ipsec_sa_t sa) { - (void)sa; + ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa); + int rc = 0; - return -1; + odp_ticketlock_lock(&ipsec_sa->lock); + if (ipsec_sa->reserved) { + ODP_ERR("Destroying unallocated ipsec_sa: %u\n", ipsec_sa->ipsec_sa_idx); + rc = -1; + } else { + 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->reserved = 1; + } + odp_ticketlock_unlock(&ipsec_sa->lock); + + return rc; +} + +#define ipv4_data_p(ip) ((uint8_t *)((_odp_ipv4hdr_t *)ip + 1)) +#define ipv4_data_len(ip) (odp_be_to_cpu_16(ip->tot_len) - sizeof(_odp_ipv4hdr_t)) +static inline +void ipv4_adjust_len(_odp_ipv4hdr_t *ip, int adj) +{ + ip->tot_len = odp_cpu_to_be_16(odp_be_to_cpu_16(ip->tot_len) + adj); +} + +/** + * Verify crypto operation completed successfully + * + * @param status Pointer to cryto completion structure + * + * @return true if all OK else false + */ +static inline +odp_bool_t is_crypto_compl_status_ok(odp_crypto_compl_status_t *status) +{ + if (status->alg_err != ODP_CRYPTO_ALG_ERR_NONE) + return false; + if (status->hw_err != ODP_CRYPTO_HW_ERR_NONE) + return false; + return true; +} + +/** + * Allocate per packet processing context and associate it with + * packet buffer + * + * @param pkt Packet + * + * @return pointer to context area + */ +static +odp_ipsec_ctx_t *odp_ipsec_alloc_pkt_ctx(void) +{ + odp_buffer_t ctx_buf = odp_buffer_alloc(odp_odp_ipsec_ctx_pool); + odp_ipsec_ctx_t *ctx; + + if (odp_unlikely(ODP_BUFFER_INVALID == ctx_buf)) + return NULL; + + ctx = odp_buffer_addr(ctx_buf); + memset(ctx, 0, sizeof(*ctx)); + ctx->buffer = ctx_buf; + + return ctx; +} + +/** + * Release per packet resources + * + * @param ctx Packet context + */ +static +void odp_ipsec_free_pkt_ctx(odp_ipsec_ctx_t *ctx) +{ + if (ODP_PACKET_INVALID != ctx->crypto.pkt) + odp_packet_free(ctx->crypto.pkt); + + odp_buffer_free(ctx->buffer); +} + +odp_ipsec_op_result_event_t odp_ipsec_op_result_event_from_event(odp_event_t ev) +{ + if (odp_unlikely(ODP_EVENT_INVALID == ev)) + return ODP_IPSEC_OP_RESULT_EVENT_INVALID; + + if (odp_event_type(ev) != ODP_EVENT_IPSEC_RESULT) + ODP_ABORT("Event not an IPsec result"); + + return (odp_ipsec_op_result_event_t)ev; +} + +static odp_ipsec_op_result_event_hdr_t *ipsec_op_result_event_hdr_from_buf(odp_buffer_t buf) +{ + return (odp_ipsec_op_result_event_hdr_t *)(void *)buf_hdl_to_hdr(buf); +} + +static odp_ipsec_op_result_event_hdr_t *ipsec_op_result_event_hdr(odp_ipsec_op_result_event_t res) +{ + odp_buffer_t buf = odp_buffer_from_event(odp_ipsec_op_result_event_to_event(res)); + + return ipsec_op_result_event_hdr_from_buf(buf); +} + +odp_event_t odp_ipsec_op_result_event_to_event(odp_ipsec_op_result_event_t res) +{ + if (odp_unlikely(res == ODP_IPSEC_OP_RESULT_EVENT_INVALID)) + return ODP_EVENT_INVALID; + + return (odp_event_t)res; +} + +static +odp_ipsec_op_result_event_t odp_ipsec_op_result_event_alloc(void) +{ + odp_buffer_t buf = odp_buffer_alloc(odp_ipsec_op_result_pool); + + if (odp_unlikely(buf == ODP_BUFFER_INVALID)) + return ODP_IPSEC_OP_RESULT_EVENT_INVALID; + + _odp_buffer_event_type_set(buf, ODP_EVENT_IPSEC_RESULT); + + return odp_ipsec_op_result_event_from_event(odp_buffer_to_event(buf)); +} + +void odp_ipsec_op_result_event_free(odp_ipsec_op_result_event_t res) +{ + odp_event_t ev = odp_ipsec_op_result_event_to_event(res); + odp_ipsec_op_result_event_hdr_t *res_hdr; + + res_hdr = ipsec_op_result_event_hdr(res); + while (NULL != res_hdr->ctx) { + odp_ipsec_ctx_t *ctx = res_hdr->ctx; + + res_hdr->ctx = ctx->next; + odp_ipsec_free_pkt_ctx(ctx); + } + + odp_buffer_free(odp_buffer_from_event(ev)); +} + +static +void odp_ipsec_postprocess(odp_packet_t pkt, odp_ipsec_ctx_t *ctx) +{ + _odp_ipv4hdr_t *ip; + int hdr_len; + int trl_len = 0; + + ip = (_odp_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + hdr_len = ctx->hdr_len; + + /* + * Finish auth + */ + if (ctx->ah_offset) { + uint8_t *buf = odp_packet_data(pkt); + _odp_ahhdr_t *ah; + + ah = (_odp_ahhdr_t *)(ctx->ah_offset + buf); + ip->proto = ah->next_header; + } + + /* + * Finish cipher by finding ESP trailer and processing + * + * FIXME: ESP authentication ICV not supported + */ + if (ctx->esp_offset) { + uint8_t *eop = (uint8_t *)(ip) + odp_be_to_cpu_16(ip->tot_len); + _odp_esptrl_t *esp_t = (_odp_esptrl_t *)(eop) - 1; + + ip->proto = esp_t->next_header; + trl_len += esp_t->pad_len + sizeof(*esp_t); + } + + /* We have a tunneled IPv4 packet */ + if (ip->proto == _ODP_IPV4) { + odp_packet_pull_head(pkt, sizeof(*ip)); + } else { + /* Finalize the IPv4 header */ + ipv4_adjust_len(ip, -(hdr_len + trl_len)); + ip->ttl = ctx->ip_ttl; + ip->tos = ctx->ip_tos; + ip->frag_offset = odp_cpu_to_be_16(ctx->ip_frag_offset); + ip->chksum = 0; + _odp_ipv4_csum_update(pkt); + + /* Correct the packet length and move payload into position */ + memmove(((uint8_t *)ip) + hdr_len, ip, sizeof(*ip)); + } + + odp_packet_pull_head(pkt, hdr_len); + odp_packet_pull_tail(pkt, trl_len); } +static +int odp_ipsec_finish(odp_ipsec_ctx_t *ctx, + odp_ipsec_packet_result_t *res, + odp_packet_t *pkt) +{ + odp_crypto_op_result_t *result = &ctx->crypto; + + res->status = ctx->status; + + /* Check crypto result */ + if (!result->ok) { + if (!is_crypto_compl_status_ok(&result->cipher_status)) { + res->status.error.alg = 1; + return -1; + } + if (!is_crypto_compl_status_ok(&result->auth_status)) { + res->status.error.auth = 1; + return -1; + } + } + + if (ctx->postprocess) { + ctx->postprocess(result->pkt, ctx); + ctx->postprocess = NULL; + } + + *pkt = result->pkt; + result->pkt = ODP_PACKET_INVALID; + + res->sa = ctx->sa; + + return 1; +} + +static +int odp_ipsec_in_single(odp_packet_t pkt, + odp_ipsec_sa_t sa, + odp_ipsec_ctx_t *ctx) +{ + ipsec_sa_t *ipsec_sa; + uint8_t *buf = odp_packet_data(pkt); + _odp_ipv4hdr_t *ip = (_odp_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + int hdr_len; + _odp_ahhdr_t *ah = NULL; + _odp_esphdr_t *esp = NULL; + odp_crypto_op_param_t params; + odp_bool_t posted = 0; + uint8_t *in = ipv4_data_p(ip); + int rc; + + ctx->status.all_error = 0; + ctx->status.all_flag = 0; + + if (ODP_IPSEC_SA_INVALID == sa) { + ctx->status.error.sa_lookup = 1; + return -1; + } + + ipsec_sa = ipsec_sa_entry_from_hdl(sa); + if (odp_unlikely(NULL == ipsec_sa)) { + ctx->status.error.alg = 1; + return -1; + } + + /* Check IP header for IPSec protocols and look it up */ + if (_ODP_IPPROTO_AH == ip->proto) { + ah = (_odp_ahhdr_t *)in; + in += ((ah)->ah_len + 2) * 4; + } else if (_ODP_IPPROTO_ESP == ip->proto) { + esp = (_odp_esphdr_t *)in; + in += sizeof(_odp_esphdr_t); + } else { + ctx->status.error.proto = 1; + return -1; + } + + hdr_len = in - (ipv4_data_p(ip)); + + /* Account for configured ESP IV length in packet */ + hdr_len += ipsec_sa->esp_iv_len; + + /* Initialize parameters block */ + memset(¶ms, 0, sizeof(params)); + params.session = ipsec_sa->session; + params.pkt = pkt; + params.ctx = ctx; + + /*Save everything to context */ + ctx->ip_tos = ip->tos; + ctx->ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset); + ctx->ip_ttl = ip->ttl; + ctx->ah_offset = ah ? ((uint8_t *)ah) - buf : 0; + ctx->esp_offset = esp ? ((uint8_t *)esp) - buf : 0; + ctx->hdr_len = hdr_len; + ctx->trl_len = 0; + //ctx->src_ip = ipsec_sa->src_ip; + //ctx->dst_ip = ipsec_sa->dst_ip; + + ctx->postprocess = odp_ipsec_postprocess; + ctx->sa = sa; + + /* If authenticating, zero the mutable fields build the request */ + if (ah) { + ip->chksum = 0; + ip->tos = 0; + ip->frag_offset = 0; + ip->ttl = 0; + + params.auth_range.offset = ((uint8_t *)ip) - buf; + params.auth_range.length = odp_be_to_cpu_16(ip->tot_len); + params.hash_result_offset = ah->icv - buf; + } + + /* If deciphering build request */ + if (esp) { + params.cipher_range.offset = ipv4_data_p(ip) + hdr_len - buf; + params.cipher_range.length = ipv4_data_len(ip) - hdr_len; + params.override_iv_ptr = esp->iv; + } + + /* Create new packet after all length extensions */ + params.out_pkt = ipsec_sa->in_place ? pkt : + odp_packet_alloc(odp_packet_pool(pkt), + odp_packet_len(pkt)); + odp_packet_user_ptr_set(params.out_pkt, + odp_packet_user_ptr(params.pkt)); + + rc = odp_crypto_operation(¶ms, &posted, &ctx->crypto); + if (rc < 0) { + ODP_DBG("Crypto failed\n"); + ctx->status.error.alg = 1; + + return rc; + } + + ODP_ASSERT(!posted); + + return 0; +} + +/** Helper for calculating encode length using data length and block size */ +#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b) + +static +int odp_ipsec_out_single(odp_packet_t pkt, + odp_ipsec_sa_t sa, + odp_ipsec_ctx_t *ctx) +{ + ipsec_sa_t *ipsec_sa; + uint8_t *buf = odp_packet_data(pkt); + _odp_ipv4hdr_t *ip = (_odp_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + uint16_t ip_data_len = ipv4_data_len(ip); + uint8_t *ip_data = ipv4_data_p(ip); + odp_crypto_op_param_t params; + odp_bool_t posted = 0; + int hdr_len = 0; + int trl_len = 0; + _odp_ahhdr_t *ah = NULL; + _odp_esphdr_t *esp = NULL; + int rc; + + ctx->status.all_error = 0; + ctx->status.all_flag = 0; + + if (ODP_IPSEC_SA_INVALID == sa) { + ctx->status.error.sa_lookup = 1; + return -1; + } + + ipsec_sa = ipsec_sa_entry_from_hdl(sa); + if (odp_unlikely(NULL == ipsec_sa)) { + ctx->status.error.alg = 1; + return -1; + } + + /* Initialize parameters block */ + memset(¶ms, 0, sizeof(params)); + params.session = ipsec_sa->session; + params.pkt = pkt; + params.ctx = ctx; + + /* Save IPv4 stuff */ + ctx->ip_tos = ip->tos; + ctx->ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset); + ctx->ip_ttl = ip->ttl; + + ctx->postprocess = NULL; + ctx->sa = sa; + +#if 0 + if (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL) { + hdr_len += sizeof(_odp_ipv4hdr_t); + ip_data = (uint8_t *)ip; + ip_data_len += sizeof(_odp_ipv4hdr_t); + } +#endif + /* Compute ah and esp, determine length of headers, move the data */ + if (ipsec_sa->ah_icv_len) { + ah = (_odp_ahhdr_t *)(ip_data + hdr_len); + hdr_len += sizeof(_odp_ahhdr_t); + hdr_len += ipsec_sa->ah_icv_len; + } + if (ipsec_sa->esp_iv_len) { + esp = (_odp_esphdr_t *)(ip_data + hdr_len); + hdr_len += sizeof(_odp_esphdr_t); + hdr_len += ipsec_sa->esp_iv_len; + } + memmove(ip_data + hdr_len, ip_data, ip_data_len); + ip_data += hdr_len; + + /* update outer header in tunnel mode */ +#if 0 + if (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL) { + /* tunnel addresses */ + ip->src_addr = odp_cpu_to_be_32(ipsec_sa->tun_src_ip); + ip->dst_addr = odp_cpu_to_be_32(ipsec_sa->tun_dst_ip); + } +#endif + + /* For cipher, compute encrypt length, build headers and request */ + if (esp) { + uint32_t encrypt_len; + _odp_esptrl_t *esp_t; + + encrypt_len = ESP_ENCODE_LEN(ip_data_len + sizeof(*esp_t), + ipsec_sa->esp_block_len); + trl_len = encrypt_len - ip_data_len; + + esp->spi = odp_cpu_to_be_32(ipsec_sa->spi); + memcpy(esp + 1, ipsec_sa->iv, ipsec_sa->esp_iv_len); + + esp_t = (_odp_esptrl_t *)(ip_data + encrypt_len) - 1; + esp_t->pad_len = trl_len - sizeof(*esp_t); +#if 0 + if (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL) + esp_t->next_header = _ODP_IPV4; + else +#endif + esp_t->next_header = ip->proto; + ip->proto = _ODP_IPPROTO_ESP; + + params.cipher_range.offset = ip_data - buf; + params.cipher_range.length = encrypt_len; + } + + /* For authentication, build header clear mutables and build request */ + if (ah) { + memset(ah, 0, sizeof(*ah) + ipsec_sa->ah_icv_len); + ah->spi = odp_cpu_to_be_32(ipsec_sa->spi); + ah->ah_len = 1 + (ipsec_sa->ah_icv_len / 4); +#if 0 + if (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL && !esp) + ah->next_header = _ODP_IPV4; + else +#endif + ah->next_header = ip->proto; + ip->proto = _ODP_IPPROTO_AH; + + ip->chksum = 0; + ip->tos = 0; + ip->frag_offset = 0; + ip->ttl = 0; + + params.auth_range.offset = ((uint8_t *)ip) - buf; + params.auth_range.length = + odp_be_to_cpu_16(ip->tot_len) + (hdr_len + trl_len); + params.hash_result_offset = ah->icv - buf; + } + + /* Set IPv4 length before authentication */ + if (!odp_packet_push_tail(pkt, hdr_len + trl_len)) { + ctx->status.error.alg = 1; + return -1; + } + + ipv4_adjust_len(ip, hdr_len + trl_len); + + /* Save remaining context */ + ctx->hdr_len = hdr_len; + ctx->trl_len = trl_len; + ctx->ah_offset = ah ? ((uint8_t *)ah) - buf : 0; + ctx->esp_offset = esp ? ((uint8_t *)esp) - buf : 0; +#if 0 + ctx->tun_hdr_offset = (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL) ? + ((uint8_t *)ip - buf) : 0; +#else + ctx->tun_hdr_offset = 0; +#endif + ctx->ah_seq = &ipsec_sa->seq; + ctx->esp_seq = &ipsec_sa->seq; + //ctx->tun_hdr_id = &ipsec_sa->tun_hdr_id; + + // FIXME: locking !!!!! + if (ctx->ah_offset) { + _odp_ahhdr_t *ah; + + ah = (_odp_ahhdr_t *)(ctx->ah_offset + buf); + ah->seq_no = odp_cpu_to_be_32((*ctx->ah_seq)++); + } + if (ctx->esp_offset) { + _odp_esphdr_t *esp; + + esp = (_odp_esphdr_t *)(ctx->esp_offset + buf); + esp->seq_no = odp_cpu_to_be_32((*ctx->esp_seq)++); + } + if (ctx->tun_hdr_offset) { + _odp_ipv4hdr_t *ip; + int ret; + + ip = (_odp_ipv4hdr_t *)(ctx->tun_hdr_offset + buf); + ip->id = odp_cpu_to_be_16((*ctx->tun_hdr_id)++); + if (!ip->id) { + /* re-init tunnel hdr id */ + ret = odp_random_data((uint8_t *)ctx->tun_hdr_id, + sizeof(*ctx->tun_hdr_id), + 1); + if (ret != sizeof(*ctx->tun_hdr_id)) + abort(); + } + } + + /* Create new packet after all length extensions */ + params.out_pkt = ipsec_sa->in_place ? pkt : + odp_packet_alloc(odp_packet_pool(pkt), + odp_packet_len(pkt)); + odp_packet_user_ptr_set(params.out_pkt, odp_packet_user_ptr(params.pkt)); + + rc = odp_crypto_operation(¶ms, &posted, &ctx->crypto); + if (rc < 0) { + ODP_DBG("Crypto failed\n"); + ctx->status.error.alg = 1; + + return rc; + } + + ODP_ASSERT(!posted); + + return 0; +} + +#if 0 +static odp_ipsec_op_opt_t default_opt = { + .mode = ODP_IPSEC_FRAG_DISABLED, +}; +#endif + int odp_ipsec_in(const odp_ipsec_op_param_t *input, odp_ipsec_op_result_t *output) { - (void)input; - (void)output; + unsigned in_pkt = 0; + unsigned out_pkt = 0; + unsigned sa_idx = 0; + unsigned opt_idx = 0; + unsigned sa_inc = (input->num_sa > 1) ? 1 : 0; + unsigned opt_inc = (input->num_opt > 1) ? 1 : 0; - return -1; + while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) { + odp_ipsec_sa_t sa; + odp_ipsec_packet_result_t *res = &output->res[out_pkt]; + odp_ipsec_ctx_t ctx; + int ret; + + if (0 == input->num_sa) + sa = ODP_IPSEC_SA_INVALID; + else + sa = input->sa[sa_idx]; + +#if 0 + odp_ipsec_op_opt_t *opt; + + if (0 == input->num_opt) + opt = &default_opt; + else + opt = &input->opt[opt_idx]; +#endif + + res->num_out = 1; + output->pkt[out_pkt] = input->pkt[in_pkt]; + + if (ODP_IPSEC_SA_INVALID == sa) { + res->status.error.sa_lookup = 1; + goto out; + } + + if (odp_ipsec_in_single(input->pkt[in_pkt], sa, &ctx) < 0) { + res->status.error.alg = 1; + goto out; + } + + ret = odp_ipsec_finish(&ctx, res, &output->pkt[out_pkt]); + if (ret < 0) + res->status.error.alg = 1; + +out: + in_pkt++; + out_pkt++; + sa_idx += sa_inc; + opt_idx += opt_inc; + } + + return in_pkt; } int odp_ipsec_out(const odp_ipsec_op_param_t *input, - odp_ipsec_op_result_t *output) + odp_ipsec_op_result_t *output) { - (void)input; - (void)output; + unsigned in_pkt = 0; + unsigned out_pkt = 0; + unsigned sa_idx = 0; + unsigned opt_idx = 0; + unsigned sa_inc = (input->num_sa > 1) ? 1 : 0; + unsigned opt_inc = (input->num_opt > 1) ? 1 : 0; - return -1; + while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) { + odp_ipsec_sa_t sa; + odp_ipsec_packet_result_t *res = &output->res[out_pkt]; + odp_ipsec_ctx_t ctx; + int ret; + + if (0 == input->num_sa) + sa = ODP_IPSEC_SA_INVALID; + else + sa = input->sa[sa_idx]; + +#if 0 + odp_ipsec_op_opt_t *opt; + + if (0 == input->num_opt) + opt = &default_opt; + else + opt = &input->opt[opt_idx]; +#endif + + res->num_out = 1; + output->pkt[out_pkt] = input->pkt[in_pkt]; + + if (odp_ipsec_out_single(input->pkt[in_pkt], sa, &ctx) < 0) { + res->status.error.alg = 1; + goto out; + } + + ret = odp_ipsec_finish(&ctx, res, &output->pkt[out_pkt]); + if (ret < 0) + res->status.error.alg = 1; + +out: + in_pkt++; + out_pkt++; + sa_idx += sa_inc; + opt_idx += opt_inc; + } + + return in_pkt; } int odp_ipsec_in_enq(const odp_ipsec_op_param_t *input) { - (void)input; + unsigned in_pkt = 0; + unsigned sa_idx = 0; + unsigned opt_idx = 0; + unsigned sa_inc = (input->num_sa > 1) ? 1 : 0; + unsigned opt_inc = (input->num_opt > 1) ? 1 : 0; - return -1; + while (in_pkt < input->num_pkt) { + odp_ipsec_op_result_event_t ipsec_ev; + odp_event_t ev; + odp_ipsec_op_result_event_hdr_t *res_hdr; + odp_ipsec_ctx_t *ctx; + odp_ipsec_sa_t sa; + odp_queue_t queue; + + ipsec_ev = odp_ipsec_op_result_event_alloc(); + if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev) + break; + + ev = odp_ipsec_op_result_event_to_event(ipsec_ev); + + res_hdr = ipsec_op_result_event_hdr(ipsec_ev); + + ctx = odp_ipsec_alloc_pkt_ctx(); + if (NULL == ctx) { + odp_event_free(ev); + break; + } + + res_hdr->ctx = ctx; + + if (0 == input->num_sa) + sa = ODP_IPSEC_SA_INVALID; + else + sa = input->sa[sa_idx]; + +#if 0 + odp_ipsec_op_opt_t *opt; + + if (0 == input->num_opt) + opt = &default_opt; + else + opt = &input->opt[opt_idx]; +#endif + + if (odp_ipsec_in_single(input->pkt[in_pkt], sa, ctx) < 0) + ctx->status.error.alg = 1; + + in_pkt++; + sa_idx += sa_inc; + opt_idx += opt_inc; + + if (ODP_IPSEC_SA_INVALID == sa) + queue = ipsec_config.inbound.default_queue; + else + queue = ipsec_sa_entry_from_hdl(sa)->queue; + + if (odp_queue_enq(queue, ev)) { + odp_event_free(ev); + return -1; + } + } + + return in_pkt; } int odp_ipsec_out_enq(const odp_ipsec_op_param_t *input) { - (void)input; + unsigned in_pkt = 0; + unsigned sa_idx = 0; + unsigned opt_idx = 0; + unsigned sa_inc = (input->num_sa > 1) ? 1 : 0; + unsigned opt_inc = (input->num_opt > 1) ? 1 : 0; - return -1; + while (in_pkt < input->num_pkt) { + odp_ipsec_op_result_event_t ipsec_ev; + odp_event_t ev; + odp_ipsec_op_result_event_hdr_t *res_hdr; + odp_ipsec_ctx_t *ctx; + odp_ipsec_sa_t sa; + odp_queue_t queue; + + ipsec_ev = odp_ipsec_op_result_event_alloc(); + if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev) + break; + + ev = odp_ipsec_op_result_event_to_event(ipsec_ev); + + res_hdr = ipsec_op_result_event_hdr(ipsec_ev); + + ctx = odp_ipsec_alloc_pkt_ctx(); + if (NULL == ctx) { + odp_event_free(ev); + break; + } + + res_hdr->ctx = ctx; + + if (0 == input->num_sa) + sa = ODP_IPSEC_SA_INVALID; + else + sa = input->sa[sa_idx]; + +#if 0 + odp_ipsec_op_opt_t *opt; + + if (0 == input->num_opt) + opt = &default_opt; + else + opt = &input->opt[opt_idx]; +#endif + + if (odp_ipsec_out_single(input->pkt[in_pkt], sa, ctx) < 0) + ctx->status.error.alg = 1; + + in_pkt++; + sa_idx += sa_inc; + opt_idx += opt_inc; + + if (ODP_IPSEC_SA_INVALID == sa) + queue = ipsec_config.outbound.default_queue; + else + queue = ipsec_sa_entry_from_hdl(sa)->queue; + + if (odp_queue_enq(queue, ev)) { + odp_event_free(ev); + return -1; + } + } + + return in_pkt; } int odp_ipsec_out_inline(const odp_ipsec_op_param_t *op_param, @@ -117,9 +1202,36 @@ int odp_ipsec_out_inline(const odp_ipsec_op_param_t *op_param, int odp_ipsec_result(odp_ipsec_op_result_t *result, odp_event_t event) { (void)result; - (void)event; - return -1; + odp_ipsec_op_result_event_t ipsec_ev; + odp_ipsec_op_result_event_hdr_t *res_hdr; + unsigned out_pkt; + odp_ipsec_ctx_t *ctx; + + ipsec_ev = odp_ipsec_op_result_event_from_event(event); + if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev) + return -1; + + res_hdr = ipsec_op_result_event_hdr(ipsec_ev); + + for (out_pkt = 0; out_pkt < result->num_pkt; out_pkt++) { + odp_ipsec_packet_result_t *res = &result->res[out_pkt]; + odp_packet_t *pkt = &result->pkt[out_pkt]; + odp_ipsec_ctx_t *ctx = res_hdr->ctx; + int ret; + + res_hdr->ctx = ctx->next; + ret = odp_ipsec_finish(ctx, res, pkt); + if (ret < 0) + res->status.error.alg = 1; + + odp_ipsec_free_pkt_ctx(ctx); + } + + for (ctx = res_hdr->ctx; NULL != ctx; ctx = ctx->next) + out_pkt++; + + return out_pkt; } int odp_ipsec_status(odp_ipsec_status_t *status, odp_event_t event) @@ -140,9 +1252,9 @@ int odp_ipsec_mtu_update(odp_ipsec_sa_t sa, uint32_t mtu) void *odp_ipsec_sa_context(odp_ipsec_sa_t sa) { - (void)sa; + ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa); - return NULL; + return ipsec_sa->context; } uint64_t odp_ipsec_sa_to_u64(odp_ipsec_sa_t sa)