Commit 9874b1ba authored by Paolo Abeni's avatar Paolo Abeni
Browse files

Merge branch 'net-bridge-mcast-fix-mdb_n_entries-counting-warning'

Nikolay Aleksandrov says:

====================
net: bridge: mcast: fix mdb_n_entries counting warning

The first patch fixes a warning in the mdb_n_entries code which was
reported by syzbot, the second patch adds tests for different ways
which used to trigger said warning. For more information please check
the individual commit messages.
====================

Link: https://patch.msgid.link/20260213070031.1400003-1-nikolay@nvidia.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents c7d9be66 a8470953
Loading
Loading
Loading
Loading
+18 −27
Original line number Diff line number Diff line
@@ -244,14 +244,11 @@ br_multicast_port_vid_to_port_ctx(struct net_bridge_port *port, u16 vid)

	lockdep_assert_held_once(&port->br->multicast_lock);

	if (!br_opt_get(port->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED))
		return NULL;

	/* Take RCU to access the vlan. */
	rcu_read_lock();

	vlan = br_vlan_find(nbp_vlan_group_rcu(port), vid);
	if (vlan && !br_multicast_port_ctx_vlan_disabled(&vlan->port_mcast_ctx))
	if (vlan)
		pmctx = &vlan->port_mcast_ctx;

	rcu_read_unlock();
@@ -701,7 +698,10 @@ br_multicast_port_ngroups_inc_one(struct net_bridge_mcast_port *pmctx,
	u32 max = READ_ONCE(pmctx->mdb_max_entries);
	u32 n = READ_ONCE(pmctx->mdb_n_entries);

	if (max && n >= max) {
	/* enforce the max limit when it's a port pmctx or a port-vlan pmctx
	 * with snooping enabled
	 */
	if (!br_multicast_port_ctx_vlan_disabled(pmctx) && max && n >= max) {
		NL_SET_ERR_MSG_FMT_MOD(extack, "%s is already in %u groups, and mcast_max_groups=%u",
				       what, n, max);
		return -E2BIG;
@@ -736,9 +736,7 @@ static int br_multicast_port_ngroups_inc(struct net_bridge_port *port,
		return err;
	}

	/* Only count on the VLAN context if VID is given, and if snooping on
	 * that VLAN is enabled.
	 */
	/* Only count on the VLAN context if VID is given */
	if (!group->vid)
		return 0;

@@ -2011,6 +2009,18 @@ void br_multicast_port_ctx_init(struct net_bridge_port *port,
	timer_setup(&pmctx->ip6_own_query.timer,
		    br_ip6_multicast_port_query_expired, 0);
#endif
	/* initialize mdb_n_entries if a new port vlan is being created */
	if (vlan) {
		struct net_bridge_port_group *pg;
		u32 n = 0;

		spin_lock_bh(&port->br->multicast_lock);
		hlist_for_each_entry(pg, &port->mglist, mglist)
			if (pg->key.addr.vid == vlan->vid)
				n++;
		WRITE_ONCE(pmctx->mdb_n_entries, n);
		spin_unlock_bh(&port->br->multicast_lock);
	}
}

void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx)
@@ -2094,25 +2104,6 @@ static void __br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx)
		br_ip4_multicast_add_router(brmctx, pmctx);
		br_ip6_multicast_add_router(brmctx, pmctx);
	}

	if (br_multicast_port_ctx_is_vlan(pmctx)) {
		struct net_bridge_port_group *pg;
		u32 n = 0;

		/* The mcast_n_groups counter might be wrong. First,
		 * BR_VLFLAG_MCAST_ENABLED is toggled before temporary entries
		 * are flushed, thus mcast_n_groups after the toggle does not
		 * reflect the true values. And second, permanent entries added
		 * while BR_VLFLAG_MCAST_ENABLED was disabled, are not reflected
		 * either. Thus we have to refresh the counter.
		 */

		hlist_for_each_entry(pg, &pmctx->port->mglist, mglist) {
			if (pg->key.addr.vid == pmctx->vlan->vid)
				n++;
		}
		WRITE_ONCE(pmctx->mdb_n_entries, n);
	}
}

static void br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx)
+88 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ ALL_TESTS="
	test_8021d
	test_8021q
	test_8021qvs
	test_mdb_count_warning
"

NUM_NETIFS=4
@@ -83,8 +84,6 @@ switch_create_8021q()
{
	local br_flags=$1; shift

	log_info "802.1q $br_flags${br_flags:+ }tests"

	ip link add name br0 type bridge vlan_filtering 1 vlan_default_pvid 0 \
		mcast_snooping 1 $br_flags \
		mcast_igmp_version 3 mcast_mld_version 2
@@ -106,6 +105,7 @@ switch_create_8021q()

switch_create_8021qvs()
{
	log_info "802.1q mcast_vlan_snooping 1 tests"
	switch_create_8021q "mcast_vlan_snooping 1"
	bridge vlan global set dev br0 vid 10 mcast_igmp_version 3
	bridge vlan global set dev br0 vid 10 mcast_mld_version 2
@@ -1272,6 +1272,76 @@ test_8021qvs_toggle_vlan_snooping()
	test_toggle_vlan_snooping_permanent
}

mdb_count_check_warn()
{
	local msg=$1; shift

	dmesg | grep -q "WARNING:.*br_multicast_port_ngroups_dec.*"
	check_fail $? "$msg"
}

test_mdb_count_mcast_vlan_snooping_flush()
{
	RET=0

	# check if we already have a warning
	mdb_count_check_warn "Check MDB entries count warning before test"

	bridge mdb add dev br0 port "$swp1" grp 239.0.0.1 permanent vid 10
	ip link set dev br0 down
	ip link set dev br0 type bridge mcast_vlan_snooping 1
	bridge mdb flush dev br0

	mdb_count_check_warn "Check MDB entries count warning after test"

	ip link set dev br0 type bridge mcast_vlan_snooping 0
	ip link set dev br0 up

	log_test "MDB count warning: mcast_vlan_snooping and MDB flush"
}

test_mdb_count_mcast_snooping_flush()
{
	RET=0

	# check if we already have a warning
	mdb_count_check_warn "Check MDB entries count warning before test"

	bridge mdb add dev br0 port "$swp1" grp 239.0.0.1 permanent vid 10
	ip link set dev br0 type bridge mcast_snooping 0
	ip link set dev br0 type bridge mcast_vlan_snooping 1
	bridge mdb flush dev br0

	mdb_count_check_warn "Check MDB entries count warning after test"

	ip link set dev br0 type bridge mcast_vlan_snooping 0
	ip link set dev br0 type bridge mcast_snooping 1

	log_test "MDB count warning: mcast_snooping and MDB flush"
}

test_mdb_count_vlan_state_flush()
{
	RET=0

	# check if we already have a warning
	mdb_count_check_warn "Check MDB entries count warning before test"

	bridge mdb add dev br0 port "$swp1" grp 239.0.0.1 permanent vid 10
	ip link set dev br0 down
	bridge vlan set vid 10 dev "$swp1" state blocking
	ip link set dev br0 type bridge mcast_vlan_snooping 1
	ip link set dev br0 up
	bridge mdb flush dev br0

	mdb_count_check_warn "Check MDB entries count warning after test"

	bridge vlan set vid 10 dev "$swp1" state forwarding
	ip link set dev br0 type bridge mcast_vlan_snooping 0

	log_test "MDB count warning: disabled vlan state and MDB flush"
}

# test groups

test_8021d()
@@ -1297,6 +1367,7 @@ test_8021q()
{
	# Tests for vlan_filtering 1 mcast_vlan_snooping 0.

	log_info "802.1q tests"
	switch_create_8021q
	setup_wait

@@ -1334,6 +1405,21 @@ test_8021qvs()
	switch_destroy
}

test_mdb_count_warning()
{
	# Tests for mdb_n_entries warning

	log_info "MDB count warning tests"
	switch_create_8021q
	setup_wait

	test_mdb_count_mcast_vlan_snooping_flush
	test_mdb_count_mcast_snooping_flush
	test_mdb_count_vlan_state_flush

	switch_destroy
}

if ! bridge link help 2>&1 | grep -q "mcast_max_groups"; then
	echo "SKIP: iproute2 too old, missing bridge \"mcast_max_groups\" support"
	exit $ksft_skip