On Sat, 21 Mar 2026 15:48:40 +0100 Robin Jarry <[email protected]> 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 <[email protected]> > --- Here is a test for this. From f1f093323a713abdfeb36a486a16b4b816095953 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger <[email protected]> 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 <[email protected]> --- 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

