For now it's only a preview with the following limitation:
 - No inline processing support
 - No SA lookups
 - Only IPv4 support
 - No tunnel support
 - No header modification according to RFCs

Signed-off-by: Dmitry Eremin-Solenikov <dmitry.ereminsoleni...@linaro.org>
---
 platform/linux-generic/include/odp_internal.h      |    4 +
 .../linux-generic/include/odp_ipsec_internal.h     |   71 ++
 platform/linux-generic/include/protocols/ip.h      |   52 +
 platform/linux-generic/odp_event.c                 |    5 +
 platform/linux-generic/odp_init.c                  |   13 +
 platform/linux-generic/odp_ipsec.c                 | 1172 +++++++++++++++++++-
 6 files changed, 1287 insertions(+), 30 deletions(-)
 create mode 100644 platform/linux-generic/include/odp_ipsec_internal.h

diff --git a/platform/linux-generic/include/odp_internal.h 
b/platform/linux-generic/include/odp_internal.h
index 05c8a422..fd7848ac 100644
--- a/platform/linux-generic/include/odp_internal.h
+++ b/platform/linux-generic/include/odp_internal.h
@@ -71,6 +71,7 @@ enum init_stage {
        CLASSIFICATION_INIT,
        TRAFFIC_MNGR_INIT,
        NAME_TABLE_INIT,
+       IPSEC_INIT,
        MODULES_INIT,
        ALL_INIT      /* All init stages completed */
 };
@@ -130,6 +131,9 @@ int _odp_ishm_init_local(void);
 int _odp_ishm_term_global(void);
 int _odp_ishm_term_local(void);
 
+int odp_ipsec_init_global(void);
+int odp_ipsec_term_global(void);
+
 int _odp_modules_init_global(void);
 
 int cpuinfo_parser(FILE *file, system_info_t *sysinfo);
diff --git a/platform/linux-generic/include/odp_ipsec_internal.h 
b/platform/linux-generic/include/odp_ipsec_internal.h
new file mode 100644
index 00000000..c7620b88
--- /dev/null
+++ b/platform/linux-generic/include/odp_ipsec_internal.h
@@ -0,0 +1,71 @@
+/* Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:    BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP internal IPsec routines
+ */
+
+#ifndef ODP_IPSEC_INTERNAL_H_
+#define ODP_IPSEC_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/plat/strong_types.h>
+
+/** @ingroup odp_ipsec
+ *  @{
+ */
+
+typedef ODP_HANDLE_T(odp_ipsec_op_result_event_t);
+
+#define ODP_IPSEC_OP_RESULT_EVENT_INVALID \
+       _odp_cast_scalar(odp_ipsec_op_result_event_t, 0xffffffff)
+
+/**
+ * Get ipsec_op_result_event handle from event
+ *
+ * Converts an ODP_EVENT_IPSEC_RESULT_EVENT type event to an IPsec result 
event.
+ *
+ * @param ev   Event handle
+ *
+ * @return IPsec result handle
+ *
+ * @see odp_event_type()
+ */
+odp_ipsec_op_result_event_t odp_ipsec_op_result_event_from_event(odp_event_t 
ev);
+
+/**
+ * Convert IPsec result event handle to event
+ *
+ * @param res  IPsec result handle
+ *
+ * @return Event handle
+ */
+odp_event_t odp_ipsec_op_result_event_to_event(odp_ipsec_op_result_event_t 
res);
+
+/**
+ * Free IPsec result event
+ *
+ * Frees the ipsec_op_result_event into the ipsec_op_result_event pool it was 
allocated from.
+ *
+ * @param res           IPsec result handle
+ */
+void odp_ipsec_op_result_event_free(odp_ipsec_op_result_event_t res);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/protocols/ip.h 
b/platform/linux-generic/include/protocols/ip.h
index 2b34a753..9f3e1616 100644
--- a/platform/linux-generic/include/protocols/ip.h
+++ b/platform/linux-generic/include/protocols/ip.h
@@ -89,6 +89,58 @@ typedef struct ODP_PACKED {
 ODP_STATIC_ASSERT(sizeof(_odp_ipv4hdr_t) == _ODP_IPV4HDR_LEN,
                  "_ODP_IPV4HDR_T__SIZE_ERROR");
 
+/**
+ * Checksum
+ *
+ * @param buffer calculate chksum for buffer
+ * @param len    buffer length
+ *
+ * @return checksum value in host cpu order
+ */
+static inline odp_u16sum_t _odp_chksum(void *buffer, int len)
+{
+       uint16_t *buf = (uint16_t *)buffer;
+       uint32_t sum = 0;
+       uint16_t result;
+
+       for (sum = 0; len > 1; len -= 2)
+               sum += *buf++;
+
+       if (len == 1)
+               sum += *(unsigned char *)buf;
+
+       sum = (sum >> 16) + (sum & 0xFFFF);
+       sum += (sum >> 16);
+       result = ~sum;
+
+       return  (__odp_force odp_u16sum_t) result;
+}
+
+/**
+ * Calculate and fill in IPv4 checksum
+ *
+ * @note when using this api to populate data destined for the wire
+ * odp_cpu_to_be_16() can be used to remove sparse warnings
+ *
+ * @param pkt  ODP packet
+ *
+ * @return IPv4 checksum in host cpu order, or 0 on failure
+ */
+static inline odp_u16sum_t _odp_ipv4_csum_update(odp_packet_t pkt)
+{
+       uint16_t *w;
+       _odp_ipv4hdr_t *ip;
+       int nleft = sizeof(_odp_ipv4hdr_t);
+
+       ip = (_odp_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+       if (ip == NULL)
+               return 0;
+
+       w = (uint16_t *)(void *)ip;
+       ip->chksum = _odp_chksum(w, nleft);
+       return ip->chksum;
+}
+
 /** IPv6 version */
 #define _ODP_IPV6 6
 
diff --git a/platform/linux-generic/odp_event.c 
b/platform/linux-generic/odp_event.c
index d71f4464..1a2cc0aa 100644
--- a/platform/linux-generic/odp_event.c
+++ b/platform/linux-generic/odp_event.c
@@ -7,10 +7,12 @@
 #include <odp/api/event.h>
 #include <odp/api/buffer.h>
 #include <odp/api/crypto.h>
+#include <odp/api/ipsec.h>
 #include <odp/api/packet.h>
 #include <odp/api/timer.h>
 #include <odp/api/pool.h>
 #include <odp_buffer_internal.h>
+#include <odp_ipsec_internal.h>
 #include <odp_buffer_inlines.h>
 #include <odp_debug_internal.h>
 
@@ -34,6 +36,9 @@ void odp_event_free(odp_event_t event)
        case ODP_EVENT_CRYPTO_COMPL:
                odp_crypto_compl_free(odp_crypto_compl_from_event(event));
                break;
+       case ODP_EVENT_IPSEC_RESULT:
+               
odp_ipsec_op_result_event_free(odp_ipsec_op_result_event_from_event(event));
+               break;
        default:
                ODP_ABORT("Invalid event type: %d\n", odp_event_type(event));
        }
diff --git a/platform/linux-generic/odp_init.c 
b/platform/linux-generic/odp_init.c
index 685e02fa..bebcc62e 100644
--- a/platform/linux-generic/odp_init.c
+++ b/platform/linux-generic/odp_init.c
@@ -266,6 +266,12 @@ int odp_init_global(odp_instance_t *instance,
        }
        stage = NAME_TABLE_INIT;
 
+       if (odp_ipsec_init_global()) {
+               ODP_ERR("ODP IPsec init failed.\n");
+               goto init_failed;
+       }
+       stage = IPSEC_INIT;
+
        if (_odp_modules_init_global()) {
                ODP_ERR("ODP modules init failed\n");
                goto init_failed;
@@ -296,6 +302,13 @@ int _odp_term_global(enum init_stage stage)
        switch (stage) {
        case ALL_INIT:
        case MODULES_INIT:
+       case IPSEC_INIT:
+               if (odp_ipsec_term_global()) {
+                       ODP_ERR("ODP IPsec term failed.\n");
+                       rc = -1;
+               }
+               /* Fall through */
+
        case NAME_TABLE_INIT:
                if (_odp_int_name_tbl_term_global()) {
                        ODP_ERR("Name table term failed.\n");
diff --git a/platform/linux-generic/odp_ipsec.c 
b/platform/linux-generic/odp_ipsec.c
index 10918dfb..2cc4c690 100644
--- a/platform/linux-generic/odp_ipsec.c
+++ b/platform/linux-generic/odp_ipsec.c
@@ -5,56 +5,392 @@
  */
 
 #include <odp/api/ipsec.h>
+#include <odp/api/packet.h>
+#include <odp/api/shared_memory.h>
+#include <odp/api/ticketlock.h>
+
+#include <odp_buffer_internal.h>
+#include <odp_buffer_inlines.h>
+#include <odp_debug_internal.h>
+#include <odp_ipsec_internal.h>
+#include <odp_pool_internal.h>
+
+#include <odp/api/plat/ticketlock_inlines.h>
+
+#include <protocols/ip.h>
+#include <protocols/ipsec.h>
 
 #include <string.h>
+#include <stdbool.h>
+
+#define ODP_CONFIG_IPSEC_SAS   8
+
+#define MAX_IV_LEN             32   /**< Maximum IV length in bytes */
+
+typedef struct ipsec_sa_t {
+       odp_ticketlock_t lock ODP_ALIGNED_CACHE;
+       int reserved;
+       odp_ipsec_sa_t  ipsec_sa_hdl;
+       uint32_t        ipsec_sa_idx;
+
+       odp_crypto_session_t session;
+       odp_bool_t      in_place;
+       void            *context;
+       odp_queue_t     queue;
+
+       uint32_t        ah_icv_len;
+       uint32_t        esp_iv_len;
+       uint32_t        esp_block_len;
+       uint32_t        spi;
+       uint32_t        seq;
+       uint8_t         iv[MAX_IV_LEN];  /**< ESP IV storage */
+} ipsec_sa_t;
+
+typedef struct ipsec_sa_table_t {
+       ipsec_sa_t ipsec_sa[ODP_CONFIG_IPSEC_SAS];
+       odp_shm_t shm;
+} ipsec_sa_table_t;
+
+static ipsec_sa_table_t *ipsec_sa_tbl;
+
+typedef struct odp_ipsec_ctx_s odp_ipsec_ctx_t;
+
+typedef void (*odp_ipsecproc_t)(odp_packet_t pkt, odp_ipsec_ctx_t *ctx);
+
+/**
+ * Per packet IPsec processing context
+ */
+struct odp_ipsec_ctx_s {
+       odp_buffer_t buffer;     /**< Buffer for context */
+       odp_ipsec_ctx_t *next;   /**< Next context in event */
+
+       uint8_t  ip_tos;         /**< Saved IP TOS value */
+       uint16_t ip_frag_offset; /**< Saved IP flags value */
+       uint8_t  ip_ttl;         /**< Saved IP TTL value */
+       int      hdr_len;        /**< Length of IPsec headers */
+       int      trl_len;        /**< Length of IPsec trailers */
+       uint16_t tun_hdr_offset; /**< Offset of tunnel header from
+                                     buffer start */
+       uint16_t ah_offset;      /**< Offset of AH header from buffer start */
+       uint16_t esp_offset;     /**< Offset of ESP header from buffer start */
+
+       odp_ipsecproc_t postprocess;
+       odp_ipsec_sa_t sa;
+       odp_crypto_op_result_t crypto;
+       odp_ipsec_op_status_t status;
+
+       /* Input only */
+       uint32_t src_ip;         /**< SA source IP address */
+       uint32_t dst_ip;         /**< SA dest IP address */
+
+       /* Output only */
+       uint32_t *ah_seq;               /**< AH sequence number location */
+       uint32_t *esp_seq;              /**< ESP sequence number location */
+       uint16_t *tun_hdr_id;           /**< Tunnel header ID > */
+};
+
+typedef struct {
+       /* common buffer header */
+       odp_buffer_hdr_t buf_hdr;
+       odp_ipsec_ctx_t *ctx;
+} odp_ipsec_op_result_event_hdr_t;
+
+#define SHM_CTX_POOL_BUF_COUNT 1024
+
+static odp_pool_t odp_odp_ipsec_ctx_pool = ODP_POOL_INVALID;
+static odp_pool_t odp_ipsec_op_result_pool = ODP_POOL_INVALID;
+
+static inline ipsec_sa_t *ipsec_sa_entry(uint32_t ipsec_sa_idx)
+{
+       return &ipsec_sa_tbl->ipsec_sa[ipsec_sa_idx];
+}
+
+static inline ipsec_sa_t *ipsec_sa_entry_from_hdl(odp_ipsec_sa_t ipsec_sa_hdl)
+{
+       return ipsec_sa_entry(_odp_typeval(ipsec_sa_hdl));
+}
+
+static inline odp_ipsec_sa_t ipsec_sa_index_to_handle(uint32_t ipsec_sa_idx)
+{
+       return _odp_cast_scalar(odp_ipsec_sa_t, ipsec_sa_idx);
+}
+
+int odp_ipsec_init_global(void)
+{
+       uint32_t i;
+       odp_shm_t shm;
+       odp_pool_param_t params;
+
+       /* Create context buffer pool */
+       params.buf.size  = sizeof(odp_ipsec_ctx_t);
+       params.buf.align = 0;
+       params.buf.num   = SHM_CTX_POOL_BUF_COUNT;
+       params.type      = ODP_POOL_BUFFER;
+
+       odp_odp_ipsec_ctx_pool = odp_pool_create("odp_odp_ipsec_ctx_pool", 
&params);
+       if (ODP_POOL_INVALID == odp_odp_ipsec_ctx_pool) {
+               ODP_ERR("Error: context pool create failed.\n");
+               return -1;
+       }
+
+       params.buf.size  = sizeof(odp_ipsec_op_result_event_hdr_t);
+       params.buf.align = 0;
+       params.buf.num   = SHM_CTX_POOL_BUF_COUNT;
+       params.type      = ODP_POOL_BUFFER;
+
+       odp_ipsec_op_result_pool = odp_pool_create("odp_ipsec_op_result_pool", 
&params);
+       if (ODP_POOL_INVALID == odp_ipsec_op_result_pool) {
+               ODP_ERR("Error: result pool create failed.\n");
+               odp_pool_destroy(odp_odp_ipsec_ctx_pool);
+               return -1;
+       }
+
+       shm = odp_shm_reserve("_odp_ipsec_sa_table",
+                             sizeof(ipsec_sa_table_t),
+                             ODP_CACHE_LINE_SIZE, 0);
+
+       ipsec_sa_tbl = odp_shm_addr(shm);
+       if (ipsec_sa_tbl == NULL) {
+               odp_pool_destroy(odp_ipsec_op_result_pool);
+               odp_pool_destroy(odp_odp_ipsec_ctx_pool);
+               return -1;
+       }
+
+       memset(ipsec_sa_tbl, 0, sizeof(ipsec_sa_table_t));
+       ipsec_sa_tbl->shm = shm;
+
+       for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) {
+               ipsec_sa_t *ipsec_sa = ipsec_sa_entry(i);
+
+               odp_ticketlock_init(&ipsec_sa->lock);
+               ipsec_sa->ipsec_sa_hdl = ipsec_sa_index_to_handle(i);
+               ipsec_sa->ipsec_sa_idx = i;
+       }
+
+       return 0;
+}
+
+int odp_ipsec_term_global(void)
+{
+       int i;
+       ipsec_sa_t *ipsec_sa;
+       int ret = 0;
+       int rc = 0;
+
+       ret = odp_pool_destroy(odp_odp_ipsec_ctx_pool);
+       if (ret < 0) {
+               ODP_ERR("ctx pool destroy failed");
+               rc = -1;
+       }
+
+       for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) {
+               ipsec_sa = ipsec_sa_entry(i);
+
+               odp_ticketlock_lock(&ipsec_sa->lock);
+               if (ipsec_sa->reserved) {
+                       ODP_ERR("Not destroyed ipsec_sa: %u\n", 
ipsec_sa->ipsec_sa_idx);
+                       rc = -1;
+               }
+               ipsec_sa->reserved = 1;
+               odp_ticketlock_unlock(&ipsec_sa->lock);
+       }
+
+       ret = odp_shm_free(ipsec_sa_tbl->shm);
+       if (ret < 0) {
+               ODP_ERR("shm free failed");
+               rc = -1;
+       }
+
+       return rc;
+}
+
+static ipsec_sa_t *reserve_ipsec_sa(void)
+{
+       int i;
+       ipsec_sa_t *ipsec_sa;
+
+       for (i = 0; i < ODP_CONFIG_IPSEC_SAS; i++) {
+               ipsec_sa = ipsec_sa_entry(i);
+
+               odp_ticketlock_lock(&ipsec_sa->lock);
+               if (ipsec_sa->reserved == 0) {
+                       ipsec_sa->reserved = 1;
+                       odp_ticketlock_unlock(&ipsec_sa->lock);
+
+                       return ipsec_sa;
+               }
+               odp_ticketlock_unlock(&ipsec_sa->lock);
+       }
+
+       return NULL;
+}
 
 int odp_ipsec_capability(odp_ipsec_capability_t *capa)
 {
+       int rc;
+       odp_crypto_capability_t crypto_capa;
+
        memset(capa, 0, sizeof(odp_ipsec_capability_t));
 
+       rc = odp_crypto_capability(&crypto_capa);
+       if (rc < 0)
+               return rc;
+
+       capa->max_num_sa = ODP_CONFIG_IPSEC_SAS;
+       capa->op_mode_sync = 2;
+       capa->ciphers = crypto_capa.ciphers;
+       capa->auths = crypto_capa.auths;
+
        return 0;
 }
 
 int odp_ipsec_cipher_capability(odp_cipher_alg_t cipher,
                                odp_crypto_cipher_capability_t capa[], int num)
 {
-       (void)cipher;
-       (void)capa;
-       (void)num;
-
-       return -1;
+       return odp_crypto_cipher_capability(cipher, capa, num);
 }
 
 int odp_ipsec_auth_capability(odp_auth_alg_t auth,
                              odp_crypto_auth_capability_t capa[], int num)
 {
-       (void)auth;
-       (void)capa;
-       (void)num;
-
-       return -1;
+       return odp_crypto_auth_capability(auth, capa, num);
 }
 
 void odp_ipsec_config_init(odp_ipsec_config_t *config)
 {
        memset(config, 0, sizeof(odp_ipsec_config_t));
+       config->inbound_mode = ODP_IPSEC_OP_MODE_SYNC;
+       config->outbound_mode = ODP_IPSEC_OP_MODE_SYNC;
+       config->max_num_sa = ODP_CONFIG_IPSEC_SAS;
+       config->inbound.default_queue = ODP_QUEUE_INVALID;
+       config->inbound.lookup.min_spi = 0;
+       config->inbound.lookup.max_spi = UINT32_MAX;
+       config->outbound.default_queue = ODP_QUEUE_INVALID;
 }
 
+static odp_ipsec_config_t ipsec_config;
+
 int odp_ipsec_config(const odp_ipsec_config_t *config)
 {
-       (void)config;
+       /* FIXME: unsupported for now */
+       if (ODP_IPSEC_OP_MODE_INLINE == config->outbound_mode)
+               return -1;
 
-       return -1;
+       /* FIXME: unsupported for now */
+       if (ODP_IPSEC_OP_MODE_INLINE == config->inbound_mode)
+               return -1;
+
+       ipsec_config = *config;
+
+       return 0;
 }
 
 void odp_ipsec_sa_param_init(odp_ipsec_sa_param_t *param)
 {
        memset(param, 0, sizeof(odp_ipsec_sa_param_t));
+       param->dest_queue = ODP_QUEUE_INVALID;
 }
 
 odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param)
 {
-       (void)param;
+       ipsec_sa_t *ipsec_sa;
+       odp_crypto_session_param_t crypto_param;
+       odp_crypto_ses_create_err_t ses_create_rc;
+
+       ipsec_sa = reserve_ipsec_sa();
+       if (NULL == ipsec_sa) {
+               ODP_ERR("No more free SA\n");
+               return ODP_IPSEC_SA_INVALID;
+       }
+
+#if 1
+       ipsec_sa->in_place = false;
+#else
+       ipsec_sa->in_place = true;
+#endif
+       ipsec_sa->spi = param->spi;
+       ipsec_sa->seq = param->seq;
+       ipsec_sa->context = param->context;
+       ipsec_sa->queue = param->dest_queue;
+
+       odp_crypto_session_param_init(&crypto_param);
+
+       /* Setup parameters and call crypto library to create session */
+       crypto_param.op = (ODP_IPSEC_DIR_INBOUND == param->dir) ?
+                       ODP_CRYPTO_OP_DECODE :
+                       ODP_CRYPTO_OP_ENCODE;
+       crypto_param.auth_cipher_text = 1;
+
+       // FIXME: is it possible to use ASYNC crypto with ASYNC IPsec?
+       crypto_param.pref_mode   = ODP_CRYPTO_SYNC;
+       crypto_param.compl_queue = ODP_QUEUE_INVALID;
+       crypto_param.output_pool = ODP_POOL_INVALID;
+
+       crypto_param.cipher_alg = param->crypto.cipher_alg;
+       crypto_param.cipher_key = param->crypto.cipher_key;
+       crypto_param.auth_alg = param->crypto.auth_alg;
+       crypto_param.auth_key = param->crypto.auth_key;
+
+       switch (crypto_param.auth_alg) {
+       case ODP_AUTH_ALG_NULL:
+               ipsec_sa->ah_icv_len = 0;
+               break;
+       case ODP_AUTH_ALG_MD5_HMAC:
+       case ODP_AUTH_ALG_MD5_96:
+               ipsec_sa->ah_icv_len = 12;
+               break;
+       case ODP_AUTH_ALG_SHA1_HMAC:
+               ipsec_sa->ah_icv_len = 12;
+               break;
+       case ODP_AUTH_ALG_SHA256_HMAC:
+       case ODP_AUTH_ALG_SHA256_128:
+               ipsec_sa->ah_icv_len = 16;
+               break;
+       case ODP_AUTH_ALG_SHA512_HMAC:
+               ipsec_sa->ah_icv_len = 32;
+               break;
+       default:
+               return ODP_IPSEC_SA_INVALID;
+       }
+
+       switch (crypto_param.cipher_alg) {
+       case ODP_CIPHER_ALG_NULL:
+               ipsec_sa->esp_iv_len = 0;
+               ipsec_sa->esp_block_len = 0;
+               break;
+       case ODP_CIPHER_ALG_DES:
+       case ODP_CIPHER_ALG_3DES_CBC:
+               ipsec_sa->esp_iv_len = 8;
+               ipsec_sa->esp_block_len = 8;
+               break;
+       case ODP_CIPHER_ALG_AES_CBC:
+       case ODP_CIPHER_ALG_AES128_CBC:
+       case ODP_CIPHER_ALG_AES_GCM:
+       case ODP_CIPHER_ALG_AES128_GCM:
+               ipsec_sa->esp_iv_len = 16;
+               ipsec_sa->esp_block_len = 16;
+               break;
+       }
+
+       /* Generate an IV */
+       if (ipsec_sa->esp_iv_len) {
+               crypto_param.iv.data = ipsec_sa->iv;
+               crypto_param.iv.length = odp_random_data(crypto_param.iv.data, 
ipsec_sa->esp_iv_len, 1);
+               if (crypto_param.iv.length != ipsec_sa->esp_iv_len)
+                       goto error;
+       }
+
+       if (odp_crypto_session_create(&crypto_param, &ipsec_sa->session, 
&ses_create_rc))
+               goto error;
+       if (ODP_CRYPTO_SES_CREATE_ERR_NONE != ses_create_rc)
+               goto error;
+
+       return ipsec_sa->ipsec_sa_hdl;
+
+error:
+       odp_ticketlock_lock(&ipsec_sa->lock);
+       ipsec_sa->reserved = 1;
+       odp_ticketlock_unlock(&ipsec_sa->lock);
 
        return ODP_IPSEC_SA_INVALID;
 }
@@ -68,41 +404,790 @@ int odp_ipsec_sa_disable(odp_ipsec_sa_t sa)
 
 int odp_ipsec_sa_destroy(odp_ipsec_sa_t sa)
 {
-       (void)sa;
+       ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+       int rc = 0;
 
-       return -1;
+       odp_ticketlock_lock(&ipsec_sa->lock);
+       if (ipsec_sa->reserved) {
+               ODP_ERR("Destroying unallocated ipsec_sa: %u\n", 
ipsec_sa->ipsec_sa_idx);
+               rc = -1;
+       } else {
+               if (odp_crypto_session_destroy(ipsec_sa->session) < 0) {
+                       ODP_ERR("Error destroying crypto session for ipsec_sa: 
%u\n", ipsec_sa->ipsec_sa_idx);
+                       rc = -1;
+               }
+
+               ipsec_sa->reserved = 1;
+       }
+       odp_ticketlock_unlock(&ipsec_sa->lock);
+
+       return rc;
+}
+
+#define ipv4_data_p(ip) ((uint8_t *)((_odp_ipv4hdr_t *)ip + 1))
+#define ipv4_data_len(ip) (odp_be_to_cpu_16(ip->tot_len) - 
sizeof(_odp_ipv4hdr_t))
+static inline
+void ipv4_adjust_len(_odp_ipv4hdr_t *ip, int adj)
+{
+       ip->tot_len = odp_cpu_to_be_16(odp_be_to_cpu_16(ip->tot_len) + adj);
+}
+
+/**
+ * Verify crypto operation completed successfully
+ *
+ * @param status  Pointer to cryto completion structure
+ *
+ * @return true if all OK else false
+ */
+static inline
+odp_bool_t is_crypto_compl_status_ok(odp_crypto_compl_status_t *status)
+{
+       if (status->alg_err != ODP_CRYPTO_ALG_ERR_NONE)
+               return false;
+       if (status->hw_err != ODP_CRYPTO_HW_ERR_NONE)
+               return false;
+       return true;
+}
+
+/**
+ * Allocate per packet processing context and associate it with
+ * packet buffer
+ *
+ * @param pkt  Packet
+ *
+ * @return pointer to context area
+ */
+static
+odp_ipsec_ctx_t *odp_ipsec_alloc_pkt_ctx(void)
+{
+       odp_buffer_t ctx_buf = odp_buffer_alloc(odp_odp_ipsec_ctx_pool);
+       odp_ipsec_ctx_t *ctx;
+
+       if (odp_unlikely(ODP_BUFFER_INVALID == ctx_buf))
+               return NULL;
+
+       ctx = odp_buffer_addr(ctx_buf);
+       memset(ctx, 0, sizeof(*ctx));
+       ctx->buffer = ctx_buf;
+
+       return ctx;
+}
+
+/**
+ * Release per packet resources
+ *
+ * @param ctx  Packet context
+ */
+static
+void odp_ipsec_free_pkt_ctx(odp_ipsec_ctx_t *ctx)
+{
+       if (ODP_PACKET_INVALID != ctx->crypto.pkt)
+               odp_packet_free(ctx->crypto.pkt);
+
+       odp_buffer_free(ctx->buffer);
+}
+
+odp_ipsec_op_result_event_t odp_ipsec_op_result_event_from_event(odp_event_t 
ev)
+{
+       if (odp_unlikely(ODP_EVENT_INVALID == ev))
+               return ODP_IPSEC_OP_RESULT_EVENT_INVALID;
+
+       if (odp_event_type(ev) != ODP_EVENT_IPSEC_RESULT)
+               ODP_ABORT("Event not an IPsec result");
+
+       return (odp_ipsec_op_result_event_t)ev;
+}
+
+static odp_ipsec_op_result_event_hdr_t 
*ipsec_op_result_event_hdr_from_buf(odp_buffer_t buf)
+{
+       return (odp_ipsec_op_result_event_hdr_t *)(void *)buf_hdl_to_hdr(buf);
+}
+
+static odp_ipsec_op_result_event_hdr_t 
*ipsec_op_result_event_hdr(odp_ipsec_op_result_event_t res)
+{
+       odp_buffer_t buf = 
odp_buffer_from_event(odp_ipsec_op_result_event_to_event(res));
+
+       return ipsec_op_result_event_hdr_from_buf(buf);
+}
+
+odp_event_t odp_ipsec_op_result_event_to_event(odp_ipsec_op_result_event_t res)
+{
+       if (odp_unlikely(res == ODP_IPSEC_OP_RESULT_EVENT_INVALID))
+               return ODP_EVENT_INVALID;
+
+       return (odp_event_t)res;
+}
+
+static
+odp_ipsec_op_result_event_t odp_ipsec_op_result_event_alloc(void)
+{
+       odp_buffer_t buf = odp_buffer_alloc(odp_ipsec_op_result_pool);
+
+       if (odp_unlikely(buf == ODP_BUFFER_INVALID))
+               return ODP_IPSEC_OP_RESULT_EVENT_INVALID;
+
+       _odp_buffer_event_type_set(buf, ODP_EVENT_IPSEC_RESULT);
+
+       return odp_ipsec_op_result_event_from_event(odp_buffer_to_event(buf));
+}
+
+void odp_ipsec_op_result_event_free(odp_ipsec_op_result_event_t res)
+{
+       odp_event_t ev = odp_ipsec_op_result_event_to_event(res);
+       odp_ipsec_op_result_event_hdr_t *res_hdr;
+
+       res_hdr = ipsec_op_result_event_hdr(res);
+       while (NULL != res_hdr->ctx) {
+               odp_ipsec_ctx_t *ctx = res_hdr->ctx;
+
+               res_hdr->ctx = ctx->next;
+               odp_ipsec_free_pkt_ctx(ctx);
+       }
+
+       odp_buffer_free(odp_buffer_from_event(ev));
+}
+
+static
+void odp_ipsec_postprocess(odp_packet_t pkt, odp_ipsec_ctx_t *ctx)
+{
+       _odp_ipv4hdr_t *ip;
+       int hdr_len;
+       int trl_len = 0;
+
+       ip = (_odp_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+       hdr_len = ctx->hdr_len;
+
+       /*
+        * Finish auth
+        */
+       if (ctx->ah_offset) {
+               uint8_t *buf = odp_packet_data(pkt);
+               _odp_ahhdr_t *ah;
+
+               ah = (_odp_ahhdr_t *)(ctx->ah_offset + buf);
+               ip->proto = ah->next_header;
+       }
+
+       /*
+        * Finish cipher by finding ESP trailer and processing
+        *
+        * FIXME: ESP authentication ICV not supported
+        */
+       if (ctx->esp_offset) {
+               uint8_t *eop = (uint8_t *)(ip) + odp_be_to_cpu_16(ip->tot_len);
+               _odp_esptrl_t *esp_t = (_odp_esptrl_t *)(eop) - 1;
+
+               ip->proto = esp_t->next_header;
+               trl_len += esp_t->pad_len + sizeof(*esp_t);
+       }
+
+       /* We have a tunneled IPv4 packet */
+       if (ip->proto == _ODP_IPV4) {
+               odp_packet_pull_head(pkt, sizeof(*ip));
+       } else {
+               /* Finalize the IPv4 header */
+               ipv4_adjust_len(ip, -(hdr_len + trl_len));
+               ip->ttl = ctx->ip_ttl;
+               ip->tos = ctx->ip_tos;
+               ip->frag_offset = odp_cpu_to_be_16(ctx->ip_frag_offset);
+               ip->chksum = 0;
+               _odp_ipv4_csum_update(pkt);
+
+               /* Correct the packet length and move payload into position */
+               memmove(((uint8_t *)ip) + hdr_len, ip, sizeof(*ip));
+       }
+
+       odp_packet_pull_head(pkt, hdr_len);
+       odp_packet_pull_tail(pkt, trl_len);
 }
 
+static
+int odp_ipsec_finish(odp_ipsec_ctx_t *ctx,
+                    odp_ipsec_packet_result_t *res,
+                    odp_packet_t *pkt)
+{
+       odp_crypto_op_result_t *result = &ctx->crypto;
+
+       res->status = ctx->status;
+
+       /* Check crypto result */
+       if (!result->ok) {
+               if (!is_crypto_compl_status_ok(&result->cipher_status)) {
+                       res->status.error.alg = 1;
+                       return -1;
+               }
+               if (!is_crypto_compl_status_ok(&result->auth_status)) {
+                       res->status.error.auth = 1;
+                       return -1;
+               }
+       }
+
+       if (ctx->postprocess) {
+               ctx->postprocess(result->pkt, ctx);
+               ctx->postprocess = NULL;
+       }
+
+       *pkt = result->pkt;
+       result->pkt = ODP_PACKET_INVALID;
+
+       res->sa = ctx->sa;
+
+       return 1;
+}
+
+static
+int odp_ipsec_in_single(odp_packet_t pkt,
+                       odp_ipsec_sa_t sa,
+                       odp_ipsec_ctx_t *ctx)
+{
+       ipsec_sa_t *ipsec_sa;
+       uint8_t *buf = odp_packet_data(pkt);
+       _odp_ipv4hdr_t *ip = (_odp_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+       int hdr_len;
+       _odp_ahhdr_t *ah = NULL;
+       _odp_esphdr_t *esp = NULL;
+       odp_crypto_op_param_t params;
+       odp_bool_t posted = 0;
+       uint8_t *in = ipv4_data_p(ip);
+       int rc;
+
+       ctx->status.all_error = 0;
+       ctx->status.all_flag = 0;
+
+       if (ODP_IPSEC_SA_INVALID == sa) {
+               ctx->status.error.sa_lookup = 1;
+               return -1;
+       }
+
+       ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+       if (odp_unlikely(NULL == ipsec_sa)) {
+               ctx->status.error.alg = 1;
+               return -1;
+       }
+
+       /* Check IP header for IPSec protocols and look it up */
+       if (_ODP_IPPROTO_AH == ip->proto) {
+               ah = (_odp_ahhdr_t *)in;
+               in += ((ah)->ah_len + 2) * 4;
+       } else if (_ODP_IPPROTO_ESP == ip->proto) {
+               esp = (_odp_esphdr_t *)in;
+               in += sizeof(_odp_esphdr_t);
+       } else {
+               ctx->status.error.proto = 1;
+               return -1;
+       }
+
+       hdr_len = in - (ipv4_data_p(ip));
+
+       /* Account for configured ESP IV length in packet */
+       hdr_len += ipsec_sa->esp_iv_len;
+
+       /* Initialize parameters block */
+       memset(&params, 0, sizeof(params));
+       params.session = ipsec_sa->session;
+       params.pkt = pkt;
+       params.ctx = ctx;
+
+       /*Save everything to context */
+       ctx->ip_tos = ip->tos;
+       ctx->ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
+       ctx->ip_ttl = ip->ttl;
+       ctx->ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
+       ctx->esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
+       ctx->hdr_len = hdr_len;
+       ctx->trl_len = 0;
+       //ctx->src_ip = ipsec_sa->src_ip;
+       //ctx->dst_ip = ipsec_sa->dst_ip;
+
+       ctx->postprocess = odp_ipsec_postprocess;
+       ctx->sa = sa;
+
+       /* If authenticating, zero the mutable fields build the request */
+       if (ah) {
+               ip->chksum = 0;
+               ip->tos = 0;
+               ip->frag_offset = 0;
+               ip->ttl = 0;
+
+               params.auth_range.offset = ((uint8_t *)ip) - buf;
+               params.auth_range.length = odp_be_to_cpu_16(ip->tot_len);
+               params.hash_result_offset = ah->icv - buf;
+       }
+
+       /* If deciphering build request */
+       if (esp) {
+               params.cipher_range.offset = ipv4_data_p(ip) + hdr_len - buf;
+               params.cipher_range.length = ipv4_data_len(ip) - hdr_len;
+               params.override_iv_ptr = esp->iv;
+       }
+
+       /* Create new packet after all length extensions */
+       params.out_pkt = ipsec_sa->in_place ? pkt :
+                       odp_packet_alloc(odp_packet_pool(pkt),
+                                        odp_packet_len(pkt));
+       odp_packet_user_ptr_set(params.out_pkt,
+                               odp_packet_user_ptr(params.pkt));
+
+       rc = odp_crypto_operation(&params, &posted, &ctx->crypto);
+       if (rc < 0) {
+               ODP_DBG("Crypto failed\n");
+               ctx->status.error.alg = 1;
+
+               return rc;
+       }
+
+       ODP_ASSERT(!posted);
+
+       return 0;
+}
+
+/** Helper for calculating encode length using data length and block size */
+#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b)
+
+static
+int odp_ipsec_out_single(odp_packet_t pkt,
+                        odp_ipsec_sa_t sa,
+                        odp_ipsec_ctx_t *ctx)
+{
+       ipsec_sa_t *ipsec_sa;
+       uint8_t *buf = odp_packet_data(pkt);
+       _odp_ipv4hdr_t *ip = (_odp_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+       uint16_t ip_data_len = ipv4_data_len(ip);
+       uint8_t *ip_data = ipv4_data_p(ip);
+       odp_crypto_op_param_t params;
+       odp_bool_t posted = 0;
+       int hdr_len = 0;
+       int trl_len = 0;
+       _odp_ahhdr_t *ah = NULL;
+       _odp_esphdr_t *esp = NULL;
+       int rc;
+
+       ctx->status.all_error = 0;
+       ctx->status.all_flag = 0;
+
+       if (ODP_IPSEC_SA_INVALID == sa) {
+               ctx->status.error.sa_lookup = 1;
+               return -1;
+       }
+
+       ipsec_sa = ipsec_sa_entry_from_hdl(sa);
+       if (odp_unlikely(NULL == ipsec_sa)) {
+               ctx->status.error.alg = 1;
+               return -1;
+       }
+
+       /* Initialize parameters block */
+       memset(&params, 0, sizeof(params));
+       params.session = ipsec_sa->session;
+       params.pkt = pkt;
+       params.ctx = ctx;
+
+       /* Save IPv4 stuff */
+       ctx->ip_tos = ip->tos;
+       ctx->ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
+       ctx->ip_ttl = ip->ttl;
+
+       ctx->postprocess = NULL;
+       ctx->sa = sa;
+
+#if 0
+       if (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL) {
+               hdr_len += sizeof(_odp_ipv4hdr_t);
+               ip_data = (uint8_t *)ip;
+               ip_data_len += sizeof(_odp_ipv4hdr_t);
+       }
+#endif
+       /* Compute ah and esp, determine length of headers, move the data */
+       if (ipsec_sa->ah_icv_len) {
+               ah = (_odp_ahhdr_t *)(ip_data + hdr_len);
+               hdr_len += sizeof(_odp_ahhdr_t);
+               hdr_len += ipsec_sa->ah_icv_len;
+       }
+       if (ipsec_sa->esp_iv_len) {
+               esp = (_odp_esphdr_t *)(ip_data + hdr_len);
+               hdr_len += sizeof(_odp_esphdr_t);
+               hdr_len += ipsec_sa->esp_iv_len;
+       }
+       memmove(ip_data + hdr_len, ip_data, ip_data_len);
+       ip_data += hdr_len;
+
+       /* update outer header in tunnel mode */
+#if 0
+       if (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL) {
+               /* tunnel addresses */
+               ip->src_addr = odp_cpu_to_be_32(ipsec_sa->tun_src_ip);
+               ip->dst_addr = odp_cpu_to_be_32(ipsec_sa->tun_dst_ip);
+       }
+#endif
+
+       /* For cipher, compute encrypt length, build headers and request */
+       if (esp) {
+               uint32_t encrypt_len;
+               _odp_esptrl_t *esp_t;
+
+               encrypt_len = ESP_ENCODE_LEN(ip_data_len + sizeof(*esp_t),
+                                            ipsec_sa->esp_block_len);
+               trl_len = encrypt_len - ip_data_len;
+
+               esp->spi = odp_cpu_to_be_32(ipsec_sa->spi);
+               memcpy(esp + 1, ipsec_sa->iv, ipsec_sa->esp_iv_len);
+
+               esp_t = (_odp_esptrl_t *)(ip_data + encrypt_len) - 1;
+               esp_t->pad_len     = trl_len - sizeof(*esp_t);
+#if 0
+               if (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL)
+                       esp_t->next_header = _ODP_IPV4;
+               else
+#endif
+                       esp_t->next_header = ip->proto;
+               ip->proto = _ODP_IPPROTO_ESP;
+
+               params.cipher_range.offset = ip_data - buf;
+               params.cipher_range.length = encrypt_len;
+       }
+
+       /* For authentication, build header clear mutables and build request */
+       if (ah) {
+               memset(ah, 0, sizeof(*ah) + ipsec_sa->ah_icv_len);
+               ah->spi = odp_cpu_to_be_32(ipsec_sa->spi);
+               ah->ah_len = 1 + (ipsec_sa->ah_icv_len / 4);
+#if 0
+               if (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL && !esp)
+                       ah->next_header = _ODP_IPV4;
+               else
+#endif
+                       ah->next_header = ip->proto;
+               ip->proto = _ODP_IPPROTO_AH;
+
+               ip->chksum = 0;
+               ip->tos = 0;
+               ip->frag_offset = 0;
+               ip->ttl = 0;
+
+               params.auth_range.offset = ((uint8_t *)ip) - buf;
+               params.auth_range.length =
+                       odp_be_to_cpu_16(ip->tot_len) + (hdr_len + trl_len);
+               params.hash_result_offset = ah->icv - buf;
+       }
+
+       /* Set IPv4 length before authentication */
+       if (!odp_packet_push_tail(pkt, hdr_len + trl_len)) {
+               ctx->status.error.alg = 1;
+               return -1;
+       }
+
+       ipv4_adjust_len(ip, hdr_len + trl_len);
+
+       /* Save remaining context */
+       ctx->hdr_len = hdr_len;
+       ctx->trl_len = trl_len;
+       ctx->ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
+       ctx->esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
+#if 0
+       ctx->tun_hdr_offset = (ipsec_sa->mode == IPSEC_SA_MODE_TUNNEL) ?
+                                      ((uint8_t *)ip - buf) : 0;
+#else
+       ctx->tun_hdr_offset = 0;
+#endif
+       ctx->ah_seq = &ipsec_sa->seq;
+       ctx->esp_seq = &ipsec_sa->seq;
+       //ctx->tun_hdr_id = &ipsec_sa->tun_hdr_id;
+
+       // FIXME: locking !!!!!
+       if (ctx->ah_offset) {
+               _odp_ahhdr_t *ah;
+
+               ah = (_odp_ahhdr_t *)(ctx->ah_offset + buf);
+               ah->seq_no = odp_cpu_to_be_32((*ctx->ah_seq)++);
+       }
+       if (ctx->esp_offset) {
+               _odp_esphdr_t *esp;
+
+               esp = (_odp_esphdr_t *)(ctx->esp_offset + buf);
+               esp->seq_no = odp_cpu_to_be_32((*ctx->esp_seq)++);
+       }
+       if (ctx->tun_hdr_offset) {
+               _odp_ipv4hdr_t *ip;
+               int ret;
+
+               ip = (_odp_ipv4hdr_t *)(ctx->tun_hdr_offset + buf);
+               ip->id = odp_cpu_to_be_16((*ctx->tun_hdr_id)++);
+               if (!ip->id) {
+                       /* re-init tunnel hdr id */
+                       ret = odp_random_data((uint8_t *)ctx->tun_hdr_id,
+                                             sizeof(*ctx->tun_hdr_id),
+                                             1);
+                       if (ret != sizeof(*ctx->tun_hdr_id))
+                               abort();
+               }
+       }
+
+       /* Create new packet after all length extensions */
+       params.out_pkt = ipsec_sa->in_place ? pkt :
+                       odp_packet_alloc(odp_packet_pool(pkt),
+                                        odp_packet_len(pkt));
+       odp_packet_user_ptr_set(params.out_pkt, 
odp_packet_user_ptr(params.pkt));
+
+       rc = odp_crypto_operation(&params, &posted, &ctx->crypto);
+       if (rc < 0) {
+               ODP_DBG("Crypto failed\n");
+               ctx->status.error.alg = 1;
+
+               return rc;
+       }
+
+       ODP_ASSERT(!posted);
+
+       return 0;
+}
+
+#if 0
+static odp_ipsec_op_opt_t default_opt = {
+       .mode = ODP_IPSEC_FRAG_DISABLED,
+};
+#endif
+
 int odp_ipsec_in(const odp_ipsec_op_param_t *input,
                 odp_ipsec_op_result_t *output)
 {
-       (void)input;
-       (void)output;
+       unsigned in_pkt = 0;
+       unsigned out_pkt = 0;
+       unsigned sa_idx = 0;
+       unsigned opt_idx = 0;
+       unsigned sa_inc = (input->num_sa > 1) ? 1 : 0;
+       unsigned opt_inc = (input->num_opt > 1) ? 1 : 0;
 
-       return -1;
+       while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {
+               odp_ipsec_sa_t sa;
+               odp_ipsec_packet_result_t *res = &output->res[out_pkt];
+               odp_ipsec_ctx_t ctx;
+               int ret;
+
+               if (0 == input->num_sa)
+                       sa = ODP_IPSEC_SA_INVALID;
+               else
+                       sa = input->sa[sa_idx];
+
+#if 0
+               odp_ipsec_op_opt_t *opt;
+
+               if (0 == input->num_opt)
+                       opt = &default_opt;
+               else
+                       opt = &input->opt[opt_idx];
+#endif
+
+               res->num_out = 1;
+               output->pkt[out_pkt] = input->pkt[in_pkt];
+
+               if (ODP_IPSEC_SA_INVALID == sa) {
+                       res->status.error.sa_lookup = 1;
+                       goto out;
+               }
+
+               if (odp_ipsec_in_single(input->pkt[in_pkt], sa, &ctx) < 0) {
+                       res->status.error.alg = 1;
+                       goto out;
+               }
+
+               ret = odp_ipsec_finish(&ctx, res, &output->pkt[out_pkt]);
+               if (ret < 0)
+                       res->status.error.alg = 1;
+
+out:
+               in_pkt++;
+               out_pkt++;
+               sa_idx += sa_inc;
+               opt_idx += opt_inc;
+       }
+
+       return in_pkt;
 }
 
 int odp_ipsec_out(const odp_ipsec_op_param_t *input,
-                 odp_ipsec_op_result_t *output)
+                odp_ipsec_op_result_t *output)
 {
-       (void)input;
-       (void)output;
+       unsigned in_pkt = 0;
+       unsigned out_pkt = 0;
+       unsigned sa_idx = 0;
+       unsigned opt_idx = 0;
+       unsigned sa_inc = (input->num_sa > 1) ? 1 : 0;
+       unsigned opt_inc = (input->num_opt > 1) ? 1 : 0;
 
-       return -1;
+       while (in_pkt < input->num_pkt && out_pkt < output->num_pkt) {
+               odp_ipsec_sa_t sa;
+               odp_ipsec_packet_result_t *res = &output->res[out_pkt];
+               odp_ipsec_ctx_t ctx;
+               int ret;
+
+               if (0 == input->num_sa)
+                       sa = ODP_IPSEC_SA_INVALID;
+               else
+                       sa = input->sa[sa_idx];
+
+#if 0
+               odp_ipsec_op_opt_t *opt;
+
+               if (0 == input->num_opt)
+                       opt = &default_opt;
+               else
+                       opt = &input->opt[opt_idx];
+#endif
+
+               res->num_out = 1;
+               output->pkt[out_pkt] = input->pkt[in_pkt];
+
+               if (odp_ipsec_out_single(input->pkt[in_pkt], sa, &ctx) < 0) {
+                       res->status.error.alg = 1;
+                       goto out;
+               }
+
+               ret = odp_ipsec_finish(&ctx, res, &output->pkt[out_pkt]);
+               if (ret < 0)
+                       res->status.error.alg = 1;
+
+out:
+               in_pkt++;
+               out_pkt++;
+               sa_idx += sa_inc;
+               opt_idx += opt_inc;
+       }
+
+       return in_pkt;
 }
 
 int odp_ipsec_in_enq(const odp_ipsec_op_param_t *input)
 {
-       (void)input;
+       unsigned in_pkt = 0;
+       unsigned sa_idx = 0;
+       unsigned opt_idx = 0;
+       unsigned sa_inc = (input->num_sa > 1) ? 1 : 0;
+       unsigned opt_inc = (input->num_opt > 1) ? 1 : 0;
 
-       return -1;
+       while (in_pkt < input->num_pkt) {
+               odp_ipsec_op_result_event_t ipsec_ev;
+               odp_event_t ev;
+               odp_ipsec_op_result_event_hdr_t *res_hdr;
+               odp_ipsec_ctx_t *ctx;
+               odp_ipsec_sa_t sa;
+               odp_queue_t queue;
+
+               ipsec_ev = odp_ipsec_op_result_event_alloc();
+               if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev)
+                       break;
+
+               ev = odp_ipsec_op_result_event_to_event(ipsec_ev);
+
+               res_hdr = ipsec_op_result_event_hdr(ipsec_ev);
+
+               ctx = odp_ipsec_alloc_pkt_ctx();
+               if (NULL == ctx) {
+                       odp_event_free(ev);
+                       break;
+               }
+
+               res_hdr->ctx = ctx;
+
+               if (0 == input->num_sa)
+                       sa = ODP_IPSEC_SA_INVALID;
+               else
+                       sa = input->sa[sa_idx];
+
+#if 0
+               odp_ipsec_op_opt_t *opt;
+
+               if (0 == input->num_opt)
+                       opt = &default_opt;
+               else
+                       opt = &input->opt[opt_idx];
+#endif
+
+               if (odp_ipsec_in_single(input->pkt[in_pkt], sa, ctx) < 0)
+                       ctx->status.error.alg = 1;
+
+               in_pkt++;
+               sa_idx += sa_inc;
+               opt_idx += opt_inc;
+
+               if (ODP_IPSEC_SA_INVALID == sa)
+                       queue = ipsec_config.inbound.default_queue;
+               else
+                       queue = ipsec_sa_entry_from_hdl(sa)->queue;
+
+               if (odp_queue_enq(queue, ev)) {
+                       odp_event_free(ev);
+                       return -1;
+               }
+       }
+
+       return in_pkt;
 }
 
 int odp_ipsec_out_enq(const odp_ipsec_op_param_t *input)
 {
-       (void)input;
+       unsigned in_pkt = 0;
+       unsigned sa_idx = 0;
+       unsigned opt_idx = 0;
+       unsigned sa_inc = (input->num_sa > 1) ? 1 : 0;
+       unsigned opt_inc = (input->num_opt > 1) ? 1 : 0;
 
-       return -1;
+       while (in_pkt < input->num_pkt) {
+               odp_ipsec_op_result_event_t ipsec_ev;
+               odp_event_t ev;
+               odp_ipsec_op_result_event_hdr_t *res_hdr;
+               odp_ipsec_ctx_t *ctx;
+               odp_ipsec_sa_t sa;
+               odp_queue_t queue;
+
+               ipsec_ev = odp_ipsec_op_result_event_alloc();
+               if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev)
+                       break;
+
+               ev = odp_ipsec_op_result_event_to_event(ipsec_ev);
+
+               res_hdr = ipsec_op_result_event_hdr(ipsec_ev);
+
+               ctx = odp_ipsec_alloc_pkt_ctx();
+               if (NULL == ctx) {
+                       odp_event_free(ev);
+                       break;
+               }
+
+               res_hdr->ctx = ctx;
+
+               if (0 == input->num_sa)
+                       sa = ODP_IPSEC_SA_INVALID;
+               else
+                       sa = input->sa[sa_idx];
+
+#if 0
+               odp_ipsec_op_opt_t *opt;
+
+               if (0 == input->num_opt)
+                       opt = &default_opt;
+               else
+                       opt = &input->opt[opt_idx];
+#endif
+
+               if (odp_ipsec_out_single(input->pkt[in_pkt], sa, ctx) < 0)
+                       ctx->status.error.alg = 1;
+
+               in_pkt++;
+               sa_idx += sa_inc;
+               opt_idx += opt_inc;
+
+               if (ODP_IPSEC_SA_INVALID == sa)
+                       queue = ipsec_config.outbound.default_queue;
+               else
+                       queue = ipsec_sa_entry_from_hdl(sa)->queue;
+
+               if (odp_queue_enq(queue, ev)) {
+                       odp_event_free(ev);
+                       return -1;
+               }
+       }
+
+       return in_pkt;
 }
 
 int odp_ipsec_out_inline(const odp_ipsec_op_param_t *op_param,
@@ -117,9 +1202,36 @@ int odp_ipsec_out_inline(const odp_ipsec_op_param_t 
*op_param,
 int odp_ipsec_result(odp_ipsec_op_result_t *result, odp_event_t event)
 {
        (void)result;
-       (void)event;
 
-       return -1;
+       odp_ipsec_op_result_event_t ipsec_ev;
+       odp_ipsec_op_result_event_hdr_t *res_hdr;
+       unsigned out_pkt;
+       odp_ipsec_ctx_t *ctx;
+
+       ipsec_ev = odp_ipsec_op_result_event_from_event(event);
+       if (ODP_IPSEC_OP_RESULT_EVENT_INVALID == ipsec_ev)
+               return -1;
+
+       res_hdr = ipsec_op_result_event_hdr(ipsec_ev);
+
+       for (out_pkt = 0; out_pkt < result->num_pkt; out_pkt++) {
+               odp_ipsec_packet_result_t *res = &result->res[out_pkt];
+               odp_packet_t *pkt = &result->pkt[out_pkt];
+               odp_ipsec_ctx_t *ctx = res_hdr->ctx;
+               int ret;
+
+               res_hdr->ctx = ctx->next;
+               ret = odp_ipsec_finish(ctx, res, pkt);
+               if (ret < 0)
+                       res->status.error.alg = 1;
+
+               odp_ipsec_free_pkt_ctx(ctx);
+       }
+
+       for (ctx = res_hdr->ctx; NULL != ctx; ctx = ctx->next)
+               out_pkt++;
+
+       return out_pkt;
 }
 
 int odp_ipsec_status(odp_ipsec_status_t *status, odp_event_t event)
@@ -140,9 +1252,9 @@ int odp_ipsec_mtu_update(odp_ipsec_sa_t sa, uint32_t mtu)
 
 void *odp_ipsec_sa_context(odp_ipsec_sa_t sa)
 {
-       (void)sa;
+       ipsec_sa_t *ipsec_sa = ipsec_sa_entry_from_hdl(sa);
 
-       return NULL;
+       return ipsec_sa->context;
 }
 
 uint64_t odp_ipsec_sa_to_u64(odp_ipsec_sa_t sa)
-- 
2.11.0

Reply via email to