aboutsummaryrefslogtreecommitdiff
path: root/example/ipsec_api
diff options
context:
space:
mode:
Diffstat (limited to 'example/ipsec_api')
-rw-r--r--example/ipsec_api/.gitignore2
-rw-r--r--example/ipsec_api/Makefile.am76
-rw-r--r--example/ipsec_api/README155
-rw-r--r--example/ipsec_api/odp_ipsec.c1405
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_ah_in.sh39
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_ah_out.sh39
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_ah_tun_in.sh40
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_ah_tun_out.sh40
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_esp_in.sh39
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_esp_out.sh43
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_esp_tun_in.sh40
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_esp_tun_out.sh40
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_live.sh61
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_router.sh57
-rwxr-xr-xexample/ipsec_api/odp_ipsec_api_run_simple.sh41
-rw-r--r--example/ipsec_api/odp_ipsec_cache.c203
-rw-r--r--example/ipsec_api/odp_ipsec_cache.h114
l---------example/ipsec_api/odp_ipsec_fwd_db.c1
l---------example/ipsec_api/odp_ipsec_fwd_db.h1
l---------example/ipsec_api/odp_ipsec_misc.h1
l---------example/ipsec_api/odp_ipsec_sa_db.c1
l---------example/ipsec_api/odp_ipsec_sa_db.h1
l---------example/ipsec_api/odp_ipsec_sp_db.c1
l---------example/ipsec_api/odp_ipsec_sp_db.h1
l---------example/ipsec_api/odp_ipsec_stream.c1
l---------example/ipsec_api/odp_ipsec_stream.h1
26 files changed, 2443 insertions, 0 deletions
diff --git a/example/ipsec_api/.gitignore b/example/ipsec_api/.gitignore
new file mode 100644
index 000000000..77f399c54
--- /dev/null
+++ b/example/ipsec_api/.gitignore
@@ -0,0 +1,2 @@
+odp_ipsec_api
+pktio_env
diff --git a/example/ipsec_api/Makefile.am b/example/ipsec_api/Makefile.am
new file mode 100644
index 000000000..5a71d04e5
--- /dev/null
+++ b/example/ipsec_api/Makefile.am
@@ -0,0 +1,76 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_ipsec_api
+
+if test_example
+TESTS = \
+ odp_ipsec_api_run_ah_in.sh \
+ odp_ipsec_api_run_ah_out.sh \
+ odp_ipsec_api_run_ah_tun_in.sh \
+ odp_ipsec_api_run_ah_tun_out.sh \
+ odp_ipsec_api_run_esp_in.sh \
+ odp_ipsec_api_run_esp_out.sh \
+ odp_ipsec_api_run_esp_tun_in.sh \
+ odp_ipsec_api_run_esp_tun_out.sh \
+ odp_ipsec_api_run_live.sh \
+ odp_ipsec_api_run_router.sh \
+ odp_ipsec_api_run_simple.sh
+endif
+
+EXTRA_DIST = \
+ odp_ipsec_api_run_ah_in.sh \
+ odp_ipsec_api_run_ah_out.sh \
+ odp_ipsec_api_run_ah_tun_in.sh \
+ odp_ipsec_api_run_ah_tun_out.sh \
+ odp_ipsec_api_run_esp_in.sh \
+ odp_ipsec_api_run_esp_out.sh \
+ odp_ipsec_api_run_esp_tun_in.sh \
+ odp_ipsec_api_run_esp_tun_out.sh \
+ odp_ipsec_api_run_live.sh \
+ odp_ipsec_api_run_router.sh \
+ odp_ipsec_api_run_simple.sh
+
+odp_ipsec_api_SOURCES = \
+ odp_ipsec.c \
+ odp_ipsec_sa_db.c \
+ odp_ipsec_sa_db.h \
+ odp_ipsec_sp_db.c \
+ odp_ipsec_sp_db.h \
+ odp_ipsec_fwd_db.c \
+ odp_ipsec_fwd_db.h \
+ odp_ipsec_cache.c \
+ odp_ipsec_cache.h \
+ odp_ipsec_misc.h
+
+if WITH_OPENSSL
+odp_ipsec_api_SOURCES += \
+ odp_ipsec_stream.c \
+ odp_ipsec_stream.h
+
+AM_CPPFLAGS = $(OPENSSL_CPPFLAGS)
+LDADD += $(OPENSSL_LIBS)
+
+else
+AM_CPPFLAGS = -DNO_OPENSSL
+endif
+
+# If building out-of-tree, make check will not copy the scripts and data to the
+# $(builddir) assuming that all commands are run locally. However this prevents
+# running tests on a remote target using LOG_COMPILER.
+# So copy all script and data files explicitly here.
+all-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ if [ -e $(srcdir)/$$f ]; then \
+ mkdir -p $(builddir)/$$(dirname $$f); \
+ cp -f $(srcdir)/$$f $(builddir)/$$f; \
+ fi \
+ done \
+ fi
+ ln -f -s ../../platform/$(with_platform)/test/example/ipsec_api/pktio_env pktio_env
+clean-local:
+ if [ "x$(srcdir)" != "x$(builddir)" ]; then \
+ for f in $(EXTRA_DIST); do \
+ rm -f $(builddir)/$$f; \
+ done \
+ fi
diff --git a/example/ipsec_api/README b/example/ipsec_api/README
new file mode 100644
index 000000000..7a392a728
--- /dev/null
+++ b/example/ipsec_api/README
@@ -0,0 +1,155 @@
+SPDX-License-Identifier: BSD-3-Clause
+Copyright (c) 2014-2018 Linaro Limited
+Copyright (c) 2020 Nokia
+
+1. Intro
+
+The IPsec API example application "odp_ipsec_api" functions as a simple L3 IPv4
+router which supports IPsec AH and ESP protocols in both transmit and receive
+directions. AH and ESP protocols are not supported simultaneously.
+
+With ESP, the application supports 3DES and NULL encryption algorithms, with
+NULL authentication. With AH, HMAC-MD5 or HMAC-SHA-256 integrity algorithms are
+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 Fedora 32. Sanity testing consists of pinging VM2 from VM0
+such that the packets traverse VM1. Packets between VM1 and VM2 are
+IPsec AH or 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 following 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 arp -s 192.168.111.1 08:00:27:04:BF:8C
+
+ 4.2 VM1 configuration
+
+For the unit under test, IP forwarding and IP tables were disabled.
+
+VM1 has the following 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:
+
+$ sudo ./odp_ipsec_api -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,esp \
+-e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224 \
+-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 \
+-c 2 -m 0
+
+ 4.3 VM2 configuration
+
+VM2 has the following 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
+
+VM2 must be setup with an IPsec configuration complementing the configuration
+used by the "odp_ipsec_api" application running on VM1. The configuration is
+applied using "setkey" (provided by ipsec-tools package).
+
+VM2 uses the following setkey configuration:
+
+$ cat setkey_vm2.conf
+# Flush the SAD and SPD
+flush;
+spdflush;
+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;
+spdadd 192.168.222.2 192.168.111.2 any -P out ipsec esp/transport//require;
+
+Apply the setkey configuration:
+$ sudo setkey -f setkey_vm2.conf
+
+5. Sanity Test with Real Traffic
+
+Once all three VMs have been configured, static ARP and route entries added,
+setkey configuration applied, and odp_ipsec_api application is running, 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_seq=1 ttl=64 time=0.614 ms
+64 bytes from 192.168.222.2: icmp_seq=2 ttl=64 time=0.560 ms
+
+At VM2 console use tcpdump to observe IPsec packets:
+
+$ sudo tcpdump -nt -i p7p1
+dropped privs to tcpdump
+tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
+listening on enp0s9, link-type EN10MB (Ethernet), capture size 262144 bytes
+IP 192.168.111.2 > 192.168.222.2: ESP(spi=0x00000201,seq=0x196), length 88
+IP 192.168.222.2 > 192.168.111.2: ESP(spi=0x00000301,seq=0xf4), length 88
+IP 192.168.111.2 > 192.168.222.2: ESP(spi=0x00000201,seq=0x197), length 88
+IP 192.168.222.2 > 192.168.111.2: ESP(spi=0x00000301,seq=0xf5), length 88
+
+6. Standalone Loopback Tests
+
+Bash script files are also included to run several simple loopback tests that
+do not require any packet IO. The scripts create internal "loopback" packet
+interfaces.
diff --git a/example/ipsec_api/odp_ipsec.c b/example/ipsec_api/odp_ipsec.c
new file mode 100644
index 000000000..a67a3a1e8
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec.c
@@ -0,0 +1,1405 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2013-2018 Linaro Limited
+ */
+
+/**
+ * @example ipsec_api/odp_ipsec.c
+ *
+ * IPsec example application using ODP IPsec API
+ *
+ * @cond _ODP_HIDE_FROM_DOXYGEN_
+ */
+
+/* enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <odp_api.h>
+
+#include <odp/helper/odph_api.h>
+
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include <odp_ipsec_misc.h>
+#include <odp_ipsec_sa_db.h>
+#include <odp_ipsec_sp_db.h>
+#include <odp_ipsec_fwd_db.h>
+#include <odp_ipsec_cache.h>
+
+#ifndef NO_OPENSSL
+#include <odp_ipsec_stream.h>
+#else
+static void init_stream_db(void) {}
+static void resolve_stream_db(void) {}
+static int create_stream_db_inputs(void)
+{
+ return 0;
+}
+
+static odp_bool_t verify_stream_db_outputs(void)
+{
+ return true;
+}
+
+static int create_stream_db_entry(char *input ODP_UNUSED)
+{
+ return -1;
+}
+#endif
+
+/* maximum number of worker threads */
+#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
+
+#define MAX_POLL_QUEUES 256
+
+/**
+ * Parsed command line application arguments
+ */
+typedef struct {
+ unsigned int cpu_count;
+ int if_count; /**< Number of interfaces to be used */
+ char **if_names; /**< Array of pointers to interface names */
+ odp_ipsec_op_mode_t mode; /**< IPsec operation mode */
+ odp_pool_t pool; /**< Buffer pool for packet IO */
+ char *if_str; /**< Storage for interface names */
+ odp_bool_t lookup;
+} appl_args_t;
+
+/**
+ * Grouping of both parsed CL args and global application data
+ */
+typedef struct {
+ /** Application (parsed) arguments */
+ appl_args_t appl;
+ odp_shm_t shm;
+ odp_pool_t ctx_pool;
+ odp_pool_t pkt_pool;
+ /** ORDERED queue for per packet crypto API completion events */
+ odp_queue_t completionq;
+ /** Synchronize threads before packet processing begins */
+ odp_barrier_t sync_barrier;
+ odp_queue_t poll_queues[MAX_POLL_QUEUES];
+ int num_polled_queues;
+ /* Stop workers if set to 1 */
+ odp_atomic_u32_t exit_threads;
+} global_data_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);
+
+/**
+ * Buffer pool for packet IO
+ */
+#define SHM_PKT_POOL_BUF_COUNT 1024
+#define SHM_PKT_POOL_BUF_SIZE 4096
+#define SHM_PKT_POOL_SIZE (SHM_PKT_POOL_BUF_COUNT * SHM_PKT_POOL_BUF_SIZE)
+
+/**
+ * Packet processing states/steps
+ */
+typedef enum {
+ PKT_STATE_INPUT_VERIFY, /**< Verify IPv4 and ETH */
+ PKT_STATE_IPSEC_IN_CLASSIFY, /**< Initiate input IPsec */
+ PKT_STATE_ROUTE_LOOKUP, /**< Use DST IP to find output IF */
+ PKT_STATE_IPSEC_OUT_CLASSIFY, /**< Intiate 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 processing context
+ */
+typedef struct {
+ odp_buffer_t buffer; /**< Buffer for context */
+ pkt_state_e state; /**< Next processing step */
+ odp_pktout_queue_t pktout; /**< Packet output queue */
+ odp_pktio_t pktio; /**< Packet I/O */
+ odph_ethhdr_t eth; /**< L2 header */
+} pkt_ctx_t;
+
+#define SHM_CTX_POOL_BUF_SIZE (sizeof(pkt_ctx_t))
+#define SHM_CTX_POOL_BUF_COUNT (SHM_PKT_POOL_BUF_COUNT)
+#define SHM_CTX_POOL_SIZE (SHM_CTX_POOL_BUF_COUNT * SHM_CTX_POOL_BUF_SIZE)
+
+static global_data_t *global;
+
+static void sig_handler(int signo ODP_UNUSED)
+{
+ if (global == NULL)
+ return;
+ odp_atomic_store_u32(&global->exit_threads, 1);
+}
+
+/**
+ * Allocate per packet processing context and associate it with
+ * packet buffer
+ *
+ * @param pkt Packet
+ *
+ * @return pointer to context area
+ */
+static
+pkt_ctx_t *alloc_pkt_ctx(odp_packet_t pkt)
+{
+ odp_buffer_t ctx_buf = odp_buffer_alloc(global->ctx_pool);
+ pkt_ctx_t *ctx;
+
+ if (odp_unlikely(ODP_BUFFER_INVALID == ctx_buf))
+ return NULL;
+
+ ctx = odp_buffer_addr(ctx_buf);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->buffer = ctx_buf;
+ odp_packet_user_ptr_set(pkt, ctx);
+
+ return ctx;
+}
+
+/**
+ * Release per packet resources
+ *
+ * @param ctx Packet context
+ */
+static
+void free_pkt_ctx(pkt_ctx_t *ctx)
+{
+ odp_buffer_free(ctx->buffer);
+}
+
+/**
+ * Example supports either polling queues or using odp_schedule
+ */
+typedef odp_queue_t (*queue_create_func_t)
+ (const char *, const odp_queue_param_t *);
+typedef odp_event_t (*schedule_func_t) (odp_queue_t *);
+
+static queue_create_func_t queue_create;
+static schedule_func_t schedule_fn;
+
+/**
+ * odp_queue_create wrapper to enable polling versus scheduling
+ */
+static
+odp_queue_t polled_odp_queue_create(const char *name,
+ const odp_queue_param_t *param)
+{
+ odp_queue_t my_queue;
+ odp_queue_param_t qp;
+ odp_queue_type_t type;
+
+ odp_queue_param_init(&qp);
+ if (param)
+ memcpy(&qp, param, sizeof(odp_queue_param_t));
+
+ type = qp.type;
+
+ if (ODP_QUEUE_TYPE_SCHED == type) {
+ printf("%s: change %s to PLAIN\n", __func__, name);
+ qp.type = ODP_QUEUE_TYPE_PLAIN;
+ }
+
+ my_queue = odp_queue_create(name, &qp);
+
+ if (ODP_QUEUE_TYPE_SCHED == type) {
+ global->poll_queues[global->num_polled_queues++] = my_queue;
+ printf("%s: adding %" PRIu64 "\n", __func__,
+ odp_queue_to_u64(my_queue));
+ }
+
+ return my_queue;
+}
+
+static inline
+odp_event_t odp_schedule_cb(odp_queue_t *from)
+{
+ return odp_schedule(from, ODP_SCHED_NO_WAIT);
+}
+
+/**
+ * odp_schedule replacement to poll queues versus using ODP scheduler
+ */
+static
+odp_event_t polled_odp_schedule_cb(odp_queue_t *from)
+{
+ int idx = 0;
+
+ while (idx < global->num_polled_queues) {
+ odp_queue_t queue = global->poll_queues[idx++];
+ odp_event_t ev;
+
+ ev = odp_queue_deq(queue);
+
+ if (ODP_EVENT_INVALID != ev) {
+ if (from)
+ *from = queue;
+
+ return ev;
+ }
+ }
+
+ if (from)
+ *from = ODP_QUEUE_INVALID;
+
+ return ODP_EVENT_INVALID;
+}
+
+/**
+ * IPsec pre argument processing initialization
+ */
+static
+void ipsec_init_pre(void)
+{
+ odp_queue_param_t qparam;
+
+ /*
+ * Create queues
+ *
+ * - completion queue (should eventually be ORDERED)
+ * - sequence number queue (must be ATOMIC)
+ */
+ odp_queue_param_init(&qparam);
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_max_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+
+ global->completionq = queue_create("completion", &qparam);
+ if (ODP_QUEUE_INVALID == global->completionq) {
+ ODPH_ERR("Error: completion queue creation failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ qparam.type = ODP_QUEUE_TYPE_SCHED;
+ qparam.sched.prio = odp_schedule_max_prio();
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_ALL;
+
+ /* Initialize our data bases */
+ init_sp_db();
+ init_sa_db();
+ init_tun_db();
+ init_ipsec_cache();
+}
+
+/**
+ * IPsec post argument processing initialization
+ *
+ * Resolve SP DB with SA DB and create corresponding IPsec cache entries
+ *
+ * @param api_mode Mode to use for IPsec operation
+ */
+static
+void ipsec_init_post(odp_ipsec_op_mode_t api_mode)
+{
+ sp_db_entry_t *entry;
+ odp_ipsec_config_t ipsec_config;
+ odp_ipsec_capability_t ipsec_cap;
+
+ if (odp_ipsec_capability(&ipsec_cap) != ODP_IPSEC_OK) {
+ ODPH_ERR("Error: failure getting IPSec caps\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_ipsec_config_init(&ipsec_config);
+ ipsec_config.inbound.parse_level = ODP_PROTO_LAYER_ALL;
+ ipsec_config.inbound_mode = api_mode;
+ ipsec_config.outbound_mode = api_mode;
+ ipsec_config.inbound.default_queue = global->completionq;
+ if (odp_ipsec_config(&ipsec_config) != ODP_IPSEC_OK) {
+ ODPH_ERR("Error: failure setting IPSec config\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Attempt to find appropriate SA for each SP */
+ for (entry = sp_db->list; NULL != entry; entry = entry->next) {
+ sa_db_entry_t *cipher_sa = NULL;
+ sa_db_entry_t *auth_sa = NULL;
+ tun_db_entry_t *tun = NULL;
+
+ if (entry->esp) {
+ cipher_sa = find_sa_db_entry(&entry->src_subnet,
+ &entry->dst_subnet,
+ 1);
+ tun = find_tun_db_entry(cipher_sa->src_ip,
+ cipher_sa->dst_ip);
+ }
+ if (entry->ah) {
+ auth_sa = find_sa_db_entry(&entry->src_subnet,
+ &entry->dst_subnet,
+ 0);
+ tun = find_tun_db_entry(auth_sa->src_ip,
+ auth_sa->dst_ip);
+ }
+
+ if (cipher_sa || auth_sa) {
+ if (create_ipsec_cache_entry(cipher_sa,
+ auth_sa,
+ tun,
+ entry->input,
+ global->completionq)) {
+ ODPH_ERR("Error: IPSec cache entry failed.\n"
+ );
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ printf(" WARNING: SA not found for SP\n");
+ dump_sp_db_entry(entry);
+ }
+ }
+}
+
+#ifndef NO_OPENSSL
+static
+int check_stream_db_in(const char *intf)
+{
+ 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) {
+ if (!strcmp(stream->input.intf, intf))
+ return 1;
+ }
+
+ return 0;
+}
+
+static
+int check_stream_db_out(const char *intf)
+{
+ 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) {
+ if (!strcmp(stream->output.intf, intf))
+ return 1;
+ }
+
+ return 0;
+}
+#else
+static
+int check_stream_db_in(const char *intf ODP_UNUSED)
+{
+ return 0;
+}
+
+static
+int check_stream_db_out(const char *intf ODP_UNUSED)
+{
+ return 0;
+}
+#endif
+
+/**
+ * Initialize interface
+ *
+ * Initialize ODP pktio and queues, query MAC address and update
+ * forwarding database.
+ *
+ * @param intf Interface name string
+ */
+static
+void initialize_intf(char *intf)
+{
+ odp_pktio_t pktio;
+ odp_pktout_queue_t pktout;
+ odp_queue_t inq;
+ int ret;
+ uint8_t src_mac[ODPH_ETHADDR_LEN];
+ char src_mac_str[MAX_STRING];
+ odp_pktio_param_t pktio_param;
+ odp_pktin_queue_param_t pktin_param;
+ odp_pktio_capability_t capa;
+ odp_pktio_config_t config;
+
+ odp_pktio_param_init(&pktio_param);
+
+ if (getenv("ODP_IPSEC_USE_POLL_QUEUES") ||
+ check_stream_db_out(intf))
+ pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
+ else
+ pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;
+
+ /*
+ * Open a packet IO instance for thread and get default output queue
+ */
+ pktio = odp_pktio_open(intf, global->pkt_pool, &pktio_param);
+ if (ODP_PKTIO_INVALID == pktio) {
+ ODPH_ERR("Error: pktio create failed for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pktin_queue_param_init(&pktin_param);
+ pktin_param.queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+
+ if (odp_pktin_queue_config(pktio, &pktin_param)) {
+ ODPH_ERR("Error: pktin config failed for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktout_queue_config(pktio, NULL)) {
+ ODPH_ERR("Error: pktout config failed for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktin_event_queue(pktio, &inq, 1) != 1) {
+ ODPH_ERR("Error: failed to get input queue for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktout_queue(pktio, &pktout, 1) != 1) {
+ ODPH_ERR("Error: failed to get pktout queue for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_pktio_capability(pktio, &capa) != 0) {
+ ODPH_ERR("Error: failed to get capabilities for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ odp_pktio_config_init(&config);
+ if (check_stream_db_in(intf) &&
+ global->appl.mode == ODP_IPSEC_OP_MODE_INLINE)
+ config.inbound_ipsec = capa.config.inbound_ipsec;
+ if (check_stream_db_out(intf) &&
+ global->appl.mode == ODP_IPSEC_OP_MODE_INLINE)
+ config.outbound_ipsec = capa.config.outbound_ipsec;
+
+ if (odp_pktio_config(pktio, &config) != 0) {
+ ODPH_ERR("Error: failed to set config for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = odp_pktio_start(pktio);
+ if (ret) {
+ ODPH_ERR("Error: unable to start %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Read the source MAC address for this interface */
+ ret = odp_pktio_mac_addr(pktio, src_mac, sizeof(src_mac));
+ if (ret <= 0) {
+ ODPH_ERR("Error: failed during MAC address get for %s\n", intf);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Created pktio:%02" PRIu64 ", queue mode (ATOMIC queues)\n"
+ " default pktio%02" PRIu64 "-INPUT queue:%" PRIu64 "\n"
+ " source mac address %s\n",
+ odp_pktio_to_u64(pktio), odp_pktio_to_u64(pktio),
+ odp_queue_to_u64(inq),
+ mac_addr_str(src_mac_str, src_mac));
+
+ /* Resolve any routes using this interface for output */
+ resolve_fwd_db(intf, pktio, pktout, src_mac);
+}
+
+/**
+ * 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 do_input_verify(odp_packet_t pkt,
+ pkt_ctx_t *ctx ODP_UNUSED)
+{
+ if (odp_unlikely(odp_packet_has_error(pkt)))
+ return PKT_DROP;
+
+ if (!odp_packet_has_eth(pkt))
+ return PKT_DROP;
+
+ if (!odp_packet_has_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 do_route_fwd_db(odp_packet_t pkt, pkt_ctx_t *ctx)
+{
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL);
+ fwd_db_entry_t *entry;
+
+ entry = find_fwd_db_entry(odp_be_to_cpu_32(ip->dst_addr));
+
+ if (entry) {
+ uint32_t l3_offset = odp_packet_l3_offset(pkt);
+
+ if (l3_offset > sizeof(odph_ethhdr_t))
+ odp_packet_pull_head(pkt,
+ l3_offset - sizeof(odph_ethhdr_t));
+ else
+ odp_packet_push_head(pkt,
+ sizeof(odph_ethhdr_t) - l3_offset);
+
+ memcpy(&ctx->eth.dst, entry->dst_mac, ODPH_ETHADDR_LEN);
+ memcpy(&ctx->eth.src, entry->src_mac, ODPH_ETHADDR_LEN);
+ ctx->eth.type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
+
+ if (global->appl.mode != ODP_IPSEC_OP_MODE_INLINE) {
+ odp_packet_l2_offset_set(pkt, 0);
+ odp_packet_copy_from_mem(pkt, 0, ODPH_ETHHDR_LEN,
+ &ctx->eth);
+ }
+
+ ctx->pktio = entry->pktio;
+ ctx->pktout = entry->pktout;
+
+ 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
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_in_classify(odp_packet_t *ppkt)
+{
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(*ppkt, NULL);
+ odph_ahhdr_t *ah = NULL;
+ odph_esphdr_t *esp = NULL;
+ ipsec_cache_entry_t *entry;
+ odp_ipsec_in_param_t in_param;
+ int rc;
+
+ /* Check IP header for IPSec protocols and look it up */
+ 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;
+
+ memset(&in_param, 0, sizeof(in_param));
+ if (global->appl.lookup) {
+ in_param.num_sa = 0;
+ in_param.sa = NULL;
+ } else {
+ in_param.num_sa = 1;
+ in_param.sa = &entry->ipsec_sa;
+ }
+
+ /* Issue crypto request */
+ if (global->appl.mode != ODP_IPSEC_OP_MODE_SYNC) {
+ rc = odp_ipsec_in_enq(ppkt, 1, &in_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ return PKT_POSTED;
+ } else {
+ int out = 1;
+ odp_ipsec_packet_result_t result;
+
+ rc = odp_ipsec_in(ppkt, 1, ppkt, &out, &in_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ if (odp_ipsec_result(&result, *ppkt) < 0) {
+ ODPH_DBG("odp_ipsec_result() failed\n");
+ return PKT_DROP;
+ }
+ if (result.status.error.all != 0) {
+ ODPH_DBG("Error in inbound IPsec processing\n");
+ return PKT_DROP;
+ }
+ 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
+ *
+ * @return PKT_CONTINUE if done else PKT_POSTED
+ */
+static
+pkt_disposition_e do_ipsec_out_classify(odp_packet_t *ppkt, pkt_ctx_t *ctx)
+{
+ odph_ipv4hdr_t *ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(*ppkt, NULL);
+ ipsec_cache_entry_t *entry;
+ odp_ipsec_out_param_t out_param;
+ int rc;
+
+ /* 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;
+
+ memset(&out_param, 0, sizeof(out_param));
+ out_param.num_sa = 1;
+ out_param.num_opt = 0;
+ out_param.sa = &entry->ipsec_sa;
+ out_param.opt = NULL;
+
+ /* Issue crypto request */
+ if (global->appl.mode == ODP_IPSEC_OP_MODE_INLINE) {
+ odp_ipsec_out_inline_param_t inline_param;
+
+ inline_param.pktio = ctx->pktio;
+ inline_param.outer_hdr.ptr = (void *)&ctx->eth;
+ inline_param.outer_hdr.len = ODPH_ETHHDR_LEN;
+ rc = odp_ipsec_out_inline(ppkt, 1, &out_param, &inline_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ return PKT_DONE;
+ } else if (global->appl.mode != ODP_IPSEC_OP_MODE_SYNC) {
+ rc = odp_ipsec_out_enq(ppkt, 1, &out_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ return PKT_POSTED;
+ } else {
+ int out = 1;
+ odp_ipsec_packet_result_t result;
+
+ rc = odp_ipsec_out(ppkt, 1, ppkt, &out, &out_param);
+ if (rc <= 0)
+ return PKT_DROP;
+
+ if (odp_ipsec_result(&result, *ppkt) < 0) {
+ ODPH_DBG("odp_ipsec_result() failed\n");
+ return PKT_DROP;
+ }
+ if (result.status.error.all != 0) {
+ ODPH_DBG("Error in outbound IPsec processing\n");
+ return PKT_DROP;
+ }
+ 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 Required by "odph_thread_create", unused
+ *
+ * @return NULL (should never return)
+ */
+static
+int pktio_thread(void *arg ODP_UNUSED)
+{
+ int thr;
+ odp_packet_t pkt;
+ odp_event_t ev;
+ unsigned long pkt_cnt = 0;
+
+ thr = odp_thread_id();
+
+ printf("Pktio thread [%02i] starts\n", thr);
+
+ odp_barrier_wait(&global->sync_barrier);
+
+ /* Loop packets */
+ while (!odp_atomic_load_u32(&global->exit_threads)) {
+ pkt_disposition_e rc = PKT_CONTINUE;
+ pkt_ctx_t *ctx;
+ odp_queue_t dispatchq;
+ odp_event_subtype_t subtype;
+
+ /* Use schedule to get event from any input queue */
+ ev = schedule_fn(&dispatchq);
+
+ if (ev == ODP_EVENT_INVALID)
+ continue;
+
+ /* Determine new work versus completion or sequence number */
+ if (ODP_EVENT_PACKET == odp_event_types(ev, &subtype)) {
+ pkt = odp_packet_from_event(ev);
+ if (ODP_EVENT_PACKET_BASIC == subtype) {
+ ctx = alloc_pkt_ctx(pkt);
+ if (!ctx) {
+ odp_packet_free(pkt);
+ continue;
+ }
+ ctx->state = PKT_STATE_INPUT_VERIFY;
+ } else if (ODP_EVENT_PACKET_IPSEC == subtype) {
+ odp_ipsec_packet_result_t result;
+
+ if (odp_unlikely(odp_ipsec_result(&result,
+ pkt) < 0)) {
+ ODPH_DBG("Error Event\n");
+ odp_event_free(ev);
+ continue;
+ }
+
+ if (result.status.error.all != 0) {
+ ODPH_DBG("Error in IPsec\n");
+ rc = PKT_DROP;
+ }
+
+ if (result.flag.inline_mode) {
+ ctx = alloc_pkt_ctx(pkt);
+ if (!ctx) {
+ odp_packet_free(pkt);
+ continue;
+ }
+ if (odp_unlikely(
+ odp_packet_has_error(pkt) ||
+ !odp_packet_has_ipv4(pkt)))
+ rc = PKT_DROP;
+ ctx->state = PKT_STATE_ROUTE_LOOKUP;
+ } else {
+ ctx = odp_packet_user_ptr(pkt);
+ }
+ } else {
+ ODPH_DBG("Unsupported Packet\n");
+ odp_event_free(ev);
+ continue;
+ }
+ } else if (ODP_EVENT_IPSEC_STATUS == odp_event_type(ev)) {
+ odp_ipsec_status_t status;
+
+ if (odp_unlikely(odp_ipsec_status(&status, ev) < 0)) {
+ ODPH_DBG("Error Event\n");
+ odp_event_free(ev);
+ continue;
+ }
+
+ printf("IPsec status %d result %d for SA %" PRIx64 "\n",
+ status.id, status.result,
+ odp_ipsec_sa_to_u64(status.sa));
+
+ odp_event_free(ev);
+ continue;
+ } else {
+ abort();
+ }
+
+ /*
+ * 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
+ */
+ while (rc == PKT_CONTINUE) {
+ switch (ctx->state) {
+ case PKT_STATE_INPUT_VERIFY:
+
+ rc = do_input_verify(pkt, ctx);
+ ctx->state = PKT_STATE_IPSEC_IN_CLASSIFY;
+ break;
+
+ case PKT_STATE_IPSEC_IN_CLASSIFY:
+
+ ctx->state = PKT_STATE_ROUTE_LOOKUP;
+ rc = do_ipsec_in_classify(&pkt);
+ break;
+
+ case PKT_STATE_ROUTE_LOOKUP:
+
+ rc = do_route_fwd_db(pkt, ctx);
+ ctx->state = PKT_STATE_IPSEC_OUT_CLASSIFY;
+ break;
+
+ case PKT_STATE_IPSEC_OUT_CLASSIFY:
+ ctx->state = PKT_STATE_TRANSMIT;
+ rc = do_ipsec_out_classify(&pkt, ctx);
+ break;
+
+ case PKT_STATE_TRANSMIT:
+
+ if (odp_pktout_send(ctx->pktout, &pkt, 1) < 1)
+ rc = PKT_DROP;
+ else
+ rc = PKT_DONE;
+ break;
+
+ default:
+ rc = PKT_DROP;
+ break;
+ }
+ }
+
+ /* Free context on drop or transmit */
+ if ((PKT_DROP == rc) || (PKT_DONE == rc))
+ free_pkt_ctx(ctx);
+
+ /* 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);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ODP ipsec example main function
+ */
+int
+main(int argc, char *argv[])
+{
+ odph_helper_options_t helper_options;
+ odph_thread_t thread_tbl[MAX_WORKERS];
+ odph_thread_common_param_t thr_common;
+ odph_thread_param_t thr_param;
+ int num_workers;
+ int i;
+ int stream_count;
+ odp_shm_t shm;
+ odp_cpumask_t cpumask;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ odp_ipsec_capability_t ipsec_capa;
+ odp_pool_param_t params;
+ odp_instance_t instance;
+ odp_init_t init_param;
+ odp_event_t ev;
+
+ /* create by default scheduled queues */
+ queue_create = odp_queue_create;
+ schedule_fn = odp_schedule_cb;
+
+ /* check for using poll queues */
+ if (getenv("ODP_IPSEC_USE_POLL_QUEUES")) {
+ queue_create = polled_odp_queue_create;
+ schedule_fn = polled_odp_schedule_cb;
+ }
+
+ /* Let helper collect its own arguments (e.g. --odph_proc) */
+ argc = odph_parse_options(argc, argv);
+ if (odph_options(&helper_options)) {
+ ODPH_ERR("Error: reading ODP helper options failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Signal handler has to be registered before global init in case ODP
+ * implementation creates internal threads/processes. */
+ signal(SIGINT, sig_handler);
+
+ odp_init_param_init(&init_param);
+ init_param.mem_model = helper_options.mem_model;
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(&instance, &init_param, NULL)) {
+ ODPH_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+ ODPH_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_ipsec_capability(&ipsec_capa)) {
+ ODPH_ERR("Error: IPsec capability request failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (queue_create == polled_odp_queue_create) {
+ if (!ipsec_capa.queue_type_plain) {
+ ODPH_ERR("Error: Plain type dest queue not supported.\n");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ if (!ipsec_capa.queue_type_sched) {
+ ODPH_ERR("Error: scheduled type dest queue not supported.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("shm_args", sizeof(global_data_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ global = odp_shm_addr(shm);
+
+ if (NULL == global) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(global, 0, sizeof(global_data_t));
+ global->shm = shm;
+ odp_atomic_init_u32(&global->exit_threads, 0);
+
+ /* Configure scheduler */
+ odp_schedule_config(NULL);
+
+ /* Must init our databases before parsing args */
+ ipsec_init_pre();
+ init_fwd_db();
+ init_stream_db();
+
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, &global->appl);
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), &global->appl);
+
+ num_workers = MAX_WORKERS;
+ if (global->appl.cpu_count && global->appl.cpu_count < MAX_WORKERS)
+ num_workers = global->appl.cpu_count;
+
+ /* Get default worker cpumask */
+ num_workers = odp_cpumask_default_worker(&cpumask, num_workers);
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ printf("num worker threads: %i\n", num_workers);
+ printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf("cpu mask: %s\n", cpumaskstr);
+
+ /* Create a barrier to synchronize thread startup */
+ odp_barrier_init(&global->sync_barrier, num_workers);
+
+ /* Create packet buffer pool */
+ odp_pool_param_init(&params);
+ params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.num = SHM_PKT_POOL_BUF_COUNT;
+ params.type = ODP_POOL_PACKET;
+
+ global->pkt_pool = odp_pool_create("packet_pool", &params);
+
+ if (ODP_POOL_INVALID == global->pkt_pool) {
+ ODPH_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create context buffer pool */
+ params.buf.size = SHM_CTX_POOL_BUF_SIZE;
+ params.buf.align = 0;
+ params.buf.num = SHM_CTX_POOL_BUF_COUNT;
+ params.type = ODP_POOL_BUFFER;
+
+ global->ctx_pool = odp_pool_create("ctx_pool", &params);
+
+ if (ODP_POOL_INVALID == global->ctx_pool) {
+ ODPH_ERR("Error: context pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Populate our IPsec cache */
+ printf("Using %s mode for IPsec API\n\n",
+ (ODP_IPSEC_OP_MODE_SYNC == global->appl.mode) ? "SYNC" :
+ (ODP_IPSEC_OP_MODE_ASYNC == global->appl.mode) ? "ASYNC" :
+ "INLINE");
+ ipsec_init_post(global->appl.mode);
+
+ /* Initialize interfaces (which resolves FWD DB entries */
+ for (i = 0; i < global->appl.if_count; i++)
+ initialize_intf(global->appl.if_names[i]);
+
+ /* If we have test streams build them before starting workers */
+ resolve_stream_db();
+ stream_count = create_stream_db_inputs();
+ if (stream_count < 0) {
+ ODPH_ERR("Error: creating input packets failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Create and init worker threads
+ */
+ odph_thread_common_param_init(&thr_common);
+ thr_common.instance = instance;
+ thr_common.cpumask = &cpumask;
+ thr_common.share_param = 1;
+
+ odph_thread_param_init(&thr_param);
+ thr_param.start = pktio_thread;
+ thr_param.arg = NULL;
+ thr_param.thr_type = ODP_THREAD_WORKER;
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+ odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers);
+
+ /* If there are streams attempt to verify them. Otherwise, run until
+ * SIGINT is received. */
+ if (stream_count) {
+ odp_bool_t done;
+
+ do {
+ done = verify_stream_db_outputs();
+ usleep(100000);
+ } while (!done);
+ printf("All received\n");
+ odp_atomic_store_u32(&global->exit_threads, 1);
+ }
+ odph_thread_join(thread_tbl, num_workers);
+
+ /* Stop and close used pktio devices */
+ for (i = 0; i < global->appl.if_count; i++) {
+ odp_pktio_t pktio = odp_pktio_lookup(global->appl.if_names[i]);
+
+ if (pktio == ODP_PKTIO_INVALID)
+ continue;
+
+ if (odp_pktio_stop(pktio) || odp_pktio_close(pktio)) {
+ ODPH_ERR("Error: failed to close pktio %s\n",
+ global->appl.if_names[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ free(global->appl.if_names);
+ free(global->appl.if_str);
+
+ if (destroy_ipsec_cache())
+ ODPH_ERR("Error: crypto session destroy failed\n");
+
+ /* Drop any remaining events. ipsec_sa_disable sends status event in
+ * async mode */
+ while ((ev = schedule_fn(NULL)) != ODP_EVENT_INVALID)
+ odp_event_free(ev);
+
+ if (odp_queue_destroy(global->completionq))
+ ODPH_ERR("Error: queue destroy failed\n");
+
+ if (odp_pool_destroy(global->pkt_pool))
+ ODPH_ERR("Error: pool destroy failed\n");
+ if (odp_pool_destroy(global->ctx_pool))
+ ODPH_ERR("Error: pool destroy failed\n");
+
+ shm = odp_shm_lookup("shm_ipsec_cache");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_ipsec_cache failed\n");
+ shm = odp_shm_lookup("shm_fwd_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_fwd_db failed\n");
+ shm = odp_shm_lookup("shm_sa_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_sa_db failed\n");
+ shm = odp_shm_lookup("shm_tun_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_tun_db failed\n");
+ shm = odp_shm_lookup("shm_sp_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free shm_sp_db failed\n");
+ shm = odp_shm_lookup("stream_db");
+ if (odp_shm_free(shm) != 0)
+ ODPH_ERR("Error: shm free stream_db failed\n");
+ if (odp_shm_free(global->shm)) {
+ ODPH_ERR("Error: shm free global data failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_local()) {
+ ODPH_ERR("Error: term local failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (odp_term_global(instance)) {
+ ODPH_ERR("Error: term global failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Exit\n\n");
+
+ return 0;
+}
+
+/**
+ * Parse and store the command line arguments
+ *
+ * @param argc argument count
+ * @param argv[] argument vector
+ * @param appl_args Store application arguments here
+ */
+static void parse_args(int argc, char *argv[], appl_args_t *appl_args)
+{
+ int opt;
+ int long_index;
+ char *token;
+ size_t len;
+ int rc = 0;
+ int i;
+
+ static const struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"interface", required_argument, NULL, 'i'}, /* return 'i' */
+ {"lookup", no_argument, NULL, 'l'},
+ {"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' */
+ {"tunnel", required_argument, NULL, 't'}, /* return 't' */
+ {"stream", required_argument, NULL, 's'}, /* return 's' */
+ {"help", no_argument, NULL, 'h'}, /* return 'h' */
+ {NULL, 0, NULL, 0}
+ };
+
+ static const char *shortopts = "+c:i:m:r:p:a:e:t:s:lh";
+
+ appl_args->cpu_count = 1; /* use one worker by default */
+
+ printf("\nParsing command line options\n");
+
+ while (!rc) {
+ opt = getopt_long(argc, argv, shortopts, longopts, &long_index);
+
+ if (-1 == opt)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->cpu_count = atoi(optarg);
+ break;
+ /* parse packet-io interface names */
+ case 'i':
+ len = strlen(optarg);
+ if (0 == len) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ appl_args->if_str = malloc(len);
+ if (appl_args->if_str == NULL) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* count the number of tokens separated by ',' */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL;
+ token = strtok(NULL, ","), i++)
+ ;
+
+ appl_args->if_count = i;
+
+ if (0 == appl_args->if_count) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* allocate storage for the if names */
+ appl_args->if_names =
+ calloc(appl_args->if_count, sizeof(char *));
+
+ /* store the if names (reset names string) */
+ strcpy(appl_args->if_str, optarg);
+ for (token = strtok(appl_args->if_str, ","), i = 0;
+ token != NULL; token = strtok(NULL, ","), i++) {
+ appl_args->if_names[i] = token;
+ }
+ break;
+
+ case 'l':
+ appl_args->lookup = true;
+ break;
+
+ case 'm':
+ appl_args->mode = atoi(optarg);
+ break;
+
+ case 'r':
+ rc = create_fwd_db_entry(optarg, appl_args->if_names,
+ appl_args->if_count);
+ break;
+
+ case 'p':
+ rc = create_sp_db_entry(optarg, FALSE);
+ break;
+
+ case 'a':
+ rc = create_sa_db_entry(optarg, FALSE);
+ break;
+
+ case 'e':
+ rc = create_sa_db_entry(optarg, TRUE);
+ break;
+
+ case 't':
+ rc = create_tun_db_entry(optarg);
+ break;
+
+ case 's':
+ rc = create_stream_db_entry(optarg);
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (rc) {
+ printf("ERROR: failed parsing -%c option\n", opt);
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (0 == appl_args->if_count) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ optind = 1; /* reset 'extern optind' from the getopt lib */
+}
+
+/**
+ * Print system and application info
+ */
+static void print_info(char *progname, appl_args_t *appl_args)
+{
+ int i;
+
+ odp_sys_info_print();
+
+ 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();
+ dump_tun_db();
+ printf("\n\n");
+ fflush(NULL);
+}
+
+/**
+ * Print 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"
+ " -m, --mode 0: SYNC\n"
+ " 1: ASYNC\n"
+ " Default: 1: ASYNC api mode\n"
+ "\n"
+ "Routing / IPSec OPTIONS:\n"
+ " -r, --route SubNet,Intf,NextHopMAC\n"
+ " -p, --policy SrcSubNet,DstSubNet,(in|out),(ah|esp)\n"
+ " -e, --esp SrcIP,DstIP,(3des|null),SPI,Key192\n"
+ " -a, --ah SrcIP,DstIP,(sha256|sha1|md5|null),SPI,Key(256|160|128)\n"
+ "\n"
+ " Where: NextHopMAC is raw hex/colon notation, i.e. 03:BA:44:9A:CE:02\n"
+ " IP is decimal/dot notation, i.e. 192.168.1.1\n"
+ " SubNet is decimal/dot/slash notation, i.e 192.168.0.0/16\n"
+ " SPI is raw hex, 32 bits\n"
+ " KeyXXX is raw hex, XXX bits long\n"
+ "\n"
+ " Examples:\n"
+ " -r 192.168.222.0/24,p8p1,08:00:27:F5:8B:DB\n"
+ " -p 192.168.111.0/24,192.168.222.0/24,out,esp\n"
+ " -e 192.168.111.2,192.168.222.2,3des,201,656c8523255ccc23a66c1917aa0cf30991fce83532a4b224\n"
+ " -a 192.168.111.2,192.168.222.2,md5,201,a731649644c5dee92cbd9c2e7e188ee6\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -c, --count <number> CPU count, 0=all available, default=1\n"
+ " -s, --stream SrcIP,DstIP,InIntf,OutIntf,Count,Length\n"
+ " -h, --help Display help and exit.\n"
+ " environment variables: ODP_IPSEC_USE_POLL_QUEUES\n"
+ " to enable use of poll queues instead of scheduled (default)\n"
+ " ODP_IPSEC_STREAM_VERIFY_MDEQ\n"
+ " to enable use of multiple dequeue for queue draining during\n"
+ " stream verification instead of single dequeue (default)\n"
+ "\n", NO_PATH(progname), NO_PATH(progname)
+ );
+}
+
+odp_bool_t sa_config_supported(const sa_db_entry_t *sa_entry, int *sa_flags);
+
+odp_bool_t sa_config_supported(const sa_db_entry_t *sa_entry ODP_UNUSED, int *sa_flags ODP_UNUSED)
+{
+ return true;
+}
diff --git a/example/ipsec_api/odp_ipsec_api_run_ah_in.sh b/example/ipsec_api/odp_ipsec_api_run_ah_in.sh
new file mode 100755
index 000000000..087c52aa3
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_ah_in.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Test input AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,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,sha1,300,27f6d123d7077b361662fc6e451f65d800000000 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_ah_out.sh b/example/ipsec_api/odp_ipsec_api_run_ah_out.sh
new file mode 100755
index 000000000..6acc8628a
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_ah_out.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Test output AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,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,sha1,200,a731649644c5dee92cbd9c2e7e188ee600000000 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_ah_tun_in.sh b/example/ipsec_api/odp_ipsec_api_run_ah_tun_in.sh
new file mode 100755
index 000000000..02fe3df3a
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_ah_tun_in.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Test input AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,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,sha1,300,27f6d123d7077b361662fc6e451f65d800000000 \
+ -t 192.168.222.2,192.168.111.2,10.0.222.2,10.0.111.2 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_ah_tun_out.sh b/example/ipsec_api/odp_ipsec_api_run_ah_tun_out.sh
new file mode 100755
index 000000000..4efb4c23d
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_ah_tun_out.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Test output AH
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,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,sha1,200,a731649644c5dee92cbd9c2e7e188ee600000000 \
+ -t 192.168.111.2,192.168.222.2,10.0.111.2,10.0.222.2 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_esp_in.sh b/example/ipsec_api/odp_ipsec_api_run_esp_in.sh
new file mode 100755
index 000000000..1c3414498
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_esp_in.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Test input ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,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,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_esp_out.sh b/example/ipsec_api/odp_ipsec_api_run_esp_out.sh
new file mode 100755
index 000000000..c9809e8ac
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_esp_out.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Test output ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,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,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_esp_tun_in.sh b/example/ipsec_api/odp_ipsec_api_run_esp_tun_in.sh
new file mode 100755
index 000000000..3b4f91e41
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_esp_tun_in.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Test input ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.111.2/32,$ROUTE_IF_INB,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 \
+ -t 192.168.222.2,192.168.111.2,10.0.222.2,10.0.111.2 \
+ -s 192.168.222.2,192.168.111.2,$OUT_IF,$IN_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_esp_tun_out.sh b/example/ipsec_api/odp_ipsec_api_run_esp_tun_out.sh
new file mode 100755
index 000000000..2bda78665
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_esp_tun_out.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Test output ESP
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+./odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,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 \
+ -t 192.168.111.2,192.168.222.2,10.0.111.2,10.0.222.2 \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_api_run_live.sh b/example/ipsec_api/odp_ipsec_api_run_live.sh
new file mode 100755
index 000000000..9febf2868
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_live.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# Live router test
+# - 2 interfaces interfaces
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=1
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+# this just turns off output buffering so that you still get periodic
+# output while piping to tee, as long as stdbuf is available.
+STDBUF="`which stdbuf 2>/dev/null` -o 0" || STDBUF=
+LOG=odp_ipsec_api_tmp.log
+PID=app_pid
+
+($STDBUF \
+ ./odp_ipsec_api -i $IF0,$IF1 \
+ -r 192.168.111.2/32,$IF0,$NEXT_HOP_MAC0 \
+ -r 192.168.222.2/32,$IF1,$NEXT_HOP_MAC1 \
+ -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 \
+ -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 \
+ -c 2 "$@" & echo $! > $PID) | tee $LOG &
+
+# Wait till application thread starts.
+APP_READY="Pktio thread \[..\] starts"
+
+until [ -f $LOG ]
+do
+ sleep 1
+done
+
+tail -f $LOG | grep -qm 1 "$APP_READY"
+
+validate_result
+ret=$?
+
+APP_PID=`cat $PID`
+
+kill -2 ${APP_PID}
+
+# Wait till the application exits
+tail --pid=$APP_PID -f /dev/null
+
+rm -f $PID
+rm -f $LOG
+
+cleanup_interfaces
+
+exit $ret
diff --git a/example/ipsec_api/odp_ipsec_api_run_router.sh b/example/ipsec_api/odp_ipsec_api_run_router.sh
new file mode 100755
index 000000000..198721ea0
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_router.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# Live router test
+# - 2 interfaces interfaces
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=2
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+# this just turns off output buffering so that you still get periodic
+# output while piping to tee, as long as stdbuf is available.
+STDBUF="`which stdbuf 2>/dev/null` -o 0" || STDBUF=
+LOG=odp_ipsec_api_tmp.log
+PID=app_pid
+
+($STDBUF \
+ ./odp_ipsec_api -i $IF0,$IF1 \
+ -r 192.168.111.2/32,$IF0,$NEXT_HOP_MAC0 \
+ -r 192.168.222.2/32,$IF1,$NEXT_HOP_MAC1 \
+ -c 1 "$@" & echo $! > $PID) | tee -a $LOG &
+
+# Wait till application thread starts.
+APP_READY="Pktio thread \[..\] starts"
+
+until [ -f $LOG ]
+do
+ sleep 1
+done
+
+tail -f $LOG | grep -qm 1 "$APP_READY"
+
+validate_result
+ret=$?
+
+APP_PID=`cat $PID`
+
+kill -2 ${APP_PID}
+
+# Wait till the application stops
+tail --pid=$APP_PID -f /dev/null
+
+rm -f $PID
+rm -f $LOG
+
+cleanup_interfaces
+
+exit $ret
diff --git a/example/ipsec_api/odp_ipsec_api_run_simple.sh b/example/ipsec_api/odp_ipsec_api_run_simple.sh
new file mode 100755
index 000000000..2921f978b
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_api_run_simple.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Simple router test
+# - 2 loop interfaces
+# - 10 packets
+# - Specify API mode on command line
+
+# IPSEC_APP_MODE: 0 - STANDALONE, 1 - LIVE, 2 - ROUTER
+IPSEC_APP_MODE=0
+
+if [ -f ./pktio_env ]; then
+ . ./pktio_env
+else
+ echo "BUG: unable to find pktio_env!"
+ echo "pktio_env has to be in current directory"
+ exit 1
+fi
+
+setup_interfaces
+
+if [ -z "$IPSEC_EXAMPLE_PATH" ]; then
+IPSEC_EXAMPLE_PATH=.
+fi
+
+${IPSEC_EXAMPLE_PATH}/odp_ipsec_api -i $IF_LIST \
+ -r 192.168.222.2/32,$ROUTE_IF_OUTB,08:00:27:F5:8B:DB \
+ -s 192.168.111.2,192.168.222.2,$IN_IF,$OUT_IF,10,100 \
+ -c 2 "$@"
+
+STATUS=$?
+
+if [ ${STATUS} -ne 0 ]; then
+ echo "Error: status ${STATUS}"
+ exit 1
+fi
+
+validate_result
+
+cleanup_interfaces
+
+exit 0
diff --git a/example/ipsec_api/odp_ipsec_cache.c b/example/ipsec_api/odp_ipsec_cache.c
new file mode 100644
index 000000000..827c9dce2
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_cache.c
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <odp_api.h>
+#include <odp/helper/odph_api.h>
+
+#include <odp_ipsec_cache.h>
+
+/** Global pointer to ipsec_cache db */
+ipsec_cache_t *ipsec_cache;
+
+void init_ipsec_cache(void)
+{
+ odp_shm_t shm;
+ int i;
+
+ shm = odp_shm_reserve("shm_ipsec_cache",
+ sizeof(ipsec_cache_t),
+ ODP_CACHE_LINE_SIZE,
+ 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ ODPH_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ipsec_cache = odp_shm_addr(shm);
+
+ if (ipsec_cache == NULL) {
+ ODPH_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ memset(ipsec_cache, 0, sizeof(*ipsec_cache));
+
+ for (i = 0; i < MAX_DB; i++)
+ ipsec_cache->array[i].ipsec_sa = ODP_IPSEC_SA_INVALID;
+}
+
+int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
+ sa_db_entry_t *auth_sa,
+ tun_db_entry_t *tun,
+ odp_bool_t in,
+ odp_queue_t completionq)
+{
+ odp_ipsec_sa_param_t param;
+ ipsec_cache_entry_t *entry;
+ odp_ipsec_sa_t ipsec_sa;
+ uint32_t tun_src_ip, tun_dst_ip;
+ sa_mode_t mode = IPSEC_SA_MODE_TRANSPORT;
+
+ /* Verify we have a good entry */
+ entry = &ipsec_cache->array[ipsec_cache->index];
+ if (MAX_DB <= ipsec_cache->index)
+ return -1;
+
+ odp_ipsec_sa_param_init(&param);
+ param.dir = in ? ODP_IPSEC_DIR_INBOUND : ODP_IPSEC_DIR_OUTBOUND;
+ param.inbound.lookup_mode = in ? ODP_IPSEC_LOOKUP_SPI :
+ ODP_IPSEC_LOOKUP_DISABLED;
+ param.proto = cipher_sa ? ODP_IPSEC_ESP : ODP_IPSEC_AH;
+
+ param.mode = tun ? ODP_IPSEC_MODE_TUNNEL : ODP_IPSEC_MODE_TRANSPORT;
+
+ param.dest_queue = completionq;
+
+ /* Cipher */
+ if (cipher_sa) {
+ param.crypto.cipher_alg = cipher_sa->alg.u.cipher;
+ param.crypto.cipher_key.data = cipher_sa->key.data;
+ param.crypto.cipher_key.length = cipher_sa->key.length;
+ param.spi = cipher_sa->spi;
+ } else {
+ param.crypto.cipher_alg = ODP_CIPHER_ALG_NULL;
+ }
+
+ /* Auth */
+ if (auth_sa) {
+ param.crypto.auth_alg = auth_sa->alg.u.auth;
+ param.crypto.auth_key.data = auth_sa->key.data;
+ param.crypto.auth_key.length = auth_sa->key.length;
+ param.spi = auth_sa->spi;
+ } else {
+ param.crypto.auth_alg = ODP_AUTH_ALG_NULL;
+ }
+
+ if (ODP_IPSEC_MODE_TUNNEL == param.mode) {
+ tun_src_ip = odp_cpu_to_be_32(tun->tun_src_ip);
+ tun_dst_ip = odp_cpu_to_be_32(tun->tun_dst_ip);
+ param.outbound.tunnel.type = ODP_IPSEC_TUNNEL_IPV4;
+ param.outbound.tunnel.ipv4.src_addr = &tun_src_ip;
+ param.outbound.tunnel.ipv4.dst_addr = &tun_dst_ip;
+ }
+
+ ipsec_sa = odp_ipsec_sa_create(&param);
+ if (ODP_IPSEC_SA_INVALID == ipsec_sa) {
+ ODPH_ERR("Error: SA creation failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* 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.u.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(ipsec_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.u.auth;
+ entry->ah.spi = auth_sa->spi;
+ entry->ah.icv_len = auth_sa->icv_len;
+ memcpy(&entry->ah.key, &auth_sa->key, sizeof(ipsec_key_t));
+ }
+
+ if (tun) {
+ entry->tun_src_ip = tun->tun_src_ip;
+ entry->tun_dst_ip = tun->tun_dst_ip;
+ mode = IPSEC_SA_MODE_TUNNEL;
+ }
+ entry->mode = mode;
+
+ /* 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;
+ }
+
+ entry->ipsec_sa = ipsec_sa;
+
+ return 0;
+}
+
+ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
+ uint32_t dst_ip,
+ odph_ahhdr_t *ah,
+ odph_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))
+ if ((entry->tun_src_ip != src_ip) ||
+ (entry->tun_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;
+}
+
+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;
+}
+
+int destroy_ipsec_cache(void)
+{
+ ipsec_cache_entry_t *entry;
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < MAX_DB; i++) {
+ entry = &ipsec_cache->array[i];
+ if (entry->ipsec_sa != ODP_IPSEC_SA_INVALID) {
+ ret += odp_ipsec_sa_disable(entry->ipsec_sa);
+ ret += odp_ipsec_sa_destroy(entry->ipsec_sa);
+ }
+ }
+
+ return ret;
+}
diff --git a/example/ipsec_api/odp_ipsec_cache.h b/example/ipsec_api/odp_ipsec_cache.h
new file mode 100644
index 000000000..5dd0c80b3
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_cache.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2014-2018 Linaro Limited
+ */
+
+/** @cond _ODP_HIDE_FROM_DOXYGEN_ */
+
+#ifndef ODP_IPSEC_CACHE_H_
+#define ODP_IPSEC_CACHE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+#include <odp/helper/ipsec.h>
+
+#include <odp_ipsec_misc.h>
+#include <odp_ipsec_sa_db.h>
+
+/**
+ * IPsec cache data base entry
+ */
+typedef struct ipsec_cache_entry_s {
+ struct ipsec_cache_entry_s *next; /**< Next entry on list */
+ uint32_t src_ip; /**< Source v4 address */
+ uint32_t dst_ip; /**< Destination v4 address */
+ sa_mode_t mode; /**< SA mode - transport/tun */
+ uint32_t tun_src_ip; /**< Tunnel src IPv4 addr */
+ uint32_t tun_dst_ip; /**< Tunnel dst IPv4 addr */
+ struct {
+ odp_cipher_alg_t alg; /**< Cipher algorithm */
+ uint32_t spi; /**< Cipher SPI */
+ uint32_t block_len; /**< Cipher block length */
+ uint32_t iv_len; /**< Cipher IV length */
+ ipsec_key_t key; /**< Cipher key */
+ } esp;
+ struct {
+ odp_auth_alg_t alg; /**< Auth algorithm */
+ uint32_t spi; /**< Auth SPI */
+ uint32_t icv_len; /**< Auth ICV length */
+ ipsec_key_t key; /**< Auth key */
+ } ah;
+
+ odp_ipsec_sa_t ipsec_sa;
+} ipsec_cache_entry_t;
+
+/**
+ * IPsec cache data base global structure
+ */
+typedef struct ipsec_cache_s {
+ uint32_t index; /**< Index of next available entry */
+ ipsec_cache_entry_t *in_list; /**< List of active input entries */
+ ipsec_cache_entry_t *out_list; /**< List of active output entries */
+ ipsec_cache_entry_t array[MAX_DB]; /**< Entry storage */
+} ipsec_cache_t;
+
+/** Global pointer to ipsec_cache db */
+extern ipsec_cache_t *ipsec_cache;
+
+/** Initialize IPsec cache */
+void init_ipsec_cache(void);
+
+/**
+ * Create an entry in the IPsec cache
+ *
+ * @param cipher_sa Cipher SA DB entry pointer
+ * @param auth_sa Auth SA DB entry pointer
+ * @param tun Tunnel DB entry pointer
+ * @param in Direction (input versus output)
+ * @param completionq Completion queue
+ *
+ * @return 0 if successful else -1
+ */
+int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa,
+ sa_db_entry_t *auth_sa,
+ tun_db_entry_t *tun,
+ odp_bool_t in,
+ odp_queue_t completionq);
+
+/**
+ * Find a matching IPsec cache entry for 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
+ */
+ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip,
+ uint32_t dst_ip,
+ odph_ahhdr_t *ah,
+ odph_esphdr_t *esp);
+
+/**
+ * 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
+ */
+ipsec_cache_entry_t *find_ipsec_cache_entry_out(uint32_t src_ip,
+ uint32_t dst_ip,
+ uint8_t proto);
+
+int destroy_ipsec_cache(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/example/ipsec_api/odp_ipsec_fwd_db.c b/example/ipsec_api/odp_ipsec_fwd_db.c
new file mode 120000
index 000000000..aba996aaf
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_fwd_db.c
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_fwd_db.c \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_fwd_db.h b/example/ipsec_api/odp_ipsec_fwd_db.h
new file mode 120000
index 000000000..f9a8ab957
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_fwd_db.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_fwd_db.h \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_misc.h b/example/ipsec_api/odp_ipsec_misc.h
new file mode 120000
index 000000000..7bb7de030
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_misc.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_misc.h \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_sa_db.c b/example/ipsec_api/odp_ipsec_sa_db.c
new file mode 120000
index 000000000..7ce1a5292
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_sa_db.c
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_sa_db.c \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_sa_db.h b/example/ipsec_api/odp_ipsec_sa_db.h
new file mode 120000
index 000000000..9b022c202
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_sa_db.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_sa_db.h \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_sp_db.c b/example/ipsec_api/odp_ipsec_sp_db.c
new file mode 120000
index 000000000..c7bd160fa
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_sp_db.c
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_sp_db.c \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_sp_db.h b/example/ipsec_api/odp_ipsec_sp_db.h
new file mode 120000
index 000000000..26369e9bb
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_sp_db.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_sp_db.h \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_stream.c b/example/ipsec_api/odp_ipsec_stream.c
new file mode 120000
index 000000000..57e25fe0e
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_stream.c
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_stream.c \ No newline at end of file
diff --git a/example/ipsec_api/odp_ipsec_stream.h b/example/ipsec_api/odp_ipsec_stream.h
new file mode 120000
index 000000000..6ad6700cc
--- /dev/null
+++ b/example/ipsec_api/odp_ipsec_stream.h
@@ -0,0 +1 @@
+../ipsec_crypto/odp_ipsec_stream.h \ No newline at end of file