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

Stephen Hemminger
2026-03-21T23:21:05Z

On Sat, 21 Mar 2026 15:48:40 +0100
Robin Jarry <rjarry@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@redhat.com>
> ---

Here is a test for this.

From f1f093323a713abdfeb36a486a16b4b816095953 Mon Sep 17 00:00:00 2001
From: Stephen Hemminger <stephen@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@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