@@ -1,4 +1,4 @@
-ACLOCAL_AMFLAGS=-I m4
+ACLOCAL_AMFLAGS=-I m4 -g
AUTOMAKE_OPTIONS = foreign
#@with_platform@ works alone in subdir but not as part of a path???
@@ -289,6 +289,7 @@ AC_CONFIG_FILES([Makefile
doc/Makefile
example/Makefile
example/classifier/Makefile
+ example/egress/Makefile
example/generator/Makefile
example/ipsec/Makefile
example/packet/Makefile
@@ -1 +1 @@
-SUBDIRS = classifier generator ipsec packet timer
+SUBDIRS = classifier egress generator ipsec packet timer
new file mode 100644
@@ -0,0 +1,10 @@
+include $(top_srcdir)/example/Makefile.inc
+
+bin_PROGRAMS = odp_egress
+odp_egress_LDFLAGS = $(AM_LDFLAGS) -static
+odp_egress_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example
+
+noinst_HEADERS = \
+ $(top_srcdir)/example/example_debug.h
+
+dist_odp_egress_SOURCES = odp_egress.c
new file mode 100644
@@ -0,0 +1,727 @@
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#define _POSIX_C_SOURCE 200112L
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <example_debug.h>
+
+#include <odp.h>
+#include <odp/helper/linux.h>
+#include <odp/helper/eth.h>
+#include <odp/helper/ip.h>
+#include <odp/helper/udp.h>
+#include <strings.h>
+#include <errno.h>
+
+/** @def MAX_WORKERS
+ * @brief Maximum number of worker threads
+ */
+#define MAX_WORKERS 32
+
+/** @def SHM_PKT_POOL_SIZE
+ * @brief Size of the shared memory block
+ */
+#define SHM_PKT_POOL_SIZE (2048 * 2048)
+
+/** @def SHM_PKT_POOL_BUF_SIZE
+ * @brief Buffer size of the packet pool buffer
+ */
+#define SHM_PKT_POOL_BUF_SIZE 1856
+
+/** @def MAX_VLAN_PRIO
+ * @brief Maximum vlan priority value
+ */
+#define MAX_VLAN_PRIO 8
+
+/** 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 {
+ int appl_mode; /**< application mode */
+ int cpu_count; /**< Number of CPUs to use */
+ uint32_t time; /**< Number of seconds to run */
+ char *if_name; /**< pointer to interface names */
+ odp_atomic_u64_t total_packets; /**< total received packets */
+ odp_atomic_u64_t drop_packets; /**< packets dropped */
+ odp_pktout_queue_t outq[MAX_VLAN_PRIO];
+ uint32_t cir_kbps[MAX_VLAN_PRIO];
+ uint32_t pir_kbps[MAX_VLAN_PRIO];
+ uint32_t cbs_bytes[MAX_VLAN_PRIO];
+ uint32_t pbs_bytes[MAX_VLAN_PRIO];
+ uint32_t rate_kbps;
+ uint32_t burst_byte;
+ uint16_t udp_port[MAX_VLAN_PRIO];
+} appl_args_t;
+
+/* helper funcs */
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len);
+static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len);
+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);
+static int parse_vlan_conf(appl_args_t *appl_args, char *argv[], char *optarg);
+
+static inline
+void statistics(appl_args_t *args)
+{
+ int i;
+ uint32_t timeout;
+ int infinite = 0;
+ struct timespec t;
+
+ t.tv_sec = 0;
+ t.tv_nsec = 10 * 1000 * 1000; /* 10 milli second */
+
+ printf("\n");
+ for (i = 0; i < 80; i++)
+ printf("-");
+ printf("\n");
+ /* print statistics */
+ printf("EGRESS EXAMPLE STATISTICS\n");
+ for (i = 0; i < 80; i++)
+ printf("-");
+ printf("\n");
+
+ printf("PKTOUT_QUEUE\t");
+ for (i = 0; i < MAX_VLAN_PRIO; i++)
+ printf("| Q%d\t", i);
+ printf("Port\t");
+ printf("\n");
+
+ for (i = 0; i < 80; i++)
+ printf("-");
+ printf("\n");
+ printf("UDP_PORT\t");
+ for (i = 0; i < MAX_VLAN_PRIO - 1; i++)
+ printf("| %d\t", args->udp_port[i]);
+ printf("| DEFAULT");
+ printf("\n");
+
+ printf("CIR\t\t");
+ for (i = 0; i < MAX_VLAN_PRIO; i++)
+ printf("| %d\t", args->cir_kbps[i]);
+ printf("| %d\t", args->rate_kbps);
+ printf("\n");
+
+ printf("CBS\t\t");
+ for (i = 0; i < MAX_VLAN_PRIO; i++)
+ printf("| %d\t", args->cbs_bytes[i]);
+ printf("| %d\t", args->burst_byte);
+ printf("\n");
+
+ printf("PIR\t\t");
+ for (i = 0; i < MAX_VLAN_PRIO; i++)
+ printf("| %d\t", args->pir_kbps[i]);
+ printf("| %d\t", args->rate_kbps);
+ printf("\n");
+
+ printf("PBS\t\t");
+ for (i = 0; i < MAX_VLAN_PRIO; i++)
+ printf("| %d\t", args->pbs_bytes[i]);
+ printf("| %d\t", args->burst_byte);
+ printf("\n");
+
+ timeout = args->time;
+ timeout *= 100; /* nanosleep is for 10 millisecond */
+
+ /* Incase if default value is given for timeout
+ run the loop infinitely */
+ if (timeout == 0)
+ infinite = 1;
+
+ for (; timeout > 0 || infinite; timeout--) {
+ printf("DEPTH\t\t");
+ /** read queue depth */
+ for (i = 0; i < MAX_VLAN_PRIO; i++)
+ printf("| %04d\t", odp_pktout_queue_packets(args->
+ outq[i]));
+
+ printf("TOTAL:\t%" PRIu64 "\t", odp_atomic_load_u64(&args->
+ total_packets));
+ printf("DROP:\t%" PRIu64 "\t", odp_atomic_load_u64(&args->
+ drop_packets));
+
+ nanosleep(&t, NULL);
+
+ printf("\r");
+ fflush(stdout);
+ }
+}
+
+/**
+ * Create a pktio handle, optionally associating a default input queue.
+ *
+ * @param dev Device name
+ * @param pool Associated Packet Pool
+ *
+ * @return The handle of the created pktio object.
+ * @retval ODP_PKTIO_INVALID if the create fails.
+ */
+static odp_pktio_t create_pktio(const char *dev, odp_pool_t pool)
+{
+ odp_pktio_t pktio;
+ odp_queue_t inq_def;
+ odp_queue_param_t qparam;
+ char inq_name[ODP_QUEUE_NAME_LEN];
+ int ret;
+
+ /* Open a packet IO instance */
+ pktio = odp_pktio_open(dev, pool);
+ if (pktio == ODP_PKTIO_INVALID) {
+ if (odp_errno() == EPERM)
+ EXAMPLE_ERR("Root level permission required\n");
+
+ EXAMPLE_ERR("pktio create failed for %s\n", dev);
+ exit(EXIT_FAILURE);
+ }
+
+ qparam.sched.prio = ODP_SCHED_PRIO_DEFAULT;
+ qparam.sched.sync = ODP_SCHED_SYNC_ATOMIC;
+ qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
+ snprintf(inq_name, sizeof(inq_name), "%" PRIu64 "-pktio_inq_def",
+ odp_pktio_to_u64(pktio));
+ inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+
+ inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam);
+ if (inq_def == ODP_QUEUE_INVALID) {
+ EXAMPLE_ERR("pktio inq create failed for %s\n", dev);
+ exit(EXIT_FAILURE);
+ }
+
+ ret = odp_pktio_inq_setdef(pktio, inq_def);
+ if (ret != 0) {
+ EXAMPLE_ERR("default input-Q setup for %s\n", dev);
+ exit(EXIT_FAILURE);
+ }
+
+ printf(" created pktio:%02" PRIu64
+ ", dev:%s, queue mode (ATOMIC queues)\n"
+ " \tdefault pktio%02" PRIu64
+ "-INPUT queue:%" PRIu64 "\n",
+ odp_pktio_to_u64(pktio), dev,
+ odp_pktio_to_u64(pktio), odp_queue_to_u64(inq_def));
+
+ return pktio;
+}
+
+/**
+ * Worker threads to receive the packet
+ *
+ */
+static void *pktio_receive_thread(void *arg)
+{
+ odp_packet_t pkt;
+ odp_event_t ev;
+ odp_queue_t queue;
+ appl_args_t *appl;
+ odph_udphdr_t *udp;
+ int ret;
+ int i;
+
+ appl = (appl_args_t *)arg;
+ /* Init this thread */
+ if (odp_init_local()) {
+ EXAMPLE_ERR("ODP thread local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Loop packets */
+ for (;;) {
+ /* Use schedule to get buf from any input queue */
+ ev = odp_schedule(&queue, ODP_SCHED_WAIT);
+
+ /* Loop back to receive packets incase of invalid event */
+ if (odp_unlikely(ev == ODP_EVENT_INVALID))
+ continue;
+
+ pkt = odp_packet_from_event(ev);
+
+ /* Total packets received */
+ odp_atomic_inc_u64(&appl->total_packets);
+
+ /* Drop packets with errors */
+ if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0))
+ continue;
+
+ /* Swap Eth MACs and possibly IP-addrs before sending back */
+ swap_pkt_addrs(&pkt, 1);
+
+ /* Check if it is a UDP packet and send into different queue
+ based on the udp source port number */
+ if (odp_packet_has_udp(pkt)) {
+ udp = (odph_udphdr_t *)odp_packet_l4_ptr(pkt, NULL);
+ uint16_t src_port;
+
+ src_port = odp_be_to_cpu_16(udp->src_port);
+ for (i = 0; i < MAX_VLAN_PRIO; i++) {
+ if (appl->udp_port[i] == src_port)
+ break;
+ }
+ if (i == MAX_VLAN_PRIO)
+ i--;
+ ret = odp_pktout_enq(appl->outq[i], pkt);
+ if (ret < 0) {
+ /* Packet drop count */
+ odp_atomic_inc_u64(&appl->drop_packets);
+ odp_packet_free(pkt);
+ }
+ } else {
+ /** enqueue into default priority queue */
+ ret = odp_pktout_enq(appl->outq[7], pkt);
+ if (ret < 0) {
+ /* Packet drop count */
+ odp_atomic_inc_u64(&appl->drop_packets);
+ odp_packet_free(pkt);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * ODP Egress example main function
+ */
+int main(int argc, char *argv[])
+{
+ odph_linux_pthread_t thread_tbl[MAX_WORKERS];
+ odp_pool_t pool;
+ int num_workers;
+ int i;
+ int cpu;
+ int priority;
+ odp_cpumask_t cpumask;
+ char cpumaskstr[ODP_CPUMASK_STR_SIZE];
+ odp_pool_param_t params;
+ odp_pktio_t pktio;
+ appl_args_t *args;
+ odp_shm_t shm;
+ odp_pktout_queue_param_t param;
+ int num;
+ odp_pktout_queue_t queue[MAX_PKTOUT_QUEUE];
+
+ /* Init ODP before calling anything else */
+ if (odp_init_global(NULL, NULL)) {
+ EXAMPLE_ERR("Error: ODP global init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Init this thread */
+ if (odp_init_local()) {
+ EXAMPLE_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Reserve memory for args from shared mem */
+ shm = odp_shm_reserve("cls_shm_args", sizeof(appl_args_t),
+ ODP_CACHE_LINE_SIZE, 0);
+
+ if (shm == ODP_SHM_INVALID) {
+ EXAMPLE_ERR("Error: shared mem reserve failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ args = odp_shm_addr(shm);
+
+ if (!args) {
+ EXAMPLE_ERR("Error: shared mem alloc failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(args, 0, sizeof(*args));
+ /* Parse and store the application arguments */
+ parse_args(argc, argv, args);
+
+ /* Print both system and application information */
+ print_info(NO_PATH(argv[0]), args);
+
+ /* Default to system CPU count unless user specified */
+ num_workers = MAX_WORKERS;
+ if (args->cpu_count)
+ num_workers = args->cpu_count;
+
+ /*
+ * By default CPU #0 runs Linux kernel background tasks.
+ * Start mapping thread from CPU #1
+ */
+ num_workers = odph_linux_cpumask_default(&cpumask, num_workers);
+ (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr));
+
+ printf("num worker threads: %i\n", num_workers);
+ printf("first CPU: %i\n", odp_cpumask_first(&cpumask));
+ printf("cpu mask: %s\n", cpumaskstr);
+
+ /* Create packet pool */
+ memset(¶ms, 0, sizeof(params));
+ params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.num = SHM_PKT_POOL_SIZE / SHM_PKT_POOL_BUF_SIZE;
+ params.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create("packet_pool", ODP_SHM_NULL, ¶ms);
+
+ if (pool == ODP_POOL_INVALID) {
+ EXAMPLE_ERR("Error: packet pool create failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ odp_atomic_init_u64(&args->total_packets, 0);
+ odp_atomic_init_u64(&args->drop_packets, 0);
+
+ /* create pktio per interface */
+ pktio = create_pktio(args->if_name, pool);
+
+ /* Create and init worker threads */
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+
+ /* Create packet output queue */
+
+ param.num = MAX_VLAN_PRIO;
+ for (i = 0; i < MAX_VLAN_PRIO; i++)
+ param.prio[i] = i;
+
+ /* todo: Since the current API version only supports only one hierarchy
+ level, this function creates packet output queues which are
+ attached at the root level.
+ Once the hierarchy APIs are finalized in the sprint the same will
+ be modified */
+ num = odp_pktout_queue_create(pktio, param, queue);
+
+ if (num < 0) {
+ EXAMPLE_ERR("Error: Packet output queue createion failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < num; i++) {
+ priority = odp_pktout_queue_priority(queue[i]);
+ args->outq[priority] = queue[i];
+ }
+
+ /** Rate limitting on the port level */
+ odp_pktio_rate_set(pktio, args->rate_kbps, args->burst_byte);
+
+ /** trTCM setting on each odp_pktout_queue_t */
+ for (i = 0; i < MAX_VLAN_PRIO; i++) {
+ odp_pktout_cir_set(args->outq[i], args->cir_kbps[i],
+ args->cbs_bytes[i]);
+ odp_pktout_pir_set(args->outq[i], args->pir_kbps[i],
+ args->pbs_bytes[i]);
+ }
+
+ cpu = odp_cpumask_first(&cpumask);
+ for (i = 0; i < num_workers; ++i) {
+ odp_cpumask_t thd_mask;
+ /*
+ * Calls odp_thread_create(cpu) for each thread
+ */
+ odp_cpumask_zero(&thd_mask);
+ odp_cpumask_set(&thd_mask, cpu);
+ odph_linux_pthread_create(&thread_tbl[i], &thd_mask,
+ pktio_receive_thread,
+ args);
+ cpu = odp_cpumask_next(&cpumask, cpu);
+ }
+
+ statistics(args);
+
+ free(args->if_name);
+ odp_shm_free(shm);
+ printf("Exit\n\n");
+
+ return 0;
+}
+
+/**
+ * Drop packets which input parsing marked as containing errors.
+ *
+ * Frees packets with error and modifies pkt_tbl[] to only contain packets with
+ * no detected errors.
+ *
+ * @param pkt_tbl Array of packet
+ * @param len Length of pkt_tbl[]
+ *
+ * @return Number of packets with no detected error
+ */
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
+{
+ odp_packet_t pkt;
+ unsigned pkt_cnt = len;
+ unsigned i, j;
+
+ for (i = 0, j = 0; i < len; ++i) {
+ pkt = pkt_tbl[i];
+
+ if (odp_unlikely(odp_packet_has_error(pkt))) {
+ odp_packet_free(pkt); /* Drop */
+ pkt_cnt--;
+ } else if (odp_unlikely(i != j++)) {
+ pkt_tbl[j - 1] = pkt;
+ }
+ }
+
+ return pkt_cnt;
+}
+
+/**
+ * Swap eth src<->dst and IP src<->dst addresses
+ *
+ * @param pkt_tbl Array of packets
+ * @param len Length of pkt_tbl[]
+ */
+static void swap_pkt_addrs(odp_packet_t pkt_tbl[], unsigned len)
+{
+ odp_packet_t pkt;
+ odph_ethhdr_t *eth;
+ odph_ethaddr_t tmp_addr;
+ odph_udphdr_t *udp;
+ odph_ipv4hdr_t *ip;
+ uint32be_t ip_tmp_addr; /* tmp ip addr */
+ uint16be_t udp_tmp;
+ unsigned i;
+
+ for (i = 0; i < len; ++i) {
+ pkt = pkt_tbl[i];
+ if (odp_packet_has_eth(pkt)) {
+ eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL);
+
+ tmp_addr = eth->dst;
+ eth->dst = eth->src;
+ eth->src = tmp_addr;
+
+ if (odp_packet_has_ipv4(pkt)) {
+ /* IPv4 */
+ ip = (odph_ipv4hdr_t *)
+ odp_packet_l3_ptr(pkt, NULL);
+
+ ip_tmp_addr = ip->src_addr;
+ ip->src_addr = ip->dst_addr;
+ ip->dst_addr = ip_tmp_addr;
+ }
+ if (odp_packet_has_udp(pkt)) {
+ udp = (odph_udphdr_t *)
+ odp_packet_l4_ptr(pkt, NULL);
+ udp_tmp = udp->src_port;
+ udp->src_port = udp->dst_port;
+ udp->dst_port = udp_tmp;
+ }
+ }
+ }
+}
+
+static int parse_vlan_conf(appl_args_t *appl_args, char *argv[], char *optarg)
+{
+ char *token;
+ size_t len;
+ char *vlan_str;
+ int prio;
+
+ len = strlen(optarg);
+ len++;
+ vlan_str = malloc(len);
+ strcpy(vlan_str, optarg);
+
+ /* vlan priority */
+ token = strtok(vlan_str, ":");
+ prio = atoi(token);
+ if (prio > 7) {
+ EXAMPLE_ERR("Invalid vlan priority");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* udp port */
+ token = strtok(NULL, ":");
+ appl_args->udp_port[prio] = atoi(token);
+
+ /* CIR kbps */
+ token = strtok(NULL, ":");
+ appl_args->cir_kbps[prio] = atoi(token);
+
+ /* Committed Burst size */
+ token = strtok(NULL, ":");
+ appl_args->cbs_bytes[prio] = atoi(token);
+
+ /* PIR kbps */
+ token = strtok(NULL, ":");
+ appl_args->pir_kbps[prio] = atoi(token);
+
+ token = strtok(NULL, ":");
+ appl_args->pbs_bytes[prio] = atoi(token);
+
+ free(vlan_str);
+ 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;
+ size_t len;
+ int interface = 0;
+ int vlan = 0;
+ int port = 0;
+
+ static struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"interface", required_argument, NULL, 'i'}, /* return 'i' */
+ {"vlan", required_argument, NULL, 'v'}, /* return 'v' */
+ {"time", required_argument, NULL, 't'}, /* return 't' */
+ {"port", required_argument, NULL, 'p'}, /* return 't' */
+ {"help", no_argument, NULL, 'h'}, /* return 'h' */
+ {NULL, 0, NULL, 0}
+ };
+
+ while (1) {
+ opt = getopt_long(argc, argv, "+c:t:i:v:p:t:h",
+ longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ appl_args->cpu_count = atoi(optarg);
+ break;
+ case 'v':
+ if (0 > parse_vlan_conf(appl_args, argv, optarg))
+ continue;
+ vlan = 1;
+ break;
+ case 'p':
+ len = strlen(optarg);
+ char *str = malloc(len);
+ char *token;
+
+ strcpy(str, optarg);
+ token = strtok(str, ":");
+ appl_args->rate_kbps = atoi(token);
+ token = strtok(NULL, ":");
+ appl_args->burst_byte = atoi(token);
+ free(str);
+ port = 1;
+ break;
+ case 't':
+ appl_args->time = atoi(optarg);
+ break;
+ case 'i':
+ len = strlen(optarg);
+ if (len == 0) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ len += 1; /* add room for '\0' */
+
+ appl_args->if_name = malloc(len);
+ if (!appl_args->if_name) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ strcpy(appl_args->if_name, optarg);
+ interface = 1;
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!interface || !vlan || !port) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ if (!appl_args->if_name) {
+ 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)
+{
+ 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_sys_cpu_model_str(),
+ odp_sys_cpu_hz(), odp_sys_cache_line_size(),
+ odp_cpu_count());
+
+ printf("Running ODP appl: \"%s\"\n"
+ "-----------------\n"
+ "Using IF:%s ",
+ progname, appl_args->if_name);
+ printf("\n\n");
+ fflush(NULL);
+}
+
+/**
+ * Prinf usage information
+ */
+static void usage(char *progname)
+{
+ printf("\n"
+ "OpenDataPlane Egress example.\n"
+ "Usage: %s OPTIONS\n"
+ " E.g. %s -i eth1 -m 0 -v \"0:1000:10:100:20:200\" \\\n"
+ "\t\t\t-v \"1:2000:10:100:20:200\" \\\n"
+ "\t\t\t-v \"2:3000:20:100:40:200\"\\\n"
+ "\t\t\t-v \"7:4000:20:100:40:200\"\\\n"
+ "\t\t\t-p \"100:2000\"\n"
+ "\n"
+ "For the above example configuration the following will be the packet distribution\n"
+ "\n"
+ "Mandatory OPTIONS:\n"
+ " -i, --interface Eth interface\n"
+ " -v, --vlan <priority>:<UDP port>:<CIR kbps>:<CSB in bytes>:<PIR in kbps>:<PBS in bytes>\n\n"
+ "\t<UDP port> UDP source port number\n"
+ "\t<vlan priority> \tvlan pcp value. Correct values are from 0 to 7\n"
+ "\t<CIR kbps> Committed Information Rate in kbps\n"
+ "\t<CBS in bytes> \tCommitted Burst Size in bytes\n"
+ "\t<PIR kbps> Peak Information Rate in kbps\n"
+ "\t<PBS in bytes> \tPeak Burst Size in bytes\n\n"
+ " -p, --port <rate in kbps>:<Burst size in bytes>\n"
+ "\t<rate in kbps> \tPort rate in kbps\n"
+ "\t<Burst size> Port burst size in bytes\n"
+ "\n"
+ "Optional OPTIONS\n"
+ " -c, --count <number> CPU count.\n"
+ " default: CPU core count.\n"
+ "\n"
+ " -t, --timeout !0: Time for which the classifier will be run in seconds\n"
+ " 0: Runs in infinite loop\n"
+ " default: Runs in infinite loop\n"
+ "\n"
+ " -h, --help Display help and exit.\n"
+ "\n", NO_PATH(progname), NO_PATH(progname)
+ );
+}