diff mbox

[[RFCv2] 4/4] linux-gen: ipsec: draft IPsec implementation

Message ID 20170427115150.19452-4-dmitry.ereminsolenikov@linaro.org
State New
Headers show

Commit Message

Dmitry Eremin-Solenikov April 27, 2017, 11:51 a.m. UTC
For now it's only a preview with the following limitation:
 - No inline processing support
 - No SA lookups
 - Only IPv4 support
 - No zeroing of mutable IPv4 options for AH ICV calculation
 - No replay protection
 - No ESN support

Signed-off-by: Dmitry Eremin-Solenikov <dmitry.ereminsolenikov@linaro.org>

---
 .../include/odp/api/plat/event_types.h             |    3 +-
 platform/linux-generic/include/odp_internal.h      |    4 +
 .../linux-generic/include/odp_ipsec_internal.h     |  107 ++
 platform/linux-generic/include/protocols/ip.h      |   52 +
 platform/linux-generic/odp_event.c                 |    8 +
 platform/linux-generic/odp_init.c                  |   13 +
 platform/linux-generic/odp_ipsec.c                 | 1450 +++++++++++++++++++-
 7 files changed, 1602 insertions(+), 35 deletions(-)
 create mode 100644 platform/linux-generic/include/odp_ipsec_internal.h

-- 
2.11.0

Comments

Bill Fischofer April 27, 2017, 5:34 p.m. UTC | #1
I realize this is still an RFC, but FYI, this has some compilation issues:

  CC       odp_ipsec.lo
odp_ipsec.c: In function ‘odp_ipsec_in’:
odp_ipsec.c:1219:16: error: comparison between signed and unsigned integer
expressions [-Werror=sign-compare]
  while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {
                ^
odp_ipsec.c:1219:44: error: comparison between signed and unsigned integer
expressions [-Werror=sign-compare]
  while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {
                                            ^
odp_ipsec.c: In function ‘odp_ipsec_out’:
odp_ipsec.c:1267:16: error: comparison between signed and unsigned integer
expressions [-Werror=sign-compare]
  while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {
                ^
odp_ipsec.c:1267:44: error: comparison between signed and unsigned integer
expressions [-Werror=sign-compare]
  while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {
                                            ^
odp_ipsec.c: In function ‘odp_ipsec_in_enq’:
odp_ipsec.c:1313:16: error: comparison between signed and unsigned integer
expressions [-Werror=sign-compare]
  while (in_pkt < input->num_pkt) {
                ^
odp_ipsec.c: In function ‘odp_ipsec_out_enq’:
odp_ipsec.c:1383:16: error: comparison between signed and unsigned integer
expressions [-Werror=sign-compare]
  while (in_pkt < input->num_pkt) {
                ^
odp_ipsec.c: In function ‘odp_ipsec_result’:
odp_ipsec.c:1470:28: error: comparison between signed and unsigned integer
expressions [-Werror=sign-compare]
  for (out_pkt = 0; out_pkt < result->num_pkt; out_pkt++) {
                            ^
cc1: all warnings being treated as errors
Makefile:986: recipe for target 'odp_ipsec.lo' failed
make[1]: *** [odp_ipsec.lo] Error 1

Checkpatch also complains about a number of lines being > 80 chars.

Other comments inline.

On Thu, Apr 27, 2017 at 6:51 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 zeroing of mutable IPv4 options for AH ICV calculation

>  - No replay protection

>  - No ESN support

>

> Signed-off-by: Dmitry Eremin-Solenikov <dmitry.ereminsolenikov@linaro.org>

> ---

>  .../include/odp/api/plat/event_types.h             |    3 +-

>  platform/linux-generic/include/odp_internal.h      |    4 +

>  .../linux-generic/include/odp_ipsec_internal.h     |  107 ++

>  platform/linux-generic/include/protocols/ip.h      |   52 +

>  platform/linux-generic/odp_event.c                 |    8 +

>  platform/linux-generic/odp_init.c                  |   13 +

>  platform/linux-generic/odp_ipsec.c                 | 1450

> +++++++++++++++++++-

>  7 files changed, 1602 insertions(+), 35 deletions(-)

>  create mode 100644 platform/linux-generic/include/odp_ipsec_internal.h

>

> diff --git a/platform/linux-generic/include/odp/api/plat/event_types.h

> b/platform/linux-generic/include/odp/api/plat/event_types.h

> index 0f517834..cb3a1f89 100644

> --- a/platform/linux-generic/include/odp/api/plat/event_types.h

> +++ b/platform/linux-generic/include/odp/api/plat/event_types.h

> @@ -39,7 +39,8 @@ typedef enum odp_event_type_t {

>         ODP_EVENT_PACKET       = 2,

>         ODP_EVENT_TIMEOUT      = 3,

>         ODP_EVENT_CRYPTO_COMPL = 4,

> -       ODP_EVENT_IPSEC_RESULT = 5

> +       ODP_EVENT_IPSEC_RESULT = 5,

> +       ODP_EVENT_IPSEC_STATUS = 6

>  } odp_event_type_t;

>

>  /**

> diff --git a/platform/linux-generic/include/odp_internal.h

> b/platform/linux-generic/include/odp_internal.h

> index acfc3012..5bbd0f13 100644

> --- a/platform/linux-generic/include/odp_internal.h

> +++ b/platform/linux-generic/include/odp_internal.h

> @@ -70,6 +70,7 @@ enum init_stage {

>         CLASSIFICATION_INIT,

>         TRAFFIC_MNGR_INIT,

>         NAME_TABLE_INIT,

> +       IPSEC_INIT,

>         MODULES_INIT,

>         ALL_INIT      /* All init stages completed */

>  };

> @@ -129,6 +130,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..21026569

> --- /dev/null

> +++ b/platform/linux-generic/include/odp_ipsec_internal.h

> @@ -0,0 +1,107 @@

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

> +

> +#define ODP_IPSEC_RESULT_INVALID \

> +       _odp_cast_scalar(odp_ipsec_result_t, 0xffffffff)

> +

> +typedef ODP_HANDLE_T(odp_ipsec_status_event_t);

> +

> +#define ODP_IPSEC_STATUS_EVENT_INVALID \

> +       _odp_cast_scalar(odp_ipsec_status_event_t, 0xffffffff)

> +

> +/**

> + * Get ipsec_result handle from event

> + *

> + * Converts an ODP_EVENT_IPSEC_RESULT type event to an IPsec result event.

> + *

> + * @param ev   Event handle

> + *

> + * @return IPsec result handle

> + *

> + * @see odp_event_type()

> + */

> +odp_ipsec_result_t odp_ipsec_result_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_result_to_event(odp_ipsec_result_t res);

> +

> +/**

> + * Free IPsec result event

> + *

> + * Frees the ipsec_result into the ipsec_result pool it was allocated

> from.

> + *

> + * @param res           IPsec result handle

> + */

> +void odp_ipsec_result_free(odp_ipsec_result_t res);

> +

> +/**

> + * Get ipsec_status handle from event

> + *

> + * Converts an ODP_EVENT_IPSEC_STATUS type event to an IPsec status event.

> + *

> + * @param ev   Event handle

> + *

> + * @return IPsec status handle

> + *

> + * @see odp_event_type()

> + */

> +odp_ipsec_status_event_t odp_ipsec_status_from_event(odp_event_t ev);

> +

> +/**

> + * Convert IPsec status event handle to event

> + *

> + * @param res  IPsec status handle

> + *

> + * @return Event handle

> + */

> +odp_event_t odp_ipsec_status_to_event(odp_ipsec_status_event_t res);

> +

> +/**

> + * Free IPsec status event

> + *

> + * Frees the ipsec_status into the ipsec_status pool it was allocated

> from.

> + *

> + * @param res           IPsec status handle

> + */

> +void odp_ipsec_status_free(odp_ipsec_status_event_t res);

>


If these are intended to be ODP IPsec APIs they belong in
include/odp/spec/api/ipsec.h, not here. If they are internal to this
implementation then they should not use the odp_ prefix and there's no need
for the doxygen (or should be marked @internal)


> +

> +/**

> + * @}

> + */

> +

> +#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..16bd35cc 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,12 @@ 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_result_free(odp_ipsec_result_from_event(event));

> +               break;

> +       case ODP_EVENT_IPSEC_STATUS:

> +               odp_ipsec_status_free(odp_ipsec_status_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..f15cd8d0 100644

> --- a/platform/linux-generic/odp_ipsec.c

> +++ b/platform/linux-generic/odp_ipsec.c

> @@ -4,105 +4,1442 @@

>   * SPDX-License-Identifier:     BSD-3-Clause

>   */

>

> +#include <odp/api/atomic.h>

>  #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 */

> +

> +#define IPSEC_USERS_DISABLING  0x80000000

> +

> +typedef struct ipsec_sa_t {

> +       odp_ticketlock_t lock ODP_ALIGNED_CACHE;

> +       unsigned        is_reserved : 1;

> +       odp_atomic_u32_t users;

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

> +

> +       odp_ipsec_mode_t mode;

> +       odp_u32be_t     tun_src_ip;

> +       odp_u32be_t     tun_dst_ip;

>


IPv6 support options? We're going to need that too.


> +       uint8_t         tun_ttl;

> +       /* 32-bit from which low 16 are used */

> +       odp_atomic_u32_t tun_hdr_id;

> +

> +       odp_ipsec_protocol_t proto;

> +       uint32_t        icv_len;

> +       uint32_t        esp_iv_len;

> +       uint32_t        esp_block_len;

> +       uint32_t        spi;

> +       odp_atomic_u32_t seq;

> +       uint8_t         iv[MAX_IV_LEN];  /**< ESP IV storage */

> +} ipsec_sa_t;

>


This could probably be better packed to minimize footprint. E.g., combining
is_reserved, in_place, tun_ttl into a single 32-bit area.


> +

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

> +

> +typedef void (*ipsec_postprocess_t)(ipsec_ctx_t *ctx);

> +

> +/**

> + * Per packet IPsec processing context

> + */

> +struct ipsec_ctx_s {

> +       odp_buffer_t buffer;     /**< Buffer for context */

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

>


ip_ttl, ip_flags, ip_frag_offset packs better.


> +       unsigned hdr_len;        /**< Length of IPsec headers */

> +       unsigned trl_len;        /**< Length of IPsec trailers */

> +       uint16_t ipsec_offset;   /**< Offset of IPsec header from

> +                                     buffer start */

> +

> +       ipsec_postprocess_t postprocess;

> +       odp_ipsec_sa_t sa;

> +       odp_crypto_op_result_t crypto;

> +       odp_ipsec_op_status_t status;

> +       odp_packet_t pkt;

> +

> +       uint32_t src_ip;         /**< SA source IP address */

> +       uint32_t dst_ip;         /**< SA dest IP address */

>


Need IPv6 variants for this as well.


> +       uint8_t iv[MAX_IV_LEN];  /**< ESP IV storage */

> +};

> +

> +typedef struct {

> +       /* common buffer header */

> +       odp_buffer_hdr_t buf_hdr;

> +       ipsec_ctx_t *ctx;

> +} ipsec_result_hdr_t;

> +

> +typedef struct {

> +       /* common buffer header */

> +       odp_buffer_hdr_t buf_hdr;

> +

> +       odp_ipsec_status_t status;

> +} ipsec_status_hdr_t;

>


Inconsistent blank line usage between this and above struct


> +

> +static

> +int odp_ipsec_status_send(odp_ipsec_sa_t sa,

> +                         odp_ipsec_status_id_t id,

> +                         int ret);

> +

> +#define IPSEC_CTX_POOL_BUF_COUNT 1024

> +

> +static odp_pool_t ipsec_ctx_pool = ODP_POOL_INVALID;

> +static odp_pool_t ipsec_result_pool = ODP_POOL_INVALID;

> +static odp_pool_t ipsec_status_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 param;

> +

> +       /* Create context buffer pool */

> +       param.buf.size  = sizeof(ipsec_ctx_t);

> +       param.buf.align = 0;

> +       param.buf.num   = IPSEC_CTX_POOL_BUF_COUNT;

> +       param.type      = ODP_POOL_BUFFER;

> +

> +       ipsec_ctx_pool = odp_pool_create("ipsec_ctx_pool", &param);

> +       if (ODP_POOL_INVALID == ipsec_ctx_pool) {

> +               ODP_ERR("Error: context pool create failed.\n");

> +               return -1;

> +       }

> +

> +       param.buf.size  = sizeof(ipsec_result_hdr_t);

> +       param.buf.align = 0;

> +       param.buf.num   = IPSEC_CTX_POOL_BUF_COUNT;

> +       param.type      = ODP_POOL_BUFFER;

> +

> +       ipsec_result_pool = odp_pool_create("ipsec_result_pool", &param);

> +       if (ODP_POOL_INVALID == ipsec_result_pool) {

> +               ODP_ERR("Error: result pool create failed.\n");

> +               (void)odp_pool_destroy(ipsec_ctx_pool);

> +               return -1;

> +       }

> +

> +       param.buf.size  = sizeof(ipsec_status_hdr_t);

> +       param.buf.align = 0;

> +       param.buf.num   = IPSEC_CTX_POOL_BUF_COUNT;

> +       param.type      = ODP_POOL_BUFFER;

> +

> +       ipsec_status_pool = odp_pool_create("ipsec_status_pool", &param);

> +       if (ODP_POOL_INVALID == ipsec_status_pool) {

> +               ODP_ERR("Error: status pool create failed.\n");

>


Also need to destroy ipsec_result_pool here


> +               (void)odp_pool_destroy(ipsec_ctx_pool);


+               return -1;
> +       }

> +

> +       shm = odp_shm_reserve("ipsec_sa_table",

> +                             sizeof(ipsec_sa_table_t),

> +                             ODP_CACHE_LINE_SIZE, 0);

> +

> +       ipsec_sa_tbl = odp_shm_addr(shm);

> +       if (ipsec_sa_tbl == NULL) {

>


 Missing  destroy call for ipsec_status_pool

+               (void)odp_pool_destroy(ipsec_result_pool);
> +               (void)odp_pool_destroy(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;

> +               odp_atomic_store_u32(&ipsec_sa->users, 0);

> +       }

> +

> +       return 0;

> +}

> +

> +int odp_ipsec_term_global(void)

> +{

> +       int i;

> +       ipsec_sa_t *ipsec_sa;

> +       int ret = 0;

> +       int rc = 0;

> +

> +       for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) {

> +               ipsec_sa = ipsec_sa_entry(i);

> +

> +               odp_ticketlock_lock(&ipsec_sa->lock);

> +               if (ipsec_sa->is_reserved) {

> +                       ODP_ERR("Not destroyed ipsec_sa: %u\n",

> ipsec_sa->ipsec_sa_idx);

> +                       rc = -1;

> +               }

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

> +       }

> +

> +       ret = odp_pool_destroy(ipsec_status_pool);

> +       if (ret < 0) {

> +               ODP_ERR("status pool destroy failed");

> +               rc = -1;

> +       }

> +

> +       ret = odp_pool_destroy(ipsec_result_pool);

> +       if (ret < 0) {

> +               ODP_ERR("result pool destroy failed");

> +               rc = -1;

> +       }

> +

> +       ret = odp_pool_destroy(ipsec_ctx_pool);

> +       if (ret < 0) {

> +               ODP_ERR("ctx pool destroy failed");

> +               rc = -1;

> +       }

> +

> +       return rc;

> +}

> +

> +static ipsec_sa_t *ipsec_sa_reserve(void)

> +{

> +       int i;

> +       ipsec_sa_t *ipsec_sa;

> +

> +       for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) {

> +               ipsec_sa = ipsec_sa_entry(i);

> +

> +               odp_ticketlock_lock(&ipsec_sa->lock);

> +               if (ipsec_sa->is_reserved == 0) {

> +                       ipsec_sa->is_reserved = 1;

> +                       odp_atomic_store_u32(&ipsec_sa->users, 0);

> +                       odp_ticketlock_unlock(&ipsec_sa->lock);

> +

> +                       return ipsec_sa;

> +               }

> +               odp_ticketlock_unlock(&ipsec_sa->lock);

> +       }

> +

> +       return NULL;

> +}

> +

> +static

> +int ipsec_sa_release(ipsec_sa_t *ipsec_sa)

> +{

> +       int rc = 0;

> +

> +       odp_ticketlock_lock(&ipsec_sa->lock);

> +       if (ipsec_sa->is_reserved) {

> +               ODP_ERR("Releasing unallocated ipsec_sa: %u\n",

> ipsec_sa->ipsec_sa_idx);

> +               rc = -1;

> +       } else {

> +               ipsec_sa->is_reserved = 0;

> +       }

> +       odp_ticketlock_unlock(&ipsec_sa->lock);

> +

> +       return rc;

> +}

>

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

> +       capa->op_mode_async = ODP_SUPPORT_YES;

> +       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 = ipsec_sa_reserve();

> +       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->proto = param->proto;

> +       ipsec_sa->spi = param->spi;

> +       odp_atomic_init_u32(&ipsec_sa->seq, param->seq);

> +       ipsec_sa->context = param->context;

> +       ipsec_sa->queue = param->dest_queue;

> +       ipsec_sa->mode = param->mode;

> +

> +       if (ODP_IPSEC_MODE_TUNNEL == ipsec_sa->mode) {

> +               if (param->tunnel.type != ODP_IPSEC_TUNNEL_IPV4) {

> +                       ipsec_sa_release(ipsec_sa);

> +

> +                       return ODP_IPSEC_SA_INVALID;

> +               }

> +               memcpy(&ipsec_sa->tun_src_ip, param->tunnel.ipv4.src_addr,

> sizeof(ipsec_sa->tun_src_ip));

> +               memcpy(&ipsec_sa->tun_dst_ip, param->tunnel.ipv4.dst_addr,

> sizeof(ipsec_sa->tun_dst_ip));

> +               odp_atomic_init_u32(&ipsec_sa->tun_hdr_id, 0);

> +               ipsec_sa->tun_ttl = param->tunnel.ipv4.ttl;

> +       }

> +

> +       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 to implement 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->icv_len = 0;

> +               break;

> +       case ODP_AUTH_ALG_MD5_HMAC:

> +       case ODP_AUTH_ALG_MD5_96:

> +               ipsec_sa->icv_len = 12;

> +               break;

> +       case ODP_AUTH_ALG_SHA1_HMAC:

> +               ipsec_sa->icv_len = 12;

> +               break;

> +       case ODP_AUTH_ALG_SHA256_HMAC:

> +       case ODP_AUTH_ALG_SHA256_128:

> +               ipsec_sa->icv_len = 16;

> +               break;

> +       case ODP_AUTH_ALG_SHA512_HMAC:

> +               ipsec_sa->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 = 1;

> +               break;

> +       case ODP_CIPHER_ALG_DES:

> +       case ODP_CIPHER_ALG_3DES_CBC:

> +               ipsec_sa->esp_iv_len = 8;

> +               ipsec_sa->esp_block_len = 8;

> +               break;

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

> +

> +       return ipsec_sa->ipsec_sa_hdl;

> +

> +error:

> +       ipsec_sa_release(ipsec_sa);

>

>         return ODP_IPSEC_SA_INVALID;

>  }

>

>  int odp_ipsec_sa_disable(odp_ipsec_sa_t sa)

>  {

> -       (void)sa;

> +       ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa);

> +       uint32_t users;

> +       int cas = 0;

>

> -       return -1;

> +       /* This is a custom rwlock implementation. It is not possible to

> use

> +        * original rwlock, because there is no way to test if current

> code is

> +        * the last reader when disable operation is pending. */

>


Minor point, but wouldn't odp_rwlock_write() do this? If active users hold
a rwlock for read then the protocol for disable would be:
- release the read lock we hold
- get a write lock. If we're the first then mark it disabled and send the
status, if it's already disabled when we get the lock (multiple threads
tried to call disable concurrently) then just return.



> +       users = odp_atomic_load_u32(&ipsec_sa->users);

> +

> +       while (0 == cas) {

> +               if (users & IPSEC_USERS_DISABLING)

> +                       return -1;

> +

> +               cas = odp_atomic_cas_acq_u32(&ipsec_sa->users, &users,

> +                                            users |

> IPSEC_USERS_DISABLING);

> +       }

> +

> +       if (ODP_QUEUE_INVALID != ipsec_sa->queue) {

> +               /*

> +                * If there were not active users when we disabled SA,

> +                * send the event.

> +                */

> +               if (IPSEC_USERS_DISABLING == users)

> +                       odp_ipsec_status_send(sa,

> +                                             ODP_IPSEC_STATUS_SA_DISABLE,

> +                                             0);

> +

> +               return 0;

> +       }

> +

> +       while (IPSEC_USERS_DISABLING != users) {

> +               odp_cpu_pause();

> +               users = odp_atomic_load_u32(&ipsec_sa->users);

> +       }

> +

> +       return 0;

>  }

>

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

> +       uint32_t users = odp_atomic_load_u32(&ipsec_sa->users);

>

> -       return -1;

> +       if (IPSEC_USERS_DISABLING != users) {

> +               ODP_ERR("Distroying not disabled ipsec_sa: %u\n",

> ipsec_sa->ipsec_sa_idx);

> +               return -1;

> +       }

> +

> +       if (odp_crypto_session_destroy(ipsec_sa->session) < 0) {

> +               ODP_ERR("Error destroying crypto session for ipsec_sa:

> %u\n", ipsec_sa->ipsec_sa_idx);

> +               rc = -1;

> +       }

> +

> +       if (ipsec_sa_release(ipsec_sa) < 0)

> +               return -1;

> +       else

> +               return rc;

> +}

> +

> +static

> +ipsec_sa_t *ipsec_sa_use(odp_ipsec_sa_t sa)

> +{

> +       ipsec_sa_t *ipsec_sa;

> +       uint32_t users;

> +       int cas = 0;

> +

> +       if (ODP_IPSEC_SA_INVALID == sa)

> +               return NULL;

> +

> +       ipsec_sa = ipsec_sa_entry_from_hdl(sa);

> +       users = odp_atomic_load_u32(&ipsec_sa->users);

> +       while (0 == cas) {

> +               if (users & IPSEC_USERS_DISABLING)

> +                       return NULL;

> +

> +               cas = odp_atomic_cas_acq_u32(&ipsec_sa->users, &users,

> +                                            users + 1);

> +       }

> +

> +       return ipsec_sa;

> +}

> +

> +static

> +void ipsec_sa_unuse(ipsec_sa_t *ipsec_sa)

> +{

> +       uint32_t users;

> +       int cas = 0;

> +

> +       users = odp_atomic_load_u32(&ipsec_sa->users);

> +       while (0 == cas)

> +               cas = odp_atomic_cas_acq_u32(&ipsec_sa->users, &users,

> +                                            users - 1);

> +

> +       if (users == IPSEC_USERS_DISABLING)

> +               odp_ipsec_status_send(ipsec_sa->ipsec_sa_hdl,

> +                                     ODP_IPSEC_STATUS_SA_DISABLE,

> +                                     0);

> +}

> +

> +#define ipv4_hdr_len(ip) (_ODP_IPV4HDR_IHL(ip->ver_ihl) * 4)

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

> +}

> +

> +/**

> + * Allocate per packet processing context and associate it with

> + * packet buffer

> + *

> + * @param pkt  Packet

> + *

> + * @return pointer to context area

> + */

> +static

> +ipsec_ctx_t *ipsec_ctx_alloc(void)

> +{

> +       odp_buffer_t ctx_buf = odp_buffer_alloc(ipsec_ctx_pool);

> +       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 ipsec_ctx_free(ipsec_ctx_t *ctx)

> +{

> +       if (ODP_PACKET_INVALID != ctx->crypto.pkt)

> +               odp_packet_free(ctx->crypto.pkt);

> +

> +       if (ODP_PACKET_INVALID != ctx->pkt)

> +               odp_packet_free(ctx->pkt);

> +

>


This isn't really safe since ipsec_ctx_alloc() clears the ctx via memset
and ODP_PACKET_INVALID is not zero in api-next yet. It will be once the
latest master code is merged back to api-next since that change was made in
the most recent packet reorg. In any event, if we want to take advantage of
that it needs to be documented since that dependency might not be obvious
to other implementations that reuse odp-linux code.


> +       odp_buffer_free(ctx->buffer);

> +}

> +

> +odp_ipsec_result_t odp_ipsec_result_from_event(odp_event_t ev)

> +{

> +       if (odp_unlikely(ODP_EVENT_INVALID == ev))

> +               return ODP_IPSEC_RESULT_INVALID;

> +

> +       if (odp_event_type(ev) != ODP_EVENT_IPSEC_RESULT)

> +               ODP_ABORT("Event not an IPsec result");

>


Since this is fast-path code I'd make this an ODP_ASSERT() for debugging
but otherwise omit this check and follow the "results are undefined" rule
if bad parameters are passed to ODP APIs.


> +

> +       return (odp_ipsec_result_t)ev;

> +}

> +

> +static ipsec_result_hdr_t *ipsec_result_hdr_from_buf(odp_buffer_t buf)

> +{

> +       return (ipsec_result_hdr_t *)(void *)buf_hdl_to_hdr(buf);

> +}

> +

> +static ipsec_result_hdr_t *ipsec_result_hdr(odp_ipsec_result_t res)

> +{

> +       odp_buffer_t buf = odp_buffer_from_event(odp_

> ipsec_result_to_event(res));

> +

> +       return ipsec_result_hdr_from_buf(buf);

> +}

> +

> +odp_event_t odp_ipsec_result_to_event(odp_ipsec_result_t res)

> +{

> +       if (odp_unlikely(res == ODP_IPSEC_RESULT_INVALID))

> +               return ODP_EVENT_INVALID;

> +

> +       return (odp_event_t)res;

> +}

> +

> +static

> +odp_ipsec_result_t odp_ipsec_result_alloc(void)

> +{

> +       odp_buffer_t buf = odp_buffer_alloc(ipsec_result_pool);

> +

> +       if (odp_unlikely(buf == ODP_BUFFER_INVALID))

> +               return ODP_IPSEC_RESULT_INVALID;

> +

> +       _odp_buffer_event_type_set(buf, ODP_EVENT_IPSEC_RESULT);

> +

> +       return odp_ipsec_result_from_event(odp_buffer_to_event(buf));

> +}

> +

> +void odp_ipsec_result_free(odp_ipsec_result_t res)

> +{

> +       odp_event_t ev = odp_ipsec_result_to_event(res);

> +       ipsec_result_hdr_t *res_hdr;

> +

> +       res_hdr = ipsec_result_hdr(res);

> +       while (NULL != res_hdr->ctx) {

> +               ipsec_ctx_t *ctx = res_hdr->ctx;

> +

> +               res_hdr->ctx = ctx->next;

> +               ipsec_ctx_free(ctx);

> +       }

> +

> +       odp_buffer_free(odp_buffer_from_event(ev));


+}
> +

> +odp_ipsec_status_event_t odp_ipsec_status_from_event(odp_event_t ev)

> +{

> +       if (odp_unlikely(ODP_EVENT_INVALID == ev))

> +               return ODP_IPSEC_STATUS_EVENT_INVALID;

> +

> +       if (odp_event_type(ev) != ODP_EVENT_IPSEC_STATUS)

> +               ODP_ABORT("Event not an IPsec status");

>


Same comment here as for result. Use ODP_ASSERT()


> +

> +       return (odp_ipsec_status_event_t)ev;

> +}

> +

> +static ipsec_status_hdr_t *ipsec_status_hdr_from_buf(odp_buffer_t buf)

> +{

> +       return (ipsec_status_hdr_t *)(void *)buf_hdl_to_hdr(buf);

> +}

> +

> +static ipsec_status_hdr_t *ipsec_status_hdr(odp_ipsec_status_event_t res)

> +{

> +       odp_buffer_t buf = odp_buffer_from_event(odp_

> ipsec_status_to_event(res));

> +

> +       return ipsec_status_hdr_from_buf(buf);

> +}

> +

> +odp_event_t odp_ipsec_status_to_event(odp_ipsec_status_event_t res)

> +{

> +       if (odp_unlikely(res == ODP_IPSEC_STATUS_EVENT_INVALID))

> +               return ODP_EVENT_INVALID;

> +

> +       return (odp_event_t)res;

> +}

> +

> +static

> +odp_ipsec_status_event_t odp_ipsec_status_alloc(void)

> +{

> +       odp_buffer_t buf = odp_buffer_alloc(ipsec_status_pool);

> +

> +       if (odp_unlikely(buf == ODP_BUFFER_INVALID))

> +               return ODP_IPSEC_STATUS_EVENT_INVALID;

> +

> +       _odp_buffer_event_type_set(buf, ODP_EVENT_IPSEC_STATUS);

> +

> +       return odp_ipsec_status_from_event(odp_buffer_to_event(buf));

> +}

> +

> +void odp_ipsec_status_free(odp_ipsec_status_event_t res)

> +{

> +       odp_event_t ev = odp_ipsec_status_to_event(res);

> +

> +       odp_buffer_free(odp_buffer_from_event(ev));

> +}

> +

> +static

> +int odp_ipsec_status_send(odp_ipsec_sa_t sa,

> +                         odp_ipsec_status_id_t id,

> +                         int ret)

> +{

> +       odp_ipsec_status_event_t ipsec_ev = odp_ipsec_status_alloc();

> +       odp_event_t ev;

> +       odp_queue_t queue;

> +       ipsec_status_hdr_t *status_hdr;

> +

> +       if (ODP_IPSEC_STATUS_EVENT_INVALID == ipsec_ev)

> +               return -1;

> +

> +       status_hdr = ipsec_status_hdr(ipsec_ev);

> +

> +       status_hdr->status.id = id;

> +       status_hdr->status.ret = ret;

> +       status_hdr->status.sa = sa;

> +

> +       if (ODP_IPSEC_SA_INVALID == sa)

> +               queue = ipsec_config.inbound.default_queue;

> +       else

> +               queue = ipsec_sa_entry_from_hdl(sa)->queue;

> +

> +       ev = odp_ipsec_status_to_event(ipsec_ev);

> +

> +       if (odp_queue_enq(queue, ev)) {

> +               odp_event_free(ev);



More direct would be:

if (odp_queue_enq(queue, odp_ipsec_status_to_event(ipsec_ev))) {
        odp_ipsec_event_free(ipsec_ev);


> +               return -1;

> +       }

> +

> +       return 0;

> +}

> +

> +static

> +void ipsec_finish(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 (result->cipher_status.alg_err ==

> ODP_CRYPTO_ALG_ERR_NONE ||

> +                   result->cipher_status.hw_err == ODP_CRYPTO_HW_ERR_NONE)

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

> +

> +               if (result->auth_status.alg_err == ODP_CRYPTO_ALG_ERR_NONE

> ||

> +                   result->auth_status.hw_err == ODP_CRYPTO_HW_ERR_NONE)

> +                       res->status.error.auth = 1;

> +       } else {

> +               ctx->pkt = result->pkt;

> +               result->pkt = ODP_PACKET_INVALID;

> +

> +               if (ctx->postprocess)

> +                       ctx->postprocess(ctx);

> +       }

> +

> +       *pkt = ctx->pkt;

> +       ctx->pkt = ODP_PACKET_INVALID;

> +

> +       res->sa = ctx->sa;

> +}

> +

> +static

> +void ipsec_in_postprocess(ipsec_ctx_t *ctx);

> +

> +static

> +int ipsec_in_single(ipsec_ctx_t *ctx)

> +{

> +       odp_packet_t pkt = ctx->pkt;

> +       ipsec_sa_t *ipsec_sa;

> +       uint32_t ip_offset = odp_packet_l3_offset(pkt);

> +       _odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);

>


Worth an ODP_ASSERT() here to be sure ip != NULL and ip_offset !=
ODP_OFFSET_INVALID.


> +       uint16_t ip_hdr_len = ipv4_hdr_len(ip);

> +       odp_crypto_op_param_t param;

> +       odp_bool_t posted = 0;

> +       int rc = -1;

> +

> +       ctx->crypto.pkt = ODP_PACKET_INVALID;

> +

> +       ipsec_sa = ipsec_sa_use(ctx->sa);

> +       if (odp_unlikely(NULL == ipsec_sa)) {

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

> +               return -1;

> +       }

> +

> +       /* Initialize parameters block */

> +       memset(&param, 0, sizeof(param));

> +       param.session = ipsec_sa->session;

> +       param.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->postprocess = ipsec_in_postprocess;

> +       ctx->ipsec_offset = ip_offset + ip_hdr_len;

> +

> +       /* Check IP header for IPSec protocols and look it up */

> +       if (_ODP_IPPROTO_AH == ip->proto) {

> +               _odp_ahhdr_t ah;

> +

> +               if (ODP_IPSEC_AH != ipsec_sa->proto) {

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

> +                       goto out;

> +               }

> +

> +               if (odp_packet_copy_to_mem(pkt, ctx->ipsec_offset,

> sizeof(ah), &ah) < 0) {

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

> +                       goto out;

> +               }

> +

> +               ctx->hdr_len = (ah.ah_len + 2) * 4;

> +               ctx->trl_len = 0;

> +

> +               /* If authenticating, zero the mutable fields build the

> request */

> +               ip->chksum = 0;

> +               ip->tos = 0;

> +               ip->frag_offset = 0;

> +               ip->ttl = 0;

> +

> +               param.auth_range.offset = ip_offset;

> +               param.auth_range.length = odp_be_to_cpu_16(ip->tot_len);

> +               param.hash_result_offset = ctx->ipsec_offset +

> _ODP_AHHDR_LEN;

> +       } else if (_ODP_IPPROTO_ESP == ip->proto) {

> +               _odp_esphdr_t esp;

> +

> +               if (ODP_IPSEC_ESP != ipsec_sa->proto) {

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

> +                       goto out;

> +               }

> +

> +               if (odp_packet_copy_to_mem(pkt, ctx->ipsec_offset,

> sizeof(esp), &esp) < 0) {

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

> +                       goto out;

> +               }

> +

> +               if (odp_packet_copy_to_mem(pkt, ctx->ipsec_offset +

> _ODP_ESPHDR_LEN, ipsec_sa->esp_iv_len, ctx->iv) < 0) {

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

> +                       goto out;

> +               }

> +

> +               ctx->hdr_len = _ODP_ESPHDR_LEN + ipsec_sa->esp_iv_len;

> +               ctx->trl_len = _ODP_ESPTRL_LEN + ipsec_sa->icv_len;

> +

> +               param.cipher_range.offset = ctx->ipsec_offset +

> ctx->hdr_len;

> +               param.cipher_range.length = odp_be_to_cpu_16(ip->tot_len)

> - ip_hdr_len - ctx->hdr_len - ipsec_sa->icv_len;

> +               param.override_iv_ptr = ctx->iv;

> +

> +               param.auth_range.offset = ctx->ipsec_offset;

> +               param.auth_range.length = odp_be_to_cpu_16(ip->tot_len) -

> ip_hdr_len;

> +               param.hash_result_offset = ip_offset +

> odp_be_to_cpu_16(ip->tot_len) - ipsec_sa->icv_len;

> +       } else {

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

> +               goto out;

> +       }

> +

> +       param.pkt = pkt;

> +       /* Create new packet after all length extensions */

> +       if (ipsec_sa->in_place) {

> +               param.out_pkt = pkt;

> +       } else {

> +               param.out_pkt = odp_packet_alloc(odp_packet_pool(pkt),

> +                                                 odp_packet_len(pkt));

> +               /* uarea will be copied by odp_crypto_operation */

> +               odp_packet_user_ptr_set(param.out_pkt,

> +                                       odp_packet_user_ptr(param.pkt));

> +       }

> +

> +       rc = odp_crypto_operation(&param, &posted, &ctx->crypto);

> +       if (rc < 0) {

> +               ODP_DBG("Crypto failed\n");

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

> +               ipsec_sa_unuse(ipsec_sa);

> +

> +               return rc;

> +       }

> +

> +       ODP_ASSERT(!posted);

> +

> +out:

> +       ipsec_sa_unuse(ipsec_sa);

> +

> +       return rc;

> +}

> +

> +static

> +void ipsec_in_postprocess(ipsec_ctx_t *ctx)

> +{

> +       odp_packet_t pkt = ctx->pkt;

> +       uint32_t ip_offset = odp_packet_l3_offset(pkt);

> +       _odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);

> +       uint16_t ip_hdr_len = ipv4_hdr_len(ip);

> +

> +       if (_ODP_IPPROTO_AH == ip->proto) {

> +               /*

> +                * Finish auth

> +                */

> +               _odp_ahhdr_t ah;

> +

> +               if (odp_packet_copy_to_mem(pkt, ctx->ipsec_offset,

> sizeof(ah), &ah) < 0) {

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

> +                       goto out;

> +               }

> +

> +               ip->proto = ah.next_header;

> +

> +               /* Restore mutable fields */

> +               ip->ttl = ctx->ip_ttl;

> +               ip->tos = ctx->ip_tos;

> +               ip->frag_offset = odp_cpu_to_be_16(ctx->ip_frag_offset);

> +       } else if (_ODP_IPPROTO_ESP == ip->proto) {

> +               /*

> +                * Finish cipher by finding ESP trailer and processing

> +                */

> +               _odp_esptrl_t esptrl;

> +               uint32_t esptrl_offset = ip_offset +

> odp_be_to_cpu_16(ip->tot_len) - ctx->trl_len;

> +

> +               if (odp_packet_copy_to_mem(pkt, esptrl_offset,

> sizeof(esptrl), &esptrl) < 0) {

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

> +                       goto out;

> +               }

> +

> +               ip->proto = esptrl.next_header;

> +               ctx->trl_len += esptrl.pad_len;

> +       } else {

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

> +               goto out;

> +       }

> +

> +       if (ip->proto == _ODP_IPV4) {

> +               ip->ttl -= 1;

> +               ip->chksum = 0;

> +               _odp_ipv4_csum_update(pkt);

> +

> +               /* We have a tunneled IPv4 packet, strip outer and IPsec

> headers */

> +               odp_packet_move_data(pkt, ip_hdr_len + ctx->hdr_len, 0,

> ip_offset);

> +               if (odp_packet_trunc_head(&pkt, ip_hdr_len + ctx->hdr_len,

> NULL, NULL) < 0) {

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

> +                       goto out;

> +               }

> +

> +       } else {

> +               /* Finalize the IPv4 header */

> +               ipv4_adjust_len(ip, -(ctx->hdr_len + ctx->trl_len));

> +

> +               ip->chksum = 0;

> +               _odp_ipv4_csum_update(pkt);

> +

> +               odp_packet_move_data(pkt, ctx->hdr_len, 0, ip_offset +

> ip_hdr_len);

> +               if (odp_packet_trunc_head(&pkt, ctx->hdr_len, NULL, NULL)

> < 0) {

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

> +                       goto out;

> +               }

> +       }

> +

> +       if (odp_packet_trunc_tail(&pkt, ctx->trl_len, NULL, NULL) < 0)

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

> +

> +out:

> +       ctx->pkt = pkt;

> +}

> +

> +/** Helper for calculating encode length using data length and block size

> */

> +#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b)

>


For expression safety, should be
#define ESP_ENCODE_LEN(x, b) ((((x) + ((b) - 1)) / (b)) * (b))

> +

> +static

> +void ipsec_out_postprocess(ipsec_ctx_t *ctx);

> +

> +static

> +int ipsec_out_single(ipsec_ctx_t *ctx)

> +{

> +       odp_packet_t pkt = ctx->pkt;

> +       ipsec_sa_t *ipsec_sa;

> +       uint32_t ip_offset = odp_packet_l3_offset(pkt);

> +       _odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);

>


Worth an ODP_ASSERT() here to be sure ip != NULL and ip_offset !=
ODP_OFFSET_INVALID.


> +       uint16_t ip_hdr_len = ipv4_hdr_len(ip);

> +       odp_crypto_op_param_t param;

> +       odp_bool_t posted = 0;

> +       int rc = -1;

> +

> +       ctx->crypto.pkt = ODP_PACKET_INVALID;

> +

> +       ipsec_sa = ipsec_sa_use(ctx->sa);

> +       if (odp_unlikely(NULL == ipsec_sa)) {

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

> +               return -1;

> +       }

> +

> +       /* Initialize parameters block */

> +       memset(&param, 0, sizeof(param));

> +       param.session = ipsec_sa->session;

> +       param.pkt = pkt;

> +       param.ctx = ctx;

> +

> +       if (ipsec_sa->mode == ODP_IPSEC_MODE_TUNNEL) {

> +               _odp_ipv4hdr_t out_ip;

> +               _odp_ipv4hdr_t *inner_ip;

> +               uint16_t tun_hdr_offset = ip_offset + ip_hdr_len;

> +

> +               if (odp_packet_extend_head(&pkt, _ODP_IPV4HDR_LEN, NULL,

> NULL) < 0) {

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

> +                       goto out;

> +               }

> +

> +               odp_packet_move_data(pkt, 0, _ODP_IPV4HDR_LEN, ip_offset);

> +

> +               inner_ip = odp_packet_offset(pkt, tun_hdr_offset, NULL,

> NULL);

> +

> +               out_ip.ver_ihl = 0x45;

> +               out_ip.tos = inner_ip->tos; // FIXME

> +               out_ip.tot_len = odp_cpu_to_be_16(odp_be_to_cpu_16(inner_ip->tot_len)

> + _ODP_IPV4HDR_LEN);

> +               /* No need to convert to BE: ID just should not be

> duplicated */

> +               out_ip.id = (odp_atomic_fetch_add_u32(&ipsec_sa->tun_hdr_id,

> 1) + 1) & 0xffff;

> +               out_ip.frag_offset = 0;

> +               out_ip.ttl = ipsec_sa->tun_ttl;

> +               out_ip.proto = _ODP_IPV4;

> +               out_ip.chksum = 0;

> +               out_ip.src_addr = ipsec_sa->tun_src_ip;

> +               out_ip.dst_addr = ipsec_sa->tun_dst_ip;

> +

> +               odp_packet_copy_from_mem(pkt, ip_offset, _ODP_IPV4HDR_LEN,

> &out_ip);

> +

> +               odp_packet_l4_offset_set(pkt, ip_offset +

> _ODP_IPV4HDR_LEN);

> +

> +               ip = odp_packet_l3_ptr(pkt, NULL);

> +               ip_hdr_len = _ODP_IPV4HDR_LEN;

> +       }

> +

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

> +

> +       ctx->ipsec_offset = ip_offset + ip_hdr_len;

> +

> +       if (ipsec_sa->proto == ODP_IPSEC_AH) {

> +               ctx->hdr_len = _ODP_AHHDR_LEN + ipsec_sa->icv_len;

> +               ctx->trl_len = 0;

> +       } else if (ipsec_sa->proto == ODP_IPSEC_ESP) {

> +               uint32_t encrypt_len;

> +               uint16_t ip_next_len = odp_be_to_cpu_16(ip->tot_len) -

> ip_hdr_len;

> +

> +               ctx->hdr_len += _ODP_ESPHDR_LEN + ipsec_sa->esp_iv_len;

> +

> +               encrypt_len = ESP_ENCODE_LEN(ip_next_len + _ODP_ESPTRL_LEN,

> +                                            ipsec_sa->esp_block_len);

> +               ctx->trl_len = encrypt_len - ip_next_len +

> ipsec_sa->icv_len;

> +       } else {

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

> +               goto out;

> +       }

> +

> +       if (odp_packet_extend_tail(&pkt, ctx->trl_len, NULL, NULL) < 0) {

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

> +               goto out;

> +       }

> +

> +       if (odp_packet_extend_head(&pkt, ctx->hdr_len, NULL, NULL) < 0) {

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

> +               goto out;

> +       }

> +

> +       odp_packet_move_data(pkt, 0, ctx->hdr_len, ctx->ipsec_offset);

> +

> +       ip = odp_packet_l3_ptr(pkt, NULL);

> +

> +       /* Set IPv4 length before authentication */

> +       ipv4_adjust_len(ip, ctx->hdr_len + ctx->trl_len);

> +

> +       /* For authentication, build header clear mutables and build

> request */

> +       if (ipsec_sa->proto == ODP_IPSEC_AH) {

> +               _odp_ahhdr_t ah = {};

> +               uint8_t icv[ipsec_sa->icv_len];

> +

> +               ah.spi = odp_cpu_to_be_32(ipsec_sa->spi);

> +               ah.ah_len = 1 + (ipsec_sa->icv_len / 4);

> +               ah.seq_no = odp_cpu_to_be_32(odp_atomic_fetch_add_u32(&ipsec_sa->seq,

> 1) + 1);

> +               ah.next_header = ip->proto;

> +               ip->proto = _ODP_IPPROTO_AH;

> +

> +               odp_packet_copy_from_mem(pkt, ctx->ipsec_offset,

> _ODP_AHHDR_LEN, &ah);

> +               memset(icv, 0, ipsec_sa->icv_len);

> +               odp_packet_copy_from_mem(pkt, ctx->ipsec_offset +

> _ODP_AHHDR_LEN, ipsec_sa->icv_len, icv);

> +

> +               ip->chksum = 0;

> +               ip->tos = 0;

> +               ip->frag_offset = 0;

> +               ip->ttl = 0;

> +

> +               param.auth_range.offset = ip_offset;

> +               param.auth_range.length = odp_be_to_cpu_16(ip->tot_len);

> +               param.hash_result_offset = ctx->ipsec_offset +

> _ODP_AHHDR_LEN;

> +       }

> +

> +       if (ipsec_sa->proto == ODP_IPSEC_ESP) {

> +               _odp_esphdr_t esp = {};

> +               _odp_esptrl_t esptrl = {};

> +               uint32_t esptrl_offset = ip_offset +

> odp_be_to_cpu_16(ip->tot_len) - ipsec_sa->icv_len - _ODP_ESPTRL_LEN;

> +

> +               esp.spi = odp_cpu_to_be_32(ipsec_sa->spi);

> +               esp.seq_no = odp_cpu_to_be_32(odp_atomic_fetch_add_u32(&ipsec_sa->seq,

> 1) + 1);

> +

> +               esptrl.pad_len = ctx->trl_len - _ODP_ESPTRL_LEN -

> ipsec_sa->icv_len;

> +               esptrl.next_header = ip->proto;

> +               ip->proto = _ODP_IPPROTO_ESP;

> +

> +               odp_packet_copy_from_mem(pkt, ctx->ipsec_offset,

> _ODP_ESPHDR_LEN, &esp);

> +               odp_packet_copy_from_mem(pkt, ctx->ipsec_offset +

> _ODP_ESPHDR_LEN, ipsec_sa->esp_iv_len, ipsec_sa->iv);

> +               odp_packet_copy_from_mem(pkt, esptrl_offset,

> _ODP_ESPTRL_LEN, &esptrl);

> +

> +               param.cipher_range.offset = ctx->ipsec_offset +

> ctx->hdr_len;

> +               param.cipher_range.length = odp_be_to_cpu_16(ip->tot_len)

> - ip_hdr_len - ctx->hdr_len - ipsec_sa->icv_len;

> +

> +               param.auth_range.offset = ctx->ipsec_offset;

> +               param.auth_range.length = odp_be_to_cpu_16(ip->tot_len) -

> ip_hdr_len;

> +               param.hash_result_offset = ip_offset +

> odp_be_to_cpu_16(ip->tot_len) - ipsec_sa->icv_len;

> +       }

> +

> +       param.pkt = pkt;

> +       /* Create new packet after all length extensions */

> +       if (ipsec_sa->in_place) {

> +               param.out_pkt = pkt;

> +       } else {

> +               param.out_pkt = odp_packet_alloc(odp_packet_pool(pkt),

> +                                                 odp_packet_len(pkt));

> +               odp_packet_user_ptr_set(param.out_pkt,

> +                                       odp_packet_user_ptr(param.pkt));

> +       }

> +

> +       rc = odp_crypto_operation(&param, &posted, &ctx->crypto);

> +       if (rc < 0) {

> +               ODP_DBG("Crypto failed\n");

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

> +               ipsec_sa_unuse(ipsec_sa);

> +

> +               return rc;

> +       }

> +

> +       ODP_ASSERT(!posted);

> +

> +out:

> +       ctx->pkt = pkt;

> +       ipsec_sa_unuse(ipsec_sa);

> +

> +       return rc;

> +}

> +

> +static

> +void ipsec_out_postprocess(ipsec_ctx_t *ctx)

> +{

> +       odp_packet_t pkt = ctx->pkt;

> +       _odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);

> +

> +       /* Finalize the IPv4 header */

> +       if (ip->proto == _ODP_IPPROTO_AH) {

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

>  }

>

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

> +               ipsec_ctx_t ctx;

> +

> +               memset(&ctx, 0, sizeof(ctx));

> +               ctx.buffer = ODP_BUFFER_INVALID;

> +

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

> +

> +               ctx.pkt = input->pkt[in_pkt];

> +               ctx.sa = sa;

> +

> +               if (ipsec_in_single(&ctx) < 0)

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

> +

> +               ipsec_finish(&ctx, &output->res[out_pkt],

> &output->pkt[out_pkt]);

> +

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

> +               ipsec_ctx_t ctx;

> +

> +               memset(&ctx, 0, sizeof(ctx));

> +               ctx.buffer = ODP_BUFFER_INVALID;

> +

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

> +

> +               ctx.pkt = input->pkt[in_pkt];

> +               ctx.sa = sa;

> +

> +               if (ipsec_out_single(&ctx) < 0)

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

> +

> +               ipsec_finish(&ctx, &output->res[out_pkt],

> &output->pkt[out_pkt]);

> +

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

> +               odp_event_t ev;

> +               ipsec_result_hdr_t *res_hdr;

> +               odp_ipsec_sa_t sa;

> +               ipsec_ctx_t *ctx;

> +               odp_queue_t queue;

> +

> +               ipsec_ev = odp_ipsec_result_alloc();

> +               if (ODP_IPSEC_RESULT_INVALID == ipsec_ev)

> +                       break;

> +

> +               ev = odp_ipsec_result_to_event(ipsec_ev);

> +

> +               res_hdr = ipsec_result_hdr(ipsec_ev);

> +

> +               ctx = ipsec_ctx_alloc();

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

> +

> +               ctx->pkt = input->pkt[in_pkt];

> +               ctx->sa = sa;

> +

> +               if (ipsec_in_single(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_result_t ipsec_ev;

> +               odp_event_t ev;

> +               ipsec_result_hdr_t *res_hdr;

> +               odp_ipsec_sa_t sa;

> +               ipsec_ctx_t *ctx;

> +               odp_queue_t queue;

> +

> +               ipsec_ev = odp_ipsec_result_alloc();

> +               if (ODP_IPSEC_RESULT_INVALID == ipsec_ev)

> +                       break;

> +

> +               ev = odp_ipsec_result_to_event(ipsec_ev);

> +

> +               res_hdr = ipsec_result_hdr(ipsec_ev);

> +

> +               ctx = ipsec_ctx_alloc();

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

> +

> +               ctx->pkt = input->pkt[in_pkt];

> +               ctx->sa = sa;

> +

> +               if (ipsec_out_single(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,

> @@ -116,18 +1453,63 @@ 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;

> +       odp_ipsec_result_t ipsec_ev;

> +       ipsec_result_hdr_t *res_hdr;

> +       unsigned out_pkt;

> +       ipsec_ctx_t *ctx;

>

> -       return -1;

> +       if (odp_unlikely(ODP_EVENT_INVALID == event))

> +               return -1;

> +

> +       ipsec_ev = odp_ipsec_result_from_event(event);

> +       if (odp_unlikely(ODP_IPSEC_RESULT_INVALID == ipsec_ev))

> +               return -1;

> +

> +       res_hdr = ipsec_result_hdr(ipsec_ev);

> +

> +       for (out_pkt = 0; out_pkt < result->num_pkt; out_pkt++) {

> +               ipsec_ctx_t *ctx = res_hdr->ctx;

> +

> +               res_hdr->ctx = ctx->next;

> +               ipsec_finish(ctx, &result->res[out_pkt],

> &result->pkt[out_pkt]);

> +

> +               ipsec_ctx_free(ctx);

> +       }

> +

> +       result->num_pkt = out_pkt;

> +

> +       /* FIXME: this is place with semantics change */

> +       if (!res_hdr->ctx) {

> +               odp_ipsec_result_free(ipsec_ev);

> +               return 0;

> +       }

> +       out_pkt = 0;

> +

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

>  {

>         (void)status;

>         (void)event;

> +       odp_ipsec_status_event_t ipsec_ev;

> +       ipsec_status_hdr_t *status_hdr;

>

> -       return -1;

> +       if (odp_unlikely(ODP_EVENT_INVALID == event))

> +               return -1;

> +

> +       ipsec_ev = odp_ipsec_status_from_event(event);

> +       if (odp_unlikely(ODP_IPSEC_STATUS_EVENT_INVALID == ipsec_ev))

> +               return -1;

> +

> +       status_hdr = ipsec_status_hdr(ipsec_ev);

> +

> +       *status = status_hdr->status;

> +

> +       return 0;

>  }

>

>  int odp_ipsec_mtu_update(odp_ipsec_sa_t sa, uint32_t mtu)

> @@ -140,9 +1522,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 27, 2017, 11:34 p.m. UTC | #2
Bill,

Thank you for your review!

On 27.04.2017 20:34, Bill Fischofer wrote:
> I realize this is still an RFC, but FYI, this has some compilation issues:

> 

>   CC       odp_ipsec.lo

> odp_ipsec.c: In function ‘odp_ipsec_in’:

> odp_ipsec.c:1219:16: error: comparison between signed and unsigned

> integer expressions [-Werror=sign-compare]

>   while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {

>                 ^

> odp_ipsec.c:1219:44: error: comparison between signed and unsigned

> integer expressions [-Werror=sign-compare]

>   while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {

>                                             ^

> odp_ipsec.c: In function ‘odp_ipsec_out’:

> odp_ipsec.c:1267:16: error: comparison between signed and unsigned

> integer expressions [-Werror=sign-compare]

>   while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {

>                 ^

> odp_ipsec.c:1267:44: error: comparison between signed and unsigned

> integer expressions [-Werror=sign-compare]

>   while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {

>                                             ^

> odp_ipsec.c: In function ‘odp_ipsec_in_enq’:

> odp_ipsec.c:1313:16: error: comparison between signed and unsigned

> integer expressions [-Werror=sign-compare]

>   while (in_pkt < input->num_pkt) {

>                 ^

> odp_ipsec.c: In function ‘odp_ipsec_out_enq’:

> odp_ipsec.c:1383:16: error: comparison between signed and unsigned

> integer expressions [-Werror=sign-compare]

>   while (in_pkt < input->num_pkt) {

>                 ^

> odp_ipsec.c: In function ‘odp_ipsec_result’:

> odp_ipsec.c:1470:28: error: comparison between signed and unsigned

> integer expressions [-Werror=sign-compare]

>   for (out_pkt = 0; out_pkt < result->num_pkt; out_pkt++) {


Do you have patch 1 applied? Both in/out_pkt and num_pkt should be
unsigned now.

> Checkpatch also complains about a number of lines being > 80 chars.


Yes, sure. I just hate that requirement, so I usually fix long lines
during the last stages before actually submitting the patch. So that if
I change smth in the 'split' line, I don't have to resplit/realign it.

> 

> Other comments inline.

> 

> On Thu, Apr 27, 2017 at 6:51 AM, Dmitry Eremin-Solenikov

> <dmitry.ereminsolenikov@linaro.org

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

>     +/**

>     + * Free IPsec status event

>     + *

>     + * Frees the ipsec_status into the ipsec_status pool it was

>     allocated from.

>     + *

>     + * @param res           IPsec status handle

>     + */

>     +void odp_ipsec_status_free(odp_ipsec_status_event_t res);

> 

> 

> If these are intended to be ODP IPsec APIs they belong in

> include/odp/spec/api/ipsec.h, not here. If they are internal to this

> implementation then they should not use the odp_ prefix and there's no

> need for the doxygen (or should be marked @internal)


Internal. Is it suitable to use _odp_ prefix?
I'll add @internal in the next revision.

>     +       odp_ipsec_mode_t mode;

>     +       odp_u32be_t     tun_src_ip;

>     +       odp_u32be_t     tun_dst_ip;

> 

> 

> IPv6 support options? We're going to need that too.


I'll start looking onto IPv6 after IPv4 support being robust enough.

>     +       uint8_t         tun_ttl;

>     +       /* 32-bit from which low 16 are used */

>     +       odp_atomic_u32_t tun_hdr_id;

>     +

>     +       odp_ipsec_protocol_t proto;

>     +       uint32_t        icv_len;

>     +       uint32_t        esp_iv_len;

>     +       uint32_t        esp_block_len;

>     +       uint32_t        spi;

>     +       odp_atomic_u32_t seq;

>     +       uint8_t         iv[MAX_IV_LEN];  /**< ESP IV storage */

>     +} ipsec_sa_t;

> 

> 

> This could probably be better packed to minimize footprint. E.g.,

> combining is_reserved, in_place, tun_ttl into a single 32-bit area.


Ack.

>  

> 

>     +

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

>     +

>     +typedef void (*ipsec_postprocess_t)(ipsec_ctx_t *ctx);

>     +

>     +/**

>     + * Per packet IPsec processing context

>     + */

>     +struct ipsec_ctx_s {

>     +       odp_buffer_t buffer;     /**< Buffer for context */

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

> 

> 

> ip_ttl, ip_flags, ip_frag_offset packs better.


Ack

>  

> 

>     +       unsigned hdr_len;        /**< Length of IPsec headers */

>     +       unsigned trl_len;        /**< Length of IPsec trailers */

>     +       uint16_t ipsec_offset;   /**< Offset of IPsec header from

>     +                                     buffer start */

>     +

>     +       ipsec_postprocess_t postprocess;

>     +       odp_ipsec_sa_t sa;

>     +       odp_crypto_op_result_t crypto;

>     +       odp_ipsec_op_status_t status;

>     +       odp_packet_t pkt;

>     +

>     +       uint32_t src_ip;         /**< SA source IP address */

>     +       uint32_t dst_ip;         /**< SA dest IP address */

> 

> 

> Need IPv6 variants for this as well.


Later.

>  

> 

>     +       uint8_t iv[MAX_IV_LEN];  /**< ESP IV storage */

>     +};

>     +

>     +typedef struct {

>     +       /* common buffer header */

>     +       odp_buffer_hdr_t buf_hdr;

>     +       ipsec_ctx_t *ctx;

>     +} ipsec_result_hdr_t;

>     +

>     +typedef struct {

>     +       /* common buffer header */

>     +       odp_buffer_hdr_t buf_hdr;

>     +

>     +       odp_ipsec_status_t status;

>     +} ipsec_status_hdr_t;

> 

> 

> Inconsistent blank line usage between this and above struct


Ack

>     +       param.buf.size  = sizeof(ipsec_status_hdr_t);

>     +       param.buf.align = 0;

>     +       param.buf.num   = IPSEC_CTX_POOL_BUF_COUNT;

>     +       param.type      = ODP_POOL_BUFFER;

>     +

>     +       ipsec_status_pool = odp_pool_create("ipsec_status_pool",

>     &param);

>     +       if (ODP_POOL_INVALID == ipsec_status_pool) {

>     +               ODP_ERR("Error: status pool create failed.\n");

> 

> 

> Also need to destroy ipsec_result_pool here

>   


Ack

> 

>     +               (void)odp_pool_destroy(ipsec_ctx_pool);

> 

>     +               return -1;

>     +       }

>     +

>     +       shm = odp_shm_reserve("ipsec_sa_table",

>     +                             sizeof(ipsec_sa_table_t),

>     +                             ODP_CACHE_LINE_SIZE, 0);

>     +

>     +       ipsec_sa_tbl = odp_shm_addr(shm);

>     +       if (ipsec_sa_tbl == NULL) {

> 

>  

>  Missing  destroy call for ipsec_status_pool


Ack


> 

>      int odp_ipsec_sa_disable(odp_ipsec_sa_t sa)

>      {

>     -       (void)sa;

>     +       ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa);

>     +       uint32_t users;

>     +       int cas = 0;

> 

>     -       return -1;

>     +       /* This is a custom rwlock implementation. It is not

>     possible to use

>     +        * original rwlock, because there is no way to test if

>     current code is

>     +        * the last reader when disable operation is pending. */

> 

> 

> Minor point, but wouldn't odp_rwlock_write() do this? If active users

> hold a rwlock for read then the protocol for disable would be:

> - release the read lock we hold

> - get a write lock. If we're the first then mark it disabled and send

> the status, if it's already disabled when we get the lock (multiple

> threads tried to call disable concurrently) then just return.


I thought about it (thus I've noted the issue in odp_rwlock.c), but
unfortunately no, it won't work. Our rwlock implementation prefers read
locks, so writer can stay 'waiting' forever. Also there is no way to
upgrade from read to write lock (no, releasing read and acquiring write
does not seem to be an option).


>     +/**

>     + * Release per packet resources

>     + *

>     + * @param ctx  Packet context

>     + */

>     +static

>     +void ipsec_ctx_free(ipsec_ctx_t *ctx)

>     +{

>     +       if (ODP_PACKET_INVALID != ctx->crypto.pkt)

>     +               odp_packet_free(ctx->crypto.pkt);

>     +

>     +       if (ODP_PACKET_INVALID != ctx->pkt)

>     +               odp_packet_free(ctx->pkt);

>     +

> 

> 

> This isn't really safe since ipsec_ctx_alloc() clears the ctx via memset

> and ODP_PACKET_INVALID is not zero in api-next yet. It will be once the

> latest master code is merged back to api-next since that change was made

> in the most recent packet reorg. In any event, if we want to take

> advantage of that it needs to be documented since that dependency might

> not be obvious to other implementations that reuse odp-linux code.


Good point. I should fix those two fields in ctx_alloc.

>  

> 

>     +       odp_buffer_free(ctx->buffer);

>     +}

>     +

>     +odp_ipsec_result_t odp_ipsec_result_from_event(odp_event_t ev)

>     +{

>     +       if (odp_unlikely(ODP_EVENT_INVALID == ev))

>     +               return ODP_IPSEC_RESULT_INVALID;

>     +

>     +       if (odp_event_type(ev) != ODP_EVENT_IPSEC_RESULT)

>     +               ODP_ABORT("Event not an IPsec result");

> 

> 

> Since this is fast-path code I'd make this an ODP_ASSERT() for debugging

> but otherwise omit this check and follow the "results are undefined"

> rule if bad parameters are passed to ODP APIs.


Heh. odp_packet_from_event() has first check.
odp_crypto_compl_from_event() has second. Can one fix the rest of the
code to have consistent design?


>     +

>     +odp_ipsec_status_event_t odp_ipsec_status_from_event(odp_event_t ev)

>     +{

>     +       if (odp_unlikely(ODP_EVENT_INVALID == ev))

>     +               return ODP_IPSEC_STATUS_EVENT_INVALID;

>     +

>     +       if (odp_event_type(ev) != ODP_EVENT_IPSEC_STATUS)

>     +               ODP_ABORT("Event not an IPsec status");

> 

> 

> Same comment here as for result. Use ODP_ASSERT()


Same question as above. These two checks were copied from existing
functions. Shouldn't we aim for consistent implementation?

>     +       if (odp_queue_enq(queue, ev)) {

>     +               odp_event_free(ev);

> 

> 

> More direct would be:

> 

> if (odp_queue_enq(queue, odp_ipsec_status_to_event(ipsec_ev))) {

>         odp_ipsec_event_free(ipsec_ev);

>  


Ack.

>     +static

>     +int ipsec_in_single(ipsec_ctx_t *ctx)

>     +{

>     +       odp_packet_t pkt = ctx->pkt;

>     +       ipsec_sa_t *ipsec_sa;

>     +       uint32_t ip_offset = odp_packet_l3_offset(pkt);

>     +       _odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);

> 

> 

> Worth an ODP_ASSERT() here to be sure ip != NULL and ip_offset !=

> ODP_OFFSET_INVALID.


Ack.


>     +/** Helper for calculating encode length using data length and

>     block size */

>     +#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b)

> 

> 

> For expression safety, should be

> #define ESP_ENCODE_LEN(x, b) ((((x) + ((b) - 1)) / (b)) * (b))


Ack. I'll probably change this anyway to add padding support to AH.


-- 
With best wishes
Dmitry
Bill Fischofer April 28, 2017, 12:56 a.m. UTC | #3
On Thu, Apr 27, 2017 at 6:34 PM, Dmitry Eremin-Solenikov <
dmitry.ereminsolenikov@linaro.org> wrote:

> Bill,

>

> Thank you for your review!

>

> On 27.04.2017 20:34, Bill Fischofer wrote:

> > I realize this is still an RFC, but FYI, this has some compilation

> issues:

> >

> >   CC       odp_ipsec.lo

> > odp_ipsec.c: In function ‘odp_ipsec_in’:

> > odp_ipsec.c:1219:16: error: comparison between signed and unsigned

> > integer expressions [-Werror=sign-compare]

> >   while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {

> >                 ^

> > odp_ipsec.c:1219:44: error: comparison between signed and unsigned

> > integer expressions [-Werror=sign-compare]

> >   while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {

> >                                             ^

> > odp_ipsec.c: In function ‘odp_ipsec_out’:

> > odp_ipsec.c:1267:16: error: comparison between signed and unsigned

> > integer expressions [-Werror=sign-compare]

> >   while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {

> >                 ^

> > odp_ipsec.c:1267:44: error: comparison between signed and unsigned

> > integer expressions [-Werror=sign-compare]

> >   while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {

> >                                             ^

> > odp_ipsec.c: In function ‘odp_ipsec_in_enq’:

> > odp_ipsec.c:1313:16: error: comparison between signed and unsigned

> > integer expressions [-Werror=sign-compare]

> >   while (in_pkt < input->num_pkt) {

> >                 ^

> > odp_ipsec.c: In function ‘odp_ipsec_out_enq’:

> > odp_ipsec.c:1383:16: error: comparison between signed and unsigned

> > integer expressions [-Werror=sign-compare]

> >   while (in_pkt < input->num_pkt) {

> >                 ^

> > odp_ipsec.c: In function ‘odp_ipsec_result’:

> > odp_ipsec.c:1470:28: error: comparison between signed and unsigned

> > integer expressions [-Werror=sign-compare]

> >   for (out_pkt = 0; out_pkt < result->num_pkt; out_pkt++) {

>

> Do you have patch 1 applied? Both in/out_pkt and num_pkt should be

> unsigned now.

>


Yes, this is after applying the v2 RFC series.


>

> > Checkpatch also complains about a number of lines being > 80 chars.

>

> Yes, sure. I just hate that requirement, so I usually fix long lines

> during the last stages before actually submitting the patch. So that if

> I change smth in the 'split' line, I don't have to resplit/realign it.

>


OK


>

> >

> > Other comments inline.

> >

> > On Thu, Apr 27, 2017 at 6:51 AM, Dmitry Eremin-Solenikov

> > <dmitry.ereminsolenikov@linaro.org

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

> >     +/**

> >     + * Free IPsec status event

> >     + *

> >     + * Frees the ipsec_status into the ipsec_status pool it was

> >     allocated from.

> >     + *

> >     + * @param res           IPsec status handle

> >     + */

> >     +void odp_ipsec_status_free(odp_ipsec_status_event_t res);

> >

> >

> > If these are intended to be ODP IPsec APIs they belong in

> > include/odp/spec/api/ipsec.h, not here. If they are internal to this

> > implementation then they should not use the odp_ prefix and there's no

> > need for the doxygen (or should be marked @internal)

>

> Internal. Is it suitable to use _odp_ prefix?

> I'll add @internal in the next revision.

>


Yes, that avoids confusion since we (try) to restrict the odp_ prefix to
use by external APIs. We haven't always done so in the past, but best to
avoid that going forward.


>

> >     +       odp_ipsec_mode_t mode;

> >     +       odp_u32be_t     tun_src_ip;

> >     +       odp_u32be_t     tun_dst_ip;

> >

> >

> > IPv6 support options? We're going to need that too.

>

> I'll start looking onto IPv6 after IPv4 support being robust enough.

>

> >     +       uint8_t         tun_ttl;

> >     +       /* 32-bit from which low 16 are used */

> >     +       odp_atomic_u32_t tun_hdr_id;

> >     +

> >     +       odp_ipsec_protocol_t proto;

> >     +       uint32_t        icv_len;

> >     +       uint32_t        esp_iv_len;

> >     +       uint32_t        esp_block_len;

> >     +       uint32_t        spi;

> >     +       odp_atomic_u32_t seq;

> >     +       uint8_t         iv[MAX_IV_LEN];  /**< ESP IV storage */

> >     +} ipsec_sa_t;

> >

> >

> > This could probably be better packed to minimize footprint. E.g.,

> > combining is_reserved, in_place, tun_ttl into a single 32-bit area.

>

> Ack.

>

> >

> >

> >     +

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

> >     +

> >     +typedef void (*ipsec_postprocess_t)(ipsec_ctx_t *ctx);

> >     +

> >     +/**

> >     + * Per packet IPsec processing context

> >     + */

> >     +struct ipsec_ctx_s {

> >     +       odp_buffer_t buffer;     /**< Buffer for context */

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

> >

> >

> > ip_ttl, ip_flags, ip_frag_offset packs better.

>

> Ack

>

> >

> >

> >     +       unsigned hdr_len;        /**< Length of IPsec headers */

> >     +       unsigned trl_len;        /**< Length of IPsec trailers */

> >     +       uint16_t ipsec_offset;   /**< Offset of IPsec header from

> >     +                                     buffer start */

> >     +

> >     +       ipsec_postprocess_t postprocess;

> >     +       odp_ipsec_sa_t sa;

> >     +       odp_crypto_op_result_t crypto;

> >     +       odp_ipsec_op_status_t status;

> >     +       odp_packet_t pkt;

> >     +

> >     +       uint32_t src_ip;         /**< SA source IP address */

> >     +       uint32_t dst_ip;         /**< SA dest IP address */

> >

> >

> > Need IPv6 variants for this as well.

>

> Later.

>

> >

> >

> >     +       uint8_t iv[MAX_IV_LEN];  /**< ESP IV storage */

> >     +};

> >     +

> >     +typedef struct {

> >     +       /* common buffer header */

> >     +       odp_buffer_hdr_t buf_hdr;

> >     +       ipsec_ctx_t *ctx;

> >     +} ipsec_result_hdr_t;

> >     +

> >     +typedef struct {

> >     +       /* common buffer header */

> >     +       odp_buffer_hdr_t buf_hdr;

> >     +

> >     +       odp_ipsec_status_t status;

> >     +} ipsec_status_hdr_t;

> >

> >

> > Inconsistent blank line usage between this and above struct

>

> Ack

>

> >     +       param.buf.size  = sizeof(ipsec_status_hdr_t);

> >     +       param.buf.align = 0;

> >     +       param.buf.num   = IPSEC_CTX_POOL_BUF_COUNT;

> >     +       param.type      = ODP_POOL_BUFFER;

> >     +

> >     +       ipsec_status_pool = odp_pool_create("ipsec_status_pool",

> >     &param);

> >     +       if (ODP_POOL_INVALID == ipsec_status_pool) {

> >     +               ODP_ERR("Error: status pool create failed.\n");

> >

> >

> > Also need to destroy ipsec_result_pool here

> >

>

> Ack

>

> >

> >     +               (void)odp_pool_destroy(ipsec_ctx_pool);

> >

> >     +               return -1;

> >     +       }

> >     +

> >     +       shm = odp_shm_reserve("ipsec_sa_table",

> >     +                             sizeof(ipsec_sa_table_t),

> >     +                             ODP_CACHE_LINE_SIZE, 0);

> >     +

> >     +       ipsec_sa_tbl = odp_shm_addr(shm);

> >     +       if (ipsec_sa_tbl == NULL) {

> >

> >

> >  Missing  destroy call for ipsec_status_pool

>

> Ack

>

>

> >

> >      int odp_ipsec_sa_disable(odp_ipsec_sa_t sa)

> >      {

> >     -       (void)sa;

> >     +       ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa);

> >     +       uint32_t users;

> >     +       int cas = 0;

> >

> >     -       return -1;

> >     +       /* This is a custom rwlock implementation. It is not

> >     possible to use

> >     +        * original rwlock, because there is no way to test if

> >     current code is

> >     +        * the last reader when disable operation is pending. */

> >

> >

> > Minor point, but wouldn't odp_rwlock_write() do this? If active users

> > hold a rwlock for read then the protocol for disable would be:

> > - release the read lock we hold

> > - get a write lock. If we're the first then mark it disabled and send

> > the status, if it's already disabled when we get the lock (multiple

> > threads tried to call disable concurrently) then just return.

>

> I thought about it (thus I've noted the issue in odp_rwlock.c), but

> unfortunately no, it won't work. Our rwlock implementation prefers read

> locks, so writer can stay 'waiting' forever. Also there is no way to

> upgrade from read to write lock (no, releasing read and acquiring write

> does not seem to be an option).

>


OK. rwlocks shouldn't allow writer starvation so that might be worth
raising as a bug against the current odp-linux implementation if it has
that property.


>

>

> >     +/**

> >     + * Release per packet resources

> >     + *

> >     + * @param ctx  Packet context

> >     + */

> >     +static

> >     +void ipsec_ctx_free(ipsec_ctx_t *ctx)

> >     +{

> >     +       if (ODP_PACKET_INVALID != ctx->crypto.pkt)

> >     +               odp_packet_free(ctx->crypto.pkt);

> >     +

> >     +       if (ODP_PACKET_INVALID != ctx->pkt)

> >     +               odp_packet_free(ctx->pkt);

> >     +

> >

> >

> > This isn't really safe since ipsec_ctx_alloc() clears the ctx via memset

> > and ODP_PACKET_INVALID is not zero in api-next yet. It will be once the

> > latest master code is merged back to api-next since that change was made

> > in the most recent packet reorg. In any event, if we want to take

> > advantage of that it needs to be documented since that dependency might

> > not be obvious to other implementations that reuse odp-linux code.

>

> Good point. I should fix those two fields in ctx_alloc.

>

> >

> >

> >     +       odp_buffer_free(ctx->buffer);

> >     +}

> >     +

> >     +odp_ipsec_result_t odp_ipsec_result_from_event(odp_event_t ev)

> >     +{

> >     +       if (odp_unlikely(ODP_EVENT_INVALID == ev))

> >     +               return ODP_IPSEC_RESULT_INVALID;

> >     +

> >     +       if (odp_event_type(ev) != ODP_EVENT_IPSEC_RESULT)

> >     +               ODP_ABORT("Event not an IPsec result");

> >

> >

> > Since this is fast-path code I'd make this an ODP_ASSERT() for debugging

> > but otherwise omit this check and follow the "results are undefined"

> > rule if bad parameters are passed to ODP APIs.

>

> Heh. odp_packet_from_event() has first check.

> odp_crypto_compl_from_event() has second. Can one fix the rest of the

> code to have consistent design?

>


We haven't always followed our own advice, so there's always room for
improvement :)


>

>

> >     +

> >     +odp_ipsec_status_event_t odp_ipsec_status_from_event(odp_event_t

> ev)

> >     +{

> >     +       if (odp_unlikely(ODP_EVENT_INVALID == ev))

> >     +               return ODP_IPSEC_STATUS_EVENT_INVALID;

> >     +

> >     +       if (odp_event_type(ev) != ODP_EVENT_IPSEC_STATUS)

> >     +               ODP_ABORT("Event not an IPsec status");

> >

> >

> > Same comment here as for result. Use ODP_ASSERT()

>

> Same question as above. These two checks were copied from existing

> functions. Shouldn't we aim for consistent implementation?

>


Yes we should. Runtime tests are preferred for cases where mismatches could
be for reasons other than programming errors. ODP_ASSERT() is for the rest.


>

> >     +       if (odp_queue_enq(queue, ev)) {

> >     +               odp_event_free(ev);

> >

> >

> > More direct would be:

> >

> > if (odp_queue_enq(queue, odp_ipsec_status_to_event(ipsec_ev))) {

> >         odp_ipsec_event_free(ipsec_ev);

> >

>

> Ack.

>

> >     +static

> >     +int ipsec_in_single(ipsec_ctx_t *ctx)

> >     +{

> >     +       odp_packet_t pkt = ctx->pkt;

> >     +       ipsec_sa_t *ipsec_sa;

> >     +       uint32_t ip_offset = odp_packet_l3_offset(pkt);

> >     +       _odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);

> >

> >

> > Worth an ODP_ASSERT() here to be sure ip != NULL and ip_offset !=

> > ODP_OFFSET_INVALID.

>

> Ack.

>

>

> >     +/** Helper for calculating encode length using data length and

> >     block size */

> >     +#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b)

> >

> >

> > For expression safety, should be

> > #define ESP_ENCODE_LEN(x, b) ((((x) + ((b) - 1)) / (b)) * (b))

>

> Ack. I'll probably change this anyway to add padding support to AH.

>

>

> --

> With best wishes

> Dmitry

>
diff mbox

Patch

diff --git a/platform/linux-generic/include/odp/api/plat/event_types.h b/platform/linux-generic/include/odp/api/plat/event_types.h
index 0f517834..cb3a1f89 100644
--- a/platform/linux-generic/include/odp/api/plat/event_types.h
+++ b/platform/linux-generic/include/odp/api/plat/event_types.h
@@ -39,7 +39,8 @@  typedef enum odp_event_type_t {
 	ODP_EVENT_PACKET       = 2,
 	ODP_EVENT_TIMEOUT      = 3,
 	ODP_EVENT_CRYPTO_COMPL = 4,
-	ODP_EVENT_IPSEC_RESULT = 5
+	ODP_EVENT_IPSEC_RESULT = 5,
+	ODP_EVENT_IPSEC_STATUS = 6
 } odp_event_type_t;
 
 /**
diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h
index acfc3012..5bbd0f13 100644
--- a/platform/linux-generic/include/odp_internal.h
+++ b/platform/linux-generic/include/odp_internal.h
@@ -70,6 +70,7 @@  enum init_stage {
 	CLASSIFICATION_INIT,
 	TRAFFIC_MNGR_INIT,
 	NAME_TABLE_INIT,
+	IPSEC_INIT,
 	MODULES_INIT,
 	ALL_INIT      /* All init stages completed */
 };
@@ -129,6 +130,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..21026569
--- /dev/null
+++ b/platform/linux-generic/include/odp_ipsec_internal.h
@@ -0,0 +1,107 @@ 
+/* 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_result_t);
+
+#define ODP_IPSEC_RESULT_INVALID \
+	_odp_cast_scalar(odp_ipsec_result_t, 0xffffffff)
+
+typedef ODP_HANDLE_T(odp_ipsec_status_event_t);
+
+#define ODP_IPSEC_STATUS_EVENT_INVALID \
+	_odp_cast_scalar(odp_ipsec_status_event_t, 0xffffffff)
+
+/**
+ * Get ipsec_result handle from event
+ *
+ * Converts an ODP_EVENT_IPSEC_RESULT type event to an IPsec result event.
+ *
+ * @param ev   Event handle
+ *
+ * @return IPsec result handle
+ *
+ * @see odp_event_type()
+ */
+odp_ipsec_result_t odp_ipsec_result_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_result_to_event(odp_ipsec_result_t res);
+
+/**
+ * Free IPsec result event
+ *
+ * Frees the ipsec_result into the ipsec_result pool it was allocated from.
+ *
+ * @param res           IPsec result handle
+ */
+void odp_ipsec_result_free(odp_ipsec_result_t res);
+
+/**
+ * Get ipsec_status handle from event
+ *
+ * Converts an ODP_EVENT_IPSEC_STATUS type event to an IPsec status event.
+ *
+ * @param ev   Event handle
+ *
+ * @return IPsec status handle
+ *
+ * @see odp_event_type()
+ */
+odp_ipsec_status_event_t odp_ipsec_status_from_event(odp_event_t ev);
+
+/**
+ * Convert IPsec status event handle to event
+ *
+ * @param res  IPsec status handle
+ *
+ * @return Event handle
+ */
+odp_event_t odp_ipsec_status_to_event(odp_ipsec_status_event_t res);
+
+/**
+ * Free IPsec status event
+ *
+ * Frees the ipsec_status into the ipsec_status pool it was allocated from.
+ *
+ * @param res           IPsec status handle
+ */
+void odp_ipsec_status_free(odp_ipsec_status_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..16bd35cc 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,12 @@  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_result_free(odp_ipsec_result_from_event(event));
+		break;
+	case ODP_EVENT_IPSEC_STATUS:
+		odp_ipsec_status_free(odp_ipsec_status_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..f15cd8d0 100644
--- a/platform/linux-generic/odp_ipsec.c
+++ b/platform/linux-generic/odp_ipsec.c
@@ -4,105 +4,1442 @@ 
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
+#include <odp/api/atomic.h>
 #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 */
+
+#define IPSEC_USERS_DISABLING	0x80000000
+
+typedef struct ipsec_sa_t {
+	odp_ticketlock_t lock ODP_ALIGNED_CACHE;
+	unsigned	is_reserved : 1;
+	odp_atomic_u32_t users;
+	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;
+
+	odp_ipsec_mode_t mode;
+	odp_u32be_t	tun_src_ip;
+	odp_u32be_t	tun_dst_ip;
+	uint8_t		tun_ttl;
+	/* 32-bit from which low 16 are used */
+	odp_atomic_u32_t tun_hdr_id;
+
+	odp_ipsec_protocol_t proto;
+	uint32_t	icv_len;
+	uint32_t	esp_iv_len;
+	uint32_t	esp_block_len;
+	uint32_t	spi;
+	odp_atomic_u32_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 ipsec_ctx_s ipsec_ctx_t;
+
+typedef void (*ipsec_postprocess_t)(ipsec_ctx_t *ctx);
+
+/**
+ * Per packet IPsec processing context
+ */
+struct ipsec_ctx_s {
+	odp_buffer_t buffer;     /**< Buffer for context */
+	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 */
+	unsigned hdr_len;        /**< Length of IPsec headers */
+	unsigned trl_len;        /**< Length of IPsec trailers */
+	uint16_t ipsec_offset;   /**< Offset of IPsec header from
+				      buffer start */
+
+	ipsec_postprocess_t postprocess;
+	odp_ipsec_sa_t sa;
+	odp_crypto_op_result_t crypto;
+	odp_ipsec_op_status_t status;
+	odp_packet_t pkt;
+
+	uint32_t src_ip;         /**< SA source IP address */
+	uint32_t dst_ip;         /**< SA dest IP address */
+	uint8_t	iv[MAX_IV_LEN];  /**< ESP IV storage */
+};
+
+typedef struct {
+	/* common buffer header */
+	odp_buffer_hdr_t buf_hdr;
+	ipsec_ctx_t *ctx;
+} ipsec_result_hdr_t;
+
+typedef struct {
+	/* common buffer header */
+	odp_buffer_hdr_t buf_hdr;
+
+	odp_ipsec_status_t status;
+} ipsec_status_hdr_t;
+
+static
+int odp_ipsec_status_send(odp_ipsec_sa_t sa,
+			  odp_ipsec_status_id_t id,
+			  int ret);
+
+#define IPSEC_CTX_POOL_BUF_COUNT 1024
+
+static odp_pool_t ipsec_ctx_pool = ODP_POOL_INVALID;
+static odp_pool_t ipsec_result_pool = ODP_POOL_INVALID;
+static odp_pool_t ipsec_status_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 param;
+
+	/* Create context buffer pool */
+	param.buf.size  = sizeof(ipsec_ctx_t);
+	param.buf.align = 0;
+	param.buf.num   = IPSEC_CTX_POOL_BUF_COUNT;
+	param.type      = ODP_POOL_BUFFER;
+
+	ipsec_ctx_pool = odp_pool_create("ipsec_ctx_pool", &param);
+	if (ODP_POOL_INVALID == ipsec_ctx_pool) {
+		ODP_ERR("Error: context pool create failed.\n");
+		return -1;
+	}
+
+	param.buf.size  = sizeof(ipsec_result_hdr_t);
+	param.buf.align = 0;
+	param.buf.num   = IPSEC_CTX_POOL_BUF_COUNT;
+	param.type      = ODP_POOL_BUFFER;
+
+	ipsec_result_pool = odp_pool_create("ipsec_result_pool", &param);
+	if (ODP_POOL_INVALID == ipsec_result_pool) {
+		ODP_ERR("Error: result pool create failed.\n");
+		(void)odp_pool_destroy(ipsec_ctx_pool);
+		return -1;
+	}
+
+	param.buf.size  = sizeof(ipsec_status_hdr_t);
+	param.buf.align = 0;
+	param.buf.num   = IPSEC_CTX_POOL_BUF_COUNT;
+	param.type      = ODP_POOL_BUFFER;
+
+	ipsec_status_pool = odp_pool_create("ipsec_status_pool", &param);
+	if (ODP_POOL_INVALID == ipsec_status_pool) {
+		ODP_ERR("Error: status pool create failed.\n");
+		(void)odp_pool_destroy(ipsec_ctx_pool);
+		return -1;
+	}
+
+	shm = odp_shm_reserve("ipsec_sa_table",
+			      sizeof(ipsec_sa_table_t),
+			      ODP_CACHE_LINE_SIZE, 0);
+
+	ipsec_sa_tbl = odp_shm_addr(shm);
+	if (ipsec_sa_tbl == NULL) {
+		(void)odp_pool_destroy(ipsec_result_pool);
+		(void)odp_pool_destroy(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;
+		odp_atomic_store_u32(&ipsec_sa->users, 0);
+	}
+
+	return 0;
+}
+
+int odp_ipsec_term_global(void)
+{
+	int i;
+	ipsec_sa_t *ipsec_sa;
+	int ret = 0;
+	int rc = 0;
+
+	for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) {
+		ipsec_sa = ipsec_sa_entry(i);
+
+		odp_ticketlock_lock(&ipsec_sa->lock);
+		if (ipsec_sa->is_reserved) {
+			ODP_ERR("Not destroyed ipsec_sa: %u\n", ipsec_sa->ipsec_sa_idx);
+			rc = -1;
+		}
+		ipsec_sa->is_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;
+	}
+
+	ret = odp_pool_destroy(ipsec_status_pool);
+	if (ret < 0) {
+		ODP_ERR("status pool destroy failed");
+		rc = -1;
+	}
+
+	ret = odp_pool_destroy(ipsec_result_pool);
+	if (ret < 0) {
+		ODP_ERR("result pool destroy failed");
+		rc = -1;
+	}
+
+	ret = odp_pool_destroy(ipsec_ctx_pool);
+	if (ret < 0) {
+		ODP_ERR("ctx pool destroy failed");
+		rc = -1;
+	}
+
+	return rc;
+}
+
+static ipsec_sa_t *ipsec_sa_reserve(void)
+{
+	int i;
+	ipsec_sa_t *ipsec_sa;
+
+	for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) {
+		ipsec_sa = ipsec_sa_entry(i);
+
+		odp_ticketlock_lock(&ipsec_sa->lock);
+		if (ipsec_sa->is_reserved == 0) {
+			ipsec_sa->is_reserved = 1;
+			odp_atomic_store_u32(&ipsec_sa->users, 0);
+			odp_ticketlock_unlock(&ipsec_sa->lock);
+
+			return ipsec_sa;
+		}
+		odp_ticketlock_unlock(&ipsec_sa->lock);
+	}
+
+	return NULL;
+}
+
+static
+int ipsec_sa_release(ipsec_sa_t *ipsec_sa)
+{
+	int rc = 0;
+
+	odp_ticketlock_lock(&ipsec_sa->lock);
+	if (ipsec_sa->is_reserved) {
+		ODP_ERR("Releasing unallocated ipsec_sa: %u\n", ipsec_sa->ipsec_sa_idx);
+		rc = -1;
+	} else {
+		ipsec_sa->is_reserved = 0;
+	}
+	odp_ticketlock_unlock(&ipsec_sa->lock);
+
+	return rc;
+}
 
 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 = ODP_SUPPORT_PREFERRED;
+	capa->op_mode_async = ODP_SUPPORT_YES;
+	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 = ipsec_sa_reserve();
+	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->proto = param->proto;
+	ipsec_sa->spi = param->spi;
+	odp_atomic_init_u32(&ipsec_sa->seq, param->seq);
+	ipsec_sa->context = param->context;
+	ipsec_sa->queue = param->dest_queue;
+	ipsec_sa->mode = param->mode;
+
+	if (ODP_IPSEC_MODE_TUNNEL == ipsec_sa->mode) {
+		if (param->tunnel.type != ODP_IPSEC_TUNNEL_IPV4) {
+			ipsec_sa_release(ipsec_sa);
+
+			return ODP_IPSEC_SA_INVALID;
+		}
+		memcpy(&ipsec_sa->tun_src_ip, param->tunnel.ipv4.src_addr, sizeof(ipsec_sa->tun_src_ip));
+		memcpy(&ipsec_sa->tun_dst_ip, param->tunnel.ipv4.dst_addr, sizeof(ipsec_sa->tun_dst_ip));
+		odp_atomic_init_u32(&ipsec_sa->tun_hdr_id, 0);
+		ipsec_sa->tun_ttl = param->tunnel.ipv4.ttl;
+	}
+
+	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 to implement 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->icv_len = 0;
+		break;
+	case ODP_AUTH_ALG_MD5_HMAC:
+	case ODP_AUTH_ALG_MD5_96:
+		ipsec_sa->icv_len = 12;
+		break;
+	case ODP_AUTH_ALG_SHA1_HMAC:
+		ipsec_sa->icv_len = 12;
+		break;
+	case ODP_AUTH_ALG_SHA256_HMAC:
+	case ODP_AUTH_ALG_SHA256_128:
+		ipsec_sa->icv_len = 16;
+		break;
+	case ODP_AUTH_ALG_SHA512_HMAC:
+		ipsec_sa->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 = 1;
+		break;
+	case ODP_CIPHER_ALG_DES:
+	case ODP_CIPHER_ALG_3DES_CBC:
+		ipsec_sa->esp_iv_len = 8;
+		ipsec_sa->esp_block_len = 8;
+		break;
+	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, 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;
+
+	return ipsec_sa->ipsec_sa_hdl;
+
+error:
+	ipsec_sa_release(ipsec_sa);
 
 	return ODP_IPSEC_SA_INVALID;
 }
 
 int odp_ipsec_sa_disable(odp_ipsec_sa_t sa)
 {
-	(void)sa;
+	ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+	uint32_t users;
+	int cas = 0;
 
-	return -1;
+	/* This is a custom rwlock implementation. It is not possible to use
+	 * original rwlock, because there is no way to test if current code is
+	 * the last reader when disable operation is pending. */
+	users = odp_atomic_load_u32(&ipsec_sa->users);
+
+	while (0 == cas) {
+		if (users & IPSEC_USERS_DISABLING)
+			return -1;
+
+		cas = odp_atomic_cas_acq_u32(&ipsec_sa->users, &users,
+					     users | IPSEC_USERS_DISABLING);
+	}
+
+	if (ODP_QUEUE_INVALID != ipsec_sa->queue) {
+		/*
+		 * If there were not active users when we disabled SA,
+		 * send the event.
+		 */
+		if (IPSEC_USERS_DISABLING == users)
+			odp_ipsec_status_send(sa,
+					      ODP_IPSEC_STATUS_SA_DISABLE,
+					      0);
+
+		return 0;
+	}
+
+	while (IPSEC_USERS_DISABLING != users) {
+		odp_cpu_pause();
+		users = odp_atomic_load_u32(&ipsec_sa->users);
+	}
+
+	return 0;
 }
 
 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;
+	uint32_t users = odp_atomic_load_u32(&ipsec_sa->users);
 
-	return -1;
+	if (IPSEC_USERS_DISABLING != users) {
+		ODP_ERR("Distroying not disabled ipsec_sa: %u\n", ipsec_sa->ipsec_sa_idx);
+		return -1;
+	}
+
+	if (odp_crypto_session_destroy(ipsec_sa->session) < 0) {
+		ODP_ERR("Error destroying crypto session for ipsec_sa: %u\n", ipsec_sa->ipsec_sa_idx);
+		rc = -1;
+	}
+
+	if (ipsec_sa_release(ipsec_sa) < 0)
+		return -1;
+	else
+		return rc;
+}
+
+static
+ipsec_sa_t *ipsec_sa_use(odp_ipsec_sa_t sa)
+{
+	ipsec_sa_t *ipsec_sa;
+	uint32_t users;
+	int cas = 0;
+
+	if (ODP_IPSEC_SA_INVALID == sa)
+		return NULL;
+
+	ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+	users = odp_atomic_load_u32(&ipsec_sa->users);
+	while (0 == cas) {
+		if (users & IPSEC_USERS_DISABLING)
+			return NULL;
+
+		cas = odp_atomic_cas_acq_u32(&ipsec_sa->users, &users,
+					     users + 1);
+	}
+
+	return ipsec_sa;
+}
+
+static
+void ipsec_sa_unuse(ipsec_sa_t *ipsec_sa)
+{
+	uint32_t users;
+	int cas = 0;
+
+	users = odp_atomic_load_u32(&ipsec_sa->users);
+	while (0 == cas)
+		cas = odp_atomic_cas_acq_u32(&ipsec_sa->users, &users,
+					     users - 1);
+
+	if (users == IPSEC_USERS_DISABLING)
+		odp_ipsec_status_send(ipsec_sa->ipsec_sa_hdl,
+				      ODP_IPSEC_STATUS_SA_DISABLE,
+				      0);
+}
+
+#define ipv4_hdr_len(ip) (_ODP_IPV4HDR_IHL(ip->ver_ihl) * 4)
+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);
+}
+
+/**
+ * Allocate per packet processing context and associate it with
+ * packet buffer
+ *
+ * @param pkt  Packet
+ *
+ * @return pointer to context area
+ */
+static
+ipsec_ctx_t *ipsec_ctx_alloc(void)
+{
+	odp_buffer_t ctx_buf = odp_buffer_alloc(ipsec_ctx_pool);
+	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 ipsec_ctx_free(ipsec_ctx_t *ctx)
+{
+	if (ODP_PACKET_INVALID != ctx->crypto.pkt)
+		odp_packet_free(ctx->crypto.pkt);
+
+	if (ODP_PACKET_INVALID != ctx->pkt)
+		odp_packet_free(ctx->pkt);
+
+	odp_buffer_free(ctx->buffer);
+}
+
+odp_ipsec_result_t odp_ipsec_result_from_event(odp_event_t ev)
+{
+	if (odp_unlikely(ODP_EVENT_INVALID == ev))
+		return ODP_IPSEC_RESULT_INVALID;
+
+	if (odp_event_type(ev) != ODP_EVENT_IPSEC_RESULT)
+		ODP_ABORT("Event not an IPsec result");
+
+	return (odp_ipsec_result_t)ev;
+}
+
+static ipsec_result_hdr_t *ipsec_result_hdr_from_buf(odp_buffer_t buf)
+{
+	return (ipsec_result_hdr_t *)(void *)buf_hdl_to_hdr(buf);
+}
+
+static ipsec_result_hdr_t *ipsec_result_hdr(odp_ipsec_result_t res)
+{
+	odp_buffer_t buf = odp_buffer_from_event(odp_ipsec_result_to_event(res));
+
+	return ipsec_result_hdr_from_buf(buf);
+}
+
+odp_event_t odp_ipsec_result_to_event(odp_ipsec_result_t res)
+{
+	if (odp_unlikely(res == ODP_IPSEC_RESULT_INVALID))
+		return ODP_EVENT_INVALID;
+
+	return (odp_event_t)res;
+}
+
+static
+odp_ipsec_result_t odp_ipsec_result_alloc(void)
+{
+	odp_buffer_t buf = odp_buffer_alloc(ipsec_result_pool);
+
+	if (odp_unlikely(buf == ODP_BUFFER_INVALID))
+		return ODP_IPSEC_RESULT_INVALID;
+
+	_odp_buffer_event_type_set(buf, ODP_EVENT_IPSEC_RESULT);
+
+	return odp_ipsec_result_from_event(odp_buffer_to_event(buf));
+}
+
+void odp_ipsec_result_free(odp_ipsec_result_t res)
+{
+	odp_event_t ev = odp_ipsec_result_to_event(res);
+	ipsec_result_hdr_t *res_hdr;
+
+	res_hdr = ipsec_result_hdr(res);
+	while (NULL != res_hdr->ctx) {
+		ipsec_ctx_t *ctx = res_hdr->ctx;
+
+		res_hdr->ctx = ctx->next;
+		ipsec_ctx_free(ctx);
+	}
+
+	odp_buffer_free(odp_buffer_from_event(ev));
+}
+
+odp_ipsec_status_event_t odp_ipsec_status_from_event(odp_event_t ev)
+{
+	if (odp_unlikely(ODP_EVENT_INVALID == ev))
+		return ODP_IPSEC_STATUS_EVENT_INVALID;
+
+	if (odp_event_type(ev) != ODP_EVENT_IPSEC_STATUS)
+		ODP_ABORT("Event not an IPsec status");
+
+	return (odp_ipsec_status_event_t)ev;
+}
+
+static ipsec_status_hdr_t *ipsec_status_hdr_from_buf(odp_buffer_t buf)
+{
+	return (ipsec_status_hdr_t *)(void *)buf_hdl_to_hdr(buf);
+}
+
+static ipsec_status_hdr_t *ipsec_status_hdr(odp_ipsec_status_event_t res)
+{
+	odp_buffer_t buf = odp_buffer_from_event(odp_ipsec_status_to_event(res));
+
+	return ipsec_status_hdr_from_buf(buf);
+}
+
+odp_event_t odp_ipsec_status_to_event(odp_ipsec_status_event_t res)
+{
+	if (odp_unlikely(res == ODP_IPSEC_STATUS_EVENT_INVALID))
+		return ODP_EVENT_INVALID;
+
+	return (odp_event_t)res;
+}
+
+static
+odp_ipsec_status_event_t odp_ipsec_status_alloc(void)
+{
+	odp_buffer_t buf = odp_buffer_alloc(ipsec_status_pool);
+
+	if (odp_unlikely(buf == ODP_BUFFER_INVALID))
+		return ODP_IPSEC_STATUS_EVENT_INVALID;
+
+	_odp_buffer_event_type_set(buf, ODP_EVENT_IPSEC_STATUS);
+
+	return odp_ipsec_status_from_event(odp_buffer_to_event(buf));
+}
+
+void odp_ipsec_status_free(odp_ipsec_status_event_t res)
+{
+	odp_event_t ev = odp_ipsec_status_to_event(res);
+
+	odp_buffer_free(odp_buffer_from_event(ev));
+}
+
+static
+int odp_ipsec_status_send(odp_ipsec_sa_t sa,
+			  odp_ipsec_status_id_t id,
+			  int ret)
+{
+	odp_ipsec_status_event_t ipsec_ev = odp_ipsec_status_alloc();
+	odp_event_t ev;
+	odp_queue_t queue;
+	ipsec_status_hdr_t *status_hdr;
+
+	if (ODP_IPSEC_STATUS_EVENT_INVALID == ipsec_ev)
+		return -1;
+
+	status_hdr = ipsec_status_hdr(ipsec_ev);
+
+	status_hdr->status.id = id;
+	status_hdr->status.ret = ret;
+	status_hdr->status.sa = sa;
+
+	if (ODP_IPSEC_SA_INVALID == sa)
+		queue = ipsec_config.inbound.default_queue;
+	else
+		queue = ipsec_sa_entry_from_hdl(sa)->queue;
+
+	ev = odp_ipsec_status_to_event(ipsec_ev);
+
+	if (odp_queue_enq(queue, ev)) {
+		odp_event_free(ev);
+		return -1;
+	}
+
+	return 0;
+}
+
+static
+void ipsec_finish(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 (result->cipher_status.alg_err == ODP_CRYPTO_ALG_ERR_NONE ||
+		    result->cipher_status.hw_err == ODP_CRYPTO_HW_ERR_NONE)
+			res->status.error.alg = 1;
+
+		if (result->auth_status.alg_err == ODP_CRYPTO_ALG_ERR_NONE ||
+		    result->auth_status.hw_err == ODP_CRYPTO_HW_ERR_NONE)
+			res->status.error.auth = 1;
+	} else {
+		ctx->pkt = result->pkt;
+		result->pkt = ODP_PACKET_INVALID;
+
+		if (ctx->postprocess)
+			ctx->postprocess(ctx);
+	}
+
+	*pkt = ctx->pkt;
+	ctx->pkt = ODP_PACKET_INVALID;
+
+	res->sa = ctx->sa;
+}
+
+static
+void ipsec_in_postprocess(ipsec_ctx_t *ctx);
+
+static
+int ipsec_in_single(ipsec_ctx_t *ctx)
+{
+	odp_packet_t pkt = ctx->pkt;
+	ipsec_sa_t *ipsec_sa;
+	uint32_t ip_offset = odp_packet_l3_offset(pkt);
+	_odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+	uint16_t ip_hdr_len = ipv4_hdr_len(ip);
+	odp_crypto_op_param_t param;
+	odp_bool_t posted = 0;
+	int rc = -1;
+
+	ctx->crypto.pkt = ODP_PACKET_INVALID;
+
+	ipsec_sa = ipsec_sa_use(ctx->sa);
+	if (odp_unlikely(NULL == ipsec_sa)) {
+		ctx->status.error.sa_lookup = 1;
+		return -1;
+	}
+
+	/* Initialize parameters block */
+	memset(&param, 0, sizeof(param));
+	param.session = ipsec_sa->session;
+	param.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->postprocess = ipsec_in_postprocess;
+	ctx->ipsec_offset = ip_offset + ip_hdr_len;
+
+	/* Check IP header for IPSec protocols and look it up */
+	if (_ODP_IPPROTO_AH == ip->proto) {
+		_odp_ahhdr_t ah;
+
+		if (ODP_IPSEC_AH != ipsec_sa->proto) {
+			ctx->status.error.proto = 1;
+			goto out;
+		}
+
+		if (odp_packet_copy_to_mem(pkt, ctx->ipsec_offset, sizeof(ah), &ah) < 0) {
+			ctx->status.error.alg = 1;
+			goto out;
+		}
+
+		ctx->hdr_len = (ah.ah_len + 2) * 4;
+		ctx->trl_len = 0;
+
+		/* If authenticating, zero the mutable fields build the request */
+		ip->chksum = 0;
+		ip->tos = 0;
+		ip->frag_offset = 0;
+		ip->ttl = 0;
+
+		param.auth_range.offset = ip_offset;
+		param.auth_range.length = odp_be_to_cpu_16(ip->tot_len);
+		param.hash_result_offset = ctx->ipsec_offset + _ODP_AHHDR_LEN;
+	} else if (_ODP_IPPROTO_ESP == ip->proto) {
+		_odp_esphdr_t esp;
+
+		if (ODP_IPSEC_ESP != ipsec_sa->proto) {
+			ctx->status.error.proto = 1;
+			goto out;
+		}
+
+		if (odp_packet_copy_to_mem(pkt, ctx->ipsec_offset, sizeof(esp), &esp) < 0) {
+			ctx->status.error.alg = 1;
+			goto out;
+		}
+
+		if (odp_packet_copy_to_mem(pkt, ctx->ipsec_offset + _ODP_ESPHDR_LEN, ipsec_sa->esp_iv_len, ctx->iv) < 0) {
+			ctx->status.error.alg = 1;
+			goto out;
+		}
+
+		ctx->hdr_len = _ODP_ESPHDR_LEN + ipsec_sa->esp_iv_len;
+		ctx->trl_len = _ODP_ESPTRL_LEN + ipsec_sa->icv_len;
+
+		param.cipher_range.offset = ctx->ipsec_offset + ctx->hdr_len;
+		param.cipher_range.length = odp_be_to_cpu_16(ip->tot_len) - ip_hdr_len - ctx->hdr_len - ipsec_sa->icv_len;
+		param.override_iv_ptr = ctx->iv;
+
+		param.auth_range.offset = ctx->ipsec_offset;
+		param.auth_range.length = odp_be_to_cpu_16(ip->tot_len) - ip_hdr_len;
+		param.hash_result_offset = ip_offset + odp_be_to_cpu_16(ip->tot_len) - ipsec_sa->icv_len;
+	} else {
+		ctx->status.error.proto = 1;
+		goto out;
+	}
+
+	param.pkt = pkt;
+	/* Create new packet after all length extensions */
+	if (ipsec_sa->in_place) {
+		param.out_pkt = pkt;
+	} else {
+		param.out_pkt = odp_packet_alloc(odp_packet_pool(pkt),
+						  odp_packet_len(pkt));
+		/* uarea will be copied by odp_crypto_operation */
+		odp_packet_user_ptr_set(param.out_pkt,
+					odp_packet_user_ptr(param.pkt));
+	}
+
+	rc = odp_crypto_operation(&param, &posted, &ctx->crypto);
+	if (rc < 0) {
+		ODP_DBG("Crypto failed\n");
+		ctx->status.error.alg = 1;
+		ipsec_sa_unuse(ipsec_sa);
+
+		return rc;
+	}
+
+	ODP_ASSERT(!posted);
+
+out:
+	ipsec_sa_unuse(ipsec_sa);
+
+	return rc;
+}
+
+static
+void ipsec_in_postprocess(ipsec_ctx_t *ctx)
+{
+	odp_packet_t pkt = ctx->pkt;
+	uint32_t ip_offset = odp_packet_l3_offset(pkt);
+	_odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+	uint16_t ip_hdr_len = ipv4_hdr_len(ip);
+
+	if (_ODP_IPPROTO_AH == ip->proto) {
+		/*
+		 * Finish auth
+		 */
+		_odp_ahhdr_t ah;
+
+		if (odp_packet_copy_to_mem(pkt, ctx->ipsec_offset, sizeof(ah), &ah) < 0) {
+			ctx->status.error.alg = 1;
+			goto out;
+		}
+
+		ip->proto = ah.next_header;
+
+		/* Restore mutable fields */
+		ip->ttl = ctx->ip_ttl;
+		ip->tos = ctx->ip_tos;
+		ip->frag_offset = odp_cpu_to_be_16(ctx->ip_frag_offset);
+	} else if (_ODP_IPPROTO_ESP == ip->proto) {
+		/*
+		 * Finish cipher by finding ESP trailer and processing
+		 */
+		_odp_esptrl_t esptrl;
+		uint32_t esptrl_offset = ip_offset + odp_be_to_cpu_16(ip->tot_len) - ctx->trl_len;
+
+		if (odp_packet_copy_to_mem(pkt, esptrl_offset, sizeof(esptrl), &esptrl) < 0) {
+			ctx->status.error.alg = 1;
+			goto out;
+		}
+
+		ip->proto = esptrl.next_header;
+		ctx->trl_len += esptrl.pad_len;
+	} else {
+		ctx->status.error.proto = 1;
+		goto out;
+	}
+
+	if (ip->proto == _ODP_IPV4) {
+		ip->ttl -= 1;
+		ip->chksum = 0;
+		_odp_ipv4_csum_update(pkt);
+
+		/* We have a tunneled IPv4 packet, strip outer and IPsec headers */
+		odp_packet_move_data(pkt, ip_hdr_len + ctx->hdr_len, 0, ip_offset);
+		if (odp_packet_trunc_head(&pkt, ip_hdr_len + ctx->hdr_len, NULL, NULL) < 0) {
+			ctx->status.error.alg = 1;
+			goto out;
+		}
+
+	} else {
+		/* Finalize the IPv4 header */
+		ipv4_adjust_len(ip, -(ctx->hdr_len + ctx->trl_len));
+
+		ip->chksum = 0;
+		_odp_ipv4_csum_update(pkt);
+
+		odp_packet_move_data(pkt, ctx->hdr_len, 0, ip_offset + ip_hdr_len);
+		if (odp_packet_trunc_head(&pkt, ctx->hdr_len, NULL, NULL) < 0) {
+			ctx->status.error.alg = 1;
+			goto out;
+		}
+	}
+
+	if (odp_packet_trunc_tail(&pkt, ctx->trl_len, NULL, NULL) < 0)
+		ctx->status.error.alg = 1;
+
+out:
+	ctx->pkt = pkt;
+}
+
+/** Helper for calculating encode length using data length and block size */
+#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b)
+
+static
+void ipsec_out_postprocess(ipsec_ctx_t *ctx);
+
+static
+int ipsec_out_single(ipsec_ctx_t *ctx)
+{
+	odp_packet_t pkt = ctx->pkt;
+	ipsec_sa_t *ipsec_sa;
+	uint32_t ip_offset = odp_packet_l3_offset(pkt);
+	_odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+	uint16_t ip_hdr_len = ipv4_hdr_len(ip);
+	odp_crypto_op_param_t param;
+	odp_bool_t posted = 0;
+	int rc = -1;
+
+	ctx->crypto.pkt = ODP_PACKET_INVALID;
+
+	ipsec_sa = ipsec_sa_use(ctx->sa);
+	if (odp_unlikely(NULL == ipsec_sa)) {
+		ctx->status.error.sa_lookup = 1;
+		return -1;
+	}
+
+	/* Initialize parameters block */
+	memset(&param, 0, sizeof(param));
+	param.session = ipsec_sa->session;
+	param.pkt = pkt;
+	param.ctx = ctx;
+
+	if (ipsec_sa->mode == ODP_IPSEC_MODE_TUNNEL) {
+		_odp_ipv4hdr_t out_ip;
+		_odp_ipv4hdr_t *inner_ip;
+		uint16_t tun_hdr_offset = ip_offset + ip_hdr_len;
+
+		if (odp_packet_extend_head(&pkt, _ODP_IPV4HDR_LEN, NULL, NULL) < 0) {
+			ctx->status.error.alg = 1;
+			goto out;
+		}
+
+		odp_packet_move_data(pkt, 0, _ODP_IPV4HDR_LEN, ip_offset);
+
+		inner_ip = odp_packet_offset(pkt, tun_hdr_offset, NULL, NULL);
+
+		out_ip.ver_ihl = 0x45;
+		out_ip.tos = inner_ip->tos; // FIXME
+		out_ip.tot_len = odp_cpu_to_be_16(odp_be_to_cpu_16(inner_ip->tot_len) + _ODP_IPV4HDR_LEN);
+		/* No need to convert to BE: ID just should not be duplicated */
+		out_ip.id = (odp_atomic_fetch_add_u32(&ipsec_sa->tun_hdr_id, 1) + 1) & 0xffff;
+		out_ip.frag_offset = 0;
+		out_ip.ttl = ipsec_sa->tun_ttl;
+		out_ip.proto = _ODP_IPV4;
+		out_ip.chksum = 0;
+		out_ip.src_addr = ipsec_sa->tun_src_ip;
+		out_ip.dst_addr = ipsec_sa->tun_dst_ip;
+
+		odp_packet_copy_from_mem(pkt, ip_offset, _ODP_IPV4HDR_LEN, &out_ip);
+
+		odp_packet_l4_offset_set(pkt, ip_offset + _ODP_IPV4HDR_LEN);
+
+		ip = odp_packet_l3_ptr(pkt, NULL);
+		ip_hdr_len = _ODP_IPV4HDR_LEN;
+	}
+
+	/* 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 = ipsec_out_postprocess;
+
+	ctx->ipsec_offset = ip_offset + ip_hdr_len;
+
+	if (ipsec_sa->proto == ODP_IPSEC_AH) {
+		ctx->hdr_len = _ODP_AHHDR_LEN + ipsec_sa->icv_len;
+		ctx->trl_len = 0;
+	} else if (ipsec_sa->proto == ODP_IPSEC_ESP) {
+		uint32_t encrypt_len;
+		uint16_t ip_next_len = odp_be_to_cpu_16(ip->tot_len) - ip_hdr_len;
+
+		ctx->hdr_len += _ODP_ESPHDR_LEN + ipsec_sa->esp_iv_len;
+
+		encrypt_len = ESP_ENCODE_LEN(ip_next_len + _ODP_ESPTRL_LEN,
+					     ipsec_sa->esp_block_len);
+		ctx->trl_len = encrypt_len - ip_next_len + ipsec_sa->icv_len;
+	} else {
+		ctx->status.error.proto = 1;
+		goto out;
+	}
+
+	if (odp_packet_extend_tail(&pkt, ctx->trl_len, NULL, NULL) < 0) {
+		ctx->status.error.alg = 1;
+		goto out;
+	}
+
+	if (odp_packet_extend_head(&pkt, ctx->hdr_len, NULL, NULL) < 0) {
+		ctx->status.error.alg = 1;
+		goto out;
+	}
+
+	odp_packet_move_data(pkt, 0, ctx->hdr_len, ctx->ipsec_offset);
+
+	ip = odp_packet_l3_ptr(pkt, NULL);
+
+	/* Set IPv4 length before authentication */
+	ipv4_adjust_len(ip, ctx->hdr_len + ctx->trl_len);
+
+	/* For authentication, build header clear mutables and build request */
+	if (ipsec_sa->proto == ODP_IPSEC_AH) {
+		_odp_ahhdr_t ah = {};
+		uint8_t icv[ipsec_sa->icv_len];
+
+		ah.spi = odp_cpu_to_be_32(ipsec_sa->spi);
+		ah.ah_len = 1 + (ipsec_sa->icv_len / 4);
+		ah.seq_no = odp_cpu_to_be_32(odp_atomic_fetch_add_u32(&ipsec_sa->seq, 1) + 1);
+		ah.next_header = ip->proto;
+		ip->proto = _ODP_IPPROTO_AH;
+
+		odp_packet_copy_from_mem(pkt, ctx->ipsec_offset, _ODP_AHHDR_LEN, &ah);
+		memset(icv, 0, ipsec_sa->icv_len);
+		odp_packet_copy_from_mem(pkt, ctx->ipsec_offset + _ODP_AHHDR_LEN, ipsec_sa->icv_len, icv);
+
+		ip->chksum = 0;
+		ip->tos = 0;
+		ip->frag_offset = 0;
+		ip->ttl = 0;
+
+		param.auth_range.offset = ip_offset;
+		param.auth_range.length = odp_be_to_cpu_16(ip->tot_len);
+		param.hash_result_offset = ctx->ipsec_offset + _ODP_AHHDR_LEN;
+	}
+
+	if (ipsec_sa->proto == ODP_IPSEC_ESP) {
+		_odp_esphdr_t esp = {};
+		_odp_esptrl_t esptrl = {};
+		uint32_t esptrl_offset = ip_offset + odp_be_to_cpu_16(ip->tot_len) - ipsec_sa->icv_len - _ODP_ESPTRL_LEN;
+
+		esp.spi = odp_cpu_to_be_32(ipsec_sa->spi);
+		esp.seq_no = odp_cpu_to_be_32(odp_atomic_fetch_add_u32(&ipsec_sa->seq, 1) + 1);
+
+		esptrl.pad_len = ctx->trl_len - _ODP_ESPTRL_LEN - ipsec_sa->icv_len;
+		esptrl.next_header = ip->proto;
+		ip->proto = _ODP_IPPROTO_ESP;
+
+		odp_packet_copy_from_mem(pkt, ctx->ipsec_offset, _ODP_ESPHDR_LEN, &esp);
+		odp_packet_copy_from_mem(pkt, ctx->ipsec_offset + _ODP_ESPHDR_LEN, ipsec_sa->esp_iv_len, ipsec_sa->iv);
+		odp_packet_copy_from_mem(pkt, esptrl_offset, _ODP_ESPTRL_LEN, &esptrl);
+
+		param.cipher_range.offset = ctx->ipsec_offset + ctx->hdr_len;
+		param.cipher_range.length = odp_be_to_cpu_16(ip->tot_len) - ip_hdr_len - ctx->hdr_len - ipsec_sa->icv_len;
+
+		param.auth_range.offset = ctx->ipsec_offset;
+		param.auth_range.length = odp_be_to_cpu_16(ip->tot_len) - ip_hdr_len;
+		param.hash_result_offset = ip_offset + odp_be_to_cpu_16(ip->tot_len) - ipsec_sa->icv_len;
+	}
+
+	param.pkt = pkt;
+	/* Create new packet after all length extensions */
+	if (ipsec_sa->in_place) {
+		param.out_pkt = pkt;
+	} else {
+		param.out_pkt = odp_packet_alloc(odp_packet_pool(pkt),
+						  odp_packet_len(pkt));
+		odp_packet_user_ptr_set(param.out_pkt,
+					odp_packet_user_ptr(param.pkt));
+	}
+
+	rc = odp_crypto_operation(&param, &posted, &ctx->crypto);
+	if (rc < 0) {
+		ODP_DBG("Crypto failed\n");
+		ctx->status.error.alg = 1;
+		ipsec_sa_unuse(ipsec_sa);
+
+		return rc;
+	}
+
+	ODP_ASSERT(!posted);
+
+out:
+	ctx->pkt = pkt;
+	ipsec_sa_unuse(ipsec_sa);
+
+	return rc;
+}
+
+static
+void ipsec_out_postprocess(ipsec_ctx_t *ctx)
+{
+	odp_packet_t pkt = ctx->pkt;
+	_odp_ipv4hdr_t *ip = odp_packet_l3_ptr(pkt, NULL);
+
+	/* Finalize the IPv4 header */
+	if (ip->proto == _ODP_IPPROTO_AH) {
+		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);
 }
 
+#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;
+		ipsec_ctx_t ctx;
+
+		memset(&ctx, 0, sizeof(ctx));
+		ctx.buffer = ODP_BUFFER_INVALID;
+
+		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
+
+		ctx.pkt = input->pkt[in_pkt];
+		ctx.sa = sa;
+
+		if (ipsec_in_single(&ctx) < 0)
+			ctx.status.error.alg = 1;
+
+		ipsec_finish(&ctx, &output->res[out_pkt], &output->pkt[out_pkt]);
+
+		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;
+		ipsec_ctx_t ctx;
+
+		memset(&ctx, 0, sizeof(ctx));
+		ctx.buffer = ODP_BUFFER_INVALID;
+
+		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
+
+		ctx.pkt = input->pkt[in_pkt];
+		ctx.sa = sa;
+
+		if (ipsec_out_single(&ctx) < 0)
+			ctx.status.error.alg = 1;
+
+		ipsec_finish(&ctx, &output->res[out_pkt], &output->pkt[out_pkt]);
+
+		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_result_t ipsec_ev;
+		odp_event_t ev;
+		ipsec_result_hdr_t *res_hdr;
+		odp_ipsec_sa_t sa;
+		ipsec_ctx_t *ctx;
+		odp_queue_t queue;
+
+		ipsec_ev = odp_ipsec_result_alloc();
+		if (ODP_IPSEC_RESULT_INVALID == ipsec_ev)
+			break;
+
+		ev = odp_ipsec_result_to_event(ipsec_ev);
+
+		res_hdr = ipsec_result_hdr(ipsec_ev);
+
+		ctx = ipsec_ctx_alloc();
+		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
+
+		ctx->pkt = input->pkt[in_pkt];
+		ctx->sa = sa;
+
+		if (ipsec_in_single(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_result_t ipsec_ev;
+		odp_event_t ev;
+		ipsec_result_hdr_t *res_hdr;
+		odp_ipsec_sa_t sa;
+		ipsec_ctx_t *ctx;
+		odp_queue_t queue;
+
+		ipsec_ev = odp_ipsec_result_alloc();
+		if (ODP_IPSEC_RESULT_INVALID == ipsec_ev)
+			break;
+
+		ev = odp_ipsec_result_to_event(ipsec_ev);
+
+		res_hdr = ipsec_result_hdr(ipsec_ev);
+
+		ctx = ipsec_ctx_alloc();
+		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
+
+		ctx->pkt = input->pkt[in_pkt];
+		ctx->sa = sa;
+
+		if (ipsec_out_single(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,
@@ -116,18 +1453,63 @@  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;
+	odp_ipsec_result_t ipsec_ev;
+	ipsec_result_hdr_t *res_hdr;
+	unsigned out_pkt;
+	ipsec_ctx_t *ctx;
 
-	return -1;
+	if (odp_unlikely(ODP_EVENT_INVALID == event))
+		return -1;
+
+	ipsec_ev = odp_ipsec_result_from_event(event);
+	if (odp_unlikely(ODP_IPSEC_RESULT_INVALID == ipsec_ev))
+		return -1;
+
+	res_hdr = ipsec_result_hdr(ipsec_ev);
+
+	for (out_pkt = 0; out_pkt < result->num_pkt; out_pkt++) {
+		ipsec_ctx_t *ctx = res_hdr->ctx;
+
+		res_hdr->ctx = ctx->next;
+		ipsec_finish(ctx, &result->res[out_pkt], &result->pkt[out_pkt]);
+
+		ipsec_ctx_free(ctx);
+	}
+
+	result->num_pkt = out_pkt;
+
+	/* FIXME: this is place with semantics change */
+	if (!res_hdr->ctx) {
+		odp_ipsec_result_free(ipsec_ev);
+		return 0;
+	}
+	out_pkt = 0;
+
+	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)
 {
 	(void)status;
 	(void)event;
+	odp_ipsec_status_event_t ipsec_ev;
+	ipsec_status_hdr_t *status_hdr;
 
-	return -1;
+	if (odp_unlikely(ODP_EVENT_INVALID == event))
+		return -1;
+
+	ipsec_ev = odp_ipsec_status_from_event(event);
+	if (odp_unlikely(ODP_IPSEC_STATUS_EVENT_INVALID == ipsec_ev))
+		return -1;
+
+	status_hdr = ipsec_status_hdr(ipsec_ev);
+
+	*status = status_hdr->status;
+
+	return 0;
 }
 
 int odp_ipsec_mtu_update(odp_ipsec_sa_t sa, uint32_t mtu)
@@ -140,9 +1522,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)