Files
infix/patches/linux/6.18.39/0006-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch
T

251 lines
9.6 KiB
Diff

From 08f6224ebcba517410b4dc2b98f7b50db842dc30 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 4 Mar 2024 16:47:28 +0100
Subject: [PATCH 06/50] net: bridge: avoid classifying unknown multicast as
mrouters_only
Unknown multicast, MAC/IPv4/IPv6, should always be flooded according to
the per-port mcast_flood setting, as well as to detected and configured
mcast_router ports.
This patch drops the mrouters_only classifier of unknown IP multicast
and moves the flow handling from br_multicast_flood() to br_flood().
This in turn means br_flood() must know about multicast router ports.
Because a multicast router should always receive both known and unknown
multicast.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
include/uapi/linux/if_bridge.h | 1 +
net/bridge/br.c | 5 +++++
net/bridge/br_device.c | 10 ++++++----
net/bridge/br_forward.c | 14 ++++++++++++--
net/bridge/br_input.c | 12 ++++++++----
net/bridge/br_multicast.c | 11 +++++++++--
net/bridge/br_private.h | 18 ++++++++++++++++--
7 files changed, 57 insertions(+), 14 deletions(-)
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index e52f8207ab27..306fa8a94c81 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -835,6 +835,7 @@ enum br_boolopt_id {
BR_BOOLOPT_MST_ENABLE,
BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION,
BR_BOOLOPT_FDB_LOCAL_VLAN_0,
+ BR_BOOLOPT_MCAST_FLOOD_ALWAYS,
BR_BOOLOPT_MAX
};
diff --git a/net/bridge/br.c b/net/bridge/br.c
index c37e52e2f29a..0759bc37d6f0 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -312,6 +312,9 @@ int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
case BR_BOOLOPT_FDB_LOCAL_VLAN_0:
err = br_toggle_fdb_local_vlan_0(br, on, extack);
break;
+ case BR_BOOLOPT_MCAST_FLOOD_ALWAYS:
+ br_opt_toggle(br, BROPT_MCAST_FLOOD_ALWAYS, on);
+ break;
default:
/* shouldn't be called with unsupported options */
WARN_ON(1);
@@ -334,6 +337,8 @@ int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
return br_opt_get(br, BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION);
case BR_BOOLOPT_FDB_LOCAL_VLAN_0:
return br_opt_get(br, BROPT_FDB_LOCAL_VLAN_0);
+ case BR_BOOLOPT_MCAST_FLOOD_ALWAYS:
+ return br_opt_get(br, BROPT_MCAST_FLOOD_ALWAYS);
default:
/* shouldn't be called with unsupported options */
WARN_ON(1);
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 525d4eccd194..6278dfc9612f 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -89,10 +89,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
dest = eth_hdr(skb)->h_dest;
if (is_broadcast_ether_addr(dest)) {
- br_flood(br, skb, BR_PKT_BROADCAST, false, true, vid);
+ br_flood(br, skb, NULL, BR_PKT_BROADCAST, false, true, vid);
} else if (is_multicast_ether_addr(dest)) {
if (unlikely(netpoll_tx_running(dev))) {
- br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid);
+ br_flood(br, skb, brmctx, BR_PKT_MULTICAST, false, true, vid);
goto out;
}
if (br_multicast_rcv(&brmctx, &pmctx_null, vlan, skb, vid)) {
@@ -105,11 +105,11 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst))
br_multicast_flood(mdst, skb, brmctx, false, true);
else
- br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid);
+ br_flood(br, skb, brmctx, BR_PKT_MULTICAST, false, true, vid);
} else if ((dst = br_fdb_find_rcu(br, dest, vid)) != NULL) {
br_forward(dst->dst, skb, false, true);
} else {
- br_flood(br, skb, BR_PKT_UNICAST, false, true, vid);
+ br_flood(br, skb, NULL, BR_PKT_UNICAST, false, true, vid);
}
out:
rcu_read_unlock();
@@ -528,6 +528,8 @@ void br_dev_setup(struct net_device *dev)
br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME;
dev->max_mtu = ETH_MAX_MTU;
+ br_opt_toggle(br, BROPT_MCAST_FLOOD_ALWAYS, false);
+
br_netfilter_rtable_init(br);
br_stp_timer_init(br);
br_multicast_init(br);
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index dea09096ad0f..f0a613238e0d 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -199,15 +199,20 @@ static struct net_bridge_port *maybe_deliver(
/* called under rcu_read_lock */
void br_flood(struct net_bridge *br, struct sk_buff *skb,
- enum br_pkt_type pkt_type, bool local_rcv, bool local_orig,
- u16 vid)
+ struct net_bridge_mcast *brmctx, enum br_pkt_type pkt_type,
+ bool local_rcv, bool local_orig, u16 vid)
{
enum skb_drop_reason reason = SKB_DROP_REASON_NO_TX_TARGET;
+ struct net_bridge_port *rport = NULL;
struct net_bridge_port *prev = NULL;
+ struct hlist_node *rp = NULL;
struct net_bridge_port *p;
br_tc_skb_miss_set(skb, pkt_type != BR_PKT_BROADCAST);
+ if (pkt_type == BR_PKT_MULTICAST)
+ rp = br_multicast_get_first_rport_node(brmctx, skb);
+
list_for_each_entry_rcu(p, &br->port_list, list) {
/* Do not flood unicast traffic to ports that turn it off, nor
* other traffic if flood off, except for traffic we originate
@@ -218,6 +223,11 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb,
continue;
break;
case BR_PKT_MULTICAST:
+ rport = br_multicast_rport_from_node_skb(rp, skb);
+ if (rport == p) {
+ rp = rcu_dereference(hlist_next_rcu(rp));
+ break;
+ }
if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
continue;
break;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 2cbae0f9ae1f..0c539bf4bebe 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -186,9 +186,13 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
switch (pkt_type) {
case BR_PKT_MULTICAST:
mdst = 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)) {
- if ((mdst && mdst->host_joined) ||
+ /* Only use strict multicast filtering when:
+ * 1. There's an MDB entry (someone joined this group), AND
+ * 2. A querier exists (we can rely on IGMP/MLD)
+ * Unknown multicast (no MDB entry) is always flooded.
+ */
+ if (mdst && br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) {
+ if (mdst->host_joined ||
br_multicast_is_router(brmctx, skb) ||
br->dev->flags & IFF_ALLMULTI) {
local_rcv = true;
@@ -226,7 +230,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
br_forward(dst->dst, skb, local_rcv, false);
} else {
if (!mcast_hit)
- br_flood(br, skb, pkt_type, local_rcv, false, vid);
+ br_flood(br, skb, brmctx, pkt_type, local_rcv, false, vid);
else
br_multicast_flood(mdst, skb, brmctx, local_rcv, false);
}
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index b77b3250afbd..f27cb9b54211 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -3836,6 +3836,11 @@ static void br_multicast_err_count(const struct net_bridge *br,
u64_stats_update_end(&pstats->syncp);
}
+static bool br_flood_mrouters(const struct net_bridge *br)
+{
+ return br_opt_get(br, BROPT_MCAST_FLOOD_ALWAYS) ? false : true;
+}
+
static void br_multicast_pim(struct net_bridge_mcast *brmctx,
struct net_bridge_mcast_port *pmctx,
const struct sk_buff *skb)
@@ -3882,7 +3887,8 @@ static int br_multicast_ipv4_rcv(struct net_bridge_mcast *brmctx,
if (err == -ENOMSG) {
if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) {
- BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
+ BR_INPUT_SKB_CB(skb)->mrouters_only =
+ br_flood_mrouters(brmctx->br);
} else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) {
if (ip_hdr(skb)->protocol == IPPROTO_PIM)
br_multicast_pim(brmctx, pmctx, skb);
@@ -3951,7 +3957,8 @@ static int br_multicast_ipv6_rcv(struct net_bridge_mcast *brmctx,
if (err == -ENOMSG || err == -ENODATA) {
if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
- BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
+ BR_INPUT_SKB_CB(skb)->mrouters_only =
+ br_flood_mrouters(brmctx->br);
if (err == -ENODATA &&
ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr))
br_ip6_multicast_mrd_rcv(brmctx, pmctx, skb);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index b9b2981c4841..ccbaaa96f7c7 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -489,6 +489,7 @@ enum net_bridge_opts {
BROPT_MST_ENABLED,
BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION,
BROPT_FDB_LOCAL_VLAN_0,
+ BROPT_MCAST_FLOOD_ALWAYS,
};
struct net_bridge {
@@ -895,8 +896,8 @@ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb,
bool local_rcv, bool local_orig);
int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
void br_flood(struct net_bridge *br, struct sk_buff *skb,
- enum br_pkt_type pkt_type, bool local_rcv, bool local_orig,
- u16 vid);
+ struct net_bridge_mcast *brmctx, enum br_pkt_type pkt_type,
+ bool local_rcv, bool local_orig, u16 vid);
/* return true if both source port and dest port are isolated */
static inline bool br_skb_isolated(const struct net_bridge_port *to,
@@ -1435,6 +1436,19 @@ static inline void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
{
}
+static inline struct hlist_node *
+br_multicast_get_first_rport_node(struct net_bridge_mcast *brmctx,
+ struct sk_buff *skb)
+{
+ return NULL;
+}
+
+static inline struct net_bridge_port *
+br_multicast_rport_from_node_skb(struct hlist_node *rp, struct sk_buff *skb)
+{
+ return NULL;
+}
+
static inline bool br_multicast_is_router(struct net_bridge_mcast *brmctx,
struct sk_buff *skb)
{