diff mbox

[PATCHv2] example:ipsec_offload: Adding ipsec_offload example

Message ID 20170222122705.4960-1-nikhil.agarwal@linaro.org
State Superseded
Headers show

Commit Message

Nikhil Agarwal Feb. 22, 2017, 12:27 p.m. UTC
Signed-off-by: Nikhil Agarwal <nikhil.agarwal@linaro.org>

---
 example/Makefile.am                              |   1 +
 example/ipsec_offload/.gitignore                 |   1 +
 example/ipsec_offload/Makefile.am                |  19 +
 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 +
 example/m4/configure.m4                          |   1 +
 16 files changed, 2677 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

-- 
2.9.3

Comments

Maxim Uvarov Feb. 22, 2017, 10:27 a.m. UTC | #1
please do not define internal defines with ODP_ prefixes. We use that
prefixes for API calls.


Maxim.

On 22 February 2017 at 15:27, Nikhil Agarwal <nikhil.agarwal@linaro.org>
wrote:

> Signed-off-by: Nikhil Agarwal <nikhil.agarwal@linaro.org>

> ---

>  example/Makefile.am                              |   1 +

>  example/ipsec_offload/.gitignore                 |   1 +

>  example/ipsec_offload/Makefile.am                |  19 +

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

>  example/m4/configure.m4                          |   1 +

>  16 files changed, 2677 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..a61b923

> --- /dev/null

> +++ b/example/ipsec_offload/Makefile.am

> @@ -0,0 +1,19 @@

> +include $(top_srcdir)/example/Makefile.inc

> +

> +bin_PROGRAMS = odp_ipsec_offload$(EXEEXT)

> +odp_ipsec_offload_LDFLAGS = $(AM_LDFLAGS) -static

> +odp_ipsec_offload_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example

> +

> +noinst_HEADERS = \

> +                 $(top_srcdir)/example/ipsec_offload/odp_ipsec_offload_cache.h

> \

> +                 $(top_srcdir)/example/ipsec_

> offload/odp_ipsec_offload_fwd_db.h \

> +                 $(top_srcdir)/example/ipsec_offload/odp_ipsec_offload_misc.h

> \

> +                 $(top_srcdir)/example/ipsec_

> offload/odp_ipsec_offload_sa_db.h \

> +                 $(top_srcdir)/example/ipsec_

> offload/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"

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

> +              "     -p 192.168.111.0/24:192.168.222.0/24:out:esp\n"

> +              "     -e 192.168.111.2:192.168.222.2:3des:201:

> 656c8523255ccc23a66c1917aa0cf30991fce83532a4b224\n"

> +              "     -a 192.168.111.2:192.168.222.2:md5: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:a731649644c5dee92cbd9c2e7e188e

> e6aa0cf309a731649644c5dee92cbd9c2e \

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

> e6aa0cf309a731649644c5dee92cbd9c2e \

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

> e6aa0cf309a731649644c5dee92cbd9c2e \

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

> e6aa0cf309a731649644c5dee92cbd9c2e \

> +-t 192.168.222.2:192.168.111.2:192.168.200.1:192.168.100.1 &

> diff --git a/example/m4/configure.m4 b/example/m4/configure.m4

> index 620db04..03c006a 100644

> --- a/example/m4/configure.m4

> +++ b/example/m4/configure.m4

> @@ -14,6 +14,7 @@ AC_CONFIG_FILES([example/classifier/Makefile

>                  example/generator/Makefile

>                  example/hello/Makefile

>                  example/ipsec/Makefile

> +                example/ipsec_offload/Makefile

>                  example/l2fwd_simple/Makefile

>                  example/l3fwd/Makefile

>                  example/packet/Makefile

> --

> 2.9.3

>

>
Dmitry Eremin-Solenikov March 8, 2017, noon UTC | #2
Let's get some final changes, so that we can hopefully merge this patch

On 22.02.2017 15:27, Nikhil Agarwal wrote:
> Signed-off-by: Nikhil Agarwal <nikhil.agarwal@linaro.org>

> ---


[skipped]

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


This needs #include <inttypes.h>

> +

> +	/* Resolve any routes using this interface for output */

> +	resolve_fwd_db(intf, pktout, src_mac);

> +}

> +


[skipped]

> +

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


This should be odp_event_free()

> +				continue;

> +			}

> +

> +			if (odp_unlikely(res.status.all)) {

> +				odp_packet_free((odp_packet_t)ev);


I'm unsure here. event should be already freed by odp_isec_result(). So
you should only free packets from result, not the event.

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


Void pointer arithmetics is an extension. Could you please change that
to uint8_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);


And here also just free packet, not an event.

> +		} else {

> +			EXAMPLE_DBG("Invalid Event\n");

> +			odp_packet_free((odp_packet_t)ev);


And here of course just call odp_event_free(), not packet_free!

> +			continue;

> +		}

> +	}

> +


[skipped]

> +/**

> + * Hash calculation utility

> + */

> +#define JHASH_GOLDEN_RATIO	0x9e3779b9

> +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))

> +#define ODP_BJ3_MIX(a, b, c) \


This is what Maxim wrote about. Could you please rename these two
defines so that they don't pollute ODP_ namespace.

> +{ \

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


[skipped]

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


ODP_CIPHER_ALG_AES_CBC?

> +		    (KEY_BITS_AES == key_bits_in))

> +			key->length = key_bits_in / 8;

> +	} else {

> +		if ((alg->u.auth == ODP_AUTH_ALG_MD5_96) &&


ODP_AUTH_ALG_MD5_HMAC

> +		    (KEY_BITS_MD5_96 == key_bits_in))

> +			key->length = key_bits_in / 8;

> +		if ((alg->u.auth == ODP_AUTH_ALG_SHA1_96) &&


ODP_AUTH_ALG_SHA1_HMAC

> +		    (KEY_BITS_SHA1_96 == key_bits_in))

> +			key->length = key_bits_in / 8;

> +		if ((alg->u.auth == ODP_AUTH_ALG_SHA256_128) &&


OD_AUTH_ALG_SHA256_HMAC

> +		    (KEY_BITS_SHA2_256 == key_bits_in))

> +			key->length = key_bits_in / 8;

> +	}

> +


[skipped]

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


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


ODP_AUTH_ALG_MD5_HMAC


> +				} else if (0 == strcmp(token, "sha1")) {

> +					entry->alg.u.auth =

> +						ODP_AUTH_ALG_SHA1_96;


ODP_AUTH_ALG_SHA1_HMAC


> +				} else if (0 == strcmp(token, "sha256")) {

> +					entry->alg.u.auth =

> +						ODP_AUTH_ALG_SHA256_128;


ODP_AUTH_ALG_SHA256_HMAC

> +				} else {

> +					entry->alg.u.auth = ODP_AUTH_ALG_NULL;

> +				}

> +			}

> +			break;


-- 
With best wishes
Dmitry
diff mbox

Patch

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..a61b923
--- /dev/null
+++ b/example/ipsec_offload/Makefile.am
@@ -0,0 +1,19 @@ 
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_ipsec_offload$(EXEEXT)
+odp_ipsec_offload_LDFLAGS = $(AM_LDFLAGS) -static
+odp_ipsec_offload_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+
+noinst_HEADERS = \
+		  $(top_srcdir)/example/ipsec_offload/odp_ipsec_offload_cache.h \
+		  $(top_srcdir)/example/ipsec_offload/odp_ipsec_offload_fwd_db.h \
+		  $(top_srcdir)/example/ipsec_offload/odp_ipsec_offload_misc.h \
+		  $(top_srcdir)/example/ipsec_offload/odp_ipsec_offload_sa_db.h \
+		  $(top_srcdir)/example/ipsec_offload/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"
+	       "         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"
+	       "     -p 192.168.111.0/24:192.168.222.0/24:out:esp\n"
+	       "     -e 192.168.111.2:192.168.222.2:3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224\n"
+	       "     -a 192.168.111.2:192.168.222.2:md5: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:a731649644c5dee92cbd9c2e7e188ee6aa0cf309a731649644c5dee92cbd9c2e \
+-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:a731649644c5dee92cbd9c2e7e188ee6aa0cf309a731649644c5dee92cbd9c2e \
+-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:a731649644c5dee92cbd9c2e7e188ee6aa0cf309a731649644c5dee92cbd9c2e \
+-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:a731649644c5dee92cbd9c2e7e188ee6aa0cf309a731649644c5dee92cbd9c2e \
+-t 192.168.222.2:192.168.111.2:192.168.200.1:192.168.100.1 &
diff --git a/example/m4/configure.m4 b/example/m4/configure.m4
index 620db04..03c006a 100644
--- a/example/m4/configure.m4
+++ b/example/m4/configure.m4
@@ -14,6 +14,7 @@  AC_CONFIG_FILES([example/classifier/Makefile
 		 example/generator/Makefile
 		 example/hello/Makefile
 		 example/ipsec/Makefile
+		 example/ipsec_offload/Makefile
 		 example/l2fwd_simple/Makefile
 		 example/l3fwd/Makefile
 		 example/packet/Makefile