Hi,

Some comments on the patch:

> >>  - No tunnel support
> >>  - No header modification according to RFCs

There is some tunneling and header modification code in the patch.
Now it is not easy to tell which part of the code is supposed to work
and which not. Other limitations include: There is no replay protection;
IV is not generated properly for each packet but is a constant.

The code does not have the two-phase SA deletion of the latest IPsec
API. An application has to first call odp_ipsec_sa_disable() and
later, when disabling has completed, odp_ipsec_sa_destroy().

The code performs potentially unaligned accesses when accessing
and modifying packet headers. I wonder if that is ok in ODP or
whether the linux generic implementation needs to support targets
that do not support unaligned accesses.

The code makes assumptions that the relevant packet headers
are in one contiguous segment of a packet. Even if that assumption
held for packet received from the network, it does not necessarily
hold for packets processed by the application. Related to this,
the success of odp_packet_pull_head() etc are not checked but they
cannot be assumed to always succeed.

There is also an assumption that the L3 header is in the same
segment as the start of the packet (see the pointer arithmetic
in odp_ipsec_in_single()).

There is an assumption that the size of an IP header equals
sizeof(_odp_ipv4hdr_t) which is not true when the IP packet
has options.

SA creation does not work for AES-GCM, which requires that auth
algo is set to ODP_AUTH_ALG_AES_GCM and cipher algo is set to
ODP_CIPHER_ALG_AES_GCM. I suppose ICV length in that case would
always be 16 since the IPsec API lacks a way to specify it (it
assumes that the algorithm fully determines the ICV length which
is not true for AES-GCM).

The meaning of ipsec_sa->reserved seems to vary. Sometimes 1
means reserved (as in: in use) and sometimes 0.

The comment of odp_ipsec_alloc_pkt_ctx() does not quite match
the code which does not associate the ctx with a packet buffer.

As discussed in one arch meeting, checking whether an SA handle
passed by the application is valid is unnecessary.

odp_ipsec_out_single() zeroes several packet fields for AH
processing but does not restore the fields anywhere.

The content of user area is not copied from input packets
to output packets.

The IPv4 ID field generation for the outer IP header of tunneled
packets is weird. There is no point in randomizing its value
at wraparound.

A few of the comments are written as if AH is the authentication
and ESP is the encryption of an IPsec transformation, even
though AH versus ESP is the protocol selection and then each
protocol can include authentication and encryption (well, except
that AH cannot have encryption). The code could also be written
so that AH and ESP are mutually exclusive for one SA (and
the IPsec API does not support AH+ESP in a single operation).

odp_ipsec_result() does not work according to the API spec,
but probably this is because the patch was not meant for the
latest API-NEXT but on top of other patches that modified
the API?

        Janne


> -----Original Message-----
> From: lng-odp [mailto:lng-odp-boun...@lists.linaro.org] On Behalf Of Dmitry 
> Eremin-
> Solenikov
> Sent: Tuesday, April 11, 2017 4:42 PM
> To: lng-odp@lists.linaro.org
> Subject: [lng-odp] [API-NEXT][RFC][rebased] linux-gen: ipsec: draft IPsec 
> implementation
> 
> 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 <dmitry.ereminsoleni...@linaro.org>
> ---
>  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
> 
> 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 <odp/api/std_types.h>
> +#include <odp/api/plat/strong_types.h>
> +
> +/** @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 <odp/api/event.h>
>  #include <odp/api/buffer.h>
>  #include <odp/api/crypto.h>
> +#include <odp/api/ipsec.h>
>  #include <odp/api/packet.h>
>  #include <odp/api/timer.h>
>  #include <odp/api/pool.h>
>  #include <odp_buffer_internal.h>
> +#include <odp_ipsec_internal.h>
>  #include <odp_buffer_inlines.h>
>  #include <odp_debug_internal.h>
> 
> @@ -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 <odp/api/ipsec.h>
> +#include <odp/api/packet.h>
> +#include <odp/api/shared_memory.h>
> +#include <odp/api/ticketlock.h>
> +
> +#include <odp_buffer_internal.h>
> +#include <odp_buffer_inlines.h>
> +#include <odp_debug_internal.h>
> +#include <odp_ipsec_internal.h>
> +#include <odp_pool_internal.h>
> +
> +#include <odp/api/plat/ticketlock_inlines.h>
> +
> +#include <protocols/ip.h>
> +#include <protocols/ipsec.h>
> 
>  #include <string.h>
> +#include <stdbool.h>
> +
> +#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", 
> &params);
> +     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", 
> &params);
> +     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(&params, 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(&params, &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(&params, 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(&params, &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)
> --
> 2.11.0

Reply via email to