multi-thread and bi-directional forwarding.
support at most cpu-1 forward threads.
each thread handles traffic of one port,
and each port is handled by only one thread.

Signed-off-by: Xuelin Shi <[email protected]>
---
change history:
 v2: merge v1 patch set into one patch 

 example/Makefile.am          |   2 +-
 example/l3fwd/Makefile.am    |  11 +
 example/l3fwd/odp_l3fwd.c    | 473 +++++++++++++++++++++++++++++++++++++++++++
 example/l3fwd/odp_l3fwd_db.c | 414 +++++++++++++++++++++++++++++++++++++
 example/l3fwd/odp_l3fwd_db.h | 137 +++++++++++++
 example/m4/configure.m4      |   1 +
 6 files changed, 1037 insertions(+), 1 deletion(-)
 create mode 100644 example/l3fwd/Makefile.am
 create mode 100644 example/l3fwd/odp_l3fwd.c
 create mode 100644 example/l3fwd/odp_l3fwd_db.c
 create mode 100644 example/l3fwd/odp_l3fwd_db.h

diff --git a/example/Makefile.am b/example/Makefile.am
index 7f82c4d..67e4389 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -1 +1 @@
-SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt 
l2fwd_simple switch
+SUBDIRS = classifier generator ipsec packet time timer traffic_mgmt 
l2fwd_simple l3fwd switch
diff --git a/example/l3fwd/Makefile.am b/example/l3fwd/Makefile.am
new file mode 100644
index 0000000..0ba4527
--- /dev/null
+++ b/example/l3fwd/Makefile.am
@@ -0,0 +1,11 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_l3fwd$(EXEEXT)
+odp_l3fwd_LDFLAGS = $(AM_LDFLAGS) -static
+odp_l3fwd_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+
+noinst_HEADERS = \
+                 $(top_srcdir)/example/l3fwd/odp_l3fwd_db.h \
+                 $(top_srcdir)/example/example_debug.h
+
+dist_odp_l3fwd_SOURCES = odp_l3fwd.c odp_l3fwd_db.c
diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c
new file mode 100644
index 0000000..838f3e5
--- /dev/null
+++ b/example/l3fwd/odp_l3fwd.c
@@ -0,0 +1,473 @@
+/* Copyright (c) 2016, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <stdlib.h>
+#include <stdio.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/udp.h>
+#include <odp/helper/tcp.h>
+
+#include <getopt.h>
+
+#include "odp_l3fwd_db.h"
+
+#define POOL_NUM_PKT 8192
+#define POOL_SEG_LEN 1856
+#define MAX_PKT_BURST 32
+
+#define MAX_NB_WORKER  8
+#define MAX_NB_PKTIO   4
+#define MAX_NB_ROUTE    32
+
+/** 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))
+
+typedef struct {
+       char *if_names[MAX_NB_PKTIO];
+       int if_count;
+       char *route_str[MAX_NB_ROUTE];
+       int worker_count;
+} app_args_t;
+
+struct l3fwd_pktio_s {
+       odp_pktio_t pktio;
+       odp_pktin_queue_t ifin;
+       odp_pktout_queue_t ifout;
+};
+
+struct thread_arg_s {
+       uint32_t if_idx;
+};
+
+struct {
+       app_args_t              cmd_args;
+       struct l3fwd_pktio_s    l3fwd_pktios[MAX_NB_PKTIO];
+       odph_linux_pthread_t    l3fwd_workers[MAX_NB_WORKER];
+       struct thread_arg_s     worker_args[MAX_NB_WORKER];
+       uint32_t                nb_pktio;  /* effective pktios */
+       uint32_t                nb_worker; /* effective workers */
+} global;
+
+static void print_usage(char *progname);
+static void print_info(char *progname, app_args_t *args);
+static void parse_cmdline_args(int argc, char *argv[], app_args_t *args);
+
+static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
+                               odp_pktin_queue_t *pktin,
+                               odp_pktout_queue_t *pktout)
+{
+       odp_pktio_param_t pktio_param;
+       odp_pktin_queue_param_t in_queue_param;
+       odp_pktout_queue_param_t out_queue_param;
+       odp_pktio_t pktio;
+
+       odp_pktio_param_init(&pktio_param);
+
+       pktio = odp_pktio_open(name, pool, &pktio_param);
+       if (pktio == ODP_PKTIO_INVALID) {
+               printf("Failed to open %s\n", name);
+               exit(1);
+       }
+
+       odp_pktin_queue_param_init(&in_queue_param);
+       odp_pktout_queue_param_init(&out_queue_param);
+
+       in_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+
+       if (odp_pktin_queue_config(pktio, &in_queue_param)) {
+               printf("Failed to config input queue for %s\n", name);
+               exit(1);
+       }
+
+       out_queue_param.op_mode = ODP_PKTIO_OP_MT_UNSAFE;
+
+       if (odp_pktout_queue_config(pktio, &out_queue_param)) {
+               printf("Failed to config output queue for %s\n", name);
+               exit(1);
+       }
+
+       if (odp_pktin_queue(pktio, pktin, 1) != 1) {
+               printf("pktin queue query failed for %s\n", name);
+               exit(1);
+       }
+       if (odp_pktout_queue(pktio, pktout, 1) != 1) {
+               printf("pktout queue query failed for %s\n", name);
+               exit(1);
+       }
+       return pktio;
+}
+
+static void *run_worker(void *arg)
+{
+       odp_packet_t pkt_tbl[MAX_PKT_BURST];
+       odp_packet_t pkt_tbl_drop[MAX_PKT_BURST];
+       uint32_t pkts, i;
+       struct l3fwd_pktio_s *port;
+       char *if_name;
+
+       i = ((struct thread_arg_s *)arg)->if_idx;
+       port = &global.l3fwd_pktios[i];
+       if_name = global.cmd_args.if_names[i];
+       if (odp_pktio_start(port->pktio)) {
+               printf("unable to start pktio: %s\n", if_name);
+               exit(1);
+       }
+
+       printf("start pktio: %s\n", if_name);
+       for (;;) {
+               int need_to_drop = 0;
+
+               pkts = odp_pktin_recv(port->ifin, pkt_tbl, MAX_PKT_BURST);
+               if (odp_unlikely(pkts <= 0))
+                       continue;
+               for (i = 0; i < pkts; i++) {
+                       odp_packet_t pkt = pkt_tbl[i];
+                       odph_ethhdr_t *eth;
+                       odph_ipv4hdr_t *ip;
+                       odph_udphdr_t  *udp;
+                       uint32_t len;
+                       fwd_db_entry_t *entry;
+                       odp_pktout_queue_t outq;
+                       ipv4_tuple5_t key;
+
+                       if (odp_unlikely(!odp_packet_has_ipv4(pkt))) {
+                               printf("warning: packet has no ipv4 header\n");
+                               return NULL;
+                       }
+
+                       /*TODO: ipv6 need to be done */
+                       ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, &len);
+                       key.dst_ip = odp_be_to_cpu_32(ip->dst_addr);
+                       key.src_ip = odp_be_to_cpu_32(ip->src_addr);
+                       key.proto = ip->proto;
+                       if (odp_packet_has_udp(pkt) ||
+                           odp_packet_has_tcp(pkt)) {
+                               /* UDP or TCP*/
+                               void *ptr = odp_packet_l4_ptr(pkt, NULL);
+
+                               udp = (odph_udphdr_t *)ptr;
+                               key.src_port = odp_be_to_cpu_16(udp->src_port);
+                               key.dst_port = odp_be_to_cpu_16(udp->dst_port);
+                       }
+
+                       entry = find_fwd_db_entry(&key);
+                       if (!entry) {
+                               pkt_tbl_drop[need_to_drop] = pkt;
+                               need_to_drop++;
+                               continue;
+                       }
+
+                       if (odp_unlikely(!odp_packet_has_eth(pkt))) {
+                               printf("warning: packet has no eth header\n");
+                               return NULL;
+                       }
+
+                       ip->ttl--;
+                       ip->chksum = odph_ipv4_csum_update(pkt);
+                       eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+                       memcpy(eth->src.addr, entry->src_mac, ODPH_ETHADDR_LEN);
+                       memcpy(eth->dst.addr, entry->dst_mac, ODPH_ETHADDR_LEN);
+                       odp_pktout_queue(entry->pktio, &outq, 1);
+                       odp_pktout_send(outq, &pkt, 1);
+               }
+
+               odp_packet_free_multi(pkt_tbl_drop, need_to_drop);
+       }
+       return NULL;
+}
+
+int main(int argc, char **argv)
+{
+       odp_pool_t pool;
+       odp_pool_param_t params;
+       odp_cpumask_t cpumask;
+       odp_instance_t instance;
+       odph_linux_thr_params_t thr_params;
+       uint32_t cpu, i;
+       uint8_t mac[ODPH_ETHADDR_LEN];
+       app_args_t *args;
+
+       if (odp_init_global(&instance, NULL, NULL)) {
+               printf("Error: ODP global init failed.\n");
+               exit(1);
+       }
+
+       if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
+               printf("Error: ODP local init failed.\n");
+               exit(1);
+       }
+
+       /* Clear global argument */
+       memset(&global, 0, sizeof(global));
+
+       /* Parse cmdline arguments */
+       args = &global.cmd_args;
+       parse_cmdline_args(argc, argv, args);
+
+       /* Init l3fwd tale */
+       init_fwd_db();
+
+       /* Add route into table */
+       for (i = 0; i < MAX_NB_ROUTE; i++) {
+               if (args->route_str[i])
+                       create_fwd_db_entry(args->route_str[i]);
+       }
+
+       print_info(NO_PATH(argv[0]), args);
+
+       /* Create packet pool */
+       odp_pool_param_init(&params);
+       params.pkt.seg_len = POOL_SEG_LEN;
+       params.pkt.len     = POOL_SEG_LEN;
+       params.pkt.num     = POOL_NUM_PKT;
+       params.type        = ODP_POOL_PACKET;
+
+       pool = odp_pool_create("packet pool", &params);
+
+       if (pool == ODP_POOL_INVALID) {
+               printf("Error: packet pool create failed.\n");
+               exit(1);
+       }
+
+       global.nb_pktio = args->if_count;
+       for (i = 0; i < global.nb_pktio; i++) {
+               struct l3fwd_pktio_s *port;
+               char buf[16];
+
+               port = &global.l3fwd_pktios[i];
+               port->pktio = create_pktio(args->if_names[i], pool, &port->ifin,
+                                          &port->ifout);
+               odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN);
+               resolve_fwd_db(args->if_names[i], port->pktio, mac);
+
+               /* print mac string, could be used to config pktgen */
+               sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
+                       mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+               printf("create pktio %s, mac %s\n", args->if_names[i], buf);
+       }
+
+       /* at most cpu_count-1 threads, only 1 thread on 1 port.
+        * TODO: assign more threads for each port
+        */
+       if (args->worker_count == 0 || args->worker_count > args->if_count)
+               args->worker_count = args->if_count;
+
+       if (args->worker_count >= odp_cpu_count())
+               args->worker_count = odp_cpu_count() - 1;
+
+       global.nb_worker = args->worker_count;
+
+       memset(&thr_params, 0, sizeof(thr_params));
+       thr_params.start    = run_worker;
+       thr_params.thr_type = ODP_THREAD_WORKER;
+       thr_params.instance = instance;
+
+       odp_cpumask_default_worker(&cpumask, global.nb_worker);
+       cpu = odp_cpumask_first(&cpumask);
+       for (i = 0; i < global.nb_worker; i++) {
+               struct thread_arg_s *arg;
+               odp_cpumask_t thr_mask;
+
+               odp_cpumask_zero(&thr_mask);
+               odp_cpumask_set(&thr_mask, cpu);
+               arg = &global.worker_args[i];
+               arg->if_idx = i;
+               thr_params.arg      = arg;
+               odph_linux_pthread_create(&global.l3fwd_workers[i], &thr_mask,
+                                         &thr_params);
+               cpu = odp_cpumask_next(&cpumask, cpu);
+       }
+       odph_linux_pthread_join(&global.l3fwd_workers[0], global.nb_worker);
+
+       return 0;
+}
+
+static void print_usage(char *progname)
+{
+       printf("\n"
+              "ODP L3 forwarding application.\n"
+              "\n"
+              "Usage: %s OPTIONS\n"
+              "  E.g. %s -i eth0,eth1\n"
+              " In the above example,\n"
+              " eth0 will send pkts to eth1 and vice versa\n"
+              "\n"
+              "Mandatory OPTIONS:\n"
+              "  -i, --interface eth interfaces (comma-separated, no spaces)\n"
+              "  -r, --route SubNet:Intf[:NextHopMAC]\n"
+              "        NextHopMAC can be optional, in this case, zeroed mac\n"
+              "\n"
+              "Optional OPTIONS:\n"
+              "  -t, --thread number of threads to do forwarding\n"
+              "        optional, default as cpu count\n"
+              "  -h, --help   Display help and exit.\n\n"
+              "\n", NO_PATH(progname), NO_PATH(progname)
+           );
+}
+
+static void parse_cmdline_args(int argc, char *argv[], app_args_t *args)
+{
+       int opt;
+       int long_index;
+       char *token, *local;
+       size_t len, route_index = 0;
+       int i, mem_failure = 0;
+
+       static struct option longopts[] = {
+               {"interface", required_argument, NULL, 'i'},    /* return 'i' */
+               {"route", required_argument, NULL, 'r'},        /* return 'r' */
+               {"thread", optional_argument, NULL, 't'},       /* return 't'*/
+               {"help", no_argument, NULL, 'h'},               /* return 'h' */
+               {NULL, 0, NULL, 0}
+       };
+
+       while (1) {
+               opt = getopt_long(argc, argv, "+t:i:r:h",
+                                 longopts, &long_index);
+
+               if (opt == -1)
+                       break;  /* No more options */
+
+               switch (opt) {
+               /* parse number of worker threads to be run*/
+               case 't':
+                       i = odp_cpu_count();
+                       args->worker_count = atoi(optarg);
+                       if (args->worker_count > i) {
+                               printf("Too many threads,"
+                                      "truncate to cpu count: %d\n", i);
+                               args->worker_count = i;
+                       }
+
+                       break;
+               /* parse packet-io interface names */
+               case 'i':
+                       len = strlen(optarg);
+                       if (len == 0) {
+                               print_usage(argv[0]);
+                               exit(EXIT_FAILURE);
+                       }
+                       len += 1;       /* add room for '\0' */
+
+                       local = malloc(len);
+                       if (!local) {
+                               print_usage(argv[0]);
+                               exit(EXIT_FAILURE);
+                       }
+
+                       /* count the number of tokens separated by ',' */
+                       strcpy(local, optarg);
+                       for (token = strtok(local, ","), i = 0;
+                            token != NULL;
+                            token = strtok(NULL, ","), i++)
+                               ;
+
+                       if (i == 0) {
+                               print_usage(argv[0]);
+                               exit(EXIT_FAILURE);
+                       } else if ((i & 1) != 0) {
+                               printf("even number of ports expected, "
+                                      "got %u.\n", i);
+                               exit(EXIT_FAILURE);
+                       } else if (i > MAX_NB_PKTIO) {
+                               printf("too many ports specified, "
+                                      "truncated to max %d", MAX_NB_PKTIO);
+                       }
+                       args->if_count = i;
+
+                       /* store the if names (reset names string) */
+                       strcpy(local, optarg);
+                       for (token = strtok(local, ","), i = 0;
+                            token != NULL; token = strtok(NULL, ","), i++) {
+                               args->if_names[i] = token;
+                       }
+                       break;
+
+               /*Configure Route in forwarding database*/
+               case 'r':
+                       if (route_index >= MAX_NB_ROUTE) {
+                               printf("No more routes can be added\n");
+                               break;
+                       }
+                       local = calloc(1, strlen(optarg) + 1);
+                       if (!local) {
+                               mem_failure = 1;
+                               break;
+                       }
+                       memcpy(local, optarg, strlen(optarg));
+                       local[strlen(optarg)] = '\0';
+                       args->route_str[route_index++] = local;
+                       break;
+
+               case 'h':
+                       print_usage(argv[0]);
+                       exit(EXIT_SUCCESS);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       /* checking arguments */
+       if (args->if_count == 0) {
+               printf("\nNo option -i specified.\n");
+               goto out;
+       }
+
+       if (args->route_str[0]== NULL) {
+               printf("\nNo option -r specified.\n");
+               goto out;
+       }
+
+       if (mem_failure == 1) {
+               printf("\nAllocate memory failure.\n");
+               goto out;
+       }
+       optind = 1;             /* reset 'extern optind' from the getopt lib */
+       return;
+
+out:
+       print_usage(argv[0]);
+       exit(EXIT_FAILURE);
+}
+
+static void print_info(char *progname, app_args_t *args)
+{
+       int i;
+
+       printf("\n"
+              "ODP system info\n"
+              "---------------\n"
+              "ODP API version: %s\n"
+              "ODP impl name:   %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_version_impl_name(),
+              odp_cpu_model_str(), odp_cpu_hz_max(),
+              odp_sys_cache_line_size(), odp_cpu_count());
+
+       printf("Running ODP appl: \"%s\"\n"
+              "-----------------\n"
+              "IF-count:        %i\n"
+              "Using IFs:      ",
+              progname, args->if_count);
+
+       for (i = 0; i < args->if_count; ++i)
+               printf(" %s", args->if_names[i]);
+
+       printf("\n\n");
+       fflush(NULL);
+}
diff --git a/example/l3fwd/odp_l3fwd_db.c b/example/l3fwd/odp_l3fwd_db.c
new file mode 100644
index 0000000..c1e1b5b
--- /dev/null
+++ b/example/l3fwd/odp_l3fwd_db.c
@@ -0,0 +1,414 @@
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+/* enable strtok */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <example_debug.h>
+
+#include <odp_api.h>
+
+#include <odp_l3fwd_db.h>
+
+/**
+ * Compute hash value from a flow
+ */
+static inline
+uint64_t odp_l3fwd_calc_hash(ipv4_tuple5_t *key)
+{
+       uint64_t l4_ports = 0;
+       uint32_t dst_ip, src_ip;
+
+       src_ip = key->src_ip;
+       dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;
+       ODP_BJ3_MIX(src_ip, dst_ip, l4_ports);
+
+       return l4_ports;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * Flow cache table entry
+ */
+typedef struct flow_entry_s {
+       ipv4_tuple5_t key;              /**< match key */
+       struct flow_entry_s *next;      /**< next entry on the list */
+       fwd_db_entry_t *fwd_entry;      /**< entry info in db */
+} flow_entry_t;
+
+/**
+ * Flow cache table bucket
+ */
+typedef struct flow_bucket_s {
+       odp_spinlock_t          lock;   /**< Bucket lock*/
+       flow_entry_t            *next;  /**< Pointer to first flow entry in 
bucket*/
+} flow_bucket_t;
+
+/**
+ * Flow hash table, fast lookup cache
+ */
+typedef struct flow_table_s {
+       flow_bucket_t *bucket;
+       uint32_t count;
+} flow_table_t;
+
+static flow_table_t fwd_lookup_cache;
+
+static inline
+void init_fwd_cache(void)
+{
+       odp_shm_t               hash_shm;
+       flow_bucket_t           *bucket;
+       uint32_t                bucket_count;
+       uint32_t                i;
+
+       bucket_count = ODP_DEF_BUCKET_COUNT;
+
+       /*Reserve memory for Routing hash table*/
+       hash_shm = odp_shm_reserve("route_table",
+                                  sizeof(flow_bucket_t) * bucket_count,
+                                  ODP_CACHE_LINE_SIZE, 0);
+
+       bucket = odp_shm_addr(hash_shm);
+       if (!bucket) {
+               EXAMPLE_ERR("Error: shared mem alloc failed.\n");
+               exit(-1);
+       }
+
+       fwd_lookup_cache.bucket = bucket;
+       fwd_lookup_cache.count = bucket_count;
+
+       /*Inialize Locks*/
+       for (i = 0; i < bucket_count; i++) {
+               bucket = &fwd_lookup_cache.bucket[i];
+               odp_spinlock_init(&bucket->lock);
+               bucket->next = NULL;
+       }
+}
+
+static inline
+int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow)
+{
+       if (key->src_ip == flow->key.src_ip &&
+           key->dst_ip == flow->key.dst_ip &&
+           key->src_port == flow->key.src_port &&
+           key->dst_port == flow->key.dst_port &&
+           key->proto == flow->key.proto)
+               return 1;
+
+       return 0;
+}
+
+static inline
+flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t *bucket)
+{
+       flow_entry_t *rst;
+
+       for (rst = bucket->next; rst != NULL; rst = rst->next) {
+               if (match_key_flow(key, rst))
+                       break;
+       }
+
+       return rst;
+}
+
+static inline
+flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,
+                              flow_bucket_t *bucket,
+                              fwd_db_entry_t *entry)
+{
+       flow_entry_t *flow;
+
+       flow = lookup_fwd_cache(key, bucket);
+       if (flow)
+               return flow;
+
+       flow = malloc(sizeof(flow_entry_t));
+       flow->key = *key;
+       flow->fwd_entry = entry;
+
+       odp_spinlock_lock(&bucket->lock);
+       if (!bucket->next) {
+               bucket->next = flow;
+       } else {
+               flow->next = bucket->next;
+               bucket->next = flow;
+       }
+       odp_spinlock_unlock(&bucket->lock);
+
+       return flow;
+}
+
+/** Global pointer to fwd db */
+fwd_db_t *fwd_db;
+
+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_ERR("Error: shared mem alloc failed.\n");
+               exit(EXIT_FAILURE);
+       }
+       memset(fwd_db, 0, sizeof(*fwd_db));
+
+       init_fwd_cache();
+}
+
+int create_fwd_db_entry(char *input)
+{
+       int pos = 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;
+                       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++;
+       }
+
+       /* Reset pktio to invalid */
+       entry->pktio = ODP_PKTIO_INVALID;
+
+       /* Add route to the list */
+       fwd_db->index++;
+       entry->next = fwd_db->list;
+       fwd_db->list = entry;
+
+       free(local);
+       return 0;
+}
+
+void resolve_fwd_db(char *intf, odp_pktio_t pktio, uint8_t *mac)
+{
+       fwd_db_entry_t *entry;
+
+       /* Walk the list and attempt to set output and MAC */
+       for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
+               if (strcmp(intf, entry->oif))
+                       continue;
+
+               entry->pktio = pktio;
+               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(ipv4_tuple5_t *key)
+{
+       fwd_db_entry_t *entry;
+       flow_entry_t *flow;
+       flow_bucket_t *bucket;
+       uint64_t hash;
+
+       /* first find in cache */
+       hash = odp_l3fwd_calc_hash(key);
+       hash &= fwd_lookup_cache.count - 1;
+       bucket = &fwd_lookup_cache.bucket[hash];
+       flow = lookup_fwd_cache(key, bucket);
+       if (flow)
+               return flow->fwd_entry;
+
+       for (entry = fwd_db->list; NULL != entry; entry = entry->next)
+               if (entry->subnet.addr == (key->dst_ip & entry->subnet.mask))
+                       break;
+
+       return entry;
+}
diff --git a/example/l3fwd/odp_l3fwd_db.h b/example/l3fwd/odp_l3fwd_db.h
new file mode 100644
index 0000000..c632f5b
--- /dev/null
+++ b/example/l3fwd/odp_l3fwd_db.h
@@ -0,0 +1,137 @@
+/* Copyright (c) 2014, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#ifndef ODP_L3FWD_DB_H_
+#define ODP_L3FWD_DB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_api.h>
+#include <odp/helper/eth.h>
+
+#define OIF_LEN 32
+#define MAX_DB  32
+#define MAX_STRING  32
+
+/**
+ * Default number of flows
+ */
+#define ODP_DEF_FLOW_COUNT             100000
+
+/**
+ * Default Hash bucket number
+ */
+#define ODP_DEF_BUCKET_COUNT   (ODP_DEF_FLOW_COUNT / 8)
+
+/**
+ * Hash calculation utility
+ */
+#define JHASH_GOLDEN_RATIO     0x9e3779b9
+#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
+#define ODP_BJ3_MIX(a, b, c) \
+{ \
+       a -= c; a ^= rot(c, 4); c += b; \
+       b -= a; b ^= rot(a, 6); a += c; \
+       c -= b; c ^= rot(b, 8); b += a; \
+       a -= c; a ^= rot(c, 16); c += b; \
+       b -= a; b ^= rot(a, 19); a += c; \
+       c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/**
+ * 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;
+
+/**
+ * TCP/UDP flow
+ */
+typedef struct ipv4_tuple5_s {
+       uint32_t src_ip;
+       uint32_t dst_ip;
+       uint16_t src_port;
+       uint16_t dst_port;
+       uint8_t  proto;
+} ipv4_tuple5_t;
+
+/**
+ * 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_pktio_t             pktio;        /**< Output transmit port */
+       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
+ */
+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;
+
+/** 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
+ *
+ * @return 0 if successful else -1
+ */
+int create_fwd_db_entry(char *input);
+
+/**
+ * Scan FWD DB entries and resolve output queue and source MAC address
+ *
+ * @param intf   Interface name string
+ * @param pktio   Output port for packet transmit
+ * @param mac    MAC address of this interface
+ */
+void resolve_fwd_db(char *intf, odp_pktio_t pktio, 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 key  ipv4 tuple
+ *
+ * @return pointer to forwarding DB entry else NULL
+ */
+fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/example/m4/configure.m4 b/example/m4/configure.m4
index 9731d81..7868574 100644
--- a/example/m4/configure.m4
+++ b/example/m4/configure.m4
@@ -19,4 +19,5 @@ AC_CONFIG_FILES([example/classifier/Makefile
                 example/timer/Makefile
                 example/traffic_mgmt/Makefile
                 example/l2fwd_simple/Makefile
+                example/l3fwd/Makefile
                 example/switch/Makefile])
-- 
2.1.0.27.g96db324

_______________________________________________
lng-odp mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to