[source] kernel: Fix ipv6 mc snooping if bridge has no ipv6 address

LEDE Commits lede-commits at lists.infradead.org
Thu Jun 30 21:44:59 PDT 2016


blogic pushed a commit to source.git, branch master:
https://git.lede-project.org/?p=source.git;a=commitdiff;h=f576ff05a1523a0c61a3399087454034d4739f2c

commit f576ff05a1523a0c61a3399087454034d4739f2c
Author: daniel <daniel at dd-wrt.com>
AuthorDate: Thu Jun 30 23:49:52 2016 +0200

    kernel: Fix ipv6 mc snooping if bridge has no ipv6 address
    
    This patch is a backport for current LEDE 4.4 Kernels.
    It is already upstream, for linux-next and stable.
    The initial commit message is below:
    
    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.
    
    Signed-off-by: Daniel Danzberger <daniel at dd-wrt.com>
---
 ...mc_snooping_if_bridge_has_no_ipv6_address.patch | 113 +++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/target/linux/generic/patches-4.4/646-bridge_fix_ipv6_mc_snooping_if_bridge_has_no_ipv6_address.patch b/target/linux/generic/patches-4.4/646-bridge_fix_ipv6_mc_snooping_if_bridge_has_no_ipv6_address.patch
new file mode 100644
index 0000000..4388cda
--- /dev/null
+++ b/target/linux/generic/patches-4.4/646-bridge_fix_ipv6_mc_snooping_if_bridge_has_no_ipv6_address.patch
@@ -0,0 +1,113 @@
+From: daniel <daniel at dd-wrt.com>
+Date: Fri, 24 Jun 2016 12:35:18 +0200
+Subject: [PATCH] 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: d1d81d4c3dd8 ("bridge: check return value of ipv6_dev_get_saddr()")
+Signed-off-by: Daniel Danzberger <daniel at dd-wrt.com>
+Acked-by: Linus Lüssing <linus.luessing at c0d3.blue>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ net/bridge/br_multicast.c |  4 ++++
+ net/bridge/br_private.h   | 23 +++++++++++++++++++----
+ 2 files changed, 23 insertions(+), 4 deletions(-)
+
+diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
+index c8c2a8a..d063a10 100644
+--- a/net/bridge/br_multicast.c
++++ b/net/bridge/br_multicast.c
+@@ -465,8 +465,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);
+@@ -1768,6 +1771,7 @@ void br_multicast_init(struct net_bridge *br)
+ 	br->ip6_other_query.delay_time = 0;
+ 	br->ip6_querier.port = NULL;
+ #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 e24abfd..3dd7e2c 100644
+--- a/net/bridge/br_private.h
++++ b/net/bridge/br_private.h
+@@ -303,6 +303,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;
+@@ -577,10 +578,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_other_query *querier)
++				struct bridge_mcast_other_query *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,
+@@ -588,10 +601,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_other_query);
++		return __br_multicast_querier_exists(br,
++			&br->ip4_other_query, false);
+ #if IS_ENABLED(CONFIG_IPV6)
+ 	case (htons(ETH_P_IPV6)):
+-		return __br_multicast_querier_exists(br, &br->ip6_other_query);
++		return __br_multicast_querier_exists(br,
++			&br->ip6_other_query, true);
+ #endif
+ 	default:
+ 		return false;



More information about the lede-commits mailing list