From 0da1bf826cfc3f82ccec6d3c4cb0dcd332704446 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 24 Jun 2016 12:35:18 +0200 Subject: [PATCH] Bridge: Fix ipv6 mc snooping if bridge has no ipv6 address MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit [ Upstream commit 0888d5f3c0f183ea6177355752ada433d370ac89 ] 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: d1d81d4c3dd8 ("bridge: check return value of ipv6_dev_get_saddr()") Signed-off-by: Daniel Danzberger Acked-by: Linus Lüssing Signed-off-by: David S. Miller Signed-off-by: Jiri Slaby --- net/bridge/br_multicast.c | 4 ++++ net/bridge/br_private.h | 25 ++++++++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index f2c104900163..91fed8147c39 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -449,8 +449,11 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0, &ip6h->saddr)) { kfree_skb(skb); + br->has_ipv6_addr = 0; return NULL; } + + br->has_ipv6_addr = 1; ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); hopopt = (u8 *)(ip6h + 1); @@ -1776,6 +1779,7 @@ void br_multicast_init(struct net_bridge *br) #if IS_ENABLED(CONFIG_IPV6) br->ip6_querier.delay_time = 0; #endif + br->has_ipv6_addr = 1; spin_lock_init(&br->multicast_lock); setup_timer(&br->multicast_router_timer, diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index f02acd7c5472..8b7db15212b9 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -265,6 +265,7 @@ struct net_bridge u8 multicast_disabled:1; u8 multicast_querier:1; u8 multicast_query_use_ifaddr:1; + u8 has_ipv6_addr:1; u32 hash_elasticity; u32 hash_max; @@ -512,10 +513,22 @@ static inline bool br_multicast_is_router(struct net_bridge *br) static inline bool __br_multicast_querier_exists(struct net_bridge *br, - struct bridge_mcast_querier *querier) -{ + struct bridge_mcast_querier *querier, + const bool is_ipv6) +{ + bool own_querier_enabled; + + if (br->multicast_querier) { + if (is_ipv6 && !br->has_ipv6_addr) + own_querier_enabled = false; + else + own_querier_enabled = true; + } else { + own_querier_enabled = false; + } + return time_is_before_jiffies(querier->delay_time) && - (br->multicast_querier || timer_pending(&querier->timer)); + (own_querier_enabled || timer_pending(&querier->timer)); } static inline bool br_multicast_querier_exists(struct net_bridge *br, @@ -523,10 +536,12 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br, { switch (eth->h_proto) { case (htons(ETH_P_IP)): - return __br_multicast_querier_exists(br, &br->ip4_querier); + return __br_multicast_querier_exists(br, + &br->ip4_querier, false); #if IS_ENABLED(CONFIG_IPV6) case (htons(ETH_P_IPV6)): - return __br_multicast_querier_exists(br, &br->ip6_querier); + return __br_multicast_querier_exists(br, + &br->ip6_querier, true); #endif default: return false; -- 2.43.0