Skip to content

Commit 0888d5f

Browse files
danieldavem330
daniel
authored andcommitted
Bridge: Fix ipv6 mc snooping if bridge has no ipv6 address
The bridge is falsly dropping ipv6 mulitcast packets if there is: 1. No ipv6 address assigned on the brigde. 2. No external mld querier present. 3. The internal querier enabled. When the bridge fails to build mld queries, because it has no ipv6 address, it slilently returns, but keeps the local querier enabled. This specific case causes confusing packet loss. Ipv6 multicast snooping can only work if: a) An external querier is present OR b) The bridge has an ipv6 address an is capable of sending own queries Otherwise it has to forward/flood the ipv6 multicast traffic, because snooping cannot work. This patch fixes the issue by adding a flag to the bridge struct that indicates that there is currently no ipv6 address assinged to the bridge and returns a false state for the local querier in __br_multicast_querier_exists(). Special thanks to Linus Lüssing. Fixes: d1d81d4 ("bridge: check return value of ipv6_dev_get_saddr()") Signed-off-by: Daniel Danzberger <daniel@dd-wrt.com> Acked-by: Linus Lüssing <linus.luessing@c0d3.blue> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent f299a02 commit 0888d5f

File tree

2 files changed

+23
-4
lines changed

2 files changed

+23
-4
lines changed

net/bridge/br_multicast.c

+4
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,11 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
464464
if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0,
465465
&ip6h->saddr)) {
466466
kfree_skb(skb);
467+
br->has_ipv6_addr = 0;
467468
return NULL;
468469
}
470+
471+
br->has_ipv6_addr = 1;
469472
ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
470473

471474
hopopt = (u8 *)(ip6h + 1);
@@ -1745,6 +1748,7 @@ void br_multicast_init(struct net_bridge *br)
17451748
br->ip6_other_query.delay_time = 0;
17461749
br->ip6_querier.port = NULL;
17471750
#endif
1751+
br->has_ipv6_addr = 1;
17481752

17491753
spin_lock_init(&br->multicast_lock);
17501754
setup_timer(&br->multicast_router_timer,

net/bridge/br_private.h

+19-4
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ struct net_bridge
314314
u8 multicast_disabled:1;
315315
u8 multicast_querier:1;
316316
u8 multicast_query_use_ifaddr:1;
317+
u8 has_ipv6_addr:1;
317318

318319
u32 hash_elasticity;
319320
u32 hash_max;
@@ -588,21 +589,35 @@ static inline bool br_multicast_is_router(struct net_bridge *br)
588589

589590
static inline bool
590591
__br_multicast_querier_exists(struct net_bridge *br,
591-
struct bridge_mcast_other_query *querier)
592+
struct bridge_mcast_other_query *querier,
593+
const bool is_ipv6)
592594
{
595+
bool own_querier_enabled;
596+
597+
if (br->multicast_querier) {
598+
if (is_ipv6 && !br->has_ipv6_addr)
599+
own_querier_enabled = false;
600+
else
601+
own_querier_enabled = true;
602+
} else {
603+
own_querier_enabled = false;
604+
}
605+
593606
return time_is_before_jiffies(querier->delay_time) &&
594-
(br->multicast_querier || timer_pending(&querier->timer));
607+
(own_querier_enabled || timer_pending(&querier->timer));
595608
}
596609

597610
static inline bool br_multicast_querier_exists(struct net_bridge *br,
598611
struct ethhdr *eth)
599612
{
600613
switch (eth->h_proto) {
601614
case (htons(ETH_P_IP)):
602-
return __br_multicast_querier_exists(br, &br->ip4_other_query);
615+
return __br_multicast_querier_exists(br,
616+
&br->ip4_other_query, false);
603617
#if IS_ENABLED(CONFIG_IPV6)
604618
case (htons(ETH_P_IPV6)):
605-
return __br_multicast_querier_exists(br, &br->ip6_other_query);
619+
return __br_multicast_querier_exists(br,
620+
&br->ip6_other_query, true);
606621
#endif
607622
default:
608623
return false;

0 commit comments

Comments
 (0)