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