diff --git a/examples/bond_rss/Makefile b/examples/bond_rss/Makefile
new file mode 100644
index 0000000..db457a3
--- /dev/null
+++ b/examples/bond_rss/Makefile
@@ -0,0 +1,59 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = bondrss
+
+# all source are stored in SRCS-y
+SRCS-y := bondrss.c
+SRCS-y += ui.c
+SRCS-y += config.c
+
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+LDLIBS += -lncurses
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/bond_rss/bondrss.c b/examples/bond_rss/bondrss.c
new file mode 100644
index 0000000..2dc9979
--- /dev/null
+++ b/examples/bond_rss/bondrss.c
@@ -0,0 +1,293 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bondrss.h"
+
+#define RSS_HASH_KEY_LENGTH 40
+
+static const struct rte_eth_conf port_conf_default = {
+		.rxmode = {
+				.mq_mode		= ETH_MQ_RX_RSS,
+				.max_rx_pkt_len	= ETHER_MAX_LEN,
+				.split_hdr_size	= 0,
+				.header_split	= 0, /**< Header Split disabled. */
+				.hw_ip_checksum	= 0, /**< IP checksum offload disabled. */
+				.hw_vlan_filter	= 1, /**< VLAN filtering enabled. */
+				.hw_vlan_strip	= 1, /**< VLAN strip enabled. */
+				.hw_vlan_extend	= 0, /**< Extended VLAN disabled. */
+				.jumbo_frame	= 0, /**< Jumbo Frame Support disabled. */
+				.hw_strip_crc	= 0, /**< CRC stripping by hardware disabled. */
+				.enable_scatter	= 0, /**< scatter rx disabled */
+		},
+		.rx_adv_conf = {
+				.rss_conf = {
+						.rss_key	= NULL,
+						.rss_hf		= ETH_RSS_IPV4,
+				},
+		},
+};
+
+static const struct rte_eth_conf port_conf_bonding = {
+		.rxmode = {
+				.mq_mode		= ETH_MQ_RX_RSS,
+				.max_rx_pkt_len	= ETHER_MAX_LEN,
+				.split_hdr_size	= 0,
+				.header_split	= 0, /**< Header Split disabled. */
+				.hw_ip_checksum	= 0, /**< IP checksum offload disabled. */
+				.hw_vlan_filter	= 1, /**< VLAN filtering enabled. */
+				.hw_vlan_strip	= 1, /**< VLAN strip enabled. */
+				.hw_vlan_extend	= 0, /**< Extended VLAN disabled. */
+				.jumbo_frame	= 0, /**< Jumbo Frame Support disabled. */
+				.hw_strip_crc	= 0, /**< CRC stripping by hardware disabled. */
+				.enable_scatter	= 0, /**< scatter rx disabled */
+		},
+		.rx_adv_conf = {
+				.rss_conf = {
+						.rss_key	= NULL,
+						.rss_hf		= ETH_RSS_IPV6,
+				},
+		},
+};
+
+/*
+ * Configurable number of RX/TX queues.
+ */
+int nb_rxq = 4;			/**< Number of RX queues per port. */
+int nb_txq = 1;			/**< Number of TX queues per port. */
+
+int bond_port_id = -1;	/**< Bonded device port id */
+
+int nb_ports;
+struct rte_port *ports;
+rte_atomic64_t counter;	/**< Received packet's counter */
+
+/*
+ * Initializes a given port using global settings and with the rx buffers
+ * coming from the mbuf_pool passed as parameter
+ */
+static inline
+int init_port(uint8_t port_id, struct rte_mempool *mbuf_pool,
+		struct rte_eth_conf port_conf)
+{
+
+	int retval;
+	uint16_t q;
+	struct rte_eth_rxconf rxconf;
+
+	struct rte_port *port;
+
+	if (port_id >= rte_eth_dev_count())
+		return -1;
+
+	port = &ports[port_id];
+	port->nb_rx_queues = nb_rxq;
+	port->nb_tx_queues = nb_txq;
+
+	memcpy(&(port->dev_conf), &port_conf, sizeof(port_conf));
+	retval = rte_eth_dev_configure(port_id, port->nb_rx_queues,
+	                               port->nb_tx_queues, &port_conf);
+
+	if (retval != 0)
+		return retval;
+
+	rte_eth_dev_info_get(port_id, &(port->dev_info));
+	rxconf = (port->dev_info).default_rxconf;
+	rxconf.rx_free_thresh = 32;
+
+	for (q = 0; q < port->nb_rx_queues; q++) {
+		retval = rte_eth_rx_queue_setup(port_id, q, RX_RING_SIZE,
+		                                rte_eth_dev_socket_id(port_id), &rxconf,
+		                                mbuf_pool);
+		if (retval < 0)
+			return retval;
+	}
+
+	for (q = 0; q < port->nb_tx_queues; q++) {
+		retval = rte_eth_tx_queue_setup(port_id, q, TX_RING_SIZE,
+		                                rte_eth_dev_socket_id(port_id),
+		                                &((port->dev_info).default_txconf));
+		if (retval < 0)
+			return retval;
+	}
+
+	port->bond_mode = -1;
+	port->promiscuous = 0;
+	port->enabled = 1;
+
+	return 0;
+}
+
+static int
+rx_loop(__attribute__((unused)) void *dummy)
+{
+	uint8_t port_id;
+	int nq;
+	int n;
+
+	for (;;)
+		for (port_id = 0; port_id < nb_ports; port_id++) {
+			/* Pool only bonding ports */
+			if (ports[port_id].bond_mode >= 0)
+				for (nq = 0; nq < ports[port_id].nb_rx_queues; nq++) {
+					struct rte_mbuf *bufs[BURST_SIZE];
+
+					const uint16_t nb_rx = rte_eth_rx_burst(port_id, nq, bufs,
+					BURST_SIZE);
+					if (unlikely(nb_rx == 0))
+						continue;
+
+					ports[port_id].rxq_ipackets[nq] += nb_rx;
+					for (n = 0; n < nb_rx; n++) {
+						record_pkt(port_id, nq, bufs[n]);
+						rte_pktmbuf_free(bufs[n]);
+					}
+				}
+		}
+
+	return 0;
+}
+
+static uint16_t
+rx_callback(uint8_t port_id, uint16_t qidx, struct rte_mbuf **pkts,
+		uint16_t nb_pkts, uint16_t max_pkts __rte_unused, void *_ __rte_unused)
+{
+	int n;
+
+	ports[port_id].rxq_ipackets[qidx] += nb_pkts;
+	for (n = 0; n < nb_pkts; n++) {
+		rte_atomic64_inc(&counter);
+		pkts[n]->udata64 = counter.cnt;
+		record_pkt(port_id, qidx, pkts[n]);
+	}
+
+	return nb_pkts;
+}
+
+int
+main(int argc, char *argv[])
+{
+	struct rte_mempool *mbuf_pool;
+	unsigned lcore_id = 0;
+	uint8_t port_id, queue_id;
+
+	/* init EAL */
+	int ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+	argc -= ret;
+	argv += ret;
+
+	nb_ports = rte_eth_dev_count();
+	if (nb_ports < 1)
+		rte_exit(EXIT_FAILURE,
+			"At least one port must be available to create a bonding\n");
+
+	mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS * nb_ports, MBUF_SIZE,
+	                               MBUF_CACHE_SIZE,
+	                               sizeof(struct rte_pktmbuf_pool_private),
+	                               rte_pktmbuf_pool_init, NULL,
+	                               rte_pktmbuf_init, NULL, rte_socket_id(), 0);
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Configuration of Ethernet ports. */
+	ports = rte_zmalloc("bondrss: ports",
+	                    sizeof(struct rte_port) * RTE_MAX_ETHPORTS,
+	                    RTE_CACHE_LINE_SIZE);
+	if (ports == NULL)
+		rte_exit(EXIT_FAILURE, "rte_zmalloc(%d struct rte_port) failed\n",
+		         RTE_MAX_ETHPORTS);
+
+	/* enabled allocated ports */
+	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
+		ports[port_id].enabled = 0;
+
+	/* initialize all ports */
+	for (port_id = 0; port_id < nb_ports; port_id++) {
+		if (init_port(port_id, mbuf_pool, port_conf_default) != 0)
+			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8"\n", port_id);
+
+		/* Add rx callbacks to the slave's port */
+		for (queue_id = 0; queue_id < ports[port_id].nb_rx_queues; queue_id++)
+			rte_eth_add_rx_callback(port_id, queue_id, rx_callback, NULL);
+
+	}
+
+	/* create bonding port */
+	bond_port_id = rte_eth_bond_create("eth_bond", 0, 0);
+	if (bond_port_id < 0)
+		rte_exit(EXIT_FAILURE, "Cannot create bonding port\n");
+
+	for (port_id = 0; port_id < nb_ports; port_id++)
+		rte_eth_bond_slave_add(bond_port_id, port_id);
+
+	/* count again */
+	nb_ports = rte_eth_dev_count();
+
+	init_port(bond_port_id, mbuf_pool, port_conf_bonding);
+
+	/* start bonding port*/
+	ret = rte_eth_dev_start(bond_port_id);
+
+	/* enable promiscuous by default */
+	rte_eth_promiscuous_enable(bond_port_id);
+
+	for (port_id = 0; port_id < nb_ports; port_id++) {
+		ports[port_id].bond_mode = rte_eth_bond_mode_get(port_id);
+		ports[port_id].promiscuous = rte_eth_promiscuous_get(port_id);
+
+		/* map queues to stats */
+		if (ports[port_id].bond_mode >= 0) {
+			for (queue_id = 0; queue_id < ports[port_id].nb_rx_queues;
+			        queue_id++) {
+				rte_eth_dev_set_rx_queue_stats_mapping(port_id, queue_id,
+						queue_id);
+			}
+		}
+
+	}
+
+	/* Initialize received packet's counter */
+	rte_atomic64_init(&counter);
+
+	if (rte_lcore_count() <= 1)
+		rte_exit(EXIT_FAILURE, "More than one free lcore is needed\n");
+
+	lcore_id = rte_get_next_lcore(rte_lcore_id(), 1, 0);
+	rte_eal_remote_launch(rx_loop, NULL, lcore_id);
+
+	/* call lcore_main on master core only */
+	ui_loop();
+
+	return 0;
+}
diff --git a/examples/bond_rss/bondrss.h b/examples/bond_rss/bondrss.h
new file mode 100644
index 0000000..305a690
--- /dev/null
+++ b/examples/bond_rss/bondrss.h
@@ -0,0 +1,163 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BONDRSS_H_
+#define BONDRSS_H_
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+#include <rte_malloc.h>
+
+#include <rte_eth_bond.h>
+
+#define RX_RING_SIZE 128
+#define TX_RING_SIZE 512
+
+#define NUM_MBUFS 8191
+#define MBUF_SIZE (1600 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
+#define MBUF_CACHE_SIZE 250
+#define BURST_SIZE 32
+
+/*
+ * Configurable number of RX/TX queues.
+ */
+extern int nb_rxq; /**< Number of RX queues per port. */
+extern int nb_txq; /**< Number of TX queues per port. */
+
+extern int bond_port_id;	/**< Bonded device port id */
+extern int bond_mode;
+
+struct rss_type_info {
+	char str[32];
+	uint64_t rss_type;
+};
+
+static struct rss_type_info rss_type_table[] = {
+	{"ipv4", ETH_RSS_IPV4},
+	{"ipv4-frag", ETH_RSS_FRAG_IPV4},
+	{"ipv4-tcp", ETH_RSS_NONFRAG_IPV4_TCP},
+	{"ipv4-udp", ETH_RSS_NONFRAG_IPV4_UDP},
+	{"ipv4-sctp", ETH_RSS_NONFRAG_IPV4_SCTP},
+	{"ipv4-other", ETH_RSS_NONFRAG_IPV4_OTHER},
+	{"ipv6", ETH_RSS_IPV6},
+	{"ipv6-frag", ETH_RSS_FRAG_IPV6},
+	{"ipv6-tcp", ETH_RSS_NONFRAG_IPV6_TCP},
+	{"ipv6-udp", ETH_RSS_NONFRAG_IPV6_UDP},
+	{"ipv6-sctp", ETH_RSS_NONFRAG_IPV6_SCTP},
+	{"ipv6-other", ETH_RSS_NONFRAG_IPV6_OTHER},
+	{"l2-payload", ETH_RSS_L2_PAYLOAD},
+	{"ipv6-ex", ETH_RSS_IPV6_EX},
+	{"ipv6-tcp-ex", ETH_RSS_IPV6_TCP_EX},
+	{"ipv6-udp-ex", ETH_RSS_IPV6_UDP_EX},
+};
+
+struct rss_type_fn {
+	uint8_t enabled;
+	struct rss_type_info *info;
+};
+
+/**
+ * Buffer  for last received packets
+ */
+#define PKT_RECORD_SIZE		256
+
+struct pkt_info {
+	uint64_t	n;			/**< Packet number */
+	uint8_t		port_id;
+	uint8_t		queue_id;
+	uint64_t	ol_flags;	/**< Offload features. */
+	uint32_t	rss;		/**< RSS hash result if RSS enabled */
+};
+
+struct pkt_record {
+	uint64_t		count;			/**< Total number of received packets */
+	int				top;
+	struct pkt_info pkt_info[PKT_RECORD_SIZE];
+};
+
+/**
+ * The data structure associated with each port.
+ */
+struct rte_port {
+	struct rte_eth_dev_info dev_info;   /**< PCI info + driver name */
+	struct rte_eth_conf     dev_conf;   /**< Port configuration. */
+
+	uint16_t nb_rx_queues;			/**< Total number of rx queues */
+	uint16_t nb_tx_queues;			/**< Total number of tx queues*/
+
+	int bond_mode;
+	struct ether_addr addr;
+
+	int rss_type_count;
+	struct rss_type_fn rss_type_fn[RTE_DIM(rss_type_table)];
+	struct rte_eth_rss_reta_entry64 reta_conf[512 / RTE_RETA_GROUP_SIZE];
+	uint8_t rss_key[52];
+
+
+	struct rte_eth_stats stats;
+	uint64_t rxq_ipackets[128];
+
+	struct pkt_record record;
+
+	uint8_t rss;				/**< RSS enabled for port */
+	uint8_t promiscuous;		/**< promiscuous mode enabled for port */
+	uint8_t enabled;			/**< port is enabled */
+};
+
+extern int nb_ports;
+extern struct rte_port *ports;
+extern rte_atomic64_t counter;	/**< Received packet's counter */
+
+void update_port_info(uint8_t port_id);
+
+int rss_enable(uint8_t port_id, int enabled);
+
+int key_set(uint8_t port_id, uint8_t *key, uint8_t len);
+int key_set_random(uint8_t port_id, uint8_t len);
+
+int reta_set_default(uint8_t port_id);
+int reta_set_random(uint8_t port_id);
+int reta_set_all(uint8_t port_id, uint8_t value);
+int reta_set_weights(uint8_t port_id, uint64_t *weights);
+
+struct pkt_info *record_pkt(uint8_t port_id, int queue_id, struct rte_mbuf *mb);
+
+void ui_loop(void);
+
+
+#endif /* BONDRSS_H_ */
diff --git a/examples/bond_rss/config.c b/examples/bond_rss/config.c
new file mode 100644
index 0000000..39c00e5
--- /dev/null
+++ b/examples/bond_rss/config.c
@@ -0,0 +1,251 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <time.h>
+#include <stdlib.h>
+#include "bondrss.h"
+
+static const struct rte_eth_conf port_conf_default = {
+		.rxmode = {
+				.max_rx_pkt_len	= ETHER_MAX_LEN,
+				.split_hdr_size	= 0,
+				.header_split	= 0, /**< Header Split disabled. */
+				.hw_ip_checksum	= 0, /**< IP checksum offload disabled. */
+				.hw_vlan_filter	= 1, /**< VLAN filtering enabled. */
+				.hw_vlan_strip	= 1, /**< VLAN strip enabled. */
+				.hw_vlan_extend	= 0, /**< Extended VLAN disabled. */
+				.jumbo_frame	= 0, /**< Jumbo Frame Support disabled. */
+				.hw_strip_crc	= 0, /**< CRC stripping by hardware disabled. */
+				.enable_scatter	= 0, /**< scatter rx disabled */
+		},
+		.rx_adv_conf = {
+				.rss_conf = {
+						.rss_key	= NULL,
+						.rss_hf		= ETH_RSS_IPV4,
+				},
+		},
+};
+
+
+void
+update_port_info(uint8_t port_id)
+{
+	int i;
+	struct rte_port *port;
+
+	port = &ports[port_id];
+
+	rte_eth_dev_info_get(port_id, &(port->dev_info));
+
+	port->promiscuous = rte_eth_promiscuous_get(port_id);
+
+	/* Update device information */
+	rte_eth_dev_info_get(port_id, &port->dev_info);
+	rte_eth_macaddr_get(port_id, &port->addr);
+
+	port->dev_conf.rx_adv_conf.rss_conf.rss_key = port->rss_key;
+	port->dev_conf.rx_adv_conf.rss_conf.rss_key_len = 40;
+	rte_eth_dev_rss_hash_conf_get(port_id,
+			&(port->dev_conf.rx_adv_conf.rss_conf));
+
+	/* select all fields to be fetched */
+	for (i = 0; i < port->dev_info.reta_size / RTE_RETA_GROUP_SIZE; i++)
+		port->reta_conf[i].mask = ~0LL;
+
+	rte_eth_dev_rss_reta_query(port_id, port->reta_conf,
+			port->dev_info.reta_size);
+
+}
+
+/**
+ * Try to enable RSS for port_id.
+ */
+int
+rss_enable(uint8_t port_id, int enabled)
+{
+	struct rte_port *port;
+	int retval;
+
+	port = &ports[port_id];
+
+	rte_eth_dev_stop(port_id);
+
+	if (enabled) {
+		port->dev_conf.rxmode.mq_mode = ETH_MQ_RX_RSS;
+		port->dev_conf.rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IPV4;
+	} else {
+		port->dev_conf.rxmode.mq_mode = 0;
+		port->dev_conf.rx_adv_conf.rss_conf.rss_hf = 0;
+	}
+
+	retval = rte_eth_dev_configure(port_id, port->nb_rx_queues,
+	                               port->nb_tx_queues, &port->dev_conf);
+	if (retval != 0)
+		return retval;
+
+	retval = rte_eth_dev_start(port_id);
+
+	return 0;
+}
+
+int
+key_set(uint8_t port_id, uint8_t *key, uint8_t len)
+{
+	struct rte_eth_rss_conf rss_conf;
+
+	int i;
+
+	rss_conf.rss_key = NULL;
+	rss_conf.rss_key_len = 0;
+	i = rte_eth_dev_rss_hash_conf_get(port_id, &rss_conf);
+
+	rss_conf.rss_key = key;
+	rss_conf.rss_key_len = len;
+
+	i = rte_eth_dev_rss_hash_update(port_id,
+			&rss_conf);
+
+	return i;
+}
+
+/**
+ * Set random RSS key value
+ */
+int
+key_set_random(uint8_t port_id, uint8_t len)
+{
+	int i;
+	uint8_t key[40];
+
+	for (i = 0; i < len; i++)
+		key[i] = rand() % 256;
+
+	return key_set(port_id, key, len);
+}
+
+/**
+ * Set random RETA values
+ */
+int
+reta_set_random(uint8_t port_id)
+{
+	struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_RETA_GROUP_SIZE];
+	int i, j;
+
+	int reta_size = ports[port_id].dev_info.reta_size;
+
+	for (i = 0; i < reta_size / RTE_RETA_GROUP_SIZE; i++) {
+		/* select all fields to set */
+		reta_conf[i].mask = ~0LL;
+		for (j = 0; j < RTE_RETA_GROUP_SIZE; j++)
+			reta_conf[i].reta[j] = rand() % ports[port_id].nb_rx_queues;
+	}
+
+	return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size);
+}
+
+/**
+ * Set default RETA values
+ */
+int
+reta_set_default(uint8_t port_id)
+{
+	struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_RETA_GROUP_SIZE];
+	int i, j;
+
+	int reta_size = ports[port_id].dev_info.reta_size;
+
+	for (i = 0; i < reta_size / RTE_RETA_GROUP_SIZE; i++) {
+		/* select all fields to set */
+		reta_conf[i].mask = ~0LL;
+		for (j = 0; j < RTE_RETA_GROUP_SIZE; j++)
+			reta_conf[i].reta[j] = j % nb_rxq;
+	}
+
+	return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size);
+}
+
+/**
+ * Fill the RETA with values
+ */
+int
+reta_set_all(uint8_t port_id, uint8_t value)
+{
+	struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_RETA_GROUP_SIZE];
+	int i, j;
+
+	int reta_size = ports[port_id].dev_info.reta_size;
+
+	for (i = 0; i < reta_size / RTE_RETA_GROUP_SIZE; i++) {
+		/* select all fields to set */
+		reta_conf[i].mask = ~0LL;
+		for (j = 0; j < RTE_RETA_GROUP_SIZE; j++)
+			reta_conf[i].reta[j] = value;
+	}
+
+	return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size);
+}
+
+/**
+ * Set the RETA basing on weights table
+ */
+int
+reta_set_weights(uint8_t port_id, uint64_t *weights)
+{
+	struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_RETA_GROUP_SIZE];
+	unsigned i, j, k;
+
+	unsigned reta_size = ports[port_id].dev_info.reta_size;
+
+	uint64_t sum = 0, sum2;
+
+	for (i = 0; i < ports[port_id].nb_rx_queues; i++)
+		sum += weights[i];
+	sum2 = sum * (ports[port_id].nb_rx_queues-1);
+
+	if (sum2 == 0)
+		return 0;
+
+	for (i = 0; i < reta_size / RTE_RETA_GROUP_SIZE; i++)
+		reta_conf[i].mask = ~0LL;
+	k = 0;
+	for (i = 0; i < ports[port_id].nb_rx_queues; i++) {
+		for (j = 0; k < reta_size &&
+				j < ((sum - weights[i]) * reta_size + 1) / sum2; j++) {
+			reta_conf[k/RTE_RETA_GROUP_SIZE].reta[k%RTE_RETA_GROUP_SIZE] = i;
+			k++;
+		}
+	}
+
+	return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size);
+}
diff --git a/examples/bond_rss/ui.c b/examples/bond_rss/ui.c
new file mode 100644
index 0000000..90cae09
--- /dev/null
+++ b/examples/bond_rss/ui.c
@@ -0,0 +1,920 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rte_atomic.h>
+
+#include <ncurses.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <menu.h>
+#include "bondrss.h"
+
+WINDOW *win_footer	= NULL;
+WINDOW *win_header	= NULL;
+WINDOW *win_list	= NULL;
+WINDOW *win_stats	= NULL;
+WINDOW *win_reta	= NULL;
+WINDOW *win_err		= NULL;
+
+int scr_lines		= 0;
+int scr_cols		= 0;
+
+unsigned win_header_lines;
+unsigned win_list_lines;
+unsigned win_stats_lines;
+unsigned win_reta_lines;
+unsigned win_err_lines = 0;
+
+const char  *ui_status_text;
+static char *ui_err_text[5];
+
+struct rte_port *selected_port;
+static uint8_t  selected_port_id;
+
+struct rte_port *focused_port;
+static uint8_t focused_port_id;
+
+#define UI_MODE_PORT		0
+#define UI_MODE_RSS_FN		1
+#define UI_MODE_RSS_KEY		2
+#define UI_MODE_RETA		3
+#define UI_MODE_HELP		4
+
+int ui_mode = UI_MODE_PORT;
+
+static uint16_t _is_lock;
+static uint16_t _is_ready;
+
+static uint16_t _do_configure;
+static uint16_t _do_repaint;
+static uint16_t _do_repaint_stats;
+static uint16_t _do_repaint_list;
+static uint16_t _do_repaint_info;
+static uint16_t _do_repaint_err;
+
+
+struct help_page {
+	const char *title;
+	const char *body;
+};
+
+static struct help_page help_pages[3] = {
+		{
+				.title = "About",
+
+				.body =
+				"\n\n"
+				" ______   ______   ______   _____    ______   ______   ______\n"
+				"| |  | \\ / |  | \\ | |  \\ \\ | | \\ \\  | |  | \\ / |      / |\n"
+				"| |--| < | |  | | | |  | | | |  | | | |__| | '------. '------.\n"
+				"|_|__|_/ \\_|__|_/ |_|  |_| |_|_/_/  |_|  \\_\\  ____|_/  ____|_/\n"
+				"\n\n"
+				"This application allows you to test RSS configuration for "
+				"bonded devices\nchanging configuration dynamically, during "
+				"receiving packets on slaves and\nobserve the changes in its "
+				"distribution over queues.\n"
+				"\n"
+
+		},
+		{
+				.title = "Introduction",
+				.body = "\n\n"
+				"After initialization process, all accessible ports are "
+				"attached to one\nbonding as slaves.\n"
+				"\n"
+
+				"Monitor screen is divided into five main parts:\n"
+				" - Port selection (on the very top)\n"
+				" - RSS Configuration for selected port including hash "
+				"function, key and\n   RETA\n"
+				" - Incoming packets statistics\n"
+				" - Incoming packets list for selected port\n"
+				" - Status bar with contextual information about selected "
+				"part\n"
+				"\n\n"
+
+				"[up], [down] arrows key lets you highlight a part "
+				"and parameter to change.\n\n"
+		},
+		{
+				.title = "Key bindings",
+				.body =
+				"_Port_selection_\n\n"
+				"  [left], [right] - select port,\n"
+				"  [space]         - force turning on/off RSS mode,\n"
+				"  [p]             - turn on/off promiscuous mode,\n"
+				"  [c]             - clear statistics and incoming packet list,\n"
+				"\n"
+				"_RSS_hash_function_\n\n"
+				"  [1]-[9]         - toggle selected hash function,\n"
+				"  [a]             - select all,\n"
+				"\n"
+				"_RSS_key_\n\n"
+				"  [1]-[4]         - set one of predefined key value,\n"
+				"  [r]             - randomize value,\n"
+				"\n"
+				"_RSS_RETA_\n\n"
+				"  [number]        - fill RETA with number,\n"
+				"  [r]             - randomize value,\n"
+				"  [d]             - set default value,"
+
+		}
+};
+
+static int
+ui_lock(int wait)
+{
+	int result;
+
+	while (!(result = rte_atomic16_cmpset(&_is_lock, 0, 1)) && wait)
+		rte_pause();
+
+	return result;
+}
+
+static int
+ui_unlock(void)
+{
+	return rte_atomic16_cmpset(&_is_lock, 1, 0);
+}
+
+/**
+ * Predefined key values
+ */
+static uint8_t RSS_KEY[4][40] = {
+	{	0x6D, 0x5A, 0x56, 0xDA, 0x25, 0x5B, 0x0E, 0xC2, 0x41, 0x67, 0x25, 0x3D,
+		0x43, 0xA3, 0x8F, 0xB0, 0xD0, 0xCA, 0x2B, 0xCB, 0xAE, 0x7B, 0x30, 0xB4,
+		0x77, 0xCB, 0x2D, 0xA3, 0x80, 0x30, 0xF2, 0x0C, 0x6A, 0x42, 0xB7, 0x3B,
+		0xBE, 0xAC, 0x01, 0xFA
+	},
+	{	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00,
+	},
+	{	0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
+		0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
+		0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
+		0x6D, 0x5A, 0x6D, 0x5A,
+	},
+	{	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
+		0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+		0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+		0x25, 0x26, 0x27, 0x28,
+	}
+};
+
+static void
+win_err_addline(const char *text)
+{
+	int i;
+
+	if (win_err_lines < 5) {
+		win_err_lines++;
+		_do_configure = 1;
+	} else {
+		free(ui_err_text[0]);
+		for (i = 1; i < 5; i++)
+			ui_err_text[i - 1] = ui_err_text[i];
+	}
+
+	ui_err_text[win_err_lines - 1] = strdup(text);
+	_do_repaint_err = 1;
+}
+
+static void
+win_err_fini(void)
+{
+	int i;
+	for (i = 0; i < (int)win_err_lines; i++)
+		free(ui_err_text[i]);
+}
+
+/**
+ * Draw full information for port
+ */
+static void
+win_header_draw(uint8_t port_id)
+{
+	int i, j;
+	attr_t a;
+
+	update_port_info(selected_port_id);
+
+	/* Draw title bar */
+	if (ui_mode == UI_MODE_PORT)
+		wattron(win_header, COLOR_PAIR(2));
+	else
+		wattron(win_header, COLOR_PAIR(4));
+
+	mvwprintw(win_header, 0, 0, "::");
+
+	for (i = 0; i < nb_ports; i++) {
+		if (ports[i].enabled) {
+			const char *name = (ports[i].bond_mode >= 0 ? "BOND" : "SLAVE");
+
+			if (i == port_id) {
+				wattron(win_header, A_REVERSE);
+				wprintw(win_header, " %s-%d ", name, (unsigned) i);
+				wattroff(win_header, A_REVERSE);
+			} else
+				wprintw(win_header, " %s-%d ", name, (unsigned) i);
+		}
+	}
+
+	for (i = scr_cols - getcurx(win_header) - 3; i > 0; i--)
+		waddch(win_header, ':');
+
+	waddch(win_header,
+			(ports[port_id].rss ? 'R' : '-'));
+	waddch(win_header, ports[port_id].bond_mode >= 0 ?
+			ports[port_id].bond_mode + '0' : '-');
+	waddch(win_header, ports[port_id].promiscuous == 0 ? '-' : 'P');
+
+	if (ui_mode == UI_MODE_PORT)
+		wattroff(win_header, COLOR_PAIR(2));
+	else
+		wattroff(win_header, COLOR_PAIR(4));
+
+	/* Redraw RSS-Fn */
+	selected_port->rss_type_count = 0;
+	for (i = 0; i < (int) RTE_DIM(rss_type_table); i++) {
+		if (selected_port->dev_info.flow_type_rss_offloads
+				& rss_type_table[i].rss_type) {
+			selected_port->rss_type_fn[selected_port->rss_type_count].info =
+				&rss_type_table[i];
+			selected_port->rss_type_fn[selected_port->rss_type_count].enabled =
+				((selected_port->dev_conf.rx_adv_conf.rss_conf.rss_hf
+						& rss_type_table[i].rss_type) ? 1 : 0);
+			selected_port->rss_type_count++;
+		}
+	}
+
+	a = (ui_mode == UI_MODE_RSS_FN ? COLOR_PAIR(2) : COLOR_PAIR(1));
+
+	wattron(win_header, a);
+	mvwprintw(win_header, 1, 0, "FN:  ");
+	for (i = 0; i < selected_port->rss_type_count; i++) {
+		if (selected_port->rss_type_fn[i].enabled)
+			wattron(win_header, COLOR_PAIR(3));
+		waddstr(win_header, selected_port->rss_type_fn[i].info->str);
+		if (selected_port->rss_type_fn[i].enabled)
+			wattron(win_header, a);
+		waddch(win_header, ' ');
+	}
+	wattroff(win_header, a);
+
+	/* Redraw RSS-Key */
+	if (ui_mode == UI_MODE_RSS_KEY)
+		wattron(win_header, COLOR_PAIR(2));
+	mvwprintw(win_header, 2, 0, "KEY: ");
+	for (i = 0; i < 40; i++)
+		wprintw(win_header, "%02X",
+		        selected_port->dev_conf.rx_adv_conf.rss_conf.rss_key[i]);
+	if (ui_mode == UI_MODE_RSS_KEY)
+		wattroff(win_header, COLOR_PAIR(2));
+
+
+	/* Redraw RETA window */
+	int idx, shift;
+
+	if (ui_mode == UI_MODE_RETA)
+		wattron(win_reta, COLOR_PAIR(2));
+
+	for (j = 0; j < selected_port->dev_info.reta_size / 16; j++) {
+		wmove(win_reta, j, 0);
+		for (i = 0; i < 16; i++) {
+			idx = (j*16 + i) / RTE_RETA_GROUP_SIZE;
+			shift = (j*16 + i) % RTE_RETA_GROUP_SIZE;
+			waddch(win_reta, ACS_VLINE);
+			wprintw(win_reta, "%d", selected_port->reta_conf[idx].reta[shift]);
+		}
+		waddch(win_reta, ACS_VLINE);
+	}
+
+	if (ui_mode == UI_MODE_RETA)
+		wattroff(win_reta, COLOR_PAIR(2));
+	wnoutrefresh(win_reta);
+
+
+	/* Stats repaint */
+	if (_do_repaint_stats) {
+		uint64_t total;
+
+		rte_eth_stats_get(selected_port_id, &(selected_port->stats));
+
+		wmove(win_stats, 0, 0);
+		total = 0;
+		for (i = 0; i < selected_port->nb_rx_queues; i++) {
+			wprintw(win_stats, "Queue %d: %10"PRIu64
+		        " (%10" PRIu64 ")\n", i, selected_port->rxq_ipackets[i],
+				selected_port->stats.q_ipackets[i]);
+			total += selected_port->rxq_ipackets[i];
+		}
+
+		wprintw(win_stats, "  Total: %10"PRIu64
+	        " (%10" PRIu64 ")\n", total, selected_port->stats.ipackets);
+
+		_do_repaint_stats = 0;
+		wnoutrefresh(win_stats);
+	}
+
+	if (_do_repaint_err && win_err_lines > 0) {
+		for (i = 0; i < (int)win_err_lines; i++) {
+			mvwprintw(win_err, i, 0, ui_err_text[i]);
+			wclrtoeol(win_err);
+		}
+		_do_repaint_err = 0;
+		wnoutrefresh(win_err);
+	}
+
+	mvwhline(win_header, win_header_lines - 1, 0, 0, scr_cols);
+}
+
+static void
+win_footer_draw(const char *text)
+{
+	if (text != NULL)
+		ui_status_text = text;
+
+	wclear(win_footer);
+	wmove(win_footer, 0, 0);
+	wprintw(win_footer, ui_status_text);
+
+	wprintw(win_footer, "; [arrows]navigate; [q]quit");
+	wnoutrefresh(win_footer);
+}
+
+static void
+win_list_draw(void)
+{
+	unsigned n, i;
+
+	struct pkt_info *pkt_info;
+
+	struct rte_port *port = &ports[focused_port_id];
+	struct pkt_record *record = &(port->record);
+
+	n = (win_list_lines > record->count) ? record->count : win_list_lines;
+
+	wmove(win_list, 0, 0);
+
+	for (i = 0; i < n; i++) {
+
+		pkt_info = &record->pkt_info[(record->top - n + i + PKT_RECORD_SIZE)
+				% PKT_RECORD_SIZE];
+
+		wmove(win_list, i, 0);
+
+		wprintw(win_list, "%5"PRIu64, (unsigned) pkt_info->n);
+		waddch(win_list, ACS_VLINE);
+
+		wprintw(win_list, "%2d: ", (unsigned) pkt_info->queue_id);
+		wprintw(win_list, "%08x  ", (unsigned) pkt_info->rss);
+
+		if (pkt_info->ol_flags != 0) {
+			unsigned rxf;
+			const char *name;
+
+			for (rxf = 0; rxf < sizeof(pkt_info->ol_flags) * 8; rxf++) {
+				if ((pkt_info->ol_flags & (1ULL << rxf)) == 0)
+					continue;
+				name = rte_get_rx_ol_flag_name(1ULL << rxf);
+				if (name == NULL)
+					continue;
+				wprintw(win_list, "%s ", name);
+			}
+		}
+		wclrtoeol(win_list);
+	}
+
+	wclrtobot(win_list);
+	wnoutrefresh(win_list);
+}
+
+static void
+ui_repaint(void)
+{
+	switch (ui_mode) {
+	default:
+		win_header_draw(selected_port_id);
+		wnoutrefresh(win_header);
+
+		if (_do_repaint_list) {
+			win_list_draw();
+			_do_repaint_list = 0;
+		}
+
+		break;
+	}
+
+	win_footer_draw(NULL);
+	_do_repaint = 0;
+}
+
+static void
+ui_mode_set(int mode)
+{
+	ui_mode = (mode + 4) % 4;
+	switch (ui_mode) {
+	case UI_MODE_PORT:
+		ui_status_text = "Port: [p]promiscuous; [c]clear stats";
+		break;
+	case UI_MODE_RSS_FN:
+		ui_status_text = "RSS Fn: [1-9]toggle fn";
+		break;
+	case UI_MODE_RETA:
+		ui_status_text =
+			"RETA: [d]set default; [r]randomize; [0-9]fill table with value";
+		break;
+	case UI_MODE_RSS_KEY:
+		ui_status_text = "RSS Key: [r]randomize; [1-4]select predefined key";
+		break;
+	}
+	_do_repaint = 1;
+}
+
+static void
+ui_configure(void)
+{
+	int win_reta_cols = (16 * 2 + 1);
+	win_reta_lines = selected_port->dev_info.reta_size / 16;
+	win_stats_lines = selected_port->nb_rx_queues + 1;
+	win_header_lines = win_reta_lines;
+	if (win_header_lines < win_stats_lines)
+		win_header_lines = win_stats_lines;
+	win_header_lines += 5;
+	win_list_lines = scr_lines - win_header_lines - win_err_lines - 1;
+
+	if (win_footer == NULL) {
+		win_footer = newwin(1, scr_cols, scr_lines - 1, 0);
+		ui_status_text = "";
+		wbkgdset(win_footer, COLOR_PAIR(1));
+	} else {
+		wresize(win_footer, 1, scr_cols);
+		mvwin(win_footer, scr_lines - 1, 0);
+	}
+
+	if (win_err == NULL) {
+		win_err = newwin(1, scr_cols, scr_lines - 1 - win_err_lines, 0);
+		ui_status_text = "";
+		wbkgdset(win_err, COLOR_PAIR(5));
+	} else {
+		wresize(win_err, win_err_lines, scr_cols);
+		mvwin(win_err, scr_lines - win_err_lines - 1, 0);
+	}
+
+
+	if (win_header == NULL) {
+		win_header = newwin(win_header_lines, scr_cols, 0, 0);
+		wbkgdset(win_header, COLOR_PAIR(1));
+	} else
+		wresize(win_header, win_header_lines, scr_cols);
+
+	if (win_stats == NULL)
+		win_stats = subwin(win_header, win_stats_lines,
+				scr_cols - win_reta_cols - 7, 4, win_reta_cols + 7);
+	else
+		wresize(win_stats, win_stats_lines, scr_cols - win_reta_cols - 7);
+
+	if (win_reta == NULL)
+		win_reta = subwin(win_header, win_reta_lines, win_reta_cols,
+				4, 5);
+	else
+		wresize(win_reta, win_reta_lines, win_reta_cols);
+
+	if (win_list == NULL)
+		win_list = newwin(win_list_lines, scr_cols, win_header_lines, 0);
+	else {
+		wresize(win_list, win_list_lines, scr_cols);
+		mvwin(win_list, win_header_lines, 0);
+	}
+
+	wclear(win_header);
+	wclear(win_reta);
+
+	_do_configure = 0;
+	_do_repaint = 1;
+	_do_repaint_list = 1;
+	_do_repaint_stats = 1;
+	_do_repaint_info = 1;
+	_do_repaint_err = 1;
+
+}
+
+static void
+ui_resize(void)
+{
+	if (COLS != scr_cols || LINES != scr_lines) {
+		scr_cols = COLS;
+		scr_lines = LINES;
+		_do_configure = 1;
+	}
+}
+
+static void
+ui_update(void)
+{
+	if (ui_lock(1)) {
+		if (_is_ready) {
+			if (_do_configure)
+				ui_configure();
+			if (_do_repaint) {
+				ui_repaint();
+				doupdate();
+			}
+		}
+		ui_unlock();
+	}
+}
+
+static void
+ui_fini(void)
+{
+	win_err_fini();
+	endwin();
+}
+
+static void
+ui_init(void)
+{
+	initscr();
+	start_color();
+	init_pair(1, COLOR_YELLOW, COLOR_BLUE);
+	init_pair(2, COLOR_BLACK, COLOR_CYAN);
+	init_pair(3, COLOR_BLACK, COLOR_YELLOW);
+	init_pair(4, COLOR_BLACK, COLOR_WHITE);
+	init_pair(5, COLOR_YELLOW, COLOR_RED);
+
+	cbreak();
+	noecho();
+	curs_set(FALSE);
+	keypad(stdscr, TRUE);
+	timeout(100);
+
+	scr_cols = COLS;
+	scr_lines = LINES;
+	_do_configure = 1;
+	_is_ready = 1;
+}
+
+static int
+port_select(uint8_t port_id)
+{
+	if (ports[port_id].enabled == 1) {
+		focused_port_id = selected_port_id = port_id;
+
+		selected_port = &ports[selected_port_id];
+		focused_port = &ports[focused_port_id];
+
+		update_port_info(selected_port_id);
+
+		_do_configure = 1;
+		_do_repaint = 1;
+		_do_repaint_info = 1;
+		_do_repaint_list = 1;
+		_do_repaint_stats = 1;
+		return selected_port_id;
+	}
+	return -1;
+}
+
+/**
+ * Record packet from mbuf
+ */
+struct pkt_info *
+record_pkt(uint8_t port_id, int queue_id, struct rte_mbuf *mb)
+{
+	struct rte_port *port = &ports[port_id];
+	struct pkt_record *record = &(port->record);
+	struct pkt_info *pkt_info = &(record->pkt_info[record->top]);
+
+	ui_lock(1);
+
+	record->count++;
+	pkt_info->n = mb->udata64;
+	pkt_info->ol_flags = mb->ol_flags;
+	pkt_info->queue_id = queue_id;
+	pkt_info->rss = mb->hash.rss;
+
+	record->top = (record->top + 1) % PKT_RECORD_SIZE;
+
+	_do_repaint = 1;
+	_do_repaint_list = 1;
+	_do_repaint_stats = 1;
+	ui_unlock();
+
+	return pkt_info;
+}
+
+static void
+ui_help(void) {
+	timeout(-1);
+
+	WINDOW *win_help_border = NULL;
+	WINDOW *win_help = NULL;
+	char title[256];
+	int page = 0;
+	int page_prev;
+	int page_next;
+	int c;
+	int cols;
+	int lines;
+	int top;
+	int left;
+
+	endwin();
+	initscr();
+	ui_resize();
+	ui_update();
+
+	cols = scr_cols > 80 ? 80 : scr_cols;
+	lines = scr_lines > 25 ? 25 : scr_lines;
+	top = (scr_lines - lines) / 2;
+	left = (scr_cols - cols) / 2;
+
+	win_help_border = newwin(lines, cols, top, left);
+	win_help = derwin(win_help_border, lines - 2, cols - 4, 1, 2);
+	wbkgdset(win_help_border, COLOR_PAIR(2));
+	wbkgdset(win_help, COLOR_PAIR(2));
+
+	do {
+		page_prev = (page + RTE_DIM(help_pages) - 1) % RTE_DIM(help_pages);
+		page_next = (page + 1) % RTE_DIM(help_pages);
+
+		wclear(win_help_border);
+		box(win_help_border, 0 , 0);
+
+		sprintf(title, "[ Help: %s ]", help_pages[page].title);
+		mvwprintw(win_help_border, 0, (cols - strlen(title)) / 2 - 2, "%s",
+		          title);
+
+		sprintf(title, "< %s ]", help_pages[page_prev].title);
+		mvwprintw(win_help_border, lines - 1, 1, "%s", title);
+
+		sprintf(title, "[ Page %d of %d ]", page + 1, (int)RTE_DIM(help_pages));
+		mvwprintw(win_help_border, lines - 1, (cols - strlen(title)) / 2 - 2,
+		          "%s", title);
+
+		sprintf(title, "[ %s >", help_pages[page_next].title);
+		mvwprintw(win_help_border, lines - 1, cols - strlen(title) - 1, "%s",
+		          title);
+
+		wrefresh(win_help_border);
+
+		wclear(win_help);
+
+		wmove(win_help, 0, 0);
+
+		wprintw(win_help,
+				help_pages[page].body
+			);
+
+		wrefresh(win_help);
+
+		switch (c = getch()) {
+
+		case KEY_RESIZE:
+			endwin();
+			initscr();
+			ui_resize();
+			ui_update();
+
+			cols = scr_cols > 80 ? 80 : scr_cols;
+			lines = scr_lines > 25 ? 25 : scr_cols;
+			top = (scr_lines - lines) / 2;
+			left = (scr_cols - cols) / 2;
+
+			wresize(win_help_border, lines, cols);
+			wresize(win_help, lines - 2, cols - 4);
+			break;
+
+		case KEY_LEFT:
+			page = page_prev;
+			break;
+
+		case KEY_RIGHT:
+			page = page_next;
+			break;
+
+		default:
+			c = -1; /* Exit */
+			break;
+		}
+
+
+	} while (c != -1);
+
+	timeout(100);
+	delwin(win_help);
+	delwin(win_help_border);
+	_do_configure = 1;
+	_do_repaint = 1;
+}
+
+/* main processing loop */
+void
+ui_loop(void)
+{
+	int c;
+	int i;
+	int port_id;
+	int refresh = 0;
+
+	ui_init();
+	port_select(bond_port_id);
+
+	ui_mode_set(UI_MODE_PORT);
+
+	while ((c = getch()) != 'q') {
+		switch (c) {
+		case -1:
+			refresh++;
+			if (refresh % 10) {
+				_do_configure = 1;
+			}
+			ui_update();
+			break;
+
+		case 'c':
+			/* clear stats */
+			ui_lock(1);
+
+			rte_atomic64_clear(&counter);
+			for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
+				if (ports[port_id].enabled) {
+					ports[port_id].record.count = 0;
+					ports[port_id].record.top = 0;
+					for (i = 0; i < ports[port_id].nb_rx_queues; i++)
+						ports[port_id].rxq_ipackets[i] = 0;
+					rte_eth_stats_reset(port_id);
+				}
+			}
+
+			_do_repaint = 1;
+			ui_unlock();
+			break;
+
+		case ' ':
+			focused_port->rss ^= 1;
+			rss_enable(focused_port_id, focused_port->rss);
+			_do_configure = 1;
+			_do_repaint = 1;
+			break;
+
+		case 'p':
+			if (focused_port->promiscuous)
+				rte_eth_promiscuous_disable(focused_port_id);
+			else
+				rte_eth_promiscuous_enable(focused_port_id);
+			focused_port->promiscuous ^= 1;
+			_do_configure = 1;
+			_do_repaint = 1;
+			break;
+
+		case KEY_RESIZE:
+			ui_resize();
+			break;
+
+		case KEY_UP:
+			ui_mode_set(ui_mode - 1);
+			break;
+		case KEY_DOWN:
+			ui_mode_set(ui_mode + 1);
+			break;
+		case 'h':
+		case '?':
+			ui_help();
+			break;
+
+		default:
+			switch (ui_mode) {
+			case UI_MODE_PORT:
+				switch (c) {
+				case KEY_LEFT:
+					for (i = 1; i < nb_ports; i++)
+						if (port_select(
+								(selected_port_id - i + nb_ports) % nb_ports)
+								!= -1)
+							break;
+					break;
+
+				case KEY_RIGHT:
+					for (i = 1; i < nb_ports; i++)
+						if (port_select(
+								(selected_port_id + i + nb_ports) % nb_ports)
+								!= -1)
+							break;
+					break;
+
+				default:
+					i = (c - '1');
+					port_select(i);
+					break;
+				}
+				break;
+
+			case UI_MODE_RSS_KEY:
+				switch (c) {
+				case 'r':
+					i = key_set_random(focused_port_id, 40);
+					if (i < 0)
+						win_err_addline("Cannot update RSS key");
+					break;
+
+				default:
+					c = c - '1';
+					if (c >= 0 && c < (int) RTE_DIM(RSS_KEY)) {
+						i = key_set(focused_port_id, RSS_KEY[c], 40);
+						if (i < 0)
+							win_err_addline("Cannot update RSS key");
+					}
+					break;
+				}
+				update_port_info(focused_port_id);
+				break;
+
+			case UI_MODE_RETA:
+				switch (c) {
+				case 'r':
+					reta_set_random(focused_port_id);
+					break;
+				case 'd':
+					reta_set_default(focused_port_id);
+					break;
+				default:
+					c = c - '0';
+					reta_set_all(focused_port_id, c);
+					break;
+				}
+				break;
+
+			case UI_MODE_RSS_FN:
+				switch (c) {
+				case 'a':
+					/* Set all */
+					focused_port->dev_conf.rx_adv_conf.rss_conf.rss_hf =
+						ports[focused_port_id].dev_info.flow_type_rss_offloads;
+					rte_eth_dev_rss_hash_update(focused_port_id,
+						&focused_port->dev_conf.rx_adv_conf.rss_conf);
+					break;
+
+				default:
+					c -= '1';
+					if (c >= 0 && c < focused_port->rss_type_count) {
+						uint64_t rss_type = 0;
+
+						focused_port->rss_type_fn[c].enabled ^= 1;	/* toggle */
+
+						for (i = 0; i < focused_port->rss_type_count; i++)
+							if (focused_port->rss_type_fn[i].enabled)
+								rss_type |=
+									focused_port->rss_type_fn[i].info->rss_type;
+						focused_port->dev_conf.rx_adv_conf.rss_conf.rss_hf =
+								rss_type;
+
+						rte_eth_dev_rss_hash_update(focused_port_id,
+								&focused_port->dev_conf.rx_adv_conf.rss_conf);
+
+					}
+				}
+				break;
+
+			}
+			_do_repaint = 1;
+			break;
+		}
+	}
+	ui_fini();
+}
