and add to TESTS to make example run with 'make check'

Maxim.

On 02/21/17 21:18, Bill Fischofer wrote:
> Also, it looks like example/ipsec_offload/Makefile.am is incomplete. It's
> missing the line:
> 
> bin_PROGRAMS = odp_ipsec_offload$(EXEEXT)
> 
> Without this nothing tries to compile. With this and the earlier m4 add
> everything configures properly but then make dies due to Petri's ipsec
> stubs not yet being integrated. I'll test again after they are against your
> v2 of this patch.
> 
> On Tue, Feb 21, 2017 at 11:42 AM, Bill Fischofer <bill.fischo...@linaro.org>
> wrote:
> 
>> You're missing an entry in example/m4/configure.m4 to process the
>> Makefile.am to generate the makefile for this new example. As a result
>> compile fails.
>>
>> On Tue, Feb 21, 2017 at 12:23 PM, Nikhil Agarwal <
>> nikhil.agar...@linaro.org> wrote:
>>
>>> Signed-off-by: Nikhil Agarwal <nikhil.agar...@linaro.org>
>>> ---
>>>  example/Makefile.am                              |   1 +
>>>  example/ipsec_offload/.gitignore                 |   1 +
>>>  example/ipsec_offload/Makefile.am                |  18 +
>>>  example/ipsec_offload/odp_ipsec_offload.c        | 871
>>> +++++++++++++++++++++++
>>>  example/ipsec_offload/odp_ipsec_offload_cache.c  | 148 ++++
>>>  example/ipsec_offload/odp_ipsec_offload_cache.h  |  78 ++
>>>  example/ipsec_offload/odp_ipsec_offload_fwd_db.c | 223 ++++++
>>>  example/ipsec_offload/odp_ipsec_offload_fwd_db.h | 198 ++++++
>>>  example/ipsec_offload/odp_ipsec_offload_misc.h   | 384 ++++++++++
>>>  example/ipsec_offload/odp_ipsec_offload_sa_db.c  | 361 ++++++++++
>>>  example/ipsec_offload/odp_ipsec_offload_sa_db.h  | 126 ++++
>>>  example/ipsec_offload/odp_ipsec_offload_sp_db.c  | 166 +++++
>>>  example/ipsec_offload/odp_ipsec_offload_sp_db.h  |  72 ++
>>>  example/ipsec_offload/run_left                   |  14 +
>>>  example/ipsec_offload/run_right                  |  14 +
>>>  15 files changed, 2675 insertions(+)
>>>  create mode 100644 example/ipsec_offload/.gitignore
>>>  create mode 100644 example/ipsec_offload/Makefile.am
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload.c
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload_cache.c
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload_cache.h
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload_fwd_db.c
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload_fwd_db.h
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload_misc.h
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload_sa_db.c
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload_sa_db.h
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload_sp_db.c
>>>  create mode 100644 example/ipsec_offload/odp_ipsec_offload_sp_db.h
>>>  create mode 100644 example/ipsec_offload/run_left
>>>  create mode 100644 example/ipsec_offload/run_right
>>>
>>> diff --git a/example/Makefile.am b/example/Makefile.am
>>> index dfc07b6..24b9e52 100644
>>> --- a/example/Makefile.am
>>> +++ b/example/Makefile.am
>>> @@ -2,6 +2,7 @@ SUBDIRS = classifier \
>>>           generator \
>>>           hello \
>>>           ipsec \
>>> +         ipsec_offload \
>>>           l2fwd_simple \
>>>           l3fwd \
>>>           packet \
>>> diff --git a/example/ipsec_offload/.gitignore
>>> b/example/ipsec_offload/.gitignore
>>> new file mode 100644
>>> index 0000000..2fc73aa
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/.gitignore
>>> @@ -0,0 +1 @@
>>> +odp_ipsec_offload
>>> diff --git a/example/ipsec_offload/Makefile.am
>>> b/example/ipsec_offload/Makefile.am
>>> new file mode 100644
>>> index 0000000..ec66030
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/Makefile.am
>>> @@ -0,0 +1,18 @@
>>> +include $(top_srcdir)/example/Makefile.inc
>>> +
>>> +odp_ipsec_offload_LDFLAGS = $(AM_LDFLAGS) -static
>>> +odp_ipsec_offload_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
>>> +
>>> +noinst_HEADERS = \
>>> +                 $(top_srcdir)/example/ipsec_o
>>> ffload/odp_ipsec_offload_cache.h \
>>> +                 $(top_srcdir)/example/ipsec_o
>>> ffload/odp_ipsec_offload_fwd_db.h \
>>> +                 $(top_srcdir)/example/ipsec_o
>>> ffload/odp_ipsec_offload_misc.h \
>>> +                 $(top_srcdir)/example/ipsec_o
>>> ffload/odp_ipsec_offload_sa_db.h \
>>> +                 $(top_srcdir)/example/ipsec_o
>>> ffload/odp_ipsec_offload_sp_db.h \
>>> +                 $(top_srcdir)/example/example_debug.h
>>> +
>>> +dist_odp_ipsec_offload_SOURCES = odp_ipsec_offload.c \
>>> +                        odp_ipsec_offload_sa_db.c \
>>> +                        odp_ipsec_offload_sp_db.c \
>>> +                        odp_ipsec_offload_fwd_db.c \
>>> +                        odp_ipsec_offload_cache.c
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload.c
>>> b/example/ipsec_offload/odp_ipsec_offload.c
>>> new file mode 100644
>>> index 0000000..4a494d0
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload.c
>>> @@ -0,0 +1,871 @@
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * Copyright (C) 2017 NXP
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +/**
>>> + * @file
>>> + *
>>> + * @example odp_ipsec_offload.c  ODP basic packet IO cross connect with
>>> IPsec
>>> + * test application
>>> + */
>>> +
>>> +#define _DEFAULT_SOURCE
>>> +/* enable strtok */
>>> +#define _POSIX_C_SOURCE 200112L
>>> +#include <stdlib.h>
>>> +#include <getopt.h>
>>> +#include <unistd.h>
>>> +
>>> +#include <example_debug.h>
>>> +
>>> +#include <odp_api.h>
>>> +#include <odp/helper/linux.h>
>>> +#include <odp/helper/eth.h>
>>> +#include <odp/helper/ip.h>
>>> +#include <odp/helper/icmp.h>
>>> +#include <odp/helper/udp.h>
>>> +#include <odp/helper/ipsec.h>
>>> +
>>> +#include <stdbool.h>
>>> +#include <sys/socket.h>
>>> +#include <net/if.h>
>>> +#include <sys/ioctl.h>
>>> +#include <sys/socket.h>
>>> +#include <netpacket/packet.h>
>>> +#include <net/ethernet.h>
>>> +#include <arpa/inet.h>
>>> +
>>> +#include <odp_ipsec_offload_misc.h>
>>> +#include <odp_ipsec_offload_sa_db.h>
>>> +#include <odp_ipsec_offload_sp_db.h>
>>> +#include <odp_ipsec_offload_fwd_db.h>
>>> +#include <odp_ipsec_offload_cache.h>
>>> +
>>> +#define MAX_WORKERS     32   /**< maximum number of worker threads */
>>> +
>>> +/**
>>> + * Parsed command line application arguments
>>> + */
>>> +typedef struct {
>>> +       int cpu_count;
>>> +       int flows;
>>> +       int if_count;           /**< Number of interfaces to be used */
>>> +       char **if_names;        /**< Array of pointers to interface names
>>> */
>>> +       char *if_str;           /**< Storage for interface names */
>>> +       int queue_type;         /**< Queue synchronization type*/
>>> +} appl_args_t;
>>> +/**
>>> + * Grouping of both parsed CL args and thread specific args - alloc
>>> together
>>> + */
>>> +typedef struct {
>>> +       /** Application (parsed) arguments */
>>> +       appl_args_t appl;
>>> +} args_t;
>>> +
>>> +/* helper funcs */
>>> +static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
>>> +static void print_info(char *progname, appl_args_t *appl_args);
>>> +static void usage(char *progname);
>>> +
>>> +/** Global pointer to args */
>>> +static args_t *args;
>>> +
>>> +/**
>>> + * Buffer pool for packet IO
>>> + */
>>> +#define SHM_PKT_POOL_BUF_COUNT 1024
>>> +#define SHM_PKT_POOL_BUF_SIZE  4096
>>> +#define SHM_PKT_POOL_SIZE      (SHM_PKT_POOL_BUF_COUNT *
>>> SHM_PKT_POOL_BUF_SIZE)
>>> +
>>> +static odp_pool_t pkt_pool = ODP_POOL_INVALID;
>>> +
>>> +/** Synchronize threads before packet processing begins */
>>> +static odp_barrier_t sync_barrier;
>>> +
>>> +/**
>>> + * Packet processing result codes
>>> + */
>>> +typedef enum {
>>> +       PKT_CONTINUE,    /**< No events posted, keep processing */
>>> +       PKT_POSTED,      /**< Event posted, stop processing */
>>> +       PKT_DROP,        /**< Reason to drop detected, stop processing */
>>> +       PKT_DONE         /**< Finished with packet, stop processing */
>>> +} pkt_disposition_e;
>>> +
>>> +#define MAX_COMPL_QUEUES               32
>>> +#define GET_THR_QUEUE_ID(x)            ((odp_thread_id()-1) % (x))
>>> +
>>> +/** Atomic queue IPSEC completion events */
>>> +static odp_queue_t completionq[MAX_COMPL_QUEUES];
>>> +
>>> +static int num_compl_queues;
>>> +static int num_workers;
>>> +
>>> +
>>> +/**
>>> + * Calculate hash value on given 2-tuple i.e. sip, dip
>>> + *
>>> + * @param ip_src       Source IP Address
>>> + * @param ip_dst       Destination IP Address
>>> + *
>>> + * @return Resultant hash value
>>> + */
>>> +static inline uint64_t calculate_flow_hash(uint32_t ip_src, uint32_t
>>> ip_dst)
>>> +{
>>> +       uint64_t hash = 0;
>>> +
>>> +       ip_dst += JHASH_GOLDEN_RATIO;
>>> +       ODP_BJ3_MIX(ip_src, ip_dst, hash);
>>> +       return hash;
>>> +}
>>> +
>>> +/**
>>> + * IPsec pre argument processing intialization
>>> + */
>>> +static
>>> +void ipsec_init_pre(void)
>>> +{
>>> +       /* Initialize our data bases */
>>> +       init_sp_db();
>>> +       init_sa_db();
>>> +       init_tun_db();
>>> +       init_ipsec_cache();
>>> +}
>>> +
>>> +/**
>>> + * IPsec post argument processing intialization
>>> + *
>>> + * Resolve SP DB with SA DB and create corresponding IPsec cache entries
>>> + */
>>> +static
>>> +void ipsec_init_post(void)
>>> +{
>>> +       sp_db_entry_t *entry;
>>> +       int queue_id = 0;
>>> +
>>> +       /* Attempt to find appropriate SA for each SP */
>>> +       for (entry = sp_db->list; NULL != entry; entry = entry->next) {
>>> +               sa_db_entry_t *cipher_sa = NULL;
>>> +               sa_db_entry_t *auth_sa = NULL;
>>> +               tun_db_entry_t *tun = NULL;
>>> +               queue_id %= num_workers;
>>> +               if (num_compl_queues < num_workers)
>>> +                       num_compl_queues++;
>>> +               queue_id++;
>>> +               if (entry->esp) {
>>> +                       cipher_sa = find_sa_db_entry(&entry->src_subnet,
>>> +                                       &entry->dst_subnet, 1);
>>> +                       tun = find_tun_db_entry(cipher_sa->src_ip,
>>> +                                       cipher_sa->dst_ip);
>>> +               }
>>> +               if (entry->ah) {
>>> +                       auth_sa = find_sa_db_entry(&entry->src_subnet,
>>> +                                       &entry->dst_subnet, 0);
>>> +                       tun = find_tun_db_entry(auth_sa->src_ip,
>>> +                                       auth_sa->dst_ip);
>>> +               }
>>> +
>>> +               if (cipher_sa && auth_sa) {
>>> +                       if (create_ipsec_cache_entry(cipher_sa,
>>> +                                                    auth_sa,
>>> +                                                    tun,
>>> +                                                    entry->input,
>>> +                                                    completionq[queue_id
>>> - 1])) {
>>> +                               EXAMPLE_ABORT("Error: IPSec cache entry
>>> failed.\n");
>>> +                       }
>>> +               } else {
>>> +                       printf(" WARNING: SA not found for SP\n");
>>> +                       dump_sp_db_entry(entry);
>>> +               }
>>> +       }
>>> +}
>>> +
>>> +/**
>>> + * Initialize interface
>>> + *
>>> + * Initialize ODP pktio and queues, query MAC address and update
>>> + * forwarding database.
>>> + *
>>> + * @param intf         Interface name string
>>> + * @param queue_type   Type of queue to configure.
>>> + */
>>> +static void initialize_intf(char *intf, int queue_type)
>>> +{
>>> +       odp_pktio_t pktio;
>>> +       odp_pktout_queue_t pktout;
>>> +       int ret;
>>> +       uint8_t src_mac[ODPH_ETHADDR_LEN];
>>> +       odp_pktio_param_t pktio_param;
>>> +       odp_pktio_capability_t capa;
>>> +       odp_pktin_queue_param_t pktin_param;
>>> +
>>> +       odp_pktio_param_init(&pktio_param);
>>> +
>>> +       pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
>>> +
>>> +       /*
>>> +        * Open a packet IO instance for thread and get default output
>>> queue
>>> +        */
>>> +       pktio = odp_pktio_open(intf, pkt_pool, &pktio_param);
>>> +       if (ODP_PKTIO_INVALID == pktio) {
>>> +               EXAMPLE_ABORT("Error: pktio create failed for %s\n",
>>> intf);
>>> +       }
>>> +
>>> +       odp_pktin_queue_param_init(&pktin_param);
>>> +
>>> +       ret = odp_pktio_capability(pktio, &capa);
>>> +       if (ret != 0)
>>> +               EXAMPLE_ABORT("Error: Unable to get pktio capability
>>> %s\n", intf);
>>> +
>>> +       pktin_param.queue_param.type = ODP_QUEUE_TYPE_SCHED;
>>> +       pktin_param.queue_param.sched.sync = queue_type;
>>> +       pktin_param.queue_param.sched.prio = ODP_SCHED_PRIO_DEFAULT;
>>> +       pktin_param.num_queues = capa.max_input_queues;
>>> +
>>> +       if (pktin_param.num_queues > 1)
>>> +               pktin_param.hash_enable = 1;
>>> +
>>> +       if (odp_pktin_queue_config(pktio, &pktin_param))
>>> +               EXAMPLE_ABORT("Error: pktin config failed for %s\n",
>>> intf);
>>> +
>>> +       if (odp_pktout_queue_config(pktio, NULL))
>>> +               EXAMPLE_ABORT("Error: pktout config failed for %s\n",
>>> intf);
>>> +
>>> +       if (odp_pktout_queue(pktio, &pktout, 1) != 1)
>>> +               EXAMPLE_ABORT("Error: failed to get pktout queue for
>>> %s\n", intf);
>>> +
>>> +       ret = odp_pktio_start(pktio);
>>> +       if (ret) {
>>> +               EXAMPLE_ABORT("Error: unable to start %s\n", intf);
>>> +       }
>>> +
>>> +       /* Read the source MAC address for this interface */
>>> +       ret = odp_pktio_mac_addr(pktio, src_mac, sizeof(src_mac));
>>> +       if (ret < 0) {
>>> +               EXAMPLE_ABORT("Error: failed during MAC address get for
>>> %s\n",
>>> +                             intf);
>>> +       }
>>> +
>>> +       printf("Created pktio:%02" PRIu64 "\n", odp_pktio_to_u64(pktio));
>>> +
>>> +       /* Resolve any routes using this interface for output */
>>> +       resolve_fwd_db(intf, pktout, src_mac);
>>> +}
>>> +
>>> +/**
>>> + * Packet Processing - Input verification
>>> + *
>>> + * @param pkt  Packet to inspect
>>> + *
>>> + * @return PKT_CONTINUE if good, supported packet else PKT_DROP
>>> + */
>>> +static pkt_disposition_e do_input_verify(odp_packet_t pkt)
>>> +{
>>> +       if (odp_unlikely(odp_packet_has_error(pkt))) {
>>> +               odp_packet_free(pkt);
>>> +               return PKT_DROP;
>>> +       }
>>> +
>>> +       if (!odp_packet_has_eth(pkt)) {
>>> +               odp_packet_free(pkt);
>>> +               return PKT_DROP;
>>> +       }
>>> +
>>> +       if (!odp_packet_has_ipv4(pkt)) {
>>> +               odp_packet_free(pkt);
>>> +               return PKT_DROP;
>>> +       }
>>> +
>>> +       return PKT_CONTINUE;
>>> +}
>>> +
>>> +/**
>>> + * Packet Processing - Route lookup in forwarding database
>>> + *
>>> + * @param pkt  Packet to route
>>> + *
>>> + * @return PKT_CONTINUE if route found else PKT_DROP
>>> + */
>>> +static
>>> +pkt_disposition_e do_route_fwd_db(odp_packet_t pkt)
>>> +{
>>> +       odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt,
>>> NULL);
>>> +       fwd_db_entry_t *fwd_entry;
>>> +       ipsec_cache_entry_t *ipsec_entry;
>>> +       odp_ipsec_op_param_t params;
>>> +       uint32_t        sip, dip;
>>> +       uint64_t        hash;
>>> +       odp_flow_entry_t *flow = NULL;
>>> +
>>> +       if (ip->ttl > 1) {
>>> +               ip->ttl -= 1;
>>> +               if (ip->chksum >= odp_cpu_to_be_16(0xffff - 0x100))
>>> +                       ip->chksum += odp_cpu_to_be_16(0x100) + 1;
>>> +               else
>>> +                       ip->chksum += odp_cpu_to_be_16(0x100);
>>> +       } else {
>>> +               odp_packet_free(pkt);
>>> +               return PKT_DROP;
>>> +       }
>>> +
>>> +       sip = odp_be_to_cpu_32(ip->src_addr);
>>> +       dip = odp_be_to_cpu_32(ip->dst_addr);
>>> +
>>> +       hash = calculate_flow_hash(sip, dip);
>>> +
>>> +       flow = odp_route_flow_lookup_in_bucket(sip, dip,
>>> +                                              &flow_table[hash &
>>> (bucket_count - 1)]);
>>> +       if (flow) {
>>> +do_opt:
>>> +               odp_packet_user_ptr_set(pkt, &flow->out_port);
>>> +               if (flow->out_port.sa == ODP_IPSEC_SA_INVALID)
>>> +                       return PKT_CONTINUE;
>>> +
>>> +               /* Initialize parameters block */
>>> +               params.sa = &flow->out_port.sa;
>>> +               params.pkt = &pkt;
>>> +               params.opt = NULL;
>>> +               params.num_pkt = 1;
>>> +               params.num_sa = 1;
>>> +               params.num_opt = 1;
>>> +
>>> +               /* Issue ipsec request */
>>> +               if (odp_unlikely(odp_ipsec_out_enq(&params) < 0)) {
>>> +                       EXAMPLE_DBG("Unable to out enqueue\n");
>>> +                       odp_packet_free(pkt);
>>> +                       return PKT_DROP;
>>> +               }
>>> +               return PKT_POSTED;
>>> +       } else {
>>> +               /*Check into Routing table*/
>>> +               fwd_entry = find_fwd_db_entry(dip);
>>> +               if (fwd_entry) {
>>> +                       /*Entry found. Updated in Flow table first.*/
>>> +                       flow = calloc(1, sizeof(odp_flow_entry_t));
>>> +                       if (!flow) {
>>> +                               EXAMPLE_ABORT("Failure to allocate
>>> memory");
>>> +                       }
>>> +                       flow->l3_src = sip;
>>> +                       flow->l3_dst = dip;
>>> +                       flow->out_port.pktout = fwd_entry->pktout;
>>> +                       memcpy(flow->out_port.addr.addr,
>>> fwd_entry->src_mac, ODPH_ETHADDR_LEN);
>>> +                       memcpy(flow->out_port.next_hop_addr.addr,
>>> fwd_entry->dst_mac, ODPH_ETHADDR_LEN);
>>> +                       ipsec_entry = find_ipsec_cache_entry_out(sip,
>>> dip);
>>> +                       if (ipsec_entry)
>>> +                               flow->out_port.sa = ipsec_entry->sa;
>>> +                       else
>>> +                               flow->out_port.sa = ODP_IPSEC_SA_INVALID;
>>> +                       flow->next = NULL;
>>> +                       /*Insert new flow into flow cache table*/
>>> +                       odp_route_flow_insert_in_bucket(flow,
>>> &flow_table[hash & (bucket_count - 1)]);
>>> +                       goto do_opt;
>>> +               } else {
>>> +                       EXAMPLE_DBG("No flow match found. Packet is
>>> dropped.\n");
>>> +                       odp_packet_free(pkt);
>>> +                       return PKT_DROP;
>>> +
>>> +               }
>>> +       }
>>> +}
>>> +
>>> +
>>> +/**
>>> + * Packet Processing - Input IPsec packet classification
>>> + *
>>> + * Verify the received packet has IPsec headers,
>>> + * if so issue ipsec request else skip.
>>> + *
>>> + * @param pkt   Packet to classify
>>> + *
>>> + * @return PKT_CONTINUE if done else PKT_POSTED
>>> + */
>>> +static
>>> +pkt_disposition_e do_ipsec_in_classify(odp_packet_t pkt)
>>> +{
>>> +       odp_ipsec_op_param_t params;
>>> +
>>> +       if (!odp_packet_has_ipsec(pkt))
>>> +               return PKT_CONTINUE;
>>> +
>>> +       /* Initialize parameters block */
>>> +       params.pkt = &pkt;
>>> +       params.num_pkt = 1;
>>> +       params.num_sa = 0;
>>> +       params.num_opt = 0;
>>> +       params.opt = NULL;
>>> +
>>> +       /* Issue ipsec request */
>>> +       if (odp_unlikely(odp_ipsec_in_enq(&params) < 0)) {
>>> +               EXAMPLE_DBG("Unable to in enqueue\n");
>>> +               odp_packet_free(pkt);
>>> +               return PKT_DROP;
>>> +       }
>>> +       return PKT_POSTED;
>>> +}
>>> +/**
>>> + * Packet IO worker thread
>>> + *
>>> + * Loop calling odp_schedule to obtain packet from the two sources,
>>> + * and continue processing the packet.
>>> + *
>>> + *  - Input interfaces (i.e. new work)
>>> + *  - Per packet ipsec API completion queue
>>> + *
>>> + * @param arg  Required by "odph_linux_pthread_create", unused
>>> + *
>>> + * @return NULL (should never return)
>>> + */
>>> +static
>>> +void *pktio_thread(void *arg EXAMPLE_UNUSED)
>>> +{
>>> +       int thr;
>>> +       odp_packet_t pkt;
>>> +       odp_pktout_queue_t out_queue;
>>> +       odp_out_entry_t *out_port;
>>> +       odp_event_t ev = ODP_EVENT_INVALID;
>>> +       thr = odp_thread_id();
>>> +
>>> +       printf("Pktio thread [%02i] starts\n", thr);
>>> +       odp_barrier_wait(&sync_barrier);
>>> +
>>> +       /* Loop packets */
>>> +       for (;;) {
>>> +               pkt_disposition_e rc;
>>> +
>>> +               ev = odp_schedule(NULL, ODP_SCHED_WAIT);
>>> +               /* Use schedule to get event from any input queue */
>>> +               /* Determine new work versus completion */
>>> +               if (ODP_EVENT_PACKET == odp_event_type(ev)) {
>>> +                       pkt = odp_packet_from_event(ev);
>>> +
>>> +                       rc = do_input_verify(pkt);
>>> +                       if (odp_unlikely(rc))
>>> +                               continue;
>>> +
>>> +                       rc = do_ipsec_in_classify(pkt);
>>> +                       if (rc)
>>> +                               continue;
>>> +
>>> +                       rc = do_route_fwd_db(pkt);
>>> +                       if (rc)
>>> +                               continue;
>>> +
>>> +                       out_port = (odp_out_entry_t
>>> *)odp_packet_user_ptr(pkt);
>>> +                       out_queue = (odp_pktout_queue_t)out_port->pktout;
>>> +
>>> +                       if (odp_unlikely(odp_pktout_send(out_queue,
>>> &pkt, 1) < 0))
>>> +                               odp_packet_free(pkt);
>>> +
>>> +               } else if (ODP_EVENT_IPSEC_RESULT == odp_event_type(ev)) {
>>> +                       odp_ipsec_op_result_t result;
>>> +                       odp_ipsec_packet_result_t res;
>>> +                       odph_ethhdr_t   *eth;
>>> +                       odp_packet_t out_pkt;
>>> +
>>> +                       result.pkt = &out_pkt;
>>> +                       result.res = &res;
>>> +
>>> +                       if (odp_unlikely(odp_ipsec_result(&result, ev) <
>>> 0)) {
>>> +                               EXAMPLE_DBG("Error Event\n");
>>> +                               odp_packet_free((odp_packet_t)ev);
>>> +                               continue;
>>> +                       }
>>> +
>>> +                       if (odp_unlikely(res.status.all)) {
>>> +                               odp_packet_free((odp_packet_t)ev);
>>> +                               continue;
>>> +                       }
>>> +
>>> +                       odph_ipv4hdr_t *ip = (odph_ipv4hdr_t
>>> *)odp_packet_l3_ptr(out_pkt, NULL);
>>> +
>>> +                       if (ip->proto != IPPROTO_ESP) {
>>> +                               rc = do_route_fwd_db(out_pkt);
>>> +                               if (odp_unlikely(rc))
>>> +                                       continue;
>>> +                       }
>>> +
>>> +                       out_port = (odp_out_entry_t
>>> *)odp_packet_user_ptr(out_pkt);
>>> +                       out_queue = (odp_pktout_queue_t)out_port->pktout;
>>> +
>>> +                       eth = (odph_ethhdr_t *)((void *)ip -
>>> sizeof(odph_ethhdr_t));
>>> +                       eth->dst = out_port->next_hop_addr;
>>> +                       eth->src = out_port->addr;
>>> +                       eth->type = odp_cpu_to_be_16(0x800);
>>> +
>>> +                       if (odp_unlikely(odp_pktout_send(out_queue,
>>> &out_pkt, 1) < 0))
>>> +                               odp_packet_free(out_pkt);
>>> +               } else {
>>> +                       EXAMPLE_DBG("Invalid Event\n");
>>> +                       odp_packet_free((odp_packet_t)ev);
>>> +                       continue;
>>> +               }
>>> +       }
>>> +
>>> +       /* unreachable */
>>> +       return NULL;
>>> +}
>>> +
>>> +/**
>>> + * ODP ipsec proto example main function
>>> + */
>>> +int
>>> +main(int argc, char *argv[])
>>> +{
>>> +       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>>> +       int i;
>>> +       odp_shm_t shm;
>>> +       odp_cpumask_t cpumask;
>>> +       char cpumaskstr[ODP_CPUMASK_STR_SIZE];
>>> +       odp_pool_param_t params;
>>> +       odp_queue_param_t qparam;
>>> +       odp_instance_t instance;
>>> +       odph_linux_thr_params_t thr_params;
>>> +       odp_ipsec_config_t config;
>>> +       odp_ipsec_capability_t capa;
>>> +
>>> +       /*Validate if user has passed only help option*/
>>> +       if (argc == 2) {
>>> +               if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
>>> {
>>> +                       usage(argv[0]);
>>> +                       exit(EXIT_SUCCESS);
>>> +               }
>>> +       }
>>> +
>>> +       /* Initialize ODP before calling anything else */
>>> +       if (odp_init_global(&instance, NULL, NULL)) {
>>> +               EXAMPLE_ABORT("Error: ODP global init failed.\n");
>>> +       }
>>> +       /* Initialize this thread */
>>> +       if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
>>> +               EXAMPLE_ABORT("Error: ODP local init failed.\n");
>>> +       }
>>> +       /* Reserve memory for arguments from shared memory */
>>> +       shm = odp_shm_reserve("shm_args", sizeof(args_t),
>>> +                             ODP_CACHE_LINE_SIZE, 0);
>>> +       args = odp_shm_addr(shm);
>>> +
>>> +       if (NULL == args) {
>>> +               EXAMPLE_ABORT("Error: shared mem alloc failed.\n");
>>> +       }
>>> +       memset(args, 0, sizeof(*args));
>>> +
>>> +       /* Must init our databases before parsing args */
>>> +       ipsec_init_pre();
>>> +       init_fwd_db();
>>> +
>>> +       /* Parse and store the application arguments */
>>> +       parse_args(argc, argv, &args->appl);
>>> +
>>> +       /*Initialize route table for user given parameter*/
>>> +       odp_init_routing_table();
>>> +
>>> +       /* Print both system and application information */
>>> +       print_info(NO_PATH(argv[0]), &args->appl);
>>> +
>>> +       if (odp_ipsec_capability(&capa))
>>> +               EXAMPLE_ABORT("Error: Capability not configured.\n");
>>> +
>>> +       odp_ipsec_config_init(&config);
>>> +
>>> +       if (capa.op_mode_async && (capa.op_mode_async >=
>>> capa.op_mode_sync))
>>> +               config.op_mode = ODP_IPSEC_OP_MODE_ASYNC;
>>> +       else
>>> +               EXAMPLE_ABORT("Error: Sync mode not supported.\n");
>>> +
>>> +       if (odp_ipsec_config(&config))
>>> +               EXAMPLE_ABORT("Error: IPSec not configured.\n");
>>> +
>>> +       /* Default to system CPU count unless user specified */
>>> +       num_workers = MAX_WORKERS;
>>> +       if (args->appl.cpu_count && args->appl.cpu_count <= MAX_WORKERS)
>>> +               num_workers = args->appl.cpu_count;
>>> +
>>> +       /*
>>> +        * By default CPU #0 runs Linux kernel background tasks.
>>> +        * Start mapping thread from CPU #1
>>> +        */
>>> +       num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
>>> +       (void)odp_cpumask_to_str(&cpumask, cpumaskstr,
>>> sizeof(cpumaskstr));
>>> +
>>> +       /*
>>> +        * Create completion queues
>>> +        */
>>> +       odp_queue_param_init(&qparam);
>>> +       qparam.type       = ODP_QUEUE_TYPE_SCHED;
>>> +       qparam.sched.prio  = ODP_SCHED_PRIO_HIGHEST;
>>> +       qparam.sched.sync  = args->appl.queue_type;
>>> +       qparam.sched.group = ODP_SCHED_GROUP_ALL;
>>> +
>>> +       for (i = 0; i < num_workers; i++) {
>>> +               completionq[i] = odp_queue_create("completion", &qparam);
>>> +               if (ODP_QUEUE_INVALID == completionq[i]) {
>>> +                       EXAMPLE_ABORT("Error: completion queue creation
>>> failed\n");
>>> +               }
>>> +       }
>>> +       printf("num worker threads: %i\n", num_workers);
>>> +       printf("first CPU:          %i\n", odp_cpumask_first(&cpumask));
>>> +       printf("cpu mask:           %s\n", cpumaskstr);
>>> +
>>> +       /* Create a barrier to synchronize thread startup */
>>> +       odp_barrier_init(&sync_barrier, num_workers);
>>> +
>>> +       /* Create packet buffer pool */
>>> +       odp_pool_param_init(&params);
>>> +       params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
>>> +       params.pkt.len     = SHM_PKT_POOL_BUF_SIZE;
>>> +       params.pkt.num     = SHM_PKT_POOL_BUF_COUNT;
>>> +       params.type        = ODP_POOL_PACKET;
>>> +
>>> +       pkt_pool = odp_pool_create("packet_pool", &params);
>>> +
>>> +       if (ODP_POOL_INVALID == pkt_pool) {
>>> +               EXAMPLE_ABORT("Error: packet pool create failed.\n");
>>> +       }
>>> +
>>> +       ipsec_init_post();
>>> +
>>> +       /* Initialize interfaces (which resolves FWD DB entries */
>>> +       for (i = 0; i < args->appl.if_count; i++) {
>>> +               initialize_intf(args->appl.if_names[i],
>>> args->appl.queue_type);
>>> +       }
>>> +
>>> +       printf("  Configured queues SYNC type: [%s]\n",
>>> (args->appl.queue_type == 0)?
>>> +
>>>  "PARALLEL":(args->appl.queue_type == 1)?
>>> +
>>>  "ATOMIC":"ORDERED");
>>> +       memset(&thr_params, 0, sizeof(thr_params));
>>> +       thr_params.start    = pktio_thread;
>>> +       thr_params.arg      = NULL;
>>> +       thr_params.thr_type = ODP_THREAD_WORKER;
>>> +       thr_params.instance = instance;
>>> +
>>> +       /* Create and initialize worker threads */
>>> +       odph_linux_pthread_create(thread_tbl, &cpumask,
>>> +                                         &thr_params);
>>> +       odph_linux_pthread_join(thread_tbl, num_workers);
>>> +
>>> +       free(args->appl.if_names);
>>> +       free(args->appl.if_str);
>>> +       printf("Exit\n\n");
>>> +       return 0;
>>> +}
>>> +
>>> +/**
>>> + * Parse and store the command line arguments
>>> + *
>>> + * @param argc       argument count
>>> + * @param argv[]     argument vector
>>> + * @param appl_args  Store application arguments here
>>> + */
>>> +static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
>>> +{
>>> +       int opt;
>>> +       int long_index;
>>> +       char *token;
>>> +       size_t len;
>>> +       int rc = 0;
>>> +       int i;
>>> +
>>> +       static struct option longopts[] = {
>>> +               {"count", required_argument, NULL, 'c'},
>>> +               {"interface", required_argument, NULL, 'i'},    /* return
>>> 'i' */
>>> +               {"route", required_argument, NULL, 'r'},        /* return
>>> 'r' */
>>> +               {"policy", required_argument, NULL, 'p'},       /* return
>>> 'p' */
>>> +               {"ah", required_argument, NULL, 'a'},           /* return
>>> 'a' */
>>> +               {"esp", required_argument, NULL, 'e'},          /* return
>>> 'e' */
>>> +               {"tunnel", required_argument, NULL, 't'},       /* return
>>> 't' */
>>> +               {"flows", no_argument, NULL, 'f'},              /* return
>>> 'f' */
>>> +               {"queue type", required_argument, NULL, 'q'},   /* return
>>> 'q' */
>>> +               {"help", no_argument, NULL, 'h'},               /* return
>>> 'h' */
>>> +               {NULL, 0, NULL, 0}
>>> +       };
>>> +
>>> +       appl_args->flows = 1;
>>> +       appl_args->queue_type = ODP_SCHED_SYNC_ATOMIC;
>>> +
>>> +       while (!rc) {
>>> +               opt = getopt_long(argc, argv, "+c:i:h:r:p:a:e:t:s:q:f:",
>>> +                                 longopts, &long_index);
>>> +               if (opt < 0)
>>> +                       break;  /* No more options */
>>> +               switch (opt) {
>>> +               case 'f':
>>> +                       appl_args->flows = atoi(optarg);
>>> +                       if (appl_args->flows > 256) {
>>> +                               printf("Maximum acceptable value for -f
>>> is 256\n");
>>> +                               rc = -1;
>>> +                       }
>>> +                       if (optind != 3) {
>>> +                               printf("-f must be the 1st argument of
>>> the command\n");
>>> +                               rc = -1;
>>> +                       }
>>> +                       EXAMPLE_DBG("Bucket count = %d\n", bucket_count);
>>> +                       break;
>>> +               case 'c':
>>> +                       appl_args->cpu_count = atoi(optarg);
>>> +                       break;
>>> +               case 'i':
>>> +                       /* parse packet-io interface names */
>>> +                       len = strlen(optarg);
>>> +                       if (0 == len) {
>>> +                               usage(argv[0]);
>>> +                               exit(EXIT_FAILURE);
>>> +                       }
>>> +                       len += 1;       /* add room for '\0' */
>>> +
>>> +                       appl_args->if_str = malloc(len);
>>> +                       if (appl_args->if_str == NULL) {
>>> +                               usage(argv[0]);
>>> +                               exit(EXIT_FAILURE);
>>> +                       }
>>> +
>>> +                       /* count the number of tokens separated by ',' */
>>> +                       strcpy(appl_args->if_str, optarg);
>>> +                       for (token = strtok(appl_args->if_str, ","), i =
>>> 0;
>>> +                            token; token = strtok(NULL, ","), i++);
>>> +                       appl_args->if_count = i;
>>> +                       if (!appl_args->if_count) {
>>> +                               usage(argv[0]);
>>> +                               exit(EXIT_FAILURE);
>>> +                       }
>>> +                       /* Allocate storage for the if names */
>>> +                       appl_args->if_names =
>>> +                               calloc(appl_args->if_count, sizeof(char
>>> *));
>>> +                       if (!appl_args->if_names) {
>>> +                               EXAMPLE_ABORT("Memory allocation
>>> failure\n");
>>> +                       }
>>> +                       /* Store the if names (reset names string) */
>>> +                       strcpy(appl_args->if_str, optarg);
>>> +                       for (token = strtok(appl_args->if_str, ","), i =
>>> 0;
>>> +                            token; token = strtok(NULL, ","), i++) {
>>> +                               appl_args->if_names[i] = token;
>>> +                       }
>>> +                       break;
>>> +               case 'r':
>>> +                       rc = create_fwd_db_entry(optarg,
>>> appl_args->if_names,
>>> +                                                appl_args->if_count,
>>> appl_args->flows);
>>> +                       break;
>>> +               case 'p':
>>> +                       rc = create_sp_db_entry(optarg, appl_args->flows);
>>> +                       break;
>>> +               case 'a':
>>> +                       rc = create_sa_db_entry(optarg, FALSE,
>>> appl_args->flows);
>>> +                       break;
>>> +               case 'e':
>>> +                       rc = create_sa_db_entry(optarg, TRUE,
>>> appl_args->flows);
>>> +                       break;
>>> +               case 't':
>>> +                       rc = create_tun_db_entry(optarg,
>>> appl_args->flows);
>>> +                       break;
>>> +               case 'q':
>>> +                       i = atoi(optarg);
>>> +                       if (i > ODP_SCHED_SYNC_ORDERED || i <
>>> ODP_SCHED_SYNC_PARALLEL) {
>>> +                               printf("Invalid queue type: setting
>>> default to atomic");
>>> +                               break;
>>> +                       }
>>> +                       appl_args->queue_type = i;
>>> +                       break;
>>> +               case 'h':
>>> +                       usage(argv[0]);
>>> +                       exit(EXIT_SUCCESS);
>>> +                       break;
>>> +               default:
>>> +                       break;
>>> +               }
>>> +       }
>>> +
>>> +       if (rc) {
>>> +               printf("ERROR: failed parsing -%c option\n", opt);
>>> +               usage(argv[0]);
>>> +               exit(EXIT_FAILURE);
>>> +       }
>>> +
>>> +       if (0 == appl_args->if_count) {
>>> +               usage(argv[0]);
>>> +               exit(EXIT_FAILURE);
>>> +       }
>>> +
>>> +       optind = 1;             /* reset 'extern optind' from the getopt
>>> lib */
>>> +}
>>> +
>>> +/**
>>> + * Print system and application info
>>> + */
>>> +static void print_info(char *progname, appl_args_t *appl_args)
>>> +{
>>> +       int i;
>>> +
>>> +       printf("\n"
>>> +              "ODP system info\n"
>>> +              "---------------\n"
>>> +              "ODP API version: %s\n"
>>> +              "CPU model:       %s\n"
>>> +              "CPU freq (hz):   %"PRIu64"\n"
>>> +              "Cache line size: %i\n"
>>> +              "CPU count:       %i\n"
>>> +              "\n",
>>> +              odp_version_api_str(), odp_cpu_model_str(),
>>> odp_cpu_hz_max(),
>>> +              odp_sys_cache_line_size(), odp_cpu_count());
>>> +       printf("Running ODP application: \"%s\"\n"
>>> +              "------------------------\n"
>>> +              "IF-count:        %i\n"
>>> +              "Using IFs:      ",
>>> +              progname, appl_args->if_count);
>>> +       for (i = 0; i < appl_args->if_count; ++i)
>>> +               printf(" %s", appl_args->if_names[i]);
>>> +       printf("\n");
>>> +       dump_fwd_db();
>>> +       dump_sp_db();
>>> +       dump_sa_db();
>>> +       dump_tun_db();
>>> +       printf("\n\n");
>>> +       fflush(NULL);
>>> +}
>>> +
>>> +/**
>>> + * Prinf usage information
>>> + */
>>> +static void usage(char *progname)
>>> +{
>>> +       printf("\n"
>>> +              "Usage: %s OPTIONS\n"
>>> +              "  E.g. %s -i eth1,eth2,eth3 -m 0\n"
>>> +              "\n"
>>> +              "OpenDataPlane example application.\n"
>>> +              "\n"
>>> +              "Mandatory OPTIONS:\n"
>>> +              " -i, --interface Eth interfaces (comma-separated, no
>>> spaces)\n"
>>> +              "Routing / IPSec OPTIONS:\n"
>>> +              " -r, --route SubNet:Intf:NextHopMAC\n"
>>> +              " -p, --policy SrcSubNet:DstSubNet:(in|out):(
>>> ah|esp|both)\n"
>>> +              " -e, --esp SrcIP:DstIP:(3des|null):SPI:Key192\n"
>>> +              " -a, --ah SrcIP:DstIP:(md5|null):SPI:Key128\n"
>>> +              " -t, --tun SrcIP:DstIP:TunSrcIP:TunDstIP\n"
>>> +              "\n"
>>> +              "  Where: NextHopMAC is raw hex/dot notation, i.e.
>>> 03.BA.44.9A.CE.02\n"
>>> +              "         IP is decimal/dot notation, i.e. 192.168.1.1\n"
>>> +              "         SubNet is decimal/dot/slash notation, i.e
>>> 192.168.0.0/16\n <http://192.168.0.0/16%5Cn>"
>>> +              "         SPI is raw hex, 32 bits\n"
>>> +              "         KeyXXX is raw hex, XXX bits long\n"
>>> +              "\n"
>>> +              "  Examples:\n"
>>> +              "     -r 192.168.222.0/24:p8p1:08.00.27.F5.8B.DB\n
>>> <http://192.168.222.0/24:p8p1:08.00.27.F5.8B.DB%5Cn>"
>>> +              "     -p 192.168.111.0/24:192.168.222.0/24:out:esp\n
>>> <http://192.168.111.0/24:192.168.222.0/24:out:esp%5Cn>"
>>> +              "     -e 192.168.111.2:192.168.222.2:3d
>>> es:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224\n"
>>> +              "     -a 192.168.111.2:192.168.222.2:md
>>> 5:201:a731649644c5dee92cbd9c2e7e188ee6\n"
>>> +              "     -t 192.168.111.2:192.168.222.2:192.168.150.1:192
>>> .168.150.2\n"
>>> +              "\n"
>>> +              "Optional OPTIONS\n"
>>> +              "  -f, --flows <number> routes count.\n"
>>> +              "  -c, --count <number> CPU count.\n"
>>> +              "  -q            specify the queue type\n"
>>> +              "                0:      ODP_SCHED_SYNC_PARALLEL\n"
>>> +              "                1:      ODP_SCHED_SYNC_ATOMIC\n"
>>> +              "                2:      ODP_SCHED_SYNC_ORDERED\n"
>>> +              "                        default is
>>> ODP_SCHED_SYNC_ATOMIC\n"
>>> +              "  -h, --help           Display help and exit.\n"
>>> +              "\n", NO_PATH(progname), NO_PATH(progname)
>>> +               );
>>> +}
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload_cache.c
>>> b/example/ipsec_offload/odp_ipsec_offload_cache.c
>>> new file mode 100644
>>> index 0000000..5b6a036
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload_cache.c
>>> @@ -0,0 +1,148 @@
>>> +/*
>>> + * Copyright (c) 2017 NXP. All rights reserved.
>>> + */
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +
>>> +#include <example_debug.h>
>>> +
>>> +#include <odp.h>
>>> +
>>> +#include <odp/helper/ipsec.h>
>>> +#include <odp/helper/ip.h>
>>> +
>>> +#include <odp_ipsec_offload_cache.h>
>>> +
>>> +/** Global pointer to ipsec_cache db */
>>> +ipsec_cache_t *ipsec_cache;
>>> +
>>> +#define IPDEFTTL 64
>>> +
>>> +void init_ipsec_cache(void)
>>> +{
>>> +       odp_shm_t shm;
>>> +
>>> +       shm = odp_shm_reserve("shm_ipsec_cache",
>>> +                             sizeof(ipsec_cache_t),
>>> +                             ODP_CACHE_LINE_SIZE,
>>> +                             0);
>>> +
>>> +       ipsec_cache = odp_shm_addr(shm);
>>> +
>>> +       if (ipsec_cache == NULL) {
>>> +               EXAMPLE_ABORT("Error: shared mem alloc failed.\n");
>>> +       }
>>> +       memset(ipsec_cache, 0, sizeof(*ipsec_cache));
>>> +}
>>> +
>>> +int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
>>> +                            sa_db_entry_t *auth_sa,
>>> +                            tun_db_entry_t *tun,
>>> +                            odp_bool_t in,
>>> +                            odp_queue_t completionq)
>>> +{
>>> +       odp_ipsec_sa_param_t sa_params;
>>> +       ipsec_cache_entry_t *entry;
>>> +       odp_ipsec_sa_t sa;
>>> +       uint32_t src_ip, dst_ip;
>>> +
>>> +       odp_ipsec_sa_param_init(&sa_params);
>>> +
>>> +       /* Verify we have a good entry */
>>> +       entry = &ipsec_cache->array[ipsec_cache->index];
>>> +       if (MAX_DB <= ipsec_cache->index)
>>> +               return -1;
>>> +
>>> +       /* Verify SA mode match in case of cipher&auth */
>>> +       if (!tun) {
>>> +               printf("\n TRANSPORT MODE not supported");
>>> +               return -1;
>>> +       }
>>> +
>>> +       /* Setup parameters and call ipsec library to create sa */
>>> +       if (in) {
>>> +               sa_params.dir = ODP_IPSEC_DIR_INBOUND;
>>> +               sa_params.lookup_mode = ODP_IPSEC_LOOKUP_IN_UNIQUE_SA;
>>> +       } else {
>>> +               sa_params.dir = ODP_IPSEC_DIR_OUTBOUND;
>>> +               sa_params.lookup_mode = ODP_IPSEC_LOOKUP_DISABLED;
>>> +       }
>>> +
>>> +       sa_params.dest_queue = completionq;
>>> +       sa_params.mode = ODP_IPSEC_MODE_TUNNEL;
>>> +
>>> +       /* Cipher */
>>> +       if (cipher_sa) {
>>> +               sa_params.crypto.cipher_alg  = cipher_sa->alg.u.cipher;
>>> +               sa_params.crypto.cipher_key.data  = cipher_sa->key.data;
>>> +               sa_params.crypto.cipher_key.length  =
>>> cipher_sa->key.length;
>>> +               sa_params.spi = cipher_sa->spi;
>>> +       } else {
>>> +               sa_params.crypto.cipher_alg = ODP_CIPHER_ALG_NULL;
>>> +       }
>>> +
>>> +       /* Auth */
>>> +       if (auth_sa) {
>>> +               sa_params.crypto.auth_alg = auth_sa->alg.u.auth;
>>> +               sa_params.crypto.auth_key.data = auth_sa->key.data;
>>> +               sa_params.crypto.auth_key.length = auth_sa->key.length;
>>> +       } else {
>>> +               sa_params.crypto.auth_alg = ODP_AUTH_ALG_NULL;
>>> +       }
>>> +
>>> +       src_ip = odp_cpu_to_be_32(tun->tun_src_ip);
>>> +       dst_ip = odp_cpu_to_be_32(tun->tun_dst_ip);
>>> +       sa_params.tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
>>> +       sa_params.tunnel.ipv4.src_addr = &src_ip;
>>> +       sa_params.tunnel.ipv4.dst_addr = &dst_ip;
>>> +       sa_params.tunnel.ipv4.ttl = IPDEFTTL;
>>> +       sa_params.tunnel.ipv4.dscp = 0;
>>> +       sa_params.tunnel.ipv4.df = 1;
>>> +
>>> +       sa = odp_ipsec_sa_create(&sa_params);
>>> +       if (sa == ODP_IPSEC_SA_INVALID)
>>> +               return -1;
>>> +
>>> +       /* Copy selector IPs in cache entry*/
>>> +       if (cipher_sa) {
>>> +               entry->src_ip = cipher_sa->src_ip;
>>> +               entry->dst_ip = cipher_sa->dst_ip;
>>> +       } else if (auth_sa) {
>>> +               entry->src_ip = auth_sa->src_ip;
>>> +               entry->dst_ip = auth_sa->dst_ip;
>>> +       }
>>> +
>>> +       /* Initialize state */
>>> +       entry->sa = sa;
>>> +
>>> +       /* Add entry to the appropriate list */
>>> +       ipsec_cache->index++;
>>> +       if (in) {
>>> +               entry->next = ipsec_cache->in_list;
>>> +               ipsec_cache->in_list = entry;
>>> +       } else {
>>> +               entry->next = ipsec_cache->out_list;
>>> +               ipsec_cache->out_list = entry;
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
>>> +                                               uint32_t dst_ip)
>>> +{
>>> +       ipsec_cache_entry_t *entry = ipsec_cache->out_list;
>>> +
>>> +       /* Look for a hit */
>>> +       for (; NULL != entry; entry = entry->next) {
>>> +               if ((entry->src_ip == src_ip) && (entry->dst_ip ==
>>> dst_ip))
>>> +                       break;
>>> +       }
>>> +       return entry;
>>> +}
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload_cache.h
>>> b/example/ipsec_offload/odp_ipsec_offload_cache.h
>>> new file mode 100644
>>> index 0000000..65f4dda
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload_cache.h
>>> @@ -0,0 +1,78 @@
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +#ifndef ODP_IPSEC_CACHE_H_
>>> +#define ODP_IPSEC_CACHE_H_
>>> +
>>> +#ifdef __cplusplus
>>> +extern "C" {
>>> +#endif
>>> +
>>> +#include <odp.h>
>>> +#include <odp/helper/ipsec.h>
>>> +
>>> +#include <odp_ipsec_offload_misc.h>
>>> +#include <odp_ipsec_offload_sa_db.h>
>>> +
>>> +/**
>>> + * IPsec cache data base entry
>>> + */
>>> +typedef struct ipsec_cache_entry_s {
>>> +       struct ipsec_cache_entry_s      *next;          /**< Next entry
>>> on list */
>>> +       uint32_t                        src_ip;         /**< Source v4
>>> address */
>>> +       uint32_t                        dst_ip;         /**< Destination
>>> v4 address */
>>> +       odp_ipsec_sa_t                  sa;             /**< IPSec sa
>>> handle */
>>> +} ipsec_cache_entry_t;
>>> +
>>> +/**
>>> + * IPsec cache data base global structure
>>> + */
>>> +typedef struct ipsec_cache_s {
>>> +       uint32_t             index;       /**< Index of next available
>>> entry */
>>> +       ipsec_cache_entry_t *in_list;     /**< List of active input
>>> entries */
>>> +       ipsec_cache_entry_t *out_list;    /**< List of active output
>>> entries */
>>> +       ipsec_cache_entry_t  array[MAX_DB]; /**< Entry storage */
>>> +} ipsec_cache_t;
>>> +
>>> +/** Global pointer to ipsec_cache db */
>>> +extern ipsec_cache_t *ipsec_cache;
>>> +
>>> +/** Initialize IPsec cache */
>>> +void init_ipsec_cache(void);
>>> +
>>> +/**
>>> + * Create an entry in the IPsec cache
>>> + *
>>> + * @param cipher_sa   Cipher SA DB entry pointer
>>> + * @param auth_sa     Auth SA DB entry pointer
>>> + * @param tun         Tunnel DB entry pointer
>>> + * @param in          Direction (input versus output)
>>> + * @param completionq Completion queue
>>> + *
>>> + * @return 0 if successful else -1
>>> + */
>>> +int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
>>> +                            sa_db_entry_t *auth_sa,
>>> +                            tun_db_entry_t *tun,
>>> +                            odp_bool_t in,
>>> +                            odp_queue_t completionq);
>>> +
>>> +/**
>>> + * Find a matching IPsec cache entry for output packet
>>> + *
>>> + * @param src_ip    Source IPv4 address
>>> + * @param dst_ip    Destination IPv4 address
>>> + *
>>> + * @return pointer to IPsec cache entry else NULL
>>> + */
>>> +ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
>>> +                                               uint32_t dst_ip);
>>> +
>>> +#ifdef __cplusplus
>>> +}
>>> +#endif
>>> +
>>> +#endif
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload_fwd_db.c
>>> b/example/ipsec_offload/odp_ipsec_offload_fwd_db.c
>>> new file mode 100644
>>> index 0000000..860b3ee
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload_fwd_db.c
>>> @@ -0,0 +1,223 @@
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +/* enable strtok */
>>> +#define _POSIX_C_SOURCE 200112L
>>> +
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +
>>> +#include <example_debug.h>
>>> +#include <odp.h>
>>> +
>>> +#include <odp_ipsec_offload_fwd_db.h>
>>> +
>>> +/**
>>> + * Pointer to Flow cache table
>>> + */
>>> +flow_bucket_t *flow_table;
>>> +
>>> +/**
>>> + * bucket count. It will be updated with user argument if provided
>>> + */
>>> +uint32_t bucket_count = ODP_DEFAULT_BUCKET_COUNT;
>>> +
>>> +/** Global pointer to fwd db */
>>> +fwd_db_t *fwd_db;
>>> +
>>> +void odp_init_routing_table(void)
>>> +{
>>> +       odp_shm_t               hash_shm;
>>> +       uint32_t                i;
>>> +       flow_bucket_t           *bucket;
>>> +
>>> +       /*Reserve memory for Routing hash table*/
>>> +       hash_shm = odp_shm_reserve("route_table",
>>> +                       sizeof(flow_bucket_t) * bucket_count,
>>> +                                               ODP_CACHE_LINE_SIZE, 0);
>>> +       flow_table = odp_shm_addr(hash_shm);
>>> +       if (!flow_table) {
>>> +               EXAMPLE_ABORT("Error: shared mem alloc failed.\n");
>>> +       }
>>> +       /*Inialize Locks*/
>>> +       for (i = 0; i < bucket_count; i++) {
>>> +               bucket = &flow_table[i];
>>> +               LOCK_INIT(&bucket->lock);
>>> +       }
>>> +
>>> +       memset(flow_table, 0, bucket_count * sizeof(flow_bucket_t));
>>> +}
>>> +
>>> +void init_fwd_db(void)
>>> +{
>>> +       odp_shm_t shm;
>>> +
>>> +       shm = odp_shm_reserve("shm_fwd_db",
>>> +                             sizeof(fwd_db_t),
>>> +                             ODP_CACHE_LINE_SIZE,
>>> +                             0);
>>> +
>>> +       fwd_db = odp_shm_addr(shm);
>>> +
>>> +       if (fwd_db == NULL) {
>>> +               EXAMPLE_ABORT("Error: shared mem alloc failed.\n");
>>> +       }
>>> +       memset(fwd_db, 0, sizeof(*fwd_db));
>>> +}
>>> +
>>> +int create_fwd_db_entry(char *input, char **if_names, int if_count, int
>>> entries)
>>> +{
>>> +       int pos = 0, i, match = 0, count = 0;
>>> +       char *local;
>>> +       char *str;
>>> +       char *save;
>>> +       char *token;
>>> +       fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];
>>> +
>>> +       /* Verify we haven't run out of space */
>>> +       if (MAX_DB <= fwd_db->index)
>>> +               return -1;
>>> +
>>> +       /* Make a local copy */
>>> +       local = malloc(strlen(input) + 1);
>>> +       if (NULL == local)
>>> +               return -1;
>>> +       strcpy(local, input);
>>> +
>>> +       /* Setup for using "strtok_r" to search input string */
>>> +       str = local;
>>> +       save = NULL;
>>> +
>>> +       /* Parse tokens separated by ':' */
>>> +       while (NULL != (token = strtok_r(str, ":", &save))) {
>>> +               str = NULL;  /* reset str for subsequent strtok_r calls */
>>> +
>>> +               /* Parse token based on its position */
>>> +               switch (pos) {
>>> +               case 0:
>>> +                       parse_ipv4_string(token,
>>> +                                         &entry->subnet.addr,
>>> +                                         &entry->subnet.mask);
>>> +                       break;
>>> +               case 1:
>>> +                       strncpy(entry->oif, token, OIF_LEN - 1);
>>> +                       entry->oif[OIF_LEN - 1] = 0;
>>> +                       for (i = 0; i < if_count; i++) {
>>> +                               if (!strcmp(if_names[i], entry->oif)) {
>>> +                                       match = 1;
>>> +                                       break;
>>> +                               }
>>> +                       }
>>> +                       if (!match) {
>>> +                               printf("ERROR: interface name not correct
>>> for route\n");
>>> +                               free(local);
>>> +                               return -1;
>>> +                       }
>>> +                       break;
>>> +               case 2:
>>> +                       parse_mac_string(token, entry->dst_mac);
>>> +                       break;
>>> +               default:
>>> +                       printf("ERROR: extra token \"%s\" at position
>>> %d\n",
>>> +                              token, pos);
>>> +                       break;
>>> +               }
>>> +
>>> +               /* Advance to next position */
>>> +               pos++;
>>> +       }
>>> +
>>> +       /* Verify we parsed exactly the number of tokens we expected */
>>> +       if (3 != pos) {
>>> +               printf("ERROR: \"%s\" contains %d tokens, expected 3\n",
>>> +                      input,
>>> +                      pos);
>>> +               free(local);
>>> +               return -1;
>>> +       }
>>> +
>>> +       /* Add route to the list */
>>> +       fwd_db->index++;
>>> +       entry->next = fwd_db->list;
>>> +       fwd_db->list = entry;
>>> +
>>> +       count++;
>>> +
>>> +       while (count < entries) {
>>> +               fwd_db_entry_t *new_entry = &fwd_db->array[fwd_db->index];
>>> +
>>> +               /* Verify we haven't run out of space */
>>> +               if (MAX_DB <= fwd_db->index)
>>> +                       return -1;
>>> +
>>> +               new_entry->subnet.addr = entry->subnet.addr + count;
>>> +               new_entry->subnet.mask = entry->subnet.mask;
>>> +               strncpy(new_entry->oif, entry->oif, OIF_LEN - 1);
>>> +               new_entry->oif[OIF_LEN - 1] = 0;
>>> +               new_entry->dst_mac[0] = entry->dst_mac[0];
>>> +               new_entry->dst_mac[1] = entry->dst_mac[1];
>>> +               new_entry->dst_mac[2] = entry->dst_mac[2];
>>> +               new_entry->dst_mac[3] = entry->dst_mac[3];
>>> +               new_entry->dst_mac[4] = entry->dst_mac[4];
>>> +               new_entry->dst_mac[5] = entry->dst_mac[5];
>>> +
>>> +               /* Add route to the list */
>>> +               fwd_db->index++;
>>> +               new_entry->next = fwd_db->list;
>>> +               fwd_db->list = new_entry;
>>> +               count++;
>>> +       }
>>> +
>>> +       free(local);
>>> +       return 0;
>>> +}
>>> +
>>> +void resolve_fwd_db(char *intf, odp_pktout_queue_t pktout, uint8_t *mac)
>>> +{
>>> +       fwd_db_entry_t *entry;
>>> +
>>> +       /* Walk the list and attempt to set output queue and MAC */
>>> +       for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
>>> +               if (strcmp(intf, entry->oif))
>>> +                       continue;
>>> +
>>> +               entry->pktout = pktout;
>>> +               memcpy(entry->src_mac, mac, ODPH_ETHADDR_LEN);
>>> +       }
>>> +}
>>> +
>>> +void dump_fwd_db_entry(fwd_db_entry_t *entry)
>>> +{
>>> +       char subnet_str[MAX_STRING];
>>> +       char mac_str[MAX_STRING];
>>> +
>>> +       printf(" %s %s %s\n",
>>> +              ipv4_subnet_str(subnet_str, &entry->subnet),
>>> +              entry->oif,
>>> +              mac_addr_str(mac_str, entry->dst_mac));
>>> +}
>>> +
>>> +void dump_fwd_db(void)
>>> +{
>>> +       fwd_db_entry_t *entry;
>>> +
>>> +       printf("\n"
>>> +              "Routing table\n"
>>> +              "-------------\n");
>>> +
>>> +       for (entry = fwd_db->list; NULL != entry; entry = entry->next)
>>> +               dump_fwd_db_entry(entry);
>>> +}
>>> +
>>> +fwd_db_entry_t *find_fwd_db_entry(uint32_t dst_ip)
>>> +{
>>> +       fwd_db_entry_t *entry;
>>> +
>>> +       for (entry = fwd_db->list; NULL != entry; entry = entry->next)
>>> +               if (entry->subnet.addr == (dst_ip & entry->subnet.mask))
>>> +                       break;
>>> +       return entry;
>>> +}
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload_fwd_db.h
>>> b/example/ipsec_offload/odp_ipsec_offload_fwd_db.h
>>> new file mode 100644
>>> index 0000000..2f42596
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload_fwd_db.h
>>> @@ -0,0 +1,198 @@
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +#ifndef ODP_IPSEC_FWD_DB_H_
>>> +#define ODP_IPSEC_FWD_DB_H_
>>> +
>>> +#ifdef __cplusplus
>>> +extern "C" {
>>> +#endif
>>> +
>>> +#include <odp.h>
>>> +#include <odp/helper/eth.h>
>>> +#include <odp_ipsec_offload_misc.h>
>>> +
>>> +#define OIF_LEN 32
>>> +
>>> +/**
>>> + * Forwarding data base entry
>>> + */
>>> +
>>> +typedef struct fwd_db_entry_s {
>>> +       struct fwd_db_entry_s *next;          /**< Next entry on list */
>>> +       char                   oif[OIF_LEN];  /**< Output interface name
>>> */
>>> +       odp_pktout_queue_t      pktout;         /**< Output transmit
>>> queue */
>>> +       uint8_t   src_mac[ODPH_ETHADDR_LEN];  /**< Output source MAC */
>>> +       uint8_t   dst_mac[ODPH_ETHADDR_LEN];  /**< Output destination MAC
>>> */
>>> +       ip_addr_range_t        subnet;        /**< Subnet for this router
>>> */
>>> +} fwd_db_entry_t;
>>> +
>>> +/**
>>> + * Forwarding data base global structure
>>> + */
>>> +typedef struct fwd_db_s {
>>> +       uint32_t          index;          /**< Next available entry */
>>> +       fwd_db_entry_t   *list;           /**< List of active routes */
>>> +       fwd_db_entry_t    array[MAX_DB];  /**< Entry storage */
>>> +} fwd_db_t;
>>> +
>>> +/** Global pointer to fwd db */
>>> +extern fwd_db_t *fwd_db;
>>> +
>>> +/**
>>> + * Flow cache table entry
>>> + */
>>> +typedef struct {
>>> +       void                    *next;          /**< Pointer to next flow
>>> in list*/
>>> +       uint32_t                l3_src;         /**< Source IP Address*/
>>> +       uint32_t                l3_dst;         /**< Destination IP
>>> Address*/
>>> +       odp_out_entry_t         out_port;       /**< Out interface of
>>> matching flow*/
>>> +} odp_flow_entry_t;
>>> +
>>> +/**
>>> + * Flow cache table bucket
>>> + */
>>> +typedef struct {
>>> +       odp_spinlock_t          lock;   /**< Bucket lock*/
>>> +       odp_flow_entry_t        *next;  /**< Pointer to first flow entry
>>> in bucket*/
>>> +} flow_bucket_t;
>>> +
>>> +/**
>>> +* Pointers to Flow cache tables
>>> +*/
>>> +extern flow_bucket_t *flow_table;
>>> +
>>> +extern flow_bucket_t *ipsec_out_flow_table;
>>> +
>>> +extern flow_bucket_t *ipsec_in_flow_table;
>>> +
>>> +/**
>>> + * Number of buckets in hash table
>>> + */
>>> +extern uint32_t bucket_count;
>>> +
>>> +/*
>>> + * Allocate and Initialize routing table with default Route entries.
>>> + *
>>> + */
>>> +void odp_init_routing_table(void);
>>> +
>>> +/*
>>> + * Searches flow entry in given hash bucket according to given 5-tuple
>>> information
>>> + *
>>> + * @param sip           Source IP Address
>>> + * @param dip           Destination IP Address
>>> + * @param sport         Source Port Number
>>> + * @param dport         Destination Port Number
>>> + * @param proto         IP protocol
>>> + * @param bucket        Hash Bucket
>>> + *
>>> + * @return Matching flow entry
>>> + */
>>> +static inline odp_flow_entry_t *odp_route_flow_lookup_in_bucket(uint32_t
>>> sip,
>>> +                                               uint32_t dip, void
>>> *bucket)
>>> +{
>>> +       odp_flow_entry_t      *flow, *head;
>>> +
>>> +       head = ((flow_bucket_t *)bucket)->next;
>>> +       for (flow = head; flow != NULL; flow = flow->next) {
>>> +               if ((flow->l3_src == sip) && (flow->l3_dst == dip))
>>> +                       return flow;
>>> +       }
>>> +       return NULL;
>>> +}
>>> +
>>> +/**
>>> + * Insert the flow into given hash bucket
>>> + *
>>> + * @param flow         Which is to be inserted
>>> + * @param bucket       Target Hash Bucket
>>> + *
>>> + */
>>> +static inline void odp_route_flow_insert_in_bucket(odp_flow_entry_t
>>> *flow,
>>> +                                                               void
>>> *bucket)
>>> +{
>>> +       odp_flow_entry_t *temp;
>>> +       flow_bucket_t *bkt = (flow_bucket_t *)bucket;
>>> +
>>> +       if (!flow) {
>>> +               EXAMPLE_ERR("Invalid flow entry passed\n");
>>> +               return;
>>> +       }
>>> +
>>> +       LOCK(&bkt->lock);
>>> +       /*Check that entry already exist or not*/
>>> +       temp = odp_route_flow_lookup_in_bucket(flow->l3_src,
>>> flow->l3_dst, bkt);
>>> +       if (temp) {
>>> +               UNLOCK(&bkt->lock);
>>> +               return;
>>> +       }
>>> +
>>> +       if (!bkt->next) {
>>> +               bkt->next = flow;
>>> +       } else {
>>> +               temp = bkt->next;
>>> +               flow->next = temp;
>>> +               bkt->next = flow;
>>> +       }
>>> +       UNLOCK(&bkt->lock);
>>> +}
>>> +
>>> +/** Initialize FWD DB */
>>> +void init_fwd_db(void);
>>> +
>>> +/**
>>> + * Create a forwarding database entry
>>> + *
>>> + * String is of the format "SubNet:Intf:NextHopMAC"
>>> + *
>>> + * @param input  Pointer to string describing route
>>> + *
>>> + * @param if_names  Array of Name of the interfaces available
>>> + *
>>> + * @param if_count  number of interfaces in if_names array
>>> + *
>>> + * @param entries number of entries
>>> + *
>>> + * @return 0 if successful else -1
>>> + */
>>> +int create_fwd_db_entry(char *input, char **if_names, int if_count, int
>>> entries);
>>> +
>>> +/**
>>> + * Scan FWD DB entries and resolve output queue and source MAC address
>>> + *
>>> + * @param intf   Interface name string
>>> + * @param outq   Output queue for packet transmit
>>> + * @param mac    MAC address of this interface
>>> + */
>>> +void resolve_fwd_db(char *intf, odp_pktout_queue_t pktout, uint8_t *mac);
>>> +
>>> +/**
>>> + * Display one fowarding database entry
>>> + *
>>> + * @param entry  Pointer to entry to display
>>> + */
>>> +void dump_fwd_db_entry(fwd_db_entry_t *entry);
>>> +
>>> +/**
>>> + * Display the forwarding database
>>> + */
>>> +void dump_fwd_db(void);
>>> +
>>> +/**
>>> + * Find a matching forwarding database entry
>>> + *
>>> + * @param dst_ip  Destination IPv4 address
>>> + *
>>> + * @return pointer to forwarding DB entry else NULL
>>> + */
>>> +fwd_db_entry_t *find_fwd_db_entry(uint32_t dst_ip);
>>> +
>>> +#ifdef __cplusplus
>>> +}
>>> +#endif
>>> +
>>> +#endif
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload_misc.h
>>> b/example/ipsec_offload/odp_ipsec_offload_misc.h
>>> new file mode 100644
>>> index 0000000..dbe6dc9
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload_misc.h
>>> @@ -0,0 +1,384 @@
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +#ifndef ODP_IPSEC_MISC_H_
>>> +#define ODP_IPSEC_MISC_H_
>>> +
>>> +#ifdef __cplusplus
>>> +extern "C" {
>>> +#endif
>>> +
>>> +#include <odp.h>
>>> +#include <odp/helper/eth.h>
>>> +#include <odp/helper/ip.h>
>>> +#include <odp/helper/ipsec.h>
>>> +
>>> +#ifndef TRUE
>>> +#define TRUE  1
>>> +#endif
>>> +#ifndef FALSE
>>> +#define FALSE 0
>>> +#endif
>>> +
>>> +#define MAX_DB          1024   /**< maximum number of data base entries
>>> */
>>> +#define MAX_STRING      32   /**< maximum string length */
>>> +#define KEY_BITS_3DES      192  /**< 3DES cipher key length in bits */
>>> +#define KEY_BITS_MD5_96    128  /**< MD5_96 auth key length in bits */
>>> +#define KEY_BITS_AES       128  /**< AES cipher key length in bits */
>>> +#define KEY_BITS_SHA1_96   160  /**< SHA1_96 auth key length in bits */
>>> +#define KEY_BITS_SHA2_256   256  /**< SHA2_256 auth key length in bits */
>>> +
>>> +/**
>>> + * Number of buckets in hash table
>>> + */
>>> +extern uint32_t bucket_count;
>>> +
>>> +#define LOCK(a)      odp_spinlock_lock(a)
>>> +#define UNLOCK(a)    odp_spinlock_unlock(a)
>>> +#define LOCK_INIT(a) odp_spinlock_init(a)
>>> +
>>> +/**
>>> + * Hash calculation utility
>>> + */
>>> +#define JHASH_GOLDEN_RATIO     0x9e3779b9
>>> +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
>>> +#define ODP_BJ3_MIX(a, b, c) \
>>> +{ \
>>> +       a -= c; a ^= rot(c, 4); c += b; \
>>> +       b -= a; b ^= rot(a, 6); a += c; \
>>> +       c -= b; c ^= rot(b, 8); b += a; \
>>> +       a -= c; a ^= rot(c, 16); c += b; \
>>> +       b -= a; b ^= rot(a, 19); a += c; \
>>> +       c -= b; c ^= rot(b, 4); b += a; \
>>> +}
>>> +
>>> +/**
>>> + * Default Hash bucket number
>>> + */
>>> +#define ODP_DEFAULT_BUCKET_COUNT       1024
>>> +
>>> +/**< Number of bits represnted by a string of hexadecimal characters */
>>> +#define KEY_STR_BITS(str) (4 * strlen(str))
>>> +
>>> +/** IPv4 helpers for data length and uint8t pointer */
>>> +#define ipv4_data_p(ip) ((uint8_t *)((odph_ipv4hdr_t *)ip + 1))
>>> +
>>> +/** Get rid of path in filename - only for unix-type paths using '/' */
>>> +#define NO_PATH(file_name) (strrchr((file_name), '/') ?                 \
>>> +                           strrchr((file_name), '/') + 1 : (file_name))
>>> +
>>> +/**
>>> + * Actual entries
>>> + */
>>> +typedef struct {
>>> +       odp_pktout_queue_t pktout;              /**< queue handle*/
>>> +       odph_ethaddr_t  addr;           /**< pktio MAC Address*/
>>> +       odph_ethaddr_t  next_hop_addr;  /**< Next Hop MAC Address*/
>>> +       odp_ipsec_sa_t sa;      /**< IPSec sa handle*/
>>> +} odp_out_entry_t;
>>> +
>>> +/**
>>> + * IPsec key
>>> + */
>>> +typedef struct {
>>> +       uint8_t  data[32];  /**< Key data */
>>> +       uint8_t  length;    /**< Key length */
>>> +} ipsec_key_t;
>>> +
>>> +/**
>>> + * IPsec algorithm
>>> + */
>>> +typedef struct {
>>> +       odp_bool_t cipher;
>>> +       union {
>>> +               odp_cipher_alg_t cipher;
>>> +               odp_auth_alg_t   auth;
>>> +       } u;
>>> +} ipsec_alg_t;
>>> +
>>> +/**
>>> + * IP address range (subnet)
>>> + */
>>> +typedef struct ip_addr_range_s {
>>> +       uint32_t  addr;     /**< IP address */
>>> +       uint32_t  mask;     /**< mask, 1 indicates bits are valid */
>>> +} ip_addr_range_t;
>>> +
>>> +/**
>>> + * Parse text string representing a key into ODP key structure
>>> + *
>>> + * @param keystring  Pointer to key string to convert
>>> + * @param key        Pointer to ODP key structure to populate
>>> + * @param alg        Cipher/authentication algorithm associated with the
>>> key
>>> + *
>>> + * @return 0 if successful else -1
>>> + */
>>> +static inline
>>> +int parse_key_string(char *keystring,
>>> +                    ipsec_key_t *key,
>>> +                    ipsec_alg_t *alg)
>>> +{
>>> +       int idx;
>>> +       int key_bits_in = KEY_STR_BITS(keystring);
>>> +       char temp[3];
>>> +
>>> +       key->length = 0;
>>> +
>>> +       /* Algorithm is either cipher or authentication */
>>> +       if (alg->cipher) {
>>> +               if ((alg->u.cipher == ODP_CIPHER_ALG_3DES_CBC) &&
>>> +                   (KEY_BITS_3DES == key_bits_in))
>>> +                       key->length = key_bits_in / 8;
>>> +               if ((alg->u.cipher == ODP_CIPHER_ALG_AES128_CBC) &&
>>> +                   (KEY_BITS_AES == key_bits_in))
>>> +                       key->length = key_bits_in / 8;
>>> +       } else {
>>> +               if ((alg->u.auth == ODP_AUTH_ALG_MD5_96) &&
>>> +                   (KEY_BITS_MD5_96 == key_bits_in))
>>> +                       key->length = key_bits_in / 8;
>>> +               if ((alg->u.auth == ODP_AUTH_ALG_SHA1_96) &&
>>> +                   (KEY_BITS_SHA1_96 == key_bits_in))
>>> +                       key->length = key_bits_in / 8;
>>> +               if ((alg->u.auth == ODP_AUTH_ALG_SHA256_128) &&
>>> +                   (KEY_BITS_SHA2_256 == key_bits_in))
>>> +                       key->length = key_bits_in / 8;
>>> +       }
>>> +
>>> +       for (idx = 0; idx < key->length; idx++) {
>>> +               temp[0] = *keystring++;
>>> +               temp[1] = *keystring++;
>>> +               temp[2] = 0;
>>> +               key->data[idx] = strtol(temp, NULL, 16);
>>> +       }
>>> +
>>> +       return key->length ? 0 : -1;
>>> +}
>>> +
>>> +/**
>>> + * Check IPv4 address against a range/subnet
>>> + *
>>> + * @param addr  IPv4 address to check
>>> + * @param range Pointer to address range to check against
>>> + *
>>> + * @return 1 if match else 0
>>> + */
>>> +static inline
>>> +int match_ip_range(uint32_t addr, ip_addr_range_t *range)
>>> +{
>>> +       return (range->addr == (addr & range->mask));
>>> +}
>>> +
>>> +/**
>>> + * Generate text string representing IPv4 address
>>> + *
>>> + * @param b    Pointer to buffer to store string
>>> + * @param addr IPv4 address
>>> + *
>>> + * @return Pointer to supplied buffer
>>> + */
>>> +static inline
>>> +char *ipv4_addr_str(char *b, uint32_t addr)
>>> +{
>>> +       sprintf(b, "%03d.%03d.%03d.%03d",
>>> +               0xFF & ((addr) >> 24),
>>> +               0xFF & ((addr) >> 16),
>>> +               0xFF & ((addr) >>  8),
>>> +               0xFF & ((addr) >>  0));
>>> +       return b;
>>> +}
>>> +
>>> +/**
>>> + * Parse text string representing an IPv4 address or subnet
>>> + *
>>> + * String is of the format "XXX.XXX.XXX.XXX(/W)" where
>>> + * "XXX" is decimal value and "/W" is optional subnet length
>>> + *
>>> + * @param ipaddress  Pointer to IP address/subnet string to convert
>>> + * @param addr       Pointer to return IPv4 address
>>> + * @param mask       Pointer (optional) to return IPv4 mask
>>> + *
>>> + * @return 0 if successful else -1
>>> + */
>>> +static inline
>>> +int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *mask)
>>> +{
>>> +       int b[4];
>>> +       int qualifier = 32;
>>> +       int converted;
>>> +
>>> +       if (strchr(ipaddress, '/')) {
>>> +               converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",
>>> +                                  &b[3], &b[2], &b[1], &b[0],
>>> +                                  &qualifier);
>>> +               if (5 != converted)
>>> +                       return -1;
>>> +       } else {
>>> +               converted = sscanf(ipaddress, "%d.%d.%d.%d",
>>> +                                  &b[3], &b[2], &b[1], &b[0]);
>>> +               if (4 != converted)
>>> +                       return -1;
>>> +       }
>>> +
>>> +       if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))
>>> +               return -1;
>>> +       if (!qualifier || (qualifier > 32))
>>> +               return -1;
>>> +
>>> +       *addr = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
>>> +       if (mask)
>>> +               *mask = ~(0xFFFFFFFF & ((1ULL << (32 - qualifier)) - 1));
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +/**
>>> + * Generate text string representing IPv4 range/subnet, output
>>> + * in "XXX.XXX.XXX.XXX/W" format
>>> + *
>>> + * @param b     Pointer to buffer to store string
>>> + * @param range Pointer to IPv4 address range
>>> + *
>>> + * @return Pointer to supplied buffer
>>> + */
>>> +static inline
>>> +char *ipv4_subnet_str(char *b, ip_addr_range_t *range)
>>> +{
>>> +       int idx;
>>> +       int len;
>>> +
>>> +       for (idx = 0; idx < 32; idx++)
>>> +               if (range->mask & (1 << idx))
>>> +                       break;
>>> +       len = 32 - idx;
>>> +
>>> +       sprintf(b, "%03d.%03d.%03d.%03d/%d",
>>> +               0xFF & ((range->addr) >> 24),
>>> +               0xFF & ((range->addr) >> 16),
>>> +               0xFF & ((range->addr) >>  8),
>>> +               0xFF & ((range->addr) >>  0),
>>> +               len);
>>> +       return b;
>>> +}
>>> +
>>> +/**
>>> + * Generate text string representing MAC address
>>> + *
>>> + * @param b     Pointer to buffer to store string
>>> + * @param mac   Pointer to MAC address
>>> + *
>>> + * @return Pointer to supplied buffer
>>> + */
>>> +static inline
>>> +char *mac_addr_str(char *b, uint8_t *mac)
>>> +{
>>> +       sprintf(b, "%02X.%02X.%02X.%02X.%02X.%02X",
>>> +               mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
>>> +       return b;
>>> +}
>>> +
>>> +/**
>>> + * Parse text string representing a MAC address into byte araray
>>> + *
>>> + * String is of the format "XX.XX.XX.XX.XX.XX" where XX is hexadecimal
>>> + *
>>> + * @param macaddress  Pointer to MAC address string to convert
>>> + * @param mac         Pointer to MAC address byte array to populate
>>> + *
>>> + * @return 0 if successful else -1
>>> + */
>>> +static inline
>>> +int parse_mac_string(char *macaddress, uint8_t *mac)
>>> +{
>>> +       int macwords[ODPH_ETHADDR_LEN];
>>> +       int converted;
>>> +
>>> +       converted = sscanf(macaddress,
>>> +                          "%x.%x.%x.%x.%x.%x",
>>> +                          &macwords[0], &macwords[1], &macwords[2],
>>> +                          &macwords[3], &macwords[4], &macwords[5]);
>>> +       if (6 != converted)
>>> +               return -1;
>>> +
>>> +       mac[0] = macwords[0];
>>> +       mac[1] = macwords[1];
>>> +       mac[2] = macwords[2];
>>> +       mac[3] = macwords[3];
>>> +       mac[4] = macwords[4];
>>> +       mac[5] = macwords[5];
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +/**
>>> + * Locate IPsec headers (AH and/or ESP) in packet
>>> + *
>>> + * @param ip     Pointer to packets IPv4 header
>>> + * @param ah_p   Pointer to location to return AH header pointer
>>> + * @param esp_p  Pointer to location to return ESP header pointer
>>> + *
>>> + * @return length of IPsec headers found
>>> + */
>>> +static inline
>>> +int locate_ipsec_headers(odph_ipv4hdr_t *ip,
>>> +                        odph_ahhdr_t **ah_p,
>>> +                        odph_esphdr_t **esp_p)
>>> +{
>>> +       uint8_t *in = ipv4_data_p(ip);
>>> +       odph_ahhdr_t *ah = NULL;
>>> +       odph_esphdr_t *esp = NULL;
>>> +
>>> +       if (ODPH_IPPROTO_AH == ip->proto) {
>>> +               ah = (odph_ahhdr_t *)in;
>>> +               in += ((ah)->ah_len + 2) * 4;
>>> +               if (ODPH_IPPROTO_ESP == ah->next_header) {
>>> +                       esp = (odph_esphdr_t *)in;
>>> +                       in += sizeof(odph_esphdr_t);
>>> +               }
>>> +       } else if (ODPH_IPPROTO_ESP == ip->proto) {
>>> +               esp = (odph_esphdr_t *)in;
>>> +               in += sizeof(odph_esphdr_t);
>>> +       }
>>> +
>>> +       *ah_p = ah;
>>> +       *esp_p = esp;
>>> +       return in - (ipv4_data_p(ip));
>>> +}
>>> +
>>> +/**
>>> + * Adjust IPv4 length
>>> + *
>>> + * @param ip   Pointer to IPv4 header
>>> + * @param adj  Signed adjustment value
>>> + */
>>> +static inline
>>> +void ipv4_adjust_len(odph_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;
>>> +}
>>> +
>>> +
>>> +#ifdef __cplusplus
>>> +}
>>> +#endif
>>> +
>>> +#endif
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload_sa_db.c
>>> b/example/ipsec_offload/odp_ipsec_offload_sa_db.c
>>> new file mode 100644
>>> index 0000000..c299daa
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload_sa_db.c
>>> @@ -0,0 +1,361 @@
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:    BSD-3-Clause
>>> + */
>>> +
>>> +/* enable strtok */
>>> +#define _POSIX_C_SOURCE 200112L
>>> +
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +
>>> +#include <example_debug.h>
>>> +
>>> +#include <odp.h>
>>> +
>>> +#include <odp_ipsec_offload_sa_db.h>
>>> +
>>> +/** Global pointer to sa db */
>>> +static sa_db_t *sa_db;
>>> +
>>> +/** Global pointer to tun db */
>>> +static tun_db_t *tun_db;
>>> +
>>> +void init_sa_db(void)
>>> +{
>>> +       odp_shm_t shm;
>>> +
>>> +       shm = odp_shm_reserve("shm_sa_db",
>>> +                             sizeof(sa_db_t),
>>> +                             ODP_CACHE_LINE_SIZE,
>>> +                             0);
>>> +
>>> +       sa_db = odp_shm_addr(shm);
>>> +
>>> +       if (sa_db == NULL) {
>>> +               EXAMPLE_ABORT("Error: shared mem alloc failed.\n");
>>> +       }
>>> +       memset(sa_db, 0, sizeof(*sa_db));
>>> +}
>>> +
>>> +void init_tun_db(void)
>>> +{
>>> +       odp_shm_t shm;
>>> +
>>> +       shm = odp_shm_reserve("shm_tun_db",
>>> +                             sizeof(tun_db_t),
>>> +                             ODP_CACHE_LINE_SIZE,
>>> +                             0);
>>> +       tun_db = odp_shm_addr(shm);
>>> +
>>> +       if (!tun_db) {
>>> +               EXAMPLE_ABORT("Error: shared mem alloc failed.\n");
>>> +       }
>>> +       memset(tun_db, 0, sizeof(*tun_db));
>>> +}
>>> +
>>> +int create_sa_db_entry(char *input, odp_bool_t cipher, int entries)
>>> +{
>>> +       int pos = 0, count = 0;
>>> +       char *local;
>>> +       char *str;
>>> +       char *save;
>>> +       char *token;
>>> +       sa_db_entry_t *entry = &sa_db->array[sa_db->index];
>>> +
>>> +       /* Verify we have a good entry */
>>> +       if (MAX_DB <= sa_db->index)
>>> +               return -1;
>>> +
>>> +       /* Make a local copy */
>>> +       local = malloc(strlen(input) + 1);
>>> +       if (NULL == local)
>>> +               return -1;
>>> +       strcpy(local, input);
>>> +
>>> +       /* Set cipher versus auth */
>>> +       entry->alg.cipher = cipher;
>>> +
>>> +       /* Setup for using "strtok_r" to search input string */
>>> +       str = local;
>>> +       save = NULL;
>>> +
>>> +       /* Parse tokens separated by ':' */
>>> +       while (NULL != (token = strtok_r(str, ":", &save))) {
>>> +               str = NULL;  /* reset str for subsequent strtok_r calls */
>>> +
>>> +               /* Parse token based on its position */
>>> +               switch (pos) {
>>> +               case 0:
>>> +                       parse_ipv4_string(token, &entry->src_ip, NULL);
>>> +                       break;
>>> +               case 1:
>>> +                       parse_ipv4_string(token, &entry->dst_ip, NULL);
>>> +                       break;
>>> +               case 2:
>>> +                       if (cipher) {
>>> +                               if (0 == strcmp(token, "3des")) {
>>> +                                       entry->alg.u.cipher =
>>> +                                               ODP_CIPHER_ALG_3DES_CBC;
>>> +                               } else if (0 == strcmp(token, "aes")) {
>>> +                                       entry->alg.u.cipher =
>>> +                                               ODP_CIPHER_ALG_AES128_CBC;
>>> +                               } else {
>>> +                                       entry->alg.u.cipher =
>>> +                                               ODP_CIPHER_ALG_NULL;
>>> +                               }
>>> +                       } else {
>>> +                               if (0 == strcmp(token, "md5")) {
>>> +                                       entry->alg.u.auth =
>>> +                                               ODP_AUTH_ALG_MD5_96;
>>> +                               } else if (0 == strcmp(token, "sha1")) {
>>> +                                       entry->alg.u.auth =
>>> +                                               ODP_AUTH_ALG_SHA1_96;
>>> +                               } else if (0 == strcmp(token, "sha256")) {
>>> +                                       entry->alg.u.auth =
>>> +                                               ODP_AUTH_ALG_SHA256_128;
>>> +                               } else {
>>> +                                       entry->alg.u.auth =
>>> ODP_AUTH_ALG_NULL;
>>> +                               }
>>> +                       }
>>> +                       break;
>>> +               case 3:
>>> +                       entry->spi = strtol(token, NULL, 16);
>>> +                       break;
>>> +               case 4:
>>> +                       parse_key_string(token,
>>> +                                        &entry->key,
>>> +                                        &entry->alg);
>>> +                       break;
>>> +               default:
>>> +                       printf("ERROR: extra token \"%s\" at position
>>> %d\n",
>>> +                              token, pos);
>>> +                       break;
>>> +               }
>>> +
>>> +               /* Advance to next position */
>>> +               pos++;
>>> +       }
>>> +
>>> +       /* Verify we parsed exactly the number of tokens we expected */
>>> +       if (5 != pos) {
>>> +               printf("ERROR: \"%s\" contains %d tokens, expected 5\n",
>>> +                      input,
>>> +                      pos);
>>> +               free(local);
>>> +               return -1;
>>> +       }
>>> +
>>> +       /* Add route to the list */
>>> +       sa_db->index++;
>>> +       entry->next = sa_db->list;
>>> +       sa_db->list = entry;
>>> +       count++;
>>> +
>>> +       while (count < entries) {
>>> +               sa_db_entry_t *new_entry = &sa_db->array[sa_db->index];
>>> +
>>> +               /* Verify we have a good entry */
>>> +               if (MAX_DB <= sa_db->index)
>>> +                       return -1;
>>> +
>>> +               new_entry->alg.cipher = entry->alg.cipher;
>>> +               new_entry->src_ip = entry->src_ip + count;
>>> +               new_entry->dst_ip = entry->dst_ip + count;
>>> +               new_entry->alg.u.cipher = entry->alg.u.cipher;
>>> +               new_entry->alg.u.auth = entry->alg.u.auth;
>>> +               new_entry->spi = entry->spi + count;
>>> +               new_entry->key = entry->key;
>>> +               new_entry->alg = entry->alg;
>>> +               /* Add route to the list */
>>> +               sa_db->index++;
>>> +               new_entry->next = sa_db->list;
>>> +               sa_db->list = new_entry;
>>> +               count++;
>>> +       }
>>> +
>>> +       free(local);
>>> +       return 0;
>>> +}
>>> +
>>> +int create_tun_db_entry(char *input, int entries)
>>> +{
>>> +       int pos = 0, count = 0;
>>> +       char *local;
>>> +       char *str;
>>> +       char *save;
>>> +       char *token;
>>> +       tun_db_entry_t *entry = &tun_db->array[tun_db->index];
>>> +
>>> +       /* Verify we have a good entry */
>>> +       if (MAX_DB <= tun_db->index)
>>> +               return -1;
>>> +
>>> +       /* Make a local copy */
>>> +       local = malloc(strlen(input) + 1);
>>> +       if (NULL == local)
>>> +               return -1;
>>> +       strcpy(local, input);
>>> +
>>> +       /* Setup for using "strtok_r" to search input string */
>>> +       str = local;
>>> +       save = NULL;
>>> +
>>> +       /* Parse tokens separated by ':' */
>>> +       while (NULL != (token = strtok_r(str, ":", &save))) {
>>> +               str = NULL;  /* reset str for subsequent strtok_r calls */
>>> +
>>> +               /* Parse token based on its position */
>>> +               switch (pos) {
>>> +               case 0:
>>> +                       parse_ipv4_string(token, &entry->src_ip, NULL);
>>> +                       break;
>>> +               case 1:
>>> +                       parse_ipv4_string(token, &entry->dst_ip, NULL);
>>> +                       break;
>>> +               case 2:
>>> +                       parse_ipv4_string(token, &entry->tun_src_ip,
>>> NULL);
>>> +                       break;
>>> +               case 3:
>>> +                       parse_ipv4_string(token, &entry->tun_dst_ip,
>>> NULL);
>>> +                       break;
>>> +               default:
>>> +                       printf("ERROR: extra token \"%s\" at position
>>> %d\n",
>>> +                              token, pos);
>>> +                       break;
>>> +               }
>>> +               pos++;
>>> +       }
>>> +
>>> +       /* Verify we parsed exactly the number of tokens we expected */
>>> +       if (4 != pos) {
>>> +               printf("ERROR: \"%s\" contains %d tokens, expected 4\n",
>>> +                      input,
>>> +                      pos);
>>> +               free(local);
>>> +               return -1;
>>> +       }
>>> +
>>> +       /* Add route to the list */
>>> +       tun_db->index++;
>>> +       entry->next = tun_db->list;
>>> +       tun_db->list = entry;
>>> +       count++;
>>> +
>>> +       while (count < entries) {
>>> +               tun_db_entry_t *new_entry = &tun_db->array[tun_db->index];
>>> +
>>> +               /* Verify we have a good entry */
>>> +               if (MAX_DB <= tun_db->index)
>>> +                       return -1;
>>> +
>>> +               new_entry->src_ip = entry->src_ip + count;
>>> +               new_entry->dst_ip = entry->dst_ip + count;
>>> +               new_entry->tun_src_ip = entry->tun_src_ip + count;
>>> +               new_entry->tun_dst_ip = entry->tun_dst_ip + count;
>>> +               /* Add route to the list */
>>> +               tun_db->index++;
>>> +               new_entry->next = tun_db->list;
>>> +               tun_db->list = new_entry;
>>> +               count++;
>>> +       }
>>> +
>>> +       free(local);
>>> +       return 0;
>>> +}
>>> +
>>> +tun_db_entry_t *find_tun_db_entry(uint32_t ip_src,
>>> +                                 uint32_t ip_dst)
>>> +{
>>> +       tun_db_entry_t *entry = NULL;
>>> +
>>> +       /* Scan all entries and return first match */
>>> +       for (entry = tun_db->list; NULL != entry; entry = entry->next) {
>>> +               if (entry->src_ip != ip_src)
>>> +                       continue;
>>> +               if (entry->dst_ip != ip_dst)
>>> +                       continue;
>>> +               break;
>>> +       }
>>> +       return entry;
>>> +}
>>> +
>>> +void dump_sa_db(void)
>>> +{
>>> +       sa_db_entry_t *entry;
>>> +
>>> +       printf("\n"
>>> +              "Security association table (ESP Only)\n"
>>> +              "--------------------------\n");
>>> +
>>> +       for (entry = sa_db->list; NULL != entry; entry = entry->next) {
>>> +               uint32_t idx;
>>> +               char src_ip_str[MAX_STRING];
>>> +               char dst_ip_str[MAX_STRING];
>>> +               uint8_t *p = entry->key.data;
>>> +
>>> +               if (entry->alg.cipher) {
>>> +                       printf(" %s %s %s %X %d ",
>>> +                              "cipher",
>>> +                              ipv4_addr_str(src_ip_str, entry->src_ip),
>>> +                              ipv4_addr_str(dst_ip_str, entry->dst_ip),
>>> +                              entry->spi,
>>> +                              (int)entry->alg.u.cipher);
>>> +               } else {
>>> +                       printf(" %s \t\t\t\t\t %X %d ",
>>> +                              "auth",
>>> +                              entry->spi,
>>> +                              (int)entry->alg.u.auth);
>>> +               }
>>> +               /* Brute force key display */
>>> +               for (idx = 0; idx < entry->key.length; idx++)
>>> +                       printf("%02X", *p++);
>>> +
>>> +               printf("\n");
>>> +       }
>>> +}
>>> +
>>> +sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src,
>>> +                               ip_addr_range_t *dst,
>>> +                               odp_bool_t cipher)
>>> +{
>>> +       sa_db_entry_t *entry = NULL;
>>> +
>>> +       /* Scan all entries and return first match */
>>> +       for (entry = sa_db->list; NULL != entry; entry = entry->next) {
>>> +               if (cipher != entry->alg.cipher)
>>> +                       continue;
>>> +               if (!match_ip_range(entry->src_ip, src))
>>> +                       continue;
>>> +               if (!match_ip_range(entry->dst_ip, dst))
>>> +                       continue;
>>> +               break;
>>> +       }
>>> +       return entry;
>>> +}
>>> +
>>> +void dump_tun_db(void)
>>> +{
>>> +       tun_db_entry_t *entry;
>>> +
>>> +       printf("\n"
>>> +              "Tunnel table\n"
>>> +              "--------------------------\n");
>>> +
>>> +       for (entry = tun_db->list; NULL != entry; entry = entry->next) {
>>> +               char src_ip_str[MAX_STRING];
>>> +               char dst_ip_str[MAX_STRING];
>>> +               char tun_src_ip_str[MAX_STRING];
>>> +               char tun_dst_ip_str[MAX_STRING];
>>> +
>>> +               printf(" %s:%s %s:%s ",
>>> +                      ipv4_addr_str(src_ip_str, entry->src_ip),
>>> +                      ipv4_addr_str(dst_ip_str, entry->dst_ip),
>>> +                      ipv4_addr_str(tun_src_ip_str, entry->tun_src_ip),
>>> +                      ipv4_addr_str(tun_dst_ip_str, entry->tun_dst_ip)
>>> +                     );
>>> +
>>> +               printf("\n");
>>> +       }
>>> +}
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload_sa_db.h
>>> b/example/ipsec_offload/odp_ipsec_offload_sa_db.h
>>> new file mode 100644
>>> index 0000000..02b49d4
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload_sa_db.h
>>> @@ -0,0 +1,126 @@
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +#ifndef ODP_IPSEC_SA_DB_H_
>>> +#define ODP_IPSEC_SA_DB_H_
>>> +
>>> +#ifdef __cplusplus
>>> +extern "C" {
>>> +#endif
>>> +
>>> +#include <odp_ipsec_offload_misc.h>
>>> +
>>> +/**
>>> + * Security Association (SA) data base entry
>>> + */
>>> +typedef struct sa_db_entry_s {
>>> +       struct sa_db_entry_s *next;      /**< Next entry on list */
>>> +       uint32_t              src_ip;    /**< Source IPv4 address */
>>> +       uint32_t              dst_ip;    /**< Desitnation IPv4 address */
>>> +       uint32_t              spi;       /**< Security Parameter Index */
>>> +       ipsec_alg_t           alg;       /**< Cipher/auth algorithm */
>>> +       ipsec_key_t           key;       /**< Cipher/auth key */
>>> +       odp_ipsec_mode_t      mode;     /**< SA mode - transport/tun */
>>> +} sa_db_entry_t;
>>> +
>>> +/**
>>> + * Security Association (SA) data base global structure
>>> + */
>>> +typedef struct sa_db_s {
>>> +       uint32_t         index;          /**< Index of next available
>>> entry */
>>> +       sa_db_entry_t   *list;           /**< List of active entries */
>>> +       sa_db_entry_t    array[MAX_DB];  /**< Entry storage */
>>> +} sa_db_t;
>>> +
>>> +/** Initialize SA database global control structure */
>>> +void init_sa_db(void);
>>> +
>>> +/**
>>> + * Create an SA DB entry
>>> + *
>>> + * String is of the format "SrcIP:DstIP:Alg:SPI:Key"
>>> + *
>>> + * @param input  Pointer to string describing SA
>>> + * @param cipher TRUE if cipher else FALSE for auth
>>> + * @param entries number of entries
>>> + *
>>> + * @return 0 if successful else -1
>>> + */
>>> +int create_sa_db_entry(char *input, odp_bool_t cipher, int entries);
>>> +/**
>>> + * Display the SA DB
>>> + */
>>> +void dump_sa_db(void);
>>> +
>>> +/**
>>> + * Find a matching SA DB entry
>>> + *
>>> + * @param src    Pointer to source subnet/range
>>> + * @param dst    Pointer to destination subnet/range
>>> + * @param cipher TRUE if cipher else FALSE for auth
>>> + *
>>> + * @return pointer to SA DB entry else NULL
>>> + */
>>> +sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src,
>>> +                               ip_addr_range_t *dst,
>>> +                               odp_bool_t cipher);
>>> +
>>> +/**
>>> + * Tunnel entry
>>> + */
>>> +typedef struct tun_db_entry_s {
>>> +       struct tun_db_entry_s *next;
>>> +       uint32_t        src_ip;        /**< Inner Source IPv4 address */
>>> +       uint32_t        dst_ip;        /**< Inner Destination IPv4
>>> address */
>>> +       uint32_t        tun_src_ip; /**< Tunnel Source IPv4 address */
>>> +       uint32_t        tun_dst_ip; /**< Tunnel Source IPv4 address */
>>> +} tun_db_entry_t;
>>> +
>>> +/**
>>> + * Tunnel database
>>> + */
>>> +typedef struct tun_db_s {
>>> +       uint32_t         index;          /**< Index of next available
>>> entry */
>>> +       tun_db_entry_t *list;    /**< List of active entries */
>>> +       tun_db_entry_t array[MAX_DB]; /**< Entry storage */
>>> +} tun_db_t;
>>> +
>>> +/** Initialize tun database global control structure */
>>> +void init_tun_db(void);
>>> +
>>> +/**
>>> + * Create an tunnel DB entry
>>> + *
>>> + * String is of the format "SrcIP:DstIP:TunSrcIp:TunDstIp"
>>> + *
>>> + * @param input  Pointer to string describing tun
>>> + * @param entries  number of entries
>>> + *
>>> + * @return 0 if successful else -1
>>> + */
>>> +int create_tun_db_entry(char *input, int entries);
>>> +
>>> +/**
>>> + * Display the tun DB
>>> + */
>>> +void dump_tun_db(void);
>>> +
>>> +/**
>>> + * Find a matching tun DB entry
>>> + *
>>> + * @param ip_src    Inner source IP address
>>> + * @param ip_dst    Inner destination IP address
>>> + *
>>> + * @return pointer to tun DB entry else NULL
>>> + */
>>> +tun_db_entry_t *find_tun_db_entry(uint32_t ip_src,
>>> +                                 uint32_t ip_dst);
>>> +
>>> +#ifdef __cplusplus
>>> +}
>>> +#endif
>>> +
>>> +#endif
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload_sp_db.c
>>> b/example/ipsec_offload/odp_ipsec_offload_sp_db.c
>>> new file mode 100644
>>> index 0000000..9fcaaaa
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload_sp_db.c
>>> @@ -0,0 +1,166 @@
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +/* enable strtok */
>>> +#define _POSIX_C_SOURCE 200112L
>>> +
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +
>>> +#include <example_debug.h>
>>> +
>>> +#include <odp.h>
>>> +
>>> +#include <odp_ipsec_offload_sp_db.h>
>>> +
>>> +/** Global pointer to sp db */
>>> +sp_db_t *sp_db;
>>> +
>>> +void init_sp_db(void)
>>> +{
>>> +       odp_shm_t shm;
>>> +
>>> +       shm = odp_shm_reserve("shm_sp_db",
>>> +                             sizeof(sp_db_t),
>>> +                             ODP_CACHE_LINE_SIZE,
>>> +                             0);
>>> +
>>> +       sp_db = odp_shm_addr(shm);
>>> +
>>> +       if (sp_db == NULL) {
>>> +               EXAMPLE_ABORT("Error: shared mem alloc failed.\n");
>>> +       }
>>> +       memset(sp_db, 0, sizeof(*sp_db));
>>> +}
>>> +
>>> +int create_sp_db_entry(char *input, int entries)
>>> +{
>>> +       int pos = 0, count = 0;
>>> +       char *local;
>>> +       char *str;
>>> +       char *save;
>>> +       char *token;
>>> +       sp_db_entry_t *entry = &sp_db->array[sp_db->index];
>>> +
>>> +       /* Verify we have a good entry */
>>> +       if (MAX_DB <= sp_db->index)
>>> +               return -1;
>>> +
>>> +       /* Make a local copy */
>>> +       local = malloc(strlen(input) + 1);
>>> +       if (NULL == local)
>>> +               return -1;
>>> +       strcpy(local, input);
>>> +
>>> +       /* Setup for using "strtok_r" to search input string */
>>> +       str = local;
>>> +       save = NULL;
>>> +
>>> +       /* Parse tokens separated by ':' */
>>> +       while (NULL != (token = strtok_r(str, ":", &save))) {
>>> +               str = NULL;  /* reset str for subsequent strtok_r calls */
>>> +
>>> +               /* Parse token based on its position */
>>> +               switch (pos) {
>>> +               case 0:
>>> +                       parse_ipv4_string(token,
>>> +                                         &entry->src_subnet.addr,
>>> +                                         &entry->src_subnet.mask);
>>> +                       break;
>>> +               case 1:
>>> +                       parse_ipv4_string(token,
>>> +                                         &entry->dst_subnet.addr,
>>> +                                         &entry->dst_subnet.mask);
>>> +                       break;
>>> +               case 2:
>>> +                       if (0 == strcmp(token, "in"))
>>> +                               entry->input = TRUE;
>>> +                       else
>>> +                               entry->input = FALSE;
>>> +                       break;
>>> +               case 3:
>>> +                       if (0 == strcmp(token, "esp")) {
>>> +                               entry->esp = TRUE;
>>> +                       } else if (0 == strcmp(token, "ah")) {
>>> +                               entry->ah = TRUE;
>>> +                       } else if (0 == strcmp(token, "both")) {
>>> +                               entry->esp = TRUE;
>>> +                               entry->ah = TRUE;
>>> +                       }
>>> +                       break;
>>> +               default:
>>> +                       printf("ERROR: extra token \"%s\" at position
>>> %d\n",
>>> +                              token, pos);
>>> +                       break;
>>> +               }
>>> +
>>> +               /* Advance to next position */
>>> +               pos++;
>>> +       }
>>> +
>>> +       /* Verify we parsed exactly the number of tokens we expected */
>>> +       if (4 != pos) {
>>> +               printf("ERROR: \"%s\" contains %d tokens, expected 4\n",
>>> +                      input,
>>> +                      pos);
>>> +               free(local);
>>> +               return -1;
>>> +       }
>>> +
>>> +       /* Add route to the list */
>>> +       sp_db->index++;
>>> +       entry->next = sp_db->list;
>>> +       sp_db->list = entry;
>>> +       count++;
>>> +       while (count < entries) {
>>> +               sp_db_entry_t *new_entry = &sp_db->array[sp_db->index];
>>> +
>>> +               /* Verify we have a good entry */
>>> +               if (MAX_DB <= sp_db->index)
>>> +                       return -1;
>>> +
>>> +               new_entry->src_subnet.addr = entry->src_subnet.addr +
>>> count;
>>> +               new_entry->src_subnet.mask = entry->src_subnet.mask;
>>> +               new_entry->dst_subnet.addr = entry->dst_subnet.addr +
>>> count;
>>> +               new_entry->dst_subnet.mask = entry->dst_subnet.mask;
>>> +               new_entry->input = entry->input;
>>> +               new_entry->esp = entry->esp;
>>> +               new_entry->ah = entry->ah;
>>> +               /* Add route to the list */
>>> +               sp_db->index++;
>>> +               new_entry->next = sp_db->list;
>>> +               sp_db->list = new_entry;
>>> +               count++;
>>> +       }
>>> +
>>> +       free(local);
>>> +       return 0;
>>> +}
>>> +
>>> +void dump_sp_db_entry(sp_db_entry_t *entry)
>>> +{
>>> +       char src_subnet_str[MAX_STRING];
>>> +       char dst_subnet_str[MAX_STRING];
>>> +
>>> +       printf(" %s %s %s %s:%s\n",
>>> +              ipv4_subnet_str(src_subnet_str, &entry->src_subnet),
>>> +              ipv4_subnet_str(dst_subnet_str, &entry->dst_subnet),
>>> +              entry->input ? "in" : "out",
>>> +              entry->esp ? "esp" : "none",
>>> +              entry->ah ? "ah" : "none");
>>> +}
>>> +
>>> +void dump_sp_db(void)
>>> +{
>>> +       sp_db_entry_t *entry;
>>> +
>>> +       printf("\n"
>>> +              "Security policy table\n"
>>> +              "---------------------\n");
>>> +
>>> +       for (entry = sp_db->list; NULL != entry; entry = entry->next)
>>> +               dump_sp_db_entry(entry);
>>> +}
>>> diff --git a/example/ipsec_offload/odp_ipsec_offload_sp_db.h
>>> b/example/ipsec_offload/odp_ipsec_offload_sp_db.h
>>> new file mode 100644
>>> index 0000000..bc6ba1a
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/odp_ipsec_offload_sp_db.h
>>> @@ -0,0 +1,72 @@
>>> +/* Copyright (c) 2017, Linaro Limited
>>> + * All rights reserved.
>>> + *
>>> + * SPDX-License-Identifier:     BSD-3-Clause
>>> + */
>>> +
>>> +#ifndef ODP_IPSEC_SP_DB_H_
>>> +#define ODP_IPSEC_SP_DB_H_
>>> +
>>> +#ifdef __cplusplus
>>> +extern "C" {
>>> +#endif
>>> +
>>> +#include <odp_ipsec_offload_misc.h>
>>> +
>>> +/**
>>> + * Security Policy (SP) data base entry
>>> + */
>>> +typedef struct sp_db_entry_s {
>>> +       struct sp_db_entry_s *next;        /**< Next entry on list */
>>> +       ip_addr_range_t       src_subnet;  /**< Source IPv4 subnet/range
>>> */
>>> +       ip_addr_range_t       dst_subnet;  /**< Destination IPv4
>>> subnet/range */
>>> +       odp_bool_t            input;       /**< Direction when applied */
>>> +       odp_bool_t            esp;         /**< Enable cipher (ESP) */
>>> +       odp_bool_t            ah;          /**< Enable authentication
>>> (AH) */
>>> +} sp_db_entry_t;
>>> +
>>> +/**
>>> + * Security Policy (SP) data base global structure
>>> + */
>>> +typedef struct sp_db_s {
>>> +       uint32_t         index;          /**< Index of next available
>>> entry */
>>> +       sp_db_entry_t   *list;           /**< List of active entries */
>>> +       sp_db_entry_t    array[MAX_DB];  /**< Entry storage */
>>> +} sp_db_t;
>>> +
>>> +/** Global pointer to sp db */
>>> +extern sp_db_t *sp_db;
>>> +
>>> +/** Initialize SP database global control structure */
>>> +void init_sp_db(void);
>>> +
>>> +/**
>>> + * Create an SP DB entry
>>> + *
>>> + * String is of the format "SrcSubNet:DstSubNet:(in|out):(ah|esp|both)"
>>> + *
>>> + * @param input  Pointer to string describing SP
>>> + *
>>> + * @param entries  number of entries
>>> + *
>>> + * @return 0 if successful else -1
>>> + */
>>> +int create_sp_db_entry(char *input, int entries);
>>> +
>>> +/**
>>> + * Display one SP DB entry
>>> + *
>>> + * @param entry  Pointer to entry to display
>>> + */
>>> +void dump_sp_db_entry(sp_db_entry_t *entry);
>>> +
>>> +/**
>>> + * Display the SP DB
>>> + */
>>> +void dump_sp_db(void);
>>> +
>>> +#ifdef __cplusplus
>>> +}
>>> +#endif
>>> +
>>> +#endif
>>> diff --git a/example/ipsec_offload/run_left
>>> b/example/ipsec_offload/run_left
>>> new file mode 100644
>>> index 0000000..58986a2
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/run_left
>>> @@ -0,0 +1,14 @@
>>> +#!/bin/bash
>>> +#
>>> +
>>> +./odp_ipsec_offload -f 64 -i dpni.1,dpni.2 \
>>> +-r 192.168.111.2/32:dpni.1:00.10.94.00.00.02 \
>>> +-r 192.168.222.2/32:dpni.2:00.00.00.00.00.1 \
>>> +-p 192.168.111.2:192.168.222.2:out:both \
>>> +-e 192.168.111.2:192.168.222.2:aes:201:656c8523255ccc23a66c1917aa0cf309
>>> \
>>> +-a 192.168.111.2:192.168.222.2:sha256:201:a731649644c5dee92cbd9
>>> c2e7e188ee6aa0cf309a731649644c5dee92cbd9c2e \
>>> +-t 192.168.111.2:192.168.222.2:192.168.100.1:192.168.200.1 \
>>> +-p 192.168.222.2:192.168.111.2:in:both \
>>> +-e 192.168.222.2:192.168.111.2:aes:201:656c8523255ccc23a66c1917aa0cf309
>>> \
>>> +-a 192.168.222.2:192.168.111.2:sha256:201:a731649644c5dee92cbd9
>>> c2e7e188ee6aa0cf309a731649644c5dee92cbd9c2e \
>>> +-t 192.168.222.2:192.168.111.2:192.168.200.1:192.168.100.1 &
>>> diff --git a/example/ipsec_offload/run_right
>>> b/example/ipsec_offload/run_right
>>> new file mode 100644
>>> index 0000000..d49aa19
>>> --- /dev/null
>>> +++ b/example/ipsec_offload/run_right
>>> @@ -0,0 +1,14 @@
>>> +#!/bin/bash
>>> +#
>>> +
>>> +./odp_ipsec_offload -f 64 -i dpni.1,dpni.2 \
>>> +-r 192.168.111.2/32:dpni.1:00.00.00.00.00.2 \
>>> +-r 192.168.222.2/32:dpni.2:00.10.94.00.00.03 \
>>> +-p 192.168.111.2:192.168.222.2:in:both \
>>> +-e 192.168.111.2:192.168.222.2:aes:201:656c8523255ccc23a66c1917aa0cf309
>>> \
>>> +-a 192.168.111.2:192.168.222.2:sha256:201:a731649644c5dee92cbd9
>>> c2e7e188ee6aa0cf309a731649644c5dee92cbd9c2e \
>>> +-t 192.168.111.2:192.168.222.2:192.168.100.1:192.168.200.1 \
>>> +-p 192.168.222.2:192.168.111.2:out:both \
>>> +-e 192.168.222.2:192.168.111.2:aes:201:656c8523255ccc23a66c1917aa0cf309
>>> \
>>> +-a 192.168.222.2:192.168.111.2:sha256:201:a731649644c5dee92cbd9
>>> c2e7e188ee6aa0cf309a731649644c5dee92cbd9c2e \
>>> +-t 192.168.222.2:192.168.111.2:192.168.200.1:192.168.100.1 &
>>> --
>>> 2.9.3
>>>
>>>
>>

Reply via email to