DPDK patches and discussions
 help / color / mirror / Atom feed
From: Declan Doherty <declan.doherty@intel.com>
To: dev@dpdk.org
To: dev@dpdk.org
Subject: [dpdk-dev] [PATCH v3 3/5] Link Bonding PMD Library (Unit Test Suite)
Date: Fri, 13 Jun 2014 15:42:00 +0100	[thread overview]
Message-ID: <3e846e0c5e2175761cf4ba64c283667e8d59f351.1402662300.git.declan.doherty@intel.com> (raw)
In-Reply-To: <cover.1401287412.git.declan.doherty@intel.com>
In-Reply-To: <cover.1402662300.git.declan.doherty@intel.com>

Link bonding unit tests, including:
 - code to generate packet bursts for testing rx and tx
   functionality of bonded device
 - virtual/stubbed out ethdev for use as slave ethdev in testing


Signed-off-by: Declan Doherty <declan.doherty@intel.com>
---
 app/test/Makefile                 |    4 +-
 app/test/commands.c               |    7 +
 app/test/packet_burst_generator.c |  288 +++
 app/test/packet_burst_generator.h |   78 +
 app/test/test.h                   |    1 +
 app/test/test_link_bonding.c      | 3958 +++++++++++++++++++++++++++++++++++++
 app/test/virtual_pmd.c            |  574 ++++++
 app/test/virtual_pmd.h            |   74 +
 8 files changed, 4983 insertions(+), 1 deletion(-)
 create mode 100644 app/test/packet_burst_generator.c
 create mode 100644 app/test/packet_burst_generator.h
 create mode 100644 app/test/test_link_bonding.c
 create mode 100644 app/test/virtual_pmd.c
 create mode 100644 app/test/virtual_pmd.h

diff --git a/app/test/Makefile b/app/test/Makefile
index 3b050c3..02eff0f 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -96,7 +96,9 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_ivshmem.c
 SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor.c
 SRCS-$(CONFIG_RTE_APP_TEST) += test_distributor_perf.c
 SRCS-$(CONFIG_RTE_APP_TEST) += test_devargs.c
-
+SRCS-$(CONFIG_RTE_APP_TEST) += virtual_pmd.c
+SRCS-$(CONFIG_RTE_APP_TEST) += packet_burst_generator.c
+SRCS-$(CONFIG_RTE_APP_TEST) += test_link_bonding.c
 ifeq ($(CONFIG_RTE_APP_TEST),y)
 SRCS-$(CONFIG_RTE_LIBRTE_ACL) += test_acl.c
 SRCS-$(CONFIG_RTE_LIBRTE_PMD_RING) += test_pmd_ring.c
diff --git a/app/test/commands.c b/app/test/commands.c
index 016410d..c31389d 100644
--- a/app/test/commands.c
+++ b/app/test/commands.c
@@ -157,6 +157,10 @@ static void cmd_autotest_parsed(void *parsed_result,
 		ret = test_timer();
 	if (!strcmp(res->autotest, "timer_perf_autotest"))
 		ret = test_timer_perf();
+#ifdef RTE_LIBRTE_PMD_BOND
+	if (!strcmp(res->autotest, "link_bonding_autotest"))
+		ret = test_link_bonding();
+#endif
 	if (!strcmp(res->autotest, "mempool_autotest"))
 		ret = test_mempool();
 	if (!strcmp(res->autotest, "mempool_perf_autotest"))
@@ -225,6 +229,9 @@ cmdline_parse_token_string_t cmd_autotest_autotest =
 			"alarm_autotest#interrupt_autotest#"
 			"version_autotest#eal_fs_autotest#"
 			"cmdline_autotest#func_reentrancy_autotest#"
+#ifdef RTE_LIBRTE_PMD_BOND
+			"link_bonding_autotest#"
+#endif
 			"mempool_perf_autotest#hash_perf_autotest#"
 			"memcpy_perf_autotest#ring_perf_autotest#"
 			"red_autotest#meter_autotest#sched_autotest#"
diff --git a/app/test/packet_burst_generator.c b/app/test/packet_burst_generator.c
new file mode 100644
index 0000000..38db383
--- /dev/null
+++ b/app/test/packet_burst_generator.c
@@ -0,0 +1,288 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 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_byteorder.h>
+#include <rte_mbuf.h>
+
+#include "packet_burst_generator.h"
+
+#define UDP_SRC_PORT 1024
+#define UDP_DST_PORT 1024
+
+
+#define IP_DEFTTL  64   /* from RFC 1340. */
+#define IP_VERSION 0x40
+#define IP_HDRLEN  0x05 /* default IP header length == five 32-bits words. */
+#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN)
+
+static void
+copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt,
+		unsigned offset)
+{
+	struct rte_mbuf *seg;
+	void *seg_buf;
+	unsigned copy_len;
+
+	seg = pkt;
+	while (offset >= seg->pkt.data_len) {
+		offset -= seg->pkt.data_len;
+		seg = seg->pkt.next;
+	}
+	copy_len = seg->pkt.data_len - offset;
+	seg_buf = ((char *) seg->pkt.data + offset);
+	while (len > copy_len) {
+		rte_memcpy(seg_buf, buf, (size_t) copy_len);
+		len -= copy_len;
+		buf = ((char *) buf + copy_len);
+		seg = seg->pkt.next;
+		seg_buf = seg->pkt.data;
+	}
+	rte_memcpy(seg_buf, buf, (size_t) len);
+}
+
+static inline void
+copy_buf_to_pkt(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset)
+{
+	if (offset + len <= pkt->pkt.data_len) {
+		rte_memcpy(((char *) pkt->pkt.data + offset), buf, (size_t) len);
+		return;
+	}
+	copy_buf_to_pkt_segs(buf, len, pkt, offset);
+}
+
+
+void
+initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac,
+		struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id)
+{
+	ether_addr_copy(dst_mac, &eth_hdr->d_addr);
+	ether_addr_copy(src_mac, &eth_hdr->s_addr);
+
+	if (vlan_enabled) {
+		struct vlan_hdr *vhdr = (struct vlan_hdr *)((uint8_t *)eth_hdr +
+				sizeof(struct ether_hdr));
+
+		eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN);
+
+		vhdr->eth_proto =  rte_cpu_to_be_16(ETHER_TYPE_IPv4);
+		vhdr->vlan_tci = van_id;
+	} else {
+		eth_hdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN);
+	}
+
+}
+
+uint16_t
+initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port,
+		uint16_t dst_port, uint16_t pkt_data_len)
+{
+	uint16_t pkt_len;
+
+	pkt_len = (uint16_t) (pkt_data_len + sizeof(struct udp_hdr));
+
+	udp_hdr->src_port = rte_cpu_to_be_16(src_port);
+	udp_hdr->dst_port = rte_cpu_to_be_16(dst_port);
+	udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);
+	udp_hdr->dgram_cksum = 0; /* No UDP checksum. */
+
+	return pkt_len;
+}
+
+
+uint16_t
+initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr,
+		uint8_t *dst_addr, uint16_t pkt_data_len)
+{
+	ip_hdr->vtc_flow = 0;
+	ip_hdr->payload_len = pkt_data_len;
+	ip_hdr->proto = IPPROTO_UDP;
+	ip_hdr->hop_limits = IP_DEFTTL;
+
+	rte_memcpy(ip_hdr->src_addr, src_addr, sizeof(ip_hdr->src_addr));
+	rte_memcpy(ip_hdr->dst_addr, dst_addr, sizeof(ip_hdr->dst_addr));
+
+	return (uint16_t) (pkt_data_len + sizeof(struct ipv6_hdr));
+}
+
+uint16_t
+initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr,
+		uint32_t dst_addr, uint16_t pkt_data_len)
+{
+	uint16_t pkt_len;
+	uint16_t *ptr16;
+	uint32_t ip_cksum;
+
+	/*
+	 * Initialize IP header.
+	 */
+	pkt_len = (uint16_t) (pkt_data_len + sizeof(struct ipv4_hdr));
+
+	ip_hdr->version_ihl   = IP_VHL_DEF;
+	ip_hdr->type_of_service   = 0;
+	ip_hdr->fragment_offset = 0;
+	ip_hdr->time_to_live   = IP_DEFTTL;
+	ip_hdr->next_proto_id = IPPROTO_UDP;
+	ip_hdr->packet_id = 0;
+	ip_hdr->total_length   = rte_cpu_to_be_16(pkt_len);
+	ip_hdr->src_addr = rte_cpu_to_be_32(src_addr);
+	ip_hdr->dst_addr = rte_cpu_to_be_32(dst_addr);
+
+	/*
+	 * Compute IP header checksum.
+	 */
+	ptr16 = (uint16_t *)ip_hdr;
+	ip_cksum = 0;
+	ip_cksum += ptr16[0]; ip_cksum += ptr16[1];
+	ip_cksum += ptr16[2]; ip_cksum += ptr16[3];
+	ip_cksum += ptr16[4];
+	ip_cksum += ptr16[6]; ip_cksum += ptr16[7];
+	ip_cksum += ptr16[8]; ip_cksum += ptr16[9];
+
+	/*
+	 * Reduce 32 bit checksum to 16 bits and complement it.
+	 */
+	ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) +
+		(ip_cksum & 0x0000FFFF);
+	ip_cksum %= 65536;
+	ip_cksum = (~ip_cksum) & 0x0000FFFF;
+	if (ip_cksum == 0)
+		ip_cksum = 0xFFFF;
+	ip_hdr->hdr_checksum = (uint16_t) ip_cksum;
+
+	return pkt_len;
+}
+
+
+
+/*
+ * The maximum number of segments per packet is used when creating
+ * scattered transmit packets composed of a list of mbufs.
+ */
+#define RTE_MAX_SEGS_PER_PKT 255 /**< pkt.nb_segs is a 8-bit unsigned char. */
+
+#define TXONLY_DEF_PACKET_LEN 64
+#define TXONLY_DEF_PACKET_LEN_128 128
+
+uint16_t tx_pkt_length = TXONLY_DEF_PACKET_LEN;
+uint16_t tx_pkt_seg_lengths[RTE_MAX_SEGS_PER_PKT] = {
+		TXONLY_DEF_PACKET_LEN_128,
+};
+
+uint8_t  tx_pkt_nb_segs = 1;
+
+int
+generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst,
+		struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr,
+		uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst)
+{
+	int i, nb_pkt = 0;
+	size_t eth_hdr_size;
+
+	struct rte_mbuf *pkt_seg;
+	struct rte_mbuf *pkt;
+
+	for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) {
+		pkt = rte_pktmbuf_alloc(mp);
+		if (pkt == NULL) {
+nomore_mbuf:
+			if (nb_pkt == 0)
+				return -1;
+			break;
+		}
+
+		pkt->pkt.data_len = tx_pkt_seg_lengths[0];
+		pkt_seg = pkt;
+		for (i = 1; i < tx_pkt_nb_segs; i++) {
+			pkt_seg->pkt.next = rte_pktmbuf_alloc(mp);
+			if (pkt_seg->pkt.next == NULL) {
+				pkt->pkt.nb_segs = i;
+				rte_pktmbuf_free(pkt);
+				goto nomore_mbuf;
+			}
+			pkt_seg = pkt_seg->pkt.next;
+			pkt_seg->pkt.data_len = tx_pkt_seg_lengths[i];
+		}
+		pkt_seg->pkt.next = NULL; /* Last segment of packet. */
+
+		/*
+		 * Copy headers in first packet segment(s).
+		 */
+		if (vlan_enabled)
+			eth_hdr_size = sizeof(struct ether_hdr) + sizeof(struct vlan_hdr);
+		else
+			eth_hdr_size = sizeof(struct ether_hdr);
+
+		copy_buf_to_pkt(eth_hdr, eth_hdr_size, pkt, 0);
+
+		if (ipv4) {
+			copy_buf_to_pkt(ip_hdr, sizeof(struct ipv4_hdr), pkt, eth_hdr_size);
+			copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size +
+					sizeof(struct ipv4_hdr));
+		} else {
+			copy_buf_to_pkt(ip_hdr, sizeof(struct ipv6_hdr), pkt, eth_hdr_size);
+			copy_buf_to_pkt(udp_hdr, sizeof(*udp_hdr), pkt, eth_hdr_size +
+					sizeof(struct ipv6_hdr));
+		}
+
+		/*
+		 * Complete first mbuf of packet and append it to the
+		 * burst of packets to be transmitted.
+		 */
+		pkt->pkt.nb_segs = tx_pkt_nb_segs;
+		pkt->pkt.pkt_len = tx_pkt_length;
+		pkt->pkt.vlan_macip.f.l2_len = eth_hdr_size;
+
+		if (ipv4) {
+			pkt->pkt.vlan_macip.f.vlan_tci  = ETHER_TYPE_IPv4;
+			pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv4_hdr);
+
+			if (vlan_enabled)
+				pkt->ol_flags = PKT_RX_IPV4_HDR | PKT_RX_VLAN_PKT;
+			else
+				pkt->ol_flags = PKT_RX_IPV4_HDR;
+		} else {
+			pkt->pkt.vlan_macip.f.vlan_tci  = ETHER_TYPE_IPv6;
+			pkt->pkt.vlan_macip.f.l3_len = sizeof(struct ipv6_hdr);
+
+			if (vlan_enabled)
+				pkt->ol_flags = PKT_RX_IPV6_HDR | PKT_RX_VLAN_PKT;
+			else
+				pkt->ol_flags = PKT_RX_IPV6_HDR;
+		}
+
+		pkts_burst[nb_pkt] = pkt;
+	}
+
+	return nb_pkt;
+}
+
diff --git a/app/test/packet_burst_generator.h b/app/test/packet_burst_generator.h
new file mode 100644
index 0000000..5b3cd6c
--- /dev/null
+++ b/app/test/packet_burst_generator.h
@@ -0,0 +1,78 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 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 PACKET_BURST_GENERATOR_H_
+#define PACKET_BURST_GENERATOR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_mbuf.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_udp.h>
+
+
+#define IPV4_ADDR(a, b, c, d)(((a & 0xff) << 24) | ((b & 0xff) << 16) | \
+		((c & 0xff) << 8) | (d & 0xff))
+
+
+void
+initialize_eth_header(struct ether_hdr *eth_hdr, struct ether_addr *src_mac,
+		struct ether_addr *dst_mac, uint8_t vlan_enabled, uint16_t van_id);
+
+uint16_t
+initialize_udp_header(struct udp_hdr *udp_hdr, uint16_t src_port,
+		uint16_t dst_port, uint16_t pkt_data_len);
+
+
+uint16_t
+initialize_ipv6_header(struct ipv6_hdr *ip_hdr, uint8_t *src_addr,
+		uint8_t *dst_addr, uint16_t pkt_data_len);
+
+uint16_t
+initialize_ipv4_header(struct ipv4_hdr *ip_hdr, uint32_t src_addr,
+		uint32_t dst_addr, uint16_t pkt_data_len);
+
+int
+generate_packet_burst(struct rte_mempool *mp, struct rte_mbuf **pkts_burst,
+		struct ether_hdr *eth_hdr, uint8_t vlan_enabled, void *ip_hdr,
+		uint8_t ipv4, struct udp_hdr *udp_hdr, int nb_pkt_per_burst);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* PACKET_BURST_GENERATOR_H_ */
diff --git a/app/test/test.h b/app/test/test.h
index f992ceb..574841b 100644
--- a/app/test/test.h
+++ b/app/test/test.h
@@ -96,6 +96,7 @@ int test_distributor(void);
 int test_distributor_perf(void);
 int test_kvargs(void);
 int test_devargs(void);
+int test_link_bonding(void);
 
 int test_pci_run;
 
diff --git a/app/test/test_link_bonding.c b/app/test/test_link_bonding.c
new file mode 100644
index 0000000..f76a6d5
--- /dev/null
+++ b/app/test/test_link_bonding.c
@@ -0,0 +1,3958 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 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 <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_byteorder.h>
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_ethdev.h>
+#include <rte_log.h>
+#include <rte_lcore.h>
+#include <rte_memory.h>
+#include <rte_string_fns.h>
+#include <rte_eth_bond.h>
+
+#include "virtual_pmd.h"
+#include "packet_burst_generator.h"
+
+#include "test.h"
+
+#define TEST_MAX_NUMBER_OF_PORTS (16)
+
+#define RX_RING_SIZE 128
+#define RX_FREE_THRESH 32
+#define RX_PTHRESH 8
+#define RX_HTHRESH 8
+#define RX_WTHRESH 0
+
+#define TX_RING_SIZE 512
+#define TX_FREE_THRESH 32
+#define TX_PTHRESH 32
+#define TX_HTHRESH 0
+#define TX_WTHRESH 0
+#define TX_RSBIT_THRESH 32
+#define TX_Q_FLAGS (ETH_TXQ_FLAGS_NOMULTSEGS | ETH_TXQ_FLAGS_NOVLANOFFL |\
+	ETH_TXQ_FLAGS_NOXSUMSCTP | ETH_TXQ_FLAGS_NOXSUMUDP | \
+	ETH_TXQ_FLAGS_NOXSUMTCP)
+
+#define MBUF_PAYLOAD_SIZE	(2048)
+#define MBUF_SIZE (MBUF_PAYLOAD_SIZE + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
+#define MBUF_CACHE_SIZE (250)
+#define BURST_SIZE (32)
+
+#define DEFAULT_MBUF_DATA_SIZE	(2048)
+#define RTE_TEST_RX_DESC_MAX	(2048)
+#define RTE_TEST_TX_DESC_MAX	(2048)
+#define MAX_PKT_BURST			(512)
+#define DEF_PKT_BURST			(16)
+
+#define BONDED_DEV_NAME			("unit_test_bonded_device")
+
+#define INVALID_SOCKET_ID		(-1)
+#define INVALID_PORT_ID			(-1)
+#define INVALID_BONDING_MODE	(-1)
+
+
+uint8_t slave_mac[] = {0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00 };
+uint8_t bonded_mac[] = {0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF };
+
+struct link_bonding_unittest_params {
+	int8_t bonded_port_id;
+	int8_t slave_port_ids[TEST_MAX_NUMBER_OF_PORTS];
+	uint8_t bonded_slave_count;
+	uint8_t bonding_mode;
+
+	uint16_t nb_rx_q;
+	uint16_t nb_tx_q;
+
+	struct rte_mempool *mbuf_pool;
+
+	struct ether_addr *default_slave_mac;
+	struct ether_addr *default_bonded_mac;
+
+	/* Packet Headers */
+	struct ether_hdr *pkt_eth_hdr;
+	struct ipv4_hdr *pkt_ipv4_hdr;
+	struct ipv6_hdr *pkt_ipv6_hdr;
+	struct udp_hdr *pkt_udp_hdr;
+
+};
+
+static struct ipv4_hdr pkt_ipv4_hdr;
+static struct ipv6_hdr pkt_ipv6_hdr;
+static struct udp_hdr pkt_udp_hdr;
+
+static struct link_bonding_unittest_params default_params  = {
+	.bonded_port_id = -1,
+	.slave_port_ids = { 0 },
+	.bonded_slave_count = 0,
+	.bonding_mode = BONDING_MODE_ROUND_ROBIN,
+
+	.nb_rx_q = 1,
+	.nb_tx_q = 1,
+
+	.mbuf_pool = NULL,
+
+	.default_slave_mac = (struct ether_addr *)slave_mac,
+	.default_bonded_mac = (struct ether_addr *)bonded_mac,
+
+	.pkt_eth_hdr = NULL,
+	.pkt_ipv4_hdr = &pkt_ipv4_hdr,
+	.pkt_ipv6_hdr = &pkt_ipv6_hdr,
+	.pkt_udp_hdr = &pkt_udp_hdr
+
+};
+
+static struct link_bonding_unittest_params *test_params = &default_params;
+
+static uint8_t src_mac[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA };
+static uint8_t dst_mac_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA };
+static uint8_t dst_mac_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAB };
+
+static uint32_t src_addr = IPV4_ADDR(192, 168, 1, 98);
+static uint32_t dst_addr_0 = IPV4_ADDR(192, 168, 1, 98);
+static uint32_t dst_addr_1 = IPV4_ADDR(193, 166, 10, 97);
+
+static uint8_t src_ipv6_addr[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF,
+		0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA  };
+static uint8_t dst_ipv6_addr_0[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF,
+		0xAA, 0xFF, 0xAA,  0xFF, 0xAA , 0xFF, 0xAA, 0xFF, 0xAA  };
+static uint8_t dst_ipv6_addr_1[] = { 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF,
+		0xAA, 0xFF, 0xAA, 0xFF, 0xAA , 0xFF, 0xAA , 0xFF, 0xAB  };
+
+static uint16_t src_port = 1024;
+static uint16_t dst_port_0 = 1024;
+static uint16_t dst_port_1 = 2024;
+
+static uint16_t vlan_id = 0x100;
+
+struct rte_eth_rxmode rx_mode = {
+	.max_rx_pkt_len = ETHER_MAX_LEN, /**< Default maximum frame length. */
+	.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. */
+};
+
+struct rte_fdir_conf fdir_conf = {
+	.mode = RTE_FDIR_MODE_NONE,
+	.pballoc = RTE_FDIR_PBALLOC_64K,
+	.status = RTE_FDIR_REPORT_STATUS,
+	.flexbytes_offset = 0x6,
+	.drop_queue = 127,
+};
+
+static struct rte_eth_conf default_pmd_conf = {
+	.rxmode = {
+		.mq_mode = ETH_MQ_RX_NONE,
+		.max_rx_pkt_len = ETHER_MAX_LEN,
+		.split_hdr_size = 0,
+		.header_split   = 0, /**< Header Split disabled */
+		.hw_ip_checksum = 0, /**< IP checksum offload enabled */
+		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
+		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
+		.hw_strip_crc   = 0, /**< CRC stripped by hardware */
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+	.lpbk_mode = 0,
+};
+
+static const struct rte_eth_rxconf rx_conf_default = {
+	.rx_thresh = {
+		.pthresh = RX_PTHRESH,
+		.hthresh = RX_HTHRESH,
+		.wthresh = RX_WTHRESH,
+	},
+	.rx_free_thresh = RX_FREE_THRESH,
+	.rx_drop_en = 0,
+};
+
+static struct rte_eth_txconf tx_conf_default = {
+	.tx_thresh = {
+		.pthresh = TX_PTHRESH,
+		.hthresh = TX_HTHRESH,
+		.wthresh = TX_WTHRESH,
+	},
+	.tx_free_thresh = TX_FREE_THRESH,
+	.tx_rs_thresh = TX_RSBIT_THRESH,
+	.txq_flags = TX_Q_FLAGS
+
+};
+
+static int
+configure_ethdev(uint8_t port_id, uint8_t start)
+{
+	int q_id;
+
+	if (rte_eth_dev_configure(port_id, test_params->nb_rx_q,
+			test_params->nb_tx_q, &default_pmd_conf) != 0) {
+		goto error;
+	}
+
+	for (q_id = 0; q_id < test_params->nb_rx_q; q_id++) {
+		if (rte_eth_rx_queue_setup(port_id, q_id, RX_RING_SIZE,
+				rte_eth_dev_socket_id(port_id), &rx_conf_default,
+				test_params->mbuf_pool) < 0) {
+			goto error;
+		}
+	}
+
+	for (q_id = 0; q_id < test_params->nb_tx_q; q_id++) {
+		if (rte_eth_tx_queue_setup(port_id, q_id, TX_RING_SIZE,
+				rte_eth_dev_socket_id(port_id), &tx_conf_default) < 0) {
+			printf("Failed to setup tx queue (%d).\n", q_id);
+			goto error;
+		}
+	}
+
+	if (start) {
+		if (rte_eth_dev_start(port_id) < 0) {
+			printf("Failed to start device (%d).\n", port_id);
+			goto error;
+		}
+	}
+	return 0;
+
+error:
+	printf("Failed to configure ethdev %d", port_id);
+	return -1;
+}
+
+
+static int
+test_setup(void)
+{
+	int i, retval, nb_mbuf_per_pool;
+	struct ether_addr *mac_addr = (struct ether_addr *)slave_mac;
+
+	/* Allocate ethernet packet header with space for VLAN header */
+	test_params->pkt_eth_hdr = malloc(sizeof(struct ether_hdr) +
+			sizeof(struct vlan_hdr));
+	if (test_params->pkt_eth_hdr == NULL) {
+		printf("ethernet header struct allocation failed!\n");
+		return -1;
+	}
+
+
+	nb_mbuf_per_pool = RTE_TEST_RX_DESC_MAX + DEF_PKT_BURST +
+			RTE_TEST_TX_DESC_MAX + MAX_PKT_BURST;
+
+	test_params->mbuf_pool = rte_mempool_create("MBUF_POOL", nb_mbuf_per_pool,
+			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 (test_params->mbuf_pool == NULL) {
+		printf("rte_mempool_create failed\n");
+		return -1;
+	}
+
+	/* Create / Initialize virtual eth devs */
+	for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) {
+		char pmd_name[RTE_ETH_NAME_MAX_LEN];
+
+		mac_addr->addr_bytes[ETHER_ADDR_LEN-1] = i;
+
+		rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "test_slave_pmd_%d",i);
+
+		test_params->slave_port_ids[i] = virtual_ethdev_create(pmd_name,
+				mac_addr, rte_socket_id());
+		if (test_params->slave_port_ids[i] < 0) {
+			printf("Failed to create virtual pmd eth device.\n");
+			return -1;
+		}
+
+		retval = configure_ethdev(test_params->slave_port_ids[i], 1);
+		if (retval != 0) {
+			printf("Failed to configure virtual pmd eth device.\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+test_create_bonded_device(void)
+{
+	int retval, current_slave_count;
+
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	test_params->bonded_port_id = rte_eth_bond_create(BONDED_DEV_NAME,
+			test_params->bonding_mode, rte_socket_id());
+	if (test_params->bonded_port_id < 0) {
+		printf("\t Failed to create bonded device.\n");
+		return -1;
+	}
+
+	retval = configure_ethdev(test_params->bonded_port_id, 0);
+	if (retval != 0) {
+		printf("Failed to configure bonded pmd eth device.\n");
+		return -1;
+	}
+
+	current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count > 0) {
+		printf("Number of slaves is great than expected.\n");
+		return -1;
+	}
+
+	current_slave_count = rte_eth_bond_active_slaves_get(
+			test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count > 0) {
+		printf("Number of active slaves is great than expected.\n");
+		return -1;
+	}
+
+
+	if (rte_eth_bond_mode_get(test_params->bonded_port_id) !=
+			test_params->bonding_mode) {
+		printf("Bonded device mode not as expected.\n");
+		return -1;
+
+	}
+
+	return 0;
+}
+
+
+static int
+test_create_bonded_device_with_invalid_params(void)
+{
+	int port_id;
+
+	test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN;
+
+	/* Invalid name */
+	port_id = rte_eth_bond_create(NULL, test_params->bonding_mode,
+			rte_socket_id());
+	if (port_id >= 0) {
+		printf("Created bonded device unexpectedly.\n");
+		return -1;
+	}
+
+	test_params->bonding_mode = INVALID_BONDING_MODE;
+
+	/* Invalid bonding mode */
+	port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode,
+			rte_socket_id());
+	if (port_id >= 0) {
+		printf("Created bonded device unexpectedly.\n");
+		return -1;
+	}
+
+	test_params->bonding_mode = BONDING_MODE_ROUND_ROBIN;
+
+	/* Invalid socket id */
+	port_id = rte_eth_bond_create(BONDED_DEV_NAME, test_params->bonding_mode,
+			INVALID_SOCKET_ID);
+	if (port_id >= 0) {
+		printf("Created bonded device unexpectedly.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_add_slave_to_bonded_device(void)
+{
+	int retval, current_slave_count;
+
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	retval = rte_eth_bond_slave_add(test_params->bonded_port_id,
+			test_params->slave_port_ids[test_params->bonded_slave_count]);
+	if (retval != 0) {
+		printf("Failed to add slave (%d) to bonded port (%d).\n",
+				test_params->bonded_port_id,
+				test_params->slave_port_ids[test_params->bonded_slave_count]);
+		return -1;
+	}
+
+	current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count != test_params->bonded_slave_count + 1) {
+		printf("Number of slaves (%d) is greater than expected (%d).\n",
+				current_slave_count, test_params->bonded_slave_count + 1);
+		return -1;
+	}
+
+	current_slave_count = rte_eth_bond_active_slaves_get(
+			test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count != 0) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				current_slave_count, 0);
+		return -1;
+	}
+
+	test_params->bonded_slave_count++;
+
+	return 0;
+}
+
+static int
+test_add_slave_to_invalid_bonded_device(void)
+{
+	int retval;
+
+	/* Invalid port ID */
+	retval = rte_eth_bond_slave_add(test_params->bonded_port_id + 5,
+			test_params->slave_port_ids[test_params->bonded_slave_count]);
+	if (retval == 0) {
+		printf("Expected call to failed as invalid port specified.\n");
+		return -1;
+	}
+
+	/* Non bonded device */
+	retval = rte_eth_bond_slave_add(test_params->slave_port_ids[0],
+			test_params->slave_port_ids[test_params->bonded_slave_count]);
+	if (retval == 0) {
+		printf("Expected call to failed as invalid port specified.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int
+test_remove_slave_from_bonded_device(void)
+{
+	int retval, current_slave_count;
+	struct ether_addr read_mac_addr, *mac_addr;
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	retval = rte_eth_bond_slave_remove(test_params->bonded_port_id,
+			test_params->slave_port_ids[test_params->bonded_slave_count-1]);
+	if (retval != 0) {
+		printf("\t Failed to remove slave %d from bonded port (%d).\n",
+				test_params->slave_port_ids[test_params->bonded_slave_count-1],
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+
+	current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count != test_params->bonded_slave_count - 1) {
+		printf("Number of slaves (%d) is great than expected (%d).\n",
+				current_slave_count, 0);
+		return -1;
+	}
+
+
+	mac_addr = (struct ether_addr *)slave_mac;
+	mac_addr->addr_bytes[ETHER_ADDR_LEN-1] =
+			test_params->slave_port_ids[test_params->bonded_slave_count-1];
+
+	rte_eth_macaddr_get(
+			test_params->slave_port_ids[test_params->bonded_slave_count-1],
+			&read_mac_addr);
+	if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port mac address not set to that of primary port\n");
+		return -1;
+	}
+
+	rte_eth_stats_reset(
+			test_params->slave_port_ids[test_params->bonded_slave_count-1]);
+
+	virtual_ethdev_simulate_link_status_interrupt(test_params->bonded_port_id,
+			0);
+
+	test_params->bonded_slave_count--;
+
+	return 0;
+}
+
+static int
+test_remove_slave_from_invalid_bonded_device(void)
+{
+	int retval;
+
+	/* Invalid port ID */
+	retval = rte_eth_bond_slave_remove(test_params->bonded_port_id + 5,
+			test_params->slave_port_ids[test_params->bonded_slave_count - 1]);
+	if (retval == 0) {
+		printf("Expected call to failed as invalid port specified.\n");
+		return -1;
+	}
+
+	/* Non bonded device */
+	retval = rte_eth_bond_slave_remove(test_params->slave_port_ids[0],
+			test_params->slave_port_ids[test_params->bonded_slave_count - 1]);
+	if (retval == 0) {
+		printf("Expected call to failed as invalid port specified.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_add_already_bonded_slave_to_bonded_device(void)
+{
+	int retval, port_id, current_slave_count;
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+	char pmd_name[RTE_ETH_NAME_MAX_LEN];
+
+	test_add_slave_to_bonded_device();
+
+	current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count != 1) {
+		printf("Number of slaves (%d) is not that expected (%d).\n",
+				current_slave_count, 1);
+		return -1;
+	}
+
+	rte_snprintf(pmd_name, RTE_ETH_NAME_MAX_LEN, "%s_2",BONDED_DEV_NAME);
+
+	port_id = rte_eth_bond_create(pmd_name, test_params->bonding_mode,
+			rte_socket_id());
+	if (port_id < 0) {
+		printf("Failed to create bonded device.\n");
+		return -1;
+	}
+
+	retval = rte_eth_bond_slave_add(port_id,
+			test_params->slave_port_ids[test_params->bonded_slave_count - 1]);
+	if (retval == 0) {
+		printf("Added slave (%d) to bonded port (%d) unexpectedly.\n",
+				test_params->slave_port_ids[test_params->bonded_slave_count-1],
+				port_id);
+		return -1;
+	}
+
+	return test_remove_slave_from_bonded_device();
+}
+
+
+static int
+test_get_slaves_from_bonded_device(void)
+{
+	int retval, current_slave_count;
+
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	retval = test_add_slave_to_bonded_device();
+	if (retval != 0)
+		return -1;
+
+	/* Invalid port id */
+	current_slave_count = rte_eth_bond_slaves_get(INVALID_PORT_ID, slaves,
+			RTE_MAX_ETHPORTS);
+	if (current_slave_count >= 0)
+		return -1;
+
+	current_slave_count = rte_eth_bond_active_slaves_get(INVALID_PORT_ID,
+			slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count >= 0)
+		return -1;
+
+	/* Invalid slaves pointer */
+	current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id,
+			NULL, RTE_MAX_ETHPORTS);
+	if (current_slave_count >= 0)
+		return -1;
+
+	current_slave_count = rte_eth_bond_active_slaves_get(
+			test_params->bonded_port_id, NULL, RTE_MAX_ETHPORTS);
+	if (current_slave_count >= 0)
+		return -1;
+
+	/* non bonded device*/
+	current_slave_count = rte_eth_bond_slaves_get(
+			test_params->slave_port_ids[0], NULL, RTE_MAX_ETHPORTS);
+	if (current_slave_count >= 0)
+		return -1;
+
+	current_slave_count = rte_eth_bond_active_slaves_get(
+			test_params->slave_port_ids[0],	NULL, RTE_MAX_ETHPORTS);
+	if (current_slave_count >= 0)
+		return -1;
+
+	retval = test_remove_slave_from_bonded_device();
+	if (retval != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int
+test_add_remove_multiple_slaves_to_from_bonded_device(void)
+{
+	int i;
+
+	for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) {
+		if (test_add_slave_to_bonded_device() != 0)
+			return -1;
+	}
+
+	for (i = 0; i < TEST_MAX_NUMBER_OF_PORTS; i++) {
+		if (test_remove_slave_from_bonded_device() != 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+static void
+enable_bonded_slaves(void)
+{
+	int i;
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		virtual_ethdev_simulate_link_status_interrupt(
+				test_params->slave_port_ids[i], 1);
+	}
+}
+
+static int
+test_start_bonded_device(void)
+{
+	struct rte_eth_link link_status;
+
+	int retval, current_slave_count, current_bonding_mode, primary_port;
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	/* Add slave to bonded device*/
+	if (test_add_slave_to_bonded_device() != 0)
+		return -1;
+
+	retval = rte_eth_dev_start(test_params->bonded_port_id);
+	if (retval != 0) {
+		printf("Failed to start bonded pmd eth device.\n");
+		return -1;
+	}
+
+	/* Change link status of virtual pmd so it will be added to the active
+	 * slave list of the bonded device*/
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[test_params->bonded_slave_count-1], 1);
+
+	current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count != test_params->bonded_slave_count) {
+		printf("Number of slaves (%d) is not expected value (%d).\n",
+				current_slave_count, test_params->bonded_slave_count);
+		return -1;
+	}
+
+	current_slave_count = rte_eth_bond_active_slaves_get(
+			test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count != test_params->bonded_slave_count) {
+		printf("Number of active slaves (%d) is not expected value (%d).\n",
+				current_slave_count, test_params->bonded_slave_count);
+		return -1;
+	}
+
+	current_bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id);
+	if (current_bonding_mode != test_params->bonding_mode) {
+		printf("Bonded device mode (%d) is not expected value (%d).\n",
+				current_bonding_mode, test_params->bonding_mode);
+		return -1;
+
+	}
+
+	primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id);
+	if (primary_port != test_params->slave_port_ids[0]) {
+		printf("Primary port (%d) is not expected value (%d).\n",
+				primary_port, test_params->slave_port_ids[0]);
+		return -1;
+
+	}
+
+	rte_eth_link_get(test_params->bonded_port_id, &link_status);
+	if (!link_status.link_status) {
+		printf("Bonded port (%d) status (%d) is not expected value (%d).\n",
+				test_params->bonded_port_id, link_status.link_status, 1);
+		return -1;
+
+	}
+
+	return 0;
+}
+
+static int
+test_stop_bonded_device(void)
+{
+	int current_slave_count;
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	struct rte_eth_link link_status;
+
+	rte_eth_dev_stop(test_params->bonded_port_id);
+
+	rte_eth_link_get(test_params->bonded_port_id, &link_status);
+	if (link_status.link_status) {
+		printf("Bonded port (%d) status (%d) is not expected value (%d).\n",
+				test_params->bonded_port_id, link_status.link_status, 0);
+		return -1;
+	}
+
+	current_slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count != test_params->bonded_slave_count) {
+		printf("Number of slaves (%d) is not expected value (%d).\n",
+				current_slave_count, test_params->bonded_slave_count);
+		return -1;
+	}
+
+	current_slave_count = rte_eth_bond_active_slaves_get(
+			test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS);
+	if (current_slave_count != 0) {
+		printf("Number of active slaves (%d) is not expected value (%d).\n",
+				current_slave_count, 0);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int remove_slaves_and_stop_bonded_device(void)
+{
+	/* Clean up and remove slaves from bonded device */
+	while (test_params->bonded_slave_count > 0) {
+		if (test_remove_slave_from_bonded_device() != 0) {
+			printf("test_remove_slave_from_bonded_device failed\n");
+			return -1;
+		}
+	}
+
+	rte_eth_dev_stop(test_params->bonded_port_id);
+	rte_eth_stats_reset(test_params->bonded_port_id);
+	rte_eth_bond_mac_address_reset(test_params->bonded_port_id);
+
+	return 0;
+}
+
+static int
+test_set_bonding_mode(void)
+{
+	int i;
+	int retval, bonding_mode;
+
+	int bonding_modes[] = { BONDING_MODE_ROUND_ROBIN,
+							BONDING_MODE_ACTIVE_BACKUP,
+							BONDING_MODE_BALANCE,
+							BONDING_MODE_BROADCAST };
+
+	/* Test supported link bonding modes */
+	for (i = 0; i < (int)RTE_DIM(bonding_modes);	i++) {
+		/* Invalid port ID */
+		retval = rte_eth_bond_mode_set(INVALID_PORT_ID, bonding_modes[i]);
+		if (retval == 0) {
+			printf("Expected call to failed as invalid port (%d) specified.\n",
+					INVALID_PORT_ID);
+			return -1;
+		}
+
+		/* Non bonded device */
+		retval = rte_eth_bond_mode_set(test_params->slave_port_ids[0],
+				bonding_modes[i]);
+		if (retval == 0) {
+			printf("Expected call to failed as invalid port (%d) specified.\n",
+					test_params->slave_port_ids[0]);
+			return -1;
+		}
+
+		retval = rte_eth_bond_mode_set(test_params->bonded_port_id,
+				bonding_modes[i]);
+		if (retval != 0) {
+			printf("Failed to set link bonding mode on port (%d) to (%d).\n",
+					test_params->bonded_port_id, bonding_modes[i]);
+			return -1;
+		}
+
+		bonding_mode = rte_eth_bond_mode_get(test_params->bonded_port_id);
+		if (bonding_mode != bonding_modes[i]) {
+			printf("Link bonding mode (%d) of port (%d) is not expected value (%d).\n",
+					bonding_mode, test_params->bonded_port_id,
+					bonding_modes[i]);
+			return -1;
+		}
+
+
+		/* Invalid port ID */
+		bonding_mode = rte_eth_bond_mode_get(INVALID_PORT_ID);
+		if (bonding_mode >= 0) {
+			printf("Expected call to failed as invalid port (%d) specified.\n",
+					INVALID_PORT_ID);
+			return -1;
+		}
+
+
+		/* Non bonded device */
+		bonding_mode = rte_eth_bond_mode_get(test_params->slave_port_ids[0]);
+		if (bonding_mode >= 0) {
+			printf("Expected call to failed as invalid port (%d) specified.\n",
+					test_params->slave_port_ids[0]);
+			return -1;
+		}
+
+	}
+
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_set_primary_slave(void)
+{
+	int i, j, retval;
+	struct ether_addr read_mac_addr;
+	struct ether_addr *expected_mac_addr;
+
+	/* Add 4 slaves to bonded device */
+	for (i = test_params->bonded_slave_count; i < 4; i++) {
+		retval = test_add_slave_to_bonded_device();
+		if (retval != 0) {
+			printf("Failed to add slave to bonded device.\n");
+			return -1;
+		}
+	}
+	retval = rte_eth_bond_mode_set(test_params->bonded_port_id,
+			BONDING_MODE_ROUND_ROBIN);
+	if (retval != 0) {
+		printf("Failed to set link bonding mode on port (%d) to (%d).\n",
+				test_params->bonded_port_id, BONDING_MODE_ROUND_ROBIN);
+		return -1;
+	}
+
+	/* Invalid port ID */
+	retval = rte_eth_bond_primary_set(INVALID_PORT_ID,
+			test_params->slave_port_ids[i]);
+	if (retval == 0) {
+		printf("Expected call to failed as invalid port specified.\n");
+		return -1;
+	}
+
+	/* Set slave as primary
+	 * Verify slave it is now primary slave
+	 * Verify that MAC address of bonded device is that of primary slave
+	 * Verify that MAC address of all bonded slaves are that of primary slave
+	 */
+	for (i = 0; i < 4; i++) {
+
+		/* Non bonded device */
+		retval = rte_eth_bond_primary_set(test_params->slave_port_ids[i],
+				test_params->slave_port_ids[i]);
+		if (retval == 0) {
+			printf("Expected call to failed as invalid port specified.\n");
+			return -1;
+		}
+
+		retval = rte_eth_bond_primary_set(test_params->bonded_port_id,
+				test_params->slave_port_ids[i]);
+		if (retval != 0) {
+			printf("Failed to set bonded port (%d) primary port to (%d)\n",
+					test_params->bonded_port_id,
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+
+		retval = rte_eth_bond_primary_get(test_params->bonded_port_id);
+		if (retval < 0) {
+			printf("Failed to read primary port from bonded port (%d)\n",
+					test_params->bonded_port_id);
+			return -1;
+		} else if (retval != test_params->slave_port_ids[i]) {
+			printf("Bonded port (%d) primary port (%d) not expected value (%d)\n",
+					test_params->bonded_port_id, retval,
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+
+		/* stop/start bonded eth dev to apply new MAC */
+		rte_eth_dev_stop(test_params->bonded_port_id);
+		if (rte_eth_dev_start(test_params->bonded_port_id) != 0)
+			return -1;
+
+		expected_mac_addr = (struct ether_addr *)&slave_mac;
+		expected_mac_addr->addr_bytes[ETHER_ADDR_LEN-1] =
+				test_params->slave_port_ids[i];
+
+		/* Check primary slave MAC */
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(expected_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) {
+			printf("bonded port mac address not set to that of primary port\n");
+			return -1;
+		}
+
+		/* Check bonded MAC */
+		rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+		if (memcmp(&read_mac_addr, &read_mac_addr, sizeof(read_mac_addr))) {
+			printf("bonded port mac address not set to that of primary port\n");
+			return -1;
+		}
+
+		/* Check other slaves MACs */
+		for (j = 0; j < 4; j++) {
+			if (j != i) {
+				rte_eth_macaddr_get(test_params->slave_port_ids[j],
+						&read_mac_addr);
+				if (memcmp(expected_mac_addr, &read_mac_addr,
+						sizeof(read_mac_addr))) {
+					printf("slave port mac address not set to that of primary port\n");
+					return -1;
+				}
+			}
+		}
+	}
+
+
+	/* Test with none existent port */
+	retval = rte_eth_bond_primary_get(test_params->bonded_port_id + 10);
+	if (retval >= 0) {
+		printf("read primary port from expectedly\n");
+		return -1;
+	}
+
+	/* Test with slave port */
+	retval = rte_eth_bond_primary_get(test_params->slave_port_ids[0]);
+	if (retval >= 0) {
+		printf("read primary port from expectedly\n");
+		return -1;
+	}
+
+	if (remove_slaves_and_stop_bonded_device() != 0)
+		return -1;
+
+	/* No slaves  */
+	retval = rte_eth_bond_primary_get(test_params->bonded_port_id);
+	if (retval >= 0) {
+		printf("read primary port from expectedly\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_set_explicit_bonded_mac(void)
+{
+	int i, retval;
+	struct ether_addr read_mac_addr;
+	struct ether_addr *mac_addr;
+
+	uint8_t explicit_bonded_mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 };
+
+	mac_addr = (struct ether_addr *)explicit_bonded_mac;
+
+	/* Invalid port ID */
+	retval = rte_eth_bond_mac_address_set(INVALID_PORT_ID, mac_addr);
+	if (retval == 0) {
+		printf("Expected call to failed as invalid port specified.\n");
+		return -1;
+	}
+
+	/* Non bonded device */
+	retval = rte_eth_bond_mac_address_set(test_params->slave_port_ids[0],
+			mac_addr);
+	if (retval == 0) {
+		printf("Expected call to failed as invalid port specified.\n");
+		return -1;
+	}
+
+	/* NULL MAC address */
+	retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id, NULL);
+	if (retval == 0) {
+		printf("Expected call to failed as NULL MAC specified\n");
+		return -1;
+	}
+
+	retval = rte_eth_bond_mac_address_set(test_params->bonded_port_id,
+			mac_addr);
+	if (retval != 0) {
+		printf("Failed to set MAC address on bonded port (%d)\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	/* Add 4 slaves to bonded device */
+	for (i = test_params->bonded_slave_count; i < 4; i++) {
+		retval = test_add_slave_to_bonded_device();
+		if (retval != 0) {
+			printf("Failed to add slave to bonded device.\n");
+			return -1;
+		}
+	}
+
+	/* Check bonded MAC */
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port mac address not set to that of primary port\n");
+		return -1;
+	}
+
+	/* Check other slaves MACs */
+	for (i = 0; i < 4; i++) {
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(mac_addr, &read_mac_addr, sizeof(read_mac_addr))) {
+			printf("slave port mac address not set to that of primary port\n");
+			return -1;
+		}
+	}
+
+	/* test resetting mac address on bonded device */
+	if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) {
+		printf("Failed to reset MAC address on bonded port (%d)\n",
+				test_params->bonded_port_id);
+
+		return -1;
+	}
+
+	if (rte_eth_bond_mac_address_reset(test_params->slave_port_ids[0]) == 0) {
+		printf("Reset MAC address on bonded port (%d) unexpectedly\n",
+				test_params->slave_port_ids[1]);
+
+		return -1;
+	}
+
+	/* test resetting mac address on bonded device with no slaves */
+
+	if (remove_slaves_and_stop_bonded_device() != 0)
+		return -1;
+
+	if (rte_eth_bond_mac_address_reset(test_params->bonded_port_id) != 0) {
+		printf("Failed to reset MAC address on bonded port (%d)\n",
+				test_params->bonded_port_id);
+
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int
+initialize_bonded_device_with_slaves(uint8_t bonding_mode,
+		uint8_t number_of_slaves, uint8_t enable_slave)
+{
+	int retval;
+
+	/* configure bonded device */
+	retval = configure_ethdev(test_params->bonded_port_id, 0);
+	if (retval != 0) {
+		printf("Failed to configure bonding port (%d) in mode %d with (%d) slaves.\n",
+				test_params->bonded_port_id, bonding_mode, number_of_slaves);
+		return -1;
+	}
+
+	while (number_of_slaves > test_params->bonded_slave_count) {
+		/* Add slaves to bonded device */
+		retval = test_add_slave_to_bonded_device();
+		if (retval != 0) {
+			printf("Failed to add slave (%d to  bonding port (%d).\n",
+					test_params->bonded_slave_count - 1,
+					test_params->bonded_port_id);
+			return -1;
+		}
+	}
+
+	/* Set link bonding mode  */
+	retval = rte_eth_bond_mode_set(test_params->bonded_port_id, bonding_mode);
+	if (retval != 0) {
+		printf("Failed to set link bonding mode on port (%d) to (%d).\n",
+				test_params->bonded_port_id, bonding_mode);
+		return -1;
+	}
+
+	retval = rte_eth_dev_start(test_params->bonded_port_id);
+	if (retval != 0) {
+		printf("Failed to start bonded pmd eth device.\n");
+		return -1;
+	}
+
+	if (enable_slave)
+		enable_bonded_slaves();
+
+	return 0;
+}
+
+static int
+test_adding_slave_after_bonded_device_started(void)
+{
+	int i;
+
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 0) !=
+			0)
+		return -1;
+
+	/* Enabled slave devices */
+	for (i = 0; i < test_params->bonded_slave_count + 1; i++) {
+		virtual_ethdev_simulate_link_status_interrupt(
+				test_params->slave_port_ids[i], 1);
+	}
+
+	if (rte_eth_bond_slave_add(test_params->bonded_port_id,
+			test_params->slave_port_ids[test_params->bonded_slave_count]) !=
+					0) {
+		printf("\t Failed to add slave to bonded port.\n");
+		return -1;
+	}
+
+	test_params->bonded_slave_count++;
+
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+generate_test_burst(struct rte_mbuf **pkts_burst, uint16_t burst_size,
+		uint8_t vlan, uint8_t ipv4, uint8_t toggle_dst_mac,
+		uint8_t toggle_ip_addr, uint8_t toggle_udp_port)
+{
+	uint16_t pktlen, generated_burst_size;
+	void *ip_hdr;
+
+	if (toggle_dst_mac)
+		initialize_eth_header(test_params->pkt_eth_hdr,
+				(struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1,
+				vlan, vlan_id);
+	else
+		initialize_eth_header(test_params->pkt_eth_hdr,
+				(struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0,
+				vlan, vlan_id);
+
+
+	if (toggle_udp_port)
+		pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port,
+				dst_port_1, 64);
+	else
+		pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port,
+				dst_port_0, 64);
+
+	if (ipv4) {
+		if (toggle_ip_addr)
+			pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr,
+					dst_addr_1, pktlen);
+		else
+			pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr,
+					dst_addr_0, pktlen);
+
+		ip_hdr = test_params->pkt_ipv4_hdr;
+	} else {
+		if (toggle_ip_addr)
+			pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr,
+					(uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_1,
+					pktlen);
+		else
+			pktlen = initialize_ipv6_header(test_params->pkt_ipv6_hdr,
+					(uint8_t *)src_ipv6_addr, (uint8_t *)dst_ipv6_addr_0,
+					pktlen);
+
+		ip_hdr = test_params->pkt_ipv6_hdr;
+	}
+
+	/* Generate burst of packets to transmit */
+	generated_burst_size = generate_packet_burst(test_params->mbuf_pool,
+			pkts_burst,	test_params->pkt_eth_hdr, vlan, ip_hdr, ipv4,
+			test_params->pkt_udp_hdr, burst_size);
+	if (generated_burst_size != burst_size) {
+		printf("Failed to generate packet burst");
+		return -1;
+	}
+
+	return generated_burst_size;
+}
+
+/** Round Robin Mode Tests */
+
+static int
+test_roundrobin_tx_burst(void)
+{
+	int i, burst_size, nb_tx;
+	struct rte_mbuf *pkt_burst[MAX_PKT_BURST];
+	struct rte_eth_stats port_stats;
+
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 2, 1)
+			!= 0)
+		return -1;
+
+	burst_size = 20 * test_params->bonded_slave_count;
+
+	if (burst_size > MAX_PKT_BURST) {
+		printf("Burst size specified is greater than supported.\n");
+		return -1;
+	}
+
+	/* Generate test bursts of packets to transmit */
+	if (generate_test_burst(pkt_burst, burst_size, 0, 1, 0, 0, 0) != burst_size)
+		return -1;
+
+	/* Send burst on bonded port */
+	nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst,
+			burst_size);
+	if (nb_tx != burst_size)
+		return -1;
+
+	/* Verify bonded port tx stats */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size) {
+		printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id, (unsigned int)port_stats.opackets,
+				burst_size);
+		return -1;
+	}
+
+	/* Verify slave ports tx stats */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats);
+		if (port_stats.opackets !=
+				(uint64_t)burst_size / test_params->bonded_slave_count) {
+			printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+					test_params->bonded_port_id,
+					(unsigned int)port_stats.opackets,
+					burst_size / test_params->bonded_slave_count);
+			return -1;
+		}
+	}
+
+	/* Put all slaves down and try and transmit */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		virtual_ethdev_simulate_link_status_interrupt(
+				test_params->slave_port_ids[i], 0);
+	}
+
+	/* Send burst on bonded port */
+	nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkt_burst,
+			burst_size);
+	if (nb_tx != 0)
+		return -1;
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_roundrobin_rx_burst_on_single_slave(void)
+{
+	struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL };
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL };
+
+	struct rte_eth_stats port_stats;
+
+	int i, j, nb_rx, burst_size = 25;
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) !=
+			0)
+		return -1;
+
+	/* Generate test bursts of packets to transmit */
+	if (generate_test_burst(gen_pkt_burst, burst_size, 0, 1, 0, 0, 0) !=
+			burst_size)
+		return -1;
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		/* Add rx data to slave */
+		virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i],
+				&gen_pkt_burst[0], burst_size);
+
+		/* Call rx burst on bonded device */
+		/* Send burst on bonded port */
+		nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst,
+				MAX_PKT_BURST);
+		if (nb_rx != burst_size) {
+			printf("round-robin rx burst failed");
+			return -1;
+		}
+
+		/* Verify bonded device rx count */
+		rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+		if (port_stats.ipackets != (uint64_t)burst_size) {
+			printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n",
+					test_params->bonded_port_id,
+					(unsigned int)port_stats.ipackets, burst_size);
+			return -1;
+		}
+
+
+		/* Verify bonded slave devices rx count */
+		/* Verify slave ports tx stats */
+		for (j = 0; j < test_params->bonded_slave_count; j++) {
+			rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats);
+
+			if (i == j) {
+				if (port_stats.ipackets != (uint64_t)burst_size) {
+					printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+							test_params->slave_port_ids[i],
+							(unsigned int)port_stats.ipackets, burst_size);
+					return -1;
+				}
+			} else {
+				if (port_stats.ipackets != 0) {
+					printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+							test_params->slave_port_ids[i],
+							(unsigned int)port_stats.ipackets, 0);
+					return -1;
+				}
+			}
+
+			/* Reset bonded slaves stats */
+			rte_eth_stats_reset(test_params->slave_port_ids[j]);
+		}
+		/* reset bonded device stats */
+		rte_eth_stats_reset(test_params->bonded_port_id);
+	}
+
+	/* free mbufs */
+	for (i = 0; i < MAX_PKT_BURST; i++) {
+		if (gen_pkt_burst[i] != NULL)
+			rte_pktmbuf_free(gen_pkt_burst[i]);
+
+		if (rx_pkt_burst[i] != NULL)
+			rte_pktmbuf_free(rx_pkt_burst[i]);
+	}
+
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+#define TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT (3)
+
+static int
+test_roundrobin_rx_burst_on_multiple_slaves(void)
+{
+	struct rte_mbuf *gen_pkt_burst[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST];
+
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL };
+	struct rte_eth_stats port_stats;
+
+	int burst_size[TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT] = { 15, 13, 36 };
+	int i, nb_rx;
+
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) !=
+			0)
+		return -1;
+
+	/* Generate test bursts of packets to transmit */
+	for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) {
+		if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 1, 0, 0,
+				0) != burst_size[i])
+			return -1;
+	}
+
+	/* Add rx data to slaves */
+	for (i = 0; i < TEST_ROUNDROBIN_TX_BURST_SLAVE_COUNT; i++) {
+		virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i],
+				&gen_pkt_burst[i][0], burst_size[i]);
+	}
+
+	/* Call rx burst on bonded device */
+	/* Send burst on bonded port */
+	nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst,
+			MAX_PKT_BURST);
+	if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) {
+		printf("round-robin rx burst failed (%d != %d)\n", nb_rx,
+				burst_size[0] + burst_size[1] + burst_size[2]);
+		return -1;
+	}
+
+	/* Verify bonded device rx count */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] +
+			burst_size[2])) {
+		printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id, (unsigned int)port_stats.ipackets,
+				burst_size[0] + burst_size[1] + burst_size[2]);
+		return -1;
+	}
+
+
+	/* Verify bonded slave devices rx counts */
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size[0]) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[0],
+				(unsigned int)port_stats.ipackets, burst_size[0]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size[1]) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[1],
+				(unsigned int)port_stats.ipackets, burst_size[1]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size[2]) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[2],
+				(unsigned int)port_stats.ipackets, burst_size[2]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats);
+	if (port_stats.ipackets != 0) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[3],
+				(unsigned int)port_stats.ipackets, 0);
+		return -1;
+	}
+
+	/* free mbufs */
+	for (i = 0; i < MAX_PKT_BURST; i++) {
+		if (rx_pkt_burst[i] != NULL)
+			rte_pktmbuf_free(rx_pkt_burst[i]);
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_roundrobin_verify_mac_assignment(void)
+{
+	struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_2;
+
+	int i, retval;
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0);
+	rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_2);
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1)
+			!= 0)
+		return -1;
+
+	/* Verify that all MACs are the same as first slave added to bonded dev */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(&expected_mac_addr_0, &read_mac_addr,
+				sizeof(read_mac_addr))) {
+			printf("slave port (%d) mac address not set to that of primary port\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* change primary and verify that MAC addresses haven't changed */
+	retval = rte_eth_bond_primary_set(test_params->bonded_port_id,
+			test_params->slave_port_ids[2]);
+	if (retval != 0) {
+		printf("Failed to set bonded port (%d) primary port to (%d)\n",
+				test_params->bonded_port_id, test_params->slave_port_ids[i]);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(&expected_mac_addr_0, &read_mac_addr,
+				sizeof(read_mac_addr))) {
+			printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* stop / start bonded device and verify that primary MAC address is
+	 * propagate to bonded device and slaves */
+
+	rte_eth_dev_stop(test_params->bonded_port_id);
+
+	if (rte_eth_dev_start(test_params->bonded_port_id) != 0)
+		return -1;
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&expected_mac_addr_2, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of new primary port\n",
+				test_params->slave_port_ids[i]);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(&expected_mac_addr_2, &read_mac_addr,
+				sizeof(read_mac_addr))) {
+			printf("slave port (%d) mac address not set to that of new primary port\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* Set explicit MAC address */
+	if (rte_eth_bond_mac_address_set(test_params->bonded_port_id,
+			(struct ether_addr *)bonded_mac) != 0) {
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of new primary port\n",
+				test_params->slave_port_ids[i]);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) {
+			printf("slave port (%d) mac address not set to that of new primary port\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_roundrobin_verify_promiscuous_enable_disable(void)
+{
+	int i, promiscuous_en;
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN, 4, 1) !=
+			0)
+		return -1;
+
+	rte_eth_promiscuous_enable(test_params->bonded_port_id);
+
+	promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id);
+	if (promiscuous_en != 1) {
+		printf("Port (%d) promiscuous mode not enabled\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]);
+		if (promiscuous_en != 1) {
+			printf("slave port (%d) promiscuous mode not enabled\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	rte_eth_promiscuous_disable(test_params->bonded_port_id);
+
+	promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id);
+	if (promiscuous_en != 0) {
+		printf("Port (%d) promiscuous mode not disabled\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		promiscuous_en = rte_eth_promiscuous_get(test_params->slave_port_ids[i]);
+		if (promiscuous_en != 0) {
+			printf("slave port (%d) promiscuous mode not disabled\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+#define TEST_RR_LINK_STATUS_SLAVE_COUNT (4)
+#define TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT (2)
+
+static int
+test_roundrobin_verify_slave_link_status_change_behaviour(void)
+{
+	struct rte_mbuf *tx_pkt_burst[MAX_PKT_BURST] = { NULL };
+	struct rte_mbuf *gen_pkt_burst[TEST_RR_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST];
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL };
+
+	struct rte_eth_stats port_stats;
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	int i, burst_size, slave_count;
+
+	/* NULL all pointers in array to simplify cleanup */
+	memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst));
+
+	/* Initialize bonded device with TEST_RR_LINK_STATUS_SLAVE_COUNT slaves
+	 * in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ROUND_ROBIN,
+			TEST_RR_LINK_STATUS_SLAVE_COUNT, 1) != 0)
+		return -1;
+
+	/* Verify Current Slaves Count /Active Slave Count is */
+	slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves,
+			RTE_MAX_ETHPORTS);
+	if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) {
+		printf("Number of slaves (%d) is not as expected (%d).\n", slave_count,
+				TEST_RR_LINK_STATUS_SLAVE_COUNT);
+		return -1;
+	}
+
+	slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (slave_count != TEST_RR_LINK_STATUS_SLAVE_COUNT) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, TEST_RR_LINK_STATUS_SLAVE_COUNT);
+		return -1;
+	}
+
+	/* Set 2 slaves eth_devs link status to down */
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[1], 0);
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[3], 0);
+
+	if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS) !=
+					TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT);
+		return -1;
+	}
+
+	burst_size = 21;
+
+	/* Verify that pkts are not sent on slaves with link status down:
+	 *
+	 * 1. Generate test burst of traffic
+	 * 2. Transmit burst on bonded eth_dev
+	 * 3. Verify stats for bonded eth_dev (opackets = burst_size)
+	 * 4. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0)
+	 */
+	if (generate_test_burst(tx_pkt_burst, burst_size, 0, 1, 0, 0, 0) !=
+			burst_size) {
+		printf("generate_test_burst failed\n");
+		return -1;
+	}
+
+	if (rte_eth_tx_burst(test_params->bonded_port_id, 0, tx_pkt_burst,
+			burst_size) != burst_size) {
+		printf("rte_eth_tx_burst failed\n");
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.opackets != 11) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats);
+	if (port_stats.opackets != 10) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[2]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[3]);
+		return -1;
+	}
+
+	/* Verify that pkts are not sent on slaves with link status down:
+	 *
+	 * 1. Generate test bursts of traffic
+	 * 2. Add bursts on to virtual eth_devs
+	 * 3. Rx burst on bonded eth_dev, expected (burst_ size *
+	 *    TEST_RR_LINK_STATUS_EXPECTED_ACTIVE_SLAVE_COUNT) received
+	 * 4. Verify stats for bonded eth_dev
+	 * 6. Verify stats for slave eth_devs (s0 = 11, s1 = 0, s2 = 10, s3 = 0)
+	 */
+	for (i = 0; i < TEST_RR_LINK_STATUS_SLAVE_COUNT; i++) {
+		if (generate_test_burst(&gen_pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0)
+				!= burst_size) {
+			return -1;
+		}
+		virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i],
+				&gen_pkt_burst[i][0], burst_size);
+	}
+
+	if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst,
+			MAX_PKT_BURST) != burst_size + burst_size) {
+		printf("rte_eth_rx_burst\n");
+		return -1;
+	}
+
+	/* Verify bonded device rx count */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) {
+		printf("(%d) port_stats.ipackets not as expected\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	/* free mbufs */
+	for (i = 0; i < MAX_PKT_BURST; i++) {
+		if (rx_pkt_burst[i] != NULL)
+			rte_pktmbuf_free(rx_pkt_burst[i]);
+
+		if (gen_pkt_burst[1][i] != NULL)
+			rte_pktmbuf_free(gen_pkt_burst[1][i]);
+
+		if (gen_pkt_burst[3][i] != NULL)
+			rte_pktmbuf_free(gen_pkt_burst[1][i]);
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+/** Active Backup Mode Tests */
+
+static int
+test_activebackup_tx_burst(void)
+{
+	int i, retval, pktlen, primary_port, burst_size, generated_burst_size, nb_tx;
+	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
+	struct rte_eth_stats port_stats;
+
+	retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1);
+	if (retval != 0) {
+		printf("Failed to initialize_bonded_device_with_slaves.\n");
+		return -1;
+	}
+
+	initialize_eth_header(test_params->pkt_eth_hdr,
+			(struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0);
+	pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port,
+			dst_port_0, 16);
+	pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr,
+			dst_addr_0, pktlen);
+
+	burst_size = 20 * test_params->bonded_slave_count;
+
+	if (burst_size > MAX_PKT_BURST) {
+		printf("Burst size specified is greater than supported.\n");
+		return -1;
+	}
+
+	/* Generate a burst of packets to transmit */
+	generated_burst_size = generate_packet_burst(test_params->mbuf_pool,
+			pkts_burst,	test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr,
+			1, test_params->pkt_udp_hdr, burst_size);
+	if (generated_burst_size != burst_size)
+		return -1;
+
+	/* Send burst on bonded port */
+	nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst,
+			burst_size);
+	if (nb_tx != burst_size)
+		return -1;
+
+	/* Verify bonded port tx stats */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size) {
+		printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id, (unsigned int)port_stats.opackets,
+				burst_size);
+		return -1;
+	}
+
+	primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id);
+
+	/* Verify slave ports tx stats */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats);
+		if (test_params->slave_port_ids[i] == primary_port) {
+			if (port_stats.opackets != (uint64_t)burst_size) {
+				printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+						test_params->bonded_port_id,
+						(unsigned int)port_stats.opackets,
+						burst_size / test_params->bonded_slave_count);
+				return -1;
+			}
+		} else {
+			if (port_stats.opackets != 0) {
+				printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+						test_params->bonded_port_id,
+						(unsigned int)port_stats.opackets, 0);
+				return -1;
+			}
+		}
+	}
+
+	/* Put all slaves down and try and transmit */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+
+		virtual_ethdev_simulate_link_status_interrupt(
+				test_params->slave_port_ids[i], 0);
+	}
+
+	/* Send burst on bonded port */
+	nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst,
+			burst_size);
+	if (nb_tx != 0)
+		return -1;
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+#define TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT (4)
+
+static int
+test_activebackup_rx_burst(void)
+{
+	struct rte_mbuf *gen_pkt_burst[MAX_PKT_BURST] = { NULL };
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL };
+
+	struct rte_eth_stats port_stats;
+
+	int primary_port;
+
+	int i, j, nb_rx, burst_size = 17;
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP,
+			TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1)
+			!= 0)
+		return -1;
+
+
+	primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id);
+	if (primary_port < 0) {
+		printf("failed to get primary slave for bonded port (%d)",
+				test_params->bonded_port_id);
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		/* Generate test bursts of packets to transmit */
+		if (generate_test_burst(&gen_pkt_burst[0], burst_size, 0, 1, 0, 0, 0)
+				!= burst_size) {
+			return -1;
+		}
+
+		/* Add rx data to slave */
+		virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i],
+				&gen_pkt_burst[0], burst_size);
+
+		/* Call rx burst on bonded device */
+		nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0,
+				&rx_pkt_burst[0], MAX_PKT_BURST);
+		if (nb_rx < 0) {
+			printf("rte_eth_rx_burst failed\n");
+			return -1;
+		}
+
+		if (test_params->slave_port_ids[i] == primary_port) {
+			/* Verify bonded device rx count */
+			rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+			if (port_stats.ipackets != (uint64_t)burst_size) {
+				printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n",
+						test_params->bonded_port_id,
+						(unsigned int)port_stats.ipackets, burst_size);
+				return -1;
+			}
+
+			/* Verify bonded slave devices rx count */
+			for (j = 0; j < test_params->bonded_slave_count; j++) {
+				rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats);
+				if (i == j) {
+					if (port_stats.ipackets != (uint64_t)burst_size) {
+						printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+								test_params->slave_port_ids[i],
+								(unsigned int)port_stats.ipackets, burst_size);
+						return -1;
+					}
+				} else {
+					if (port_stats.ipackets != 0) {
+						printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+								test_params->slave_port_ids[i],
+								(unsigned int)port_stats.ipackets, 0);
+						return -1;
+					}
+				}
+			}
+		} else {
+			for (j = 0; j < test_params->bonded_slave_count; j++) {
+				rte_eth_stats_get(test_params->slave_port_ids[j], &port_stats);
+				if (port_stats.ipackets != 0) {
+					printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+							test_params->slave_port_ids[i],
+							(unsigned int)port_stats.ipackets, 0);
+					return -1;
+				}
+			}
+		}
+
+		/* free mbufs */
+		for (i = 0; i < MAX_PKT_BURST; i++) {
+			if (rx_pkt_burst[i] != NULL) {
+				rte_pktmbuf_free(rx_pkt_burst[i]);
+				rx_pkt_burst[i] = NULL;
+			}
+		}
+
+		/* reset bonded device stats */
+		rte_eth_stats_reset(test_params->bonded_port_id);
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_activebackup_verify_promiscuous_enable_disable(void)
+{
+	int i, primary_port, promiscuous_en;
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 4, 1)
+			!= 0)
+		return -1;
+
+	primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id);
+	if (primary_port < 0) {
+		printf("failed to get primary slave for bonded port (%d)",
+				test_params->bonded_port_id);
+	}
+
+	rte_eth_promiscuous_enable(test_params->bonded_port_id);
+
+	promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id);
+	if (promiscuous_en != 1) {
+		printf("Port (%d) promiscuous mode not enabled\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		promiscuous_en = rte_eth_promiscuous_get(
+				test_params->slave_port_ids[i]);
+		if (primary_port == test_params->slave_port_ids[i]) {
+			if (promiscuous_en != 1) {
+				printf("slave port (%d) promiscuous mode not enabled\n",
+						test_params->slave_port_ids[i]);
+				return -1;
+			}
+		} else {
+			if (promiscuous_en != 0) {
+				printf("slave port (%d) promiscuous mode enabled\n",
+						test_params->slave_port_ids[i]);
+				return -1;
+			}
+		}
+
+	}
+
+	rte_eth_promiscuous_disable(test_params->bonded_port_id);
+
+	promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id);
+	if (promiscuous_en != 0) {
+		printf("Port (%d) promiscuous mode not disabled\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		promiscuous_en = rte_eth_promiscuous_get(
+				test_params->slave_port_ids[i]);
+		if (promiscuous_en != 0) {
+			printf("slave port (%d) promiscuous mode not disabled\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_activebackup_verify_mac_assignment(void)
+{
+	struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1;
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0);
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1);
+
+	/* Initialize bonded device with 2 slaves in active backup mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP, 2, 1)
+			!= 0)
+		return -1;
+
+	/* Verify that bonded MACs is that of first slave and that the other slave
+	 * MAC hasn't been changed */
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of primary port\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of primary port\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not as expected\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	/* change primary and verify that MAC addresses haven't changed */
+	if (rte_eth_bond_primary_set(test_params->bonded_port_id,
+			test_params->slave_port_ids[1]) != 0) {
+		printf("Failed to set bonded port (%d) primary port to (%d)\n",
+				test_params->bonded_port_id, test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of primary port\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of primary port\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not as expected\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	/* stop / start bonded device and verify that primary MAC address is
+	 * propagated to bonded device and slaves */
+
+	rte_eth_dev_stop(test_params->bonded_port_id);
+
+	if (rte_eth_dev_start(test_params->bonded_port_id) != 0)
+		return -1;
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of primary port\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not as expected\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of primary port\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	/* Set explicit MAC address */
+	if (rte_eth_bond_mac_address_set(test_params->bonded_port_id,
+			(struct ether_addr *)bonded_mac) != 0) {
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of bonded port\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not as expected\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr);
+	if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of bonded port\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_activebackup_verify_slave_link_status_change_failover(void)
+{
+	struct rte_mbuf *pkt_burst[TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST];
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL };
+	struct rte_eth_stats port_stats;
+
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	int i, j, burst_size, slave_count, primary_port;
+
+	burst_size = 21;
+
+	memset(pkt_burst, 0, sizeof(pkt_burst));
+
+	/* Generate packet burst for testing */
+	if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) !=
+			burst_size) {
+		printf("generate_test_burst failed\n");
+		return -1;
+	}
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP,
+			TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT, 1)
+			!= 0)
+		return -1;
+
+	/* Verify Current Slaves Count /Active Slave Count is */
+	slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves,
+			RTE_MAX_ETHPORTS);
+	if (slave_count != 4) {
+		printf("Number of slaves (%d) is not as expected (%d).\n",
+				slave_count, 4);
+		return -1;
+	}
+
+	slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (slave_count != 4) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, 4);
+		return -1;
+	}
+
+	primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id);
+	if (primary_port != test_params->slave_port_ids[0])
+		printf("Primary port not as expected");
+
+	/* Bring 2 slaves down and verify active slave count */
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[1], 0);
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[3], 0);
+
+	if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves,
+			RTE_MAX_ETHPORTS) != 2) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, 2);
+		return -1;
+	}
+
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[1], 1);
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[3], 1);
+
+
+	/* Bring primary port down, verify that active slave count is 3 and primary
+	 *  has changed */
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[0], 0);
+
+	if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves,
+			RTE_MAX_ETHPORTS) != 3) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, 3);
+		return -1;
+	}
+
+	primary_port = rte_eth_bond_primary_get(test_params->bonded_port_id);
+	if (primary_port != test_params->slave_port_ids[2])
+		printf("Primary port not as expected");
+
+	/* Verify that pkts are sent on new primary slave */
+
+	if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0], burst_size)
+			!= burst_size) {
+		printf("rte_eth_tx_burst failed\n");
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[2]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+	rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[3]);
+		return -1;
+	}
+
+	/* Generate packet burst for testing */
+
+	for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) {
+		if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) !=
+				burst_size)
+			return -1;
+
+		virtual_ethdev_add_mbufs_to_rx_queue(
+			test_params->slave_port_ids[i], &pkt_burst[i][0], burst_size);
+	}
+
+	if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst,
+			MAX_PKT_BURST) != burst_size) {
+		printf("rte_eth_rx_burst\n");
+		return -1;
+	}
+
+	/* Verify bonded device rx count */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size) {
+		printf("(%d) port_stats.ipackets not as expected\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[2]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[3]);
+		return -1;
+	}
+
+	/* free mbufs */
+
+	for (i = 0; i < TEST_ACTIVE_BACKUP_RX_BURST_SLAVE_COUNT; i++) {
+		for (j = 0; j < MAX_PKT_BURST; j++) {
+			if (pkt_burst[i][j] != NULL) {
+				rte_pktmbuf_free(pkt_burst[i][j]);
+				pkt_burst[i][j] = NULL;
+			}
+		}
+	}
+
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+/** Balance Mode Tests */
+
+static int
+test_balance_xmit_policy_configuration(void)
+{
+	int retval;
+
+	retval = initialize_bonded_device_with_slaves(BONDING_MODE_ACTIVE_BACKUP,
+			2, 1);
+	if (retval != 0) {
+		printf("Failed to initialize_bonded_device_with_slaves.\n");
+		return -1;
+	}
+
+	/* Invalid port id */
+	retval = rte_eth_bond_xmit_policy_set(INVALID_PORT_ID,
+			BALANCE_XMIT_POLICY_LAYER2);
+	if (retval == 0) {
+		printf("Expected call to failed as invalid port specified.\n");
+		return -1;
+	}
+
+	/* Set xmit policy on non bonded device */
+	retval = rte_eth_bond_xmit_policy_set(test_params->slave_port_ids[0],
+			BALANCE_XMIT_POLICY_LAYER2);
+	if (retval == 0) {
+		printf("Expected call to failed as invalid port specified.\n");
+		return -1;
+	}
+
+	retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id,
+			BALANCE_XMIT_POLICY_LAYER2);
+	if (retval != 0) {
+		printf("Failed to set balance xmit policy.\n");
+		return -1;
+	}
+	if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) !=
+			BALANCE_XMIT_POLICY_LAYER2) {
+		printf("balance xmit policy not as expected.\n");
+		return -1;
+	}
+
+	retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id,
+			BALANCE_XMIT_POLICY_LAYER23);
+	if (retval != 0) {
+		printf("Failed to set balance xmit policy.\n");
+		return -1;
+	}
+	if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) !=
+			BALANCE_XMIT_POLICY_LAYER23) {
+		printf("balance xmit policy not as expected.\n");
+		return -1;
+	}
+
+	retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id,
+			BALANCE_XMIT_POLICY_LAYER34);
+	if (retval != 0) {
+		printf("Failed to set balance xmit policy.\n");
+		return -1;
+	}
+	if (rte_eth_bond_xmit_policy_get(test_params->bonded_port_id) !=
+			BALANCE_XMIT_POLICY_LAYER34) {
+		printf("balance xmit policy not as expected.\n");
+		return -1;
+	}
+
+	/* Invalid port id */
+	if (rte_eth_bond_xmit_policy_get(INVALID_PORT_ID) >= 0)
+		return -1;
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+#define TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT (2)
+
+static int
+test_balance_l2_tx_burst(void)
+{
+	struct rte_mbuf *pkts_burst[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT][MAX_PKT_BURST];
+	int burst_size[TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT] = { 10, 15 };
+
+	uint16_t pktlen;
+
+	int retval, i;
+	struct rte_eth_stats port_stats;
+
+	retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE,
+			TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT, 1);
+	if (retval != 0) {
+		printf("Failed to initialize_bonded_device_with_slaves.\n");
+		return -1;
+	}
+
+	retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id,
+			BALANCE_XMIT_POLICY_LAYER2);
+	if (retval != 0) {
+		printf("Failed to set balance xmit policy.\n");
+		return -1;
+	}
+
+
+	initialize_eth_header(test_params->pkt_eth_hdr,
+			(struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0);
+	pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port,
+			dst_port_0, 16);
+	pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr,
+			dst_addr_0, pktlen);
+
+	/* Generate a burst 1 of packets to transmit */
+	if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[0][0],
+			test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1,
+			test_params->pkt_udp_hdr, burst_size[0]) != burst_size[0])
+		return -1;
+
+	initialize_eth_header(test_params->pkt_eth_hdr,
+			(struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_1, 0, 0);
+
+	/* Generate a burst 2 of packets to transmit */
+	if (generate_packet_burst(test_params->mbuf_pool, &pkts_burst[1][0],
+			test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr, 1,
+			test_params->pkt_udp_hdr, burst_size[1]) != burst_size[1])
+		return -1;
+
+	/* Send burst 1 on bonded port */
+	for (i = 0; i < TEST_BALANCE_L2_TX_BURST_SLAVE_COUNT; i++) {
+		if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[i][0],
+			burst_size[i]) != burst_size[i])
+			return -1;
+	}
+	/* Verify bonded port tx stats */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)(burst_size[0] + burst_size[1])) {
+		printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id,
+				(unsigned int)port_stats.opackets, burst_size[0] + burst_size[1]);
+		return -1;
+	}
+
+
+	/* Verify slave ports tx stats */
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size[0]) {
+		printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[0],
+				(unsigned int)port_stats.opackets, burst_size[0]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size[1]) {
+		printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[1], (
+						unsigned int)port_stats.opackets, burst_size[1]);
+		return -1;
+	}
+
+	/* Put all slaves down and try and transmit */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+
+		virtual_ethdev_simulate_link_status_interrupt(
+				test_params->slave_port_ids[i], 0);
+	}
+
+	/* Send burst on bonded port */
+	if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkts_burst[0][0],
+			burst_size[0]) != 0)
+		return -1;
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+balance_l23_tx_burst(uint8_t vlan_enabled, uint8_t ipv4,
+		uint8_t toggle_mac_addr, uint8_t toggle_ip_addr)
+{
+	int retval, i;
+	int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2;
+
+	struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST];
+	struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST];
+
+	struct rte_eth_stats port_stats;
+
+	retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1);
+	if (retval != 0) {
+		printf("Failed to initialize_bonded_device_with_slaves.\n");
+		return -1;
+	}
+
+	retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id,
+			BALANCE_XMIT_POLICY_LAYER23);
+	if (retval != 0) {
+		printf("Failed to set balance xmit policy.\n");
+		return -1;
+	}
+
+	burst_size_1 = 20;
+	burst_size_2 = 10;
+
+	if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) {
+		printf("Burst size specified is greater than supported.\n");
+		return -1;
+	}
+
+	/* Generate test bursts of packets to transmit */
+	if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4,
+			0, 0, 0) != burst_size_1)
+		return -1;
+
+	if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4,
+			toggle_mac_addr, toggle_ip_addr, 0) != burst_size_2)
+		return -1;
+
+	/* Send burst 1 on bonded port */
+	nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1,
+			burst_size_1);
+	if (nb_tx_1 != burst_size_1)
+		return -1;
+
+	/* Send burst 2 on bonded port */
+	nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2,
+			burst_size_2);
+	if (nb_tx_2 != burst_size_2)
+		return -1;
+
+	/* Verify bonded port tx stats */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) {
+		printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id,
+				(unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2);
+		return -1;
+	}
+
+	/* Verify slave ports tx stats */
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.opackets != (uint64_t)nb_tx_1) {
+		printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[0],
+				(unsigned int)port_stats.opackets, nb_tx_1);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.opackets != (uint64_t)nb_tx_2) {
+		printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[1],
+				(unsigned int)port_stats.opackets, nb_tx_2);
+		return -1;
+	}
+
+	/* Put all slaves down and try and transmit */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+
+		virtual_ethdev_simulate_link_status_interrupt(
+				test_params->slave_port_ids[i], 0);
+	}
+
+	/* Send burst on bonded port */
+	nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1,
+			burst_size_1);
+	if (nb_tx_1 != 0)
+		return -1;
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_balance_l23_tx_burst_ipv4_toggle_ip_addr(void)
+{
+	return balance_l23_tx_burst(0, 1, 1, 0);
+}
+
+static int
+test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr(void)
+{
+	return balance_l23_tx_burst(1, 1, 0, 1);
+}
+
+static int
+test_balance_l23_tx_burst_ipv6_toggle_ip_addr(void)
+{
+	return balance_l23_tx_burst(0, 0, 0, 1);
+}
+
+static int
+test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr(void)
+{
+	return balance_l23_tx_burst(1, 0, 0, 1);
+}
+
+static int
+test_balance_l23_tx_burst_toggle_mac_addr(void)
+{
+	return balance_l23_tx_burst(0, 0, 1, 0);
+}
+
+static int
+balance_l34_tx_burst(uint8_t vlan_enabled, uint8_t ipv4,
+		uint8_t toggle_mac_addr, uint8_t toggle_ip_addr,
+		uint8_t toggle_udp_port)
+{
+	int retval, i;
+	int burst_size_1, burst_size_2, nb_tx_1, nb_tx_2;
+
+	struct rte_mbuf *pkts_burst_1[MAX_PKT_BURST];
+	struct rte_mbuf *pkts_burst_2[MAX_PKT_BURST];
+
+	struct rte_eth_stats port_stats;
+
+	retval = initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1);
+	if (retval != 0) {
+		printf("Failed to initialize_bonded_device_with_slaves.\n");
+		return -1;
+	}
+
+	retval = rte_eth_bond_xmit_policy_set(test_params->bonded_port_id,
+			BALANCE_XMIT_POLICY_LAYER34);
+	if (retval != 0) {
+		printf("Failed to set balance xmit policy.\n");
+		return -1;
+	}
+
+	burst_size_1 = 20;
+	burst_size_2 = 10;
+
+	if (burst_size_1 > MAX_PKT_BURST || burst_size_2 > MAX_PKT_BURST) {
+		printf("Burst size specified is greater than supported.\n");
+		return -1;
+	}
+
+	/* Generate test bursts of packets to transmit */
+	if (generate_test_burst(pkts_burst_1, burst_size_1, vlan_enabled, ipv4, 0,
+			0, 0) != burst_size_1)
+		return -1;
+
+	if (generate_test_burst(pkts_burst_2, burst_size_2, vlan_enabled, ipv4,
+			toggle_mac_addr, toggle_ip_addr, toggle_udp_port) != burst_size_2)
+		return -1;
+
+	/* Send burst 1 on bonded port */
+	nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1,
+			burst_size_1);
+	if (nb_tx_1 != burst_size_1)
+		return -1;
+
+	/* Send burst 2 on bonded port */
+	nb_tx_2 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_2,
+			burst_size_2);
+	if (nb_tx_2 != burst_size_2)
+		return -1;
+
+
+	/* Verify bonded port tx stats */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)(nb_tx_1 + nb_tx_2)) {
+		printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id,
+				(unsigned int)port_stats.opackets, nb_tx_1 + nb_tx_2);
+		return -1;
+	}
+
+	/* Verify slave ports tx stats */
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.opackets != (uint64_t)nb_tx_1) {
+		printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[0],
+				(unsigned int)port_stats.opackets, nb_tx_1);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.opackets != (uint64_t)nb_tx_2) {
+		printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[1],
+				(unsigned int)port_stats.opackets, nb_tx_2);
+		return -1;
+	}
+
+	/* Put all slaves down and try and transmit */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+
+		virtual_ethdev_simulate_link_status_interrupt(
+				test_params->slave_port_ids[i], 0);
+	}
+
+	/* Send burst on bonded port */
+	nb_tx_1 = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst_1,
+			burst_size_1);
+	if (nb_tx_1 != 0)
+		return -1;
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_balance_l34_tx_burst_ipv4_toggle_ip_addr(void)
+{
+	return balance_l34_tx_burst(0, 1, 0, 1, 0);
+}
+
+static int
+test_balance_l34_tx_burst_ipv4_toggle_udp_port(void)
+{
+	return balance_l34_tx_burst(0, 1, 0, 0, 1);
+}
+
+static int
+test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr(void)
+{
+	return balance_l34_tx_burst(1, 1, 0, 1, 0);
+}
+
+static int
+test_balance_l34_tx_burst_ipv6_toggle_ip_addr(void)
+{
+	return balance_l34_tx_burst(0, 0, 0, 1, 0);
+}
+
+static int
+test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr(void)
+{
+	return balance_l34_tx_burst(1, 0, 0, 1, 0);
+}
+
+static int
+test_balance_l34_tx_burst_ipv6_toggle_udp_port(void)
+{
+	return balance_l34_tx_burst(0, 0, 0, 0, 1);
+}
+
+#define TEST_BALANCE_RX_BURST_SLAVE_COUNT (3)
+
+static int
+test_balance_rx_burst(void)
+{
+	struct rte_mbuf *gen_pkt_burst[TEST_BALANCE_RX_BURST_SLAVE_COUNT][MAX_PKT_BURST];
+
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL };
+	struct rte_eth_stats port_stats;
+
+	int burst_size[TEST_BALANCE_RX_BURST_SLAVE_COUNT] = { 10, 5, 30 };
+	int i, j, nb_rx;
+
+	memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst));
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 3, 1)
+			!= 0)
+		return -1;
+
+	/* Generate test bursts of packets to transmit */
+	for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) {
+		if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1,
+				0, 0) != burst_size[i])
+			return -1;
+	}
+	/* Add rx data to slaves */
+	for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) {
+		virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i],
+				&gen_pkt_burst[i][0], burst_size[i]);
+	}
+
+	/* Call rx burst on bonded device */
+	/* Send burst on bonded port */
+	nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst,
+			MAX_PKT_BURST);
+	if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) {
+		printf("balance rx burst failed\n");
+		return -1;
+	}
+
+	/* Verify bonded device rx count */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] +
+			burst_size[2])) {
+		printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id, (unsigned int)port_stats.ipackets,
+				burst_size[0] + burst_size[1] + burst_size[2]);
+		return -1;
+	}
+
+
+	/* Verify bonded slave devices rx counts */
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size[0]) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[0],
+				(unsigned int)port_stats.ipackets, burst_size[0]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size[1]) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[1],
+				(unsigned int)port_stats.ipackets, burst_size[1]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size[2]) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[2],
+				(unsigned int)port_stats.ipackets, burst_size[2]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats);
+	if (port_stats.ipackets != 0) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[3],
+				(unsigned int)port_stats.ipackets, 0);
+		return -1;
+	}
+
+	/* free mbufs */
+	for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) {
+		for (j = 0; j < MAX_PKT_BURST; j++) {
+			if (gen_pkt_burst[i][j] != NULL) {
+				rte_pktmbuf_free(gen_pkt_burst[i][j]);
+				gen_pkt_burst[i][j] = NULL;
+			}
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_balance_verify_promiscuous_enable_disable(void)
+{
+	int i, promiscuous_en;
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0)
+		return -1;
+
+	rte_eth_promiscuous_enable(test_params->bonded_port_id);
+
+	promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id);
+	if (promiscuous_en != 1) {
+		printf("Port (%d) promiscuous mode not enabled\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		promiscuous_en = rte_eth_promiscuous_get(
+				test_params->slave_port_ids[i]);
+		if (promiscuous_en != 1) {
+			printf("slave port (%d) promiscuous mode not enabled\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	rte_eth_promiscuous_disable(test_params->bonded_port_id);
+
+	promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id);
+	if (promiscuous_en != 0) {
+		printf("Port (%d) promiscuous mode not disabled\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		promiscuous_en = rte_eth_promiscuous_get(
+				test_params->slave_port_ids[i]);
+		if (promiscuous_en != 0) {
+			printf("slave port (%d) promiscuous mode not disabled\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_balance_verify_mac_assignment(void)
+{
+	struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1;
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0);
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &expected_mac_addr_1);
+
+	/* Initialize bonded device with 2 slaves in active backup mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 2, 1) != 0)
+		return -1;
+
+	/* Verify that bonded MACs is that of first slave and that the other slave
+	 * MAC hasn't been changed */
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of primary port\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of primary port\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of primary port\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	/* change primary and verify that MAC addresses haven't changed */
+	if (rte_eth_bond_primary_set(test_params->bonded_port_id,
+			test_params->slave_port_ids[1]) != 0) {
+		printf("Failed to set bonded port (%d) primary port to (%d)\n",
+				test_params->bonded_port_id, test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of primary port\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of primary port\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_0, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of primary port\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	/* stop / start bonded device and verify that primary MAC address is
+	 * propagated to bonded device and slaves */
+
+	rte_eth_dev_stop(test_params->bonded_port_id);
+
+	if (rte_eth_dev_start(test_params->bonded_port_id) != 0)
+		return -1;
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of primary port\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of primary port\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr);
+	if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of primary port\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	/* Set explicit MAC address */
+	if (rte_eth_bond_mac_address_set(test_params->bonded_port_id,
+			(struct ether_addr *)bonded_mac) != 0)
+		return -1;
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of bonded port\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &read_mac_addr);
+	if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not as expected\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[1], &read_mac_addr);
+	if (memcmp(&bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("slave port (%d) mac address not set to that of bonded port\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+#define TEST_BALANCE_LINK_STATUS_SLAVE_COUNT (4)
+
+static int
+test_balance_verify_slave_link_status_change_behaviour(void)
+{
+	struct rte_mbuf *pkt_burst[TEST_BALANCE_LINK_STATUS_SLAVE_COUNT][MAX_PKT_BURST];
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL };
+	struct rte_eth_stats port_stats;
+
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	int i, j, burst_size, slave_count;
+
+	memset(pkt_burst, 0, sizeof(pkt_burst));
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE,
+			TEST_BALANCE_LINK_STATUS_SLAVE_COUNT, 1) != 0)
+		return -1;
+
+	if (rte_eth_bond_xmit_policy_set(test_params->bonded_port_id,
+			BALANCE_XMIT_POLICY_LAYER2)) {
+		printf("Failed to set balance xmit policy.\n");
+		return -1;
+	}
+
+	/* Verify Current Slaves Count /Active Slave Count is */
+	slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves,
+			RTE_MAX_ETHPORTS);
+	if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) {
+		printf("Number of slaves (%d) is not as expected (%d).\n", slave_count,
+				TEST_BALANCE_LINK_STATUS_SLAVE_COUNT);
+		return -1;
+	}
+
+	slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (slave_count != TEST_BALANCE_LINK_STATUS_SLAVE_COUNT) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, TEST_BALANCE_LINK_STATUS_SLAVE_COUNT);
+		return -1;
+	}
+
+	/* Set 2 slaves link status to down */
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[1], 0);
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[3], 0);
+
+	if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS) != 2) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, 2);
+		return -1;
+	}
+
+	/* Send to sets of packet burst and verify that they are balanced across
+	 *  slaves */
+	burst_size = 21;
+
+	if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 1, 0, 0, 0) !=
+			burst_size) {
+		printf("generate_test_burst failed\n");
+		return -1;
+	}
+
+	if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) !=
+			burst_size) {
+		printf("generate_test_burst failed\n");
+		return -1;
+	}
+
+	if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0],
+			burst_size) != burst_size) {
+		printf("rte_eth_tx_burst failed\n");
+		return -1;
+	}
+
+	if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0],
+			burst_size) != burst_size) {
+		printf("rte_eth_tx_burst failed\n");
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) {
+		printf("(%d) port_stats.opackets (%d) not as expected (%d).\n",
+				test_params->bonded_port_id, (int)port_stats.opackets,
+				burst_size + burst_size);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size) {
+		printf("(%d) port_stats.opackets (%d) not as expected (%d).\n",
+				test_params->slave_port_ids[0], (int)port_stats.opackets,
+				burst_size);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size) {
+		printf("(%d) port_stats.opackets (%d) not as expected (%d).\n",
+				test_params->slave_port_ids[2], (int)port_stats.opackets,
+				burst_size);
+		return -1;
+	}
+
+	/* verify that all packets get send on primary slave when no other slaves
+	 * are available */
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[2], 0);
+
+	if (rte_eth_bond_active_slaves_get(test_params->bonded_port_id, slaves,
+			RTE_MAX_ETHPORTS) != 1) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, 1);
+		return -1;
+	}
+
+	if (generate_test_burst(&pkt_burst[1][0], burst_size, 0, 1, 1, 0, 0) !=
+			burst_size) {
+		printf("generate_test_burst failed\n");
+		return -1;
+	}
+
+	if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[1][0],
+			burst_size) != burst_size) {
+		printf("rte_eth_tx_burst failed\n");
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)(burst_size + burst_size +
+			burst_size)) {
+		printf("(%d) port_stats.opackets (%d) not as expected (%d).\n",
+				test_params->bonded_port_id, (int)port_stats.opackets,
+				burst_size + burst_size + burst_size);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.opackets != (uint64_t)(burst_size + burst_size)) {
+		printf("(%d) port_stats.opackets (%d) not as expected (%d).\n",
+				test_params->slave_port_ids[0], (int)port_stats.opackets,
+				burst_size + burst_size);
+		return -1;
+	}
+
+
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[0], 0);
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[1], 1);
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[2], 1);
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[3], 1);
+
+	for (i = 0; i < TEST_BALANCE_LINK_STATUS_SLAVE_COUNT; i++) {
+		if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 1, 0, 0, 0) !=
+				burst_size)
+			return -1;
+
+		virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i],
+				&pkt_burst[i][0], burst_size);
+	}
+
+
+
+	/* Verify that pkts are not received on slaves with link status down */
+
+	rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst,
+			MAX_PKT_BURST);
+
+	/* Verify bonded device rx count */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.ipackets != (uint64_t)(burst_size * 3)) {
+		printf("(%d) port_stats.ipackets (%d) not as expected (%d)\n",
+				test_params->bonded_port_id, (int)port_stats.ipackets,
+				burst_size * 3);
+		return -1;
+	}
+
+	/* free mbufs allocate for rx testing */
+	for (i = 0; i < TEST_BALANCE_RX_BURST_SLAVE_COUNT; i++) {
+		for (j = 0; j < MAX_PKT_BURST; j++) {
+			if (pkt_burst[i][j] != NULL) {
+				rte_pktmbuf_free(pkt_burst[i][j]);
+				pkt_burst[i][j] = NULL;
+			}
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+/** Broadcast Mode Tests */
+
+static int
+test_broadcast_tx_burst(void)
+{
+	int i, pktlen, retval, burst_size, generated_burst_size, nb_tx;
+	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
+
+	struct rte_eth_stats port_stats;
+
+	retval = initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 2, 1);
+	if (retval != 0) {
+		printf("Failed to initialize_bonded_device_with_slaves.\n");
+		return -1;
+	}
+
+	initialize_eth_header(test_params->pkt_eth_hdr,
+			(struct ether_addr *)src_mac, (struct ether_addr *)dst_mac_0, 0, 0);
+
+	pktlen = initialize_udp_header(test_params->pkt_udp_hdr, src_port,
+			dst_port_0, 16);
+	pktlen = initialize_ipv4_header(test_params->pkt_ipv4_hdr, src_addr,
+			dst_addr_0, pktlen);
+
+	burst_size = 20 * test_params->bonded_slave_count;
+
+	if (burst_size > MAX_PKT_BURST) {
+		printf("Burst size specified is greater than supported.\n");
+		return -1;
+	}
+
+	/* Generate a burst of packets to transmit */
+	generated_burst_size = generate_packet_burst(test_params->mbuf_pool,
+			pkts_burst,	test_params->pkt_eth_hdr, 0, test_params->pkt_ipv4_hdr,
+			1, test_params->pkt_udp_hdr, burst_size);
+	if (generated_burst_size != burst_size)
+		return -1;
+
+	/* Send burst on bonded port */
+	nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst,
+			burst_size);
+	if (nb_tx != burst_size * test_params->bonded_slave_count) {
+		printf("Bonded Port (%d) rx burst failed, packets transmitted value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id,
+				nb_tx, burst_size);
+		return -1;
+	}
+
+	/* Verify bonded port tx stats */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size *
+			test_params->bonded_slave_count) {
+		printf("Bonded Port (%d) opackets value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id, (unsigned int)port_stats.opackets,
+				burst_size);
+	}
+
+	/* Verify slave ports tx stats */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_stats_get(test_params->slave_port_ids[i], &port_stats);
+		if (port_stats.opackets != (uint64_t)burst_size) {
+			printf("Slave Port (%d) opackets value (%u) not as expected (%d)\n",
+					test_params->bonded_port_id,
+					(unsigned int)port_stats.opackets, burst_size);
+		}
+	}
+
+	/* Put all slaves down and try and transmit */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+
+		virtual_ethdev_simulate_link_status_interrupt(
+				test_params->slave_port_ids[i], 0);
+	}
+
+	/* Send burst on bonded port */
+	nb_tx = rte_eth_tx_burst(test_params->bonded_port_id, 0, pkts_burst,
+			burst_size);
+	if (nb_tx != 0)
+		return -1;
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+#define BROADCAST_RX_BURST_NUM_OF_SLAVES (3)
+
+static int
+test_broadcast_rx_burst(void)
+{
+	struct rte_mbuf *gen_pkt_burst[BROADCAST_RX_BURST_NUM_OF_SLAVES][MAX_PKT_BURST];
+
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL };
+	struct rte_eth_stats port_stats;
+
+	int burst_size[BROADCAST_RX_BURST_NUM_OF_SLAVES] = { 10, 5, 30 };
+	int i, j, nb_rx;
+
+	memset(gen_pkt_burst, 0, sizeof(gen_pkt_burst));
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 3, 1) != 0)
+		return -1;
+
+
+	/* Generate test bursts of packets to transmit */
+	for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) {
+		if (generate_test_burst(&gen_pkt_burst[i][0], burst_size[i], 0, 0, 1, 0,
+				0) != burst_size[i])
+			return -1;
+	}
+
+	/* Add rx data to slave 0 */
+	for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) {
+		virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i],
+				&gen_pkt_burst[i][0], burst_size[i]);
+	}
+
+
+	/* Call rx burst on bonded device */
+	/* Send burst on bonded port */
+	nb_rx = rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst,
+			MAX_PKT_BURST);
+	if (nb_rx != burst_size[0] + burst_size[1] + burst_size[2]) {
+		printf("round-robin rx burst failed");
+		return -1;
+	}
+
+	/* Verify bonded device rx count */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.ipackets != (uint64_t)(burst_size[0] + burst_size[1] +
+			burst_size[2])) {
+		printf("Bonded Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->bonded_port_id, (unsigned int)port_stats.ipackets,
+				burst_size[0] + burst_size[1] + burst_size[2]);
+		return -1;
+	}
+
+
+	/* Verify bonded slave devices rx counts */
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size[0]) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[0],
+				(unsigned int)port_stats.ipackets, burst_size[0]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size[1]) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[1],
+				(unsigned int)port_stats.ipackets, burst_size[1]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats);
+	if (port_stats.ipackets != (uint64_t)burst_size[2]) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[2],
+				(unsigned int)port_stats.ipackets,
+				burst_size[2]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats);
+	if (port_stats.ipackets != 0) {
+		printf("Slave Port (%d) ipackets value (%u) not as expected (%d)\n",
+				test_params->slave_port_ids[3],
+				(unsigned int)port_stats.ipackets, 0);
+		return -1;
+	}
+
+	/* free mbufs allocate for rx testing */
+	for (i = 0; i < BROADCAST_RX_BURST_NUM_OF_SLAVES; i++) {
+		for (j = 0; j < MAX_PKT_BURST; j++) {
+			if (gen_pkt_burst[i][j] != NULL) {
+				rte_pktmbuf_free(gen_pkt_burst[i][j]);
+				gen_pkt_burst[i][j] = NULL;
+			}
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_broadcast_verify_promiscuous_enable_disable(void)
+{
+	int i, promiscuous_en;
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_BALANCE, 4, 1) != 0)
+		return -1;
+
+	rte_eth_promiscuous_enable(test_params->bonded_port_id);
+
+	promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id);
+	if (promiscuous_en != 1) {
+		printf("Port (%d) promiscuous mode not enabled\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		promiscuous_en = rte_eth_promiscuous_get(
+				test_params->slave_port_ids[i]);
+		if (promiscuous_en != 1) {
+			printf("slave port (%d) promiscuous mode not enabled\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	rte_eth_promiscuous_disable(test_params->bonded_port_id);
+
+	promiscuous_en = rte_eth_promiscuous_get(test_params->bonded_port_id);
+	if (promiscuous_en != 0) {
+		printf("Port (%d) promiscuous mode not disabled\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		promiscuous_en = rte_eth_promiscuous_get(
+				test_params->slave_port_ids[i]);
+		if (promiscuous_en != 0) {
+			printf("slave port (%d) promiscuous mode not disabled\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_broadcast_verify_mac_assignment(void)
+{
+	struct ether_addr read_mac_addr, expected_mac_addr_0, expected_mac_addr_1;
+
+	int i, retval;
+
+	rte_eth_macaddr_get(test_params->slave_port_ids[0], &expected_mac_addr_0);
+	rte_eth_macaddr_get(test_params->slave_port_ids[2], &expected_mac_addr_1);
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST, 4, 1) != 0)
+		return -1;
+
+	/* Verify that all MACs are the same as first slave added to bonded
+	 * device */
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(&expected_mac_addr_0, &read_mac_addr,
+				sizeof(read_mac_addr))) {
+			printf("slave port (%d) mac address not set to that of primary port\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* change primary and verify that MAC addresses haven't changed */
+	retval = rte_eth_bond_primary_set(test_params->bonded_port_id,
+			test_params->slave_port_ids[2]);
+	if (retval != 0) {
+		printf("Failed to set bonded port (%d) primary port to (%d)\n",
+				test_params->bonded_port_id, test_params->slave_port_ids[i]);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(&expected_mac_addr_0, &read_mac_addr,
+				sizeof(read_mac_addr))) {
+			printf("slave port (%d) mac address has changed to that of primary port without stop/start toggle of bonded device\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* stop / start bonded device and verify that primary MAC address is
+	 * propagated to bonded device and slaves */
+
+	rte_eth_dev_stop(test_params->bonded_port_id);
+
+	if (rte_eth_dev_start(test_params->bonded_port_id) != 0)
+		return -1;
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(&expected_mac_addr_1, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of new primary port\n",
+				test_params->slave_port_ids[i]);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(&expected_mac_addr_1, &read_mac_addr,
+				sizeof(read_mac_addr))) {
+			printf("slave port (%d) mac address not set to that of new primary port\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* Set explicit MAC address */
+	if (rte_eth_bond_mac_address_set(test_params->bonded_port_id,
+			(struct ether_addr *)bonded_mac) != 0)
+		return -1;
+
+	rte_eth_macaddr_get(test_params->bonded_port_id, &read_mac_addr);
+	if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) {
+		printf("bonded port (%d) mac address not set to that of new primary port\n",
+				test_params->slave_port_ids[i]);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++) {
+		rte_eth_macaddr_get(test_params->slave_port_ids[i], &read_mac_addr);
+		if (memcmp(bonded_mac, &read_mac_addr, sizeof(read_mac_addr))) {
+			printf("slave port (%d) mac address not set to that of new primary port\n",
+					test_params->slave_port_ids[i]);
+			return -1;
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+#define BROADCAST_LINK_STATUS_NUM_OF_SLAVES (4)
+static int
+test_broadcast_verify_slave_link_status_change_behaviour(void)
+{
+	struct rte_mbuf *pkt_burst[BROADCAST_LINK_STATUS_NUM_OF_SLAVES][MAX_PKT_BURST];
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST] = { NULL };
+	struct rte_eth_stats port_stats;
+
+	uint8_t slaves[RTE_MAX_ETHPORTS];
+
+	int i, j, burst_size, slave_count;
+
+	memset(pkt_burst, 0, sizeof(pkt_burst));
+
+	/* Initialize bonded device with 4 slaves in round robin mode */
+	if (initialize_bonded_device_with_slaves(BONDING_MODE_BROADCAST,
+			BROADCAST_LINK_STATUS_NUM_OF_SLAVES, 1) != 0)
+		return -1;
+
+	/* Verify Current Slaves Count /Active Slave Count is */
+	slave_count = rte_eth_bond_slaves_get(test_params->bonded_port_id, slaves,
+			RTE_MAX_ETHPORTS);
+	if (slave_count != 4) {
+		printf("Number of slaves (%d) is not as expected (%d).\n",
+				slave_count, 4);
+		return -1;
+	}
+
+	slave_count = rte_eth_bond_active_slaves_get(
+			test_params->bonded_port_id, slaves, RTE_MAX_ETHPORTS);
+	if (slave_count != 4) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, 4);
+		return -1;
+	}
+
+	/* Set 2 slaves link status to down */
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[1], 0);
+	virtual_ethdev_simulate_link_status_interrupt(
+			test_params->slave_port_ids[3], 0);
+
+	slave_count = rte_eth_bond_active_slaves_get(test_params->bonded_port_id,
+			slaves, RTE_MAX_ETHPORTS);
+	if (slave_count != 2) {
+		printf("Number of active slaves (%d) is not as expected (%d).\n",
+				slave_count, 2);
+		return -1;
+	}
+
+	for (i = 0; i < test_params->bonded_slave_count; i++)
+		rte_eth_stats_reset(test_params->slave_port_ids[i]);
+
+	/* Verify that pkts are not sent on slaves with link status down */
+	burst_size = 21;
+
+	if (generate_test_burst(&pkt_burst[0][0], burst_size, 0, 0, 1, 0, 0) !=
+			burst_size) {
+		printf("generate_test_burst failed\n");
+		return -1;
+	}
+
+	if (rte_eth_tx_burst(test_params->bonded_port_id, 0, &pkt_burst[0][0],
+			burst_size) != (burst_size * slave_count)) {
+		printf("rte_eth_tx_burst failed\n");
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.opackets != (uint64_t)(burst_size * slave_count)) {
+		printf("(%d) port_stats.opackets (%d) not as expected (%d)\n",
+				test_params->bonded_port_id, (int)port_stats.opackets,
+				burst_size * slave_count);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[0], &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[0]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[1], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[1]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[2], &port_stats);
+	if (port_stats.opackets != (uint64_t)burst_size) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[2]);
+		return -1;
+	}
+
+	rte_eth_stats_get(test_params->slave_port_ids[3], &port_stats);
+	if (port_stats.opackets != 0) {
+		printf("(%d) port_stats.opackets not as expected\n",
+				test_params->slave_port_ids[3]);
+		return -1;
+	}
+
+	for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) {
+		if (generate_test_burst(&pkt_burst[i][0], burst_size, 0, 0, 1, 0, 0) !=
+				burst_size) {
+			return -1;
+		}
+
+		virtual_ethdev_add_mbufs_to_rx_queue(test_params->slave_port_ids[i],
+				&pkt_burst[i][0], burst_size);
+	}
+
+	/* Verify that pkts are not received on slaves with link status down */
+
+	if (rte_eth_rx_burst(test_params->bonded_port_id, 0, rx_pkt_burst,
+			MAX_PKT_BURST) !=
+			burst_size + burst_size) {
+		printf("rte_eth_rx_burst\n");
+		return -1;
+	}
+
+	/* Verify bonded device rx count */
+	rte_eth_stats_get(test_params->bonded_port_id, &port_stats);
+	if (port_stats.ipackets != (uint64_t)(burst_size + burst_size)) {
+		printf("(%d) port_stats.ipackets not as expected\n",
+				test_params->bonded_port_id);
+		return -1;
+	}
+
+	/* free mbufs allocate for rx testing */
+	for (i = 0; i < BROADCAST_LINK_STATUS_NUM_OF_SLAVES; i++) {
+		for (j = 0; j < MAX_PKT_BURST; j++) {
+			if (pkt_burst[i][j] != NULL) {
+				rte_pktmbuf_free(pkt_burst[i][j]);
+				pkt_burst[i][j] = NULL;
+			}
+		}
+	}
+
+	/* Clean up and remove slaves from bonded device */
+	return remove_slaves_and_stop_bonded_device();
+}
+
+static int
+test_reconfigure_bonded_device(void)
+{
+	test_params->nb_rx_q = 4;
+	test_params->nb_tx_q = 4;
+
+	if (configure_ethdev(test_params->bonded_port_id, 0)  != 0) {
+		printf("failed to reconfigure bonded device");
+		return -1;
+	}
+
+
+	test_params->nb_rx_q = 2;
+	test_params->nb_tx_q = 2;
+
+	if (configure_ethdev(test_params->bonded_port_id, 0)  != 0) {
+		printf("failed to reconfigure bonded device with less rx/tx queues");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int
+test_close_bonded_device(void)
+{
+	rte_eth_dev_close(test_params->bonded_port_id);
+	return 0;
+}
+
+static int
+testsuite_teardown(void)
+{
+	if (test_params->pkt_eth_hdr != NULL)
+		free(test_params->pkt_eth_hdr);
+
+	return 0;
+}
+
+struct unittest {
+	int (*test_function)(void);
+	const char *success_msg;
+	const char *fail_msg;
+};
+
+struct unittest_suite {
+	int (*setup_function)(void);
+	int (*teardown_function)(void);
+	struct unittest unittests[];
+};
+
+static struct unittest_suite link_bonding_test_suite  = {
+	.setup_function = test_setup,
+	.teardown_function = testsuite_teardown,
+	.unittests = {
+		{ test_create_bonded_device, "test_create_bonded_device succeeded",
+			"test_create_bonded_device failed" },
+		{ test_create_bonded_device_with_invalid_params,
+			"test_create_bonded_device_with_invalid_params succeeded",
+			"test_create_bonded_device_with_invalid_params failed" },
+		{ test_add_slave_to_bonded_device,
+			"test_add_slave_to_bonded_device succeeded",
+			"test_add_slave_to_bonded_device failed" },
+		{ test_add_slave_to_invalid_bonded_device,
+			"test_add_slave_to_invalid_bonded_device succeeded",
+			"test_add_slave_to_invalid_bonded_device failed" },
+		{ test_remove_slave_from_bonded_device,
+			"test_remove_slave_from_bonded_device succeeded ",
+			"test_remove_slave_from_bonded_device failed" },
+		{ test_remove_slave_from_invalid_bonded_device,
+			"test_remove_slave_from_invalid_bonded_device succeeded",
+			"test_remove_slave_from_invalid_bonded_device failed" },
+		{ test_get_slaves_from_bonded_device,
+			"test_get_slaves_from_bonded_device succeeded",
+			"test_get_slaves_from_bonded_device failed" },
+		{ test_add_already_bonded_slave_to_bonded_device,
+			"test_add_already_bonded_slave_to_bonded_device succeeded",
+			"test_add_already_bonded_slave_to_bonded_device failed" },
+		{ test_add_remove_multiple_slaves_to_from_bonded_device,
+			"test_add_remove_multiple_slaves_to_from_bonded_device succeeded",
+			"test_add_remove_multiple_slaves_to_from_bonded_device failed" },
+		{ test_start_bonded_device,
+			"test_start_bonded_device succeeded",
+			"test_start_bonded_device failed" },
+		{ test_stop_bonded_device,
+			"test_stop_bonded_device succeeded",
+			"test_stop_bonded_device failed" },
+		{ test_set_bonding_mode,
+			"test_set_bonding_mode succeeded",
+			"test_set_bonding_mode failed" },
+		{ test_set_primary_slave,
+			"test_set_primary_slave succeeded",
+			"test_set_primary_slave failed" },
+		{ test_set_explicit_bonded_mac,
+			"test_set_explicit_bonded_mac succeeded",
+			"test_set_explicit_bonded_mac failed" },
+		{ test_adding_slave_after_bonded_device_started,
+			"test_adding_slave_after_bonded_device_started succeeded",
+			"test_adding_slave_after_bonded_device_started failed" },
+		{ test_roundrobin_tx_burst,
+			"test_roundrobin_tx_burst succeeded",
+			"test_roundrobin_tx_burst failed" },
+		{ test_roundrobin_rx_burst_on_single_slave,
+			"test_roundrobin_rx_burst_on_single_slave succeeded",
+			"test_roundrobin_rx_burst_on_single_slave failed" },
+		{ test_roundrobin_rx_burst_on_multiple_slaves,
+			"test_roundrobin_rx_burst_on_multiple_slaves succeeded",
+			"test_roundrobin_rx_burst_on_multiple_slaves failed" },
+		{ test_roundrobin_verify_promiscuous_enable_disable,
+			"test_roundrobin_verify_promiscuous_enable_disable succeeded",
+			"test_roundrobin_verify_promiscuous_enable_disable failed" },
+		{ test_roundrobin_verify_mac_assignment,
+			"test_roundrobin_verify_mac_assignment succeeded",
+			"test_roundrobin_verify_mac_assignment failed" },
+		{ test_roundrobin_verify_slave_link_status_change_behaviour,
+			"test_roundrobin_verify_slave_link_status_change_behaviour succeeded",
+			"test_roundrobin_verify_slave_link_status_change_behaviour failed" },
+		{ test_activebackup_tx_burst,
+			"test_activebackup_tx_burst succeeded",
+			"test_activebackup_tx_burst failed" },
+		{ test_activebackup_rx_burst,
+			"test_activebackup_rx_burst succeeded",
+			"test_activebackup_rx_burst failed" },
+		{ test_activebackup_verify_promiscuous_enable_disable,
+			"test_activebackup_verify_promiscuous_enable_disable succeeded",
+			"test_activebackup_verify_promiscuous_enable_disable failed" },
+		{ test_activebackup_verify_mac_assignment,
+			"test_activebackup_verify_mac_assignment succeeded",
+			"test_activebackup_verify_mac_assignment failed" },
+		{ test_activebackup_verify_slave_link_status_change_failover,
+			"test_activebackup_verify_slave_link_status_change_failover succeeded",
+			"test_activebackup_verify_slave_link_status_change_failover failed" },
+		{ test_balance_xmit_policy_configuration,
+			"test_balance_xmit_policy_configuration succeeded",
+			"test_balance_xmit_policy_configuration failed" },
+		{ test_balance_l2_tx_burst,
+			"test_balance_l2_tx_burst succeeded",
+			"test_balance_l2_tx_burst failed" },
+		{ test_balance_l23_tx_burst_ipv4_toggle_ip_addr,
+			"test_balance_l23_tx_burst_ipv4_toggle_ip_addr succeeded",
+			"test_balance_l23_tx_burst_ipv4_toggle_ip_addr failed" },
+		{ test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr,
+			"test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr succeeded",
+			"test_balance_l23_tx_burst_vlan_ipv4_toggle_ip_addr failed" },
+		{ test_balance_l23_tx_burst_ipv6_toggle_ip_addr,
+			"test_balance_l23_tx_burst_ipv6_toggle_ip_addr succeeded",
+			"test_balance_l23_tx_burst_ipv6_toggle_ip_addr failed" },
+		{ test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr,
+			"test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr succeeded",
+			"test_balance_l23_tx_burst_vlan_ipv6_toggle_ip_addr failed" },
+		{ test_balance_l23_tx_burst_toggle_mac_addr,
+			"test_balance_l23_tx_burst_toggle_mac_addr succeeded",
+			"test_balance_l23_tx_burst_toggle_mac_addr failed" },
+		{ test_balance_l34_tx_burst_ipv4_toggle_ip_addr,
+			"test_balance_l34_tx_burst_ipv4_toggle_ip_addr succeeded",
+			"test_balance_l34_tx_burst_ipv4_toggle_ip_addr failed" },
+		{ test_balance_l34_tx_burst_ipv4_toggle_udp_port,
+			"test_balance_l34_tx_burst_ipv4_toggle_udp_port succeeded",
+			"test_balance_l34_tx_burst_ipv4_toggle_udp_port failed" },
+		{ test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr,
+			"test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr succeeded",
+			"test_balance_l34_tx_burst_vlan_ipv4_toggle_ip_addr failed" },
+		{ test_balance_l34_tx_burst_ipv6_toggle_ip_addr,
+			"test_balance_l34_tx_burst_ipv6_toggle_ip_addr succeeded",
+			"test_balance_l34_tx_burst_ipv6_toggle_ip_addr failed" },
+		{ test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr,
+			"test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr succeeded",
+			"test_balance_l34_tx_burst_vlan_ipv6_toggle_ip_addr failed" },
+		{ test_balance_l34_tx_burst_ipv6_toggle_udp_port,
+			"test_balance_l34_tx_burst_ipv6_toggle_udp_port succeeded",
+			"test_balance_l34_tx_burst_ipv6_toggle_udp_port failed" },
+		{ test_balance_rx_burst,
+			"test_balance_rx_burst succeeded",
+			"test_balance_rx_burst failed" },
+		{ test_balance_verify_promiscuous_enable_disable,
+			"test_balance_verify_promiscuous_enable_disable succeeded",
+			"test_balance_verify_promiscuous_enable_disable failed" },
+		{ test_balance_verify_mac_assignment,
+			"test_balance_verify_mac_assignment succeeded",
+			"test_balance_verify_mac_assignment failed" },
+		{ test_balance_verify_slave_link_status_change_behaviour,
+			"test_balance_verify_slave_link_status_change_behaviour succeeded",
+			"test_balance_verify_slave_link_status_change_behaviour failed" },
+		{ test_broadcast_tx_burst,
+			"test_broadcast_tx_burst succeeded",
+			"test_broadcast_tx_burst failed" },
+		{ test_broadcast_rx_burst,
+			"test_broadcast_rx_burst succeeded",
+			"test_broadcast_rx_burst failed" },
+		{ test_broadcast_verify_promiscuous_enable_disable,
+			"test_broadcast_verify_promiscuous_enable_disable succeeded",
+			"test_broadcast_verify_promiscuous_enable_disable failed" },
+		{ test_broadcast_verify_mac_assignment,
+			"test_broadcast_verify_mac_assignment succeeded",
+			"test_broadcast_verify_mac_assignment failed" },
+		{ test_broadcast_verify_slave_link_status_change_behaviour,
+			"test_broadcast_verify_slave_link_status_change_behaviour succeeded",
+			"test_broadcast_verify_slave_link_status_change_behaviour failed" },
+		{ test_reconfigure_bonded_device,
+			"test_reconfigure_bonded_device succeeded",
+			"test_reconfigure_bonded_device failed" },
+		{ test_close_bonded_device,
+			"test_close_bonded_device succeeded",
+			"test_close_bonded_device failed" },
+
+		{ NULL , NULL, NULL } /**< NULL terminate unit test array */
+	}
+};
+
+
+int
+test_link_bonding(void)
+{
+	int i = 0;
+
+	if (link_bonding_test_suite.setup_function) {
+		if (link_bonding_test_suite.setup_function() != 0)
+			return -1;
+	}
+
+	while (link_bonding_test_suite.unittests[i].test_function) {
+		if (link_bonding_test_suite.unittests[i].test_function() == 0) {
+			printf("%s", link_bonding_test_suite.unittests[i].success_msg ?
+					link_bonding_test_suite.unittests[i].success_msg :
+					"unit test succeeded");
+		} else {
+			printf("%s", link_bonding_test_suite.unittests[i].fail_msg ?
+					link_bonding_test_suite.unittests[i].fail_msg :
+					"unit test failed");
+			return -1;
+		}
+		printf("\n");
+		i++;
+	}
+
+	if (link_bonding_test_suite.teardown_function) {
+		if (link_bonding_test_suite.teardown_function() != 0)
+			return -1;
+	}
+
+	return 0;
+}
diff --git a/app/test/virtual_pmd.c b/app/test/virtual_pmd.c
new file mode 100644
index 0000000..0c67a10
--- /dev/null
+++ b/app/test/virtual_pmd.c
@@ -0,0 +1,574 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 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_mbuf.h>
+#include <rte_ethdev.h>
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+#include <rte_memory.h>
+
+#include "virtual_pmd.h"
+
+#define MAX_PKT_BURST 512
+
+static const char *virtual_ethdev_driver_name = "Virtual PMD";
+
+struct virtual_ethdev_private {
+	struct rte_eth_stats eth_stats;
+
+	struct rte_mbuf *rx_pkt_burst[MAX_PKT_BURST];
+	int rx_pkt_burst_len;
+};
+
+struct virtual_ethdev_queue {
+	int port_id;
+	int queue_id;
+};
+
+static int
+virtual_ethdev_start_success(struct rte_eth_dev *eth_dev __rte_unused)
+{
+	eth_dev->data->dev_started = 1;
+
+	return 0;
+}
+
+static int
+virtual_ethdev_start_fail(struct rte_eth_dev *eth_dev __rte_unused)
+{
+	eth_dev->data->dev_started = 0;
+
+	return -1;
+}
+static void  virtual_ethdev_stop(struct rte_eth_dev *eth_dev __rte_unused)
+{
+	eth_dev->data->dev_link.link_status = 0;
+	eth_dev->data->dev_started = 0;
+}
+
+static void
+virtual_ethdev_close(struct rte_eth_dev *dev __rte_unused)
+{}
+
+static int
+virtual_ethdev_configure_success(struct rte_eth_dev *dev __rte_unused)
+{
+	return 0;
+}
+
+static int
+virtual_ethdev_configure_fail(struct rte_eth_dev *dev __rte_unused)
+{
+	return -1;
+}
+
+static void
+virtual_ethdev_info_get(struct rte_eth_dev *dev __rte_unused,
+		struct rte_eth_dev_info *dev_info)
+{
+	dev_info->driver_name = virtual_ethdev_driver_name;
+	dev_info->max_mac_addrs = 1;
+
+	dev_info->max_rx_pktlen = (uint32_t)2048;
+
+	dev_info->max_rx_queues = (uint16_t)128;
+	dev_info->max_tx_queues = (uint16_t)512;
+
+	dev_info->min_rx_bufsize = 0;
+	dev_info->pci_dev = NULL;
+}
+
+static int
+virtual_ethdev_rx_queue_setup_success(struct rte_eth_dev *dev,
+		uint16_t rx_queue_id, uint16_t nb_rx_desc __rte_unused,
+		unsigned int socket_id,
+		const struct rte_eth_rxconf *rx_conf __rte_unused,
+		struct rte_mempool *mb_pool __rte_unused)
+{
+	struct virtual_ethdev_queue *rx_q;
+
+	rx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL,
+			sizeof(struct virtual_ethdev_queue), 0, socket_id);
+
+	if (rx_q == NULL)
+		return -1;
+
+	rx_q->port_id = dev->data->port_id;
+	rx_q->queue_id = rx_queue_id;
+
+	dev->data->rx_queues[rx_queue_id] = rx_q;
+
+	return 0;
+}
+
+static int
+virtual_ethdev_rx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused,
+		uint16_t rx_queue_id __rte_unused, uint16_t nb_rx_desc __rte_unused,
+		unsigned int socket_id __rte_unused,
+		const struct rte_eth_rxconf *rx_conf __rte_unused,
+		struct rte_mempool *mb_pool __rte_unused)
+{
+	return -1;
+}
+
+static int
+virtual_ethdev_tx_queue_setup_success(struct rte_eth_dev *dev,
+		uint16_t tx_queue_id, uint16_t nb_tx_desc __rte_unused,
+		unsigned int socket_id,
+		const struct rte_eth_txconf *tx_conf __rte_unused)
+{
+	struct virtual_ethdev_queue *tx_q;
+
+	tx_q = (struct virtual_ethdev_queue *)rte_zmalloc_socket(NULL,
+			sizeof(struct virtual_ethdev_queue), 0, socket_id);
+
+	if (tx_q == NULL)
+		return -1;
+
+	tx_q->port_id = dev->data->port_id;
+	tx_q->queue_id = tx_queue_id;
+
+	dev->data->tx_queues[tx_queue_id] = tx_q;
+
+	return 0;
+}
+
+static int
+virtual_ethdev_tx_queue_setup_fail(struct rte_eth_dev *dev __rte_unused,
+		uint16_t tx_queue_id __rte_unused, uint16_t nb_tx_desc __rte_unused,
+		unsigned int socket_id __rte_unused,
+		const struct rte_eth_txconf *tx_conf __rte_unused)
+{
+	return -1;
+}
+
+static void
+virtual_ethdev_rx_queue_release(void *q __rte_unused)
+{
+}
+
+static void
+virtual_ethdev_tx_queue_release(void *q __rte_unused)
+{
+}
+
+static int
+virtual_ethdev_link_update_success(struct rte_eth_dev *bonded_eth_dev,
+		int wait_to_complete __rte_unused)
+{
+	if (!bonded_eth_dev->data->dev_started)
+		bonded_eth_dev->data->dev_link.link_status = 0;
+
+	return 0;
+}
+
+static int
+virtual_ethdev_link_update_fail(struct rte_eth_dev *bonded_eth_dev __rte_unused,
+		int wait_to_complete __rte_unused)
+{
+	return -1;
+}
+
+static void
+virtual_ethdev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
+{
+	struct virtual_ethdev_private *dev_private = dev->data->dev_private;
+
+	if (stats)
+		rte_memcpy(stats, &dev_private->eth_stats, sizeof(*stats));
+}
+
+static void
+virtual_ethdev_stats_reset(struct rte_eth_dev *dev)
+{
+	struct virtual_ethdev_private *dev_private = dev->data->dev_private;
+
+	dev_private->rx_pkt_burst_len = 0;
+
+	/* Reset internal statistics */
+	memset(&dev_private->eth_stats, 0, sizeof(dev_private->eth_stats));
+}
+
+static void
+virtual_ethdev_promiscuous_mode_enable(struct rte_eth_dev *dev __rte_unused)
+{}
+
+static void
+virtual_ethdev_promiscuous_mode_disable(struct rte_eth_dev *dev __rte_unused)
+{}
+
+
+static struct eth_dev_ops virtual_ethdev_default_dev_ops = {
+		.dev_configure = virtual_ethdev_configure_success,
+		.dev_start = virtual_ethdev_start_success,
+		.dev_stop = virtual_ethdev_stop,
+		.dev_close = virtual_ethdev_close,
+		.dev_infos_get = virtual_ethdev_info_get,
+		.rx_queue_setup = virtual_ethdev_rx_queue_setup_success,
+		.tx_queue_setup = virtual_ethdev_tx_queue_setup_success,
+		.rx_queue_release = virtual_ethdev_rx_queue_release,
+		.tx_queue_release = virtual_ethdev_tx_queue_release,
+		.link_update = virtual_ethdev_link_update_success,
+		.stats_get = virtual_ethdev_stats_get,
+		.stats_reset = virtual_ethdev_stats_reset,
+		.promiscuous_enable = virtual_ethdev_promiscuous_mode_enable,
+		.promiscuous_disable = virtual_ethdev_promiscuous_mode_disable
+};
+
+
+void
+virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success)
+{
+	struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id];
+
+	if (success)
+		vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_success;
+	else
+		vrtl_eth_dev->dev_ops->dev_start = virtual_ethdev_start_fail;
+
+}
+
+void
+virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success)
+{
+	struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id];
+
+	if (success)
+		vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_success;
+	else
+		vrtl_eth_dev->dev_ops->dev_configure = virtual_ethdev_configure_fail;
+}
+
+void
+virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success)
+{
+	struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id];
+
+	if (success)
+		vrtl_eth_dev->dev_ops->rx_queue_setup =
+				virtual_ethdev_rx_queue_setup_success;
+	else
+		vrtl_eth_dev->dev_ops->rx_queue_setup =
+				virtual_ethdev_rx_queue_setup_fail;
+}
+
+void
+virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success)
+{
+	struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id];
+
+	if (success)
+		vrtl_eth_dev->dev_ops->tx_queue_setup =
+				virtual_ethdev_tx_queue_setup_success;
+	else
+		vrtl_eth_dev->dev_ops->tx_queue_setup =
+				virtual_ethdev_tx_queue_setup_fail;
+}
+
+void
+virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success)
+{
+	struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id];
+
+	if (success)
+		vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_success;
+	else
+		vrtl_eth_dev->dev_ops->link_update = virtual_ethdev_link_update_fail;
+}
+
+
+static uint16_t
+virtual_ethdev_rx_burst_success(void *queue __rte_unused,
+							 struct rte_mbuf **bufs,
+							 uint16_t nb_pkts)
+{
+	struct rte_eth_dev *vrtl_eth_dev;
+	struct virtual_ethdev_queue *pq_map;
+	struct virtual_ethdev_private *dev_private;
+
+	int i;
+
+	pq_map = (struct virtual_ethdev_queue *)queue;
+
+	vrtl_eth_dev = &rte_eth_devices[pq_map->port_id];
+
+	dev_private = vrtl_eth_dev->data->dev_private;
+
+	if (dev_private->rx_pkt_burst_len > 0) {
+		if (dev_private->rx_pkt_burst_len < nb_pkts) {
+
+			for (i = 0; i < dev_private->rx_pkt_burst_len; i++) {
+				bufs[i] = dev_private->rx_pkt_burst[i];
+				dev_private->rx_pkt_burst[i] = NULL;
+			}
+
+			dev_private->eth_stats.ipackets = dev_private->rx_pkt_burst_len;
+		}
+		/* reset private burst values */
+		dev_private->rx_pkt_burst_len = 0;
+	}
+
+	return dev_private->eth_stats.ipackets;
+}
+
+static uint16_t
+virtual_ethdev_rx_burst_fail(void *queue __rte_unused,
+							 struct rte_mbuf **bufs __rte_unused,
+							 uint16_t nb_pkts __rte_unused)
+{
+	return 0;
+}
+
+static uint16_t
+virtual_ethdev_tx_burst_success(void *queue,
+							 struct rte_mbuf **bufs __rte_unused,
+							 uint16_t nb_pkts)
+{
+	struct rte_eth_dev *vrtl_eth_dev;
+	struct virtual_ethdev_queue *tx_q;
+	struct virtual_ethdev_private *dev_private;
+	int i;
+
+	tx_q = (struct virtual_ethdev_queue *)queue;
+
+	vrtl_eth_dev = &rte_eth_devices[tx_q->port_id];
+
+	if (vrtl_eth_dev->data->dev_link.link_status) {
+		dev_private = vrtl_eth_dev->data->dev_private;
+		dev_private->eth_stats.opackets += nb_pkts;
+
+		return nb_pkts;
+	}
+
+	/* free packets in burst */
+	for (i = 0; i < nb_pkts; i++) {
+		if (bufs[i] != NULL)
+			rte_pktmbuf_free(bufs[i]);
+
+		bufs[i] = NULL;
+	}
+
+	return 0;
+}
+
+
+static uint16_t
+virtual_ethdev_tx_burst_fail(void *queue __rte_unused,
+		struct rte_mbuf **bufs __rte_unused, uint16_t nb_pkts __rte_unused)
+{
+	return 0;
+}
+
+
+void
+virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success)
+{
+	struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id];
+
+	if (success)
+		vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success;
+	else
+		vrtl_eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_fail;
+}
+
+
+void
+virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success)
+{
+	struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id];
+
+	if (success)
+		vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success;
+	else
+		vrtl_eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_fail;
+}
+
+
+void
+virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status)
+{
+	struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id];
+
+	vrtl_eth_dev->data->dev_link.link_status = link_status;
+
+	_rte_eth_dev_callback_process(vrtl_eth_dev, RTE_ETH_EVENT_INTR_LSC);
+}
+
+
+
+void
+virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id,
+		struct rte_mbuf **pkt_burst, int burst_length)
+{
+	struct virtual_ethdev_private *dev_private = NULL;
+	struct rte_eth_dev *vrtl_eth_dev = &rte_eth_devices[port_id];
+
+	int i;
+
+	dev_private = vrtl_eth_dev->data->dev_private;
+
+	for (i = 0; i < burst_length; i++)
+		dev_private->rx_pkt_burst[i] = pkt_burst[i];
+
+	dev_private->rx_pkt_burst_len = burst_length;
+}
+
+static uint8_t
+get_number_of_sockets(void)
+{
+	int sockets = 0;
+	int i;
+	const struct rte_memseg *ms = rte_eal_get_physmem_layout();
+
+	for (i = 0; i < RTE_MAX_MEMSEG && ms[i].addr != NULL; i++) {
+		if (sockets < ms[i].socket_id)
+			sockets = ms[i].socket_id;
+	}
+	/* Number of sockets = maximum socket_id + 1 */
+	return ++sockets;
+}
+
+
+int
+virtual_ethdev_create(const char *name, struct ether_addr *mac_addr,
+		uint8_t socket_id)
+{
+	struct rte_pci_device *pci_dev = NULL;
+	struct rte_eth_dev *eth_dev = NULL;
+	struct eth_driver *eth_drv = NULL;
+	struct rte_pci_driver *pci_drv = NULL;
+	struct eth_dev_ops *dev_ops = NULL;
+	struct rte_pci_id *id_table = NULL;
+	struct virtual_ethdev_private *dev_private = NULL;
+
+
+	/* now do all data allocation - for eth_dev structure, dummy pci driver
+	 * and internal (dev_private) data
+	 */
+
+	if (socket_id >= get_number_of_sockets())
+		goto err;
+
+	pci_dev = rte_zmalloc_socket(name, sizeof(*pci_dev), 0, socket_id);
+	if (pci_dev == NULL)
+		goto err;
+
+	eth_drv = rte_zmalloc_socket(name, sizeof(*eth_drv), 0, socket_id);
+	if (eth_drv == NULL)
+		goto err;
+
+	pci_drv = rte_zmalloc_socket(name, sizeof(*pci_drv), 0, socket_id);
+	if (pci_drv == NULL)
+		goto err;
+
+	dev_ops = rte_zmalloc_socket(name, sizeof(*dev_ops), 0, socket_id);
+	if (dev_ops == NULL)
+		goto err;
+
+	id_table = rte_zmalloc_socket(name, sizeof(*id_table), 0, socket_id);
+	if (id_table == NULL)
+		goto err;
+
+	dev_private = rte_zmalloc_socket(name, sizeof(*dev_private), 0, socket_id);
+	if (dev_private == NULL)
+		goto err;
+
+	/* reserve an ethdev entry */
+	eth_dev = rte_eth_dev_allocate(name);
+	if (eth_dev == NULL)
+		goto err;
+
+	pci_dev->numa_node = socket_id;
+	pci_drv->name = virtual_ethdev_driver_name;
+	pci_drv->id_table = id_table;
+
+	eth_drv->pci_drv = (struct rte_pci_driver)(*pci_drv);
+	eth_dev->driver = eth_drv;
+
+	eth_dev->data->nb_rx_queues = (uint16_t)1;
+	eth_dev->data->nb_tx_queues = (uint16_t)1;
+
+	TAILQ_INIT(&(eth_dev->callbacks));
+
+	eth_dev->data->dev_link.link_status = 0;
+	eth_dev->data->dev_link.link_speed = ETH_LINK_SPEED_10000;
+	eth_dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX;
+
+	eth_dev->data->mac_addrs = rte_zmalloc(name, ETHER_ADDR_LEN, 0);
+	if (eth_dev->data->mac_addrs == NULL)
+		goto err;
+
+	memcpy(eth_dev->data->mac_addrs, mac_addr,
+			sizeof(*eth_dev->data->mac_addrs));
+	eth_dev->data->mac_addrs->addr_bytes[5] = eth_dev->data->port_id;
+
+	eth_dev->data->dev_started = 0;
+	eth_dev->data->promiscuous = 0;
+	eth_dev->data->scattered_rx = 0;
+	eth_dev->data->all_multicast = 0;
+
+	memset(dev_private, 0, sizeof(*dev_private));
+	eth_dev->data->dev_private = dev_private;
+
+	eth_dev->dev_ops = dev_ops;
+
+	/* Copy default device operation functions */
+	memcpy(eth_dev->dev_ops, &virtual_ethdev_default_dev_ops,
+			sizeof(*eth_dev->dev_ops));
+
+	eth_dev->pci_dev = pci_dev;
+	eth_dev->pci_dev->driver = &eth_drv->pci_drv;
+
+	eth_dev->pci_dev->driver->id_table->device_id = 0xBEEF;
+
+	eth_dev->rx_pkt_burst = virtual_ethdev_rx_burst_success;
+	eth_dev->tx_pkt_burst = virtual_ethdev_tx_burst_success;
+
+	return eth_dev->data->port_id;
+
+err:
+	if (pci_dev)
+		rte_free(pci_dev);
+	if (pci_drv)
+		rte_free(pci_drv);
+	if (eth_drv)
+		rte_free(eth_drv);
+	if (dev_ops)
+		rte_free(dev_ops);
+	if (id_table)
+		rte_free(id_table);
+	if (dev_private)
+		rte_free(dev_private);
+
+	return -1;
+}
diff --git a/app/test/virtual_pmd.h b/app/test/virtual_pmd.h
new file mode 100644
index 0000000..766b6ac
--- /dev/null
+++ b/app/test/virtual_pmd.h
@@ -0,0 +1,74 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 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 __VIRTUAL_ETHDEV_H_
+#define __VIRTUAL_ETHDEV_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_ether.h>
+
+int virtual_ethdev_init(void);
+
+int virtual_ethdev_create(const char *name, struct ether_addr *mac_addr, uint8_t socket_id);
+
+void virtual_ethdev_simulate_link_status_interrupt(uint8_t port_id, uint8_t link_status);
+
+void virtual_ethdev_add_mbufs_to_rx_queue(uint8_t port_id, struct rte_mbuf **pkts_burst, int burst_length);
+
+
+/** Control methods for the dev_ops functions pointer to control the behavior of the Virtual PMD */
+
+void virtual_ethdev_start_fn_set_success(uint8_t port_id, uint8_t success);
+
+void virtual_ethdev_stop_fn_set_success(uint8_t port_id, uint8_t success);
+
+void virtual_ethdev_configure_fn_set_success(uint8_t port_id, uint8_t success);
+
+void virtual_ethdev_rx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success);
+
+void virtual_ethdev_tx_queue_setup_fn_set_success(uint8_t port_id, uint8_t success);
+
+void virtual_ethdev_link_update_fn_set_success(uint8_t port_id, uint8_t success);
+
+void virtual_ethdev_rx_burst_fn_set_success(uint8_t port_id, uint8_t success);
+
+void virtual_ethdev_tx_burst_fn_set_success(uint8_t port_id, uint8_t success);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VIRTUAL_ETHDEV_H_ */
-- 
1.8.5.3

  parent reply	other threads:[~2014-06-13 14:42 UTC|newest]

Thread overview: 127+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-05-28 15:32 [dpdk-dev] [PATCH 0/4] Link Bonding Library declan.doherty
2014-05-28 15:32 ` [dpdk-dev] [PATCH 1/4] " declan.doherty
2014-05-28 16:54   ` Shaw, Jeffrey B
2014-05-29 13:32     ` Doherty, Declan
2014-05-28 15:32 ` [dpdk-dev] [PATCH 2/4] Link bonding unit tests declan.doherty
2014-05-28 15:32 ` [dpdk-dev] [PATCH 3/4] Link bonding integration into testpmd declan.doherty
2014-05-28 15:32 ` [dpdk-dev] [PATCH 4/4] Add Link Bonding Library to Doxygen declan.doherty
2014-05-28 17:49 ` [dpdk-dev] [PATCH 0/4] Link Bonding Library Neil Horman
2014-05-29 10:33   ` Doherty, Declan
2014-05-29 11:33     ` Neil Horman
2014-05-29  3:23 ` Cao, Waterman
2014-05-29 10:35   ` Doherty, Declan
2014-06-04 15:18 ` [dpdk-dev] [PATCH v2 " declan.doherty
2014-06-04 15:18   ` [dpdk-dev] [PATCH v2 1/4] " declan.doherty
2014-06-04 15:18     ` declan.doherty
2014-06-05 15:15     ` Stephen Hemminger
2014-06-06  9:07       ` Doherty, Declan
2014-06-06 15:13         ` Stephen Hemminger
2014-06-09 21:11     ` Eric Kinzie
2014-06-13 14:03       ` Doherty, Declan
2014-06-04 15:18   ` [dpdk-dev] [PATCH v2 2/4] Link bonding unit tests, including: - code to generate packet bursts for testing rx and tx functionality of bonded device - virtual/stubbed out ethdev for use as slave ethdev in testing - checkpack fixes declan.doherty
2014-06-04 15:18     ` declan.doherty
2014-06-04 15:18   ` [dpdk-dev] [PATCH v2 0/4] Link Bonding Library declan.doherty
2014-06-04 15:18   ` [dpdk-dev] [PATCH v2 3/4] Adding link bonding support to testpmd. - Includes the ability to create new bonded devices. - Add /remove bonding slave devices. - Interogate bonded device stats/configuration - Change bonding modes and select balance transmit polices declan.doherty
2014-06-04 15:18   ` [dpdk-dev] [PATCH v2 4/4] Add Link Bonding Library to Doxygen declan.doherty
2014-06-04 16:10   ` [dpdk-dev] [PATCH v2 0/4] Link Bonding Library Doherty, Declan
2014-06-05  8:03   ` De Lara Guarch, Pablo
2014-06-05 11:03   ` Neil Horman
2014-06-06  8:23     ` Doherty, Declan
2014-06-06 14:54       ` Neil Horman
2014-06-13 14:56         ` Doherty, Declan
2014-06-13 15:11           ` Neil Horman
2014-06-06  3:26   ` Cao, Waterman
2014-06-11 16:33   ` Thomas Monjalon
2014-06-13 14:08     ` Doherty, Declan
2014-06-13 15:15       ` Thomas Monjalon
2014-06-13 14:41 ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Declan Doherty
2014-06-13 14:41   ` [dpdk-dev] [PATCH v3 1/5] " Declan Doherty
2014-06-13 14:41   ` [dpdk-dev] [PATCH v3 2/5] Link Bonding PMD Library (librte_eal/librte_ether link bonding support changes) Declan Doherty
2014-06-13 16:08     ` Neil Horman
2014-06-13 18:34       ` Doherty, Declan
2014-06-13 19:38         ` Neil Horman
2014-06-16  8:59           ` Doherty, Declan
2014-06-16 11:07             ` Neil Horman
2014-06-16 16:17               ` Richardson, Bruce
2014-06-16 17:47                 ` Neil Horman
2014-06-16 18:07                   ` Richardson, Bruce
2014-06-16 18:09                   ` Thomas Monjalon
2014-06-13 21:59     ` Stephen Hemminger
2014-06-16  7:59       ` Doherty, Declan
2014-06-13 14:42   ` Declan Doherty [this message]
2014-06-13 14:42   ` [dpdk-dev] [PATCH v3 4/5] Link Bonding PMD Library (testpmd link bonding API support) Declan Doherty
2014-06-13 14:42   ` [dpdk-dev] [PATCH v3 5/5] Link Bonding PMD Library (Doxygen Additions) Declan Doherty
2014-06-13 15:20   ` [dpdk-dev] [PATCH v3 0/5] Link Bonding PMD Library Neil Horman
2014-06-16 11:18   ` [dpdk-dev] [PATCH v4 0/6] Link Bonding Library Declan Doherty
2014-06-18 16:14     ` [dpdk-dev] [PATCH v5 " Declan Doherty
2014-06-18 16:18       ` Neil Horman
2014-06-24 14:52       ` [dpdk-dev] [PATCH v6 " Declan Doherty
2014-06-24 16:03         ` [dpdk-dev] [PATCH v7 " Declan Doherty
2014-06-25 20:07           ` [dpdk-dev] [PATCH v8 " Declan Doherty
2014-06-26 16:02             ` De Lara Guarch, Pablo
2014-06-26 23:57             ` [dpdk-dev] [PATCH v9 0/5] link bonding Thomas Monjalon
2014-06-26 23:57               ` [dpdk-dev] [PATCH v9 1/5] bond: new link bonding library Thomas Monjalon
2014-06-27  0:45                 ` Thomas Monjalon
2014-06-26 23:57               ` [dpdk-dev] [PATCH v9 2/5] ethdev: add unique name to devices Thomas Monjalon
2014-06-26 23:57               ` [dpdk-dev] [PATCH v9 3/5] eal: support link bonding device initialization Thomas Monjalon
2014-06-26 23:57               ` [dpdk-dev] [PATCH v9 4/5] bond: unit tests Thomas Monjalon
2014-06-26 23:57               ` [dpdk-dev] [PATCH v9 5/5] bond: testpmd support Thomas Monjalon
2014-06-27 10:18               ` [dpdk-dev] [PATCH v10 0/5] link bonding Declan Doherty
2014-06-27 20:58                 ` Thomas Monjalon
2014-06-29 17:49                 ` [dpdk-dev] [PATCH v11 0/5] link bonding library Declan Doherty
2014-06-30  9:21                   ` Thomas Monjalon
2014-06-30  9:28                     ` Doherty, Declan
2014-07-01 22:01                       ` Thomas Monjalon
2014-06-29 17:49                 ` [dpdk-dev] [PATCH v11 1/5] bond: new " Declan Doherty
2014-06-30  9:13                   ` Thomas Monjalon
2014-06-30 22:29                   ` Robert Sanford
2014-07-01 14:16                     ` Thomas Monjalon
2014-07-01 14:19                     ` Doherty, Declan
2014-07-01 14:26                       ` Thomas Monjalon
2014-06-29 17:49                 ` [dpdk-dev] [PATCH v11 2/5] ethdev: add unique name to devices Declan Doherty
2014-06-29 17:49                 ` [dpdk-dev] [PATCH v11 3/5] eal: support link bonding device initialization Declan Doherty
2014-06-29 17:49                 ` [dpdk-dev] [PATCH v11 4/5] bond: unit tests Declan Doherty
2014-06-30  8:56                   ` Thomas Monjalon
2014-06-29 17:49                 ` [dpdk-dev] [PATCH v11 5/5] bond: testpmd support Declan Doherty
2014-06-27 10:18               ` [dpdk-dev] [PATCH v10 1/5] bond: new link bonding library Declan Doherty
2014-06-27 10:18               ` [dpdk-dev] [PATCH v10 2/5] ethdev: add unique name to devices Declan Doherty
2014-06-27 10:18               ` [dpdk-dev] [PATCH v10 3/5] eal: support link bonding device initialization Declan Doherty
2014-06-27 10:18               ` [dpdk-dev] [PATCH v10 4/5] bond: unit tests Declan Doherty
2014-06-27 10:18               ` [dpdk-dev] [PATCH v10 5/5] bond: testpmd support Declan Doherty
2014-06-25 20:07           ` [dpdk-dev] [PATCH v8 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty
2014-06-25 20:07           ` [dpdk-dev] [PATCH v8 2/6] Support for unique interface naming of pmds Declan Doherty
2014-06-25 20:07           ` [dpdk-dev] [PATCH v8 3/6] EAL support for link bonding device initialization Declan Doherty
2014-06-25 20:07           ` [dpdk-dev] [PATCH v8 4/6] Link bonding Unit Tests Declan Doherty
2014-06-25 20:07           ` [dpdk-dev] [PATCH v8 5/6] testpmd link bonding additions Declan Doherty
2014-06-25 20:07           ` [dpdk-dev] [PATCH v8 6/6] Link Bonding Library doxygen additions Declan Doherty
2014-06-24 16:03         ` [dpdk-dev] [PATCH v7 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty
2014-06-24 16:03         ` [dpdk-dev] [PATCH v7 2/6] Support for unique interface naming of pmds Declan Doherty
2014-06-24 16:03         ` [dpdk-dev] [PATCH v7 3/6] EAL support for link bonding device initialization Declan Doherty
2014-06-25 13:54           ` Thomas Monjalon
2014-06-25 14:41             ` Doherty, Declan
2014-06-25 16:00               ` Thomas Monjalon
2014-06-25 16:15                 ` Richardson, Bruce
2014-06-24 16:03         ` [dpdk-dev] [PATCH v7 4/6] Link bonding Unit Tests Declan Doherty
2014-06-24 16:03         ` [dpdk-dev] [PATCH v7 5/6] testpmd link bonding additions Declan Doherty
2014-06-24 16:03         ` [dpdk-dev] [PATCH v7 6/6] Link Bonding Library doxygen additions Declan Doherty
2014-06-25 13:43           ` Thomas Monjalon
2014-06-25 14:19             ` Doherty, Declan
2014-06-25 14:23               ` Thomas Monjalon
2014-06-24 14:52       ` [dpdk-dev] [PATCH v6 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty
2014-06-24 14:52       ` [dpdk-dev] [PATCH v6 2/6] Support for unique interface naming of pmds Declan Doherty
2014-06-24 14:52       ` [dpdk-dev] [PATCH v6 3/6] EAL support for link bonding device initialization Declan Doherty
2014-06-24 14:52       ` [dpdk-dev] [PATCH v6 4/6] Link bonding Unit Tests Declan Doherty
2014-06-24 14:52       ` [dpdk-dev] [PATCH v6 5/6] testpmd link bonding additions Declan Doherty
2014-06-24 14:52       ` [dpdk-dev] [PATCH v6 6/6] Link Bonding Library doxygen additions Declan Doherty
2014-06-18 16:14     ` [dpdk-dev] [PATCH v5 1/6] Link Bonding Library (lib/librte_pmd_bond) Declan Doherty
2014-06-18 16:14     ` [dpdk-dev] [PATCH v5 2/6] Support for unique interface naming of pmds Declan Doherty
2014-06-18 16:14     ` [dpdk-dev] [PATCH v5 3/6] EAL support for link bonding device initialization Declan Doherty
2014-06-18 16:14     ` [dpdk-dev] [PATCH v5 4/6] Link bonding Unit Tests Declan Doherty
2014-06-18 16:14     ` [dpdk-dev] [PATCH v5 5/6] testpmd link bonding additions Declan Doherty
2014-06-18 16:14     ` [dpdk-dev] [PATCH v5 6/6] Link Bonding Library doxygen additions Declan Doherty
2014-06-16 11:18   ` [dpdk-dev] [PATCH v4 1/6] Link Bonding Library (lib/librte_pmd_bond) initial release with support for Mode 0 - Round Robin Mode 1 - Active Backup Mode 2 - Balance -> Supports 3 transmit polices (layer 2, layer 2+3, la Mode 3 - Broadcast Declan Doherty
2014-06-16 11:18   ` [dpdk-dev] [PATCH v4 2/6] Support for unique interface naming of pmds Declan Doherty
2014-06-16 11:18   ` [dpdk-dev] [PATCH v4 3/6] EAL support for link bonding device initialization Declan Doherty
2014-06-16 11:18   ` [dpdk-dev] [PATCH v4 4/6] Link bonding Unit Tests Declan Doherty
2014-06-16 11:18   ` [dpdk-dev] [PATCH v4 5/6] testpmd link bonding additions Declan Doherty
2014-06-16 11:18   ` [dpdk-dev] [PATCH v4 6/6] Link Bonding Library doxygen additions Declan Doherty

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3e846e0c5e2175761cf4ba64c283667e8d59f351.1402662300.git.declan.doherty@intel.com \
    --to=declan.doherty@intel.com \
    --cc=dev@dpdk.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).