diff mbox

[PATCHv2] IPsec example application

Message ID C1BBCB674C57E643932FC661E0B0D4660EDE6A0C@xmb-aln-x01.cisco.com
State New
Headers show

Commit Message

Robbie King June 19, 2014, 6:29 p.m. UTC
Hi Mike, how hard would it be to have a “ping through” test be part of the regression?
It would require the UUT to have two interfaces available to the ODP application, and
a device connected to each interface (one on the “clear” side, the other on the “crypto”
side).

From: Mike Holmes [mailto:mike.holmes@linaro.org]

Sent: Friday, June 06, 2014 11:05 AM
To: Robbie King (robking)
Cc: lng-odp-forward
Subject: Re: [lng-odp] [PATCHv2] IPsec example application

Robbie

When we get done I would like to be able to take the standalone version as a regression test given the simplicity of running it, and in addition the real application can be in the repo as an example.

Mike

On 6 June 2014 10:41, Robbie King <robking@cisco.com<mailto:robking@cisco.com>> wrote:
Last update before sprint.  I have added the standalone support for
packet generation and verification, fixed several issues found along
the way.  Please do not consider this patch for GIT, just want anyone
working on the sprint for next week to have the standalone support.

Have added several scripts for running standalone tests.

Signed-off-by: Robbie King <robking@cisco.com<mailto:robking@cisco.com>>

---
 include/helper/odp_ipsec.h     |   73 +
 test/ipsec/Makefile            |   44 +
 test/ipsec/README              |  164 +++
 test/ipsec/odp_example_ipsec.c | 3120 ++++++++++++++++++++++++++++++++++++++++
 test/ipsec/run_ah_in.sh        |   13 +
 test/ipsec/run_ah_out.sh       |   12 +
 test/ipsec/run_both_in.sh      |   14 +
 test/ipsec/run_both_out.sh     |   14 +
 test/ipsec/run_esp_in.sh       |   13 +
 test/ipsec/run_esp_out.sh      |   13 +
 test/ipsec/run_simple.sh       |   10 +
 11 files changed, 3490 insertions(+)
 create mode 100644 include/helper/odp_ipsec.h
 create mode 100644 test/ipsec/Makefile
 create mode 100644 test/ipsec/README
 create mode 100644 test/ipsec/odp_example_ipsec.c
 create mode 100755 test/ipsec/run_ah_in.sh
 create mode 100755 test/ipsec/run_ah_out.sh
 create mode 100755 test/ipsec/run_both_in.sh
 create mode 100755 test/ipsec/run_both_out.sh
 create mode 100755 test/ipsec/run_esp_in.sh
 create mode 100755 test/ipsec/run_esp_out.sh
 create mode 100755 test/ipsec/run_simple.sh

--
1.9.2


_______________________________________________
lng-odp mailing list
lng-odp@lists.linaro.org<mailto:lng-odp@lists.linaro.org>
http://lists.linaro.org/mailman/listinfo/lng-odp



--
Mike Holmes
Linaro Technical Manager / Lead
LNG - ODP

Comments

Mike Holmes June 19, 2014, 7:28 p.m. UTC | #1
In principle very easy.

The lab has this configuration after the latest changes are made this week
to support demo configurations - no vlan capability yet, Steve is working
on it that upgrade to LAVA.
https://docs.google.com/a/linaro.org/drawings/d/1v1mFO6P-nSJEdDFgwYkl6xb5wj59XXZ8lao02gbU2Oc/edit

So the issue is just the list of devices which have two interfaces free +
one to deploy the job over in LAVA (left side of the diagram)

   - Would X86 suffice ?
   - KS2 would be ideal, but I imagine we can only get another interface by
   adding a USB dongle, the expansion board for KS2 was not developed fully I
   believe.
   - D01 is not up fully yet.
   - Amarillo still only has the close source ODP implementation, and it
   will differ from your pure application due to the changes forced by its
   protocol offload model.
   - We have no access to the APM HW.
   - Calcedia has folded so there is no midway support.
   - Arndale can have two ports, we just add another via USB, but it has no
   HW acceleration.

So I think KS2 + USB dongle is the only way to get ARM + HW acceleration,
or we go with X86.

Mike


On 19 June 2014 14:29, Robbie King (robking) <robking@cisco.com> wrote:

>  Hi Mike, how hard would it be to have a “ping through” test be part of
> the regression?
>
> It would require the UUT to have two interfaces available to the ODP
> application, and
>
> a device connected to each interface (one on the “clear” side, the other
> on the “crypto”
>
> side).
>
>
>
> *From:* Mike Holmes [mailto:mike.holmes@linaro.org]
> *Sent:* Friday, June 06, 2014 11:05 AM
> *To:* Robbie King (robking)
> *Cc:* lng-odp-forward
> *Subject:* Re: [lng-odp] [PATCHv2] IPsec example application
>
>
>
> Robbie
>
>
>
> When we get done I would like to be able to take the standalone version as
> a regression test given the simplicity of running it, and in addition the
> real application can be in the repo as an example.
>
>
>
> Mike
>
>
>
> On 6 June 2014 10:41, Robbie King <robking@cisco.com> wrote:
>
> Last update before sprint.  I have added the standalone support for
> packet generation and verification, fixed several issues found along
> the way.  Please do not consider this patch for GIT, just want anyone
> working on the sprint for next week to have the standalone support.
>
> Have added several scripts for running standalone tests.
>
> Signed-off-by: Robbie King <robking@cisco.com>
> ---
>  include/helper/odp_ipsec.h     |   73 +
>  test/ipsec/Makefile            |   44 +
>  test/ipsec/README              |  164 +++
>  test/ipsec/odp_example_ipsec.c | 3120
> ++++++++++++++++++++++++++++++++++++++++
>  test/ipsec/run_ah_in.sh        |   13 +
>  test/ipsec/run_ah_out.sh       |   12 +
>  test/ipsec/run_both_in.sh      |   14 +
>  test/ipsec/run_both_out.sh     |   14 +
>  test/ipsec/run_esp_in.sh       |   13 +
>  test/ipsec/run_esp_out.sh      |   13 +
>  test/ipsec/run_simple.sh       |   10 +
>  11 files changed, 3490 insertions(+)
>  create mode 100644 include/helper/odp_ipsec.h
>  create mode 100644 test/ipsec/Makefile
>  create mode 100644 test/ipsec/README
>  create mode 100644 test/ipsec/odp_example_ipsec.c
>  create mode 100755 test/ipsec/run_ah_in.sh
>  create mode 100755 test/ipsec/run_ah_out.sh
>  create mode 100755 test/ipsec/run_both_in.sh
>  create mode 100755 test/ipsec/run_both_out.sh
>  create mode 100755 test/ipsec/run_esp_in.sh
>  create mode 100755 test/ipsec/run_esp_out.sh
>  create mode 100755 test/ipsec/run_simple.sh
>
> diff --git a/include/helper/odp_ipsec.h b/include/helper/odp_ipsec.h
> new file mode 100644
> index 0000000..9d29651
> --- /dev/null
> +++ b/include/helper/odp_ipsec.h
> @@ -0,0 +1,73 @@
> +/* Copyright (c) 2014, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +
> +/**
> + * @file
> + *
> + * ODP IPSec headers
> + */
> +
> +#ifndef ODP_IPSEC_H_
> +#define ODP_IPSEC_H_
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#include <odp_std_types.h>
> +#include <odp_byteorder.h>
> +#include <odp_align.h>
> +#include <odp_debug.h>
> +
> +#define ODP_ESPHDR_LEN      8    /**< IPSec ESP header length */
> +#define ODP_ESPTRL_LEN      2    /**< IPSec ESP trailer length */
> +#define ODP_AHHDR_LEN      12    /**< IPSec AH header length */
> +
> +/**
> + * IPSec ESP header
> + */
> +typedef struct ODP_PACKED {
> +       uint32be_t spi;      /**< Security Parameter Index */
> +       uint32be_t seq_no;   /**< Sequence Number */
> +       uint8_t    iv[0];    /**< Initialization vector */
> +} odp_esphdr_t;
> +
> +/** @internal Compile time assert */
> +ODP_ASSERT(sizeof(odp_esphdr_t) == ODP_ESPHDR_LEN,
> ODP_ESPHDR_T__SIZE_ERROR);
> +
> +/**
> + * IPSec ESP trailer
> + */
> +typedef struct ODP_PACKED {
> +       uint8_t pad_len;      /**< Padding length (0-255) */
> +       uint8_t next_header;  /**< Next header protocol */
> +       uint8_t icv[0];       /**< Integrity Check Value (optional) */
> +} odp_esptrl_t;
> +
> +/** @internal Compile time assert */
> +ODP_ASSERT(sizeof(odp_esptrl_t) == ODP_ESPTRL_LEN,
> ODP_ESPTRL_T__SIZE_ERROR);
> +
> +/**
> + * IPSec AH header
> + */
> +typedef struct ODP_PACKED {
> +       uint8_t    next_header;  /**< Next header protocol */
> +       uint8_t    ah_len;       /**< AH header length */
> +       uint16be_t pad;          /**< Padding (must be 0) */
> +       uint32be_t spi;          /**< Security Parameter Index */
> +       uint32be_t seq_no;       /**< Sequence Number */
> +       uint8_t    icv[0];       /**< Integrity Check Value */
> +} odp_ahhdr_t;
> +
> +/** @internal Compile time assert */
> +ODP_ASSERT(sizeof(odp_ahhdr_t) == ODP_AHHDR_LEN, ODP_AHHDR_T__SIZE_ERROR);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif
> diff --git a/test/ipsec/Makefile b/test/ipsec/Makefile
> new file mode 100644
> index 0000000..be822b5
> --- /dev/null
> +++ b/test/ipsec/Makefile
> @@ -0,0 +1,44 @@
> +# Copyright (c) 2013, Linaro Limited
> +# All rights reserved.
> +#
> +# SPDX-License-Identifier:     BSD-3-Clause
> +
> +ODP_ROOT = ../..
> +ODP_APP  = odp_ipsec
> +
> +include $(ODP_ROOT)/Makefile.inc
> +include ../Makefile.inc
> +
> +OBJS     =
> +OBJS    += $(OBJ_DIR)/odp_example_ipsec.o
> +
> +DEPS     = $(OBJS:.o=.d)
> +
> +.PHONY: default
> +default: $(OBJ_DIR) $(ODP_APP)
> +
> +-include $(DEPS)
> +
> +#
> +# Compile rules
> +#
> +$(OBJ_DIR)/%.o: %.c
> +       $(ECHO) Compiling $<
> +       $(CC) -c -MD $(EXTRA_CFLAGS) $(CFLAGS) -o $@ $<
> +
> +#
> +# Link rule
> +#
> +$(ODP_APP): $(ODP_LIB) $(OBJS)
> +       $(CC) $(LDFLAGS) $(OBJS) $(ODP_LIB) $(STD_LIBS) -lcrypto -o $@
> +
> +.PHONY: clean
> +clean:
> +       $(RMDIR) $(OBJ_DIR)
> +       $(RM) $(ODP_APP)
> +       $(MAKE) -C $(ODP_DIR) clean
> +
> +.PHONY: install
> +install:
> +       install -d $(DESTDIR)/share/odp
> +       install -m 0755 $(ODP_APP) $(DESTDIR)/share/odp/
> diff --git a/test/ipsec/README b/test/ipsec/README
> new file mode 100644
> index 0000000..7b9dcc3
> --- /dev/null
> +++ b/test/ipsec/README
> @@ -0,0 +1,164 @@
> +1. Intro
> +
> +The IPsec example application "odp_ipsec" functions as a simple L3 IPv4
> router
> +with support IPsec 3DES cipher and HMAC-MD5 authentication in both the
> transmit
> +and receive directions.  Note that only IPsec "transport" mode is
> supported.
> +
> +2. Prerequisites
> +
> +  2.1 SSL development libraries
> +
> +Development has been done to this point with the openssl-devel libraries,
> +the makefile specifically links with "-lcrypto".
> +
> +3. Topology
> +
> +The following test topology was used for development.  Each of the VMs
> +is running Fedora16.  Sanity testing consists of pinging VM2 from VM0
> +such that the packets traverse VM1.  Packets between VM1 and VM2 are
> +IPsec AH and ESP encapsulated.
> +
> +     VM0                            VM1 (UUT)                          VM2
> ++------------+                  +--------------+
>  +------------+
> +|            |     (clear)      |              |     (crypto)     |
>      |
> +|            |      subnet      |              |      subnet      |
>      |
> +|       p7p1 |<---------------->| p7p1    p8p1 |<---------------->| p7p1
>       |
> +|        .2  |   192.168.111.0  |  .1      .1  |   192.168.222.0  |  .2
>      |
> +|            |                  |              |                  |
>      |
> ++------------+                  +--------------+
>  +------------+
> +
> +4. VM configurations
> +
> +  4.1 VM0 configuration
> +
> +VM0 has the follwing interface configuration:
> +
> +         cat /etc/sysconfig/network-scripts/ifcfg-p7p1
> +         DEVICE=p7p1
> +         HWADDR=08:00:27:76:B5:E0
> +         BOOTPROTO=static
> +         IPADDR=192.168.111.2
> +         NETMASK=255.255.255.0
> +         ONBOOT=yes
> +
> +In addition, static ARP and IPv4 routes must be added on VM0:
> +
> +         sudo ip route add 192.168.222.0/24 via 192.168.111.1
> +         sudo ip route add 192.168.222.0/24 via 192.168.111.1
> +
> +  4.2 VM1 configuration
> +
> +For the unit under test, IP forwarding and IP tables were disabled.
> +
> +VM1 has the follwing interface configurations:
> +
> +         cat /etc/sysconfig/network-scripts/ifcfg-p7p1
> +         DEVICE=p7p1
> +         HWADDR=08:00:27:04:BF:8C
> +         BOOTPROTO=static
> +         IPADDR=192.168.111.1
> +         NETMASK=255.255.255.0
> +         ONBOOT=yes
> +
> +         cat /etc/sysconfig/network-scripts/ifcfg-p8p1
> +         DEVICE=p8p1
> +         HWADDR=08:00:27:4C:55:CC
> +         BOOTPROTO=static
> +         IPADDR=192.168.222.1
> +         NETMASK=255.255.255.0
> +         ONBOOT=yes
> +
> +The application is launched on VM1 with the following command line
> +using a bash script:
> +
> +         cat test/ipsec/run_test.sh
> +         #!/bin/bash
> +         sudo ./odp_ipsec -i p7p1,p8p1 \
> +         -r 192.168.111.2/32:p7p1:08.00.27.76.B5.E0 \
> +         -r 192.168.222.2/32:p8p1:08.00.27.F5.8B.DB \
> +         -p 192.168.111.0/24:192.168.222.0/24:out:both \
> +         -e 192.168.111.2:192.168.222.2:3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224
> \
> +         -a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6
> \
> +         -p 192.168.222.0/24:192.168.111.0/24:in:both \
> +         -e 192.168.222.2:192.168.111.2:3des:301:c966199f24d095f3990a320d749056401e82b26570320292
> \
> +         -a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8
> \
> +         -c 2 -f 0 -m 0
> +
> +  4.3 VM2 configuration
> +
> +VM2 must be setup with an IPsec configuration complementing
> +the configuration used by the "odp_ipsec" application running
> +on VM1.  The configuration is applied using "setkey"
> +
> +VM2 has the following setkey configuration file applied:
> +
> +         cat /media/sf_SharedVM2/setkey_vm2.txt
> +         #!/sbin/setkey -f
> +
> +         # Flush the SAD and SPD
> +         flush;
> +         spdflush;
> +
> +         add 192.168.111.2 192.168.222.2 ah 0x200 -A hmac-md5
> +         0xa731649644c5dee92cbd9c2e7e188ee6;
> +         add 192.168.222.2 192.168.111.2 ah 0x300 -A hmac-md5
> +         0x27f6d123d7077b361662fc6e451f65d8;
> +
> +         add 192.168.111.2 192.168.222.2 esp 0x201 -E 3des-cbc
> +         0x656c8523255ccc23a66c1917aa0cf30991fce83532a4b224;
> +         add 192.168.222.2 192.168.111.2 esp 0x301 -E 3des-cbc
> +         0xc966199f24d095f3990a320d749056401e82b26570320292;
> +
> +         spdadd 192.168.111.2 192.168.222.2 any -P in ipsec
> +                    esp/transport//require
> +                    ah/transport//require;
> +
> +         spdadd 192.168.222.2 192.168.111.2 any -P out ipsec
> +                    esp/transport//require
> +                    ah/transport//require;
> +
> +VM2 has the follwing interface configuration:
> +
> +         cat /etc/sysconfig/network-scripts/ifcfg-p7p1
> +         DEVICE=p7p1
> +         HWADDR=08:00:27:F5:8B:DB
> +         BOOTPROTO=static
> +         IPADDR=192.168.222.2
> +         NETMASK=255.255.255.0
> +         ONBOOT=yes
> +
> +In addition, static ARP and IPv4 routes must be added on VM2:
> +
> +         sudo ip route add 192.168.111.0/24 via 192.168.222.1
> +         sudo arp -s 192.168.222.1 08:00:27:4c:55:cc
> +
> +5. Sanity Test with Real Traffic
> +
> +Once all three VMs have been configured and static ARP and route
> +entries added, VM0 should be able to ping VM2 at the 192.168.222.2
> +address.
> +
> +At VM0 console issue the ping to VM2's address:
> +
> +         sudo ping -c 2 -i 0.1 192.168.222.2
> +         PING 192.168.222.2 (192.168.222.2) 56(84) bytes of data.
> +         64 bytes from 192.168.222.2: icmp_req=1 ttl=64 time=33.9 ms
> +         64 bytes from 192.168.222.2: icmp_req=2 ttl=64 time=23.3 ms
> +
> +At VM2 console use tcpdump to observe IPsec packets :
> +
> +         sudo tcpdump -nt -i p7p1
> +         tcpdump: verbose output suppressed, use -v or -vv for full
> protocol decode
> +         listening on p7p1, link-type EN10MB (Ethernet), capture size
> 65535 bytes
> +
> +         IP 192.168.111.2 > 192.168.222.2: AH(spi=0x00000200,seq=0x6):
> ESP(spi=0x00000201,seq=0x6), length 88
> +         IP 192.168.222.2 > 192.168.111.2: AH(spi=0x00000300,seq=0x7a):
> ESP(spi=0x00000301,seq=0x7a), length 88
> +         IP 192.168.111.2 > 192.168.222.2: AH(spi=0x00000200,seq=0x7):
> ESP(spi=0x00000201,seq=0x7), length 88
> +         IP 192.168.222.2 > 192.168.111.2: AH(spi=0x00000300,seq=0x7b):
> ESP(spi=0x00000301,seq=0x7b), length 88
> +
> +6. Standalone Loopback Tests
> +
> +BASH batch files are now included to run several simple loopback tests
> that
> +do not require any packet IO.  The scripts create internal "loopback" (not
> +real Linux loopback interfaces but simply ODP queues) as opposed to packet
> +interfaces.
> diff --git a/test/ipsec/odp_example_ipsec.c
> b/test/ipsec/odp_example_ipsec.c
> new file mode 100644
> index 0000000..16fd14e
> --- /dev/null
> +++ b/test/ipsec/odp_example_ipsec.c
> @@ -0,0 +1,3120 @@
> +/* Copyright (c) 2013, Linaro Limited
> + * All rights reserved.
> + *
> + * SPDX-License-Identifier:     BSD-3-Clause
> + */
> +
> +/**
> + * @file
> + *
> + * @example odp_example_ipsec.c  ODP basic packet IO cross connect with
> IPsec test application
> + */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +#include <getopt.h>
> +#include <unistd.h>
> +
> +#include <odp.h>
> +#include <odp_align.h>
> +#include <odp_crypto.h>
> +#include <helper/odp_linux.h>
> +#include <helper/odp_packet_helper.h>
> +#include <helper/odp_eth.h>
> +#include <helper/odp_ip.h>
> +#include <helper/odp_icmp.h>
> +#include <helper/odp_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 <openssl/des.h>
> +#include <openssl/rand.h>
> +#include <openssl/hmac.h>
> +#include <openssl/evp.h>
> +
> +#define boolean bool
> +#define TRUE  1
> +#define FALSE 0
> +
> +#define MAX_WORKERS            32
> +#define SHM_PKT_POOL_SIZE      (1 * 1024 * 4096)
> +#define SHM_PKT_POOL_BUF_SIZE  4096
> +#define MAX_PKT_BURST          16
> +
> +#define MAX_DB 32
> +
> +
> +#define LOOP_DEQ_MULTIPLE     0     /**< enable multi packet dequeue */
> +#define MAX_LOOPBACK          10
> +
> +#define MSG_POOL_SIZE         (1024 * 1024) /**< Message pool size */
> +#define MSG_POOL_BUF_SIZE     (1024)
> +
> +#define STREAM_MAGIC 0xBABE01234567CAFE
> +
> +/**
> + * Parsed command line application arguments
> + */
> +typedef struct {
> +       int core_count;
> +       int if_count;           /**< Number of interfaces to be used */
> +       char **if_names;        /**< Array of pointers to interface names
> */
> +       int type;               /**< Packet IO type */
> +       int fanout;             /**< Packet IO fanout */
> +       int async;              /**< Crypto API preferred mode (1 = async)
> */
> +       odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
> +} appl_args_t;
> +
> +/**
> + * Thread specific arguments (currently none, leave as placeholder)
> + */
> +typedef struct {
> +} thread_args_t;
> +
> +/**
> + * Grouping of both parsed CL args and thread specific args - alloc
> together
> + */
> +typedef struct {
> +       /** Application (parsed) arguments */
> +       appl_args_t appl;
> +       /** Thread specific arguments */
> +       thread_args_t thread[MAX_WORKERS];
> +} args_t;
> +
> +/**
> + * Stream packet header
> + */
> +typedef struct ODP_PACKED stream_pkt_hdr_s {
> +       uint64be_t magic;    /**< Stream magic value for verification */
> +       uint8_t    data[0];  /**< Incrementing data stream */
> +} stream_pkt_hdr_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 crypto session creation completion events */
> +static odp_buffer_pool_t msg_pool = ODP_BUFFER_POOL_INVALID;
> +
> +/** ATOMIC queue for IPsec sequence number assignment */
> +static odp_queue_t seqnumq;
> +
> +/** ORDERED queue (eventually) for per packet crypto API completion
> events */
> +static odp_queue_t completionq;
> +
> +/** IPv4 helpers for data length and uint8t pointer */
> +#define ipv4_data_len(ip) (odp_be_to_cpu_16(ip->tot_len) -
> sizeof(odp_ipv4hdr_t))
> +#define ipv4_data_p(ip) ((uint8_t *)((odp_ipv4hdr_t *)ip + 1))
> +
> +/** Helper for calculating encode length using data length and block size
> */
> +#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b)
> +
> +/** 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))
> +
> +/** Synchronize threads before packet processing begins */
> +static odp_barrier_t sync_barrier;
> +
> +/**
> + * 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;
> +
> +/**
> + * Packet processing states/steps
> + */
> +typedef enum {
> +       PKT_STATE_INPUT_VERIFY,        /**< Verify IPv4 and ETH */
> +       PKT_STATE_IPSEC_IN_CLASSIFY,   /**< Initiate input IPsec */
> +       PKT_STATE_IPSEC_IN_FINISH,     /**< Finish input IPsec */
> +       PKT_STATE_ROUTE_LOOKUP,        /**< Use DST IP to find output IF */
> +       PKT_STATE_IPSEC_OUT_CLASSIFY,  /**< Intiate output IPsec */
> +       PKT_STATE_IPSEC_OUT_SEQ,       /**< Assign IPsec sequence numbers
> */
> +       PKT_STATE_IPSEC_OUT_FINISH,    /**< Finish output IPsec */
> +       PKT_STATE_TRANSMIT,            /**< Send packet to output IF queue
> */
> +} pkt_state_e;
> +
> +/**
> + * 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;
> +
> +/**
> + * Per packet IPsec processing context
> + */
> +typedef struct {
> +       uint8_t  ip_tos;         /**< Saved IP TOS value */
> +       uint16_t ip_frag_offset; /**< Saved IP flags value */
> +       uint8_t  ip_ttl;         /**< Saved IP TTL value */
> +       int      hdr_len;        /**< Length of IPsec headers */
> +       int      trl_len;        /**< Length of IPsec trailers */
> +       uint16_t ah_offset;      /**< Offset of AH header from buffer
> start */
> +       uint16_t esp_offset;     /**< Offset of ESP header from buffer
> start */
> +
> +       /* Output only */
> +       struct odp_crypto_op_params params;  /**< Parameters for crypto
> call */
> +       uint32_t *ah_seq;                    /**< AH sequence number
> location */
> +       uint32_t *esp_seq;                   /**< ESP sequence number
> location */
> +} ipsec_ctx_t;
> +
> +/**
> + * Per packet processing context
> + */
> +typedef union {
> +       struct {
> +               pkt_state_e state;   /**< Next processing step */
> +               ipsec_ctx_t ipsec;   /**< IPsec specific context */
> +               odp_queue_t outq;    /**< transmit queue */
> +       };
> +       uint8_t pad[256];            /**< Ensure no overlap with crypto */
> +} pkt_ctx_t;
> +
> +/**
> + * Adjust IPv4 length
> + *
> + * @param ip   Pointer to IPv4 header
> + * @param adj  Signed adjustment value
> + */
> +static
> +void ipv4_adjust_len(odp_ipv4hdr_t *ip, int adj)
> +{
> +       ip->tot_len = odp_cpu_to_be_16(odp_be_to_cpu_16(ip->tot_len) +
> adj);
> +}
> +
> +/**
> + * Get per packet processing context from packet buffer
> + *
> + * This is a hack for the moment to place it at the end of the
> + * buffer.  Note that the crypto library also has a similar hack
> + * so we space our packet context 256 bytes from the end to prevent
> + * overlap.
> + *
> + * @param buf  Buffer associated with packet
> + *
> + * @return pointer to context area
> + */
> +static
> +pkt_ctx_t *get_ctx_from_buf(odp_buffer_t buf)
> +{
> +       uint8_t   *temp;
> +       pkt_ctx_t *ctx;
> +
> +       /*
> +        * Setup packet context,
> +        */
> +       temp  = odp_buffer_addr(buf);
> +       temp += odp_buffer_size(buf);
> +       temp -= sizeof(*ctx);
> +       ctx = (pkt_ctx_t *)(void *)temp;
> +       return ctx;
> +}
> +
> +/**
> + * 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
> +int match_ip_range(uint32_t addr, ip_addr_range_t *range)
> +{
> +       return (range->addr == (addr & range->mask));
> +}
> +
> +/**
> + * Query MAC address associated with an interface
> + *
> + * @param intf    String name of the interface
> + * @param src_mac MAC address used by the interface
> + *
> + * @return 0 if successful else -1
> + */
> +static
> +int query_mac_address(char *intf, uint8_t *src_mac)
> +{
> +       int sd;
> +       struct ifreq ifr;
> +
> +       /* Get a socket descriptor */
> +       sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
> +       if (sd < 0) {
> +               ODP_ERR("Error: socket() failed for %s\n", intf);
> +               return -1;
> +       }
> +
> +       /* Use ioctl() to look up interface name and get its MAC address */
> +       memset(&ifr, 0, sizeof(ifr));
> +       snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", intf);
> +       if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) {
> +               ODP_ERR("Error: ioctl() failed for %s\n", intf);
> +               return -1;
> +       }
> +       memcpy(src_mac, ifr.ifr_hwaddr.sa_data, 6);
> +
> +       /* Fini */
> +       close(sd);
> +
> +       return 0;
> +}
> +
> +/**
> + * 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 cipher     TRUE indicates cipher key else authentication
> + * @param alg        Cipher/authentication algorithm associated with the
> key
> + *
> + * @return 0 if successful else -1
> + */
> +static
> +int parse_key_string(char *keystring,
> +                    odp_key_t *key,
> +                    boolean cipher,
> +                    odp_crypto_alg_t alg)
> +{
> +       /* Brute force */
> +       if (cipher && (alg.cipher == ODP_CIPHER_ALG_3DES_CBC)) {
> +               if (48 == strlen(keystring)) {
> +                       int idx;
> +                       char temp[3];
> +
> +                       /* K1 */
> +                       for (idx = 0; idx < 8; idx++) {
> +                               temp[0] = *keystring++;
> +                               temp[1] = *keystring++;
> +                               temp[2] = 0;
> +                               key->des.k1[idx] = strtol(temp, NULL, 16);
> +                       }
> +
> +                       /* K2 */
> +                       for (idx = 0; idx < 8; idx++) {
> +                               temp[0] = *keystring++;
> +                               temp[1] = *keystring++;
> +                               temp[2] = 0;
> +                               key->des.k2[idx] = strtol(temp, NULL, 16);
> +                       }
> +
> +                       /* K3 */
> +                       for (idx = 0; idx < 8; idx++) {
> +                               temp[0] = *keystring++;
> +                               temp[1] = *keystring++;
> +                               temp[2] = 0;
> +                               key->des.k3[idx] = strtol(temp, NULL, 16);
> +                       }
> +                       return 0;
> +               }
> +       }
> +       if (!cipher && (alg.auth == ODP_AUTH_ALG_MD5_96)) {
> +               if (32 == strlen(keystring)) {
> +                       int idx;
> +                       char temp[3];
> +
> +                       for (idx = 0; idx < 16; idx++) {
> +                               temp[0] = *keystring++;
> +                               temp[1] = *keystring++;
> +                               temp[2] = 0;
> +                               key->md5.key[idx] = strtol(temp, NULL, 16);
> +                       }
> +                       return 0;
> +               }
> +       }
> +       return -1;
> +}
> +
> +/**
> + * 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
> +int parse_mac_string(char *macaddress, uint8_t *mac)
> +{
> +       int macwords[6];
> +       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;
> +}
> +
> +/**
> + * 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
> +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 address
> + *
> + * @param b    Pointer to buffer to store string
> + * @param addr IPv4 address
> + *
> + * @return Pointer to supplied buffer
> + */
> +static
> +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;
> +}
> +
> +/**
> + * 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
> +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
> +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 loop interface index
> + *
> + * @param b     Pointer to buffer to parse
> + *
> + * @return interface index (0 to (MAX_LOOPBACK - 1)) else -1
> + */
> +static
> +int loop_if_index(char *b)
> +{
> +       int ret;
> +       int idx;
> +
> +       /* Derive loopback interface index */
> +       ret = sscanf(b, "loop%d", &idx);
> +       if ((1 != ret) || (idx >= MAX_LOOPBACK))
> +               return -1;
> +       return idx;
> +}
> +
> +/**
> + * 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
> +int locate_ipsec_headers(odp_ipv4hdr_t *ip,
> +                        odp_ahhdr_t **ah_p,
> +                        odp_esphdr_t **esp_p)
> +{
> +       uint8_t *in = ipv4_data_p(ip);
> +       odp_ahhdr_t *ah = NULL;
> +       odp_esphdr_t *esp = NULL;
> +
> +       if (ODP_IPPROTO_AH == ip->proto) {
> +               ah = (odp_ahhdr_t *)in;
> +               in += ((ah)->ah_len + 2) * 4;
> +               if (ODP_IPPROTO_ESP == ah->next_header) {
> +                       esp = (odp_esphdr_t *)in;
> +                       in += sizeof(odp_esphdr_t);
> +               }
> +       } else if (ODP_IPPROTO_ESP == ip->proto) {
> +               esp = (odp_esphdr_t *)in;
> +               in += sizeof(odp_esphdr_t);
> +       }
> +
> +       *ah_p = ah;
> +       *esp_p = esp;
> +       return in - (ipv4_data_p(ip));
> +}
> +
> +/**
> + * Loopback database entry structure
> + */
> +typedef struct loopback_db_entry_s {
> +       odp_queue_t   inq_def;
> +       odp_queue_t   outq_def;
> +       uint8_t       mac[6];
> +} loopback_db_entry_t;
> +
> +typedef struct loopback_db_s {
> +       loopback_db_entry_t  intf[MAX_LOOPBACK];
> +} loopback_db_t;
> +
> +static loopback_db_t *loopback_db;
> +
> +/** Initialize loopback database global control structure */
> +static
> +void init_loopback_db(void)
> +{
> +       int idx;
> +
> +       loopback_db = odp_shm_reserve("loopback_db",
> +                                     sizeof(loopback_db_t),
> +                                     ODP_CACHE_LINE_SIZE);
> +       if (loopback_db == NULL) {
> +               ODP_ERR("Error: shared mem alloc failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       memset(loopback_db, 0, sizeof(*loopback_db));
> +
> +       for (idx = 0; idx < MAX_LOOPBACK; idx++) {
> +               loopback_db->intf[idx].inq_def = ODP_QUEUE_INVALID;
> +               loopback_db->intf[idx].outq_def = ODP_QUEUE_INVALID;
> +       }
> +}
> +/**
> + * Security Assocation (SA) data base entry
> + */
> +typedef struct sa_db_entry_s {
> +       struct sa_db_entry_s *next;      /**< Next entry on list */
> +       boolean               cipher;    /**< TRUE if cipher else auth */
> +       uint32_t              src_ip;    /**< Source IPv4 address */
> +       uint32_t              dst_ip;    /**< Desitnation IPv4 address */
> +       uint32_t              spi;       /**< Security Parameter Index */
> +       odp_crypto_alg_t      alg;       /**< Cipher/auth algorithm */
> +       odp_key_t             key;       /**< Cipher/auth key */
> +       uint32_t              block_len; /**< Cipher block length */
> +       uint32_t              iv_len;    /**< Initialization Vector length
> */
> +       uint32_t              icv_len;   /**< Integrity Check Value length
> */
> +} sa_db_entry_t;
> +
> +/**
> + * Security Assocation (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;
> +
> +/** Global pointer to sa db */
> +static sa_db_t *sa_db;
> +
> +/** Initialize SA database global control structure */
> +static
> +void init_sa_db(void)
> +{
> +       sa_db = odp_shm_reserve("shm_sa_db",
> +                               sizeof(sa_db_t),
> +                               ODP_CACHE_LINE_SIZE);
> +       if (sa_db == NULL) {
> +               ODP_ERR("Error: shared mem alloc failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       memset(sa_db, 0, sizeof(*sa_db));
> +}
> +
> +/**
> + * 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
> + *
> + * @return 0 if successful else -1
> + */
> +static
> +int create_sa_db_entry(char *input, boolean cipher)
> +{
> +       int pos;
> +       char *local, *str, *save;
> +       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 (local == NULL)
> +               return -1;
> +       strcpy(local, input);
> +
> +       /* Set cipher versus auth */
> +       entry->cipher = cipher;
> +
> +       /* count the number of tokens separated by ',' */
> +       for (str = local, save = NULL, pos = 0;; str = NULL, pos++) {
> +               char *token = strtok_r(str, ":", &save);
> +
> +               /* Check for no more tokens */
> +               if (token == NULL)
> +                       break;
> +
> +               /* Parse based on postion */
> +               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.cipher =
> +                                               ODP_CIPHER_ALG_3DES_CBC;
> +                                       entry->block_len  = 8;
> +                                       entry->iv_len     = 8;
> +                               } else {
> +                                       entry->alg.cipher =
> ODP_CIPHER_ALG_NULL;
> +                               }
> +                       } else {
> +                               if (0 == strcmp(token, "md5")) {
> +                                       entry->alg.cipher =
> +                                               ODP_AUTH_ALG_MD5_96;
> +                                       entry->icv_len    = 12;
> +                               } else {
> +                                       entry->alg.auth =
> ODP_AUTH_ALG_NULL;
> +                               }
> +                       }
> +                       break;
> +               case 3:
> +                       entry->spi = strtol(token, NULL, 16);
> +                       break;
> +               case 4:
> +                       parse_key_string(token,
> +                                        &entry->key,
> +                                        cipher,
> +                                        entry->alg);
> +                       break;
> +               default:
> +                       return -1;
> +               }
> +       }
> +
> +       /* Verify all positions filled */
> +       if (5 != pos)
> +               return -1;
> +
> +       /* Add route to the list */
> +       sa_db->index++;
> +       entry->next = sa_db->list;
> +       sa_db->list = entry;
> +
> +       return 0;
> +}
> +
> +/**
> + * Display the SA DB
> + */
> +static
> +void dump_sa_db(void)
> +{
> +       sa_db_entry_t *entry;
> +
> +       printf("\n"
> +              "Security association table\n"
> +              "--------------------------\n");
> +
> +       for (entry = sa_db->list; NULL != entry; entry = entry->next) {
> +               uint32_t idx;
> +               char src_ip_str[32];
> +               char dst_ip_str[32];
> +               uint8_t *p = (uint8_t *)&entry->key;
> +
> +
> +               printf(" %s %s %s %X %d ",
> +                      entry->cipher ? "esp" : "ah ",
> +                      ipv4_addr_str(src_ip_str, entry->src_ip),
> +                      ipv4_addr_str(dst_ip_str, entry->dst_ip),
> +                      entry->spi,
> +                      entry->cipher ?
> +                      (int)entry->alg.cipher :
> +                      (int)entry->alg.auth);
> +
> +               /* Brute force key display */
> +               for (idx = 0; idx < sizeof(odp_key_t); idx++)
> +                       printf("%02X", *p++);
> +
> +               printf("\n");
> +       }
> +}
> +
> +/**
> + * 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
> + */
> +static
> +sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src,
> +                               ip_addr_range_t *dst,
> +                               boolean 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->cipher)
> +                       continue;
> +               if (!match_ip_range(entry->src_ip, src))
> +                       continue;
> +               if (!match_ip_range(entry->dst_ip, dst))
> +                       continue;
> +               break;
> +       }
> +       return entry;
> +}
> +
> +/**
> + * 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 */
> +       boolean               input;       /**< Direction when applied */
> +       boolean               esp;         /**< Enable cipher (ESP) */
> +       boolean               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 */
> +static sp_db_t *sp_db;
> +
> +/** Initialize SP database global control structure */
> +static
> +void init_sp_db(void)
> +{
> +       sp_db = odp_shm_reserve("shm_sp_db",
> +                               sizeof(sp_db_t),
> +                               ODP_CACHE_LINE_SIZE);
> +       if (sp_db == NULL) {
> +               ODP_ERR("Error: shared mem alloc failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       memset(sp_db, 0, sizeof(*sp_db));
> +}
> +
> +/**
> + * Create an SP DB entry
> + *
> + * String is of the format "SrcSubNet:DstSubNet:(in|out):(ah|esp|both)"
> + *
> + * @param input  Pointer to string describing SP
> + *
> + * @return 0 if successful else -1
> + */
> +static
> +int create_sp_db_entry(char *input)
> +{
> +       int pos;
> +       char *local, *str, *save;
> +       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 (local == NULL)
> +               return -1;
> +       strcpy(local, input);
> +
> +       /* count the number of tokens separated by ',' */
> +       for (str = local, save = NULL, pos = 0;; str = NULL, pos++) {
> +               char *token = strtok_r(str, ":", &save);
> +
> +               /* Check for no more tokens */
> +               if (token == NULL)
> +                       break;
> +
> +               /* Parse based on postion */
> +               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:
> +                       return -1;
> +               }
> +       }
> +
> +       /* Verify all positions filled */
> +       if (4 != pos)
> +               return -1;
> +
> +       /* Add route to the list */
> +       sp_db->index++;
> +       entry->next = sp_db->list;
> +       sp_db->list = entry;
> +
> +       return 0;
> +}
> +
> +/**
> + * Display one SP DB entry
> + *
> + * @param entry  Pointer to entry to display
> + */
> +static
> +void dump_sp_db_entry(sp_db_entry_t *entry)
> +{
> +       char src_subnet_str[32];
> +       char dst_subnet_str[32];
> +
> +       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");
> +}
> +
> +/**
> + * Display the SP DB
> + */
> +static
> +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);
> +}
> +
> +/**
> + * 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 */
> +       struct {
> +               enum  odp_cipher_alg alg;         /**< Cipher algorithm */
> +               uint32_t             spi;         /**< Cipher SPI */
> +               uint32_t             block_len;   /**< Cipher block length
> */
> +               uint32_t             iv_len;      /**< Cipher IV length */
> +               odp_key_t            key;         /**< Cipher key */
> +       } esp;
> +       struct {
> +               enum  odp_auth_alg   alg;         /**< Auth algorithm */
> +               uint32_t             spi;         /**< Auth SPI */
> +               uint32_t             icv_len;     /**< Auth ICV length */
> +               odp_key_t            key;         /**< Auth key */
> +       } ah;
> +
> +       /* Per SA state */
> +       struct {
> +               odp_crypto_session_t session;     /**< Crypto session
> handle */
> +               uint32_t             esp_seq;     /**< ESP TX sequence
> number */
> +               uint32_t             ah_seq;      /**< AH TX sequence
> number */
> +               uint8_t              iv[32];      /**< ESP IV storage */
> +       } state;
> +} 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 */
> +static ipsec_cache_t *ipsec_cache;
> +
> +/** Initialize IPsec cache */
> +static
> +void init_ipsec_cache(void)
> +{
> +       ipsec_cache = odp_shm_reserve("shm_ipsec_cache",
> +                                     sizeof(ipsec_cache_t),
> +                                     ODP_CACHE_LINE_SIZE);
> +       if (ipsec_cache == NULL) {
> +               ODP_ERR("Error: shared mem alloc failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       memset(ipsec_cache, 0, sizeof(*ipsec_cache));
> +}
> +
> +/**
> + * Encode cipher and auth flags into valid cypto mode value
> + *
> + * @param cipher   Cipher is required
> + * @param auth     Authentication is required
> + *
> + * @return enum representing mode
> + */
> +static
> +enum odp_crypto_combination derive_comb(boolean cipher, boolean auth)
> +{
> +       if (cipher && auth)
> +               return ODP_CRYPTO_AUTH_CIPHERTEXT;
> +       else if (cipher)
> +               return ODP_CRYPTO_CIPHER_ONLY;
> +       return ODP_CRYPTO_AUTH_ONLY;
> +}
> +
> +/**
> + * Create an entry in the IPsec cache
> + *
> + * @param cipher_sa   Cipher SA DB entry pointer
> + * @param auth_sa     Auth SA DB entry pointer
> + * @param async       Crypto API preferred mode is async
> + * @param in          Direction (input versus output)
> + *
> + * @return 0 if successful else -1
> + */
> +static
> +int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
> +                            sa_db_entry_t *auth_sa,
> +                            boolean async,
> +                            boolean in)
> +{
> +       odp_crypto_rc_e rc;
> +       struct odp_crypto_session_params params;
> +       odp_buffer_t compl_event = ODP_BUFFER_INVALID;
> +       ipsec_cache_entry_t *entry;
> +       enum odp_crypto_ses_create_err ses_create_rc;
> +       odp_crypto_session_t session;
> +
> +       /* Verify we have a good entry */
> +       entry = &ipsec_cache->array[ipsec_cache->index];
> +       if (MAX_DB <= ipsec_cache->index)
> +               return -1;
> +
> +       /* Setup parameters and call crypto library to create session */
> +       params.op = (in) ? ODP_CRYPTO_OP_DECODE : ODP_CRYPTO_OP_ENCODE;
> +       params.comb = derive_comb((NULL != cipher_sa), (NULL != auth_sa));
> +       if (async) {
> +               params.pref_mode   = ODP_CRYPTO_ASYNC;
> +               params.compl_queue = completionq;
> +       } else {
> +               params.pref_mode   = ODP_CRYPTO_SYNC;
> +               params.compl_queue = ODP_QUEUE_INVALID;
> +       }
> +
> +       /* Cipher */
> +       if (cipher_sa) {
> +               params.cipher_alg  = cipher_sa->alg.cipher;
> +               params.cipher_key  = &cipher_sa->key;
> +               params.iv = entry->state.iv;
> +               params.iv_len = cipher_sa->iv_len;
> +       } else {
> +               params.cipher_alg = ODP_CIPHER_ALG_NULL;
> +               params.iv = NULL;
> +               params.iv_len = 0;
> +       }
> +
> +       /* Auth */
> +       if (auth_sa) {
> +               params.auth_alg = auth_sa->alg.auth;
> +               params.auth_key = &auth_sa->key;
> +       } else {
> +               params.auth_alg = ODP_AUTH_ALG_NULL;
> +       }
> +
> +       /* Generate an IV */
> +       if (params.iv_len)
> +               odp_hw_random_get(params.iv, &params.iv_len, 1);
> +
> +       /* Synchronous session create for now */
> +       compl_event = odp_buffer_alloc(msg_pool);
> +       rc = odp_crypto_session_create(&params, compl_event,
> ODP_QUEUE_INVALID);
> +       if (ODP_CRYPTO_OP_OK != rc)
> +               return -1;
> +       odp_crypto_get_ses_create_compl_status(compl_event,
> &ses_create_rc);
> +       if (ODP_CRYPTO_SES_CREATE_OK != ses_create_rc)
> +               return -1;
> +       odp_crypto_get_ses_create_compl_session(compl_event, &session);
> +       odp_buffer_free(compl_event);
> +
> +       /* Copy remainder */
> +       if (cipher_sa) {
> +               entry->src_ip = cipher_sa->src_ip;
> +               entry->dst_ip = cipher_sa->dst_ip;
> +               entry->esp.alg = cipher_sa->alg.cipher;
> +               entry->esp.spi = cipher_sa->spi;
> +               entry->esp.block_len = cipher_sa->block_len;
> +               entry->esp.iv_len = cipher_sa->iv_len;
> +               memcpy(&entry->esp.key, &cipher_sa->key,
> sizeof(odp_key_t));
> +       }
> +       if (auth_sa) {
> +               entry->src_ip = auth_sa->src_ip;
> +               entry->dst_ip = auth_sa->dst_ip;
> +               entry->ah.alg = auth_sa->alg.auth;
> +               entry->ah.spi = auth_sa->spi;
> +               entry->ah.icv_len = auth_sa->icv_len;
> +               memcpy(&entry->ah.key, &auth_sa->key, sizeof(odp_key_t));
> +       }
> +
> +       /* Initialize state */
> +       entry->state.esp_seq = 0;
> +       entry->state.ah_seq = 0;
> +       entry->state.session = session;
> +
> +       /* 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;
> +}
> +
> +/**
> + * Find a matching IPsec cache entry for input packet
> + *
> + * @param src_ip    Source IPv4 address
> + * @param dst_ip    Destination IPv4 address
> + * @param ah        Pointer to AH header in packet else NULL
> + * @param esp       Pointer to ESP header in packet else NULL
> + *
> + * @return pointer to IPsec cache entry else NULL
> + */
> +static
> +ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
> +                                              uint32_t dst_ip,
> +                                              odp_ahhdr_t *ah,
> +                                              odp_esphdr_t *esp)
> +{
> +       ipsec_cache_entry_t *entry = ipsec_cache->in_list;
> +
> +       /* Look for a hit */
> +       for (; NULL != entry; entry = entry->next) {
> +               if ((entry->src_ip != src_ip) || (entry->dst_ip != dst_ip))
> +                       continue;
> +               if (ah &&
> +                   ((!entry->ah.alg) ||
> +                    (entry->ah.spi != odp_be_to_cpu_32(ah->spi))))
> +                       continue;
> +               if (esp &&
> +                   ((!entry->esp.alg) ||
> +                    (entry->esp.spi != odp_be_to_cpu_32(esp->spi))))
> +                       continue;
> +               break;
> +       }
> +
> +       return entry;
> +}
> +
> +/**
> + * Find a matching IPsec cache entry for output packet
> + *
> + * @param src_ip    Source IPv4 address
> + * @param dst_ip    Destination IPv4 address
> + * @param proto     IPv4 protocol (currently all protocols match)
> + *
> + * @return pointer to IPsec cache entry else NULL
> + */
> +static
> +ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
> +                                               uint32_t dst_ip,
> +                                               uint8_t proto ODP_UNUSED)
> +{
> +       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;
> +}
> +
> +/**
> + * Forwarding data base entry
> + */
> +typedef struct fwd_db_entry_s {
> +       struct fwd_db_entry_s *next;          /**< Next entry on list */
> +       char                  *oif;           /**< Output interface name */
> +       odp_queue_t            queue;         /**< Output transmit queue */
> +       uint8_t                src_mac[6];    /**< Output source MAC */
> +       uint8_t                dst_mac[6];    /**< 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 */
> +static fwd_db_t *fwd_db;
> +
> +/** Initialize FWD DB */
> +static
> +void init_fwd_db(void)
> +{
> +       fwd_db = odp_shm_reserve("shm_fwd_db",
> +                                sizeof(fwd_db_t),
> +                                ODP_CACHE_LINE_SIZE);
> +       if (fwd_db == NULL) {
> +               ODP_ERR("Error: shared mem alloc failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       memset(fwd_db, 0, sizeof(*fwd_db));
> +}
> +
> +/**
> + * Create a forwarding database entry
> + *
> + * String is of the format "SubNet:Intf:NextHopMAC"
> + *
> + * @param input  Pointer to string describing route
> + *
> + * @return 0 if successful else -1
> + */
> +static
> +int create_fwd_db_entry(char *input)
> +{
> +       int pos;
> +       char *local, *str, *save;
> +       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 (local == NULL)
> +               return -1;
> +       strcpy(local, input);
> +
> +       /* count the number of tokens separated by ',' */
> +       for (str = local, save = NULL, pos = 0;; str = NULL, pos++) {
> +               char *token = strtok_r(str, ":", &save);
> +
> +               /* Check for no more tokens */
> +               if (token == NULL)
> +                       break;
> +
> +               /* Parse based on postion */
> +               switch (pos) {
> +               case 0:
> +                       parse_ipv4_string(token,
> +                                         &entry->subnet.addr,
> +                                         &entry->subnet.mask);
> +                       break;
> +               case 1:
> +                       entry->oif = token;
> +                       break;
> +               case 2:
> +                       parse_mac_string(token, entry->dst_mac);
> +                       break;
> +               default:
> +                       return -1;
> +               }
> +       }
> +
> +       /* Verify all positions filled */
> +       if (3 != pos)
> +               return -1;
> +
> +       /* Reset queue to invalid */
> +       entry->queue = ODP_QUEUE_INVALID;
> +
> +       /* Add route to the list */
> +       fwd_db->index++;
> +       entry->next = fwd_db->list;
> +       fwd_db->list = entry;
> +
> +       return 0;
> +}
> +
> +/**
> + * 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
> + */
> +static
> +void resolve_fwd_db(char *intf, odp_queue_t outq, 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->queue = outq;
> +               memcpy(entry->src_mac, mac, 6);
> +       }
> +}
> +
> +/**
> + * Display one fowarding database entry
> + *
> + * @param entry  Pointer to entry to display
> + */
> +static
> +void dump_fwd_db_entry(fwd_db_entry_t *entry)
> +{
> +       char subnet_str[32];
> +       char mac_str[32];
> +
> +       printf(" %s %s %s\n",
> +              ipv4_subnet_str(subnet_str, &entry->subnet),
> +              entry->oif,
> +              mac_addr_str(mac_str, entry->dst_mac));
> +}
> +
> +/**
> + * Display the forwarding database
> + */
> +static
> +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);
> +}
> +
> +/**
> + * Find a matching forwarding database entry
> + *
> + * @param dst_ip  Destination IPv4 address
> + *
> + * @return pointer to forwarding DB entry else NULL
> + */
> +static
> +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;
> +}
> +
> +/**
> + * Stream database entry structure
> + */
> +typedef struct stream_db_entry_s {
> +       struct stream_db_entry_s *next; /**< Next entry on list */
> +       int              id;            /**< Stream ID */
> +       uint32_t         src_ip;        /**< Source IPv4 address */
> +       uint32_t         dst_ip;        /**< Destination IPv4 address */
> +       int              count;         /**< Packet count */
> +       uint             length;        /**< Packet payload length */
> +       uint32_t         created;       /**< Number successfully created */
> +       uint32_t         verified;      /**< Number successfully verified
> */
> +       struct {
> +               int      loop;          /**< Input loop interface index */
> +               uint32_t ah_seq;        /**< AH sequence number if present
> */
> +               uint32_t esp_seq;       /**< ESP sequence number if
> present */
> +               ipsec_cache_entry_t *entry;  /**< IPsec to apply on input
> */
> +       } input;
> +       struct {
> +               int      loop;          /**< Output loop interface index */
> +               ipsec_cache_entry_t *entry;  /**t IPsec to verify on
> output */
> +       } output;
> +} stream_db_entry_t;
> +
> +typedef struct stream_db_s {
> +       uint32_t           index;          /**< Index of next available
> entry */
> +       stream_db_entry_t *list;           /**< List of active entries */
> +       stream_db_entry_t  array[MAX_DB];  /**< Entry storage */
> +} stream_db_t;
> +
> +static stream_db_t *stream_db;
> +
> +/** Initialize stream database global control structure */
> +static
> +void init_stream_db(void)
> +{
> +       stream_db = odp_shm_reserve("stream_db",
> +                                   sizeof(stream_db_t),
> +                                   ODP_CACHE_LINE_SIZE);
> +       if (stream_db == NULL) {
> +               ODP_ERR("Error: shared mem alloc failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       memset(stream_db, 0, sizeof(*stream_db));
> +}
> +
> +/**
> + * Create an stream DB entry
> + *
> + * String is of the format "SrcIP:DstIP:InInt:OutIntf:Count:Length"
> + *
> + * @param input  Pointer to string describing stream
> + *
> + * @return 0 if successful else -1
> + */
> +static
> +int create_stream_db_entry(char *input)
> +{
> +       int pos;
> +       char *local, *str, *save;
> +       stream_db_entry_t *entry = &stream_db->array[stream_db->index];
> +
> +       /* Verify we have a good entry */
> +       if (MAX_DB <= stream_db->index)
> +               return -1;
> +
> +       /* Make a local copy */
> +       local = malloc(strlen(input) + 1);
> +       if (local == NULL)
> +               return -1;
> +       strcpy(local, input);
> +
> +       /* count the number of tokens separated by ',' */
> +       for (str = local, save = NULL, pos = 0;; str = NULL, pos++) {
> +               char *token = strtok_r(str, ":", &save);
> +
> +               /* Check for no more tokens */
> +               if (token == NULL)
> +                       break;
> +
> +               /* Parse based on postion */
> +               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:
> +                       entry->input.loop = loop_if_index(token);
> +                       if (entry->input.loop < 0) {
> +                               ODP_ERR("Error: stream must have input
> loop\n");
> +                               exit(EXIT_FAILURE);
> +                       }
> +                       break;
> +               case 3:
> +                       entry->output.loop = loop_if_index(token);
> +                       break;
> +               case 4:
> +                       entry->count = atoi(token);
> +                       break;
> +               case 5:
> +                       entry->length = atoi(token);
> +                       if (entry->length < sizeof(stream_pkt_hdr_t))
> +                               entry->length = 0;
> +                       else
> +                               entry->length -= sizeof(stream_pkt_hdr_t);
> +                       break;
> +               default:
> +                       return -1;
> +               }
> +       }
> +
> +       /* Verify all positions filled */
> +       if (6 != pos)
> +               return -1;
> +
> +       /* Add stream to the list */
> +       entry->id = stream_db->index++;
> +       entry->next = stream_db->list;
> +       stream_db->list = entry;
> +
> +       return 0;
> +}
> +
> +/**
> + * Create IPv4 packet for stream
> + *
> + * Create one ICMP test packet based on the stream structure.  If an input
> + * IPsec cache entry is associated with the stream, build a packet that
> should
> + * successfully match that entry and be correctly decoded by it.
> + *
> + * @param stream    Stream DB entry
> + * @param pkt_pool  Packet buffer pool to allocate from
> + *
> + * @return packet else ODP_PACKET_INVALID
> + */
> +static
> +odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
> +                               odp_buffer_pool_t pkt_pool)
> +{
> +       ipsec_cache_entry_t *entry = stream->input.entry;
> +       odp_buffer_t         bfr;
> +       odp_packet_t         pkt;
> +       uint8_t             *base;
> +       uint8_t             *data;
> +       uint8_t             *dmac;
> +       odp_ethhdr_t        *eth;
> +       odp_ipv4hdr_t       *ip;
> +       odp_ahhdr_t         *ah = NULL;
> +       odp_esphdr_t        *esp = NULL;
> +       odp_icmphdr_t       *icmp;
> +       stream_pkt_hdr_t    *test;
> +       uint                 i;
> +
> +       /* Get destination MAC address to use */
> +       dmac = loopback_db->intf[stream->input.loop].mac;
> +
> +       /* Get buffer */
> +       bfr = odp_buffer_alloc(pkt_pool);
> +       if (ODP_BUFFER_INVALID == bfr)
> +               return ODP_PACKET_INVALID;
> +       pkt = odp_packet_from_buffer(bfr);
> +       odp_packet_init(pkt);
> +       base = odp_packet_start(pkt);
> +       data = odp_packet_start(pkt);
> +
> +       /* Ethernet */
> +       odp_packet_set_l2_offset(pkt, data - base);
> +       eth = (odp_ethhdr_t *)data;
> +       data += sizeof(*eth);
> +
> +       memset((char *)eth->src.addr, (0x80 | stream->id),
> ODP_ETHADDR_LEN);
> +       memcpy((char *)eth->dst.addr, dmac, ODP_ETHADDR_LEN);
> +       eth->type = odp_cpu_to_be_16(ODP_ETHTYPE_IPV4);
> +
> +       /* IPv4 */
> +       odp_packet_set_l3_offset(pkt, data - base);
> +       ip = (odp_ipv4hdr_t *)data;
> +       data += sizeof(*ip);
> +       odp_packet_set_l4_offset(pkt, data - base);
> +
> +       /* Wait until almost finished to fill in mutable fields */
> +       memset((char *)ip, 0, sizeof(*ip));
> +       ip->ver_ihl = 0x45;
> +       ip->proto = ODP_IPPROTO_ICMP;
> +       ip->id = odp_cpu_to_be_16(stream->id);
> +       ip->src_addr = odp_cpu_to_be_32(stream->src_ip);
> +       ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip);
> +
> +       /* AH (if specified) */
> +       if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg)) {
> +               if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg)
> +                       abort();
> +
> +               ah = (odp_ahhdr_t *)data;
> +               data += sizeof(*ah);
> +               data += entry->ah.icv_len;
> +
> +               memset((char *)ah, 0, sizeof(*ah) + entry->ah.icv_len);
> +               ah->ah_len = 1 + (entry->ah.icv_len / 4);
> +               ah->spi = odp_cpu_to_be_32(entry->ah.spi);
> +               ah->seq_no = odp_cpu_to_be_32(stream->input.ah_seq++);
> +       }
> +
> +       /* ESP (if specified) */
> +       if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg)) {
> +               if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
> +                       abort();
> +
> +               esp = (odp_esphdr_t *)data;
> +               data += sizeof(*esp);
> +               data += entry->esp.iv_len;
> +
> +               esp->spi = odp_cpu_to_be_32(entry->esp.spi);
> +               esp->seq_no = odp_cpu_to_be_32(stream->input.esp_seq++);
> +               RAND_bytes(esp->iv, 8);
> +       }
> +
> +       /* ICMP header so we can see it on wireshark */
> +       icmp = (odp_icmphdr_t *)data;
> +       data += sizeof(*icmp);
> +       icmp->type = ICMP_ECHO;
> +       icmp->code = 0;
> +       icmp->un.echo.id = odp_cpu_to_be_16(0x1234);
> +       icmp->un.echo.sequence = odp_cpu_to_be_16(stream->created);
> +
> +       /* Packet payload of incrementing bytes */
> +       test = (stream_pkt_hdr_t *)data;
> +       data += sizeof(*test);
> +       test->magic = odp_cpu_to_be_64(STREAM_MAGIC);
> +       for (i = 0; i < stream->length; i++)
> +               *data++ = (uint8_t)i;
> +
> +       /* Close ICMP */
> +       icmp->chksum = 0;
> +       icmp->chksum = odp_chksum(icmp, data - (uint8_t *)icmp);
> +
> +       /* Close ESP if specified */
> +       if (esp) {
> +               int payload_len = data - (uint8_t *)icmp;
> +               int encrypt_len;
> +               odp_esptrl_t *esp_t;
> +               DES_key_schedule ks1, ks2, ks3;
> +               uint8_t iv[8];
> +
> +               memcpy(iv, esp->iv, sizeof(iv));
> +
> +               encrypt_len = ESP_ENCODE_LEN(payload_len + sizeof(*esp_t),
> +                                            entry->esp.block_len);
> +               memset(data, 0, encrypt_len - payload_len);
> +               data += encrypt_len - payload_len;
> +
> +               esp_t = (odp_esptrl_t *)(data) - 1;
> +               esp_t->pad_len = encrypt_len - payload_len -
> sizeof(*esp_t);
> +               esp_t->next_header = ip->proto;
> +               ip->proto = ODP_IPPROTO_ESP;
> +
> +               DES_set_key(&entry->esp.key.des.k1, &ks1);
> +               DES_set_key(&entry->esp.key.des.k2, &ks2);
> +               DES_set_key(&entry->esp.key.des.k3, &ks3);
> +
> +               DES_ede3_cbc_encrypt((uint8_t *)icmp,
> +                                    (uint8_t *)icmp,
> +                                    encrypt_len,
> +                                    &ks1,
> +                                    &ks2,
> +                                    &ks3,
> +                                    (DES_cblock *)iv,
> +                                    1);
> +       }
> +
> +       /* Since ESP can pad we can now fix IP length */
> +       ip->tot_len = odp_cpu_to_be_16(data - (uint8_t *)ip);
> +       odp_packet_set_len(pkt, data - base);
> +
> +       /* Close AH if specified */
> +       if (ah) {
> +               uint8_t hash[EVP_MAX_MD_SIZE];
> +               uint32_t hash_len = 12;
> +               int auth_len = data - (uint8_t *)ip;
> +
> +               ah->next_header = ip->proto;
> +               ip->proto = ODP_IPPROTO_AH;
> +
> +               HMAC(EVP_md5(),
> +                    entry->ah.key.md5.key,
> +                    16,
> +                    (uint8_t *)ip,
> +                    auth_len,
> +                    hash,
> +                    &hash_len);
> +
> +               memcpy(ah->icv, hash, 12);
> +       }
> +
> +       /* Now fill in final IP header fields */
> +       ip->ttl = 64;
> +       ip->tos = 0;
> +       ip->frag_offset = 0;
> +       ip->chksum = 0;
> +       odp_ipv4_csum_update(pkt);
> +       return pkt;
> +}
> +
> +/**
> + * Resolve the stream DB against the IPsec input and output caches
> + *
> + * For each stream, look the source and destination IP address up in the
> + * input and output IPsec caches.  If a hit is found, store the hit in
> + * the stream DB to be used when creating packets.
> + */
> +static
> +void resolve_stream_db(void)
> +{
> +       stream_db_entry_t *stream = NULL;
> +
> +       /* For each stream look for input and output IPsec entries */
> +       for (stream = stream_db->list; NULL != stream; stream =
> stream->next) {
> +               ipsec_cache_entry_t *entry;
> +
> +               /* Lookup input entry */
> +               entry = find_ipsec_cache_entry_in(stream->src_ip,
> +                                                 stream->dst_ip,
> +                                                 NULL,
> +                                                 NULL);
> +               stream->input.entry = entry;
> +
> +               /* Lookup output entry */
> +               entry = find_ipsec_cache_entry_out(stream->src_ip,
> +                                                  stream->dst_ip,
> +                                                  0);
> +               stream->output.entry = entry;
> +       }
> +}
> +
> +/**
> + * Create input packets based on the stream DB
> + *
> + * Create input packets based on the configured streams and enqueue them
> + * into loop interface input queues.  Once packet processing starts these
> + * packets will be remomved and processed as if they had come from a
> normal
> + * packet interface.
> + *
> + * @return number of streams successfully processed
> + */
> +static
> +int create_stream_db_inputs(void)
> +{
> +       int created = 0;
> +       odp_buffer_pool_t pkt_pool;
> +       stream_db_entry_t *stream = NULL;
> +
> +       /* Lookup the packet pool */
> +       pkt_pool = odp_buffer_pool_lookup("packet_pool");
> +       if (pkt_pool == ODP_BUFFER_POOL_INVALID) {
> +               ODP_ERR("Error: pkt_pool not found\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* For each stream create corresponding input packets */
> +       for (stream = stream_db->list; NULL != stream; stream =
> stream->next) {
> +               int count;
> +               odp_queue_t queue;
> +
> +               queue = loopback_db->intf[stream->input.loop].inq_def;
> +
> +               for (count = stream->count; count > 0; count--) {
> +                       odp_packet_t pkt;
> +
> +                       pkt = create_ipv4_packet(stream, pkt_pool);
> +                       if (ODP_PACKET_INVALID == pkt) {
> +                               printf("Packet buffers exhausted\n");
> +                               break;
> +                       }
> +                       stream->created++;
> +                       odp_queue_enq(queue, pkt);
> +
> +                       /* Count this stream when we create first packet */
> +                       if (1 == stream->created)
> +                               created++;
> +               }
> +       }
> +
> +       return created;
> +}
> +
> +/**
> + * Verify an IPv4 packet received on a loop output queue
> + *
> + * TODO: Better error checking, add counters, add tracing,
> + *       add order verification
> + *
> + * @param stream  Stream to verify the packet against
> + * @param pkt     Packet to verify
> + *
> + * @return TRUE if packet verifies else FALSE
> + */
> +static
> +boolean verify_ipv4_packet(stream_db_entry_t *stream,
> +                          odp_packet_t pkt)
> +{
> +       ipsec_cache_entry_t *entry = stream->output.entry;
> +       uint8_t             *data;
> +       odp_ipv4hdr_t       *ip;
> +       odp_ahhdr_t         *ah = NULL;
> +       odp_esphdr_t        *esp = NULL;
> +       int                  hdr_len;
> +       odp_icmphdr_t       *icmp;
> +       stream_pkt_hdr_t    *test;
> +
> +       /* Basic IPv4 verify (add checksum verification) */
> +       data = odp_packet_l3(pkt);
> +       ip = (odp_ipv4hdr_t *)data;
> +       data += sizeof(*ip);
> +       if (0x45 != ip->ver_ihl)
> +               return FALSE;
> +       if (stream->src_ip != odp_be_to_cpu_32(ip->src_addr))
> +               return FALSE;
> +       if (stream->dst_ip != odp_be_to_cpu_32(ip->dst_addr))
> +               return FALSE;
> +
> +       /* Find IPsec headers if any and compare against entry */
> +       hdr_len = locate_ipsec_headers(ip, &ah, &esp);
> +       if (ah) {
> +               if (!entry)
> +                       return FALSE;
> +               if (ODP_AUTH_ALG_NULL == entry->ah.alg)
> +                       return FALSE;
> +               if (odp_be_to_cpu_32(ah->spi) != entry->ah.spi)
> +                       return FALSE;
> +               if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg)
> +                       abort();
> +       } else {
> +               if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg))
> +                       return FALSE;
> +       }
> +       if (esp) {
> +               if (!entry)
> +                       return FALSE;
> +               if (ODP_CIPHER_ALG_NULL == entry->esp.alg)
> +                       return FALSE;
> +               if (odp_be_to_cpu_32(esp->spi) != entry->esp.spi)
> +                       return FALSE;
> +               if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
> +                       abort();
> +               hdr_len += entry->esp.iv_len;
> +       } else {
> +               if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg))
> +                       return FALSE;
> +       }
> +       data += hdr_len;
> +
> +       /* Verify authentication (if present) */
> +       if (ah) {
> +               uint8_t  ip_tos;
> +               uint8_t  ip_ttl;
> +               uint16_t ip_frag_offset;
> +               uint8_t  icv[12];
> +               uint8_t  hash[EVP_MAX_MD_SIZE];
> +               uint32_t hash_len = 12;
> +
> +               /* Save/clear mutable fields */
> +               ip_tos = ip->tos;
> +               ip_ttl = ip->ttl;
> +               ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
> +               ip->tos = 0;
> +               ip->ttl = 0;
> +               ip->frag_offset = 0;
> +               ip->chksum = 0;
> +               memcpy(icv, ah->icv, 12);
> +               memset(ah->icv, 0, 12);
> +
> +               /* Calculate HMAC and compare */
> +               HMAC(EVP_md5(),
> +                    entry->ah.key.md5.key,
> +                    16,
> +                    (uint8_t *)ip,
> +                    odp_be_to_cpu_16(ip->tot_len),
> +                    hash,
> +                    &hash_len);
> +
> +               if (0 != memcmp(icv, hash, sizeof(icv)))
> +                       return FALSE;
> +
> +               ip->proto = ah->next_header;
> +               ip->tos = ip_tos;
> +               ip->ttl = ip_ttl;
> +               ip->frag_offset = odp_cpu_to_be_16(ip_frag_offset);
> +       }
> +
> +       /* Decipher if present */
> +       if (esp) {
> +               odp_esptrl_t *esp_t;
> +               DES_key_schedule ks1, ks2, ks3;
> +               uint8_t iv[8];
> +               int encrypt_len = ipv4_data_len(ip) - hdr_len;
> +
> +               memcpy(iv, esp->iv, sizeof(iv));
> +
> +               DES_set_key(&entry->esp.key.des.k1, &ks1);
> +               DES_set_key(&entry->esp.key.des.k2, &ks2);
> +               DES_set_key(&entry->esp.key.des.k3, &ks3);
> +
> +               DES_ede3_cbc_encrypt((uint8_t *)data,
> +                                    (uint8_t *)data,
> +                                    encrypt_len,
> +                                    &ks1,
> +                                    &ks2,
> +                                    &ks3,
> +                                    (DES_cblock *)iv,
> +                                    0);
> +
> +               esp_t = (odp_esptrl_t *)(data + encrypt_len) - 1;
> +               ip->proto = esp_t->next_header;
> +       }
> +
> +       /* Verify ICMP packet */
> +       if (ODP_IPPROTO_ICMP != ip->proto)
> +               return FALSE;
> +
> +       /* Verify ICMP header */
> +       icmp = (odp_icmphdr_t *)data;
> +       data += sizeof(*icmp);
> +       if (ICMP_ECHO != icmp->type)
> +               return FALSE;
> +       if (0x1234 != odp_be_to_cpu_16(icmp->un.echo.id))
> +               return FALSE;
> +
> +       /* Now check our packet */
> +       test = (stream_pkt_hdr_t *)data;
> +       if (STREAM_MAGIC != odp_be_to_cpu_64(test->magic))
> +               return FALSE;
> +
> +       return TRUE;
> +}
> +
> +/**
> + * Verify stream DB outputs
> + *
> + * For each stream, poll the output loop interface queue and verify
> + * any packets found on it
> + *
> + * @return TRUE if all packets on all streams verified else FALSE
> + */
> +static
> +boolean verify_stream_db_outputs(void)
> +{
> +       boolean done = TRUE;
> +       stream_db_entry_t *stream = NULL;
> +
> +       /* For each stream look for output packets */
> +       for (stream = stream_db->list; NULL != stream; stream =
> stream->next) {
> +               int idx;
> +               int count;
> +               odp_queue_t queue;
> +               odp_buffer_t buf_tbl[32];
> +
> +               queue = loopback_db->intf[stream->output.loop].outq_def;
> +
> +               if (ODP_QUEUE_INVALID == queue)
> +                       continue;
> +
> +               for (;;) {
> +#if LOOP_DEQ_MULTIPLE
> +                       count = odp_queue_deq_multi(queue, buf_tbl, 32);
> +#else
> +                       buf_tbl[0] = odp_queue_deq(queue);
> +                       count = (buf_tbl[0] != ODP_BUFFER_INVALID) ? 1 : 0;
> +#endif
> +                       if (!count)
> +                               break;
> +                       for (idx = 0; idx < count; idx++) {
> +                               boolean good;
> +                               odp_packet_t pkt;
> +
> +                               pkt = odp_packet_from_buffer(buf_tbl[idx]);
> +
> +                               good = verify_ipv4_packet(stream, pkt);
> +                               if (good)
> +                                       stream->verified++;
> +                       }
> +               }
> +
> +               printf("Stream %d %d\n", stream->created,
> stream->verified);
> +
> +               if (stream->created != stream->verified)
> +                       done = FALSE;
> +       }
> +       return done;
> +}
> +
> +/**
> + * IPsec pre argument processing intialization
> + */
> +static
> +void ipsec_init_pre(void)
> +{
> +       odp_queue_param_t qparam;
> +       void *pool_base;
> +
> +       /*
> +        * Create queues
> +        *
> +        *  - completion queue (should eventually be ORDERED)
> +        *  - sequence number queue (must be ATOMIC)
> +        */
> +       qparam.sched.prio  = ODP_SCHED_PRIO_HIGHEST;
> +       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
> +       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
> +
> +       completionq = odp_queue_create("completion",
> +                                      ODP_QUEUE_TYPE_SCHED,
> +                                      &qparam);
> +       if (completionq == ODP_QUEUE_INVALID) {
> +               ODP_ERR("Error: completion queue creation failed\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       qparam.sched.prio  = ODP_SCHED_PRIO_HIGHEST;
> +       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
> +       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
> +
> +       seqnumq = odp_queue_create("seqnum",
> +                                  ODP_QUEUE_TYPE_SCHED,
> +                                  &qparam);
> +       if (seqnumq == ODP_QUEUE_INVALID) {
> +               ODP_ERR("Error: sequence number queue creation failed\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Create session creation message pool */
> +       pool_base = odp_shm_reserve("shm_msg_pool",
> +                                   MSG_POOL_SIZE, ODP_CACHE_LINE_SIZE);
> +
> +       msg_pool = odp_buffer_pool_create("msg_pool", pool_base,
> +                                         MSG_POOL_SIZE,
> +                                         MSG_POOL_BUF_SIZE,
> +                                         ODP_CACHE_LINE_SIZE,
> +                                         ODP_BUFFER_TYPE_RAW);
> +
> +       if (msg_pool == ODP_BUFFER_POOL_INVALID) {
> +               ODP_ERR("Error: message pool create failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Initialize our data bases */
> +       init_sp_db();
> +       init_sa_db();
> +       init_ipsec_cache();
> +}
> +
> +/**
> + * IPsec post argument processing intialization
> + *
> + * Resolve SP DB with SA DB and create corresponding IPsec cache entries
> + *
> + * @param async  Mode to use when invoking per packet crypto API
> + */
> +static
> +void ipsec_init_post(boolean async)
> +{
> +       sp_db_entry_t *entry;
> +
> +       /* 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;
> +
> +               if (entry->esp)
> +                       cipher_sa = find_sa_db_entry(&entry->src_subnet,
> +                                                    &entry->dst_subnet,
> +                                                    1);
> +               if (entry->ah)
> +                       auth_sa = find_sa_db_entry(&entry->src_subnet,
> +                                                  &entry->dst_subnet,
> +                                                  0);
> +
> +               if (cipher_sa || auth_sa)
> +                       create_ipsec_cache_entry(cipher_sa,
> +                                                auth_sa,
> +                                                async,
> +                                                entry->input);
> +               else {
> +                       printf(" WARNING: SA not found for SP\n");
> +                       dump_sp_db_entry(entry);
> +               }
> +       }
> +}
> +
> +/**
> + * Initialize loopback
> + *
> + * Initialize ODP queues to create our own idea of loopbacks, which allow
> + * testing without physical interfaces.  Interface name string will be of
> + * the format "loopX" where X is the decimal number of the interface.
> + *
> + * @param intf     Loopback interface name string
> + */
> +static
> +void initialize_loop(char *intf)
> +{
> +       int idx;
> +       loopback_db_entry_t *entry;
> +       odp_queue_t outq_def;
> +       odp_queue_t inq_def;
> +       char queue_name[ODP_QUEUE_NAME_LEN];
> +       odp_queue_param_t qparam;
> +       char mac_str[32];
> +
> +       /* Derive loopback interface index */
> +       idx = loop_if_index(intf);
> +       if (idx < 0) {
> +               ODP_ERR("Error: loopback \"%s\" invalid\n", intf);
> +               exit(EXIT_FAILURE);
> +       }
> +       entry = &loopback_db->intf[idx];
> +
> +       /* Dummy MAC address */
> +       memset(entry->mac, (0xF0 | idx), sizeof(entry->mac));
> +
> +       /* Create input queue */
> +       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
> +       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
> +       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
> +       snprintf(queue_name, sizeof(queue_name), "%i-loop_inq_def", idx);
> +       queue_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
> +
> +       inq_def = odp_queue_create(queue_name, ODP_QUEUE_TYPE_SCHED,
> &qparam);
> +       if (inq_def == ODP_QUEUE_INVALID) {
> +               ODP_ERR("Error: input queue creation failed for %s\n",
> intf);
> +               exit(EXIT_FAILURE);
> +       }
> +       entry->inq_def = inq_def;
> +
> +       /* Create output queue */
> +       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
> +       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
> +       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
> +       snprintf(queue_name, sizeof(queue_name), "%i-loop_outq_def", idx);
> +       queue_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
> +
> +       outq_def = odp_queue_create(queue_name, ODP_QUEUE_TYPE_POLL,
> &qparam);
> +       if (outq_def == ODP_QUEUE_INVALID) {
> +               ODP_ERR("Error: output queue creation failed for %s\n",
> intf);
> +               exit(EXIT_FAILURE);
> +       }
> +       entry->outq_def = outq_def;
> +
> +       printf("Created loop:%02i, queue mode (ATOMIC queues)\n"
> +              "          default loop%02i-INPUT queue:%u\n"
> +              "          default loop%02i-OUTPUT queue:%u\n"
> +              "          source mac address %s\n",
> +              idx, idx, inq_def, idx, outq_def,
> +              mac_addr_str(mac_str, entry->mac));
> +
> +       /* Resolve any routes using this interface for output */
> +       resolve_fwd_db(intf, outq_def, entry->mac);
> +}
> +
> +/**
> + * Initialize interface
> + *
> + * Initialize ODP pktio and queues, query MAC address and update
> + * forwarding database.
> + *
> + * @param intf     Interface name string
> + * @param type     Packet IO type (BASIC, MMSG, MMAP)
> + * @param fanout   Packet IO fanout
> + */
> +static
> +void initialize_intf(char *intf, int type, int fanout)
> +{
> +       odp_buffer_pool_t pkt_pool;
> +       odp_pktio_t pktio;
> +       odp_queue_t outq_def;
> +       odp_queue_t inq_def;
> +       char inq_name[ODP_QUEUE_NAME_LEN];
> +       odp_queue_param_t qparam;
> +       int ret;
> +       odp_pktio_params_t params;
> +       socket_params_t *sock_params = &params.sock_params;
> +       uint8_t src_mac[6];
> +       char src_mac_str[32];
> +
> +       /* Lookup the packet pool */
> +       pkt_pool = odp_buffer_pool_lookup("packet_pool");
> +       if (pkt_pool == ODP_BUFFER_POOL_INVALID) {
> +               ODP_ERR("Error: pkt_pool not found\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /*
> +        * Open a packet IO instance for thread and get default output
> queue
> +        */
> +       sock_params->type = type;
> +       sock_params->fanout = fanout;
> +       pktio = odp_pktio_open(intf, pkt_pool, &params);
> +       if (pktio == ODP_PKTIO_INVALID) {
> +               ODP_ERR("Error: pktio create failed for %s\n", intf);
> +               exit(EXIT_FAILURE);
> +       }
> +       outq_def = odp_pktio_outq_getdef(pktio);
> +
> +       /*
> +        * Create and set the default INPUT queue associated with the
> 'pktio'
> +        * resource
> +        */
> +       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
> +       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
> +       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
> +       snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def",
> (int)pktio);
> +       inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
> +
> +       inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN,
> &qparam);
> +       if (inq_def == ODP_QUEUE_INVALID) {
> +               ODP_ERR("Error: pktio queue creation failed for %s\n",
> intf);
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       ret = odp_pktio_inq_setdef(pktio, inq_def);
> +       if (ret != 0) {
> +               ODP_ERR("Error: default input-Q setup for %s\n", intf);
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* read the source MAC address for this interface */
> +       query_mac_address(intf, src_mac);
> +
> +       printf("Created pktio:%02i, queue mode (ATOMIC queues)\n"
> +              "          default pktio%02i-INPUT queue:%u\n"
> +              "          source mac address %s\n",
> +              pktio, pktio, inq_def, mac_addr_str(src_mac_str, src_mac));
> +
> +       /* Resolve any routes using this interface for output */
> +       resolve_fwd_db(intf, outq_def, src_mac);
> +}
> +
> +/**
> + * Verify crypto operation completed successfully
> + *
> + * @param status  Pointer to cryto completion structure
> + *
> + * @return TRUE if all OK else FALSE
> + */
> +static
> +boolean is_crypto_compl_status_ok(struct odp_crypto_compl_status *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;
> +}
> +
> +/**
> + * Packet Processing - Input verification
> + *
> + * @param pkt  Packet to inspect
> + * @param ctx  Packet process context (not used)
> + *
> + * @return PKT_CONTINUE if good, supported packet else PKT_DROP
> + */
> +static
> +pkt_disposition_e input_verify(odp_packet_t pkt, pkt_ctx_t *ctx
> ODP_UNUSED)
> +{
> +       if (odp_unlikely(odp_packet_error(pkt)))
> +               return PKT_DROP;
> +
> +       /*
> +        * TODO: for stream packets figure out how to set these flags
> +        *
> +        * if (!odp_packet_inflag_eth(pkt))
> +        *      return PKT_DROP;
> +        *
> +        * if (!odp_packet_inflag_ipv4(pkt))
> +        *      return PKT_DROP;
> +        */
> +
> +       return PKT_CONTINUE;
> +}
> +
> +/**
> + * Packet Processing - Route lookup in forwarding database
> + *
> + * @param pkt  Packet to route
> + * @param ctx  Packet process context
> + *
> + * @return PKT_CONTINUE if route found else PKT_DROP
> + */
> +static
> +pkt_disposition_e route_fwd_db(odp_packet_t pkt, pkt_ctx_t *ctx)
> +{
> +       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
> +       fwd_db_entry_t *entry;
> +
> +       entry = find_fwd_db_entry(odp_be_to_cpu_32(ip->dst_addr));
> +
> +       if (entry) {
> +               odp_ethhdr_t *eth = (odp_ethhdr_t *)odp_packet_l2(pkt);
> +
> +               memcpy(&eth->dst, entry->dst_mac, 6);
> +               memcpy(&eth->src, entry->src_mac, 6);
> +               ctx->outq = entry->queue;
> +
> +               return PKT_CONTINUE;
> +       }
> +
> +       return PKT_DROP;
> +}
> +
> +/**
> + * Packet Processing - Input IPsec packet classification
> + *
> + * Verify the received packet has IPsec headers and a match
> + * in the IPsec cache, if so issue crypto request else skip
> + * input crypto.
> + *
> + * @param pkt   Packet to classify
> + * @param ctx   Packet process context
> + * @param skip  Pointer to return "skip" indication
> + *
> + * @return PKT_CONTINUE if done else PKT_POSTED
> + */
> +static
> +pkt_disposition_e do_ipsec_in_classify(odp_packet_t pkt,
> +                                      pkt_ctx_t *ctx,
> +                                      boolean *skip)
> +{
> +       uint8_t *buf = odp_packet_buf_addr(pkt);
> +       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
> +       int hdr_len;
> +       odp_ahhdr_t *ah = NULL;
> +       odp_esphdr_t *esp = NULL;
> +       ipsec_cache_entry_t *entry;
> +       struct odp_crypto_op_params params;
> +       odp_crypto_rc_e rc;
> +
> +       /* Default to skip IPsec */
> +       *skip = TRUE;
> +
> +       /* Check IP header for IPSec protocols and look it up */
> +       hdr_len = locate_ipsec_headers(ip, &ah, &esp);
> +       if (!ah && !esp)
> +               return PKT_CONTINUE;
> +       entry = find_ipsec_cache_entry_in(odp_be_to_cpu_32(ip->src_addr),
> +                                         odp_be_to_cpu_32(ip->dst_addr),
> +                                         ah,
> +                                         esp);
> +       if (!entry)
> +               return PKT_CONTINUE;
> +
> +       /* Account for configured ESP IV length in packet */
> +       hdr_len += entry->esp.iv_len;
> +
> +       /* Initialize parameters block */
> +       memset(&params, 0, sizeof(params));
> +       params.session = entry->state.session;
> +       params.pkt = pkt;
> +       params.out_pkt = pkt;
> +
> +       /*Save everything to context */
> +       ctx->ipsec.ip_tos = ip->tos;
> +       ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
> +       ctx->ipsec.ip_ttl = ip->ttl;
> +       ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
> +       ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
> +       ctx->ipsec.hdr_len = hdr_len;
> +       ctx->ipsec.trl_len = 0;
> +
> +       /*If authenticating, zero the mutable fields build the request */
> +       if (ah) {
> +               ip->chksum = 0;
> +               ip->tos = 0;
> +               ip->frag_offset = 0;
> +               ip->ttl = 0;
> +
> +               params.auth_range.offset = ((uint8_t *)ip) - buf;
> +               params.auth_range.length = odp_be_to_cpu_16(ip->tot_len);
> +               params.hash_result_offset = ah->icv - buf;
> +       }
> +
> +       /* If deciphering build request */
> +       if (esp) {
> +               params.cipher_range.offset = ipv4_data_p(ip) + hdr_len -
> buf;
> +               params.cipher_range.length = ipv4_data_len(ip) - hdr_len;
> +               params.override_iv_ptr = esp->iv;
> +       }
> +
> +       /* Issue crypto request */
> +       *skip = FALSE;
> +       rc = odp_crypto_operation(&params, odp_buffer_from_packet(pkt));
> +       if (ODP_CRYPTO_OP_ERROR == rc)
> +               abort();
> +       if (ODP_CRYPTO_OP_POSTED == rc)
> +               return PKT_POSTED;
> +       return PKT_CONTINUE;
> +}
> +
> +/**
> + * Packet Processing - Input IPsec packet processing cleanup
> + *
> + * @param pkt  Packet to handle
> + * @param ctx  Packet process context
> + *
> + * @return PKT_CONTINUE if successful else PKT_DROP
> + */
> +static
> +pkt_disposition_e do_ipsec_in_finish(odp_packet_t pkt,
> +                                    pkt_ctx_t *ctx)
> +{
> +       odp_buffer_t event;
> +       struct odp_crypto_compl_status cipher_rc, auth_rc;
> +       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
> +       int       hdr_len = ctx->ipsec.hdr_len;
> +       int       trl_len = 0;
> +
> +       /* Check crypto result */
> +       event = odp_buffer_from_packet(pkt);
> +       odp_crypto_get_operation_compl_status(event, &cipher_rc, &auth_rc);
> +       if (!is_crypto_compl_status_ok(&cipher_rc))
> +               return PKT_DROP;
> +       if (!is_crypto_compl_status_ok(&auth_rc))
> +               return PKT_DROP;
> +
> +       /*
> +        * Finish auth
> +        */
> +       if (ctx->ipsec.ah_offset) {
> +               uint8_t *buf = odp_packet_buf_addr(pkt);
> +               odp_ahhdr_t *ah;
> +
> +               ah = (odp_ahhdr_t *)(ctx->ipsec.ah_offset + buf);
> +               ip->proto = ah->next_header;
> +       }
> +
> +       /*
> +        * Finish cipher by finding ESP trailer and processing
> +        *
> +        * NOTE: ESP authentication ICV not supported
> +        */
> +       if (ctx->ipsec.esp_offset) {
> +               uint8_t *eop = (uint8_t *)(ip) +
> odp_be_to_cpu_16(ip->tot_len);
> +               odp_esptrl_t *esp_t = (odp_esptrl_t *)(eop) - 1;
> +
> +               ip->proto = esp_t->next_header;
> +               trl_len += esp_t->pad_len + sizeof(*esp_t);
> +       }
> +
> +       /* Finalize the IPv4 header */
> +       ipv4_adjust_len(ip, -(hdr_len + trl_len));
> +       ip->ttl = ctx->ipsec.ip_ttl;
> +       ip->tos = ctx->ipsec.ip_tos;
> +       ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset);
> +       ip->chksum = 0;
> +       odp_ipv4_csum_update(pkt);
> +
> +       /* Correct the packet length and move payload into position */
> +       odp_packet_set_len(pkt, odp_packet_get_len(pkt) - (hdr_len +
> trl_len));
> +       memmove(ipv4_data_p(ip),
> +               ipv4_data_p(ip) + hdr_len,
> +               odp_be_to_cpu_16(ip->tot_len));
> +
> +       /* Fall through to next state */
> +       return PKT_CONTINUE;
> +}
> +
> +/**
> + * Packet Processing - Output IPsec packet classification
> + *
> + * Verify the outbound packet has a match in the IPsec cache,
> + * if so issue prepend IPsec headers and prepare parameters
> + * for crypto API call.  Post the packet to ATOMIC queue so
> + * that sequence numbers can be applied in packet order as
> + * the next processing step.
> + *
> + * @param pkt   Packet to classify
> + * @param ctx   Packet process context
> + * @param skip  Pointer to return "skip" indication
> + *
> + * @return PKT_CONTINUE if done else PKT_POSTED
> + */
> +static
> +pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt,
> +                                       pkt_ctx_t *ctx,
> +                                       boolean *skip)
> +{
> +       uint8_t *buf = odp_packet_buf_addr(pkt);
> +       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
> +       uint16_t ip_data_len = ipv4_data_len(ip);
> +       uint8_t *ip_data = ipv4_data_p(ip);
> +       ipsec_cache_entry_t *entry;
> +       struct odp_crypto_op_params params;
> +       int      hdr_len = 0;
> +       int      trl_len = 0;
> +       odp_ahhdr_t *ah = NULL;
> +       odp_esphdr_t *esp = NULL;
> +
> +       /* Default to skip IPsec */
> +       *skip = TRUE;
> +
> +       /* Find record */
> +       entry = find_ipsec_cache_entry_out(odp_be_to_cpu_32(ip->src_addr),
> +                                          odp_be_to_cpu_32(ip->dst_addr),
> +                                          ip->proto);
> +       if (!entry)
> +               return PKT_CONTINUE;
> +
> +       /* Save IPv4 stuff */
> +       ctx->ipsec.ip_tos = ip->tos;
> +       ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
> +       ctx->ipsec.ip_ttl = ip->ttl;
> +
> +       /* Initialize parameters block */
> +       memset(&params, 0, sizeof(params));
> +       params.session = entry->state.session;
> +       params.pkt = pkt;
> +       params.out_pkt = pkt;
> +
> +       /* Compute ah and esp, determine length of headers, move the data
> */
> +       if (entry->ah.alg) {
> +               ah = (odp_ahhdr_t *)(ip_data);
> +               hdr_len += sizeof(odp_ahhdr_t);
> +               hdr_len += entry->ah.icv_len;
> +       }
> +       if (entry->esp.alg) {
> +               esp = (odp_esphdr_t *)(ip_data + hdr_len);
> +               hdr_len += sizeof(odp_esphdr_t);
> +               hdr_len += entry->esp.iv_len;
> +       }
> +       memmove(ip_data + hdr_len, ip_data, ip_data_len);
> +       ip_data += hdr_len;
> +
> +       /* For cipher, compute encrypt length, build headers and request */
> +       if (esp) {
> +               uint32_t encrypt_len;
> +               odp_esptrl_t *esp_t;
> +
> +               encrypt_len = ESP_ENCODE_LEN(ip_data_len + sizeof(*esp_t),
> +                                            entry->esp.block_len);
> +               trl_len = encrypt_len - ip_data_len;
> +
> +               esp->spi = odp_cpu_to_be_32(entry->esp.spi);
> +               memcpy(esp + 1, entry->state.iv, entry->esp.iv_len);
> +
> +               esp_t = (odp_esptrl_t *)(ip_data + encrypt_len) - 1;
> +               esp_t->pad_len     = trl_len - sizeof(*esp_t);
> +               esp_t->next_header = ip->proto;
> +               ip->proto = ODP_IPPROTO_ESP;
> +
> +               params.cipher_range.offset = ip_data - buf;
> +               params.cipher_range.length = encrypt_len;
> +       }
> +
> +       /* For authentication, build header clear mutables and build
> request */
> +       if (ah) {
> +               memset(ah, 0, sizeof(*ah) + entry->ah.icv_len);
> +               ah->spi = odp_cpu_to_be_32(entry->ah.spi);
> +               ah->ah_len = 1 + (entry->ah.icv_len / 4);
> +               ah->next_header = ip->proto;
> +               ip->proto = ODP_IPPROTO_AH;
> +
> +               ip->chksum = 0;
> +               ip->tos = 0;
> +               ip->frag_offset = 0;
> +               ip->ttl = 0;
> +
> +               params.auth_range.offset = ((uint8_t *)ip) - buf;
> +               params.auth_range.length =
> +                       odp_be_to_cpu_16(ip->tot_len) + (hdr_len +
> trl_len);
> +               params.hash_result_offset = ah->icv - buf;
> +       }
> +
> +       /* Set IPv4 length before authentication */
> +       ipv4_adjust_len(ip, hdr_len + trl_len);
> +
> +       /* Save remaining context */
> +       ctx->ipsec.hdr_len = hdr_len;
> +       ctx->ipsec.trl_len = trl_len;
> +       ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
> +       ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
> +       ctx->ipsec.ah_seq = &entry->state.ah_seq;
> +       ctx->ipsec.esp_seq = &entry->state.esp_seq;
> +       memcpy(&ctx->ipsec.params, &params, sizeof(params));
> +
> +       /* Send packet to the atmoic queue to assign sequence numbers */
> +       *skip = FALSE;
> +       odp_queue_enq(seqnumq, odp_buffer_from_packet(pkt));
> +       return PKT_POSTED;
> +}
> +
> +/**
> + * Packet Processing - Output IPsec packet sequence number assignment
> + *
> + * Assign the necessary sequence numbers and then issue the crypto API
> call
> + *
> + * @param pkt  Packet to handle
> + * @param ctx  Packet process context
> + *
> + * @return PKT_CONTINUE if done else PKT_POSTED
> + */
> +static
> +pkt_disposition_e do_ipsec_out_seq(odp_packet_t pkt,
> +                                  pkt_ctx_t *ctx)
> +{
> +       uint8_t *buf = odp_packet_buf_addr(pkt);
> +       odp_crypto_rc_e rc;
> +
> +       /* We were dispatched from atomic queue, assign sequence numbers */
> +       if (ctx->ipsec.ah_offset) {
> +               odp_ahhdr_t *ah;
> +
> +               ah = (odp_ahhdr_t *)(ctx->ipsec.ah_offset + buf);
> +               ah->seq_no = odp_cpu_to_be_32((*ctx->ipsec.ah_seq)++);
> +       }
> +       if (ctx->ipsec.esp_offset) {
> +               odp_esphdr_t *esp;
> +
> +               esp = (odp_esphdr_t *)(ctx->ipsec.esp_offset + buf);
> +               esp->seq_no = odp_cpu_to_be_32((*ctx->ipsec.esp_seq)++);
> +       }
> +
> +       /* Issue crypto request */
> +       rc = odp_crypto_operation(&ctx->ipsec.params,
> +                                 odp_buffer_from_packet(pkt));
> +       if (ODP_CRYPTO_OP_ERROR == rc)
> +               abort();
> +       if (ODP_CRYPTO_OP_POSTED == rc)
> +               return PKT_POSTED;
> +       return PKT_CONTINUE;
> +}
> +
> +/**
> + * Packet Processing - Output IPsec packet processing cleanup
> + *
> + * @param pkt  Packet to handle
> + * @param ctx  Packet process context
> + *
> + * @return PKT_CONTINUE if successful else PKT_DROP
> + */
> +static
> +pkt_disposition_e do_ipsec_out_finish(odp_packet_t pkt,
> +                                     pkt_ctx_t *ctx)
> +{
> +       odp_buffer_t event;
> +       struct odp_crypto_compl_status cipher_rc, auth_rc;
> +       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
> +       int hdr_len = ctx->ipsec.hdr_len;
> +       int trl_len = ctx->ipsec.trl_len;
> +
> +       /* Check crypto result */
> +       event = odp_buffer_from_packet(pkt);
> +       odp_crypto_get_operation_compl_status(event, &cipher_rc, &auth_rc);
> +       if (!is_crypto_compl_status_ok(&cipher_rc))
> +               return PKT_DROP;
> +       if (!is_crypto_compl_status_ok(&auth_rc))
> +               return PKT_DROP;
> +
> +       /* Finalize the IPv4 header */
> +       ip->ttl = ctx->ipsec.ip_ttl;
> +       ip->tos = ctx->ipsec.ip_tos;
> +       ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset);
> +       ip->chksum = 0;
> +       odp_ipv4_csum_update(pkt);
> +       odp_packet_set_len(pkt, odp_packet_get_len(pkt) + (hdr_len +
> trl_len));
> +
> +       /* Fall through to next state */
> +       return PKT_CONTINUE;
> +}
> +
> +/**
> + * Packet IO worker thread
> + *
> + * Loop calling odp_schedule to obtain packets from one of three sources,
> + * and continue processing the packet based on the state stored in its
> + * per packet context.
> + *
> + *  - Input interfaces (i.e. new work)
> + *  - Sequence number assignment queue
> + *  - Per packet crypto API completion queue
> + *
> + * @param arg  thread arguments of type 'thread_args_t *'
> + *
> + * @return NULL (should never return)
> + */
> +static
> +void *pktio_thread(void *arg ODP_UNUSED)
> +{
> +       int thr;
> +       odp_packet_t pkt;
> +       odp_buffer_t buf;
> +       unsigned long pkt_cnt = 0;
> +
> +       thr = odp_thread_id();
> +
> +       printf("Pktio thread [%02i] starts\n", thr);
> +
> +       odp_barrier_sync(&sync_barrier);
> +
> +       /* Loop packets */
> +       for (;;) {
> +               pkt_disposition_e rc;
> +               pkt_ctx_t   *ctx;
> +               odp_queue_t  dispatchq;
> +
> +               /* Use schedule to get buf from any input queue */
> +               buf = odp_schedule(&dispatchq, ODP_SCHED_WAIT);
> +               pkt = odp_packet_from_buffer(buf);
> +               ctx = get_ctx_from_buf(buf);
> +
> +               /* Determine new work versus completion or sequence number
> */
> +               if ((completionq != dispatchq) && (seqnumq != dispatchq)) {
> +                       memset(ctx, 0, sizeof(*ctx));
> +                       ctx->state = PKT_STATE_INPUT_VERIFY;
> +               }
> +
> +               /*
> +                * We now have a packet and its associated context. Loop
> here
> +                * executing processing based on the current state value
> stored
> +                * in the context as long as the processing return code
> +                * indicates PKT_CONTINUE.
> +                *
> +                * For other return codes:
> +                *
> +                *  o PKT_DONE   - finished with the packet
> +                *  o PKT_DROP   - something incorrect about the packet,
> drop it
> +                *  o PKT_POSTED - packet/event has been queued for later
> +                */
> +               do {
> +                       boolean skip;
> +
> +                       switch (ctx->state) {
> +                       case PKT_STATE_INPUT_VERIFY:
> +
> +                               rc = input_verify(pkt, ctx);
> +                               ctx->state = PKT_STATE_IPSEC_IN_CLASSIFY;
> +                               break;
> +
> +                       case PKT_STATE_IPSEC_IN_CLASSIFY:
> +
> +                               rc = do_ipsec_in_classify(pkt, ctx, &skip);
> +                               ctx->state = (skip) ?
> +                                       PKT_STATE_ROUTE_LOOKUP :
> +                                       PKT_STATE_IPSEC_IN_FINISH;
> +                               break;
> +
> +                       case PKT_STATE_IPSEC_IN_FINISH:
> +
> +                               rc = do_ipsec_in_finish(pkt, ctx);
> +                               ctx->state = PKT_STATE_ROUTE_LOOKUP;
> +                               break;
> +
> +                       case PKT_STATE_ROUTE_LOOKUP:
> +
> +                               rc = route_fwd_db(pkt, ctx);
> +                               ctx->state = PKT_STATE_IPSEC_OUT_CLASSIFY;
> +                               break;
> +
> +                       case PKT_STATE_IPSEC_OUT_CLASSIFY:
> +
> +                               rc = do_ipsec_out_classify(pkt, ctx,
> &skip);
> +                               ctx->state = (skip) ?
> +                                       PKT_STATE_TRANSMIT :
> +                                       PKT_STATE_IPSEC_OUT_SEQ;
> +                               break;
> +
> +                       case PKT_STATE_IPSEC_OUT_SEQ:
> +
> +                               rc = do_ipsec_out_seq(pkt, ctx);
> +                               ctx->state = PKT_STATE_IPSEC_OUT_FINISH;
> +                               break;
> +
> +                       case PKT_STATE_IPSEC_OUT_FINISH:
> +
> +                               rc = do_ipsec_out_finish(pkt, ctx);
> +                               ctx->state = PKT_STATE_TRANSMIT;
> +                               break;
> +
> +                       case PKT_STATE_TRANSMIT:
> +
> +                               odp_queue_enq(ctx->outq, buf);
> +                               rc = PKT_DONE;
> +                               break;
> +
> +                       default:
> +                               rc = PKT_DROP;
> +                               break;
> +                       }
> +               } while (PKT_CONTINUE == rc);
> +
> +               /* Check for drop */
> +               if (PKT_DROP == rc)
> +                       odp_packet_free(pkt);
> +
> +               /* Print packet counts every once in a while */
> +               if (PKT_DONE == rc) {
> +                       if (odp_unlikely(pkt_cnt++ % 1000 == 0)) {
> +                               printf("  [%02i] pkt_cnt:%lu\n", thr,
> pkt_cnt);
> +                               fflush(NULL);
> +                       }
> +               }
> +       }
> +
> +       /* unreachable */
> +       return NULL;
> +}
> +
> +/**
> + * ODP ipsec example main function
> + */
> +int
> +main(int argc, char *argv[])
> +{
> +       odp_linux_pthread_t thread_tbl[MAX_WORKERS];
> +       odp_buffer_pool_t pool;
> +       int thr_id;
> +       int num_workers;
> +       void *pool_base;
> +       int i;
> +       int first_core;
> +       int core_count;
> +       int stream_count;
> +
> +       /* Init ODP before calling anything else */
> +       if (odp_init_global()) {
> +               ODP_ERR("Error: ODP global init failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       odp_crypto_init(32);
> +
> +       /* Reserve memory for args from shared mem */
> +       args = odp_shm_reserve("shm_args", sizeof(args_t),
> ODP_CACHE_LINE_SIZE);
> +       if (args == NULL) {
> +               ODP_ERR("Error: shared mem alloc failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +       memset(args, 0, sizeof(*args));
> +
> +       /* Must init our databases before parsing args */
> +       ipsec_init_pre();
> +       init_fwd_db();
> +       init_loopback_db();
> +       init_stream_db();
> +
> +       /* Parse and store the application arguments */
> +       parse_args(argc, argv, &args->appl);
> +
> +       /* Print both system and application information */
> +       print_info(NO_PATH(argv[0]), &args->appl);
> +
> +       core_count  = odp_sys_core_count();
> +       num_workers = core_count;
> +
> +       if (args->appl.core_count)
> +               num_workers = args->appl.core_count;
> +
> +       if (num_workers > MAX_WORKERS)
> +               num_workers = MAX_WORKERS;
> +
> +       printf("Num worker threads: %i\n", num_workers);
> +
> +       /* Create a barrier to synchronize thread startup */
> +       odp_barrier_init_count(&sync_barrier, num_workers);
> +
> +       /*
> +        * By default core #0 runs Linux kernel background tasks.
> +        * Start mapping thread from core #1
> +        */
> +       first_core = (core_count == 1) ? 0 : 1;
> +       printf("First core:         %i\n\n", first_core);
> +
> +       /* Init this thread */
> +       thr_id = odp_thread_create(0);
> +       odp_init_local(thr_id);
> +
> +       /* Create packet buffer pool */
> +       pool_base = odp_shm_reserve("shm_packet_pool",
> +                                   SHM_PKT_POOL_SIZE,
> ODP_CACHE_LINE_SIZE);
> +       if (pool_base == NULL) {
> +               ODP_ERR("Error: packet pool mem alloc failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       pool = odp_buffer_pool_create("packet_pool", pool_base,
> +                                     SHM_PKT_POOL_SIZE,
> +                                     SHM_PKT_POOL_BUF_SIZE,
> +                                     ODP_CACHE_LINE_SIZE,
> +                                     ODP_BUFFER_TYPE_PACKET);
> +       if (pool == ODP_BUFFER_POOL_INVALID) {
> +               ODP_ERR("Error: packet pool create failed.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Populate our IPsec cache */
> +       printf("Using %s mode for crypto API\n\n",
> +              (args->appl.async) ? "ASYNC" : "SYNC");
> +       ipsec_init_post(args->appl.async);
> +
> +       /* Initialize interfaces (which resolves FWD DB entries */
> +       for (i = 0; i < args->appl.if_count; i++) {
> +               if (!strncmp("loop", args->appl.if_names[i],
> strlen("loop")))
> +                       initialize_loop(args->appl.if_names[i]);
> +               else
> +                       initialize_intf(args->appl.if_names[i],
> +                                       args->appl.type,
> +                                       args->appl.fanout);
> +       }
> +
> +       /* If we have test streams build them before starting workers */
> +       resolve_stream_db();
> +       stream_count = create_stream_db_inputs();
> +
> +       /*
> +        * Create and init worker threads
> +        */
> +       memset(thread_tbl, 0, sizeof(thread_tbl));
> +       for (i = 0; i < num_workers; ++i) {
> +               int core;
> +
> +               core = (first_core + i) % core_count;
> +
> +               /*
> +                * Create threads one-by-one instead of all-at-once,
> +                * because each thread might get different arguments.
> +                * Calls odp_thread_create(cpu) for each thread
> +                */
> +               odp_linux_pthread_create(thread_tbl, 1, core, pktio_thread,
> +                                        &args->thread[i]);
> +       }
> +
> +       /*
> +        * If there are streams attempt to verify them else
> +        * wait indefinitely
> +        */
> +       if (stream_count) {
> +               boolean done;
> +
> +               do {
> +                       done = verify_stream_db_outputs();
> +                       sleep(1);
> +               } while (!done);
> +               printf("All received\n");
> +       } else {
> +               odp_linux_pthread_join(thread_tbl, num_workers);
> +       }
> +
> +       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 *names, *str, *token, *save;
> +       size_t len;
> +       int i;
> +       static struct option longopts[] = {
> +               {"count", required_argument, NULL, 'c'},
> +               {"interface", required_argument, NULL, 'i'},    /* return
> 'i' */
> +               {"mode", required_argument, NULL, 'm'},         /* return
> 'm' */
> +               {"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' */
> +               {"stream", required_argument, NULL, 's'},       /* return
> 's' */
> +               {"help", no_argument, NULL, 'h'},               /* return
> 'h' */
> +               {NULL, 0, NULL, 0}
> +       };
> +
> +       appl_args->type = 3;  /* 3: ODP_PKTIO_TYPE_SOCKET_MMAP */
> +       appl_args->fanout = 0; /* turn off fanout by default for mmap */
> +       appl_args->async = 0;  /* turn off async crypto API by default */
> +
> +       while (1) {
> +               opt = getopt_long(argc, argv, "+c:i:m:t:f:h:r:p:a:e:s:",
> +                                 longopts, &long_index);
> +
> +               if (opt == -1)
> +                       break;  /* No more options */
> +
> +               switch (opt) {
> +               case 'c':
> +                       appl_args->core_count = atoi(optarg);
> +                       break;
> +                       /* parse packet-io interface names */
> +               case 'i':
> +                       len = strlen(optarg);
> +                       if (len == 0) {
> +                               usage(argv[0]);
> +                               exit(EXIT_FAILURE);
> +                       }
> +                       len += 1;       /* add room for '\0' */
> +
> +                       names = malloc(len);
> +                       if (names == NULL) {
> +                               usage(argv[0]);
> +                               exit(EXIT_FAILURE);
> +                       }
> +
> +                       /* count the number of tokens separated by ',' */
> +                       strcpy(names, optarg);
> +                       for (str = names, i = 0;; str = NULL, i++) {
> +                               token = strtok_r(str, ",", &save);
> +                               if (token == NULL)
> +                                       break;
> +                       }
> +                       appl_args->if_count = i;
> +
> +                       if (appl_args->if_count == 0) {
> +                               usage(argv[0]);
> +                               exit(EXIT_FAILURE);
> +                       }
> +
> +                       /* allocate storage for the if names */
> +                       appl_args->if_names =
> +                               calloc(appl_args->if_count, sizeof(char
> *));
> +
> +                       /* store the if names (reset names string) */
> +                       strcpy(names, optarg);
> +                       for (str = names, i = 0;; str = NULL, i++) {
> +                               token = strtok_r(str, ",", &save);
> +                               if (token == NULL)
> +                                       break;
> +                               appl_args->if_names[i] = token;
> +                       }
> +                       break;
> +
> +               case 't':
> +                       appl_args->type = atoi(optarg);
> +                       break;
> +
> +               case 'f':
> +                       appl_args->fanout = atoi(optarg);
> +                       break;
> +
> +               case 'm':
> +                       appl_args->async = atoi(optarg);
> +                       break;
> +
> +               case 'r':
> +                       create_fwd_db_entry(optarg);
> +                       break;
> +
> +               case 'p':
> +                       create_sp_db_entry(optarg);
> +                       break;
> +
> +               case 'a':
> +                       create_sa_db_entry(optarg, FALSE);
> +                       break;
> +
> +               case 'e':
> +                       create_sa_db_entry(optarg, TRUE);
> +                       break;
> +
> +               case 's':
> +                       create_stream_db_entry(optarg);
> +                       break;
> +
> +               case 'h':
> +                       usage(argv[0]);
> +                       exit(EXIT_SUCCESS);
> +                       break;
> +
> +               default:
> +                       break;
> +               }
> +       }
> +
> +       if (appl_args->if_count == 0) {
> +               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"
> +              "Core count:      %i\n"
> +              "\n",
> +              odp_version_api_str(), odp_sys_cpu_model_str(),
> odp_sys_cpu_hz(),
> +              odp_sys_cache_line_size(), odp_sys_core_count());
> +
> +       printf("Running ODP appl: \"%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();
> +       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"
> +              " -t, --type   1: ODP_PKTIO_TYPE_SOCKET_BASIC\n"
> +              "              2: ODP_PKTIO_TYPE_SOCKET_MMSG\n"
> +              "              3: ODP_PKTIO_TYPE_SOCKET_MMAP\n"
> +              "              4: ODP_PKTIO_TYPE_NETMAP\n"
> +              "         Default: 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n"
> +              " -f, --fanout 0: off 1: on (Default 1: on)\n"
> +              " -m, --mode   0: sync 1: async crypto API (default 0)\n"
> +              "\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"
> +              "\n"
> +              "  Where: NextHopMAC is raw hex/dot notation, i.e.
> 03.BA.44.9A.CE.02\n"
> +              "         IP is decimal/dot notation, i.e. 192.168.1.1\n"
> +              "         SubNet is decimal/dot/slash notation, i.e
> 192.168.0.0/16\n <http://192.168.0.0/16/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
> <http://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
> <http://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"
> +              "\n"
> +              "Optional OPTIONS\n"
> +              "  -c, --count <number> Core count.\n"
> +              "  -h, --help           Display help and exit.\n"
> +              "\n", NO_PATH(progname), NO_PATH(progname)
> +               );
> +}
> diff --git a/test/ipsec/run_ah_in.sh b/test/ipsec/run_ah_in.sh
> new file mode 100755
> index 0000000..1618071
> --- /dev/null
> +++ b/test/ipsec/run_ah_in.sh
> @@ -0,0 +1,13 @@
> +#!/bin/bash
> +#
> +# Test input AH
> +#  - 2 loop interfaces
> +#  - 10 packets
> +#  - SYNC API mode
> +#
> +./odp_ipsec -i loop1,loop2 \
> +-r 192.168.111.2/32:loop1:08.00.27.76.B5.E0 \
> +-p 192.168.222.0/24:192.168.111.0/24:in:ah \
> +-a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8 \
> +-s 192.168.222.2:192.168.111.2:loop2:loop1:10:100 \
> +-c 2 -f 0 -m 0
> diff --git a/test/ipsec/run_ah_out.sh b/test/ipsec/run_ah_out.sh
> new file mode 100755
> index 0000000..d61b52a
> --- /dev/null
> +++ b/test/ipsec/run_ah_out.sh
> @@ -0,0 +1,12 @@
> +#!/bin/bash
> +#
> +# Test output AH
> +#  - 2 loop interfaces
> +#  - 10 packets
> +#  - SYNC API mode
> + ./odp_ipsec -i loop1,loop2 \
> +-r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB \
> +-p 192.168.111.0/24:192.168.222.0/24:out:ah \
> +-a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6 \
> +-s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
> +-c 2 -f 0 -m 0
> diff --git a/test/ipsec/run_both_in.sh b/test/ipsec/run_both_in.sh
> new file mode 100755
> index 0000000..3b27409
> --- /dev/null
> +++ b/test/ipsec/run_both_in.sh
> @@ -0,0 +1,14 @@
> +#!/bin/bash
> +#
> +# Test AH and ESP input
> +#  - 2 loop interfaces
> +#  - 10 packets
> +#  - SYNC API mode
> +./odp_ipsec -i loop1,loop2 \
> +-r 192.168.111.2/32:loop1:08.00.27.76.B5.E0 \
> +-p 192.168.222.0/24:192.168.111.0/24:in:both \
> +-a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8 \
> +-e 192.168.222.2:192.168.111.2:\
> +3des:301:c966199f24d095f3990a320d749056401e82b26570320292 \
> +-s 192.168.222.2:192.168.111.2:loop2:loop1:10:100 \
> +-c 2 -f 0 -m 0
> diff --git a/test/ipsec/run_both_out.sh b/test/ipsec/run_both_out.sh
> new file mode 100755
> index 0000000..37bb1e4
> --- /dev/null
> +++ b/test/ipsec/run_both_out.sh
> @@ -0,0 +1,14 @@
> +#!/bin/bash
> +#
> +# Test AH and ESP output
> +#  - 2 loop interfaces
> +#  - 10 packets
> +#  - SYNC API mode
> +./odp_ipsec -i loop1,loop2 \
> +-r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB \
> +-p 192.168.111.0/24:192.168.222.0/24:out:both \
> +-e 192.168.111.2:192.168.222.2:\
> +3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
> +-a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6 \
> +-s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
> +-c 2 -f 0 -m 0
> diff --git a/test/ipsec/run_esp_in.sh b/test/ipsec/run_esp_in.sh
> new file mode 100755
> index 0000000..a9a0db3
> --- /dev/null
> +++ b/test/ipsec/run_esp_in.sh
> @@ -0,0 +1,13 @@
> +#!/bin/bash
> +#
> +# Test input ESP
> +#  - 2 loop interfaces
> +#  - 10 packets
> +#  - SYNC API mode
> +./odp_ipsec -i loop1,loop2 \
> +-r 192.168.111.2/32:loop1:08.00.27.76.B5.E0 \
> +-p 192.168.222.0/24:192.168.111.0/24:in:esp \
> +-e 192.168.222.2:192.168.111.2:\
> +3des:301:c966199f24d095f3990a320d749056401e82b26570320292 \
> +-s 192.168.222.2:192.168.111.2:loop2:loop1:10:100 \
> +-c 2 -f 0 -m 0
> diff --git a/test/ipsec/run_esp_out.sh b/test/ipsec/run_esp_out.sh
> new file mode 100755
> index 0000000..f87311c
> --- /dev/null
> +++ b/test/ipsec/run_esp_out.sh
> @@ -0,0 +1,13 @@
> +#!/bin/bash
> +#
> +# Test output ESP
> +#  - 2 loop interfaces
> +#  - 10 packets
> +#  - SYNC API mode
> +./odp_ipsec -i loop1,loop2 \
> +-r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB \
> +-p 192.168.111.0/24:192.168.222.0/24:out:esp \
> +-e 192.168.111.2:192.168.222.2:\
> +3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
> +-s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
> +-c 2 -f 0 -m 0
> diff --git a/test/ipsec/run_simple.sh b/test/ipsec/run_simple.sh
> new file mode 100755
> index 0000000..1d80fa9
> --- /dev/null
> +++ b/test/ipsec/run_simple.sh
> @@ -0,0 +1,10 @@
> +#!/bin/bash
> +#
> +# Simple router test
> +#  - 2 loop interfaces
> +#  - 10 packets
> +#  - SYNC API mode
> +./odp_ipsec -i loop1,loop2 \
> +-r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB \
> +-s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
> +-c 2 -f 0 -m 0
> --
> 1.9.2
>
>
> _______________________________________________
> lng-odp mailing list
> lng-odp@lists.linaro.org
> http://lists.linaro.org/mailman/listinfo/lng-odp
>
>
>
>
>
> --
>
> *Mike Holmes*
>
> Linaro Technical Manager / Lead
>
> LNG - ODP
>
diff mbox

Patch

diff --git a/include/helper/odp_ipsec.h b/include/helper/odp_ipsec.h
new file mode 100644
index 0000000..9d29651
--- /dev/null
+++ b/include/helper/odp_ipsec.h
@@ -0,0 +1,73 @@ 
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP IPSec headers
+ */
+
+#ifndef ODP_IPSEC_H_
+#define ODP_IPSEC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_std_types.h>
+#include <odp_byteorder.h>
+#include <odp_align.h>
+#include <odp_debug.h>
+
+#define ODP_ESPHDR_LEN      8    /**< IPSec ESP header length */
+#define ODP_ESPTRL_LEN      2    /**< IPSec ESP trailer length */
+#define ODP_AHHDR_LEN      12    /**< IPSec AH header length */
+
+/**
+ * IPSec ESP header
+ */
+typedef struct ODP_PACKED {
+       uint32be_t spi;      /**< Security Parameter Index */
+       uint32be_t seq_no;   /**< Sequence Number */
+       uint8_t    iv[0];    /**< Initialization vector */
+} odp_esphdr_t;
+
+/** @internal Compile time assert */
+ODP_ASSERT(sizeof(odp_esphdr_t) == ODP_ESPHDR_LEN, ODP_ESPHDR_T__SIZE_ERROR);
+
+/**
+ * IPSec ESP trailer
+ */
+typedef struct ODP_PACKED {
+       uint8_t pad_len;      /**< Padding length (0-255) */
+       uint8_t next_header;  /**< Next header protocol */
+       uint8_t icv[0];       /**< Integrity Check Value (optional) */
+} odp_esptrl_t;
+
+/** @internal Compile time assert */
+ODP_ASSERT(sizeof(odp_esptrl_t) == ODP_ESPTRL_LEN, ODP_ESPTRL_T__SIZE_ERROR);
+
+/**
+ * IPSec AH header
+ */
+typedef struct ODP_PACKED {
+       uint8_t    next_header;  /**< Next header protocol */
+       uint8_t    ah_len;       /**< AH header length */
+       uint16be_t pad;          /**< Padding (must be 0) */
+       uint32be_t spi;          /**< Security Parameter Index */
+       uint32be_t seq_no;       /**< Sequence Number */
+       uint8_t    icv[0];       /**< Integrity Check Value */
+} odp_ahhdr_t;
+
+/** @internal Compile time assert */
+ODP_ASSERT(sizeof(odp_ahhdr_t) == ODP_AHHDR_LEN, ODP_AHHDR_T__SIZE_ERROR);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/ipsec/Makefile b/test/ipsec/Makefile
new file mode 100644
index 0000000..be822b5
--- /dev/null
+++ b/test/ipsec/Makefile
@@ -0,0 +1,44 @@ 
+# Copyright (c) 2013, Linaro Limited
+# All rights reserved.
+#
+# SPDX-License-Identifier:     BSD-3-Clause
+
+ODP_ROOT = ../..
+ODP_APP  = odp_ipsec
+
+include $(ODP_ROOT)/Makefile.inc
+include ../Makefile.inc
+
+OBJS     =
+OBJS    += $(OBJ_DIR)/odp_example_ipsec.o
+
+DEPS     = $(OBJS:.o=.d)
+
+.PHONY: default
+default: $(OBJ_DIR) $(ODP_APP)
+
+-include $(DEPS)
+
+#
+# Compile rules
+#
+$(OBJ_DIR)/%.o: %.c
+       $(ECHO) Compiling $<
+       $(CC) -c -MD $(EXTRA_CFLAGS) $(CFLAGS) -o $@ $<
+
+#
+# Link rule
+#
+$(ODP_APP): $(ODP_LIB) $(OBJS)
+       $(CC) $(LDFLAGS) $(OBJS) $(ODP_LIB) $(STD_LIBS) -lcrypto -o $@
+
+.PHONY: clean
+clean:
+       $(RMDIR) $(OBJ_DIR)
+       $(RM) $(ODP_APP)
+       $(MAKE) -C $(ODP_DIR) clean
+
+.PHONY: install
+install:
+       install -d $(DESTDIR)/share/odp
+       install -m 0755 $(ODP_APP) $(DESTDIR)/share/odp/
diff --git a/test/ipsec/README b/test/ipsec/README
new file mode 100644
index 0000000..7b9dcc3
--- /dev/null
+++ b/test/ipsec/README
@@ -0,0 +1,164 @@ 
+1. Intro
+
+The IPsec example application "odp_ipsec" functions as a simple L3 IPv4 router
+with support IPsec 3DES cipher and HMAC-MD5 authentication in both the transmit
+and receive directions.  Note that only IPsec "transport" mode is supported.
+
+2. Prerequisites
+
+  2.1 SSL development libraries
+
+Development has been done to this point with the openssl-devel libraries,
+the makefile specifically links with "-lcrypto".
+
+3. Topology
+
+The following test topology was used for development.  Each of the VMs
+is running Fedora16.  Sanity testing consists of pinging VM2 from VM0
+such that the packets traverse VM1.  Packets between VM1 and VM2 are
+IPsec AH and ESP encapsulated.
+
+     VM0                            VM1 (UUT)                          VM2
++------------+                  +--------------+                  +------------+
+|            |     (clear)      |              |     (crypto)     |            |
+|            |      subnet      |              |      subnet      |            |
+|       p7p1 |<---------------->| p7p1    p8p1 |<---------------->| p7p1       |
+|        .2  |   192.168.111.0  |  .1      .1  |   192.168.222.0  |  .2        |
+|            |                  |              |                  |            |
++------------+                  +--------------+                  +------------+
+
+4. VM configurations
+
+  4.1 VM0 configuration
+
+VM0 has the follwing interface configuration:
+
+         cat /etc/sysconfig/network-scripts/ifcfg-p7p1
+         DEVICE=p7p1
+         HWADDR=08:00:27:76:B5:E0
+         BOOTPROTO=static
+         IPADDR=192.168.111.2
+         NETMASK=255.255.255.0
+         ONBOOT=yes
+
+In addition, static ARP and IPv4 routes must be added on VM0:
+
+         sudo ip route add 192.168.222.0/24<http://192.168.222.0/24> via 192.168.111.1
+         sudo ip route add 192.168.222.0/24<http://192.168.222.0/24> via 192.168.111.1
+
+  4.2 VM1 configuration
+
+For the unit under test, IP forwarding and IP tables were disabled.
+
+VM1 has the follwing interface configurations:
+
+         cat /etc/sysconfig/network-scripts/ifcfg-p7p1
+         DEVICE=p7p1
+         HWADDR=08:00:27:04:BF:8C
+         BOOTPROTO=static
+         IPADDR=192.168.111.1
+         NETMASK=255.255.255.0
+         ONBOOT=yes
+
+         cat /etc/sysconfig/network-scripts/ifcfg-p8p1
+         DEVICE=p8p1
+         HWADDR=08:00:27:4C:55:CC
+         BOOTPROTO=static
+         IPADDR=192.168.222.1
+         NETMASK=255.255.255.0
+         ONBOOT=yes
+
+The application is launched on VM1 with the following command line
+using a bash script:
+
+         cat test/ipsec/run_test.sh
+         #!/bin/bash
+         sudo ./odp_ipsec -i p7p1,p8p1 \
+         -r 192.168.111.2/32:p7p1:08.00.27.76.B5.E0<http://192.168.111.2/32:p7p1:08.00.27.76.B5.E0> \
+         -r 192.168.222.2/32:p8p1:08.00.27.F5.8B.DB<http://192.168.222.2/32:p8p1:08.00.27.F5.8B.DB> \
+         -p 192.168.111.0/24:192.168.222.0/24:out:both<http://192.168.111.0/24:192.168.222.0/24:out:both> \
+         -e 192.168.111.2:192.168.222.2:3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+         -a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6 \
+         -p 192.168.222.0/24:192.168.111.0/24:in:both<http://192.168.222.0/24:192.168.111.0/24:in:both> \
+         -e 192.168.222.2:192.168.111.2:3des:301:c966199f24d095f3990a320d749056401e82b26570320292 \
+         -a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8 \
+         -c 2 -f 0 -m 0
+
+  4.3 VM2 configuration
+
+VM2 must be setup with an IPsec configuration complementing
+the configuration used by the "odp_ipsec" application running
+on VM1.  The configuration is applied using "setkey"
+
+VM2 has the following setkey configuration file applied:
+
+         cat /media/sf_SharedVM2/setkey_vm2.txt
+         #!/sbin/setkey -f
+
+         # Flush the SAD and SPD
+         flush;
+         spdflush;
+
+         add 192.168.111.2 192.168.222.2 ah 0x200 -A hmac-md5
+         0xa731649644c5dee92cbd9c2e7e188ee6;
+         add 192.168.222.2 192.168.111.2 ah 0x300 -A hmac-md5
+         0x27f6d123d7077b361662fc6e451f65d8;
+
+         add 192.168.111.2 192.168.222.2 esp 0x201 -E 3des-cbc
+         0x656c8523255ccc23a66c1917aa0cf30991fce83532a4b224;
+         add 192.168.222.2 192.168.111.2 esp 0x301 -E 3des-cbc
+         0xc966199f24d095f3990a320d749056401e82b26570320292;
+
+         spdadd 192.168.111.2 192.168.222.2 any -P in ipsec
+                    esp/transport//require
+                    ah/transport//require;
+
+         spdadd 192.168.222.2 192.168.111.2 any -P out ipsec
+                    esp/transport//require
+                    ah/transport//require;
+
+VM2 has the follwing interface configuration:
+
+         cat /etc/sysconfig/network-scripts/ifcfg-p7p1
+         DEVICE=p7p1
+         HWADDR=08:00:27:F5:8B:DB
+         BOOTPROTO=static
+         IPADDR=192.168.222.2
+         NETMASK=255.255.255.0
+         ONBOOT=yes
+
+In addition, static ARP and IPv4 routes must be added on VM2:
+
+         sudo ip route add 192.168.111.0/24<http://192.168.111.0/24> via 192.168.222.1
+         sudo arp -s 192.168.222.1 08:00:27:4c:55:cc
+
+5. Sanity Test with Real Traffic
+
+Once all three VMs have been configured and static ARP and route
+entries added, VM0 should be able to ping VM2 at the 192.168.222.2
+address.
+
+At VM0 console issue the ping to VM2's address:
+
+         sudo ping -c 2 -i 0.1 192.168.222.2
+         PING 192.168.222.2 (192.168.222.2) 56(84) bytes of data.
+         64 bytes from 192.168.222.2<http://192.168.222.2>: icmp_req=1 ttl=64 time=33.9 ms
+         64 bytes from 192.168.222.2<http://192.168.222.2>: icmp_req=2 ttl=64 time=23.3 ms
+
+At VM2 console use tcpdump to observe IPsec packets :
+
+         sudo tcpdump -nt -i p7p1
+         tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
+         listening on p7p1, link-type EN10MB (Ethernet), capture size 65535 bytes
+
+         IP 192.168.111.2 > 192.168.222.2<http://192.168.222.2>: AH(spi=0x00000200,seq=0x6): ESP(spi=0x00000201,seq=0x6), length 88
+         IP 192.168.222.2 > 192.168.111.2<http://192.168.111.2>: AH(spi=0x00000300,seq=0x7a): ESP(spi=0x00000301,seq=0x7a), length 88
+         IP 192.168.111.2 > 192.168.222.2<http://192.168.222.2>: AH(spi=0x00000200,seq=0x7): ESP(spi=0x00000201,seq=0x7), length 88
+         IP 192.168.222.2 > 192.168.111.2<http://192.168.111.2>: AH(spi=0x00000300,seq=0x7b): ESP(spi=0x00000301,seq=0x7b), length 88
+
+6. Standalone Loopback Tests
+
+BASH batch files are now included to run several simple loopback tests that
+do not require any packet IO.  The scripts create internal "loopback" (not
+real Linux loopback interfaces but simply ODP queues) as opposed to packet
+interfaces.
diff --git a/test/ipsec/odp_example_ipsec.c b/test/ipsec/odp_example_ipsec.c
new file mode 100644
index 0000000..16fd14e
--- /dev/null
+++ b/test/ipsec/odp_example_ipsec.c
@@ -0,0 +1,3120 @@ 
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * @example odp_example_ipsec.c  ODP basic packet IO cross connect with IPsec test application
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <odp.h>
+#include <odp_align.h>
+#include <odp_crypto.h>
+#include <helper/odp_linux.h>
+#include <helper/odp_packet_helper.h>
+#include <helper/odp_eth.h>
+#include <helper/odp_ip.h>
+#include <helper/odp_icmp.h>
+#include <helper/odp_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 <openssl/des.h>
+#include <openssl/rand.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+
+#define boolean bool
+#define TRUE  1
+#define FALSE 0
+
+#define MAX_WORKERS            32
+#define SHM_PKT_POOL_SIZE      (1 * 1024 * 4096)
+#define SHM_PKT_POOL_BUF_SIZE  4096
+#define MAX_PKT_BURST          16
+
+#define MAX_DB 32
+
+
+#define LOOP_DEQ_MULTIPLE     0     /**< enable multi packet dequeue */
+#define MAX_LOOPBACK          10
+
+#define MSG_POOL_SIZE         (1024 * 1024) /**< Message pool size */
+#define MSG_POOL_BUF_SIZE     (1024)
+
+#define STREAM_MAGIC 0xBABE01234567CAFE
+
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+       int core_count;
+       int if_count;           /**< Number of interfaces to be used */
+       char **if_names;        /**< Array of pointers to interface names */
+       int type;               /**< Packet IO type */
+       int fanout;             /**< Packet IO fanout */
+       int async;              /**< Crypto API preferred mode (1 = async) */
+       odp_buffer_pool_t pool; /**< Buffer pool for packet IO */
+} appl_args_t;
+
+/**
+ * Thread specific arguments (currently none, leave as placeholder)
+ */
+typedef struct {
+} thread_args_t;
+
+/**
+ * Grouping of both parsed CL args and thread specific args - alloc together
+ */
+typedef struct {
+       /** Application (parsed) arguments */
+       appl_args_t appl;
+       /** Thread specific arguments */
+       thread_args_t thread[MAX_WORKERS];
+} args_t;
+
+/**
+ * Stream packet header
+ */
+typedef struct ODP_PACKED stream_pkt_hdr_s {
+       uint64be_t magic;    /**< Stream magic value for verification */
+       uint8_t    data[0];  /**< Incrementing data stream */
+} stream_pkt_hdr_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 crypto session creation completion events */
+static odp_buffer_pool_t msg_pool = ODP_BUFFER_POOL_INVALID;
+
+/** ATOMIC queue for IPsec sequence number assignment */
+static odp_queue_t seqnumq;
+
+/** ORDERED queue (eventually) for per packet crypto API completion events */
+static odp_queue_t completionq;
+
+/** IPv4 helpers for data length and uint8t pointer */
+#define ipv4_data_len(ip) (odp_be_to_cpu_16(ip->tot_len) - sizeof(odp_ipv4hdr_t))
+#define ipv4_data_p(ip) ((uint8_t *)((odp_ipv4hdr_t *)ip + 1))
+
+/** Helper for calculating encode length using data length and block size */
+#define ESP_ENCODE_LEN(x, b) ((((x) + (b - 1)) / b) * b)
+
+/** 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))
+
+/** Synchronize threads before packet processing begins */
+static odp_barrier_t sync_barrier;
+
+/**
+ * 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;
+
+/**
+ * Packet processing states/steps
+ */
+typedef enum {
+       PKT_STATE_INPUT_VERIFY,        /**< Verify IPv4 and ETH */
+       PKT_STATE_IPSEC_IN_CLASSIFY,   /**< Initiate input IPsec */
+       PKT_STATE_IPSEC_IN_FINISH,     /**< Finish input IPsec */
+       PKT_STATE_ROUTE_LOOKUP,        /**< Use DST IP to find output IF */
+       PKT_STATE_IPSEC_OUT_CLASSIFY,  /**< Intiate output IPsec */
+       PKT_STATE_IPSEC_OUT_SEQ,       /**< Assign IPsec sequence numbers */
+       PKT_STATE_IPSEC_OUT_FINISH,    /**< Finish output IPsec */
+       PKT_STATE_TRANSMIT,            /**< Send packet to output IF queue */
+} pkt_state_e;
+
+/**
+ * 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;
+
+/**
+ * Per packet IPsec processing context
+ */
+typedef struct {
+       uint8_t  ip_tos;         /**< Saved IP TOS value */
+       uint16_t ip_frag_offset; /**< Saved IP flags value */
+       uint8_t  ip_ttl;         /**< Saved IP TTL value */
+       int      hdr_len;        /**< Length of IPsec headers */
+       int      trl_len;        /**< Length of IPsec trailers */
+       uint16_t ah_offset;      /**< Offset of AH header from buffer start */
+       uint16_t esp_offset;     /**< Offset of ESP header from buffer start */
+
+       /* Output only */
+       struct odp_crypto_op_params params;  /**< Parameters for crypto call */
+       uint32_t *ah_seq;                    /**< AH sequence number location */
+       uint32_t *esp_seq;                   /**< ESP sequence number location */
+} ipsec_ctx_t;
+
+/**
+ * Per packet processing context
+ */
+typedef union {
+       struct {
+               pkt_state_e state;   /**< Next processing step */
+               ipsec_ctx_t ipsec;   /**< IPsec specific context */
+               odp_queue_t outq;    /**< transmit queue */
+       };
+       uint8_t pad[256];            /**< Ensure no overlap with crypto */
+} pkt_ctx_t;
+
+/**
+ * Adjust IPv4 length
+ *
+ * @param ip   Pointer to IPv4 header
+ * @param adj  Signed adjustment value
+ */
+static
+void ipv4_adjust_len(odp_ipv4hdr_t *ip, int adj)
+{
+       ip->tot_len = odp_cpu_to_be_16(odp_be_to_cpu_16(ip->tot_len) + adj);
+}
+
+/**
+ * Get per packet processing context from packet buffer
+ *
+ * This is a hack for the moment to place it at the end of the
+ * buffer.  Note that the crypto library also has a similar hack
+ * so we space our packet context 256 bytes from the end to prevent
+ * overlap.
+ *
+ * @param buf  Buffer associated with packet
+ *
+ * @return pointer to context area
+ */
+static
+pkt_ctx_t *get_ctx_from_buf(odp_buffer_t buf)
+{
+       uint8_t   *temp;
+       pkt_ctx_t *ctx;
+
+       /*
+        * Setup packet context,
+        */
+       temp  = odp_buffer_addr(buf);
+       temp += odp_buffer_size(buf);
+       temp -= sizeof(*ctx);
+       ctx = (pkt_ctx_t *)(void *)temp;
+       return ctx;
+}
+
+/**
+ * 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
+int match_ip_range(uint32_t addr, ip_addr_range_t *range)
+{
+       return (range->addr == (addr & range->mask));
+}
+
+/**
+ * Query MAC address associated with an interface
+ *
+ * @param intf    String name of the interface
+ * @param src_mac MAC address used by the interface
+ *
+ * @return 0 if successful else -1
+ */
+static
+int query_mac_address(char *intf, uint8_t *src_mac)
+{
+       int sd;
+       struct ifreq ifr;
+
+       /* Get a socket descriptor */
+       sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+       if (sd < 0) {
+               ODP_ERR("Error: socket() failed for %s\n", intf);
+               return -1;
+       }
+
+       /* Use ioctl() to look up interface name and get its MAC address */
+       memset(&ifr, 0, sizeof(ifr));
+       snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", intf);
+       if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) {
+               ODP_ERR("Error: ioctl() failed for %s\n", intf);
+               return -1;
+       }
+       memcpy(src_mac, ifr.ifr_hwaddr.sa_data, 6);
+
+       /* Fini */
+       close(sd);
+
+       return 0;
+}
+
+/**
+ * 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 cipher     TRUE indicates cipher key else authentication
+ * @param alg        Cipher/authentication algorithm associated with the key
+ *
+ * @return 0 if successful else -1
+ */
+static
+int parse_key_string(char *keystring,
+                    odp_key_t *key,
+                    boolean cipher,
+                    odp_crypto_alg_t alg)
+{
+       /* Brute force */
+       if (cipher && (alg.cipher == ODP_CIPHER_ALG_3DES_CBC)) {
+               if (48 == strlen(keystring)) {
+                       int idx;
+                       char temp[3];
+
+                       /* K1 */
+                       for (idx = 0; idx < 8; idx++) {
+                               temp[0] = *keystring++;
+                               temp[1] = *keystring++;
+                               temp[2] = 0;
+                               key->des.k1[idx] = strtol(temp, NULL, 16);
+                       }
+
+                       /* K2 */
+                       for (idx = 0; idx < 8; idx++) {
+                               temp[0] = *keystring++;
+                               temp[1] = *keystring++;
+                               temp[2] = 0;
+                               key->des.k2[idx] = strtol(temp, NULL, 16);
+                       }
+
+                       /* K3 */
+                       for (idx = 0; idx < 8; idx++) {
+                               temp[0] = *keystring++;
+                               temp[1] = *keystring++;
+                               temp[2] = 0;
+                               key->des.k3[idx] = strtol(temp, NULL, 16);
+                       }
+                       return 0;
+               }
+       }
+       if (!cipher && (alg.auth == ODP_AUTH_ALG_MD5_96)) {
+               if (32 == strlen(keystring)) {
+                       int idx;
+                       char temp[3];
+
+                       for (idx = 0; idx < 16; idx++) {
+                               temp[0] = *keystring++;
+                               temp[1] = *keystring++;
+                               temp[2] = 0;
+                               key->md5.key[idx] = strtol(temp, NULL, 16);
+                       }
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+/**
+ * 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
+int parse_mac_string(char *macaddress, uint8_t *mac)
+{
+       int macwords[6];
+       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;
+}
+
+/**
+ * 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
+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 address
+ *
+ * @param b    Pointer to buffer to store string
+ * @param addr IPv4 address
+ *
+ * @return Pointer to supplied buffer
+ */
+static
+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;
+}
+
+/**
+ * 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
+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
+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 loop interface index
+ *
+ * @param b     Pointer to buffer to parse
+ *
+ * @return interface index (0 to (MAX_LOOPBACK - 1)) else -1
+ */
+static
+int loop_if_index(char *b)
+{
+       int ret;
+       int idx;
+
+       /* Derive loopback interface index */
+       ret = sscanf(b, "loop%d", &idx);
+       if ((1 != ret) || (idx >= MAX_LOOPBACK))
+               return -1;
+       return idx;
+}
+
+/**
+ * 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
+int locate_ipsec_headers(odp_ipv4hdr_t *ip,
+                        odp_ahhdr_t **ah_p,
+                        odp_esphdr_t **esp_p)
+{
+       uint8_t *in = ipv4_data_p(ip);
+       odp_ahhdr_t *ah = NULL;
+       odp_esphdr_t *esp = NULL;
+
+       if (ODP_IPPROTO_AH == ip->proto) {
+               ah = (odp_ahhdr_t *)in;
+               in += ((ah)->ah_len + 2) * 4;
+               if (ODP_IPPROTO_ESP == ah->next_header) {
+                       esp = (odp_esphdr_t *)in;
+                       in += sizeof(odp_esphdr_t);
+               }
+       } else if (ODP_IPPROTO_ESP == ip->proto) {
+               esp = (odp_esphdr_t *)in;
+               in += sizeof(odp_esphdr_t);
+       }
+
+       *ah_p = ah;
+       *esp_p = esp;
+       return in - (ipv4_data_p(ip));
+}
+
+/**
+ * Loopback database entry structure
+ */
+typedef struct loopback_db_entry_s {
+       odp_queue_t   inq_def;
+       odp_queue_t   outq_def;
+       uint8_t       mac[6];
+} loopback_db_entry_t;
+
+typedef struct loopback_db_s {
+       loopback_db_entry_t  intf[MAX_LOOPBACK];
+} loopback_db_t;
+
+static loopback_db_t *loopback_db;
+
+/** Initialize loopback database global control structure */
+static
+void init_loopback_db(void)
+{
+       int idx;
+
+       loopback_db = odp_shm_reserve("loopback_db",
+                                     sizeof(loopback_db_t),
+                                     ODP_CACHE_LINE_SIZE);
+       if (loopback_db == NULL) {
+               ODP_ERR("Error: shared mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       memset(loopback_db, 0, sizeof(*loopback_db));
+
+       for (idx = 0; idx < MAX_LOOPBACK; idx++) {
+               loopback_db->intf[idx].inq_def = ODP_QUEUE_INVALID;
+               loopback_db->intf[idx].outq_def = ODP_QUEUE_INVALID;
+       }
+}
+/**
+ * Security Assocation (SA) data base entry
+ */
+typedef struct sa_db_entry_s {
+       struct sa_db_entry_s *next;      /**< Next entry on list */
+       boolean               cipher;    /**< TRUE if cipher else auth */
+       uint32_t              src_ip;    /**< Source IPv4 address */
+       uint32_t              dst_ip;    /**< Desitnation IPv4 address */
+       uint32_t              spi;       /**< Security Parameter Index */
+       odp_crypto_alg_t      alg;       /**< Cipher/auth algorithm */
+       odp_key_t             key;       /**< Cipher/auth key */
+       uint32_t              block_len; /**< Cipher block length */
+       uint32_t              iv_len;    /**< Initialization Vector length */
+       uint32_t              icv_len;   /**< Integrity Check Value length */
+} sa_db_entry_t;
+
+/**
+ * Security Assocation (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;
+
+/** Global pointer to sa db */
+static sa_db_t *sa_db;
+
+/** Initialize SA database global control structure */
+static
+void init_sa_db(void)
+{
+       sa_db = odp_shm_reserve("shm_sa_db",
+                               sizeof(sa_db_t),
+                               ODP_CACHE_LINE_SIZE);
+       if (sa_db == NULL) {
+               ODP_ERR("Error: shared mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       memset(sa_db, 0, sizeof(*sa_db));
+}
+
+/**
+ * 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
+ *
+ * @return 0 if successful else -1
+ */
+static
+int create_sa_db_entry(char *input, boolean cipher)
+{
+       int pos;
+       char *local, *str, *save;
+       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 (local == NULL)
+               return -1;
+       strcpy(local, input);
+
+       /* Set cipher versus auth */
+       entry->cipher = cipher;
+
+       /* count the number of tokens separated by ',' */
+       for (str = local, save = NULL, pos = 0;; str = NULL, pos++) {
+               char *token = strtok_r(str, ":", &save);
+
+               /* Check for no more tokens */
+               if (token == NULL)
+                       break;
+
+               /* Parse based on postion */
+               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.cipher =
+                                               ODP_CIPHER_ALG_3DES_CBC;
+                                       entry->block_len  = 8;
+                                       entry->iv_len     = 8;
+                               } else {
+                                       entry->alg.cipher = ODP_CIPHER_ALG_NULL;
+                               }
+                       } else {
+                               if (0 == strcmp(token, "md5")) {
+                                       entry->alg.cipher =
+                                               ODP_AUTH_ALG_MD5_96;
+                                       entry->icv_len    = 12;
+                               } else {
+                                       entry->alg.auth = ODP_AUTH_ALG_NULL;
+                               }
+                       }
+                       break;
+               case 3:
+                       entry->spi = strtol(token, NULL, 16);
+                       break;
+               case 4:
+                       parse_key_string(token,
+                                        &entry->key,
+                                        cipher,
+                                        entry->alg);
+                       break;
+               default:
+                       return -1;
+               }
+       }
+
+       /* Verify all positions filled */
+       if (5 != pos)
+               return -1;
+
+       /* Add route to the list */
+       sa_db->index++;
+       entry->next = sa_db->list;
+       sa_db->list = entry;
+
+       return 0;
+}
+
+/**
+ * Display the SA DB
+ */
+static
+void dump_sa_db(void)
+{
+       sa_db_entry_t *entry;
+
+       printf("\n"
+              "Security association table\n"
+              "--------------------------\n");
+
+       for (entry = sa_db->list; NULL != entry; entry = entry->next) {
+               uint32_t idx;
+               char src_ip_str[32];
+               char dst_ip_str[32];
+               uint8_t *p = (uint8_t *)&entry->key;
+
+
+               printf(" %s %s %s %X %d ",
+                      entry->cipher ? "esp" : "ah ",
+                      ipv4_addr_str(src_ip_str, entry->src_ip),
+                      ipv4_addr_str(dst_ip_str, entry->dst_ip),
+                      entry->spi,
+                      entry->cipher ?
+                      (int)entry->alg.cipher :
+                      (int)entry->alg.auth);
+
+               /* Brute force key display */
+               for (idx = 0; idx < sizeof(odp_key_t); idx++)
+                       printf("%02X", *p++);
+
+               printf("\n");
+       }
+}
+
+/**
+ * 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
+ */
+static
+sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src,
+                               ip_addr_range_t *dst,
+                               boolean 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->cipher)
+                       continue;
+               if (!match_ip_range(entry->src_ip, src))
+                       continue;
+               if (!match_ip_range(entry->dst_ip, dst))
+                       continue;
+               break;
+       }
+       return entry;
+}
+
+/**
+ * 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 */
+       boolean               input;       /**< Direction when applied */
+       boolean               esp;         /**< Enable cipher (ESP) */
+       boolean               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 */
+static sp_db_t *sp_db;
+
+/** Initialize SP database global control structure */
+static
+void init_sp_db(void)
+{
+       sp_db = odp_shm_reserve("shm_sp_db",
+                               sizeof(sp_db_t),
+                               ODP_CACHE_LINE_SIZE);
+       if (sp_db == NULL) {
+               ODP_ERR("Error: shared mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       memset(sp_db, 0, sizeof(*sp_db));
+}
+
+/**
+ * Create an SP DB entry
+ *
+ * String is of the format "SrcSubNet:DstSubNet:(in|out):(ah|esp|both)"
+ *
+ * @param input  Pointer to string describing SP
+ *
+ * @return 0 if successful else -1
+ */
+static
+int create_sp_db_entry(char *input)
+{
+       int pos;
+       char *local, *str, *save;
+       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 (local == NULL)
+               return -1;
+       strcpy(local, input);
+
+       /* count the number of tokens separated by ',' */
+       for (str = local, save = NULL, pos = 0;; str = NULL, pos++) {
+               char *token = strtok_r(str, ":", &save);
+
+               /* Check for no more tokens */
+               if (token == NULL)
+                       break;
+
+               /* Parse based on postion */
+               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:
+                       return -1;
+               }
+       }
+
+       /* Verify all positions filled */
+       if (4 != pos)
+               return -1;
+
+       /* Add route to the list */
+       sp_db->index++;
+       entry->next = sp_db->list;
+       sp_db->list = entry;
+
+       return 0;
+}
+
+/**
+ * Display one SP DB entry
+ *
+ * @param entry  Pointer to entry to display
+ */
+static
+void dump_sp_db_entry(sp_db_entry_t *entry)
+{
+       char src_subnet_str[32];
+       char dst_subnet_str[32];
+
+       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");
+}
+
+/**
+ * Display the SP DB
+ */
+static
+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);
+}
+
+/**
+ * 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 */
+       struct {
+               enum  odp_cipher_alg alg;         /**< Cipher algorithm */
+               uint32_t             spi;         /**< Cipher SPI */
+               uint32_t             block_len;   /**< Cipher block length */
+               uint32_t             iv_len;      /**< Cipher IV length */
+               odp_key_t            key;         /**< Cipher key */
+       } esp;
+       struct {
+               enum  odp_auth_alg   alg;         /**< Auth algorithm */
+               uint32_t             spi;         /**< Auth SPI */
+               uint32_t             icv_len;     /**< Auth ICV length */
+               odp_key_t            key;         /**< Auth key */
+       } ah;
+
+       /* Per SA state */
+       struct {
+               odp_crypto_session_t session;     /**< Crypto session handle */
+               uint32_t             esp_seq;     /**< ESP TX sequence number */
+               uint32_t             ah_seq;      /**< AH TX sequence number */
+               uint8_t              iv[32];      /**< ESP IV storage */
+       } state;
+} 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 */
+static ipsec_cache_t *ipsec_cache;
+
+/** Initialize IPsec cache */
+static
+void init_ipsec_cache(void)
+{
+       ipsec_cache = odp_shm_reserve("shm_ipsec_cache",
+                                     sizeof(ipsec_cache_t),
+                                     ODP_CACHE_LINE_SIZE);
+       if (ipsec_cache == NULL) {
+               ODP_ERR("Error: shared mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       memset(ipsec_cache, 0, sizeof(*ipsec_cache));
+}
+
+/**
+ * Encode cipher and auth flags into valid cypto mode value
+ *
+ * @param cipher   Cipher is required
+ * @param auth     Authentication is required
+ *
+ * @return enum representing mode
+ */
+static
+enum odp_crypto_combination derive_comb(boolean cipher, boolean auth)
+{
+       if (cipher && auth)
+               return ODP_CRYPTO_AUTH_CIPHERTEXT;
+       else if (cipher)
+               return ODP_CRYPTO_CIPHER_ONLY;
+       return ODP_CRYPTO_AUTH_ONLY;
+}
+
+/**
+ * Create an entry in the IPsec cache
+ *
+ * @param cipher_sa   Cipher SA DB entry pointer
+ * @param auth_sa     Auth SA DB entry pointer
+ * @param async       Crypto API preferred mode is async
+ * @param in          Direction (input versus output)
+ *
+ * @return 0 if successful else -1
+ */
+static
+int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
+                            sa_db_entry_t *auth_sa,
+                            boolean async,
+                            boolean in)
+{
+       odp_crypto_rc_e rc;
+       struct odp_crypto_session_params params;
+       odp_buffer_t compl_event = ODP_BUFFER_INVALID;
+       ipsec_cache_entry_t *entry;
+       enum odp_crypto_ses_create_err ses_create_rc;
+       odp_crypto_session_t session;
+
+       /* Verify we have a good entry */
+       entry = &ipsec_cache->array[ipsec_cache->index];
+       if (MAX_DB <= ipsec_cache->index)
+               return -1;
+
+       /* Setup parameters and call crypto library to create session */
+       params.op = (in) ? ODP_CRYPTO_OP_DECODE : ODP_CRYPTO_OP_ENCODE;
+       params.comb = derive_comb((NULL != cipher_sa), (NULL != auth_sa));
+       if (async) {
+               params.pref_mode   = ODP_CRYPTO_ASYNC;
+               params.compl_queue = completionq;
+       } else {
+               params.pref_mode   = ODP_CRYPTO_SYNC;
+               params.compl_queue = ODP_QUEUE_INVALID;
+       }
+
+       /* Cipher */
+       if (cipher_sa) {
+               params.cipher_alg  = cipher_sa->alg.cipher;
+               params.cipher_key  = &cipher_sa->key;
+               params.iv = entry->state.iv;
+               params.iv_len = cipher_sa->iv_len;
+       } else {
+               params.cipher_alg = ODP_CIPHER_ALG_NULL;
+               params.iv = NULL;
+               params.iv_len = 0;
+       }
+
+       /* Auth */
+       if (auth_sa) {
+               params.auth_alg = auth_sa->alg.auth;
+               params.auth_key = &auth_sa->key;
+       } else {
+               params.auth_alg = ODP_AUTH_ALG_NULL;
+       }
+
+       /* Generate an IV */
+       if (params.iv_len)
+               odp_hw_random_get(params.iv, &params.iv_len, 1);
+
+       /* Synchronous session create for now */
+       compl_event = odp_buffer_alloc(msg_pool);
+       rc = odp_crypto_session_create(&params, compl_event, ODP_QUEUE_INVALID);
+       if (ODP_CRYPTO_OP_OK != rc)
+               return -1;
+       odp_crypto_get_ses_create_compl_status(compl_event, &ses_create_rc);
+       if (ODP_CRYPTO_SES_CREATE_OK != ses_create_rc)
+               return -1;
+       odp_crypto_get_ses_create_compl_session(compl_event, &session);
+       odp_buffer_free(compl_event);
+
+       /* Copy remainder */
+       if (cipher_sa) {
+               entry->src_ip = cipher_sa->src_ip;
+               entry->dst_ip = cipher_sa->dst_ip;
+               entry->esp.alg = cipher_sa->alg.cipher;
+               entry->esp.spi = cipher_sa->spi;
+               entry->esp.block_len = cipher_sa->block_len;
+               entry->esp.iv_len = cipher_sa->iv_len;
+               memcpy(&entry->esp.key, &cipher_sa->key, sizeof(odp_key_t));
+       }
+       if (auth_sa) {
+               entry->src_ip = auth_sa->src_ip;
+               entry->dst_ip = auth_sa->dst_ip;
+               entry->ah.alg = auth_sa->alg.auth;
+               entry->ah.spi = auth_sa->spi;
+               entry->ah.icv_len = auth_sa->icv_len;
+               memcpy(&entry->ah.key, &auth_sa->key, sizeof(odp_key_t));
+       }
+
+       /* Initialize state */
+       entry->state.esp_seq = 0;
+       entry->state.ah_seq = 0;
+       entry->state.session = session;
+
+       /* 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;
+}
+
+/**
+ * Find a matching IPsec cache entry for input packet
+ *
+ * @param src_ip    Source IPv4 address
+ * @param dst_ip    Destination IPv4 address
+ * @param ah        Pointer to AH header in packet else NULL
+ * @param esp       Pointer to ESP header in packet else NULL
+ *
+ * @return pointer to IPsec cache entry else NULL
+ */
+static
+ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
+                                              uint32_t dst_ip,
+                                              odp_ahhdr_t *ah,
+                                              odp_esphdr_t *esp)
+{
+       ipsec_cache_entry_t *entry = ipsec_cache->in_list;
+
+       /* Look for a hit */
+       for (; NULL != entry; entry = entry->next) {
+               if ((entry->src_ip != src_ip) || (entry->dst_ip != dst_ip))
+                       continue;
+               if (ah &&
+                   ((!entry->ah.alg) ||
+                    (entry->ah.spi != odp_be_to_cpu_32(ah->spi))))
+                       continue;
+               if (esp &&
+                   ((!entry->esp.alg) ||
+                    (entry->esp.spi != odp_be_to_cpu_32(esp->spi))))
+                       continue;
+               break;
+       }
+
+       return entry;
+}
+
+/**
+ * Find a matching IPsec cache entry for output packet
+ *
+ * @param src_ip    Source IPv4 address
+ * @param dst_ip    Destination IPv4 address
+ * @param proto     IPv4 protocol (currently all protocols match)
+ *
+ * @return pointer to IPsec cache entry else NULL
+ */
+static
+ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
+                                               uint32_t dst_ip,
+                                               uint8_t proto ODP_UNUSED)
+{
+       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;
+}
+
+/**
+ * Forwarding data base entry
+ */
+typedef struct fwd_db_entry_s {
+       struct fwd_db_entry_s *next;          /**< Next entry on list */
+       char                  *oif;           /**< Output interface name */
+       odp_queue_t            queue;         /**< Output transmit queue */
+       uint8_t                src_mac[6];    /**< Output source MAC */
+       uint8_t                dst_mac[6];    /**< 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 */
+static fwd_db_t *fwd_db;
+
+/** Initialize FWD DB */
+static
+void init_fwd_db(void)
+{
+       fwd_db = odp_shm_reserve("shm_fwd_db",
+                                sizeof(fwd_db_t),
+                                ODP_CACHE_LINE_SIZE);
+       if (fwd_db == NULL) {
+               ODP_ERR("Error: shared mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       memset(fwd_db, 0, sizeof(*fwd_db));
+}
+
+/**
+ * Create a forwarding database entry
+ *
+ * String is of the format "SubNet:Intf:NextHopMAC"
+ *
+ * @param input  Pointer to string describing route
+ *
+ * @return 0 if successful else -1
+ */
+static
+int create_fwd_db_entry(char *input)
+{
+       int pos;
+       char *local, *str, *save;
+       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 (local == NULL)
+               return -1;
+       strcpy(local, input);
+
+       /* count the number of tokens separated by ',' */
+       for (str = local, save = NULL, pos = 0;; str = NULL, pos++) {
+               char *token = strtok_r(str, ":", &save);
+
+               /* Check for no more tokens */
+               if (token == NULL)
+                       break;
+
+               /* Parse based on postion */
+               switch (pos) {
+               case 0:
+                       parse_ipv4_string(token,
+                                         &entry->subnet.addr,
+                                         &entry->subnet.mask);
+                       break;
+               case 1:
+                       entry->oif = token;
+                       break;
+               case 2:
+                       parse_mac_string(token, entry->dst_mac);
+                       break;
+               default:
+                       return -1;
+               }
+       }
+
+       /* Verify all positions filled */
+       if (3 != pos)
+               return -1;
+
+       /* Reset queue to invalid */
+       entry->queue = ODP_QUEUE_INVALID;
+
+       /* Add route to the list */
+       fwd_db->index++;
+       entry->next = fwd_db->list;
+       fwd_db->list = entry;
+
+       return 0;
+}
+
+/**
+ * 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
+ */
+static
+void resolve_fwd_db(char *intf, odp_queue_t outq, 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->queue = outq;
+               memcpy(entry->src_mac, mac, 6);
+       }
+}
+
+/**
+ * Display one fowarding database entry
+ *
+ * @param entry  Pointer to entry to display
+ */
+static
+void dump_fwd_db_entry(fwd_db_entry_t *entry)
+{
+       char subnet_str[32];
+       char mac_str[32];
+
+       printf(" %s %s %s\n",
+              ipv4_subnet_str(subnet_str, &entry->subnet),
+              entry->oif,
+              mac_addr_str(mac_str, entry->dst_mac));
+}
+
+/**
+ * Display the forwarding database
+ */
+static
+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);
+}
+
+/**
+ * Find a matching forwarding database entry
+ *
+ * @param dst_ip  Destination IPv4 address
+ *
+ * @return pointer to forwarding DB entry else NULL
+ */
+static
+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;
+}
+
+/**
+ * Stream database entry structure
+ */
+typedef struct stream_db_entry_s {
+       struct stream_db_entry_s *next; /**< Next entry on list */
+       int              id;            /**< Stream ID */
+       uint32_t         src_ip;        /**< Source IPv4 address */
+       uint32_t         dst_ip;        /**< Destination IPv4 address */
+       int              count;         /**< Packet count */
+       uint             length;        /**< Packet payload length */
+       uint32_t         created;       /**< Number successfully created */
+       uint32_t         verified;      /**< Number successfully verified */
+       struct {
+               int      loop;          /**< Input loop interface index */
+               uint32_t ah_seq;        /**< AH sequence number if present */
+               uint32_t esp_seq;       /**< ESP sequence number if present */
+               ipsec_cache_entry_t *entry;  /**< IPsec to apply on input */
+       } input;
+       struct {
+               int      loop;          /**< Output loop interface index */
+               ipsec_cache_entry_t *entry;  /**t IPsec to verify on output */
+       } output;
+} stream_db_entry_t;
+
+typedef struct stream_db_s {
+       uint32_t           index;          /**< Index of next available entry */
+       stream_db_entry_t *list;           /**< List of active entries */
+       stream_db_entry_t  array[MAX_DB];  /**< Entry storage */
+} stream_db_t;
+
+static stream_db_t *stream_db;
+
+/** Initialize stream database global control structure */
+static
+void init_stream_db(void)
+{
+       stream_db = odp_shm_reserve("stream_db",
+                                   sizeof(stream_db_t),
+                                   ODP_CACHE_LINE_SIZE);
+       if (stream_db == NULL) {
+               ODP_ERR("Error: shared mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       memset(stream_db, 0, sizeof(*stream_db));
+}
+
+/**
+ * Create an stream DB entry
+ *
+ * String is of the format "SrcIP:DstIP:InInt:OutIntf:Count:Length"
+ *
+ * @param input  Pointer to string describing stream
+ *
+ * @return 0 if successful else -1
+ */
+static
+int create_stream_db_entry(char *input)
+{
+       int pos;
+       char *local, *str, *save;
+       stream_db_entry_t *entry = &stream_db->array[stream_db->index];
+
+       /* Verify we have a good entry */
+       if (MAX_DB <= stream_db->index)
+               return -1;
+
+       /* Make a local copy */
+       local = malloc(strlen(input) + 1);
+       if (local == NULL)
+               return -1;
+       strcpy(local, input);
+
+       /* count the number of tokens separated by ',' */
+       for (str = local, save = NULL, pos = 0;; str = NULL, pos++) {
+               char *token = strtok_r(str, ":", &save);
+
+               /* Check for no more tokens */
+               if (token == NULL)
+                       break;
+
+               /* Parse based on postion */
+               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:
+                       entry->input.loop = loop_if_index(token);
+                       if (entry->input.loop < 0) {
+                               ODP_ERR("Error: stream must have input loop\n");
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
+               case 3:
+                       entry->output.loop = loop_if_index(token);
+                       break;
+               case 4:
+                       entry->count = atoi(token);
+                       break;
+               case 5:
+                       entry->length = atoi(token);
+                       if (entry->length < sizeof(stream_pkt_hdr_t))
+                               entry->length = 0;
+                       else
+                               entry->length -= sizeof(stream_pkt_hdr_t);
+                       break;
+               default:
+                       return -1;
+               }
+       }
+
+       /* Verify all positions filled */
+       if (6 != pos)
+               return -1;
+
+       /* Add stream to the list */
+       entry->id = stream_db->index++;
+       entry->next = stream_db->list;
+       stream_db->list = entry;
+
+       return 0;
+}
+
+/**
+ * Create IPv4 packet for stream
+ *
+ * Create one ICMP test packet based on the stream structure.  If an input
+ * IPsec cache entry is associated with the stream, build a packet that should
+ * successfully match that entry and be correctly decoded by it.
+ *
+ * @param stream    Stream DB entry
+ * @param pkt_pool  Packet buffer pool to allocate from
+ *
+ * @return packet else ODP_PACKET_INVALID
+ */
+static
+odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
+                               odp_buffer_pool_t pkt_pool)
+{
+       ipsec_cache_entry_t *entry = stream->input.entry;
+       odp_buffer_t         bfr;
+       odp_packet_t         pkt;
+       uint8_t             *base;
+       uint8_t             *data;
+       uint8_t             *dmac;
+       odp_ethhdr_t        *eth;
+       odp_ipv4hdr_t       *ip;
+       odp_ahhdr_t         *ah = NULL;
+       odp_esphdr_t        *esp = NULL;
+       odp_icmphdr_t       *icmp;
+       stream_pkt_hdr_t    *test;
+       uint                 i;
+
+       /* Get destination MAC address to use */
+       dmac = loopback_db->intf[stream->input.loop].mac;
+
+       /* Get buffer */
+       bfr = odp_buffer_alloc(pkt_pool);
+       if (ODP_BUFFER_INVALID == bfr)
+               return ODP_PACKET_INVALID;
+       pkt = odp_packet_from_buffer(bfr);
+       odp_packet_init(pkt);
+       base = odp_packet_start(pkt);
+       data = odp_packet_start(pkt);
+
+       /* Ethernet */
+       odp_packet_set_l2_offset(pkt, data - base);
+       eth = (odp_ethhdr_t *)data;
+       data += sizeof(*eth);
+
+       memset((char *)eth->src.addr, (0x80 | stream->id), ODP_ETHADDR_LEN);
+       memcpy((char *)eth->dst.addr, dmac, ODP_ETHADDR_LEN);
+       eth->type = odp_cpu_to_be_16(ODP_ETHTYPE_IPV4);
+
+       /* IPv4 */
+       odp_packet_set_l3_offset(pkt, data - base);
+       ip = (odp_ipv4hdr_t *)data;
+       data += sizeof(*ip);
+       odp_packet_set_l4_offset(pkt, data - base);
+
+       /* Wait until almost finished to fill in mutable fields */
+       memset((char *)ip, 0, sizeof(*ip));
+       ip->ver_ihl = 0x45;
+       ip->proto = ODP_IPPROTO_ICMP;
+       ip->id = odp_cpu_to_be_16(stream->id);
+       ip->src_addr = odp_cpu_to_be_32(stream->src_ip);
+       ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip);
+
+       /* AH (if specified) */
+       if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg)) {
+               if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg)
+                       abort();
+
+               ah = (odp_ahhdr_t *)data;
+               data += sizeof(*ah);
+               data += entry->ah.icv_len;
+
+               memset((char *)ah, 0, sizeof(*ah) + entry->ah.icv_len);
+               ah->ah_len = 1 + (entry->ah.icv_len / 4);
+               ah->spi = odp_cpu_to_be_32(entry->ah.spi);
+               ah->seq_no = odp_cpu_to_be_32(stream->input.ah_seq++);
+       }
+
+       /* ESP (if specified) */
+       if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg)) {
+               if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
+                       abort();
+
+               esp = (odp_esphdr_t *)data;
+               data += sizeof(*esp);
+               data += entry->esp.iv_len;
+
+               esp->spi = odp_cpu_to_be_32(entry->esp.spi);
+               esp->seq_no = odp_cpu_to_be_32(stream->input.esp_seq++);
+               RAND_bytes(esp->iv, 8);
+       }
+
+       /* ICMP header so we can see it on wireshark */
+       icmp = (odp_icmphdr_t *)data;
+       data += sizeof(*icmp);
+       icmp->type = ICMP_ECHO;
+       icmp->code = 0;
+       icmp->un.echo.id<http://un.echo.id> = odp_cpu_to_be_16(0x1234);
+       icmp->un.echo.sequence = odp_cpu_to_be_16(stream->created);
+
+       /* Packet payload of incrementing bytes */
+       test = (stream_pkt_hdr_t *)data;
+       data += sizeof(*test);
+       test->magic = odp_cpu_to_be_64(STREAM_MAGIC);
+       for (i = 0; i < stream->length; i++)
+               *data++ = (uint8_t)i;
+
+       /* Close ICMP */
+       icmp->chksum = 0;
+       icmp->chksum = odp_chksum(icmp, data - (uint8_t *)icmp);
+
+       /* Close ESP if specified */
+       if (esp) {
+               int payload_len = data - (uint8_t *)icmp;
+               int encrypt_len;
+               odp_esptrl_t *esp_t;
+               DES_key_schedule ks1, ks2, ks3;
+               uint8_t iv[8];
+
+               memcpy(iv, esp->iv, sizeof(iv));
+
+               encrypt_len = ESP_ENCODE_LEN(payload_len + sizeof(*esp_t),
+                                            entry->esp.block_len);
+               memset(data, 0, encrypt_len - payload_len);
+               data += encrypt_len - payload_len;
+
+               esp_t = (odp_esptrl_t *)(data) - 1;
+               esp_t->pad_len = encrypt_len - payload_len - sizeof(*esp_t);
+               esp_t->next_header = ip->proto;
+               ip->proto = ODP_IPPROTO_ESP;
+
+               DES_set_key(&entry->esp.key.des.k1, &ks1);
+               DES_set_key(&entry->esp.key.des.k2, &ks2);
+               DES_set_key(&entry->esp.key.des.k3, &ks3);
+
+               DES_ede3_cbc_encrypt((uint8_t *)icmp,
+                                    (uint8_t *)icmp,
+                                    encrypt_len,
+                                    &ks1,
+                                    &ks2,
+                                    &ks3,
+                                    (DES_cblock *)iv,
+                                    1);
+       }
+
+       /* Since ESP can pad we can now fix IP length */
+       ip->tot_len = odp_cpu_to_be_16(data - (uint8_t *)ip);
+       odp_packet_set_len(pkt, data - base);
+
+       /* Close AH if specified */
+       if (ah) {
+               uint8_t hash[EVP_MAX_MD_SIZE];
+               uint32_t hash_len = 12;
+               int auth_len = data - (uint8_t *)ip;
+
+               ah->next_header = ip->proto;
+               ip->proto = ODP_IPPROTO_AH;
+
+               HMAC(EVP_md5(),
+                    entry->ah.key.md5.key,
+                    16,
+                    (uint8_t *)ip,
+                    auth_len,
+                    hash,
+                    &hash_len);
+
+               memcpy(ah->icv, hash, 12);
+       }
+
+       /* Now fill in final IP header fields */
+       ip->ttl = 64;
+       ip->tos = 0;
+       ip->frag_offset = 0;
+       ip->chksum = 0;
+       odp_ipv4_csum_update(pkt);
+       return pkt;
+}
+
+/**
+ * Resolve the stream DB against the IPsec input and output caches
+ *
+ * For each stream, look the source and destination IP address up in the
+ * input and output IPsec caches.  If a hit is found, store the hit in
+ * the stream DB to be used when creating packets.
+ */
+static
+void resolve_stream_db(void)
+{
+       stream_db_entry_t *stream = NULL;
+
+       /* For each stream look for input and output IPsec entries */
+       for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+               ipsec_cache_entry_t *entry;
+
+               /* Lookup input entry */
+               entry = find_ipsec_cache_entry_in(stream->src_ip,
+                                                 stream->dst_ip,
+                                                 NULL,
+                                                 NULL);
+               stream->input.entry = entry;
+
+               /* Lookup output entry */
+               entry = find_ipsec_cache_entry_out(stream->src_ip,
+                                                  stream->dst_ip,
+                                                  0);
+               stream->output.entry = entry;
+       }
+}
+
+/**
+ * Create input packets based on the stream DB
+ *
+ * Create input packets based on the configured streams and enqueue them
+ * into loop interface input queues.  Once packet processing starts these
+ * packets will be remomved and processed as if they had come from a normal
+ * packet interface.
+ *
+ * @return number of streams successfully processed
+ */
+static
+int create_stream_db_inputs(void)
+{
+       int created = 0;
+       odp_buffer_pool_t pkt_pool;
+       stream_db_entry_t *stream = NULL;
+
+       /* Lookup the packet pool */
+       pkt_pool = odp_buffer_pool_lookup("packet_pool");
+       if (pkt_pool == ODP_BUFFER_POOL_INVALID) {
+               ODP_ERR("Error: pkt_pool not found\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* For each stream create corresponding input packets */
+       for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+               int count;
+               odp_queue_t queue;
+
+               queue = loopback_db->intf[stream->input.loop].inq_def;
+
+               for (count = stream->count; count > 0; count--) {
+                       odp_packet_t pkt;
+
+                       pkt = create_ipv4_packet(stream, pkt_pool);
+                       if (ODP_PACKET_INVALID == pkt) {
+                               printf("Packet buffers exhausted\n");
+                               break;
+                       }
+                       stream->created++;
+                       odp_queue_enq(queue, pkt);
+
+                       /* Count this stream when we create first packet */
+                       if (1 == stream->created)
+                               created++;
+               }
+       }
+
+       return created;
+}
+
+/**
+ * Verify an IPv4 packet received on a loop output queue
+ *
+ * TODO: Better error checking, add counters, add tracing,
+ *       add order verification
+ *
+ * @param stream  Stream to verify the packet against
+ * @param pkt     Packet to verify
+ *
+ * @return TRUE if packet verifies else FALSE
+ */
+static
+boolean verify_ipv4_packet(stream_db_entry_t *stream,
+                          odp_packet_t pkt)
+{
+       ipsec_cache_entry_t *entry = stream->output.entry;
+       uint8_t             *data;
+       odp_ipv4hdr_t       *ip;
+       odp_ahhdr_t         *ah = NULL;
+       odp_esphdr_t        *esp = NULL;
+       int                  hdr_len;
+       odp_icmphdr_t       *icmp;
+       stream_pkt_hdr_t    *test;
+
+       /* Basic IPv4 verify (add checksum verification) */
+       data = odp_packet_l3(pkt);
+       ip = (odp_ipv4hdr_t *)data;
+       data += sizeof(*ip);
+       if (0x45 != ip->ver_ihl)
+               return FALSE;
+       if (stream->src_ip != odp_be_to_cpu_32(ip->src_addr))
+               return FALSE;
+       if (stream->dst_ip != odp_be_to_cpu_32(ip->dst_addr))
+               return FALSE;
+
+       /* Find IPsec headers if any and compare against entry */
+       hdr_len = locate_ipsec_headers(ip, &ah, &esp);
+       if (ah) {
+               if (!entry)
+                       return FALSE;
+               if (ODP_AUTH_ALG_NULL == entry->ah.alg)
+                       return FALSE;
+               if (odp_be_to_cpu_32(ah->spi) != entry->ah.spi)
+                       return FALSE;
+               if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg)
+                       abort();
+       } else {
+               if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg))
+                       return FALSE;
+       }
+       if (esp) {
+               if (!entry)
+                       return FALSE;
+               if (ODP_CIPHER_ALG_NULL == entry->esp.alg)
+                       return FALSE;
+               if (odp_be_to_cpu_32(esp->spi) != entry->esp.spi)
+                       return FALSE;
+               if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg)
+                       abort();
+               hdr_len += entry->esp.iv_len;
+       } else {
+               if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg))
+                       return FALSE;
+       }
+       data += hdr_len;
+
+       /* Verify authentication (if present) */
+       if (ah) {
+               uint8_t  ip_tos;
+               uint8_t  ip_ttl;
+               uint16_t ip_frag_offset;
+               uint8_t  icv[12];
+               uint8_t  hash[EVP_MAX_MD_SIZE];
+               uint32_t hash_len = 12;
+
+               /* Save/clear mutable fields */
+               ip_tos = ip->tos;
+               ip_ttl = ip->ttl;
+               ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
+               ip->tos = 0;
+               ip->ttl = 0;
+               ip->frag_offset = 0;
+               ip->chksum = 0;
+               memcpy(icv, ah->icv, 12);
+               memset(ah->icv, 0, 12);
+
+               /* Calculate HMAC and compare */
+               HMAC(EVP_md5(),
+                    entry->ah.key.md5.key,
+                    16,
+                    (uint8_t *)ip,
+                    odp_be_to_cpu_16(ip->tot_len),
+                    hash,
+                    &hash_len);
+
+               if (0 != memcmp(icv, hash, sizeof(icv)))
+                       return FALSE;
+
+               ip->proto = ah->next_header;
+               ip->tos = ip_tos;
+               ip->ttl = ip_ttl;
+               ip->frag_offset = odp_cpu_to_be_16(ip_frag_offset);
+       }
+
+       /* Decipher if present */
+       if (esp) {
+               odp_esptrl_t *esp_t;
+               DES_key_schedule ks1, ks2, ks3;
+               uint8_t iv[8];
+               int encrypt_len = ipv4_data_len(ip) - hdr_len;
+
+               memcpy(iv, esp->iv, sizeof(iv));
+
+               DES_set_key(&entry->esp.key.des.k1, &ks1);
+               DES_set_key(&entry->esp.key.des.k2, &ks2);
+               DES_set_key(&entry->esp.key.des.k3, &ks3);
+
+               DES_ede3_cbc_encrypt((uint8_t *)data,
+                                    (uint8_t *)data,
+                                    encrypt_len,
+                                    &ks1,
+                                    &ks2,
+                                    &ks3,
+                                    (DES_cblock *)iv,
+                                    0);
+
+               esp_t = (odp_esptrl_t *)(data + encrypt_len) - 1;
+               ip->proto = esp_t->next_header;
+       }
+
+       /* Verify ICMP packet */
+       if (ODP_IPPROTO_ICMP != ip->proto)
+               return FALSE;
+
+       /* Verify ICMP header */
+       icmp = (odp_icmphdr_t *)data;
+       data += sizeof(*icmp);
+       if (ICMP_ECHO != icmp->type)
+               return FALSE;
+       if (0x1234 != odp_be_to_cpu_16(icmp->un.echo.id<http://un.echo.id>))
+               return FALSE;
+
+       /* Now check our packet */
+       test = (stream_pkt_hdr_t *)data;
+       if (STREAM_MAGIC != odp_be_to_cpu_64(test->magic))
+               return FALSE;
+
+       return TRUE;
+}
+
+/**
+ * Verify stream DB outputs
+ *
+ * For each stream, poll the output loop interface queue and verify
+ * any packets found on it
+ *
+ * @return TRUE if all packets on all streams verified else FALSE
+ */
+static
+boolean verify_stream_db_outputs(void)
+{
+       boolean done = TRUE;
+       stream_db_entry_t *stream = NULL;
+
+       /* For each stream look for output packets */
+       for (stream = stream_db->list; NULL != stream; stream = stream->next) {
+               int idx;
+               int count;
+               odp_queue_t queue;
+               odp_buffer_t buf_tbl[32];
+
+               queue = loopback_db->intf[stream->output.loop].outq_def;
+
+               if (ODP_QUEUE_INVALID == queue)
+                       continue;
+
+               for (;;) {
+#if LOOP_DEQ_MULTIPLE
+                       count = odp_queue_deq_multi(queue, buf_tbl, 32);
+#else
+                       buf_tbl[0] = odp_queue_deq(queue);
+                       count = (buf_tbl[0] != ODP_BUFFER_INVALID) ? 1 : 0;
+#endif
+                       if (!count)
+                               break;
+                       for (idx = 0; idx < count; idx++) {
+                               boolean good;
+                               odp_packet_t pkt;
+
+                               pkt = odp_packet_from_buffer(buf_tbl[idx]);
+
+                               good = verify_ipv4_packet(stream, pkt);
+                               if (good)
+                                       stream->verified++;
+                       }
+               }
+
+               printf("Stream %d %d\n", stream->created, stream->verified);
+
+               if (stream->created != stream->verified)
+                       done = FALSE;
+       }
+       return done;
+}
+
+/**
+ * IPsec pre argument processing intialization
+ */
+static
+void ipsec_init_pre(void)
+{
+       odp_queue_param_t qparam;
+       void *pool_base;
+
+       /*
+        * Create queues
+        *
+        *  - completion queue (should eventually be ORDERED)
+        *  - sequence number queue (must be ATOMIC)
+        */
+       qparam.sched.prio  = ODP_SCHED_PRIO_HIGHEST;
+       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
+       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+
+       completionq = odp_queue_create("completion",
+                                      ODP_QUEUE_TYPE_SCHED,
+                                      &qparam);
+       if (completionq == ODP_QUEUE_INVALID) {
+               ODP_ERR("Error: completion queue creation failed\n");
+               exit(EXIT_FAILURE);
+       }
+
+       qparam.sched.prio  = ODP_SCHED_PRIO_HIGHEST;
+       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
+       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+
+       seqnumq = odp_queue_create("seqnum",
+                                  ODP_QUEUE_TYPE_SCHED,
+                                  &qparam);
+       if (seqnumq == ODP_QUEUE_INVALID) {
+               ODP_ERR("Error: sequence number queue creation failed\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Create session creation message pool */
+       pool_base = odp_shm_reserve("shm_msg_pool",
+                                   MSG_POOL_SIZE, ODP_CACHE_LINE_SIZE);
+
+       msg_pool = odp_buffer_pool_create("msg_pool", pool_base,
+                                         MSG_POOL_SIZE,
+                                         MSG_POOL_BUF_SIZE,
+                                         ODP_CACHE_LINE_SIZE,
+                                         ODP_BUFFER_TYPE_RAW);
+
+       if (msg_pool == ODP_BUFFER_POOL_INVALID) {
+               ODP_ERR("Error: message pool create failed.\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Initialize our data bases */
+       init_sp_db();
+       init_sa_db();
+       init_ipsec_cache();
+}
+
+/**
+ * IPsec post argument processing intialization
+ *
+ * Resolve SP DB with SA DB and create corresponding IPsec cache entries
+ *
+ * @param async  Mode to use when invoking per packet crypto API
+ */
+static
+void ipsec_init_post(boolean async)
+{
+       sp_db_entry_t *entry;
+
+       /* 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;
+
+               if (entry->esp)
+                       cipher_sa = find_sa_db_entry(&entry->src_subnet,
+                                                    &entry->dst_subnet,
+                                                    1);
+               if (entry->ah)
+                       auth_sa = find_sa_db_entry(&entry->src_subnet,
+                                                  &entry->dst_subnet,
+                                                  0);
+
+               if (cipher_sa || auth_sa)
+                       create_ipsec_cache_entry(cipher_sa,
+                                                auth_sa,
+                                                async,
+                                                entry->input);
+               else {
+                       printf(" WARNING: SA not found for SP\n");
+                       dump_sp_db_entry(entry);
+               }
+       }
+}
+
+/**
+ * Initialize loopback
+ *
+ * Initialize ODP queues to create our own idea of loopbacks, which allow
+ * testing without physical interfaces.  Interface name string will be of
+ * the format "loopX" where X is the decimal number of the interface.
+ *
+ * @param intf     Loopback interface name string
+ */
+static
+void initialize_loop(char *intf)
+{
+       int idx;
+       loopback_db_entry_t *entry;
+       odp_queue_t outq_def;
+       odp_queue_t inq_def;
+       char queue_name[ODP_QUEUE_NAME_LEN];
+       odp_queue_param_t qparam;
+       char mac_str[32];
+
+       /* Derive loopback interface index */
+       idx = loop_if_index(intf);
+       if (idx < 0) {
+               ODP_ERR("Error: loopback \"%s\" invalid\n", intf);
+               exit(EXIT_FAILURE);
+       }
+       entry = &loopback_db->intf[idx];
+
+       /* Dummy MAC address */
+       memset(entry->mac, (0xF0 | idx), sizeof(entry->mac));
+
+       /* Create input queue */
+       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
+       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
+       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+       snprintf(queue_name, sizeof(queue_name), "%i-loop_inq_def", idx);
+       queue_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+
+       inq_def = odp_queue_create(queue_name, ODP_QUEUE_TYPE_SCHED, &qparam);
+       if (inq_def == ODP_QUEUE_INVALID) {
+               ODP_ERR("Error: input queue creation failed for %s\n", intf);
+               exit(EXIT_FAILURE);
+       }
+       entry->inq_def = inq_def;
+
+       /* Create output queue */
+       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
+       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
+       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+       snprintf(queue_name, sizeof(queue_name), "%i-loop_outq_def", idx);
+       queue_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+
+       outq_def = odp_queue_create(queue_name, ODP_QUEUE_TYPE_POLL, &qparam);
+       if (outq_def == ODP_QUEUE_INVALID) {
+               ODP_ERR("Error: output queue creation failed for %s\n", intf);
+               exit(EXIT_FAILURE);
+       }
+       entry->outq_def = outq_def;
+
+       printf("Created loop:%02i, queue mode (ATOMIC queues)\n"
+              "          default loop%02i-INPUT queue:%u\n"
+              "          default loop%02i-OUTPUT queue:%u\n"
+              "          source mac address %s\n",
+              idx, idx, inq_def, idx, outq_def,
+              mac_addr_str(mac_str, entry->mac));
+
+       /* Resolve any routes using this interface for output */
+       resolve_fwd_db(intf, outq_def, entry->mac);
+}
+
+/**
+ * Initialize interface
+ *
+ * Initialize ODP pktio and queues, query MAC address and update
+ * forwarding database.
+ *
+ * @param intf     Interface name string
+ * @param type     Packet IO type (BASIC, MMSG, MMAP)
+ * @param fanout   Packet IO fanout
+ */
+static
+void initialize_intf(char *intf, int type, int fanout)
+{
+       odp_buffer_pool_t pkt_pool;
+       odp_pktio_t pktio;
+       odp_queue_t outq_def;
+       odp_queue_t inq_def;
+       char inq_name[ODP_QUEUE_NAME_LEN];
+       odp_queue_param_t qparam;
+       int ret;
+       odp_pktio_params_t params;
+       socket_params_t *sock_params = &params.sock_params;
+       uint8_t src_mac[6];
+       char src_mac_str[32];
+
+       /* Lookup the packet pool */
+       pkt_pool = odp_buffer_pool_lookup("packet_pool");
+       if (pkt_pool == ODP_BUFFER_POOL_INVALID) {
+               ODP_ERR("Error: pkt_pool not found\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        * Open a packet IO instance for thread and get default output queue
+        */
+       sock_params->type = type;
+       sock_params->fanout = fanout;
+       pktio = odp_pktio_open(intf, pkt_pool, &params);
+       if (pktio == ODP_PKTIO_INVALID) {
+               ODP_ERR("Error: pktio create failed for %s\n", intf);
+               exit(EXIT_FAILURE);
+       }
+       outq_def = odp_pktio_outq_getdef(pktio);
+
+       /*
+        * Create and set the default INPUT queue associated with the 'pktio'
+        * resource
+        */
+       qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
+       qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
+       qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+       snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def", (int)pktio);
+       inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+
+       inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam);
+       if (inq_def == ODP_QUEUE_INVALID) {
+               ODP_ERR("Error: pktio queue creation failed for %s\n", intf);
+               exit(EXIT_FAILURE);
+       }
+
+       ret = odp_pktio_inq_setdef(pktio, inq_def);
+       if (ret != 0) {
+               ODP_ERR("Error: default input-Q setup for %s\n", intf);
+               exit(EXIT_FAILURE);
+       }
+
+       /* read the source MAC address for this interface */
+       query_mac_address(intf, src_mac);
+
+       printf("Created pktio:%02i, queue mode (ATOMIC queues)\n"
+              "          default pktio%02i-INPUT queue:%u\n"
+              "          source mac address %s\n",
+              pktio, pktio, inq_def, mac_addr_str(src_mac_str, src_mac));
+
+       /* Resolve any routes using this interface for output */
+       resolve_fwd_db(intf, outq_def, src_mac);
+}
+
+/**
+ * Verify crypto operation completed successfully
+ *
+ * @param status  Pointer to cryto completion structure
+ *
+ * @return TRUE if all OK else FALSE
+ */
+static
+boolean is_crypto_compl_status_ok(struct odp_crypto_compl_status *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;
+}
+
+/**
+ * Packet Processing - Input verification
+ *
+ * @param pkt  Packet to inspect
+ * @param ctx  Packet process context (not used)
+ *
+ * @return PKT_CONTINUE if good, supported packet else PKT_DROP
+ */
+static
+pkt_disposition_e input_verify(odp_packet_t pkt, pkt_ctx_t *ctx ODP_UNUSED)
+{
+       if (odp_unlikely(odp_packet_error(pkt)))
+               return PKT_DROP;
+
+       /*
+        * TODO: for stream packets figure out how to set these flags
+        *
+        * if (!odp_packet_inflag_eth(pkt))
+        *      return PKT_DROP;
+        *
+        * if (!odp_packet_inflag_ipv4(pkt))
+        *      return PKT_DROP;
+        */
+
+       return PKT_CONTINUE;
+}
+
+/**
+ * Packet Processing - Route lookup in forwarding database
+ *
+ * @param pkt  Packet to route
+ * @param ctx  Packet process context
+ *
+ * @return PKT_CONTINUE if route found else PKT_DROP
+ */
+static
+pkt_disposition_e route_fwd_db(odp_packet_t pkt, pkt_ctx_t *ctx)
+{
+       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
+       fwd_db_entry_t *entry;
+
+       entry = find_fwd_db_entry(odp_be_to_cpu_32(ip->dst_addr));
+
+       if (entry) {
+               odp_ethhdr_t *eth = (odp_ethhdr_t *)odp_packet_l2(pkt);
+
+               memcpy(&eth->dst, entry->dst_mac, 6);
+               memcpy(&eth->src, entry->src_mac, 6);
+               ctx->outq = entry->queue;
+
+               return PKT_CONTINUE;
+       }
+
+       return PKT_DROP;
+}
+
+/**
+ * Packet Processing - Input IPsec packet classification
+ *
+ * Verify the received packet has IPsec headers and a match
+ * in the IPsec cache, if so issue crypto request else skip
+ * input crypto.
+ *
+ * @param pkt   Packet to classify
+ * @param ctx   Packet process context
+ * @param skip  Pointer to return "skip" indication
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_in_classify(odp_packet_t pkt,
+                                      pkt_ctx_t *ctx,
+                                      boolean *skip)
+{
+       uint8_t *buf = odp_packet_buf_addr(pkt);
+       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
+       int hdr_len;
+       odp_ahhdr_t *ah = NULL;
+       odp_esphdr_t *esp = NULL;
+       ipsec_cache_entry_t *entry;
+       struct odp_crypto_op_params params;
+       odp_crypto_rc_e rc;
+
+       /* Default to skip IPsec */
+       *skip = TRUE;
+
+       /* Check IP header for IPSec protocols and look it up */
+       hdr_len = locate_ipsec_headers(ip, &ah, &esp);
+       if (!ah && !esp)
+               return PKT_CONTINUE;
+       entry = find_ipsec_cache_entry_in(odp_be_to_cpu_32(ip->src_addr),
+                                         odp_be_to_cpu_32(ip->dst_addr),
+                                         ah,
+                                         esp);
+       if (!entry)
+               return PKT_CONTINUE;
+
+       /* Account for configured ESP IV length in packet */
+       hdr_len += entry->esp.iv_len;
+
+       /* Initialize parameters block */
+       memset(&params, 0, sizeof(params));
+       params.session = entry->state.session;
+       params.pkt = pkt;
+       params.out_pkt = pkt;
+
+       /*Save everything to context */
+       ctx->ipsec.ip_tos = ip->tos;
+       ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
+       ctx->ipsec.ip_ttl = ip->ttl;
+       ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
+       ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
+       ctx->ipsec.hdr_len = hdr_len;
+       ctx->ipsec.trl_len = 0;
+
+       /*If authenticating, zero the mutable fields build the request */
+       if (ah) {
+               ip->chksum = 0;
+               ip->tos = 0;
+               ip->frag_offset = 0;
+               ip->ttl = 0;
+
+               params.auth_range.offset = ((uint8_t *)ip) - buf;
+               params.auth_range.length = odp_be_to_cpu_16(ip->tot_len);
+               params.hash_result_offset = ah->icv - buf;
+       }
+
+       /* If deciphering build request */
+       if (esp) {
+               params.cipher_range.offset = ipv4_data_p(ip) + hdr_len - buf;
+               params.cipher_range.length = ipv4_data_len(ip) - hdr_len;
+               params.override_iv_ptr = esp->iv;
+       }
+
+       /* Issue crypto request */
+       *skip = FALSE;
+       rc = odp_crypto_operation(&params, odp_buffer_from_packet(pkt));
+       if (ODP_CRYPTO_OP_ERROR == rc)
+               abort();
+       if (ODP_CRYPTO_OP_POSTED == rc)
+               return PKT_POSTED;
+       return PKT_CONTINUE;
+}
+
+/**
+ * Packet Processing - Input IPsec packet processing cleanup
+ *
+ * @param pkt  Packet to handle
+ * @param ctx  Packet process context
+ *
+ * @return PKT_CONTINUE if successful else PKT_DROP
+ */
+static
+pkt_disposition_e do_ipsec_in_finish(odp_packet_t pkt,
+                                    pkt_ctx_t *ctx)
+{
+       odp_buffer_t event;
+       struct odp_crypto_compl_status cipher_rc, auth_rc;
+       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
+       int       hdr_len = ctx->ipsec.hdr_len;
+       int       trl_len = 0;
+
+       /* Check crypto result */
+       event = odp_buffer_from_packet(pkt);
+       odp_crypto_get_operation_compl_status(event, &cipher_rc, &auth_rc);
+       if (!is_crypto_compl_status_ok(&cipher_rc))
+               return PKT_DROP;
+       if (!is_crypto_compl_status_ok(&auth_rc))
+               return PKT_DROP;
+
+       /*
+        * Finish auth
+        */
+       if (ctx->ipsec.ah_offset) {
+               uint8_t *buf = odp_packet_buf_addr(pkt);
+               odp_ahhdr_t *ah;
+
+               ah = (odp_ahhdr_t *)(ctx->ipsec.ah_offset + buf);
+               ip->proto = ah->next_header;
+       }
+
+       /*
+        * Finish cipher by finding ESP trailer and processing
+        *
+        * NOTE: ESP authentication ICV not supported
+        */
+       if (ctx->ipsec.esp_offset) {
+               uint8_t *eop = (uint8_t *)(ip) + odp_be_to_cpu_16(ip->tot_len);
+               odp_esptrl_t *esp_t = (odp_esptrl_t *)(eop) - 1;
+
+               ip->proto = esp_t->next_header;
+               trl_len += esp_t->pad_len + sizeof(*esp_t);
+       }
+
+       /* Finalize the IPv4 header */
+       ipv4_adjust_len(ip, -(hdr_len + trl_len));
+       ip->ttl = ctx->ipsec.ip_ttl;
+       ip->tos = ctx->ipsec.ip_tos;
+       ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset);
+       ip->chksum = 0;
+       odp_ipv4_csum_update(pkt);
+
+       /* Correct the packet length and move payload into position */
+       odp_packet_set_len(pkt, odp_packet_get_len(pkt) - (hdr_len + trl_len));
+       memmove(ipv4_data_p(ip),
+               ipv4_data_p(ip) + hdr_len,
+               odp_be_to_cpu_16(ip->tot_len));
+
+       /* Fall through to next state */
+       return PKT_CONTINUE;
+}
+
+/**
+ * Packet Processing - Output IPsec packet classification
+ *
+ * Verify the outbound packet has a match in the IPsec cache,
+ * if so issue prepend IPsec headers and prepare parameters
+ * for crypto API call.  Post the packet to ATOMIC queue so
+ * that sequence numbers can be applied in packet order as
+ * the next processing step.
+ *
+ * @param pkt   Packet to classify
+ * @param ctx   Packet process context
+ * @param skip  Pointer to return "skip" indication
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt,
+                                       pkt_ctx_t *ctx,
+                                       boolean *skip)
+{
+       uint8_t *buf = odp_packet_buf_addr(pkt);
+       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
+       uint16_t ip_data_len = ipv4_data_len(ip);
+       uint8_t *ip_data = ipv4_data_p(ip);
+       ipsec_cache_entry_t *entry;
+       struct odp_crypto_op_params params;
+       int      hdr_len = 0;
+       int      trl_len = 0;
+       odp_ahhdr_t *ah = NULL;
+       odp_esphdr_t *esp = NULL;
+
+       /* Default to skip IPsec */
+       *skip = TRUE;
+
+       /* Find record */
+       entry = find_ipsec_cache_entry_out(odp_be_to_cpu_32(ip->src_addr),
+                                          odp_be_to_cpu_32(ip->dst_addr),
+                                          ip->proto);
+       if (!entry)
+               return PKT_CONTINUE;
+
+       /* Save IPv4 stuff */
+       ctx->ipsec.ip_tos = ip->tos;
+       ctx->ipsec.ip_frag_offset = odp_be_to_cpu_16(ip->frag_offset);
+       ctx->ipsec.ip_ttl = ip->ttl;
+
+       /* Initialize parameters block */
+       memset(&params, 0, sizeof(params));
+       params.session = entry->state.session;
+       params.pkt = pkt;
+       params.out_pkt = pkt;
+
+       /* Compute ah and esp, determine length of headers, move the data */
+       if (entry->ah.alg) {
+               ah = (odp_ahhdr_t *)(ip_data);
+               hdr_len += sizeof(odp_ahhdr_t);
+               hdr_len += entry->ah.icv_len;
+       }
+       if (entry->esp.alg) {
+               esp = (odp_esphdr_t *)(ip_data + hdr_len);
+               hdr_len += sizeof(odp_esphdr_t);
+               hdr_len += entry->esp.iv_len;
+       }
+       memmove(ip_data + hdr_len, ip_data, ip_data_len);
+       ip_data += hdr_len;
+
+       /* For cipher, compute encrypt length, build headers and request */
+       if (esp) {
+               uint32_t encrypt_len;
+               odp_esptrl_t *esp_t;
+
+               encrypt_len = ESP_ENCODE_LEN(ip_data_len + sizeof(*esp_t),
+                                            entry->esp.block_len);
+               trl_len = encrypt_len - ip_data_len;
+
+               esp->spi = odp_cpu_to_be_32(entry->esp.spi);
+               memcpy(esp + 1, entry->state.iv, entry->esp.iv_len);
+
+               esp_t = (odp_esptrl_t *)(ip_data + encrypt_len) - 1;
+               esp_t->pad_len     = trl_len - sizeof(*esp_t);
+               esp_t->next_header = ip->proto;
+               ip->proto = ODP_IPPROTO_ESP;
+
+               params.cipher_range.offset = ip_data - buf;
+               params.cipher_range.length = encrypt_len;
+       }
+
+       /* For authentication, build header clear mutables and build request */
+       if (ah) {
+               memset(ah, 0, sizeof(*ah) + entry->ah.icv_len);
+               ah->spi = odp_cpu_to_be_32(entry->ah.spi);
+               ah->ah_len = 1 + (entry->ah.icv_len / 4);
+               ah->next_header = ip->proto;
+               ip->proto = ODP_IPPROTO_AH;
+
+               ip->chksum = 0;
+               ip->tos = 0;
+               ip->frag_offset = 0;
+               ip->ttl = 0;
+
+               params.auth_range.offset = ((uint8_t *)ip) - buf;
+               params.auth_range.length =
+                       odp_be_to_cpu_16(ip->tot_len) + (hdr_len + trl_len);
+               params.hash_result_offset = ah->icv - buf;
+       }
+
+       /* Set IPv4 length before authentication */
+       ipv4_adjust_len(ip, hdr_len + trl_len);
+
+       /* Save remaining context */
+       ctx->ipsec.hdr_len = hdr_len;
+       ctx->ipsec.trl_len = trl_len;
+       ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0;
+       ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0;
+       ctx->ipsec.ah_seq = &entry->state.ah_seq;
+       ctx->ipsec.esp_seq = &entry->state.esp_seq;
+       memcpy(&ctx->ipsec.params, &params, sizeof(params));
+
+       /* Send packet to the atmoic queue to assign sequence numbers */
+       *skip = FALSE;
+       odp_queue_enq(seqnumq, odp_buffer_from_packet(pkt));
+       return PKT_POSTED;
+}
+
+/**
+ * Packet Processing - Output IPsec packet sequence number assignment
+ *
+ * Assign the necessary sequence numbers and then issue the crypto API call
+ *
+ * @param pkt  Packet to handle
+ * @param ctx  Packet process context
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_out_seq(odp_packet_t pkt,
+                                  pkt_ctx_t *ctx)
+{
+       uint8_t *buf = odp_packet_buf_addr(pkt);
+       odp_crypto_rc_e rc;
+
+       /* We were dispatched from atomic queue, assign sequence numbers */
+       if (ctx->ipsec.ah_offset) {
+               odp_ahhdr_t *ah;
+
+               ah = (odp_ahhdr_t *)(ctx->ipsec.ah_offset + buf);
+               ah->seq_no = odp_cpu_to_be_32((*ctx->ipsec.ah_seq)++);
+       }
+       if (ctx->ipsec.esp_offset) {
+               odp_esphdr_t *esp;
+
+               esp = (odp_esphdr_t *)(ctx->ipsec.esp_offset + buf);
+               esp->seq_no = odp_cpu_to_be_32((*ctx->ipsec.esp_seq)++);
+       }
+
+       /* Issue crypto request */
+       rc = odp_crypto_operation(&ctx->ipsec.params,
+                                 odp_buffer_from_packet(pkt));
+       if (ODP_CRYPTO_OP_ERROR == rc)
+               abort();
+       if (ODP_CRYPTO_OP_POSTED == rc)
+               return PKT_POSTED;
+       return PKT_CONTINUE;
+}
+
+/**
+ * Packet Processing - Output IPsec packet processing cleanup
+ *
+ * @param pkt  Packet to handle
+ * @param ctx  Packet process context
+ *
+ * @return PKT_CONTINUE if successful else PKT_DROP
+ */
+static
+pkt_disposition_e do_ipsec_out_finish(odp_packet_t pkt,
+                                     pkt_ctx_t *ctx)
+{
+       odp_buffer_t event;
+       struct odp_crypto_compl_status cipher_rc, auth_rc;
+       odp_ipv4hdr_t *ip = (odp_ipv4hdr_t *)odp_packet_l3(pkt);
+       int hdr_len = ctx->ipsec.hdr_len;
+       int trl_len = ctx->ipsec.trl_len;
+
+       /* Check crypto result */
+       event = odp_buffer_from_packet(pkt);
+       odp_crypto_get_operation_compl_status(event, &cipher_rc, &auth_rc);
+       if (!is_crypto_compl_status_ok(&cipher_rc))
+               return PKT_DROP;
+       if (!is_crypto_compl_status_ok(&auth_rc))
+               return PKT_DROP;
+
+       /* Finalize the IPv4 header */
+       ip->ttl = ctx->ipsec.ip_ttl;
+       ip->tos = ctx->ipsec.ip_tos;
+       ip->frag_offset = odp_cpu_to_be_16(ctx->ipsec.ip_frag_offset);
+       ip->chksum = 0;
+       odp_ipv4_csum_update(pkt);
+       odp_packet_set_len(pkt, odp_packet_get_len(pkt) + (hdr_len + trl_len));
+
+       /* Fall through to next state */
+       return PKT_CONTINUE;
+}
+
+/**
+ * Packet IO worker thread
+ *
+ * Loop calling odp_schedule to obtain packets from one of three sources,
+ * and continue processing the packet based on the state stored in its
+ * per packet context.
+ *
+ *  - Input interfaces (i.e. new work)
+ *  - Sequence number assignment queue
+ *  - Per packet crypto API completion queue
+ *
+ * @param arg  thread arguments of type 'thread_args_t *'
+ *
+ * @return NULL (should never return)
+ */
+static
+void *pktio_thread(void *arg ODP_UNUSED)
+{
+       int thr;
+       odp_packet_t pkt;
+       odp_buffer_t buf;
+       unsigned long pkt_cnt = 0;
+
+       thr = odp_thread_id();
+
+       printf("Pktio thread [%02i] starts\n", thr);
+
+       odp_barrier_sync(&sync_barrier);
+
+       /* Loop packets */
+       for (;;) {
+               pkt_disposition_e rc;
+               pkt_ctx_t   *ctx;
+               odp_queue_t  dispatchq;
+
+               /* Use schedule to get buf from any input queue */
+               buf = odp_schedule(&dispatchq, ODP_SCHED_WAIT);
+               pkt = odp_packet_from_buffer(buf);
+               ctx = get_ctx_from_buf(buf);
+
+               /* Determine new work versus completion or sequence number */
+               if ((completionq != dispatchq) && (seqnumq != dispatchq)) {
+                       memset(ctx, 0, sizeof(*ctx));
+                       ctx->state = PKT_STATE_INPUT_VERIFY;
+               }
+
+               /*
+                * We now have a packet and its associated context. Loop here
+                * executing processing based on the current state value stored
+                * in the context as long as the processing return code
+                * indicates PKT_CONTINUE.
+                *
+                * For other return codes:
+                *
+                *  o PKT_DONE   - finished with the packet
+                *  o PKT_DROP   - something incorrect about the packet, drop it
+                *  o PKT_POSTED - packet/event has been queued for later
+                */
+               do {
+                       boolean skip;
+
+                       switch (ctx->state) {
+                       case PKT_STATE_INPUT_VERIFY:
+
+                               rc = input_verify(pkt, ctx);
+                               ctx->state = PKT_STATE_IPSEC_IN_CLASSIFY;
+                               break;
+
+                       case PKT_STATE_IPSEC_IN_CLASSIFY:
+
+                               rc = do_ipsec_in_classify(pkt, ctx, &skip);
+                               ctx->state = (skip) ?
+                                       PKT_STATE_ROUTE_LOOKUP :
+                                       PKT_STATE_IPSEC_IN_FINISH;
+                               break;
+
+                       case PKT_STATE_IPSEC_IN_FINISH:
+
+                               rc = do_ipsec_in_finish(pkt, ctx);
+                               ctx->state = PKT_STATE_ROUTE_LOOKUP;
+                               break;
+
+                       case PKT_STATE_ROUTE_LOOKUP:
+
+                               rc = route_fwd_db(pkt, ctx);
+                               ctx->state = PKT_STATE_IPSEC_OUT_CLASSIFY;
+                               break;
+
+                       case PKT_STATE_IPSEC_OUT_CLASSIFY:
+
+                               rc = do_ipsec_out_classify(pkt, ctx, &skip);
+                               ctx->state = (skip) ?
+                                       PKT_STATE_TRANSMIT :
+                                       PKT_STATE_IPSEC_OUT_SEQ;
+                               break;
+
+                       case PKT_STATE_IPSEC_OUT_SEQ:
+
+                               rc = do_ipsec_out_seq(pkt, ctx);
+                               ctx->state = PKT_STATE_IPSEC_OUT_FINISH;
+                               break;
+
+                       case PKT_STATE_IPSEC_OUT_FINISH:
+
+                               rc = do_ipsec_out_finish(pkt, ctx);
+                               ctx->state = PKT_STATE_TRANSMIT;
+                               break;
+
+                       case PKT_STATE_TRANSMIT:
+
+                               odp_queue_enq(ctx->outq, buf);
+                               rc = PKT_DONE;
+                               break;
+
+                       default:
+                               rc = PKT_DROP;
+                               break;
+                       }
+               } while (PKT_CONTINUE == rc);
+
+               /* Check for drop */
+               if (PKT_DROP == rc)
+                       odp_packet_free(pkt);
+
+               /* Print packet counts every once in a while */
+               if (PKT_DONE == rc) {
+                       if (odp_unlikely(pkt_cnt++ % 1000 == 0)) {
+                               printf("  [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
+                               fflush(NULL);
+                       }
+               }
+       }
+
+       /* unreachable */
+       return NULL;
+}
+
+/**
+ * ODP ipsec example main function
+ */
+int
+main(int argc, char *argv[])
+{
+       odp_linux_pthread_t thread_tbl[MAX_WORKERS];
+       odp_buffer_pool_t pool;
+       int thr_id;
+       int num_workers;
+       void *pool_base;
+       int i;
+       int first_core;
+       int core_count;
+       int stream_count;
+
+       /* Init ODP before calling anything else */
+       if (odp_init_global()) {
+               ODP_ERR("Error: ODP global init failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       odp_crypto_init(32);
+
+       /* Reserve memory for args from shared mem */
+       args = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE);
+       if (args == NULL) {
+               ODP_ERR("Error: shared mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       memset(args, 0, sizeof(*args));
+
+       /* Must init our databases before parsing args */
+       ipsec_init_pre();
+       init_fwd_db();
+       init_loopback_db();
+       init_stream_db();
+
+       /* Parse and store the application arguments */
+       parse_args(argc, argv, &args->appl);
+
+       /* Print both system and application information */
+       print_info(NO_PATH(argv[0]), &args->appl);
+
+       core_count  = odp_sys_core_count();
+       num_workers = core_count;
+
+       if (args->appl.core_count)
+               num_workers = args->appl.core_count;
+
+       if (num_workers > MAX_WORKERS)
+               num_workers = MAX_WORKERS;
+
+       printf("Num worker threads: %i\n", num_workers);
+
+       /* Create a barrier to synchronize thread startup */
+       odp_barrier_init_count(&sync_barrier, num_workers);
+
+       /*
+        * By default core #0 runs Linux kernel background tasks.
+        * Start mapping thread from core #1
+        */
+       first_core = (core_count == 1) ? 0 : 1;
+       printf("First core:         %i\n\n", first_core);
+
+       /* Init this thread */
+       thr_id = odp_thread_create(0);
+       odp_init_local(thr_id);
+
+       /* Create packet buffer pool */
+       pool_base = odp_shm_reserve("shm_packet_pool",
+                                   SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE);
+       if (pool_base == NULL) {
+               ODP_ERR("Error: packet pool mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+
+       pool = odp_buffer_pool_create("packet_pool", pool_base,
+                                     SHM_PKT_POOL_SIZE,
+                                     SHM_PKT_POOL_BUF_SIZE,
+                                     ODP_CACHE_LINE_SIZE,
+                                     ODP_BUFFER_TYPE_PACKET);
+       if (pool == ODP_BUFFER_POOL_INVALID) {
+               ODP_ERR("Error: packet pool create failed.\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Populate our IPsec cache */
+       printf("Using %s mode for crypto API\n\n",
+              (args->appl.async) ? "ASYNC" : "SYNC");
+       ipsec_init_post(args->appl.async);
+
+       /* Initialize interfaces (which resolves FWD DB entries */
+       for (i = 0; i < args->appl.if_count; i++) {
+               if (!strncmp("loop", args->appl.if_names[i], strlen("loop")))
+                       initialize_loop(args->appl.if_names[i]);
+               else
+                       initialize_intf(args->appl.if_names[i],
+                                       args->appl.type,
+                                       args->appl.fanout);
+       }
+
+       /* If we have test streams build them before starting workers */
+       resolve_stream_db();
+       stream_count = create_stream_db_inputs();
+
+       /*
+        * Create and init worker threads
+        */
+       memset(thread_tbl, 0, sizeof(thread_tbl));
+       for (i = 0; i < num_workers; ++i) {
+               int core;
+
+               core = (first_core + i) % core_count;
+
+               /*
+                * Create threads one-by-one instead of all-at-once,
+                * because each thread might get different arguments.
+                * Calls odp_thread_create(cpu) for each thread
+                */
+               odp_linux_pthread_create(thread_tbl, 1, core, pktio_thread,
+                                        &args->thread[i]);
+       }
+
+       /*
+        * If there are streams attempt to verify them else
+        * wait indefinitely
+        */
+       if (stream_count) {
+               boolean done;
+
+               do {
+                       done = verify_stream_db_outputs();
+                       sleep(1);
+               } while (!done);
+               printf("All received\n");
+       } else {
+               odp_linux_pthread_join(thread_tbl, num_workers);
+       }
+
+       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 *names, *str, *token, *save;
+       size_t len;
+       int i;
+       static struct option longopts[] = {
+               {"count", required_argument, NULL, 'c'},
+               {"interface", required_argument, NULL, 'i'},    /* return 'i' */
+               {"mode", required_argument, NULL, 'm'},         /* return 'm' */
+               {"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' */
+               {"stream", required_argument, NULL, 's'},       /* return 's' */
+               {"help", no_argument, NULL, 'h'},               /* return 'h' */
+               {NULL, 0, NULL, 0}
+       };
+
+       appl_args->type = 3;  /* 3: ODP_PKTIO_TYPE_SOCKET_MMAP */
+       appl_args->fanout = 0; /* turn off fanout by default for mmap */
+       appl_args->async = 0;  /* turn off async crypto API by default */
+
+       while (1) {
+               opt = getopt_long(argc, argv, "+c:i:m:t:f:h:r:p:a:e:s:",
+                                 longopts, &long_index);
+
+               if (opt == -1)
+                       break;  /* No more options */
+
+               switch (opt) {
+               case 'c':
+                       appl_args->core_count = atoi(optarg);
+                       break;
+                       /* parse packet-io interface names */
+               case 'i':
+                       len = strlen(optarg);
+                       if (len == 0) {
+                               usage(argv[0]);
+                               exit(EXIT_FAILURE);
+                       }
+                       len += 1;       /* add room for '\0' */
+
+                       names = malloc(len);
+                       if (names == NULL) {
+                               usage(argv[0]);
+                               exit(EXIT_FAILURE);
+                       }
+
+                       /* count the number of tokens separated by ',' */
+                       strcpy(names, optarg);
+                       for (str = names, i = 0;; str = NULL, i++) {
+                               token = strtok_r(str, ",", &save);
+                               if (token == NULL)
+                                       break;
+                       }
+                       appl_args->if_count = i;
+
+                       if (appl_args->if_count == 0) {
+                               usage(argv[0]);
+                               exit(EXIT_FAILURE);
+                       }
+
+                       /* allocate storage for the if names */
+                       appl_args->if_names =
+                               calloc(appl_args->if_count, sizeof(char *));
+
+                       /* store the if names (reset names string) */
+                       strcpy(names, optarg);
+                       for (str = names, i = 0;; str = NULL, i++) {
+                               token = strtok_r(str, ",", &save);
+                               if (token == NULL)
+                                       break;
+                               appl_args->if_names[i] = token;
+                       }
+                       break;
+
+               case 't':
+                       appl_args->type = atoi(optarg);
+                       break;
+
+               case 'f':
+                       appl_args->fanout = atoi(optarg);
+                       break;
+
+               case 'm':
+                       appl_args->async = atoi(optarg);
+                       break;
+
+               case 'r':
+                       create_fwd_db_entry(optarg);
+                       break;
+
+               case 'p':
+                       create_sp_db_entry(optarg);
+                       break;
+
+               case 'a':
+                       create_sa_db_entry(optarg, FALSE);
+                       break;
+
+               case 'e':
+                       create_sa_db_entry(optarg, TRUE);
+                       break;
+
+               case 's':
+                       create_stream_db_entry(optarg);
+                       break;
+
+               case 'h':
+                       usage(argv[0]);
+                       exit(EXIT_SUCCESS);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       if (appl_args->if_count == 0) {
+               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"
+              "Core count:      %i\n"
+              "\n",
+              odp_version_api_str(), odp_sys_cpu_model_str(), odp_sys_cpu_hz(),
+              odp_sys_cache_line_size(), odp_sys_core_count());
+
+       printf("Running ODP appl: \"%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();
+       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"
+              " -t, --type   1: ODP_PKTIO_TYPE_SOCKET_BASIC\n"
+              "              2: ODP_PKTIO_TYPE_SOCKET_MMSG\n"
+              "              3: ODP_PKTIO_TYPE_SOCKET_MMAP\n"
+              "              4: ODP_PKTIO_TYPE_NETMAP\n"
+              "         Default: 3: ODP_PKTIO_TYPE_SOCKET_MMAP\n"
+              " -f, --fanout 0: off 1: on (Default 1: on)\n"
+              " -m, --mode   0: sync 1: async crypto API (default 0)\n"
+              "\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"
+              "\n"
+              "  Where: NextHopMAC is raw hex/dot notation, i.e. 03.BA.44.9A.CE.02\n"
+              "         IP is decimal/dot notation, i.e. 192.168.1.1\n"
+              "         SubNet is decimal/dot/slash notation, i.e 192.168.0.0/16\n<http://192.168.0.0/16/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<http://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<http://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"
+              "\n"
+              "Optional OPTIONS\n"
+              "  -c, --count <number> Core count.\n"
+              "  -h, --help           Display help and exit.\n"
+              "\n", NO_PATH(progname), NO_PATH(progname)
+               );
+}
diff --git a/test/ipsec/run_ah_in.sh b/test/ipsec/run_ah_in.sh
new file mode 100755
index 0000000..1618071
--- /dev/null
+++ b/test/ipsec/run_ah_in.sh
@@ -0,0 +1,13 @@ 
+#!/bin/bash
+#
+# Test input AH
+#  - 2 loop interfaces
+#  - 10 packets
+#  - SYNC API mode
+#
+./odp_ipsec -i loop1,loop2 \
+-r 192.168.111.2/32:loop1:08.00.27.76.B5.E0<http://192.168.111.2/32:loop1:08.00.27.76.B5.E0> \
+-p 192.168.222.0/24:192.168.111.0/24:in:ah<http://192.168.222.0/24:192.168.111.0/24:in:ah> \
+-a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8 \
+-s 192.168.222.2:192.168.111.2:loop2:loop1:10:100 \
+-c 2 -f 0 -m 0
diff --git a/test/ipsec/run_ah_out.sh b/test/ipsec/run_ah_out.sh
new file mode 100755
index 0000000..d61b52a
--- /dev/null
+++ b/test/ipsec/run_ah_out.sh
@@ -0,0 +1,12 @@ 
+#!/bin/bash
+#
+# Test output AH
+#  - 2 loop interfaces
+#  - 10 packets
+#  - SYNC API mode
+ ./odp_ipsec -i loop1,loop2 \
+-r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB<http://192.168.222.2/32:loop2:08.00.27.F5.8B.DB> \
+-p 192.168.111.0/24:192.168.222.0/24:out:ah<http://192.168.111.0/24:192.168.222.0/24:out:ah> \
+-a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6 \
+-s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
+-c 2 -f 0 -m 0
diff --git a/test/ipsec/run_both_in.sh b/test/ipsec/run_both_in.sh
new file mode 100755
index 0000000..3b27409
--- /dev/null
+++ b/test/ipsec/run_both_in.sh
@@ -0,0 +1,14 @@ 
+#!/bin/bash
+#
+# Test AH and ESP input
+#  - 2 loop interfaces
+#  - 10 packets
+#  - SYNC API mode
+./odp_ipsec -i loop1,loop2 \
+-r 192.168.111.2/32:loop1:08.00.27.76.B5.E0<http://192.168.111.2/32:loop1:08.00.27.76.B5.E0> \
+-p 192.168.222.0/24:192.168.111.0/24:in:both<http://192.168.222.0/24:192.168.111.0/24:in:both> \
+-a 192.168.222.2:192.168.111.2:md5:300:27f6d123d7077b361662fc6e451f65d8 \
+-e 192.168.222.2:192.168.111.2:\
+3des:301:c966199f24d095f3990a320d749056401e82b26570320292 \
+-s 192.168.222.2:192.168.111.2:loop2:loop1:10:100 \
+-c 2 -f 0 -m 0
diff --git a/test/ipsec/run_both_out.sh b/test/ipsec/run_both_out.sh
new file mode 100755
index 0000000..37bb1e4
--- /dev/null
+++ b/test/ipsec/run_both_out.sh
@@ -0,0 +1,14 @@ 
+#!/bin/bash
+#
+# Test AH and ESP output
+#  - 2 loop interfaces
+#  - 10 packets
+#  - SYNC API mode
+./odp_ipsec -i loop1,loop2 \
+-r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB<http://192.168.222.2/32:loop2:08.00.27.F5.8B.DB> \
+-p 192.168.111.0/24:192.168.222.0/24:out:both<http://192.168.111.0/24:192.168.222.0/24:out:both> \
+-e 192.168.111.2:192.168.222.2:\
+3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+-a 192.168.111.2:192.168.222.2:md5:200:a731649644c5dee92cbd9c2e7e188ee6 \
+-s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
+-c 2 -f 0 -m 0
diff --git a/test/ipsec/run_esp_in.sh b/test/ipsec/run_esp_in.sh
new file mode 100755
index 0000000..a9a0db3
--- /dev/null
+++ b/test/ipsec/run_esp_in.sh
@@ -0,0 +1,13 @@ 
+#!/bin/bash
+#
+# Test input ESP
+#  - 2 loop interfaces
+#  - 10 packets
+#  - SYNC API mode
+./odp_ipsec -i loop1,loop2 \
+-r 192.168.111.2/32:loop1:08.00.27.76.B5.E0<http://192.168.111.2/32:loop1:08.00.27.76.B5.E0> \
+-p 192.168.222.0/24:192.168.111.0/24:in:esp<http://192.168.222.0/24:192.168.111.0/24:in:esp> \
+-e 192.168.222.2:192.168.111.2:\
+3des:301:c966199f24d095f3990a320d749056401e82b26570320292 \
+-s 192.168.222.2:192.168.111.2:loop2:loop1:10:100 \
+-c 2 -f 0 -m 0
diff --git a/test/ipsec/run_esp_out.sh b/test/ipsec/run_esp_out.sh
new file mode 100755
index 0000000..f87311c
--- /dev/null
+++ b/test/ipsec/run_esp_out.sh
@@ -0,0 +1,13 @@ 
+#!/bin/bash
+#
+# Test output ESP
+#  - 2 loop interfaces
+#  - 10 packets
+#  - SYNC API mode
+./odp_ipsec -i loop1,loop2 \
+-r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB<http://192.168.222.2/32:loop2:08.00.27.F5.8B.DB> \
+-p 192.168.111.0/24:192.168.222.0/24:out:esp<http://192.168.111.0/24:192.168.222.0/24:out:esp> \
+-e 192.168.111.2:192.168.222.2:\
+3des:201:656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+-s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
+-c 2 -f 0 -m 0
diff --git a/test/ipsec/run_simple.sh b/test/ipsec/run_simple.sh
new file mode 100755
index 0000000..1d80fa9
--- /dev/null
+++ b/test/ipsec/run_simple.sh
@@ -0,0 +1,10 @@ 
+#!/bin/bash
+#
+# Simple router test
+#  - 2 loop interfaces
+#  - 10 packets
+#  - SYNC API mode
+./odp_ipsec -i loop1,loop2 \
+-r 192.168.222.2/32:loop2:08.00.27.F5.8B.DB<http://192.168.222.2/32:loop2:08.00.27.F5.8B.DB> \
+-s 192.168.111.2:192.168.222.2:loop1:loop2:10:100 \
+-c 2 -f 0 -m 0