Merge branch 'net-bridge-fix-vlan-range-dumps-starting-with-a-pvid'

Nikolay Aleksandrov says:

====================
net: bridge: fix vlan range dumps starting with a PVID

Patch 01 fixes a bug that can skip dumping VLANs which a part of a range
starting with a PVID VLAN and share the same flags. PVID VLAN should be
always on its own. Patch 02 adds a selftest for this case. More information
can be found in the respective patches.
====================

Link: https://patch.msgid.link/20260721140922.682265-1-razor@blackwall.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-07-22 10:23:20 -07:00
commit 3cdb9f88e3
5 changed files with 36 additions and 9 deletions

View File

@ -271,7 +271,8 @@ static void __vlan_tunnel_handle_range(const struct net_bridge_port *p,
if (!*v_start)
goto out_init;
if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) {
if (v && curr_change &&
br_vlan_can_enter_range(v, *v_end, br_get_pvid(vg))) {
*v_end = v;
return;
}

View File

@ -1627,7 +1627,8 @@ void br_vlan_notify(const struct net_bridge *br,
u16 vid, u16 vid_range,
int cmd);
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *range_end);
const struct net_bridge_vlan *range_end,
u16 pvid);
void br_vlan_fill_forward_path_pvid(struct net_bridge *br,
struct net_device_path_ctx *ctx,
@ -1874,7 +1875,8 @@ static inline void br_vlan_notify(const struct net_bridge *br,
}
static inline bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *range_end)
const struct net_bridge_vlan *range_end,
u16 pvid)
{
return true;
}

View File

@ -1982,9 +1982,11 @@ void br_vlan_notify(const struct net_bridge *br,
/* check if v_curr can enter a range ending in range_end */
bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *range_end)
const struct net_bridge_vlan *range_end,
u16 pvid)
{
return v_curr->vid - range_end->vid == 1 &&
return v_curr->vid != pvid && range_end->vid != pvid &&
v_curr->vid - range_end->vid == 1 &&
range_end->flags == v_curr->flags &&
br_vlan_opts_eq_range(v_curr, range_end);
}
@ -2066,8 +2068,8 @@ static int br_vlan_dump_dev(const struct net_device *dev,
idx += range_end->vid - range_start->vid + 1;
range_start = v;
} else if (dump_stats || v->vid == pvid ||
!br_vlan_can_enter_range(v, range_end)) {
} else if (dump_stats ||
!br_vlan_can_enter_range(v, range_end, pvid)) {
u16 vlan_flags = br_vlan_flags(range_start, pvid);
if (!br_vlan_fill_vids(skb, range_start->vid,

View File

@ -350,8 +350,7 @@ int br_vlan_process_options(const struct net_bridge *br,
continue;
}
if (v->vid == pvid ||
!br_vlan_can_enter_range(v, curr_end)) {
if (!br_vlan_can_enter_range(v, curr_end, pvid)) {
br_vlan_notify(br, p, curr_start->vid,
curr_end->vid, RTM_NEWVLAN);
curr_start = v;

View File

@ -13,6 +13,7 @@ ALL_TESTS="
vlan_range_mcast_max_groups
vlan_range_mcast_n_groups
vlan_range_mcast_enabled
vlan_range_pvid
"
setup_prepare()
@ -191,6 +192,28 @@ vlan_range_mcast_enabled()
log_test "VLAN range grouping with mcast_enabled"
}
vlan_range_pvid()
{
RET=0
ip -n "$NS" link set dev br0 type bridge vlan_default_pvid 1
check_err $? "Failed to configure default PVID"
defer ip -n "$NS" link set dev br0 type bridge vlan_default_pvid 0
bridge -n "$NS" vlan add vid 2 dev dummy0 untagged
check_err $? "Failed to add VLAN 2"
defer bridge -n "$NS" vlan del vid 2 dev dummy0
bridge -n "$NS" -d vlan show dev dummy0 |
grep -Eq '(^|[[:space:]])2([[:space:]]|$)'
check_err $? "VLAN following PVID is missing from detailed dump"
bridge -n "$NS" -d vlan show dev dummy0 | grep -q "1-2"
check_fail $? "PVID was incorrectly included in a VLAN range"
log_test "PVID is isolated from VLAN dump ranges"
}
# Verify the newest tested option is supported
if ! bridge vlan help 2>&1 | grep -q "neigh_suppress"; then
echo "SKIP: iproute2 too old, missing per-VLAN neighbor suppression support"