From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2CE592472B6; Sat, 7 Mar 2026 04:46:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858772; cv=none; b=g9eCu3eHAoN23s0SoIUJGyy5gtes1mZI9Ky7r5hwqDmVfHBU9z2J6OWdY0A60OEOiZ6YshS6UDR8g1m97R00tQiUY5gizwfACW+oaJCVxLw+STWJWMZ1sHmzi6LO0n99bYmR/fDouaPOeYaYE9h5vxvJsu8HW7sM1yI+LFTt7Y0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858772; c=relaxed/simple; bh=IFecUremhg3u00+Jo1RGIULqOMOIEUZQbifAaAop3DY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=L49l3vEI5HxcXykrcpr/18I0c0U8s/MohvA8iAV3G9YTnBMAyl9LjRt4x2nGeGX6AqHbkiemzRaf3mSd1al7KYmLdVAq+KZGBZfvb9DtwdhnSt2jNjp+6WjHfkIlN33MG7zYGU3v+ryJMtkaNqIFNYsM0m50fZHVVIEkigEajmo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 24FAD54CDF2; Sat, 7 Mar 2026 05:46:02 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 01/14] net: bridge: mcast: export ip{4,6}_active state to netlink Date: Sat, 7 Mar 2026 05:45:35 +0100 Message-ID: <20260307044548.5230-2-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Export the new ip{4,6}_active variables to netlink, to be able to check from userspace that they are updated as intended. Signed-off-by: Linus L=C3=BCssing Reviewed-by: Ido Schimmel --- include/uapi/linux/if_bridge.h | 2 ++ include/uapi/linux/if_link.h | 12 ++++++++++++ net/bridge/br_netlink.c | 20 +++++++++++++++++++- net/bridge/br_private.h | 2 +- net/bridge/br_vlan_options.c | 25 +++++++++++++++++++++++-- net/core/rtnetlink.c | 2 +- 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index e52f8207ab27..910103b1ef03 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -584,6 +584,8 @@ enum { BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS, BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE, BRIDGE_VLANDB_GOPTS_MSTI, + BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4, + BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6, __BRIDGE_VLANDB_GOPTS_MAX }; #define BRIDGE_VLANDB_GOPTS_MAX (__BRIDGE_VLANDB_GOPTS_MAX - 1) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 83a96c56b8ca..66c59953d301 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -744,6 +744,16 @@ enum in6_addr_gen_mode { * @IFLA_BR_FDB_MAX_LEARNED * Set the number of max dynamically learned FDB entries for the current * bridge. + * + * @IFLA_BR_MCAST_ACTIVE_V4 + * Bridge IPv4 mcast active state, read only. + * + * 1 if an IGMP querier is present, 0 otherwise. + * + * @IFLA_BR_MCAST_ACTIVE_V6 + * Bridge IPv6 mcast active state, read only. + * + * 1 if an MLD querier is present, 0 otherwise. */ enum { IFLA_BR_UNSPEC, @@ -796,6 +806,8 @@ enum { IFLA_BR_MCAST_QUERIER_STATE, IFLA_BR_FDB_N_LEARNED, IFLA_BR_FDB_MAX_LEARNED, + IFLA_BR_MCAST_ACTIVE_V4, + IFLA_BR_MCAST_ACTIVE_V6, __IFLA_BR_MAX, }; =20 diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 0264730938f4..7fff59c3bf76 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -1264,7 +1264,9 @@ static const struct nla_policy br_policy[IFLA_BR_MAX = + 1] =3D { [IFLA_BR_VLAN_STATS_ENABLED] =3D { .type =3D NLA_U8 }, [IFLA_BR_MCAST_STATS_ENABLED] =3D { .type =3D NLA_U8 }, [IFLA_BR_MCAST_IGMP_VERSION] =3D { .type =3D NLA_U8 }, + [IFLA_BR_MCAST_ACTIVE_V4] =3D { .type =3D NLA_REJECT }, [IFLA_BR_MCAST_MLD_VERSION] =3D { .type =3D NLA_U8 }, + [IFLA_BR_MCAST_ACTIVE_V6] =3D { .type =3D NLA_REJECT }, [IFLA_BR_VLAN_STATS_PER_PORT] =3D { .type =3D NLA_U8 }, [IFLA_BR_MULTI_BOOLOPT] =3D NLA_POLICY_EXACT_LEN(sizeof(struct br_boolopt_multi)), @@ -1625,7 +1627,9 @@ static size_t br_get_size(const struct net_device *br= dev) nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_RESPONS= E_INTVL */ nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY= _INTVL */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_IGMP_VERSION */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_ACTIVE_V4 */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_MLD_VERSION */ + nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_ACTIVE_V6 */ br_multicast_querier_state_size() + /* IFLA_BR_MCAST_QUERIER_STATE= */ #endif #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) @@ -1639,6 +1643,8 @@ static size_t br_get_size(const struct net_device *br= dev) =20 static int br_fill_info(struct sk_buff *skb, const struct net_device *brde= v) { + struct ethhdr eth6 __maybe_unused =3D { .h_proto =3D htons(ETH_P_IPV6) }; + struct ethhdr eth4 __maybe_unused =3D { .h_proto =3D htons(ETH_P_IP) }; struct net_bridge *br =3D netdev_priv(brdev); u32 forward_delay =3D jiffies_to_clock_t(br->forward_delay); u32 hello_time =3D jiffies_to_clock_t(br->hello_time); @@ -1717,12 +1723,24 @@ static int br_fill_info(struct sk_buff *skb, const = struct net_device *brdev) br->multicast_ctx.multicast_startup_query_count) || nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION, br->multicast_ctx.multicast_igmp_version) || + nla_put_u8(skb, IFLA_BR_MCAST_ACTIVE_V4, + netif_running(brdev) && + br_opt_get(br, BROPT_MULTICAST_ENABLED) && + !br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && + br_multicast_querier_exists(&br->multicast_ctx, ð4, + NULL)) || br_multicast_dump_querier_state(skb, &br->multicast_ctx, IFLA_BR_MCAST_QUERIER_STATE)) return -EMSGSIZE; #if IS_ENABLED(CONFIG_IPV6) if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION, - br->multicast_ctx.multicast_mld_version)) + br->multicast_ctx.multicast_mld_version) || + nla_put_u8(skb, IFLA_BR_MCAST_ACTIVE_V6, + netif_running(brdev) && + br_opt_get(br, BROPT_MULTICAST_ENABLED) && + !br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && + br_multicast_querier_exists(&br->multicast_ctx, ð6, + NULL))) return -EMSGSIZE; #endif clockval =3D jiffies_to_clock_t(br->multicast_ctx.multicast_last_member_i= nterval); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 9b55d38ea9ed..545be319b6cb 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1902,7 +1902,7 @@ int br_vlan_rtm_process_global_options(struct net_dev= ice *dev, bool br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan *v_c= urr, const struct net_bridge_vlan *r_end); bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range, - const struct net_bridge_vlan *v_opts); + struct net_bridge_vlan *v_opts); =20 /* vlan state manipulation helpers using *_ONCE to annotate lock-free acce= ss, * while br_vlan_set_state() may access data protected by multicast_lock. diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c index 5514e1fc8d1f..06045d68a2d1 100644 --- a/net/bridge/br_vlan_options.c +++ b/net/bridge/br_vlan_options.c @@ -367,10 +367,13 @@ bool br_vlan_global_opts_can_enter_range(const struct= net_bridge_vlan *v_curr, } =20 bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range, - const struct net_bridge_vlan *v_opts) + struct net_bridge_vlan *v_opts) { + struct ethhdr eth6 __maybe_unused =3D { .h_proto =3D htons(ETH_P_IPV6) }; + struct ethhdr eth4 __maybe_unused =3D { .h_proto =3D htons(ETH_P_IP) }; struct nlattr *nest2 __maybe_unused; u64 clockval __maybe_unused; + bool mcact __maybe_unused; struct nlattr *nest; =20 nest =3D nla_nest_start(skb, BRIDGE_VLANDB_GLOBAL_OPTIONS); @@ -385,10 +388,17 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u1= 6 vid, u16 vid_range, goto out_err; =20 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING + mcact =3D netif_running(v_opts->br->dev) && + br_opt_get(v_opts->br, BROPT_MULTICAST_ENABLED) && + br_opt_get(v_opts->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && + !br_multicast_ctx_vlan_global_disabled(&v_opts->br_mcast_ctx) && + br_multicast_querier_exists(&v_opts->br_mcast_ctx, ð4, NULL); + if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING, !!(v_opts->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED)) || nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION, v_opts->br_mcast_ctx.multicast_igmp_version) || + nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4, mcact) || nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT, v_opts->br_mcast_ctx.multicast_last_member_count) || nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT, @@ -442,8 +452,15 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u16= vid, u16 vid_range, } =20 #if IS_ENABLED(CONFIG_IPV6) + mcact =3D netif_running(v_opts->br->dev) && + br_opt_get(v_opts->br, BROPT_MULTICAST_ENABLED) && + br_opt_get(v_opts->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && + !br_multicast_ctx_vlan_global_disabled(&v_opts->br_mcast_ctx) && + br_multicast_querier_exists(&v_opts->br_mcast_ctx, ð6, NULL); + if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION, - v_opts->br_mcast_ctx.multicast_mld_version)) + v_opts->br_mcast_ctx.multicast_mld_version) || + nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6, mcact)) goto out_err; #endif #endif @@ -468,7 +485,9 @@ static size_t rtnl_vlan_global_opts_nlmsg_size(const st= ruct net_bridge_vlan *v) #ifdef CONFIG_BRIDGE_IGMP_SNOOPING + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING */ + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION */ + + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4 */ + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION */ + + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6 */ + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_C= NT */ + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY= _CNT */ + nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_I= NTVL */ @@ -650,9 +669,11 @@ static const struct nla_policy br_vlan_db_gpol[BRIDGE_= VLANDB_GOPTS_MAX + 1] =3D { [BRIDGE_VLANDB_GOPTS_RANGE] =3D { .type =3D NLA_U16 }, [BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING] =3D { .type =3D NLA_U8 }, [BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION] =3D { .type =3D NLA_U8 }, + [BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6] =3D { .type =3D NLA_REJECT }, [BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL] =3D { .type =3D NLA_U64 }, [BRIDGE_VLANDB_GOPTS_MCAST_QUERIER] =3D { .type =3D NLA_U8 }, [BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION] =3D { .type =3D NLA_U8 }, + [BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4] =3D { .type =3D NLA_REJECT }, [BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT] =3D { .type =3D NLA_U32 }, [BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT] =3D { .type =3D NLA_U32 }, [BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL] =3D { .type =3D NLA_U64 }, diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index dad4b1054955..8febe7475f1f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -62,7 +62,7 @@ =20 #include "dev.h" =20 -#define RTNL_MAX_TYPE 50 +#define RTNL_MAX_TYPE 52 #define RTNL_SLAVE_MAX_TYPE 44 =20 struct rtnl_link { --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D025033343B; Sat, 7 Mar 2026 04:46:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858775; cv=none; b=Za5b9yZcfoKKyJWjDiK3H0KTP4NqB2cz56UzAh7uVWXJpT4iGOMJOiPiCffdWw4z9pgdxOwEpfsAUK3k21I86r6fxcPtZXcwQu+rBlwIk/mNqmcuMifdYaiblZonT5/fhYwPAIA4HY8+rR5w6ZbLdCnkBaIG3gJKI3ckrJSDLaI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858775; c=relaxed/simple; bh=zBJOqATIXLbw8kMDkg4KFg57IXtqIqZV3Mz7gkChBss=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=jKnDDmfqbXA81HBGkTI4rjA0G/w58YxC0k2fYrDJxYEfNzavMTF7oMdjd6IC/k/VkWh/Ld+VV2vbxltUE0dkIWKWcT7vX35/OV1zm98osZe0nj8sgZ7gU3QzTBrzMh8y3SBzVbB3XQOWOZl+ZmCDjj8+GD/BShYzK7t+pijMpSY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 9928354CE95; Sat, 7 Mar 2026 05:46:03 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 02/14] net: bridge: mcast: track active state, adding tests Date: Sat, 7 Mar 2026 05:45:36 +0100 Message-ID: <20260307044548.5230-3-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Before making any significant changes to the internals of the Linux bridge add some tests regarding the multicast activity. This is also to verify that we have the semantics of the new *_MCAST_ACTIVE_{V4,V6} netlink attributes as expected. Signed-off-by: Linus L=C3=BCssing --- .../testing/selftests/net/forwarding/Makefile | 1 + .../net/forwarding/bridge_mdb_active.sh | 742 ++++++++++++++++++ 2 files changed, 743 insertions(+) create mode 100755 tools/testing/selftests/net/forwarding/bridge_mdb_activ= e.sh diff --git a/tools/testing/selftests/net/forwarding/Makefile b/tools/testin= g/selftests/net/forwarding/Makefile index bbaf4d937dd8..6692751b24d5 100644 --- a/tools/testing/selftests/net/forwarding/Makefile +++ b/tools/testing/selftests/net/forwarding/Makefile @@ -7,6 +7,7 @@ TEST_PROGS :=3D \ bridge_igmp.sh \ bridge_locked_port.sh \ bridge_mdb.sh \ + bridge_mdb_active.sh \ bridge_mdb_host.sh \ bridge_mdb_max.sh \ bridge_mdb_port_down.sh \ diff --git a/tools/testing/selftests/net/forwarding/bridge_mdb_active.sh b/= tools/testing/selftests/net/forwarding/bridge_mdb_active.sh new file mode 100755 index 000000000000..3c93c55ffa61 --- /dev/null +++ b/tools/testing/selftests/net/forwarding/bridge_mdb_active.sh @@ -0,0 +1,742 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# shellcheck disable=3DSC2317 +# +# Disabling SC2317 because: +# 1) shellcheck does not seem to like a trap together with a final exit ca= ll +# 2) shellcheck does not seem to understand that tests_run calls all funct= ions +# listed in ALL_TESTS + +# +-------+ +---------+ +# | brq0 | | br0 | +# | + $h1 | | + $swp1 | +# +----|--+ +----|----+ +# | | +# \--------------/ +# +# +# This script checks if we have the expected mcast_active_v{4,6} state +# on br0 in a variety of scenarios. This state determines if ultimately +# multicast snooping is applied to multicast data packets +# (multicast snooping active) or if they are (by default) flooded instead +# (multicast snooping inactive). +# +# Notably, multicast snooping can be enabled but still be inactive if not = all +# requirements to safely apply multicast snooping to multicast data packets +# are met. +# +# Depending on the test case an IGMP/MLD querier might be on brq0, on br0 +# or neither. + + +ALL_TESTS=3D" + test_inactive + test_active_other_querier + test_active_own_querier + test_inactive_brdown + test_inactive_nov6 + test_inactive_snooping_off + test_inactive_querier_off + test_inactive_other_querier_norespdelay + test_inactive_own_querier_norespdelay + test_inactive + test_vlan_inactive + test_vlan_active_other_querier + test_vlan_active_own_querier + test_vlan_inactive_brdown + test_vlan_inactive_nov6 + test_vlan_inactive_snooping_off + test_vlan_inactive_vlans_snooping_off + test_vlan_inactive_vlan_snooping_off + test_vlan_inactive_other_querier_norespdelay + test_vlan_inactive_own_querier_norespdelay +" + +NUM_NETIFS=3D2 +MCAST_MAX_RESP_IVAL_SEC=3D1 +MCAST_VLAN_ID=3D42 +source lib.sh + +switch_create() +{ + ip link add dev br0 type bridge\ + vlan_filtering 0 \ + mcast_query_response_interval $((MCAST_MAX_RESP_IVAL_SEC*100))\ + mcast_snooping 0 \ + mcast_vlan_snooping 0 + ip link add dev brq0 type bridge\ + vlan_filtering 0 \ + mcast_query_response_interval $((MCAST_MAX_RESP_IVAL_SEC*100))\ + mcast_snooping 0 \ + mcast_vlan_snooping 0 + + echo 1 > /proc/sys/net/ipv6/conf/br0/disable_ipv6 + echo 1 > /proc/sys/net/ipv6/conf/brq0/disable_ipv6 + + ip link set dev "$swp1" master br0 + ip link set dev "$h1" master brq0 + + ip link set dev "$h1" up + ip link set dev "$swp1" up +} + +switch_destroy() +{ + ip link set dev "$swp1" down + ip link set dev "$h1" down + + ip link del dev brq0 + ip link del dev br0 +} + +setup_prepare() +{ + h1=3D${NETIFS[p1]} + swp1=3D${NETIFS[p2]} + + switch_create +} + +cleanup() +{ + pre_cleanup + switch_destroy +} + +mcast_active_check_failed() +{ + local af=3D"$1" + local vlan=3D"$2" + local active=3D"$3" + local lineno=3D"$4" + + check_err 1 "$lineno: Mcast ${vlan}${active} check failed ($af)" +} + +mcast_active_check_ret() +{ + local ret=3D"$1" + local af=3D"$2" + local state=3D"$3" + local vlan=3D"$4" + local lineno=3D"$5" + + if [ "$state" -eq 0 ]; then + if [ "$ret" -eq 0 ]; then + mcast_active_check_failed \ + "$af" "$vlan" "inactive" "$lineno" + fi + elif [ "$state" -eq 1 ]; then + if [ "$ret" -ne 0 ]; then + mcast_active_check_failed \ + "$af" "$vlan" "active" "$lineno" + fi + fi +} + +jq_check_mcast_active() +{ + local af=3D"$1" + + jq -e ".[] | select(.linkinfo.info_data.mcast_active_$af =3D=3D 1)" +} + +mcast_active_check() +{ + local af=3D"$1" + local state=3D"$2" + local lineno=3D"$3" + + ip -d -j link show dev br0\ + | jq_check_mcast_active "$af" &> /dev/null + mcast_active_check_ret "$?" "$af" "$state" "" "$lineno" +} + +jq_check_vlan_mcast_active() +{ + local vid=3D"$1" + local af=3D"$2" + + jq -e ".[].vlans.[] | select(.vlan =3D=3D $vid and .mcast_active_$af =3D= =3D 1)" +} + +mcast_vlan_active_check() +{ + local af=3D"$1" + local state=3D"$2" + local lineno=3D"$3" + local vid=3D"${MCAST_VLAN_ID}" + + bridge -j vlan global show dev br0\ + | jq_check_vlan_mcast_active "$vid" "$af" &> /dev/null + mcast_active_check_ret "$?" "$af" "$state" "VLAN " "$lineno" +} + +mcast_assert_active_v4() +{ + mcast_active_check "v4" "1" "$1" +} + +mcast_assert_active_v6() +{ + mcast_active_check "v6" "1" "$1" +} + +mcast_assert_inactive_v4() +{ + mcast_active_check "v4" "0" "$1" +} + +mcast_assert_inactive_v6() +{ + mcast_active_check "v6" "0" "$1" +} + +mcast_vlan_assert_active_v4() +{ + mcast_vlan_active_check "v4" "1" "$1" +} + +mcast_vlan_assert_active_v6() +{ + mcast_vlan_active_check "v6" "1" "$1" +} + +mcast_vlan_assert_inactive_v4() +{ + mcast_vlan_active_check "v4" "0" "$1" +} + +mcast_vlan_assert_inactive_v6() +{ + mcast_vlan_active_check "v6" "0" "$1" +} + +test_inactive_nolog() +{ + local lineno=3D"$1" + + ip link set dev br0 down + ip link set dev brq0 down + ip link set dev br0 type bridge mcast_snooping 0 + ip link set dev brq0 type bridge mcast_snooping 0 + ip link set dev br0 type bridge mcast_querier 0 + ip link set dev brq0 type bridge mcast_querier 0 + ip link set dev br0 type bridge mcast_vlan_snooping 0 + ip link set dev br0 type bridge vlan_filtering 0 + + echo 1 > /proc/sys/net/ipv6/conf/br0/disable_ipv6 + echo 1 > /proc/sys/net/ipv6/conf/brq0/disable_ipv6 + + mcast_assert_inactive_v4 "$lineno" + mcast_assert_inactive_v6 "$lineno" +} + +test_inactive() +{ + RET=3D0 + + test_inactive_nolog "$LINENO" + log_test "Mcast inactive test" +} + +wait_lladdr_dad() { + local jq_select + local jq_tentative + + jq_select=3D"select(.scope =3D=3D \"link\" and ((.tentative =3D=3D true) = | not))" + jq_tentative=3D"map(${jq_select}) | .[]" + + ip -6 -j a s dev "$1"\ + | jq -e ".[].addr_info | ${jq_tentative}" &> /dev/null +} + +test_active_setup_bridge() +{ + [ -n "$1" ] && echo 0 > /proc/sys/net/ipv6/conf/br0/disable_ipv6 + [ -n "$2" ] && echo 0 > /proc/sys/net/ipv6/conf/brq0/disable_ipv6 + + [ -n "$3" ] && ip link set dev br0 up + [ -n "$4" ] && ip link set dev brq0 up + [ -n "$5" ] && slowwait 3 wait_lladdr_dad br0 + [ -n "$6" ] && slowwait 3 wait_lladdr_dad brq0 +} + +test_active_setup_config() +{ + [ -n "$1" ] && ip link set dev br0 type bridge mcast_snooping 1 + [ -n "$2" ] && ip link set dev brq0 type bridge mcast_snooping 1 + [ -n "$3" ] && ip link set dev br0 type bridge mcast_querier 1 + [ -n "$4" ] && ip link set dev brq0 type bridge mcast_querier 1 +} + +test_active_setup_wait() +{ + sleep $((MCAST_MAX_RESP_IVAL_SEC * 2)) +} + +test_active_setup_reset_own_querier() +{ + ip link set dev br0 type bridge mcast_querier 0 + ip link set dev br0 type bridge mcast_querier 1 + + test_active_setup_wait +} + +test_vlan_active_setup_config() +{ + [ -n "$1" ] && ip link set dev br0 type bridge vlan_filtering 1 + [ -n "$2" ] && ip link set dev brq0 type bridge vlan_filtering 1 + [ -n "$3" ] && ip link set dev br0 type bridge mcast_vlan_snooping 1 + [ -n "$4" ] && ip link set dev brq0 type bridge mcast_vlan_snooping 1 +} + +test_vlan_active_setup_add_vlan() +{ + bridge vlan add vid "${MCAST_VLAN_ID}" dev "$swp1" + bridge vlan add vid "${MCAST_VLAN_ID}" dev "$h1" + bridge vlan global set vid "${MCAST_VLAN_ID}" dev br0\ + mcast_query_response_interval $((MCAST_MAX_RESP_IVAL_SEC*100)) + bridge vlan global set vid "${MCAST_VLAN_ID}" dev brq0\ + mcast_query_response_interval $((MCAST_MAX_RESP_IVAL_SEC*100)) + bridge vlan global set vid "${MCAST_VLAN_ID}" dev br0 mcast_snooping 0 + bridge vlan global set vid "${MCAST_VLAN_ID}" dev brq0 mcast_snooping 0 + bridge vlan global set vid "${MCAST_VLAN_ID}" dev br0 mcast_querier 0 + bridge vlan global set vid "${MCAST_VLAN_ID}" dev brq0 mcast_querier 0 +} + +test_vlan_active_setup_config_vlan() +{ + [ -n "$1" ] && bridge vlan global set \ + vid "${MCAST_VLAN_ID}" dev br0 mcast_snooping 1 + [ -n "$2" ] && bridge vlan global set \ + vid "${MCAST_VLAN_ID}" dev brq0 mcast_snooping 1 + [ -n "$3" ] && bridge vlan global set \ + vid "${MCAST_VLAN_ID}" dev br0 mcast_querier 1 + [ -n "$4" ] && bridge vlan global set \ + vid "${MCAST_VLAN_ID}" dev brq0 mcast_querier 1 +} + +test_vlan_teardown() +{ + bridge vlan del vid "${MCAST_VLAN_ID}" dev "$swp1" + bridge vlan del vid "${MCAST_VLAN_ID}" dev "$h1" + mcast_assert_inactive_v4 "$1" + mcast_assert_inactive_v6 "$1" + mcast_vlan_assert_inactive_v4 "$1" + mcast_vlan_assert_inactive_v6 "$1" +} + +test_vlan_active_setup_reset_own_querier() +{ + bridge vlan global set vid "${MCAST_VLAN_ID}" dev br0 mcast_querier 0 + bridge vlan global set vid "${MCAST_VLAN_ID}" dev br0 mcast_querier 1 + + test_active_setup_wait +} + +test_active_other_querier_nolog() +{ + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "4" + test_active_setup_wait + + mcast_assert_active_v4 "$1" + mcast_assert_active_v6 "$1" +} + +test_active_other_querier() +{ + RET=3D0 + + test_active_other_querier_nolog "$LINENO" + test_inactive_nolog "$LINENO" + log_test "Mcast active with other querier test" +} + +test_active_own_querier_nolog() +{ + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "3" "" + test_active_setup_wait + + mcast_assert_active_v4 "$1" + mcast_assert_active_v6 "$1" +} + +test_active_own_querier() +{ + RET=3D0 + + test_active_own_querier_nolog "$LINENO" + test_inactive_nolog "$LINENO" + log_test "Mcast active with own querier test" +} + +test_active_final() +{ + mcast_assert_active_v4 "$1" + mcast_assert_active_v6 "$1" + + test_inactive_nolog "$1" +} + +test_inactive_brdown() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "" "4" "" "6" + test_active_setup_config "1" "2" "3" "" + test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + test_active_setup_bridge "" "" "3" "" "" "" + mcast_assert_active_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + test_active_setup_bridge "" "" "" "" "5" "" + test_active_setup_reset_own_querier + test_active_final "$LINENO" + + log_test "Mcast inactive, bridge down test" +} + +test_inactive_nov6() +{ + RET=3D0 + + test_active_setup_bridge "" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "3" "" + test_active_setup_wait + + mcast_assert_active_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + test_active_setup_bridge "1" "" "" "" "5" "" + test_active_setup_reset_own_querier + test_active_final "$LINENO" + + log_test "Mcast inactive, own querier, no IPv6 address test" +} + +test_inactive_snooping_off() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "" "2" "3" "" + test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + test_active_setup_config "1" "" "" "" + test_active_setup_reset_own_querier + test_active_final "$LINENO" + + log_test "Mcast inactive, snooping disabled test" +} + +test_inactive_querier_off() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "" + test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + test_active_setup_config "" "" "3" "" + test_active_setup_wait + test_active_final "$LINENO" + + log_test "Mcast inactive, no querier test" +} + +test_inactive_other_querier_norespdelay() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "3" "" + # skipping: test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + test_active_setup_wait + test_active_final "$LINENO" + + log_test "Mcast inactive, other querier, no response delay test" +} + +test_inactive_own_querier_norespdelay() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "4" + # skipping: test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + test_active_setup_wait + test_active_final "$LINENO" + + log_test "Mcast inactive, own querier, no response delay test" +} + +test_vlan_inactive() +{ + RET=3D0 + + test_inactive_nolog "$LINENO" + mcast_vlan_assert_inactive_v4 "$LINENO" + mcast_vlan_assert_inactive_v6 "$LINENO" + + ip link set dev br0 type bridge vlan_filtering 1 + ip link set dev br0 type bridge mcast_vlan_snooping 1 + mcast_vlan_assert_inactive_v4 "$LINENO" + mcast_vlan_assert_inactive_v6 "$LINENO" + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + ip link set dev br0 type bridge mcast_vlan_snooping 0 + ip link set dev br0 type bridge vlan_filtering 0 + test_active_own_querier_nolog "$LINENO" + ip link set dev br0 type bridge vlan_filtering 1 + mcast_assert_active_v4 "$LINENO" + mcast_assert_active_v6 "$LINENO" + + ip link set dev br0 type bridge mcast_vlan_snooping 1 + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + test_inactive_nolog "$LINENO" + log_test "Mcast VLAN inactive test" +} + +test_vlan_active_final() +{ + mcast_assert_inactive_v4 "$1" + mcast_assert_inactive_v6 "$1" + mcast_vlan_assert_active_v4 "$1" + mcast_vlan_assert_active_v6 "$1" + + test_vlan_teardown "$1" + test_inactive_nolog "$1" +} + +test_vlan_active_other_querier() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "" + test_vlan_active_setup_config "1" "2" "3" "4" + test_vlan_active_setup_add_vlan + test_vlan_active_setup_config_vlan "1" "2" "" "4" + test_active_setup_wait + test_vlan_active_final "$LINENO" + + log_test "Mcast VLAN active, other querier test" +} + +test_vlan_active_own_querier() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "" + test_vlan_active_setup_config "1" "2" "3" "4" + test_vlan_active_setup_add_vlan + test_vlan_active_setup_config_vlan "1" "2" "3" "" + test_active_setup_wait + test_vlan_active_final "$LINENO" + + log_test "Mcast VLAN active, own querier test" +} + +test_vlan_inactive_brdown() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "" "4" "" "6" + test_active_setup_config "1" "2" "" "" + test_vlan_active_setup_config "1" "2" "3" "4" + test_vlan_active_setup_add_vlan + test_vlan_active_setup_config_vlan "1" "2" "3" "" + test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + mcast_vlan_assert_inactive_v4 "$LINENO" + mcast_vlan_assert_inactive_v6 "$LINENO" + + test_active_setup_bridge "" "" "3" "" "" "" + test_active_setup_wait + mcast_vlan_assert_active_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + + test_active_setup_bridge "" "" "" "" "5" "" + test_vlan_active_setup_reset_own_querier + test_vlan_active_final "$LINENO, RET: $RET" + + log_test "Mcast VLAN inactive, bridge down test" +} + +test_vlan_inactive_nov6() +{ + RET=3D0 + + test_active_setup_bridge "" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "" + test_vlan_active_setup_config "1" "2" "3" "4" + test_vlan_active_setup_add_vlan + test_vlan_active_setup_config_vlan "1" "2" "3" "" + test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + mcast_vlan_assert_active_v4 "$LINENO" + mcast_vlan_assert_inactive_v6 "$LINENO" + + test_active_setup_bridge "1" "" "" "" "5" "" + test_vlan_active_setup_reset_own_querier + test_vlan_active_final "$LINENO" + + log_test "Mcast VLAN inactive, own querier, no IPv6 address test" +} + +test_vlan_inactive_snooping_off() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "" "2" "" "" + test_vlan_active_setup_config "1" "2" "3" "4" + test_vlan_active_setup_add_vlan + test_vlan_active_setup_config_vlan "1" "2" "3" "" + test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + mcast_vlan_assert_inactive_v4 "$LINENO" + mcast_vlan_assert_inactive_v6 "$LINENO" + + test_active_setup_config "1" "" "" "" + test_vlan_active_setup_reset_own_querier + test_vlan_active_final "$LINENO" + + log_test "Mcast VLAN inactive, snooping disabled test" +} + +test_vlan_inactive_vlans_snooping_off() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "" + test_vlan_active_setup_config "1" "2" "" "4" + test_vlan_active_setup_add_vlan + test_vlan_active_setup_config_vlan "1" "2" "3" "" + test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + mcast_vlan_assert_inactive_v4 "$LINENO" + mcast_vlan_assert_inactive_v6 "$LINENO" + + test_vlan_active_setup_config "" "" "3" "" + test_vlan_active_setup_reset_own_querier + test_vlan_active_final "$LINENO" + + log_test "Mcast VLAN inactive, snooping for VLANs disabled test" +} + +test_vlan_inactive_vlan_snooping_off() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "" + test_vlan_active_setup_config "1" "2" "3" "4" + test_vlan_active_setup_add_vlan + test_vlan_active_setup_config_vlan "" "2" "3" "" + test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + mcast_vlan_assert_inactive_v4 "$LINENO" + mcast_vlan_assert_inactive_v6 "$LINENO" + + test_vlan_active_setup_config_vlan "1" "" "" "" + test_vlan_active_setup_reset_own_querier + test_vlan_active_final "$LINENO" + + log_test "Mcast VLAN inactive, snooping for this VLAN disabled test" +} + +test_vlan_inactive_other_querier_norespdelay() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "" + test_vlan_active_setup_config "1" "2" "3" "4" + test_vlan_active_setup_add_vlan + test_vlan_active_setup_config_vlan "1" "2" "" "4" + # skipping: test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + mcast_vlan_assert_inactive_v4 "$LINENO" + mcast_vlan_assert_inactive_v6 "$LINENO" + + test_active_setup_wait + test_vlan_active_final "$LINENO" + + log_test "Mcast VLAN inactive, other querier, no response delay test" +} + +test_vlan_inactive_own_querier_norespdelay() +{ + RET=3D0 + + test_active_setup_bridge "1" "2" "3" "4" "5" "6" + test_active_setup_config "1" "2" "" "" + test_vlan_active_setup_config "1" "2" "3" "4" + test_vlan_active_setup_add_vlan + test_vlan_active_setup_config_vlan "1" "2" "3" "" + # skipping: test_active_setup_wait + + mcast_assert_inactive_v4 "$LINENO" + mcast_assert_inactive_v6 "$LINENO" + mcast_vlan_assert_inactive_v4 "$LINENO" + mcast_vlan_assert_inactive_v6 "$LINENO" + + test_active_setup_wait + test_vlan_active_final "$LINENO" + + log_test "Mcast VLAN inactive, own querier, no response delay test" +} + +trap cleanup EXIT + +setup_prepare + +if ! ip -d link show dev br0 2>&1 | grep -q "mcast_active"; then + echo "SKIP: iproute2 too old, missing mcast_active support" + exit "$ksft_skip" +fi + +setup_wait +tests_run + +exit "$EXIT_STATUS" --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AABA633CE80; Sat, 7 Mar 2026 04:46:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858776; cv=none; b=oicXCNtXuNB+v8ueOQNA3fIvJAQc/N7PX8TapGMmJgu+es6I5MrB0+7jbpHw2dkWa2dOrvYodn2S5WF/xw7oBKV7dr58NeyqoT9SzAS+4Bp3n3smbI4tdHtvfhuOcMg+cBl5lmTeCva9NuV1Kss20RQz0LNJYP0gnGQIU81ITOo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858776; c=relaxed/simple; bh=BqcMT4Y91PQ7i2yERyfXLRZwXIdnTvjRM1ZWv8tlGQo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=R0JCBFtla3aDRSRnY1ETP9neXJFm1jshiqAgmCjwdXDIOEW/i1YfQd0HhTx2dbm/L7wBQ6HwwDuBGZZo3lF3GGwCN/YL6diYm5S/FewdWDMD18aLyZUYolXNZ2X3mj9hc/wVuh0pY0JgFLY5K8GS4/h3En9WeCHDdz5BBKxrMoo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 083C054CE9A; Sat, 7 Mar 2026 05:46:04 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 03/14] net: bridge: mcast: avoid sleeping on bridge-down Date: Sat, 7 Mar 2026 05:45:37 +0100 Message-ID: <20260307044548.5230-4-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 We later want to use the multicast lock when setting the bridge interface up or down, to be able to atomically both check all conditions to toggle the multicast active state and to subsequently toggle it. While most variables we check / contexts we check from are serialized (toggled variables through netlink/sysfs) the timer_pending() check is not and might run in parallel. However so far we are not allowed to spinlock __br_multicast_stop() as its call to timer_delete_sync() might sleep. Therefore replacing the sleeping variant with the non-sleeping timer_shutdown(). It is sufficient to only wait for any timer callback to finish when we are freeing the multicast context. While the disadvantage of using a non-syncing variant might lead us to race and still execute its timer callback just after timer_shutdown() was called timer_shutdown() also has the following additional advantage(s): It for one thing clears the callback function pointer and by that avoids rearming. For another a missing function pointer allows us to detect early in the timer callback if we, this timer, were just canceled. In other words, this also allows us to make sure that once timer_shutdown() was called while we do potentially enter its timer callback briefly we never run the main task of this timer. Similar to what a timer_delete_sync() would have avoided, too. Except we are not waiting/sleeping/syncing in br_multicast_stop() but instead (in rare cases) briefly busy-wait/sync a bit later when grabbing the multicast spinlock in the timer callback. This new check also makes the netif_running() check redundant/obsolete in these contexts. Signed-off-by: Linus L=C3=BCssing --- net/bridge/br_multicast.c | 146 ++++++++++++++++++++++++++++---------- net/bridge/br_private.h | 5 ++ 2 files changed, 113 insertions(+), 38 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 881d866d687a..cfe8d42f0214 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1663,6 +1663,14 @@ static void br_multicast_router_expired(struct net_b= ridge_mcast_port *pmctx, spin_unlock(&br->multicast_lock); } =20 +static bool br_multicast_is_stopping(const struct net_bridge *br, + const struct timer_list *timer) +{ + lockdep_assert_held_once(&br->multicast_lock); + + return !timer->function; +} + static void br_ip4_multicast_router_expired(struct timer_list *t) { struct net_bridge_mcast_port *pmctx =3D timer_container_of(pmctx, t, @@ -1698,7 +1706,8 @@ static void br_multicast_local_router_expired(struct = net_bridge_mcast *brmctx, struct timer_list *timer) { spin_lock(&brmctx->br->multicast_lock); - if (brmctx->multicast_router =3D=3D MDB_RTR_TYPE_DISABLED || + if (br_multicast_is_stopping(brmctx->br, timer) || + brmctx->multicast_router =3D=3D MDB_RTR_TYPE_DISABLED || brmctx->multicast_router =3D=3D MDB_RTR_TYPE_PERM || br_ip4_multicast_is_router(brmctx) || br_ip6_multicast_is_router(brmctx)) @@ -1728,10 +1737,11 @@ static void br_ip6_multicast_local_router_expired(s= truct timer_list *t) #endif =20 static void br_multicast_querier_expired(struct net_bridge_mcast *brmctx, - struct bridge_mcast_own_query *query) + struct bridge_mcast_own_query *query, + struct timer_list *timer) { spin_lock(&brmctx->br->multicast_lock); - if (!netif_running(brmctx->br->dev) || + if (br_multicast_is_stopping(brmctx->br, timer) || br_multicast_ctx_vlan_global_disabled(brmctx) || !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) goto out; @@ -1747,7 +1757,7 @@ static void br_ip4_multicast_querier_expired(struct t= imer_list *t) struct net_bridge_mcast *brmctx =3D timer_container_of(brmctx, t, ip4_other_query.timer); =20 - br_multicast_querier_expired(brmctx, &brmctx->ip4_own_query); + br_multicast_querier_expired(brmctx, &brmctx->ip4_own_query, t); } =20 #if IS_ENABLED(CONFIG_IPV6) @@ -1756,7 +1766,7 @@ static void br_ip6_multicast_querier_expired(struct t= imer_list *t) struct net_bridge_mcast *brmctx =3D timer_container_of(brmctx, t, ip6_other_query.timer); =20 - br_multicast_querier_expired(brmctx, &brmctx->ip6_own_query); + br_multicast_querier_expired(brmctx, &brmctx->ip6_own_query, t); } #endif =20 @@ -4040,10 +4050,12 @@ int br_multicast_rcv(struct net_bridge_mcast **brmc= tx, } =20 static void br_multicast_query_expired(struct net_bridge_mcast *brmctx, - struct bridge_mcast_own_query *query) + struct bridge_mcast_own_query *query, + struct timer_list *timer) { spin_lock(&brmctx->br->multicast_lock); - if (br_multicast_ctx_vlan_disabled(brmctx)) + if (br_multicast_is_stopping(brmctx->br, timer) || + br_multicast_ctx_vlan_disabled(brmctx)) goto out; =20 if (query->startup_sent < brmctx->multicast_startup_query_count) @@ -4059,7 +4071,7 @@ static void br_ip4_multicast_query_expired(struct tim= er_list *t) struct net_bridge_mcast *brmctx =3D timer_container_of(brmctx, t, ip4_own_query.timer); =20 - br_multicast_query_expired(brmctx, &brmctx->ip4_own_query); + br_multicast_query_expired(brmctx, &brmctx->ip4_own_query, t); } =20 #if IS_ENABLED(CONFIG_IPV6) @@ -4068,7 +4080,7 @@ static void br_ip6_multicast_query_expired(struct tim= er_list *t) struct net_bridge_mcast *brmctx =3D timer_container_of(brmctx, t, ip6_own_query.timer); =20 - br_multicast_query_expired(brmctx, &brmctx->ip6_own_query); + br_multicast_query_expired(brmctx, &brmctx->ip6_own_query, t); } #endif =20 @@ -4111,29 +4123,40 @@ void br_multicast_ctx_init(struct net_bridge *br, seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock); #endif =20 - timer_setup(&brmctx->ip4_mc_router_timer, - br_ip4_multicast_local_router_expired, 0); - timer_setup(&brmctx->ip4_other_query.timer, - br_ip4_multicast_querier_expired, 0); - timer_setup(&brmctx->ip4_other_query.delay_timer, - br_multicast_query_delay_expired, 0); - timer_setup(&brmctx->ip4_own_query.timer, - br_ip4_multicast_query_expired, 0); + timer_setup(&brmctx->ip4_mc_router_timer, NULL, 0); + timer_setup(&brmctx->ip4_other_query.timer, NULL, 0); + timer_setup(&brmctx->ip4_other_query.delay_timer, NULL, 0); + timer_setup(&brmctx->ip4_own_query.timer, NULL, 0); #if IS_ENABLED(CONFIG_IPV6) - timer_setup(&brmctx->ip6_mc_router_timer, - br_ip6_multicast_local_router_expired, 0); - timer_setup(&brmctx->ip6_other_query.timer, - br_ip6_multicast_querier_expired, 0); - timer_setup(&brmctx->ip6_other_query.delay_timer, - br_multicast_query_delay_expired, 0); - timer_setup(&brmctx->ip6_own_query.timer, - br_ip6_multicast_query_expired, 0); + timer_setup(&brmctx->ip6_mc_router_timer, NULL, 0); + timer_setup(&brmctx->ip6_other_query.timer, NULL, 0); + timer_setup(&brmctx->ip6_other_query.delay_timer, NULL, 0); + timer_setup(&brmctx->ip6_own_query.timer, NULL, 0); #endif + + /* for non-vlan multicast timer callbacks: + * they are set on br_multicast_open() instead + */ + if (!vlan) + return; + + spin_lock_bh(&br->multicast_lock); + br_multicast_reset_timer_cbs(&vlan->br_mcast_ctx); + spin_unlock_bh(&br->multicast_lock); } =20 void br_multicast_ctx_deinit(struct net_bridge_mcast *brmctx) { - __br_multicast_stop(brmctx); + timer_shutdown_sync(&brmctx->ip4_mc_router_timer); + timer_shutdown_sync(&brmctx->ip4_other_query.timer); + timer_shutdown_sync(&brmctx->ip4_other_query.delay_timer); + timer_shutdown_sync(&brmctx->ip4_own_query.timer); +#if IS_ENABLED(CONFIG_IPV6) + timer_shutdown_sync(&brmctx->ip6_mc_router_timer); + timer_shutdown_sync(&brmctx->ip6_other_query.timer); + timer_shutdown_sync(&brmctx->ip6_other_query.delay_timer); + timer_shutdown_sync(&brmctx->ip6_own_query.timer); +#endif } =20 void br_multicast_init(struct net_bridge *br) @@ -4213,9 +4236,35 @@ void br_multicast_leave_snoopers(struct net_bridge *= br) br_ip6_multicast_leave_snoopers(br); } =20 +void br_multicast_reset_timer_cbs(struct net_bridge_mcast *brmctx) +{ + lockdep_assert_held_once(&brmctx->br->multicast_lock); + + brmctx->ip4_mc_router_timer.function =3D + br_ip4_multicast_local_router_expired; + brmctx->ip4_other_query.timer.function =3D + br_ip4_multicast_querier_expired; + brmctx->ip4_other_query.delay_timer.function =3D + br_multicast_query_delay_expired; + brmctx->ip4_own_query.timer.function =3D + br_ip4_multicast_query_expired; +#if IS_ENABLED(CONFIG_IPV6) + brmctx->ip6_mc_router_timer.function =3D + br_ip6_multicast_local_router_expired; + brmctx->ip6_other_query.timer.function =3D + br_ip6_multicast_querier_expired; + brmctx->ip6_other_query.delay_timer.function =3D + br_multicast_query_delay_expired; + brmctx->ip6_own_query.timer.function =3D + br_ip6_multicast_query_expired; +#endif +} + static void __br_multicast_open_query(struct net_bridge *br, struct bridge_mcast_own_query *query) { + lockdep_assert_held_once(&br->multicast_lock); + query->startup_sent =3D 0; =20 if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) @@ -4226,13 +4275,15 @@ static void __br_multicast_open_query(struct net_br= idge *br, =20 static void __br_multicast_open(struct net_bridge_mcast *brmctx) { + br_multicast_reset_timer_cbs(brmctx); + __br_multicast_open_query(brmctx->br, &brmctx->ip4_own_query); #if IS_ENABLED(CONFIG_IPV6) __br_multicast_open_query(brmctx->br, &brmctx->ip6_own_query); #endif } =20 -void br_multicast_open(struct net_bridge *br) +static void br_multicast_open_locked(struct net_bridge *br) { ASSERT_RTNL(); =20 @@ -4256,17 +4307,26 @@ void br_multicast_open(struct net_bridge *br) } } =20 +void br_multicast_open(struct net_bridge *br) +{ + spin_lock_bh(&br->multicast_lock); + br_multicast_open_locked(br); + spin_unlock_bh(&br->multicast_lock); +} + static void __br_multicast_stop(struct net_bridge_mcast *brmctx) { - timer_delete_sync(&brmctx->ip4_mc_router_timer); - timer_delete_sync(&brmctx->ip4_other_query.timer); - timer_delete_sync(&brmctx->ip4_other_query.delay_timer); - timer_delete_sync(&brmctx->ip4_own_query.timer); + lockdep_assert_held_once(&brmctx->br->multicast_lock); + + timer_shutdown(&brmctx->ip4_mc_router_timer); + timer_shutdown(&brmctx->ip4_other_query.timer); + timer_shutdown(&brmctx->ip4_other_query.delay_timer); + timer_shutdown(&brmctx->ip4_own_query.timer); #if IS_ENABLED(CONFIG_IPV6) - timer_delete_sync(&brmctx->ip6_mc_router_timer); - timer_delete_sync(&brmctx->ip6_other_query.timer); - timer_delete_sync(&brmctx->ip6_other_query.delay_timer); - timer_delete_sync(&brmctx->ip6_own_query.timer); + timer_shutdown(&brmctx->ip6_mc_router_timer); + timer_shutdown(&brmctx->ip6_other_query.timer); + timer_shutdown(&brmctx->ip6_other_query.delay_timer); + timer_shutdown(&brmctx->ip6_own_query.timer); #endif } =20 @@ -4317,12 +4377,12 @@ void br_multicast_toggle_one_vlan(struct net_bridge= _vlan *vlan, bool on) =20 spin_lock_bh(&br->multicast_lock); vlan->priv_flags ^=3D BR_VLFLAG_MCAST_ENABLED; - spin_unlock_bh(&br->multicast_lock); =20 if (on) __br_multicast_open(&vlan->br_mcast_ctx); else __br_multicast_stop(&vlan->br_mcast_ctx); + spin_unlock_bh(&br->multicast_lock); } else { struct net_bridge_mcast *brmctx; =20 @@ -4380,6 +4440,7 @@ int br_multicast_toggle_vlan_snooping(struct net_brid= ge *br, bool on, if (!vg) return 0; =20 + spin_lock_bh(&br->multicast_lock); br_opt_toggle(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED, on); =20 /* disable/enable non-vlan mcast contexts based on vlan snooping */ @@ -4387,6 +4448,8 @@ int br_multicast_toggle_vlan_snooping(struct net_brid= ge *br, bool on, __br_multicast_stop(&br->multicast_ctx); else __br_multicast_open(&br->multicast_ctx); + spin_unlock_bh(&br->multicast_lock); + list_for_each_entry(p, &br->port_list, list) { if (on) br_multicast_disable_port_ctx(&p->multicast_ctx); @@ -4416,7 +4479,7 @@ bool br_multicast_toggle_global_vlan(struct net_bridg= e_vlan *vlan, bool on) return true; } =20 -void br_multicast_stop(struct net_bridge *br) +static void br_multicast_stop_locked(struct net_bridge *br) { ASSERT_RTNL(); =20 @@ -4440,6 +4503,13 @@ void br_multicast_stop(struct net_bridge *br) } } =20 +void br_multicast_stop(struct net_bridge *br) +{ + spin_lock_bh(&br->multicast_lock); + br_multicast_stop_locked(br); + spin_unlock_bh(&br->multicast_lock); +} + void br_multicast_dev_del(struct net_bridge *br) { struct net_bridge_mdb_entry *mp; @@ -4675,7 +4745,7 @@ int br_multicast_toggle(struct net_bridge *br, unsign= ed long val, if (!netif_running(br->dev)) goto unlock; =20 - br_multicast_open(br); + br_multicast_open_locked(br); list_for_each_entry(port, &br->port_list, list) __br_multicast_enable_port_ctx(&port->multicast_ctx); =20 diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 545be319b6cb..af206a41555c 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -976,6 +976,7 @@ void br_multicast_disable_port(struct net_bridge_port *= port); void br_multicast_init(struct net_bridge *br); void br_multicast_join_snoopers(struct net_bridge *br); void br_multicast_leave_snoopers(struct net_bridge *br); +void br_multicast_reset_timer_cbs(struct net_bridge_mcast *brmctx); void br_multicast_open(struct net_bridge *br); void br_multicast_stop(struct net_bridge *br); void br_multicast_dev_del(struct net_bridge *br); @@ -1426,6 +1427,10 @@ static inline void br_multicast_leave_snoopers(struc= t net_bridge *br) { } =20 +static inline void br_multicast_reset_timer_cbs(struct net_bridge_mcast *b= rmctx) +{ +} + static inline void br_multicast_open(struct net_bridge *br) { } --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1848E33D6D8; Sat, 7 Mar 2026 04:46:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858776; cv=none; b=Avpsj69XnOR2DSeKNA3jTITKzpUkWS5O23DS+LYbSV0MKS9JATPRo5OEiCBgHoYsZEpWf/NKjW6vyx8eHZ2/6zuSJPLE+WAZp5rFJe8QHU0LNzrC0j2y7EbdgoAZh65ixil9OBm5h3di2yBuojH18TFHQd0NISkmjxbClZlayI4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858776; c=relaxed/simple; bh=WPXd2wxhFehVvEICWkg7xI3fB2/I4jP+ieY/OcZdbDs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=PqgDxXw+zbakC1Nz5vHjviCuF78WSdx8dqhDrt1cKvzonBrnHrEstHFdinl/kj16S6aUeQ5qHS7umwuv2p+11BwixUJl2OB81APQosaYZTAy7Xe6onXMpxoppP3v0kAlmq7/zmHlX7jejz74XAhwKXrxfMNFxNCdnUvAHfYJl0k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 574AF54CE9B; Sat, 7 Mar 2026 05:46:06 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 04/14] net: bridge: mcast: track active state, IGMP/MLD querier appearance Date: Sat, 7 Mar 2026 05:45:38 +0100 Message-ID: <20260307044548.5230-5-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 This is the first step to track in dedicated, per protocol family variables if we can actively and safely use multicast snooping. To later use these in the fast/data path instead of performing all these checks for every packet and to later notify DSA/switchdev about this state. This toggles these new variables to true after a Maximum Response Delay (default: 10 seconds) if a new IGMP or MLD querier has appeared. This can be triggered either through receiving an IGMP/MLD query from another host or by a user enabling our own IGMP/MLD querier. This is the first of several requirements, similar to what br_multicast_querier_exists() already checks so far, to be able to reliably receive IGMP/MLD reports, which in turn are needed to build a complete multicast database. No functional change for the fast/data path yet. Signed-off-by: Linus L=C3=BCssing --- net/bridge/br_multicast.c | 116 +++++++++++++++++++++++++++++++++++++- net/bridge/br_private.h | 2 + 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index cfe8d42f0214..9c22ec63fc6b 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1067,6 +1067,84 @@ static struct sk_buff *br_ip4_multicast_alloc_query(= struct net_bridge_mcast *brm return skb; } =20 +static bool br_ip4_multicast_querier_exists(struct net_bridge_mcast *brmct= x) +{ + return __br_multicast_querier_exists(brmctx, &brmctx->ip4_other_query, + false); +} + +#if IS_ENABLED(CONFIG_IPV6) +static bool br_ip6_multicast_querier_exists(struct net_bridge_mcast *brmct= x) +{ + return __br_multicast_querier_exists(brmctx, &brmctx->ip6_other_query, + true); +} +#endif + +static void br_ip4_multicast_update_active(struct net_bridge_mcast *brmctx, + bool force_inactive) +{ + if (force_inactive) + brmctx->ip4_active =3D false; + else + brmctx->ip4_active =3D br_ip4_multicast_querier_exists(brmctx); +} + +static void br_ip6_multicast_update_active(struct net_bridge_mcast *brmctx, + bool force_inactive) +{ +#if IS_ENABLED(CONFIG_IPV6) + if (force_inactive) + brmctx->ip6_active =3D false; + else + brmctx->ip6_active =3D br_ip6_multicast_querier_exists(brmctx); +#endif +} + +static void br_multicast_notify_active(struct net_bridge_mcast *brmctx, + bool ip4_active_old, bool ip6_active_old) +{ + if (brmctx->ip4_active =3D=3D ip4_active_old && + brmctx->ip6_active =3D=3D ip6_active_old) + return; + + br_debug(brmctx->br, "mc_active changed, vid: %i: v4: %i->%i, v6: %i->%i\= n", + brmctx->vlan ? brmctx->vlan->vid : -1, + ip4_active_old, brmctx->ip4_active, + ip6_active_old, brmctx->ip6_active); +} + +/** + * br_multicast_update_active() - update mcast active state + * @brmctx: the bridge multicast context to check + * + * This (potentially) updates the IPv4/IPv6 multicast active state. And by + * that enables or disables snooping of multicast payload traffic in fast + * path. + * + * The multicast active state is set, per protocol family, if: + * + * - an IGMP/MLD querier is present + * + * And is unset otherwise. + * + * This function should be called by anything that changes one of the + * above prerequisites. + */ +static void br_multicast_update_active(struct net_bridge_mcast *brmctx) +{ + bool ip4_active_old =3D brmctx->ip4_active; + bool ip6_active_old =3D brmctx->ip6_active; + bool force_inactive =3D false; + + lockdep_assert_held_once(&brmctx->br->multicast_lock); + + br_ip4_multicast_update_active(brmctx, force_inactive); + br_ip6_multicast_update_active(brmctx, force_inactive); + + br_multicast_notify_active(brmctx, ip4_active_old, ip6_active_old); +} + #if IS_ENABLED(CONFIG_IPV6) static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcas= t *brmctx, struct net_bridge_mcast_port *pmctx, @@ -1770,10 +1848,40 @@ static void br_ip6_multicast_querier_expired(struct= timer_list *t) } #endif =20 -static void br_multicast_query_delay_expired(struct timer_list *t) +static void br_ip4_multicast_query_delay_expired(struct timer_list *t) { + struct net_bridge_mcast *brmctx; + + brmctx =3D timer_container_of(brmctx, t, ip4_other_query.delay_timer); + + spin_lock(&brmctx->br->multicast_lock); + if (!br_multicast_is_stopping(brmctx->br, t)) + /* an own or other IGMP querier appeared some seconds ago and + * all reports should have arrived by now, maybe set multicast + * state to active + */ + br_multicast_update_active(brmctx); + spin_unlock(&brmctx->br->multicast_lock); } =20 +#if IS_ENABLED(CONFIG_IPV6) +static void br_ip6_multicast_query_delay_expired(struct timer_list *t) +{ + struct net_bridge_mcast *brmctx; + + brmctx =3D timer_container_of(brmctx, t, ip6_other_query.delay_timer); + + spin_lock(&brmctx->br->multicast_lock); + if (!br_multicast_is_stopping(brmctx->br, t)) + /* an own or other MLD querier appeared some seconds ago and + * all reports should have arrived by now, maybe set multicast + * state to active + */ + br_multicast_update_active(brmctx); + spin_unlock(&brmctx->br->multicast_lock); +} +#endif + static void br_multicast_select_own_querier(struct net_bridge_mcast *brmct= x, struct br_ip *ip, struct sk_buff *skb) @@ -4115,6 +4223,7 @@ void br_multicast_ctx_init(struct net_bridge *br, brmctx->multicast_membership_interval =3D 260 * HZ; =20 brmctx->ip4_querier.port_ifidx =3D 0; + brmctx->ip4_active =3D 0; seqcount_spinlock_init(&brmctx->ip4_querier.seq, &br->multicast_lock); brmctx->multicast_igmp_version =3D 2; #if IS_ENABLED(CONFIG_IPV6) @@ -4122,6 +4231,7 @@ void br_multicast_ctx_init(struct net_bridge *br, brmctx->ip6_querier.port_ifidx =3D 0; seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock); #endif + brmctx->ip6_active =3D 0; =20 timer_setup(&brmctx->ip4_mc_router_timer, NULL, 0); timer_setup(&brmctx->ip4_other_query.timer, NULL, 0); @@ -4245,7 +4355,7 @@ void br_multicast_reset_timer_cbs(struct net_bridge_m= cast *brmctx) brmctx->ip4_other_query.timer.function =3D br_ip4_multicast_querier_expired; brmctx->ip4_other_query.delay_timer.function =3D - br_multicast_query_delay_expired; + br_ip4_multicast_query_delay_expired; brmctx->ip4_own_query.timer.function =3D br_ip4_multicast_query_expired; #if IS_ENABLED(CONFIG_IPV6) @@ -4254,7 +4364,7 @@ void br_multicast_reset_timer_cbs(struct net_bridge_m= cast *brmctx) brmctx->ip6_other_query.timer.function =3D br_ip6_multicast_querier_expired; brmctx->ip6_other_query.delay_timer.function =3D - br_multicast_query_delay_expired; + br_ip6_multicast_query_delay_expired; brmctx->ip6_own_query.timer.function =3D br_ip6_multicast_query_expired; #endif diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index af206a41555c..8d1c869eb522 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -160,6 +160,7 @@ struct net_bridge_mcast { struct bridge_mcast_other_query ip4_other_query; struct bridge_mcast_own_query ip4_own_query; struct bridge_mcast_querier ip4_querier; + bool ip4_active; #if IS_ENABLED(CONFIG_IPV6) struct hlist_head ip6_mc_router_list; struct timer_list ip6_mc_router_timer; @@ -167,6 +168,7 @@ struct net_bridge_mcast { struct bridge_mcast_own_query ip6_own_query; struct bridge_mcast_querier ip6_querier; #endif /* IS_ENABLED(CONFIG_IPV6) */ + bool ip6_active; #endif /* CONFIG_BRIDGE_IGMP_SNOOPING */ }; =20 --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 479BA33EAF9; Sat, 7 Mar 2026 04:46:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858777; cv=none; b=nxDsBHtj0LMlG9CNUQz8NH4F1DuQE9RviQZIEbiwAvqjymnIcoMbVQOz8o5yItOOLA0sK3JT471pCf3rxgo4NBjr5hU8uWQv/Ty6V+06gzn1rq57gyWR7TpQVvMaywx8CB/L4e3ojofYeZt6JZcENiquZP/6zAb3NExklbl64gI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858777; c=relaxed/simple; bh=iFhNWGwEOgCjB2i5dzTyAjsyVdI/FFNKvKk5tQrUHqg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=p3CkKNNvOndHslEeVaC26/Xf2f8/GWzOwrRGV65Z+Md6TFJ5pXgYoCFoF7JKLk1gWXeFsdC4qUR0PWSnTw2dIflfCupRcHAE1QONZCMlTBDBBm5dYPnVX4zPTzYcIMQDa2/KVIjUtvlc1fSg0kt3QqKLGvyWWlWZW9HPVx79rCA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id AB30854CE9C; Sat, 7 Mar 2026 05:46:07 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 05/14] net: bridge: mcast: track active state, foreign IGMP/MLD querier disappearance Date: Sat, 7 Mar 2026 05:45:39 +0100 Message-ID: <20260307044548.5230-6-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 This change ensures that the new multicast active state variable is unset again after a foreign IGMP/MLD querier has disappeared (default: 255 seconds). If no new, other IGMP/MLD querier took over then we can't reliably receive IGMP/MLD reports anymore and in turn can't ensure the completeness of our MDB anymore either. No functional change for the fast/data path yet. Signed-off-by: Linus L=C3=BCssing Reviewed-by: Ido Schimmel --- net/bridge/br_multicast.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 9c22ec63fc6b..64aac9c415e2 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1827,6 +1827,10 @@ static void br_multicast_querier_expired(struct net_= bridge_mcast *brmctx, br_multicast_start_querier(brmctx, query); =20 out: + /* another IGMP/MLD querier disappeared, set multicast state to inactive + * if our own querier is disabled, too + */ + br_multicast_update_active(brmctx); spin_unlock(&brmctx->br->multicast_lock); } =20 --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4520533B6E3; Sat, 7 Mar 2026 04:46:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858776; cv=none; b=LunEWqXL6NJGtLc9XjRG4yOCKv090X2UNGdP+52nP1I5LjcDh9K46HEmwAFRpbtdr5+qMWQtPi3mGM5MAGI2b8MPRN4FVkVkp6eDrBpt0urCbEnVfYmZOXX+mE0W4MpMMDX+FEVowROHEf6ntkWjykRiYeptH9lJtD7HBYS2YRo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858776; c=relaxed/simple; bh=8AwlC0ORjtiYenq6JoteQg3VwOEL409SWItUtfUUlxw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=N7a7m8L2eo4133H9G7MWekPsPKggg7eSVgtxqy6ZWBOo0IDNwMbco1vSkQk7B9+BtC8Wi8Wso/820e9sJuHNJoVNfbpJJek5THWNxHszjiRzzPPjvgengLrhoTSVwKdaWVOLQQn9w13oO2Vsfzl+Lpk9hA/1t9UDojLXRC/izVI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 1111854CE9E; Sat, 7 Mar 2026 05:46:09 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 06/14] net: bridge: mcast: track active state, IPv6 address availability Date: Sat, 7 Mar 2026 05:45:40 +0100 Message-ID: <20260307044548.5230-7-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 If we are the only potential MLD querier but don't have an IPv6 link-local address configured on our bridge interface then we can't create a valid MLD query and in turn can't reliably receive MLD reports and can't build a complete MDB. Hence disable the new multicast active state variable then. Or reenable it if an IPv6 link-local address became available. No functional change for the fast/data path yet. Signed-off-by: Linus L=C3=BCssing Reviewed-by: Ido Schimmel --- net/bridge/br_multicast.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 64aac9c415e2..2eebfb7c5ff9 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1125,6 +1125,8 @@ static void br_multicast_notify_active(struct net_bri= dge_mcast *brmctx, * The multicast active state is set, per protocol family, if: * * - an IGMP/MLD querier is present + * - for own IPv6 MLD querier: an IPv6 link-local address is configured on= the + * bridge * * And is unset otherwise. * @@ -1223,10 +1225,12 @@ static struct sk_buff *br_ip6_multicast_alloc_query= (struct net_bridge_mcast *brm &ip6h->daddr, 0, &ip6h->saddr)) { kfree_skb(skb); br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, false); + br_multicast_update_active(brmctx); return NULL; } =20 br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, true); + br_multicast_update_active(brmctx); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); =20 hopopt =3D (u8 *)(ip6h + 1); --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D9E83446A5; Sat, 7 Mar 2026 04:46:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858777; cv=none; b=VPltjATbXl2adX2vU2Dw+E7UbG9jsK5CTsRE5tLYJwzm+zQ7groAQGM4Q09qLaWuumRvGjcgM6sRGBAnlXz+RA2nNf1EjAShkEj7+aMgFgmBLZxaqKR9vn77mhEXsiHXER1RcT1lZaFYiI4sH01xHUXTz588sgadSBX0jLftELI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858777; c=relaxed/simple; bh=6FpFkPi+d0c1jU/wPhHu25zL1jRo1npyrF8LPnTHVp8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=rn93GI3XvCiyoItdZhiwQa3EjW6OBULajUbJPXzEkiFetYYwnzipXhMEGD06iyUgQijlEdNOVV+GIVtDVXPuMbM2izRNDR5SN5V9/KaFCIkb5pjslnRGxVeMKHVHYG8tG8ewcS6x6dPYWtvRkD8383teCMJvr4RRRRa4x6cfKjU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 5787454CEA0; Sat, 7 Mar 2026 05:46:10 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 07/14] net: bridge: mcast: track active state, own MLD querier disappearance Date: Sat, 7 Mar 2026 05:45:41 +0100 Message-ID: <20260307044548.5230-8-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 This change ensures that the new multicast active state variable is immediately unset if our internal IGMP/MLD querier was elected and now disabled. If no IGMP/MLD querier exists on the link then we can't reliably receive IGMP/MLD reports and in turn can't ensure the completeness of our MDB anymore either. No functional change for the fast/data path yet. Signed-off-by: Linus L=C3=BCssing Reviewed-by: Ido Schimmel --- net/bridge/br_multicast.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 2eebfb7c5ff9..f37a18f3713c 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -4945,6 +4945,7 @@ int br_multicast_set_querier(struct net_bridge_mcast = *brmctx, unsigned long val) #endif =20 unlock: + br_multicast_update_active(brmctx); spin_unlock_bh(&brmctx->br->multicast_lock); =20 return 0; --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 721C93446C3; Sat, 7 Mar 2026 04:46:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858777; cv=none; b=Bs9bEkZwTPyXWe3p5fqEpXaM5p6IVIULSCpkGssSdXDWrnimvZq8U/y1DTciIotVVZMTHYLSritPgAGvuCV/XOgWBusSCs5vuxzqz+YJHq31n5FGAzdykgCDHKmDMFVvh/V1lzBhIvMe1Yh0jEJl57DHJ2ZWXGoKLCtADCoUUVo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858777; c=relaxed/simple; bh=d7YT/fenrAZV23uVHfcOmBXe4qPxQQj4VvB7mBZMEcc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Re8nrneH4RH5Q/c6fizs0UY0K03hj4/A9yqQU2X9oZQG7uF2UnKnkScamucRjp4lBUCZBDvRaA7WlwyjMVu+ABN4sqeHW23Rt0fvLbHjymGn47AtK38e06ryaY9DpFSlscPhz9sC0ODltzVTin4Z/5/favOilm7x1OTuBvZ5AMg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id B378A54CEA2; Sat, 7 Mar 2026 05:46:11 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 08/14] net: bridge: mcast: track active state, if snooping is enabled Date: Sat, 7 Mar 2026 05:45:42 +0100 Message-ID: <20260307044548.5230-9-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 To be able to use the upcoming SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE as a potential replacement for SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED also check and toggle the active state if multicast snooping is enabled or disabled. So that MC_ACTIVE not only checks the querier state, but also if multicast snooping is enabled in general. No functional change for the fast/data path yet. Signed-off-by: Linus L=C3=BCssing Reviewed-by: Ido Schimmel --- include/uapi/linux/if_link.h | 6 ++++-- net/bridge/br_multicast.c | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 66c59953d301..d963be8679b5 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -748,12 +748,14 @@ enum in6_addr_gen_mode { * @IFLA_BR_MCAST_ACTIVE_V4 * Bridge IPv4 mcast active state, read only. * - * 1 if an IGMP querier is present, 0 otherwise. + * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled and an IGMP querier is prese= nt, + * 0 otherwise. * * @IFLA_BR_MCAST_ACTIVE_V6 * Bridge IPv6 mcast active state, read only. * - * 1 if an MLD querier is present, 0 otherwise. + * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled and an MLD querier is presen= t, + * 0 otherwise. */ enum { IFLA_BR_UNSPEC, diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index f37a18f3713c..cdc921b97243 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1124,6 +1124,7 @@ static void br_multicast_notify_active(struct net_bri= dge_mcast *brmctx, * * The multicast active state is set, per protocol family, if: * + * - multicast snooping is enabled * - an IGMP/MLD querier is present * - for own IPv6 MLD querier: an IPv6 link-local address is configured on= the * bridge @@ -1141,6 +1142,9 @@ static void br_multicast_update_active(struct net_bri= dge_mcast *brmctx) =20 lockdep_assert_held_once(&brmctx->br->multicast_lock); =20 + if (!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) + force_inactive =3D true; + br_ip4_multicast_update_active(brmctx, force_inactive); br_ip6_multicast_update_active(brmctx, force_inactive); =20 @@ -1355,6 +1359,12 @@ static struct sk_buff *br_multicast_alloc_query(stru= ct net_bridge_mcast *brmctx, return NULL; } =20 +static void br_multicast_toggle_enabled(struct net_bridge *br, bool on) +{ + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, on); + br_multicast_update_active(&br->multicast_ctx); +} + struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br, struct br_ip *group) { @@ -1368,7 +1378,7 @@ struct net_bridge_mdb_entry *br_multicast_new_group(s= truct net_bridge *br, if (atomic_read(&br->mdb_hash_tbl.nelems) >=3D br->hash_max) { trace_br_mdb_full(br->dev, group); br_mc_disabled_update(br->dev, false, NULL); - br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false); + br_multicast_toggle_enabled(br, false); return ERR_PTR(-E2BIG); } =20 @@ -4853,7 +4863,8 @@ int br_multicast_toggle(struct net_bridge *br, unsign= ed long val, if (err) goto unlock; =20 - br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + br_multicast_toggle_enabled(br, !!val); + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { change_snoopers =3D true; br_multicast_del_grps(br); --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CF3B334A3C4; Sat, 7 Mar 2026 04:46:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858778; cv=none; b=ACqkBY8gnTaUVrBreyyiKI52UEBMCk8gaf2B3m4zGpszkX6Lu2mfia5zHgnnRSj3ZVIjAXS6HqZoohs41RsAu9R0uzWKdQS7ZxnouwBvBPZPN3rYtb5pBmnhhC4DZVAXksI8HpM7DlsNDB5thfyYzSCzPkPNvinhhXxQAjpRn3c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858778; c=relaxed/simple; bh=W3hMNm+R7Snrp4IzJ0JPFgY2xxsdle3iCrOjJw1YdwA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=TtX5Tl4fpkoRiWb9C1wv3Fyt6tvajzpwLUVuR0eh6CNoVaqLaWdocMfvZMdP1Ex23Y/UbsqX1wexcaf81lrnHbrbApBZz8yKaWPA+6n9g+YvfRWQzLFCKemvLRGJtexs3VgPjXik2HR++jVj1GNzB3HY12dMdmwHC6PKm+CGE0c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 0599054CEA3; Sat, 7 Mar 2026 05:46:12 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 09/14] net: bridge: mcast: track active state, VLAN snooping Date: Sat, 7 Mar 2026 05:45:43 +0100 Message-ID: <20260307044548.5230-10-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 If VLAN aware multicast snooping is enabled then we need to perform a few extra checks to figure out if multicast snooping is actually enabled for a specific VLAN, as there is then an additional per VLAN multicast snooping toggle. Signed-off-by: Linus L=C3=BCssing --- net/bridge/br_multicast.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index cdc921b97243..1059984d8147 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1142,9 +1142,31 @@ static void br_multicast_update_active(struct net_br= idge_mcast *brmctx) =20 lockdep_assert_held_once(&brmctx->br->multicast_lock); =20 - if (!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) + if (!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) { force_inactive =3D true; + goto update; + } =20 + if (br_opt_get(brmctx->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) { + /* with per-vlan snooping enabled there is an extra per-vlan + * toggle to enable/disable snooping which we must check + */ + if (br_multicast_ctx_vlan_global_disabled(brmctx)) + force_inactive =3D true; + /* with per-vlan snooping enabled the non-vlan multicast + * snooping context is inactive + */ + else if (!br_multicast_ctx_is_vlan(brmctx)) + force_inactive =3D true; + } else { + /* with per-vlan snooping disabled a vlan multicast + * snooping context is inactive + */ + if (br_multicast_ctx_is_vlan(brmctx)) + force_inactive =3D true; + } + +update: br_ip4_multicast_update_active(brmctx, force_inactive); br_ip6_multicast_update_active(brmctx, force_inactive); =20 @@ -4505,6 +4527,7 @@ void br_multicast_toggle_one_vlan(struct net_bridge_v= lan *vlan, bool on) =20 spin_lock_bh(&br->multicast_lock); vlan->priv_flags ^=3D BR_VLFLAG_MCAST_ENABLED; + br_multicast_update_active(&vlan->br_mcast_ctx); =20 if (on) __br_multicast_open(&vlan->br_mcast_ctx); --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 355D934D4E9; Sat, 7 Mar 2026 04:46:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858779; cv=none; b=vCjrezaW0qgRVVOGFNjBGdgrT5IHgfa2Ea203yQjTbP+m8TQ3xBYOiSKa0JNUOvurd91RyEU3MnucYHTG7OIoadziAtv3dc5lL/KmbbQyC3vHn2P44jLrvCqyu1db9wGT4BTC6RjnTrSsfz2WiDi/CpN8/wJJ2UVL3g7/fuOA4c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858779; c=relaxed/simple; bh=aYI6noFunMVNidkq3fDpollILn8CuOzlAMZ2XG27agk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=l+ryl41RliNl38LKwXccsbyctdSyQLbABNbCBoSOx/26S5hMYz9FXK/oDot5dBuNqCg+7d5L/KgCVUBn88XY+NS92jR9wfgTRjZvLp+9ijFByaEUUQLhp7EXnIAJeTnKrLpv2fVkgD+hYZyt6yhhnyI/U14ExtVCE+BlTZqNypo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 42CA054CDF2; Sat, 7 Mar 2026 05:46:14 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 10/14] net: bridge: mcast: track active state, bridge up/down Date: Sat, 7 Mar 2026 05:45:44 +0100 Message-ID: <20260307044548.5230-11-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 This is mainly for switchdev and DSA later: To ensure that we switch to inactive before destroying a bridge interface. A switchdev/DSA driver might have allocated resources after we switched to an enabled multicast active state. This gives switchdev/DSA drivers a chance to free these resources again when we destroy the bridge (later). Putting it into the ndo_stop / bridge interface down part instead of the ndo_uninit / bridge destroy part though for a better semantic match. If the bridge interface is down / stopped then it is also inactive. No functional change for the fast/data path. Signed-off-by: Linus L=C3=BCssing --- include/uapi/linux/if_link.h | 8 ++++---- net/bridge/br_multicast.c | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index d963be8679b5..679368784643 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -748,14 +748,14 @@ enum in6_addr_gen_mode { * @IFLA_BR_MCAST_ACTIVE_V4 * Bridge IPv4 mcast active state, read only. * - * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled and an IGMP querier is prese= nt, - * 0 otherwise. + * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled, an IGMP querier is present + * and the bridge interface is up, 0 otherwise. * * @IFLA_BR_MCAST_ACTIVE_V6 * Bridge IPv6 mcast active state, read only. * - * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled and an MLD querier is presen= t, - * 0 otherwise. + * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled, an MLD querier is present + * and the bridge interface is up, 0 otherwise. */ enum { IFLA_BR_UNSPEC, diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 1059984d8147..cb78f9555db6 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1124,6 +1124,7 @@ static void br_multicast_notify_active(struct net_bri= dge_mcast *brmctx, * * The multicast active state is set, per protocol family, if: * + * - the bridge interface is up * - multicast snooping is enabled * - an IGMP/MLD querier is present * - for own IPv6 MLD querier: an IPv6 link-local address is configured on= the @@ -1142,6 +1143,11 @@ static void br_multicast_update_active(struct net_br= idge_mcast *brmctx) =20 lockdep_assert_held_once(&brmctx->br->multicast_lock); =20 + if (!netif_running(brmctx->br->dev)) { + force_inactive =3D true; + goto update; + } + if (!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) { force_inactive =3D true; goto update; @@ -4431,6 +4437,9 @@ static void __br_multicast_open(struct net_bridge_mca= st *brmctx) #if IS_ENABLED(CONFIG_IPV6) __br_multicast_open_query(brmctx->br, &brmctx->ip6_own_query); #endif + + /* bridge interface is up, maybe set multicast state to active */ + br_multicast_update_active(brmctx); } =20 static void br_multicast_open_locked(struct net_bridge *br) @@ -4478,6 +4487,9 @@ static void __br_multicast_stop(struct net_bridge_mca= st *brmctx) timer_shutdown(&brmctx->ip6_other_query.delay_timer); timer_shutdown(&brmctx->ip6_own_query.timer); #endif + + /* bridge interface is down, set multicast state to inactive */ + br_multicast_update_active(brmctx); } =20 void br_multicast_update_vlan_mcast_ctx(struct net_bridge_vlan *v, u8 stat= e) --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7E6D2355F5C; Sat, 7 Mar 2026 04:46:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858780; cv=none; b=Yxgc+p2RLUEg9NNGZ+p2Vt3q+2y9iJS1YjmWsZimqEomU5XYsjH9Bc09Vf24G0NqBX5BEDES7UwZLm0/73vcz5gzMv3YodRCt2FnXzR7aoigrfY8xHJsg1OJAx2bVA3DutZbgo39uCtkKXrfb0w1IIbEQ6xFdIxf7wqGATTtOSA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858780; c=relaxed/simple; bh=qEndSg74IrSsHyC+dBq6x34fpvQkg8HB8RGj8cYl2Wg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=gsDo6U6dVMXHZefNfQ1/GWc92br6JTVdhUz4Rm1p/+sg9Nn36NpycAgzwh72QKimOPbiYwaiGCp8XiPnseRCYl2eLZiKIVUSDAHxxyNDud8ts98MG5beWhzAhaHq/NuLfCM6aVCeu7TLJtONqjPakxwmdoj1XAIJTBK2AgaqsQo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 174B554C936; Sat, 7 Mar 2026 05:46:16 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 11/14] net: bridge: mcast: track active state, prepare for outside lock reads Date: Sat, 7 Mar 2026 05:45:45 +0100 Message-ID: <20260307044548.5230-12-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 We are updating ip{4,6}_active and check all variables their state relies on while holding the bridge multicast spinlock. However we are going to read it without this lock in a follow-up commit on fast/data path, too. As these variables are only booleans this shouldn't be a problem, ip{4,6}_active will be loaded and stored atomically. And those read sides should converge eventually. But to allow tooling to verify this use the READ_ONCE() and WRITE_ONCE macros. Signed-off-by: Linus L=C3=BCssing --- net/bridge/br_multicast.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index cb78f9555db6..6faa484dede7 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1085,9 +1085,10 @@ static void br_ip4_multicast_update_active(struct ne= t_bridge_mcast *brmctx, bool force_inactive) { if (force_inactive) - brmctx->ip4_active =3D false; + WRITE_ONCE(brmctx->ip4_active, false); else - brmctx->ip4_active =3D br_ip4_multicast_querier_exists(brmctx); + WRITE_ONCE(brmctx->ip4_active, + br_ip4_multicast_querier_exists(brmctx)); } =20 static void br_ip6_multicast_update_active(struct net_bridge_mcast *brmctx, @@ -1095,23 +1096,26 @@ static void br_ip6_multicast_update_active(struct n= et_bridge_mcast *brmctx, { #if IS_ENABLED(CONFIG_IPV6) if (force_inactive) - brmctx->ip6_active =3D false; + WRITE_ONCE(brmctx->ip6_active, false); else - brmctx->ip6_active =3D br_ip6_multicast_querier_exists(brmctx); + WRITE_ONCE(brmctx->ip6_active, + br_ip6_multicast_querier_exists(brmctx)); #endif } =20 static void br_multicast_notify_active(struct net_bridge_mcast *brmctx, bool ip4_active_old, bool ip6_active_old) { - if (brmctx->ip4_active =3D=3D ip4_active_old && - brmctx->ip6_active =3D=3D ip6_active_old) + int ip4_active =3D brmctx->ip4_active; + int ip6_active =3D brmctx->ip6_active; + + if (ip4_active =3D=3D ip4_active_old && + ip6_active =3D=3D ip6_active_old) return; =20 br_debug(brmctx->br, "mc_active changed, vid: %i: v4: %i->%i, v6: %i->%i\= n", brmctx->vlan ? brmctx->vlan->vid : -1, - ip4_active_old, brmctx->ip4_active, - ip6_active_old, brmctx->ip6_active); + ip4_active_old, ip4_active, ip6_active_old, ip6_active); } =20 /** @@ -4269,7 +4273,7 @@ void br_multicast_ctx_init(struct net_bridge *br, brmctx->multicast_membership_interval =3D 260 * HZ; =20 brmctx->ip4_querier.port_ifidx =3D 0; - brmctx->ip4_active =3D 0; + WRITE_ONCE(brmctx->ip4_active, 0); seqcount_spinlock_init(&brmctx->ip4_querier.seq, &br->multicast_lock); brmctx->multicast_igmp_version =3D 2; #if IS_ENABLED(CONFIG_IPV6) @@ -4277,7 +4281,7 @@ void br_multicast_ctx_init(struct net_bridge *br, brmctx->ip6_querier.port_ifidx =3D 0; seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock); #endif - brmctx->ip6_active =3D 0; + WRITE_ONCE(brmctx->ip6_active, 0); =20 timer_setup(&brmctx->ip4_mc_router_timer, NULL, 0); timer_setup(&brmctx->ip4_other_query.timer, NULL, 0); --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D403733B960; Sat, 7 Mar 2026 04:46:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858781; cv=none; b=Yp9qWC3KdQJxtl1C7ivfVpGqgABU0pVlktkVRsUcXxHYnGvf76hERbI19c8Q3CREd8MQfyCxZ/AuFxYO+Rw/y9G7URqidXHVV0bIhAbI89OMijjemxtLTyqhKrtx5SzmkVpRh3ry4aTDKw0yrKGlqTi97vKg29i1ISzzvnZFv8o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858781; c=relaxed/simple; bh=GmThW4QYajkqIjKYnPrIQN/OrxYNIx6aoBVIkWHo+Q8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=s5LEBWhhEQaDpqbr0IYgQjXjsNAma5Zq6ClAPZ8fa4Ip2YH5cwMoShOH9/bjrKiDATBw4X9nIPRJ0i9tDiT4jC4vAWM/Vv9A+pLht/Np1lnsE6shLimKFaduS4RrjyYtY6Gs74+wfwpjc1PqF/jQRmjmhk3DWHB5Fz0avbnVtWU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 7769454CE9A; Sat, 7 Mar 2026 05:46:17 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 12/14] net: bridge: mcast: use combined active state in netlink Date: Sat, 7 Mar 2026 05:45:46 +0100 Message-ID: <20260307044548.5230-13-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Use the new multicast ip{4,6}_active variables for the netlink output. The result for the user should be the same. But this allows us to check that from userspace tests, to ensure that ip{4,6}_active behave as intended for the upcoming fast/data path changes. Signed-off-by: Linus L=C3=BCssing Reviewed-by: Ido Schimmel --- net/bridge/br_netlink.c | 14 ++------------ net/bridge/br_private.h | 2 +- net/bridge/br_vlan_options.c | 23 +++++------------------ 3 files changed, 8 insertions(+), 31 deletions(-) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 7fff59c3bf76..e79fddf747f8 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -1643,8 +1643,6 @@ static size_t br_get_size(const struct net_device *br= dev) =20 static int br_fill_info(struct sk_buff *skb, const struct net_device *brde= v) { - struct ethhdr eth6 __maybe_unused =3D { .h_proto =3D htons(ETH_P_IPV6) }; - struct ethhdr eth4 __maybe_unused =3D { .h_proto =3D htons(ETH_P_IP) }; struct net_bridge *br =3D netdev_priv(brdev); u32 forward_delay =3D jiffies_to_clock_t(br->forward_delay); u32 hello_time =3D jiffies_to_clock_t(br->hello_time); @@ -1724,11 +1722,7 @@ static int br_fill_info(struct sk_buff *skb, const s= truct net_device *brdev) nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION, br->multicast_ctx.multicast_igmp_version) || nla_put_u8(skb, IFLA_BR_MCAST_ACTIVE_V4, - netif_running(brdev) && - br_opt_get(br, BROPT_MULTICAST_ENABLED) && - !br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && - br_multicast_querier_exists(&br->multicast_ctx, ð4, - NULL)) || + READ_ONCE(br->multicast_ctx.ip4_active)) || br_multicast_dump_querier_state(skb, &br->multicast_ctx, IFLA_BR_MCAST_QUERIER_STATE)) return -EMSGSIZE; @@ -1736,11 +1730,7 @@ static int br_fill_info(struct sk_buff *skb, const s= truct net_device *brdev) if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION, br->multicast_ctx.multicast_mld_version) || nla_put_u8(skb, IFLA_BR_MCAST_ACTIVE_V6, - netif_running(brdev) && - br_opt_get(br, BROPT_MULTICAST_ENABLED) && - !br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && - br_multicast_querier_exists(&br->multicast_ctx, ð6, - NULL))) + READ_ONCE(br->multicast_ctx.ip6_active))) return -EMSGSIZE; #endif clockval =3D jiffies_to_clock_t(br->multicast_ctx.multicast_last_member_i= nterval); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 8d1c869eb522..2bdfc93499fc 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1909,7 +1909,7 @@ int br_vlan_rtm_process_global_options(struct net_dev= ice *dev, bool br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan *v_c= urr, const struct net_bridge_vlan *r_end); bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range, - struct net_bridge_vlan *v_opts); + const struct net_bridge_vlan *v_opts); =20 /* vlan state manipulation helpers using *_ONCE to annotate lock-free acce= ss, * while br_vlan_set_state() may access data protected by multicast_lock. diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c index 06045d68a2d1..47ad35583a3f 100644 --- a/net/bridge/br_vlan_options.c +++ b/net/bridge/br_vlan_options.c @@ -367,13 +367,10 @@ bool br_vlan_global_opts_can_enter_range(const struct= net_bridge_vlan *v_curr, } =20 bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range, - struct net_bridge_vlan *v_opts) + const struct net_bridge_vlan *v_opts) { - struct ethhdr eth6 __maybe_unused =3D { .h_proto =3D htons(ETH_P_IPV6) }; - struct ethhdr eth4 __maybe_unused =3D { .h_proto =3D htons(ETH_P_IP) }; struct nlattr *nest2 __maybe_unused; u64 clockval __maybe_unused; - bool mcact __maybe_unused; struct nlattr *nest; =20 nest =3D nla_nest_start(skb, BRIDGE_VLANDB_GLOBAL_OPTIONS); @@ -388,17 +385,12 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u1= 6 vid, u16 vid_range, goto out_err; =20 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING - mcact =3D netif_running(v_opts->br->dev) && - br_opt_get(v_opts->br, BROPT_MULTICAST_ENABLED) && - br_opt_get(v_opts->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && - !br_multicast_ctx_vlan_global_disabled(&v_opts->br_mcast_ctx) && - br_multicast_querier_exists(&v_opts->br_mcast_ctx, ð4, NULL); - if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING, !!(v_opts->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED)) || nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION, v_opts->br_mcast_ctx.multicast_igmp_version) || - nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4, mcact) || + nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4, + READ_ONCE(v_opts->br_mcast_ctx.ip4_active)) || nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT, v_opts->br_mcast_ctx.multicast_last_member_count) || nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT, @@ -452,15 +444,10 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u1= 6 vid, u16 vid_range, } =20 #if IS_ENABLED(CONFIG_IPV6) - mcact =3D netif_running(v_opts->br->dev) && - br_opt_get(v_opts->br, BROPT_MULTICAST_ENABLED) && - br_opt_get(v_opts->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && - !br_multicast_ctx_vlan_global_disabled(&v_opts->br_mcast_ctx) && - br_multicast_querier_exists(&v_opts->br_mcast_ctx, ð6, NULL); - if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION, v_opts->br_mcast_ctx.multicast_mld_version) || - nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6, mcact)) + nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6, + READ_ONCE(v_opts->br_mcast_ctx.ip6_active))) goto out_err; #endif #endif --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7412E36215B; Sat, 7 Mar 2026 04:46:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858783; cv=none; b=XgyTcrY+Ek/BDQOYFc0aVQgaebgI8jk3JD5gadUUB+FbeEpiNCYXgdWKLsAWZ8lIB8fksIGSGeycTN9kfWYi1M7SJ/3q5R4//sQyJaJQjUwOQp0KuIeCPXx8++gWP/TfdrV6L0WsuJKLI0TdVh+zvzYAvKT3ZpSAp+zrHYvetZs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858783; c=relaxed/simple; bh=h1GFbg9q+xNkCOQhOYf7BN07xxwwJWztFGYUdNhjH+U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=AWlGezAGzq+v02+9VtUZRrD6jIlIy5tllvDhE2aSHwynEIZBB5MPAq6S/r+tJq1nGve6Kz5iX2WKLLT6gKKTThwYztPZqQg6odvtM+SlS3EZ6uCqKRWmc4ytLXYdFGtpXLJLzk7GsiLKPxpRN2EPR98LCsmOMoM1Fty/b059rCg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id C9E5C54C9A1; Sat, 7 Mar 2026 05:46:18 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 13/14] net: bridge: mcast: use combined active state in fast/data path Date: Sat, 7 Mar 2026 05:45:47 +0100 Message-ID: <20260307044548.5230-14-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 As the multicast active state variable is now always up to date and functionally equivalent to our manual, extensive checks in fast path we can just use this state variable in fast path, too. This allows to save some CPU cycles for every multicast packet in the fast/data path. Next to using brmctx->ip4_active / brmctx->ip6_active in fast path this mostly just moves some code around to not expose it via br_private.h anymore. While at it now also passing the ethernet protocol number directly, instead of a pointer into the ethernet header. Signed-off-by: Linus L=C3=BCssing --- net/bridge/br_device.c | 3 ++- net/bridge/br_input.c | 3 ++- net/bridge/br_multicast.c | 41 +++++++++++++++++++++++++++++++----- net/bridge/br_private.h | 44 +++++++++++---------------------------- 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index f7502e62dd35..65e03e4c09bb 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -102,7 +102,8 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net= _device *dev) =20 mdst =3D br_mdb_entry_skb_get(brmctx, skb, vid); if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && - br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) + br_multicast_snooping_active(brmctx, eth_hdr(skb)->h_proto, + mdst)) br_multicast_flood(mdst, skb, brmctx, false, true); else br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 2cbae0f9ae1f..88654a559f09 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -187,7 +187,8 @@ int br_handle_frame_finish(struct net *net, struct sock= *sk, struct sk_buff *skb case BR_PKT_MULTICAST: mdst =3D br_mdb_entry_skb_get(brmctx, skb, vid); if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && - br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) { + br_multicast_snooping_active(brmctx, eth_hdr(skb)->h_proto, + mdst)) { if ((mdst && mdst->host_joined) || br_multicast_is_router(brmctx, skb) || br->dev->flags & IFF_ALLMULTI) { diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 6faa484dede7..aa2edb63a97b 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1067,6 +1067,26 @@ static struct sk_buff *br_ip4_multicast_alloc_query(= struct net_bridge_mcast *brm return skb; } =20 +static bool +__br_multicast_querier_exists(struct net_bridge_mcast *brmctx, + struct bridge_mcast_other_query *querier, + bool is_ipv6) +{ + bool own_querier_enabled; + + if (brmctx->multicast_querier) { + if (is_ipv6 && !br_opt_get(brmctx->br, BROPT_HAS_IPV6_ADDR)) + own_querier_enabled =3D false; + else + own_querier_enabled =3D true; + } else { + own_querier_enabled =3D false; + } + + return !timer_pending(&querier->delay_timer) && + (own_querier_enabled || timer_pending(&querier->timer)); +} + static bool br_ip4_multicast_querier_exists(struct net_bridge_mcast *brmct= x) { return __br_multicast_querier_exists(brmctx, &brmctx->ip4_other_query, @@ -1081,6 +1101,21 @@ static bool br_ip6_multicast_querier_exists(struct n= et_bridge_mcast *brmctx) } #endif =20 +static bool +br_multicast_querier_exists(struct net_bridge_mcast *brmctx, u16 proto) +{ + switch (proto) { + case ETH_P_IP: + return br_ip4_multicast_querier_exists(brmctx); +#if IS_ENABLED(CONFIG_IPV6) + case ETH_P_IPV6: + return br_ip6_multicast_querier_exists(brmctx); +#endif + default: + return false; + } +} + static void br_ip4_multicast_update_active(struct net_bridge_mcast *brmctx, bool force_inactive) { @@ -5159,7 +5194,6 @@ bool br_multicast_has_querier_anywhere(struct net_dev= ice *dev, int proto) { struct net_bridge *br; struct net_bridge_port *port; - struct ethhdr eth; bool ret =3D false; =20 rcu_read_lock(); @@ -5172,10 +5206,7 @@ bool br_multicast_has_querier_anywhere(struct net_de= vice *dev, int proto) =20 br =3D port->br; =20 - memset(ð, 0, sizeof(eth)); - eth.h_proto =3D htons(proto); - - ret =3D br_multicast_querier_exists(&br->multicast_ctx, ð, NULL); + ret =3D br_multicast_querier_exists(&br->multicast_ctx, proto); =20 unlock: rcu_read_unlock(); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 2bdfc93499fc..0d1d39081acd 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1159,37 +1159,16 @@ br_multicast_is_router(struct net_bridge_mcast *brm= ctx, struct sk_buff *skb) } =20 static inline bool -__br_multicast_querier_exists(struct net_bridge_mcast *brmctx, - struct bridge_mcast_other_query *querier, - const bool is_ipv6) +br_multicast_snooping_active(const struct net_bridge_mcast *brmctx, + __be16 eth_proto, + const struct net_bridge_mdb_entry *mdb) { - bool own_querier_enabled; - - if (brmctx->multicast_querier) { - if (is_ipv6 && !br_opt_get(brmctx->br, BROPT_HAS_IPV6_ADDR)) - own_querier_enabled =3D false; - else - own_querier_enabled =3D true; - } else { - own_querier_enabled =3D false; - } - - return !timer_pending(&querier->delay_timer) && - (own_querier_enabled || timer_pending(&querier->timer)); -} - -static inline bool br_multicast_querier_exists(struct net_bridge_mcast *br= mctx, - struct ethhdr *eth, - const struct net_bridge_mdb_entry *mdb) -{ - switch (eth->h_proto) { - case (htons(ETH_P_IP)): - return __br_multicast_querier_exists(brmctx, - &brmctx->ip4_other_query, false); + switch (eth_proto) { + case htons(ETH_P_IP): + return READ_ONCE(brmctx->ip4_active); #if IS_ENABLED(CONFIG_IPV6) - case (htons(ETH_P_IPV6)): - return __br_multicast_querier_exists(brmctx, - &brmctx->ip6_other_query, true); + case htons(ETH_P_IPV6): + return READ_ONCE(brmctx->ip6_active); #endif default: return !!mdb && br_group_is_l2(&mdb->addr); @@ -1458,9 +1437,10 @@ static inline bool br_multicast_is_router(struct net= _bridge_mcast *brmctx, return false; } =20 -static inline bool br_multicast_querier_exists(struct net_bridge_mcast *br= mctx, - struct ethhdr *eth, - const struct net_bridge_mdb_entry *mdb) +static inline bool +br_multicast_snooping_active(const struct net_bridge_mcast *brmctx, + __be16 eth_proto, + const struct net_bridge_mdb_entry *mdb) { return false; } --=20 2.53.0 From nobody Sat Apr 4 01:32:58 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B7245355F5C; Sat, 7 Mar 2026 04:46:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858784; cv=none; b=SsXJorfCZVeU8ASaJWU/hMRm+I0R8FdCd3pZE8sgfhgfJpH+0yrrCy3CSAIjzi/tYsckDBZ7a4M7PaKooXpWknPKxabIZtLuhxuVql+xZzWSlTErFxf5Rrf1GaN5dYfDNxRIsuSowOxwjqxWKQXYzlazG1gC+D/Ms3I6p1HcJxc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772858784; c=relaxed/simple; bh=z13BXSFHymJ9s0ErD9fYeoIevO4Ibm8FueDQWToATl0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=igzlgk0in8qMSd4rndee/2V0GTb/7QgoscAm5dML3a1eETYNRJf3JCk42OF841VPtE8/16i3sn6U1W4OEJewcKA6ezJ+KpsHOgZLwCA50kku5W09HPHdBZT9kGZ8hRuWWyFB3xqQ6bBkqPZxZqWzPScmMZZG7OHNjZe+6Sk4Kww= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 4246F54CE95; Sat, 7 Mar 2026 05:46:20 +0100 (CET) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Nikolay Aleksandrov , Ido Schimmel , Andrew Lunn , Simon Horman , Paolo Abeni , Jakub Kicinski , Eric Dumazet , "David S . Miller" , Kuniyuki Iwashima , Stanislav Fomichev , Xiao Liang , shuah@kernel.org, petrm@nvidia.com, =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next v4 14/14] net: bridge: mcast: add inactive state assertions Date: Sat, 7 Mar 2026 05:45:48 +0100 Message-ID: <20260307044548.5230-15-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260307044548.5230-1-linus.luessing@c0d3.blue> References: <20260307044548.5230-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 To avoid packetloss and as it is very hard from a user's perspective to debug multicast snooping related issues it is even more crucial to properly switch from an active to an inactive multicast snooping state than the other way around. Therefore adding a few kernel warnings if any of our assertions to be in an inactive state would fail. Signed-off-by: Linus L=C3=BCssing --- net/bridge/br_multicast.c | 45 +++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index aa2edb63a97b..f4b6ea674988 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1426,10 +1426,31 @@ static struct sk_buff *br_multicast_alloc_query(str= uct net_bridge_mcast *brmctx, return NULL; } =20 +static void br_ip4_multicast_assert_inactive(struct net_bridge_mcast *brmc= tx) +{ + WARN_ON_ONCE(br_multicast_snooping_active(brmctx, htons(ETH_P_IP), + NULL)); +} + +static void br_ip6_multicast_assert_inactive(struct net_bridge_mcast *brmc= tx) +{ + WARN_ON_ONCE(br_multicast_snooping_active(brmctx, htons(ETH_P_IPV6), + NULL)); +} + +static void br_multicast_assert_inactive(struct net_bridge_mcast *brmctx) +{ + br_ip4_multicast_assert_inactive(brmctx); + br_ip6_multicast_assert_inactive(brmctx); +} + static void br_multicast_toggle_enabled(struct net_bridge *br, bool on) { br_opt_toggle(br, BROPT_MULTICAST_ENABLED, on); br_multicast_update_active(&br->multicast_ctx); + + if (!on) + br_multicast_assert_inactive(&br->multicast_ctx); } =20 struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br, @@ -1899,9 +1920,7 @@ static void br_multicast_querier_expired(struct net_b= ridge_mcast *brmctx, struct bridge_mcast_own_query *query, struct timer_list *timer) { - spin_lock(&brmctx->br->multicast_lock); - if (br_multicast_is_stopping(brmctx->br, timer) || - br_multicast_ctx_vlan_global_disabled(brmctx) || + if (br_multicast_ctx_vlan_global_disabled(brmctx) || !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) goto out; =20 @@ -1912,7 +1931,6 @@ static void br_multicast_querier_expired(struct net_b= ridge_mcast *brmctx, * if our own querier is disabled, too */ br_multicast_update_active(brmctx); - spin_unlock(&brmctx->br->multicast_lock); } =20 static void br_ip4_multicast_querier_expired(struct timer_list *t) @@ -1920,7 +1938,16 @@ static void br_ip4_multicast_querier_expired(struct = timer_list *t) struct net_bridge_mcast *brmctx =3D timer_container_of(brmctx, t, ip4_other_query.timer); =20 + spin_lock(&brmctx->br->multicast_lock); + if (br_multicast_is_stopping(brmctx->br, t)) + goto out; + br_multicast_querier_expired(brmctx, &brmctx->ip4_own_query, t); + + if (!brmctx->multicast_querier) + br_ip4_multicast_assert_inactive(brmctx); +out: + spin_unlock(&brmctx->br->multicast_lock); } =20 #if IS_ENABLED(CONFIG_IPV6) @@ -1929,7 +1956,16 @@ static void br_ip6_multicast_querier_expired(struct = timer_list *t) struct net_bridge_mcast *brmctx =3D timer_container_of(brmctx, t, ip6_other_query.timer); =20 + spin_lock(&brmctx->br->multicast_lock); + if (br_multicast_is_stopping(brmctx->br, t)) + goto out; + br_multicast_querier_expired(brmctx, &brmctx->ip6_own_query, t); + + if (!brmctx->multicast_querier) + br_ip6_multicast_assert_inactive(brmctx); +out: + spin_unlock(&brmctx->br->multicast_lock); } #endif =20 @@ -4529,6 +4565,7 @@ static void __br_multicast_stop(struct net_bridge_mca= st *brmctx) =20 /* bridge interface is down, set multicast state to inactive */ br_multicast_update_active(brmctx); + br_multicast_assert_inactive(brmctx); } =20 void br_multicast_update_vlan_mcast_ctx(struct net_bridge_vlan *v, u8 stat= e) --=20 2.53.0