Files
infix/patches/linux/6.12.63/0019-net-bridge-avoid-classifying-unknown-multicast-as-mr.patch
T
ael-botandJoachim Wiberg 119b92f94e Upgrade Linux kernel to 6.12.63
Backported from origin/main a999a93e63

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-12-19 11:36:54 +01:00

234 lines
8.7 KiB
Diff

From a16724e0f5ad607c5a066005870fffc90f5954e3 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 4 Mar 2024 16:47:28 +0100
Subject: [PATCH 19/30] net: bridge: avoid classifying unknown multicast as
mrouters_only
Organization: Wires
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 | 2 +-
net/bridge/br_multicast.c | 11 +++++++++--
net/bridge/br_private.h | 18 ++++++++++++++++--
7 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index a5b743a2f7750..3bffc39d78009 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -830,6 +830,7 @@ enum br_boolopt_id {
BR_BOOLOPT_NO_LL_LEARN,
BR_BOOLOPT_MCAST_VLAN_SNOOPING,
BR_BOOLOPT_MST_ENABLE,
+ BR_BOOLOPT_MCAST_FLOOD_ALWAYS,
BR_BOOLOPT_MAX
};
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 2cab878e0a39c..006fb2e5eafbf 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -277,6 +277,9 @@ int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
case BR_BOOLOPT_MST_ENABLE:
err = br_mst_set_enabled(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);
@@ -295,6 +298,8 @@ int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
return br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED);
case BR_BOOLOPT_MST_ENABLE:
return br_opt_get(br, BROPT_MST_ENABLED);
+ 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 26b79feb385d2..f3ecc70a47dc3 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -87,10 +87,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)) {
@@ -103,11 +103,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();
@@ -526,6 +526,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 e19b583ff2c6d..1094364d96e0b 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -198,14 +198,19 @@ 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)
{
+ 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
@@ -216,6 +221,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 ceaa5a89b947f..e54d648db7de8 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -212,7 +212,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 0a00c3f578156..1ae021be0a7b1 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -3835,6 +3835,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)
@@ -3881,7 +3886,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);
@@ -3950,7 +3956,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 5026a256bf92d..db6903b288af1 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -485,6 +485,7 @@ enum net_bridge_opts {
BROPT_VLAN_BRIDGE_BINDING,
BROPT_MCAST_VLAN_SNOOPING_ENABLED,
BROPT_MST_ENABLED,
+ BROPT_MCAST_FLOOD_ALWAYS,
};
struct net_bridge {
@@ -889,8 +890,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,
@@ -1412,6 +1413,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)
{