Hello everyone, Do you have any comments on this patch? Any objections?
I may send out subsequent patches to add more features in. Do you prefer a final big patch or a patch set with the enhancement history about adding a new app? Welcome any feedback. Thanks, Forrest > -----Original Message----- > From: [email protected] [mailto:[email protected]] > Sent: Monday, May 09, 2016 17:40 > To: [email protected] > Cc: [email protected]; Xuelin Shi <[email protected]> > Subject: [lng-odp][PATCH 1/2] example: introducing l3fwd > > From: Xuelin Shi <[email protected]> > > It is a simple l3fwd, only single thread, single port works. > Would be enhanced in later versions. > > Signed-off-by: Xuelin Shi <[email protected]> > --- > example/Makefile.am | 2 +- > example/l3fwd/Makefile.am | 11 ++ > example/l3fwd/odp_l3fwd.c | 218 +++++++++++++++++++++++++++++++ > example/l3fwd/odp_l3fwd_db.c | 299 > +++++++++++++++++++++++++++++++++++++++++++ > example/l3fwd/odp_l3fwd_db.h | 137 ++++++++++++++++++++ > example/m4/configure.m4 | 1 + > 6 files changed, 667 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..704d33e > --- /dev/null > +++ b/example/l3fwd/odp_l3fwd.c > @@ -0,0 +1,218 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#include <stdlib.h> > +#include <stdio.h> > + > +#include <odp_api.h> > +#include <odp/helper/linux.h> > +#include <odp/helper/eth.h> > +#include <odp/helper/ip.h> > + > +#include "odp_l3fwd_db.h" > + > +#define POOL_NUM_PKT 8192 > +#define POOL_SEG_LEN 1856 > +#define MAX_PKT_BURST 32 > + > +static const char * const route_str[] = { > + "1.1.1.0/24:fm1-mac1:00.e0.0c.00.85.00", > + "2.1.1.0/24:fm1-mac2:00.e0.0c.00.85.01", > +}; > + > +struct { > + odp_pktio_t if0, if1; > + odp_pktin_queue_t if0in, if1in; > + odp_pktout_queue_t if0out, if1out; > + odph_ethaddr_t src, dst; > +} global; > + > +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_UNUSED) { > + odp_packet_t pkt_tbl[MAX_PKT_BURST]; > + odp_packet_t pkt_tbl_drop[MAX_PKT_BURST]; > + int pkts, i; > + > + if (odp_pktio_start(global.if0)) { > + printf("unable to start input interface\n"); > + exit(1); > + } > + printf("started input interface\n"); > + if (odp_pktio_start(global.if1)) { > + printf("unable to start output interface\n"); > + exit(1); > + } > + printf("started output interface\n"); > + printf("started all\n"); > + > + for (;;) { > + int need_to_drop = 0; > + > + memset(pkt_tbl, 0, sizeof(pkt_tbl_drop)); > + > + pkts = odp_pktin_recv(global.if0in, 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; > + uint32_t len; > + uint32_t dst_ip; > + fwd_db_entry_t *entry; > + odp_pktout_queue_t outq; > + > + if (odp_unlikely(!odp_packet_has_l3(pkt))) { > + printf("warning: packet has no ip header\n"); > + return NULL; > + } > + > + /*TODO: ipv6 need to be done */ > + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, &len); > + dst_ip = odp_be_to_cpu_32(ip->dst_addr); > + entry = find_fwd_db_entry(dst_ip); > + 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; > + } > + > + 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; > + odph_linux_pthread_t thd; > + odp_instance_t instance; > + odph_linux_thr_params_t thr_params; > + size_t i; > + uint8_t mac[ODPH_ETHADDR_LEN]; > + > + if (argc != 3) { > + printf("Usage: odp_l3fwd eth0 eth1\n"); > + printf("Where eth0 and eth1 are the used interfaces" > + " (must have 2 of them)\n"); > + exit(1); > + } > + > + 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); > + } > + > + /* Init l3fwd tale */ > + init_fwd_db(); > + > + /* Add route into table */ > + for (i = 0; i < sizeof(route_str) / sizeof(char *); i++) { > + char buf[128]; > + > + snprintf(buf, 128, "%s", route_str[i]); > + create_fwd_db_entry(buf); > + } > + > + /* Create packet pool */ > + odp_pool_param_init(¶ms); > + 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", ¶ms); > + > + if (pool == ODP_POOL_INVALID) { > + printf("Error: packet pool create failed.\n"); > + exit(1); > + } > + > + global.if0 = create_pktio(argv[1], pool, &global.if0in, &global.if0out); > + global.if1 = create_pktio(argv[2], pool, &global.if1in, > +&global.if1out); > + > + /* resolve route table */ > + odp_pktio_mac_addr(global.if0, mac, ODPH_ETHADDR_LEN); > + resolve_fwd_db(argv[1], global.if0, mac); > + odp_pktio_mac_addr(global.if1, mac, ODPH_ETHADDR_LEN); > + resolve_fwd_db(argv[2], global.if1, mac); > + > + odp_cpumask_default_worker(&cpumask, 1); > + > + memset(&thr_params, 0, sizeof(thr_params)); > + thr_params.start = run_worker; > + thr_params.arg = NULL; > + thr_params.thr_type = ODP_THREAD_WORKER; > + thr_params.instance = instance; > + > + odph_linux_pthread_create(&thd, &cpumask, &thr_params); > + odph_linux_pthread_join(&thd, 1); > + return 0; > +} > diff --git a/example/l3fwd/odp_l3fwd_db.c b/example/l3fwd/odp_l3fwd_db.c > new file mode 100644 index 0000000..2140cca > --- /dev/null > +++ b/example/l3fwd/odp_l3fwd_db.c > @@ -0,0 +1,299 @@ > +/* 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 *flow) { > + uint64_t l4_ports = 0; > + ipv4_tuple5_t key; > + > + key = *flow; > + > + key.dst_ip += JHASH_GOLDEN_RATIO; > + ODP_BJ3_MIX(key.src_ip, key.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; > +} > + > +/** 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)); > +} > + > +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++; > + } > + > + /* 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; > + } > + > + /* 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(uint32_t dst_ip) { > + fwd_db_entry_t *entry; > + > + for (entry = fwd_db->list; NULL != entry; entry = entry->next) > + if (entry->subnet.addr == (dst_ip & entry->subnet.mask)) > + break; > + > + return entry; > +} > diff --git a/example/l3fwd/odp_l3fwd_db.h > b/example/l3fwd/odp_l3fwd_db.h new file mode 100644 index > 0000000..bd27b2a > --- /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_MAX_FLOW_COUNT 100000 > + > +/** > + * Default Hash bucket number > + */ > +#define ODP_MAX_BUCKET_COUNT (ODP_MAX_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 hash 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; > + > +/** 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 dst_ip Destination IPv4 address > + * > + * @return pointer to forwarding DB entry else NULL */ fwd_db_entry_t > +*find_fwd_db_entry(uint32_t dst_ip); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/example/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
