[PATCH dpdk v3] net/tap: add software MAC address filtering

Stephen Hemminger stephen at networkplumber.org
Sun Mar 22 00:20:57 CET 2026


On Sat, 21 Mar 2026 15:48:40 +0100
Robin Jarry <rjarry at redhat.com> wrote:

> Linux TAP devices deliver all packets to userspace regardless of the
> PROMISC/ALLMULTI flags on the interface. When promiscuous mode is
> disabled, drop received packets whose destination MAC does not match
> any configured unicast or multicast address.
> 
> The receive path checks the destination MAC against the device's
> unicast address table (managed by the ethdev layer), the multicast
> address list (stored by the driver since the ethdev layer does not keep
> a copy), and accepts broadcast unconditionally. Promiscuous and
> all-multicast modes bypass the respective checks.
> 
> To support multiple unicast addresses via rte_eth_dev_mac_addr_add(),
> allocate mac_addrs with rte_zmalloc (TAP_MAX_MAC_ADDRS=16) instead of
> pointing into dev_private, and advertise the new limit in dev_infos_get.
> 
> Dropped packets are reported via per-queue xstats
> (rx_q<N>_mac_filter_drops).
> 
> Signed-off-by: Robin Jarry <rjarry at redhat.com>
> ---

Here is a test for this.

From f1f093323a713abdfeb36a486a16b4b816095953 Mon Sep 17 00:00:00 2001
From: Stephen Hemminger <stephen at networkplumber.org>
Date: Sat, 21 Mar 2026 16:14:00 -0700
Subject: [PATCH 2/2] net/tap: add tests for software MAC address filtering

Extend the TAP PMD test suite to cover the new MAC filtering API.
Verify that dev_info advertises multiple unicast addresses,
mac_addr_add/remove and set_mc_addr_list work correctly,
and mac_filter_drops xstats are present and reset properly.

Signed-off-by: Stephen Hemminger <stephen at networkplumber.org>
---
 app/test/test_pmd_tap.c | 133 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 133 insertions(+)

diff --git a/app/test/test_pmd_tap.c b/app/test/test_pmd_tap.c
index dabd7d3506..bd3d4a64cc 100644
--- a/app/test/test_pmd_tap.c
+++ b/app/test/test_pmd_tap.c
@@ -757,6 +757,135 @@ test_tap_setup(void)
 	return 0;
 }
 
+/*
+ * MAC address filtering tests.
+ *
+ * These exercise the new software MAC filtering paths added to TAP:
+ * mac_addr_add/remove, set_mc_addr_list, xstats for mac_filter_drops,
+ * and the increased max_mac_addrs reported in dev_info.
+ */
+
+static int
+test_tap_mac_filter_info(void)
+{
+	struct rte_eth_dev_info dev_info;
+
+	TEST_ASSERT_SUCCESS(rte_eth_dev_info_get(tap_port0, &dev_info),
+			    "failed to get dev info");
+	TEST_ASSERT(dev_info.max_mac_addrs >= 16,
+		    "max_mac_addrs=%u, expected >= 16",
+		    dev_info.max_mac_addrs);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_tap_mac_addr_ops(void)
+{
+	struct rte_ether_addr addr, addr2;
+
+	rte_eth_random_addr(addr.addr_bytes);
+	rte_eth_random_addr(addr2.addr_bytes);
+
+	TEST_ASSERT_SUCCESS(rte_eth_dev_mac_addr_add(tap_port0, &addr, 0),
+			    "mac_addr_add first address failed");
+	TEST_ASSERT_SUCCESS(rte_eth_dev_mac_addr_add(tap_port0, &addr2, 0),
+			    "mac_addr_add second address failed");
+
+	rte_eth_dev_mac_addr_remove(tap_port0, &addr2);
+	rte_eth_dev_mac_addr_remove(tap_port0, &addr);
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_tap_mc_addr_list(void)
+{
+	struct rte_ether_addr mc_addrs[3];
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		rte_eth_random_addr(mc_addrs[i].addr_bytes);
+		mc_addrs[i].addr_bytes[0] |= RTE_ETHER_GROUP_ADDR;
+	}
+
+	TEST_ASSERT_SUCCESS(rte_eth_dev_set_mc_addr_list(tap_port0, mc_addrs, 3),
+			    "set_mc_addr_list(3) failed");
+	TEST_ASSERT_SUCCESS(rte_eth_dev_set_mc_addr_list(tap_port0, mc_addrs, 1),
+			    "set_mc_addr_list(1) failed");
+	TEST_ASSERT_SUCCESS(rte_eth_dev_set_mc_addr_list(tap_port0, NULL, 0),
+			    "set_mc_addr_list(0) failed");
+
+	return TEST_SUCCESS;
+}
+
+static int
+test_tap_xstats_mac_filter(void)
+{
+	struct rte_eth_xstat_name *names = NULL;
+	struct rte_eth_xstat *xstats = NULL;
+	int nb_xstats, ret, i;
+	int found = 0;
+	int result = TEST_FAILED;
+
+	nb_xstats = rte_eth_xstats_get_names(tap_port0, NULL, 0);
+	TEST_ASSERT(nb_xstats > 0,
+		    "expected at least 1 xstat, got %d", nb_xstats);
+
+	names = calloc(nb_xstats, sizeof(*names));
+	xstats = calloc(nb_xstats, sizeof(*xstats));
+	if (names == NULL || xstats == NULL) {
+		printf("Error: failed to allocate xstats arrays\n");
+		goto out;
+	}
+
+	ret = rte_eth_xstats_get_names(tap_port0, names, nb_xstats);
+	if (ret != nb_xstats) {
+		printf("Error: xstats_get_names returned %d, expected %d\n",
+		       ret, nb_xstats);
+		goto out;
+	}
+
+	/* Verify at least one mac_filter_drops counter exists */
+	for (i = 0; i < nb_xstats; i++) {
+		if (strstr(names[i].name, "mac_filter_drops") != NULL)
+			found++;
+	}
+	if (found == 0) {
+		printf("Error: no mac_filter_drops xstat found\n");
+		goto out;
+	}
+
+	/* Reset and verify the mac_filter_drops counters are zero */
+	if (rte_eth_xstats_reset(tap_port0) != 0) {
+		printf("Error: xstats_reset failed\n");
+		goto out;
+	}
+
+	ret = rte_eth_xstats_get(tap_port0, xstats, nb_xstats);
+	if (ret != nb_xstats) {
+		printf("Error: xstats_get after reset returned %d\n", ret);
+		goto out;
+	}
+
+	for (i = 0; i < nb_xstats; i++) {
+		if (strstr(names[i].name, "mac_filter_drops") == NULL)
+			continue;
+		if (xstats[i].value != 0) {
+			printf("Error: %s not zero after reset\n",
+			       names[i].name);
+			goto out;
+		}
+	}
+
+	result = TEST_SUCCESS;
+
+out:
+	free(names);
+	free(xstats);
+	return result;
+}
+
 /* Individual test case wrappers */
 
 static int
@@ -1128,6 +1257,10 @@ static struct unit_test_suite test_pmd_tap_suite = {
 		TEST_CASE(test_tap_multiqueue),
 		TEST_CASE(test_tap_rx_queue_setup),
 		TEST_CASE(test_tap_tx_burst),
+		TEST_CASE(test_tap_mac_filter_info),
+		TEST_CASE(test_tap_mac_addr_ops),
+		TEST_CASE(test_tap_mc_addr_list),
+		TEST_CASE(test_tap_xstats_mac_filter),
 		TEST_CASES_END()
 	}
 };
-- 
2.53.0



More information about the dev mailing list