[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250829085724.24230-2-linus.luessing@c0d3.blue>
Date: Fri, 29 Aug 2025 10:53:42 +0200
From: Linus Lüssing <linus.luessing@...3.blue>
To: bridge@...ts.linux.dev
Cc: netdev@...r.kernel.org,
linux-kernel@...r.kernel.org,
Nikolay Aleksandrov <razor@...ckwall.org>,
Ido Schimmel <idosch@...dia.com>,
Andrew Lunn <andrew+netdev@...n.ch>,
Simon Horman <horms@...nel.org>,
Paolo Abeni <pabeni@...hat.com>,
Jakub Kicinski <kuba@...nel.org>,
Eric Dumazet <edumazet@...gle.com>,
"David S . Miller" <davem@...emloft.net>,
Kuniyuki Iwashima <kuniyu@...gle.com>,
Stanislav Fomichev <sdf@...ichev.me>,
Xiao Liang <shaw.leon@...il.com>,
Linus Lüssing <linus.luessing@...3.blue>
Subject: [PATCH 1/9] net: bridge: mcast: track active state, IGMP/MLD querier appearance
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üssing <linus.luessing@...3.blue>
---
net/bridge/br_multicast.c | 91 +++++++++++++++++++++++++++++++++++++--
net/bridge/br_private.h | 2 +
2 files changed, 90 insertions(+), 3 deletions(-)
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 22d12e545966..5cc713adcf9b 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1069,6 +1069,65 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
return skb;
}
+static bool br_ip4_multicast_querier_exists(struct net_bridge_mcast *brmctx)
+{
+ 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 *brmctx)
+{
+ 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 = false;
+ else
+ brmctx->ip4_active = 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 = false;
+ else
+ brmctx->ip6_active = br_ip6_multicast_querier_exists(brmctx);
+#endif
+}
+
+/**
+ * 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 force_inactive = 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);
+}
+
#if IS_ENABLED(CONFIG_IPV6)
static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brmctx,
struct net_bridge_mcast_port *pmctx,
@@ -1762,10 +1821,34 @@ static void br_ip6_multicast_querier_expired(struct timer_list *t)
}
#endif
-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 = timer_container_of(brmctx, t,
+ ip4_other_query.delay_timer);
+
+ spin_lock(&brmctx->br->multicast_lock);
+ /* 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);
}
+#if IS_ENABLED(CONFIG_IPV6)
+static void br_ip6_multicast_query_delay_expired(struct timer_list *t)
+{
+ struct net_bridge_mcast *brmctx = timer_container_of(brmctx, t,
+ ip6_other_query.delay_timer);
+
+ spin_lock(&brmctx->br->multicast_lock);
+ /* an own or other MLD querier appeared some seconds ago and all
+ * reports should have arrived, 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 *brmctx,
struct br_ip *ip,
struct sk_buff *skb)
@@ -4112,11 +4195,13 @@ void br_multicast_ctx_init(struct net_bridge *br,
brmctx->multicast_membership_interval = 260 * HZ;
brmctx->ip4_querier.port_ifidx = 0;
+ brmctx->ip4_active = 0;
seqcount_spinlock_init(&brmctx->ip4_querier.seq, &br->multicast_lock);
brmctx->multicast_igmp_version = 2;
#if IS_ENABLED(CONFIG_IPV6)
brmctx->multicast_mld_version = 1;
brmctx->ip6_querier.port_ifidx = 0;
+ brmctx->ip6_active = 0;
seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock);
#endif
@@ -4125,7 +4210,7 @@ void br_multicast_ctx_init(struct net_bridge *br,
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);
+ br_ip4_multicast_query_delay_expired, 0);
timer_setup(&brmctx->ip4_own_query.timer,
br_ip4_multicast_query_expired, 0);
#if IS_ENABLED(CONFIG_IPV6)
@@ -4134,7 +4219,7 @@ void br_multicast_ctx_init(struct net_bridge *br,
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);
+ br_ip6_multicast_query_delay_expired, 0);
timer_setup(&brmctx->ip6_own_query.timer,
br_ip6_multicast_query_expired, 0);
#endif
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 8de0904b9627..f83c24def595 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -160,12 +160,14 @@ 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;
struct bridge_mcast_other_query ip6_other_query;
struct bridge_mcast_own_query ip6_own_query;
struct bridge_mcast_querier ip6_querier;
+ bool ip6_active;
#endif /* IS_ENABLED(CONFIG_IPV6) */
#endif /* CONFIG_BRIDGE_IGMP_SNOOPING */
};
--
2.50.1
Powered by blists - more mailing lists