diff mbox

[API-NEXT,RFC,rebased] linux-gen: ipsec: draft IPsec implementation

Message ID 20170411134132.9236-1-dmitry.ereminsolenikov@linaro.org
State New
Headers show

Commit Message

Dmitry Eremin-Solenikov April 11, 2017, 1:41 p.m. UTC
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.ereminsolenikov@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

-- 
2.11.0

Comments

Bill Fischofer April 13, 2017, 9:42 p.m. UTC | #1
Next version should be marked API-NEXT, whether or not it is still an
RFC since IPsec APIs cannot move to master until we have a complete
implementation / validation test suite.

I realize this is an RFC, but it doesn't apply to the current api-next:

Applying: linux-gen: ipsec: draft IPsec implementation
Checking patch platform/linux-generic/include/odp_internal.h...
Checking patch platform/linux-generic/include/odp_ipsec_internal.h...
Checking patch platform/linux-generic/include/protocols/ip.h...
Checking patch platform/linux-generic/odp_event.c...
Checking patch platform/linux-generic/odp_init.c...
Checking patch platform/linux-generic/odp_ipsec.c...
error: while searching for:
 */

#include <odp/api/ipsec.h>

#include <string.h>

int odp_ipsec_capability(odp_ipsec_capability_t *capa)
{
memset(capa, 0, sizeof(odp_ipsec_capability_t));

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;
}

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;
}

void odp_ipsec_config_init(odp_ipsec_config_t *config)
{
memset(config, 0, sizeof(odp_ipsec_config_t));
}

int odp_ipsec_config(const odp_ipsec_config_t *config)
{
(void)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;
}

error: patch failed: platform/linux-generic/odp_ipsec.c:5
Hunk #2 succeeded at 68 (offset -336 lines).
Hunk #3 succeeded at 866 (offset -336 lines).
Hunk #4 succeeded at 916 (offset -336 lines).
Applied patch platform/linux-generic/include/odp_internal.h cleanly.
Applied patch platform/linux-generic/include/odp_ipsec_internal.h cleanly.
Applied patch platform/linux-generic/include/protocols/ip.h cleanly.
Applied patch platform/linux-generic/odp_event.c cleanly.
Applied patch platform/linux-generic/odp_init.c cleanly.
Applying patch platform/linux-generic/odp_ipsec.c with 1 reject...
Rejected hunk #1.
Hunk #2 applied cleanly.
Hunk #3 applied cleanly.
Hunk #4 applied cleanly.
Patch failed at 0001 linux-gen: ipsec: draft IPsec implementation


On Tue, Apr 11, 2017 at 8:41 AM, Dmitry Eremin-Solenikov
<dmitry.ereminsolenikov@linaro.org> wrote:
> 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.ereminsolenikov@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);


The odp_ipsec_result() API is already defined. No need to invent a new one.

> +

> +/**

> + * 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);


For consistency with other free routines this would be
odp_ipsec_result_free(), and odp_event_free() would be extended to
cover this event type as well.

These APIs cannot be defined here, they need to be in
include/odp/api/spec/ipsec.h

> +

> +/**

> + * @}

> + */

> +

> +#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));


For consistency with other names this should be:
odp_ipsec_result_free(odp_ipsec_result_from_event(event));

The cast function in the other direction would be odp_ipsec_result_to_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;


Why does a brand new struct need a reserved field like this?

> +       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);


The odp_ prefix should only be used for external ODP APIs. Use
_odp_ipsecproc_t for internal "ODP" routines, or just have private
names without a prefix. Same comment throughout for other internal
names that use the odp_ prefix here.
I realize we weren't always consistent in this convention, but we're
trying to keep to it for new development.

> +

> +/**

> + * 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;


The duplicated prefix (odp_odp_...) looks odd here. Isn't one prefix sufficient?

> +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;


Why this constant? Shouldn't this be something more IPsec-specific?

> +       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);


Need at least a (void) here or else an RC check to keep tools like
Coverity happy.

> +               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);


Same as above. Add (void) here to keep Coverity happy.

> +               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);

> +       }


Looks like this loop should go ahead of the odp_pool_destroy. Best to
keep the term sequence a mirror of the init sequence.

> +

> +       ret = odp_shm_free(ipsec_sa_tbl->shm);

> +       if (ret < 0) {

> +               ODP_ERR("shm free failed");

> +               rc = -1;

> +       }


You're missing an odp_pool_destroy() for the odp_ipsec_op_result_pool here

> +

> +       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;


The spec is documented this way, but magic numbers like this always
look somewhat jarring. We should be using enums for this. We should
propose an IPsec API change for that.

> +       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?


Did you mean to say SYNC crypto here, since that's what you're doing?

> +       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);


The odp_random_data() API has been changed. This call should now be:
crypto_param.iv.length = odp_random_data(crypto_param.iv.data,
ipsec_sa->esp_iv_len, ODP_RANDOM_CRYPTO);

> +               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);


Since you reserve with reserve_ipsec_sa() you should create a
free_ipsec_sa() counterpart to keep things at the same semantic level.
Also since reserve_ipsec_sa() changes ipsec_sa->reserved from 0 to 1,
I'd assume that free() should set it back to 0, no?

>

>         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;


Again having a common wrapper for reserving/freeing SAs makes sense.
Also since the area is cleared at init, reserved == 0 means free, no?

> +       }

> +       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))


These simple definitions don't work if IP options are present. You
should use the IP hdr len field of the IP hdr to get the actual size.
(_ODP_IPV4HDR_IHL).

> +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;


This could be simplified as:
return status->alg_err == ODP_CRYPTO_ALG_ERR_NONE && status->hw_err ==
ODP_CRYPTO_HW_ERR_NONE;

> +}

> +

> +/**

> + * 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;

> +}


The type here is odp_ipsec_result_t, so as noted earlier this should
be named odp_ipsec_result_from_event() and use
ODP_IPSEC_RESULT_INVALID as the invalid value.

> +

> +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);


I'd simplify the internal struct name to ipsec_result_hdr_t or even
ipsec_result_t. No odp_ prefix, or perhaps _odp.

> +}

> +

> +static odp_ipsec_op_result_event_hdr_t *ipsec_op_result_event_hdr(odp_ipsec_op_result_event_t res)


Better:
static ipsec_result_t *ipsec_result_from_event(odp_ipsec_result_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)


The API should be named odp_ipsec_result_to_event() for consistency
with the other event cast functions.

> +{

> +       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)


static odp_ipsec_result_t odp_ipsec_result_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)


void odp_ipsec_result_free(odp_ipsec_result_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));

> +}


We'll likely need corresponding odp_ipsec_status_alloc/free() routines
for those as well.

> +

> +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));


Again, need to account for possible IP options.

> +       } 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));


You should use odp_packet_move_data() here as that routine
automagically handles any packet segmentation.

> +       }

> +

> +       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)


odp_ prefix is particularly confusing here since the external API is
odp_ipsec_in()

> +{

> +       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;

> +       }


We already have the odp_packet_has_ipsec() API that queries
IPsec-related fields set by the classifier. Not sure if that's useful
here.

> +

> +       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)


Same comment regarding odp_ prefix here.

> +{

> +       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);


Again, use odp_packet_move_data() for this.

> +       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;

> +       }


Use odp_packet_extend_tail() here as that will handle additional
segment adds if current tailroom is insufficient.

> +

> +       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);


Use new form of this API:
ret = odp_random_data((uint8_t *)ctx->tun_hdr_id,
sizeof(*ctx->tun_hdr_id), ODP_RANDOM_CRYPTO);

> +                       if (ret != sizeof(*ctx->tun_hdr_id))

> +                               abort();


Need to recover and set appropriate RC here. OK as an initial
prototype, but mark as TODO.

> +               }

> +       }

> +

> +       /* 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_ipsec_result_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();


ipsec_ev = odp_ipsec_result_alloc();

> +               if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev)


ODP_IPSEC_RESULT_INVALID

> +                       break;

> +

> +               ev = odp_ipsec_op_result_event_to_event(ipsec_ev);


ev = odp_ipsec_result_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_ipsec_result_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();


ipsec_ev = odp_ipsec_result_alloc();

> +               if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev)


ODP_IPSEC_RESULT_INVALID

> +                       break;

> +

> +               ev = odp_ipsec_op_result_event_to_event(ipsec_ev);


ev = odp_ipsec_result_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_result_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);


ipsec_ev = odp_ipsec_result_from_event(event);

> +       if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev)


ODP_IPSEC_RESULT_INVALID

> +               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

>
Dmitry Eremin-Solenikov April 14, 2017, 11:59 a.m. UTC | #2
Bill,

Thanks a lot for the review! I will send updated patches in few hours
hopefully.

On 14.04.2017 00:42, Bill Fischofer wrote:
> Next version should be marked API-NEXT, whether or not it is still an


Yes, that was an error from my side.

> On Tue, Apr 11, 2017 at 8:41 AM, Dmitry Eremin-Solenikov

> <dmitry.ereminsolenikov@linaro.org> wrote:

>> 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.ereminsolenikov@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);

> 

> The odp_ipsec_result() API is already defined. No need to invent a new one.


This function (and few next ones) is an internal API, specific to this
implementation, used exactly to implement odp_ipsec_result() and
odp_event_free().

If we are talking about this prototype, it is used inside
odp_ipsec_result() to convert from generic odp_event_t to type-specific
implementation.

> 

>> +

>> +/**

>> + * 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);

> 

> For consistency with other free routines this would be

> odp_ipsec_result_free(), and odp_event_free() would be extended to

> cover this event type as well.


I'll drop _event part from function and type names.

> These APIs cannot be defined here, they need to be in

> include/odp/api/spec/ipsec.h


No. They are internal platform-specific ones. They are not intended for
the application writers.

>> +       case ODP_EVENT_IPSEC_RESULT:

>> +               odp_ipsec_op_result_event_free(odp_ipsec_op_result_event_from_event(event));

> 

> For consistency with other names this should be:

> odp_ipsec_result_free(odp_ipsec_result_from_event(event));

> 

> The cast function in the other direction would be odp_ipsec_result_to_event().


Fine.

>> +typedef struct ipsec_sa_t {

>> +       odp_ticketlock_t lock ODP_ALIGNED_CACHE;

>> +       int reserved;

> 

> Why does a brand new struct need a reserved field like this?


Is is_reserved better?

> 

>> +       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);

> 

> The odp_ prefix should only be used for external ODP APIs. Use

> _odp_ipsecproc_t for internal "ODP" routines, or just have private

> names without a prefix. Same comment throughout for other internal

> names that use the odp_ prefix here.

> I realize we weren't always consistent in this convention, but we're

> trying to keep to it for new development.


Fine with me, I'll try to review code and change names accordingly.
Should I also prefix my internal result/event names with _odp_ rather
than just odp_?


>> +

>> +static odp_pool_t odp_odp_ipsec_ctx_pool = ODP_POOL_INVALID;

> 

> The duplicated prefix (odp_odp_...) looks odd here. Isn't one prefix sufficient?


Oops.


>> +

>> +       /* Create context buffer pool */

>> +       params.buf.size  = sizeof(odp_ipsec_ctx_t);

>> +       params.buf.align = 0;

>> +       params.buf.num   = SHM_CTX_POOL_BUF_COUNT;

> 

> Why this constant? Shouldn't this be something more IPsec-specific?


Fixed.

> 

>> +       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);

> 

> Need at least a (void) here or else an RC check to keep tools like

> Coverity happy.


I noticed that no other code inside platform/linux-generic uses
odp_pool_create/odp_pool_destroy. Am I using them correctly here? Should
I use any other memory management API?


>> +

>> +       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);

>> +       }

> 

> Looks like this loop should go ahead of the odp_pool_destroy. Best to

> keep the term sequence a mirror of the init sequence.


Ack

> 

>> +

>> +       ret = odp_shm_free(ipsec_sa_tbl->shm);

>> +       if (ret < 0) {

>> +               ODP_ERR("shm free failed");

>> +               rc = -1;

>> +       }

> 

> You're missing an odp_pool_destroy() for the odp_ipsec_op_result_pool here


Added

>> +       rc = odp_crypto_capability(&crypto_capa);

>> +       if (rc < 0)

>> +               return rc;

>> +

>> +       capa->max_num_sa = ODP_CONFIG_IPSEC_SAS;

>> +       capa->op_mode_sync = 2;

> 

> The spec is documented this way, but magic numbers like this always

> look somewhat jarring. We should be using enums for this. We should

> propose an IPsec API change for that.


Sending separate patch now.


>> +       /* 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?

> 

> Did you mean to say SYNC crypto here, since that's what you're doing?


No. I meant what I said. Using ASYNC crypto events to to power ASYNC
IPsec events. Unfortunately it doesn't seem possible ATM.


>> +       /* 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);

> 

> The odp_random_data() API has been changed. This call should now be:

> crypto_param.iv.length = odp_random_data(crypto_param.iv.data,

> ipsec_sa->esp_iv_len, ODP_RANDOM_CRYPTO);


Ack.

> 

>> +               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);

> 

> Since you reserve with reserve_ipsec_sa() you should create a

> free_ipsec_sa() counterpart to keep things at the same semantic level.

> Also since reserve_ipsec_sa() changes ipsec_sa->reserved from 0 to 1,

> I'd assume that free() should set it back to 0, no?


Yes.

> 

>>

>>         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;

> 

> Again having a common wrapper for reserving/freeing SAs makes sense.

> Also since the area is cleared at init, reserved == 0 means free, no?


Oops.

Yes, I'm adding free function for the ipsec_sa.


> 

>> +       }

>> +       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))

> 

> These simple definitions don't work if IP options are present. You

> should use the IP hdr len field of the IP hdr to get the actual size.

> (_ODP_IPV4HDR_IHL).


Ack.

> 

>> +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;

> 

> This could be simplified as:

> return status->alg_err == ODP_CRYPTO_ALG_ERR_NONE && status->hw_err ==

> ODP_CRYPTO_HW_ERR_NONE;


And inlined then.


>> +

>> +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;

>> +}

> 

> The type here is odp_ipsec_result_t, so as noted earlier this should

> be named odp_ipsec_result_from_event() and use

> ODP_IPSEC_RESULT_INVALID as the invalid value.


Ack

> 

>> +

>> +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);

> 

> I'd simplify the internal struct name to ipsec_result_hdr_t or even

> ipsec_result_t. No odp_ prefix, or perhaps _odp.


This was c&p from buffer/packet/timeout code, where internal types also
have odp_ prefix. Changing this instance now. Should we change other
places also?

> 

>> +}

>> +

>> +static odp_ipsec_op_result_event_hdr_t *ipsec_op_result_event_hdr(odp_ipsec_op_result_event_t res)

> 

> Better:

> static ipsec_result_t *ipsec_result_from_event(odp_ipsec_result_t res)


It is not _from_event, because it doesn't consume odp_event_t.
Changing to ipsec_result_hdr().


>> +{

>> +       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));

>> +}

> 

> We'll likely need corresponding odp_ipsec_status_alloc/free() routines

> for those as well.


Later, when I will be working on inline implementation.


>> +       /*

>> +        * 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));

> 

> Again, need to account for possible IP options.


Ack.

> 

>> +       } 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));

> 

> You should use odp_packet_move_data() here as that routine

> automagically handles any packet segmentation.


Thank you for the pointer.


>> +static

>> +int odp_ipsec_in_single(odp_packet_t pkt,

>> +                       odp_ipsec_sa_t sa,

>> +                       odp_ipsec_ctx_t *ctx)

> 

> odp_ prefix is particularly confusing here since the external API is

> odp_ipsec_in()


Well. This is an internal worker for odp_ipsec_in() and
odp_ipsec_in_enq(). Do you really want for me to change it to
ipsec_in_single()?


>> +

>> +       /* 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;

>> +       }

> 

> We already have the odp_packet_has_ipsec() API that queries

> IPsec-related fields set by the classifier. Not sure if that's useful

> here.


odp_packet_has_ipsec() just checks if the flag is set. But here I need
pointers to headers.


>> +

>> +       /* Set IPv4 length before authentication */

>> +       if (!odp_packet_push_tail(pkt, hdr_len + trl_len)) {

>> +               ctx->status.error.alg = 1;

>> +               return -1;

>> +       }

> 

> Use odp_packet_extend_tail() here as that will handle additional

> segment adds if current tailroom is insufficient.


Ack


>> +               if (!ip->id) {

>> +                       /* re-init tunnel hdr id */

>> +                       ret = odp_random_data((uint8_t *)ctx->tun_hdr_id,

>> +                                             sizeof(*ctx->tun_hdr_id),

>> +                                             1);

> 

> Use new form of this API:

> ret = odp_random_data((uint8_t *)ctx->tun_hdr_id,

> sizeof(*ctx->tun_hdr_id), ODP_RANDOM_CRYPTO);

> 

>> +                       if (ret != sizeof(*ctx->tun_hdr_id))

>> +                               abort();

> 

> Need to recover and set appropriate RC here. OK as an initial

> prototype, but mark as TODO.

> 

>


Ack.

>>  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_result_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);

> 

> ipsec_ev = odp_ipsec_result_from_event(event);

> 

>> +       if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev)

> 

> ODP_IPSEC_RESULT_INVALID


ok.


-- 
With best wishes
Dmitry
Peltonen, Janne (Nokia - FI/Espoo) April 18, 2017, 3:53 p.m. UTC | #3
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-bounces@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.ereminsolenikov@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
Peltonen, Janne (Nokia - FI/Espoo) April 18, 2017, 4:08 p.m. UTC | #4
Hi,

> >> +       /* 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?

> >

> > Did you mean to say SYNC crypto here, since that's what you're doing?

> 

> No. I meant what I said. Using ASYNC crypto events to to power ASYNC

> IPsec events. Unfortunately it doesn't seem possible ATM.


I also thought SYNC crypto was meant there in the FIXME comment.

Using ASYNC crypto to provide ASYNC IPsec is clearly possible. Maybe
it cannot be done as efficiently as one might want, but one could
e.g. do an async crypto-op, and then upon its completion create and
queue an IPsec completion event. Event queuing would occur twice.

However, since the IPsec implementation is part of the ODP implementation,
it does not necessarily have to restrict itself to the public ODP
crypto API but can use internal APIs too, which may make things easier.

> >> +       /* 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);

> >

> > The odp_random_data() API has been changed. This call should now be:

> > crypto_param.iv.length = odp_random_data(crypto_param.iv.data,

> > ipsec_sa->esp_iv_len, ODP_RANDOM_CRYPTO);


Note that for AES-GCM one cannot use random IV since IV values must
never be reused in GCM. With AES-GCM a counter or alike would work.

	Janne


> -----Original Message-----

> From: lng-odp [mailto:lng-odp-bounces@lists.linaro.org] On Behalf Of Dmitry Eremin-

> Solenikov

> Sent: Friday, April 14, 2017 3:00 PM

> To: Bill Fischofer <bill.fischofer@linaro.org>

> Cc: lng-odp-forward <lng-odp@lists.linaro.org>

> Subject: Re: [lng-odp] [API-NEXT][RFC][rebased] linux-gen: ipsec: draft IPsec

> implementation

> 

> Bill,

> 

> Thanks a lot for the review! I will send updated patches in few hours

> hopefully.

> 

> On 14.04.2017 00:42, Bill Fischofer wrote:

> > Next version should be marked API-NEXT, whether or not it is still an

> 

> Yes, that was an error from my side.

> 

> > On Tue, Apr 11, 2017 at 8:41 AM, Dmitry Eremin-Solenikov

> > <dmitry.ereminsolenikov@linaro.org> wrote:

> >> 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.ereminsolenikov@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);

> >

> > The odp_ipsec_result() API is already defined. No need to invent a new one.

> 

> This function (and few next ones) is an internal API, specific to this

> implementation, used exactly to implement odp_ipsec_result() and

> odp_event_free().

> 

> If we are talking about this prototype, it is used inside

> odp_ipsec_result() to convert from generic odp_event_t to type-specific

> implementation.

> 

> >

> >> +

> >> +/**

> >> + * 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);

> >

> > For consistency with other free routines this would be

> > odp_ipsec_result_free(), and odp_event_free() would be extended to

> > cover this event type as well.

> 

> I'll drop _event part from function and type names.

> 

> > These APIs cannot be defined here, they need to be in

> > include/odp/api/spec/ipsec.h

> 

> No. They are internal platform-specific ones. They are not intended for

> the application writers.

> 

> >> +       case ODP_EVENT_IPSEC_RESULT:

> >> +

> odp_ipsec_op_result_event_free(odp_ipsec_op_result_event_from_event(event));

> >

> > For consistency with other names this should be:

> > odp_ipsec_result_free(odp_ipsec_result_from_event(event));

> >

> > The cast function in the other direction would be odp_ipsec_result_to_event().

> 

> Fine.

> 

> >> +typedef struct ipsec_sa_t {

> >> +       odp_ticketlock_t lock ODP_ALIGNED_CACHE;

> >> +       int reserved;

> >

> > Why does a brand new struct need a reserved field like this?

> 

> Is is_reserved better?

> 

> >

> >> +       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);

> >

> > The odp_ prefix should only be used for external ODP APIs. Use

> > _odp_ipsecproc_t for internal "ODP" routines, or just have private

> > names without a prefix. Same comment throughout for other internal

> > names that use the odp_ prefix here.

> > I realize we weren't always consistent in this convention, but we're

> > trying to keep to it for new development.

> 

> Fine with me, I'll try to review code and change names accordingly.

> Should I also prefix my internal result/event names with _odp_ rather

> than just odp_?

> 

> 

> >> +

> >> +static odp_pool_t odp_odp_ipsec_ctx_pool = ODP_POOL_INVALID;

> >

> > The duplicated prefix (odp_odp_...) looks odd here. Isn't one prefix sufficient?

> 

> Oops.

> 

> 

> >> +

> >> +       /* Create context buffer pool */

> >> +       params.buf.size  = sizeof(odp_ipsec_ctx_t);

> >> +       params.buf.align = 0;

> >> +       params.buf.num   = SHM_CTX_POOL_BUF_COUNT;

> >

> > Why this constant? Shouldn't this be something more IPsec-specific?

> 

> Fixed.

> 

> >

> >> +       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);

> >

> > Need at least a (void) here or else an RC check to keep tools like

> > Coverity happy.

> 

> I noticed that no other code inside platform/linux-generic uses

> odp_pool_create/odp_pool_destroy. Am I using them correctly here? Should

> I use any other memory management API?

> 

> 

> >> +

> >> +       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);

> >> +       }

> >

> > Looks like this loop should go ahead of the odp_pool_destroy. Best to

> > keep the term sequence a mirror of the init sequence.

> 

> Ack

> 

> >

> >> +

> >> +       ret = odp_shm_free(ipsec_sa_tbl->shm);

> >> +       if (ret < 0) {

> >> +               ODP_ERR("shm free failed");

> >> +               rc = -1;

> >> +       }

> >

> > You're missing an odp_pool_destroy() for the odp_ipsec_op_result_pool here

> 

> Added

> 

> >> +       rc = odp_crypto_capability(&crypto_capa);

> >> +       if (rc < 0)

> >> +               return rc;

> >> +

> >> +       capa->max_num_sa = ODP_CONFIG_IPSEC_SAS;

> >> +       capa->op_mode_sync = 2;

> >

> > The spec is documented this way, but magic numbers like this always

> > look somewhat jarring. We should be using enums for this. We should

> > propose an IPsec API change for that.

> 

> Sending separate patch now.

> 

> 

> >> +       /* 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?

> >

> > Did you mean to say SYNC crypto here, since that's what you're doing?

> 

> No. I meant what I said. Using ASYNC crypto events to to power ASYNC

> IPsec events. Unfortunately it doesn't seem possible ATM.

> 

> 

> >> +       /* 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);

> >

> > The odp_random_data() API has been changed. This call should now be:

> > crypto_param.iv.length = odp_random_data(crypto_param.iv.data,

> > ipsec_sa->esp_iv_len, ODP_RANDOM_CRYPTO);

> 

> Ack.

> 

> >

> >> +               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);

> >

> > Since you reserve with reserve_ipsec_sa() you should create a

> > free_ipsec_sa() counterpart to keep things at the same semantic level.

> > Also since reserve_ipsec_sa() changes ipsec_sa->reserved from 0 to 1,

> > I'd assume that free() should set it back to 0, no?

> 

> Yes.

> 

> >

> >>

> >>         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;

> >

> > Again having a common wrapper for reserving/freeing SAs makes sense.

> > Also since the area is cleared at init, reserved == 0 means free, no?

> 

> Oops.

> 

> Yes, I'm adding free function for the ipsec_sa.

> 

> 

> >

> >> +       }

> >> +       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))

> >

> > These simple definitions don't work if IP options are present. You

> > should use the IP hdr len field of the IP hdr to get the actual size.

> > (_ODP_IPV4HDR_IHL).

> 

> Ack.

> 

> >

> >> +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;

> >

> > This could be simplified as:

> > return status->alg_err == ODP_CRYPTO_ALG_ERR_NONE && status->hw_err ==

> > ODP_CRYPTO_HW_ERR_NONE;

> 

> And inlined then.

> 

> 

> >> +

> >> +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;

> >> +}

> >

> > The type here is odp_ipsec_result_t, so as noted earlier this should

> > be named odp_ipsec_result_from_event() and use

> > ODP_IPSEC_RESULT_INVALID as the invalid value.

> 

> Ack

> 

> >

> >> +

> >> +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);

> >

> > I'd simplify the internal struct name to ipsec_result_hdr_t or even

> > ipsec_result_t. No odp_ prefix, or perhaps _odp.

> 

> This was c&p from buffer/packet/timeout code, where internal types also

> have odp_ prefix. Changing this instance now. Should we change other

> places also?

> 

> >

> >> +}

> >> +

> >> +static odp_ipsec_op_result_event_hdr_t

> *ipsec_op_result_event_hdr(odp_ipsec_op_result_event_t res)

> >

> > Better:

> > static ipsec_result_t *ipsec_result_from_event(odp_ipsec_result_t res)

> 

> It is not _from_event, because it doesn't consume odp_event_t.

> Changing to ipsec_result_hdr().

> 

> 

> >> +{

> >> +       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));

> >> +}

> >

> > We'll likely need corresponding odp_ipsec_status_alloc/free() routines

> > for those as well.

> 

> Later, when I will be working on inline implementation.

> 

> 

> >> +       /*

> >> +        * 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));

> >

> > Again, need to account for possible IP options.

> 

> Ack.

> 

> >

> >> +       } 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));

> >

> > You should use odp_packet_move_data() here as that routine

> > automagically handles any packet segmentation.

> 

> Thank you for the pointer.

> 

> 

> >> +static

> >> +int odp_ipsec_in_single(odp_packet_t pkt,

> >> +                       odp_ipsec_sa_t sa,

> >> +                       odp_ipsec_ctx_t *ctx)

> >

> > odp_ prefix is particularly confusing here since the external API is

> > odp_ipsec_in()

> 

> Well. This is an internal worker for odp_ipsec_in() and

> odp_ipsec_in_enq(). Do you really want for me to change it to

> ipsec_in_single()?

> 

> 

> >> +

> >> +       /* 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;

> >> +       }

> >

> > We already have the odp_packet_has_ipsec() API that queries

> > IPsec-related fields set by the classifier. Not sure if that's useful

> > here.

> 

> odp_packet_has_ipsec() just checks if the flag is set. But here I need

> pointers to headers.

> 

> 

> >> +

> >> +       /* Set IPv4 length before authentication */

> >> +       if (!odp_packet_push_tail(pkt, hdr_len + trl_len)) {

> >> +               ctx->status.error.alg = 1;

> >> +               return -1;

> >> +       }

> >

> > Use odp_packet_extend_tail() here as that will handle additional

> > segment adds if current tailroom is insufficient.

> 

> Ack

> 

> 

> >> +               if (!ip->id) {

> >> +                       /* re-init tunnel hdr id */

> >> +                       ret = odp_random_data((uint8_t *)ctx->tun_hdr_id,

> >> +                                             sizeof(*ctx->tun_hdr_id),

> >> +                                             1);

> >

> > Use new form of this API:

> > ret = odp_random_data((uint8_t *)ctx->tun_hdr_id,

> > sizeof(*ctx->tun_hdr_id), ODP_RANDOM_CRYPTO);

> >

> >> +                       if (ret != sizeof(*ctx->tun_hdr_id))

> >> +                               abort();

> >

> > Need to recover and set appropriate RC here. OK as an initial

> > prototype, but mark as TODO.

> >

> >

> 

> Ack.

> 

> >>  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_result_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);

> >

> > ipsec_ev = odp_ipsec_result_from_event(event);

> >

> >> +       if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev)

> >

> > ODP_IPSEC_RESULT_INVALID

> 

> ok.

> 

> 

> --

> With best wishes

> Dmitry
Dmitry Eremin-Solenikov April 18, 2017, 4:19 p.m. UTC | #5
On 18.04.2017 19:08, Peltonen, Janne (Nokia - FI/Espoo) wrote:
> Hi,


Thank you for your review!

> 

>>>> +       /* 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?

>>>

>>> Did you mean to say SYNC crypto here, since that's what you're doing?

>>

>> No. I meant what I said. Using ASYNC crypto events to to power ASYNC

>> IPsec events. Unfortunately it doesn't seem possible ATM.

> 

> I also thought SYNC crypto was meant there in the FIXME comment.

> 

> Using ASYNC crypto to provide ASYNC IPsec is clearly possible. Maybe

> it cannot be done as efficiently as one might want, but one could

> e.g. do an async crypto-op, and then upon its completion create and

> queue an IPsec completion event. Event queuing would occur twice.


This would require assistance from the application code and/or scheduler.

> 

> However, since the IPsec implementation is part of the ODP implementation,

> it does not necessarily have to restrict itself to the public ODP

> crypto API but can use internal APIs too, which may make things easier.


Yes, but currently I'd prefer to stay away from changing crypto API.
Petri said, that he has some ideas, so we might discuss that later.

> 

>>>> +       /* 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);

>>>

>>> The odp_random_data() API has been changed. This call should now be:

>>> crypto_param.iv.length = odp_random_data(crypto_param.iv.data,

>>> ipsec_sa->esp_iv_len, ODP_RANDOM_CRYPTO);

> 

> Note that for AES-GCM one cannot use random IV since IV values must

> never be reused in GCM. With AES-GCM a counter or alike would work.


AES-GCM definitely needs more work, as you've stated in another email.

> 

> 	Janne

> 

> 

>> -----Original Message-----

>> From: lng-odp [mailto:lng-odp-bounces@lists.linaro.org] On Behalf Of Dmitry Eremin-

>> Solenikov

>> Sent: Friday, April 14, 2017 3:00 PM

>> To: Bill Fischofer <bill.fischofer@linaro.org>

>> Cc: lng-odp-forward <lng-odp@lists.linaro.org>

>> Subject: Re: [lng-odp] [API-NEXT][RFC][rebased] linux-gen: ipsec: draft IPsec

>> implementation

>>

>> Bill,

>>

>> Thanks a lot for the review! I will send updated patches in few hours

>> hopefully.

>>

>> On 14.04.2017 00:42, Bill Fischofer wrote:

>>> Next version should be marked API-NEXT, whether or not it is still an

>>

>> Yes, that was an error from my side.

>>

>>> On Tue, Apr 11, 2017 at 8:41 AM, Dmitry Eremin-Solenikov

>>> <dmitry.ereminsolenikov@linaro.org> wrote:

>>>> 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.ereminsolenikov@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);

>>>

>>> The odp_ipsec_result() API is already defined. No need to invent a new one.

>>

>> This function (and few next ones) is an internal API, specific to this

>> implementation, used exactly to implement odp_ipsec_result() and

>> odp_event_free().

>>

>> If we are talking about this prototype, it is used inside

>> odp_ipsec_result() to convert from generic odp_event_t to type-specific

>> implementation.

>>

>>>

>>>> +

>>>> +/**

>>>> + * 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);

>>>

>>> For consistency with other free routines this would be

>>> odp_ipsec_result_free(), and odp_event_free() would be extended to

>>> cover this event type as well.

>>

>> I'll drop _event part from function and type names.

>>

>>> These APIs cannot be defined here, they need to be in

>>> include/odp/api/spec/ipsec.h

>>

>> No. They are internal platform-specific ones. They are not intended for

>> the application writers.

>>

>>>> +       case ODP_EVENT_IPSEC_RESULT:

>>>> +

>> odp_ipsec_op_result_event_free(odp_ipsec_op_result_event_from_event(event));

>>>

>>> For consistency with other names this should be:

>>> odp_ipsec_result_free(odp_ipsec_result_from_event(event));

>>>

>>> The cast function in the other direction would be odp_ipsec_result_to_event().

>>

>> Fine.

>>

>>>> +typedef struct ipsec_sa_t {

>>>> +       odp_ticketlock_t lock ODP_ALIGNED_CACHE;

>>>> +       int reserved;

>>>

>>> Why does a brand new struct need a reserved field like this?

>>

>> Is is_reserved better?

>>

>>>

>>>> +       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);

>>>

>>> The odp_ prefix should only be used for external ODP APIs. Use

>>> _odp_ipsecproc_t for internal "ODP" routines, or just have private

>>> names without a prefix. Same comment throughout for other internal

>>> names that use the odp_ prefix here.

>>> I realize we weren't always consistent in this convention, but we're

>>> trying to keep to it for new development.

>>

>> Fine with me, I'll try to review code and change names accordingly.

>> Should I also prefix my internal result/event names with _odp_ rather

>> than just odp_?

>>

>>

>>>> +

>>>> +static odp_pool_t odp_odp_ipsec_ctx_pool = ODP_POOL_INVALID;

>>>

>>> The duplicated prefix (odp_odp_...) looks odd here. Isn't one prefix sufficient?

>>

>> Oops.

>>

>>

>>>> +

>>>> +       /* Create context buffer pool */

>>>> +       params.buf.size  = sizeof(odp_ipsec_ctx_t);

>>>> +       params.buf.align = 0;

>>>> +       params.buf.num   = SHM_CTX_POOL_BUF_COUNT;

>>>

>>> Why this constant? Shouldn't this be something more IPsec-specific?

>>

>> Fixed.

>>

>>>

>>>> +       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);

>>>

>>> Need at least a (void) here or else an RC check to keep tools like

>>> Coverity happy.

>>

>> I noticed that no other code inside platform/linux-generic uses

>> odp_pool_create/odp_pool_destroy. Am I using them correctly here? Should

>> I use any other memory management API?

>>

>>

>>>> +

>>>> +       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);

>>>> +       }

>>>

>>> Looks like this loop should go ahead of the odp_pool_destroy. Best to

>>> keep the term sequence a mirror of the init sequence.

>>

>> Ack

>>

>>>

>>>> +

>>>> +       ret = odp_shm_free(ipsec_sa_tbl->shm);

>>>> +       if (ret < 0) {

>>>> +               ODP_ERR("shm free failed");

>>>> +               rc = -1;

>>>> +       }

>>>

>>> You're missing an odp_pool_destroy() for the odp_ipsec_op_result_pool here

>>

>> Added

>>

>>>> +       rc = odp_crypto_capability(&crypto_capa);

>>>> +       if (rc < 0)

>>>> +               return rc;

>>>> +

>>>> +       capa->max_num_sa = ODP_CONFIG_IPSEC_SAS;

>>>> +       capa->op_mode_sync = 2;

>>>

>>> The spec is documented this way, but magic numbers like this always

>>> look somewhat jarring. We should be using enums for this. We should

>>> propose an IPsec API change for that.

>>

>> Sending separate patch now.

>>

>>

>>>> +       /* 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?

>>>

>>> Did you mean to say SYNC crypto here, since that's what you're doing?

>>

>> No. I meant what I said. Using ASYNC crypto events to to power ASYNC

>> IPsec events. Unfortunately it doesn't seem possible ATM.

>>

>>

>>>> +       /* 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);

>>>

>>> The odp_random_data() API has been changed. This call should now be:

>>> crypto_param.iv.length = odp_random_data(crypto_param.iv.data,

>>> ipsec_sa->esp_iv_len, ODP_RANDOM_CRYPTO);

>>

>> Ack.

>>

>>>

>>>> +               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);

>>>

>>> Since you reserve with reserve_ipsec_sa() you should create a

>>> free_ipsec_sa() counterpart to keep things at the same semantic level.

>>> Also since reserve_ipsec_sa() changes ipsec_sa->reserved from 0 to 1,

>>> I'd assume that free() should set it back to 0, no?

>>

>> Yes.

>>

>>>

>>>>

>>>>         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;

>>>

>>> Again having a common wrapper for reserving/freeing SAs makes sense.

>>> Also since the area is cleared at init, reserved == 0 means free, no?

>>

>> Oops.

>>

>> Yes, I'm adding free function for the ipsec_sa.

>>

>>

>>>

>>>> +       }

>>>> +       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))

>>>

>>> These simple definitions don't work if IP options are present. You

>>> should use the IP hdr len field of the IP hdr to get the actual size.

>>> (_ODP_IPV4HDR_IHL).

>>

>> Ack.

>>

>>>

>>>> +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;

>>>

>>> This could be simplified as:

>>> return status->alg_err == ODP_CRYPTO_ALG_ERR_NONE && status->hw_err ==

>>> ODP_CRYPTO_HW_ERR_NONE;

>>

>> And inlined then.

>>

>>

>>>> +

>>>> +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;

>>>> +}

>>>

>>> The type here is odp_ipsec_result_t, so as noted earlier this should

>>> be named odp_ipsec_result_from_event() and use

>>> ODP_IPSEC_RESULT_INVALID as the invalid value.

>>

>> Ack

>>

>>>

>>>> +

>>>> +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);

>>>

>>> I'd simplify the internal struct name to ipsec_result_hdr_t or even

>>> ipsec_result_t. No odp_ prefix, or perhaps _odp.

>>

>> This was c&p from buffer/packet/timeout code, where internal types also

>> have odp_ prefix. Changing this instance now. Should we change other

>> places also?

>>

>>>

>>>> +}

>>>> +

>>>> +static odp_ipsec_op_result_event_hdr_t

>> *ipsec_op_result_event_hdr(odp_ipsec_op_result_event_t res)

>>>

>>> Better:

>>> static ipsec_result_t *ipsec_result_from_event(odp_ipsec_result_t res)

>>

>> It is not _from_event, because it doesn't consume odp_event_t.

>> Changing to ipsec_result_hdr().

>>

>>

>>>> +{

>>>> +       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));

>>>> +}

>>>

>>> We'll likely need corresponding odp_ipsec_status_alloc/free() routines

>>> for those as well.

>>

>> Later, when I will be working on inline implementation.

>>

>>

>>>> +       /*

>>>> +        * 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));

>>>

>>> Again, need to account for possible IP options.

>>

>> Ack.

>>

>>>

>>>> +       } 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));

>>>

>>> You should use odp_packet_move_data() here as that routine

>>> automagically handles any packet segmentation.

>>

>> Thank you for the pointer.

>>

>>

>>>> +static

>>>> +int odp_ipsec_in_single(odp_packet_t pkt,

>>>> +                       odp_ipsec_sa_t sa,

>>>> +                       odp_ipsec_ctx_t *ctx)

>>>

>>> odp_ prefix is particularly confusing here since the external API is

>>> odp_ipsec_in()

>>

>> Well. This is an internal worker for odp_ipsec_in() and

>> odp_ipsec_in_enq(). Do you really want for me to change it to

>> ipsec_in_single()?

>>

>>

>>>> +

>>>> +       /* 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;

>>>> +       }

>>>

>>> We already have the odp_packet_has_ipsec() API that queries

>>> IPsec-related fields set by the classifier. Not sure if that's useful

>>> here.

>>

>> odp_packet_has_ipsec() just checks if the flag is set. But here I need

>> pointers to headers.

>>

>>

>>>> +

>>>> +       /* Set IPv4 length before authentication */

>>>> +       if (!odp_packet_push_tail(pkt, hdr_len + trl_len)) {

>>>> +               ctx->status.error.alg = 1;

>>>> +               return -1;

>>>> +       }

>>>

>>> Use odp_packet_extend_tail() here as that will handle additional

>>> segment adds if current tailroom is insufficient.

>>

>> Ack

>>

>>

>>>> +               if (!ip->id) {

>>>> +                       /* re-init tunnel hdr id */

>>>> +                       ret = odp_random_data((uint8_t *)ctx->tun_hdr_id,

>>>> +                                             sizeof(*ctx->tun_hdr_id),

>>>> +                                             1);

>>>

>>> Use new form of this API:

>>> ret = odp_random_data((uint8_t *)ctx->tun_hdr_id,

>>> sizeof(*ctx->tun_hdr_id), ODP_RANDOM_CRYPTO);

>>>

>>>> +                       if (ret != sizeof(*ctx->tun_hdr_id))

>>>> +                               abort();

>>>

>>> Need to recover and set appropriate RC here. OK as an initial

>>> prototype, but mark as TODO.

>>>

>>>

>>

>> Ack.

>>

>>>>  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_result_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);

>>>

>>> ipsec_ev = odp_ipsec_result_from_event(event);

>>>

>>>> +       if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev)

>>>

>>> ODP_IPSEC_RESULT_INVALID

>>

>> ok.

>>

>>

>> --

>> With best wishes

>> Dmitry



-- 
With best wishes
Dmitry
diff mbox

Patch

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)