mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			974 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			974 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
commit 3c3769e63301fd92fcaf51870c371583dd0282ce
 | 
						|
Author: Linus Lüssing <linus.luessing@web.de>
 | 
						|
Date:   Wed Sep 4 02:13:39 2013 +0200
 | 
						|
 | 
						|
    bridge: apply multicast snooping to IPv6 link-local, too
 | 
						|
    
 | 
						|
    The multicast snooping code should have matured enough to be safely
 | 
						|
    applicable to IPv6 link-local multicast addresses (excluding the
 | 
						|
    link-local all nodes address, ff02::1), too.
 | 
						|
    
 | 
						|
    Signed-off-by: Linus Lüssing <linus.luessing@web.de>
 | 
						|
    Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
						|
 | 
						|
commit 8fad9c39f31f9ed7bf3526c43a4537b2fcf1a5d5
 | 
						|
Author: Linus Lüssing <linus.luessing@web.de>
 | 
						|
Date:   Wed Sep 4 02:13:38 2013 +0200
 | 
						|
 | 
						|
    bridge: prevent flooding IPv6 packets that do not have a listener
 | 
						|
    
 | 
						|
    Currently if there is no listener for a certain group then IPv6 packets
 | 
						|
    for that group are flooded on all ports, even though there might be no
 | 
						|
    host and router interested in it on a port.
 | 
						|
    
 | 
						|
    With this commit they are only forwarded to ports with a multicast
 | 
						|
    router.
 | 
						|
    
 | 
						|
    Just like commit bd4265fe36 ("bridge: Only flood unregistered groups
 | 
						|
    to routers") did for IPv4, let's do the same for IPv6 with the same
 | 
						|
    reasoning.
 | 
						|
    
 | 
						|
    Signed-off-by: Linus Lüssing <linus.luessing@web.de>
 | 
						|
    Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
						|
 | 
						|
commit cc0fdd802859eaeb00e1c87dbb655594bed2844c
 | 
						|
Author: Linus Lüssing <linus.luessing@web.de>
 | 
						|
Date:   Fri Aug 30 17:28:17 2013 +0200
 | 
						|
 | 
						|
    bridge: separate querier and query timer into IGMP/IPv4 and MLD/IPv6 ones
 | 
						|
    
 | 
						|
    Currently we would still potentially suffer multicast packet loss if there
 | 
						|
    is just either an IGMP or an MLD querier: For the former case, we would
 | 
						|
    possibly drop IPv6 multicast packets, for the latter IPv4 ones. This is
 | 
						|
    because we are currently assuming that if either an IGMP or MLD querier
 | 
						|
    is present that the other one is present, too.
 | 
						|
    
 | 
						|
    This patch makes the behaviour and fix added in
 | 
						|
    "bridge: disable snooping if there is no querier" (b00589af3b04)
 | 
						|
    to also work if there is either just an IGMP or an MLD querier on the
 | 
						|
    link: It refines the deactivation of the snooping to be protocol
 | 
						|
    specific by using separate timers for the snooped IGMP and MLD queries
 | 
						|
    as well as separate timers for our internal IGMP and MLD queriers.
 | 
						|
    
 | 
						|
    Signed-off-by: Linus Lüssing <linus.luessing@web.de>
 | 
						|
    Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
						|
 | 
						|
commit b00589af3b04736376f24625ab0b394642e89e29
 | 
						|
Author: Linus Lüssing <linus.luessing@web.de>
 | 
						|
Date:   Thu Aug 1 01:06:20 2013 +0200
 | 
						|
 | 
						|
    bridge: disable snooping if there is no querier
 | 
						|
    
 | 
						|
    If there is no querier on a link then we won't get periodic reports and
 | 
						|
    therefore won't be able to learn about multicast listeners behind ports,
 | 
						|
    potentially leading to lost multicast packets, especially for multicast
 | 
						|
    listeners that joined before the creation of the bridge.
 | 
						|
    
 | 
						|
    These lost multicast packets can appear since c5c23260594
 | 
						|
    ("bridge: Add multicast_querier toggle and disable queries by default")
 | 
						|
    in particular.
 | 
						|
    
 | 
						|
    With this patch we are flooding multicast packets if our querier is
 | 
						|
    disabled and if we didn't detect any other querier.
 | 
						|
    
 | 
						|
    A grace period of the Maximum Response Delay of the querier is added to
 | 
						|
    give multicast responses enough time to arrive and to be learned from
 | 
						|
    before disabling the flooding behaviour again.
 | 
						|
    
 | 
						|
    Signed-off-by: Linus Lüssing <linus.luessing@web.de>
 | 
						|
    Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
						|
 | 
						|
commit 6b7df111ece130fa979a0c4f58e53674c1e47d3e
 | 
						|
Author: Cong Wang <amwang@redhat.com>
 | 
						|
Date:   Tue May 21 21:52:56 2013 +0000
 | 
						|
 | 
						|
    bridge: send query as soon as leave is received
 | 
						|
    
 | 
						|
    Continue sending queries when leave is received if the user marks
 | 
						|
    it as a querier.
 | 
						|
    
 | 
						|
    Cc: Herbert Xu <herbert@gondor.apana.org.au>
 | 
						|
    Cc: Stephen Hemminger <stephen@networkplumber.org>
 | 
						|
    Cc: "David S. Miller" <davem@davemloft.net>
 | 
						|
    Cc: Adam Baker <linux@baker-net.org.uk>
 | 
						|
    Signed-off-by: Cong Wang <amwang@redhat.com>
 | 
						|
    Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
 | 
						|
    Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
						|
 | 
						|
commit 1c8ad5bfa2be5025b0c81e3c2decd0574d453ab1
 | 
						|
Author: Cong Wang <amwang@redhat.com>
 | 
						|
Date:   Tue May 21 21:52:54 2013 +0000
 | 
						|
 | 
						|
    bridge: use the bridge IP addr as source addr for querier
 | 
						|
    
 | 
						|
    Quote from Adam:
 | 
						|
    "If it is believed that the use of 0.0.0.0
 | 
						|
    as the IP address is what is causing strange behaviour on other devices
 | 
						|
    then is there a good reason that a bridge rather than a router shouldn't
 | 
						|
    be the active querier? If not then using the bridge IP address and
 | 
						|
    having the querier enabled by default may be a reasonable solution
 | 
						|
    (provided that our querier obeys the election rules and shuts up if it
 | 
						|
    sees a query from a lower IP address that isn't 0.0.0.0). Just because a
 | 
						|
    device is the elected querier for IGMP doesn't appear to mean it is
 | 
						|
    required to perform any other routing functions."
 | 
						|
    
 | 
						|
    And introduce a new troggle for it, as suggested by Herbert.
 | 
						|
    
 | 
						|
    Suggested-by: Adam Baker <linux@baker-net.org.uk>
 | 
						|
    Cc: Herbert Xu <herbert@gondor.apana.org.au>
 | 
						|
    Cc: Stephen Hemminger <stephen@networkplumber.org>
 | 
						|
    Cc: "David S. Miller" <davem@davemloft.net>
 | 
						|
    Cc: Adam Baker <linux@baker-net.org.uk>
 | 
						|
    Signed-off-by: Cong Wang <amwang@redhat.com>
 | 
						|
    Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
 | 
						|
    Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
						|
 | 
						|
--- a/net/bridge/br_device.c
 | 
						|
+++ b/net/bridge/br_device.c
 | 
						|
@@ -67,7 +67,8 @@ netdev_tx_t br_dev_xmit(struct sk_buff *
 | 
						|
 		}
 | 
						|
 
 | 
						|
 		mdst = br_mdb_get(br, skb, vid);
 | 
						|
-		if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb))
 | 
						|
+		if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
 | 
						|
+		    br_multicast_querier_exists(br, eth_hdr(skb)))
 | 
						|
 			br_multicast_deliver(mdst, skb);
 | 
						|
 		else
 | 
						|
 			br_flood_deliver(br, skb);
 | 
						|
--- a/net/bridge/br_input.c
 | 
						|
+++ b/net/bridge/br_input.c
 | 
						|
@@ -98,7 +98,8 @@ int br_handle_frame_finish(struct sk_buf
 | 
						|
 		skb2 = skb;
 | 
						|
 	else if (is_multicast_ether_addr(dest)) {
 | 
						|
 		mdst = br_mdb_get(br, skb, vid);
 | 
						|
-		if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) {
 | 
						|
+		if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
 | 
						|
+		    br_multicast_querier_exists(br, eth_hdr(skb))) {
 | 
						|
 			if ((mdst && mdst->mglist) ||
 | 
						|
 			    br_multicast_is_router(br))
 | 
						|
 				skb2 = skb;
 | 
						|
--- a/net/bridge/br_multicast.c
 | 
						|
+++ b/net/bridge/br_multicast.c
 | 
						|
@@ -23,16 +23,19 @@
 | 
						|
 #include <linux/skbuff.h>
 | 
						|
 #include <linux/slab.h>
 | 
						|
 #include <linux/timer.h>
 | 
						|
+#include <linux/inetdevice.h>
 | 
						|
 #include <net/ip.h>
 | 
						|
 #if IS_ENABLED(CONFIG_IPV6)
 | 
						|
 #include <net/ipv6.h>
 | 
						|
 #include <net/mld.h>
 | 
						|
 #include <net/ip6_checksum.h>
 | 
						|
+#include <net/addrconf.h>
 | 
						|
 #endif
 | 
						|
 
 | 
						|
 #include "br_private.h"
 | 
						|
 
 | 
						|
-static void br_multicast_start_querier(struct net_bridge *br);
 | 
						|
+static void br_multicast_start_querier(struct net_bridge *br,
 | 
						|
+				       struct bridge_mcast_query *query);
 | 
						|
 unsigned int br_mdb_rehash_seq;
 | 
						|
 
 | 
						|
 static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
 | 
						|
@@ -381,7 +384,8 @@ static struct sk_buff *br_ip4_multicast_
 | 
						|
 	iph->frag_off = htons(IP_DF);
 | 
						|
 	iph->ttl = 1;
 | 
						|
 	iph->protocol = IPPROTO_IGMP;
 | 
						|
-	iph->saddr = 0;
 | 
						|
+	iph->saddr = br->multicast_query_use_ifaddr ?
 | 
						|
+		     inet_select_addr(br->dev, 0, RT_SCOPE_LINK) : 0;
 | 
						|
 	iph->daddr = htonl(INADDR_ALLHOSTS_GROUP);
 | 
						|
 	((u8 *)&iph[1])[0] = IPOPT_RA;
 | 
						|
 	((u8 *)&iph[1])[1] = 4;
 | 
						|
@@ -724,7 +728,7 @@ static int br_ip6_multicast_add_group(st
 | 
						|
 {
 | 
						|
 	struct br_ip br_group;
 | 
						|
 
 | 
						|
-	if (!ipv6_is_transient_multicast(group))
 | 
						|
+	if (ipv6_addr_is_ll_all_nodes(group))
 | 
						|
 		return 0;
 | 
						|
 
 | 
						|
 	br_group.u.ip6 = *group;
 | 
						|
@@ -756,20 +760,35 @@ static void br_multicast_local_router_ex
 | 
						|
 {
 | 
						|
 }
 | 
						|
 
 | 
						|
-static void br_multicast_querier_expired(unsigned long data)
 | 
						|
+static void br_multicast_querier_expired(struct net_bridge *br,
 | 
						|
+					 struct bridge_mcast_query *query)
 | 
						|
 {
 | 
						|
-	struct net_bridge *br = (void *)data;
 | 
						|
-
 | 
						|
 	spin_lock(&br->multicast_lock);
 | 
						|
 	if (!netif_running(br->dev) || br->multicast_disabled)
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
-	br_multicast_start_querier(br);
 | 
						|
+	br_multicast_start_querier(br, query);
 | 
						|
 
 | 
						|
 out:
 | 
						|
 	spin_unlock(&br->multicast_lock);
 | 
						|
 }
 | 
						|
 
 | 
						|
+static void br_ip4_multicast_querier_expired(unsigned long data)
 | 
						|
+{
 | 
						|
+	struct net_bridge *br = (void *)data;
 | 
						|
+
 | 
						|
+	br_multicast_querier_expired(br, &br->ip4_query);
 | 
						|
+}
 | 
						|
+
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+static void br_ip6_multicast_querier_expired(unsigned long data)
 | 
						|
+{
 | 
						|
+	struct net_bridge *br = (void *)data;
 | 
						|
+
 | 
						|
+	br_multicast_querier_expired(br, &br->ip6_query);
 | 
						|
+}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
 static void __br_multicast_send_query(struct net_bridge *br,
 | 
						|
 				      struct net_bridge_port *port,
 | 
						|
 				      struct br_ip *ip)
 | 
						|
@@ -790,37 +809,45 @@ static void __br_multicast_send_query(st
 | 
						|
 }
 | 
						|
 
 | 
						|
 static void br_multicast_send_query(struct net_bridge *br,
 | 
						|
-				    struct net_bridge_port *port, u32 sent)
 | 
						|
+				    struct net_bridge_port *port,
 | 
						|
+				    struct bridge_mcast_query *query)
 | 
						|
 {
 | 
						|
 	unsigned long time;
 | 
						|
 	struct br_ip br_group;
 | 
						|
+	struct bridge_mcast_querier *querier = NULL;
 | 
						|
 
 | 
						|
 	if (!netif_running(br->dev) || br->multicast_disabled ||
 | 
						|
-	    !br->multicast_querier ||
 | 
						|
-	    timer_pending(&br->multicast_querier_timer))
 | 
						|
+	    !br->multicast_querier)
 | 
						|
 		return;
 | 
						|
 
 | 
						|
 	memset(&br_group.u, 0, sizeof(br_group.u));
 | 
						|
 
 | 
						|
-	br_group.proto = htons(ETH_P_IP);
 | 
						|
-	__br_multicast_send_query(br, port, &br_group);
 | 
						|
-
 | 
						|
+	if (port ? (query == &port->ip4_query) :
 | 
						|
+		   (query == &br->ip4_query)) {
 | 
						|
+		querier = &br->ip4_querier;
 | 
						|
+		br_group.proto = htons(ETH_P_IP);
 | 
						|
 #if IS_ENABLED(CONFIG_IPV6)
 | 
						|
-	br_group.proto = htons(ETH_P_IPV6);
 | 
						|
-	__br_multicast_send_query(br, port, &br_group);
 | 
						|
+	} else {
 | 
						|
+		querier = &br->ip6_querier;
 | 
						|
+		br_group.proto = htons(ETH_P_IPV6);
 | 
						|
 #endif
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (!querier || timer_pending(&querier->timer))
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	__br_multicast_send_query(br, port, &br_group);
 | 
						|
 
 | 
						|
 	time = jiffies;
 | 
						|
-	time += sent < br->multicast_startup_query_count ?
 | 
						|
+	time += query->startup_sent < br->multicast_startup_query_count ?
 | 
						|
 		br->multicast_startup_query_interval :
 | 
						|
 		br->multicast_query_interval;
 | 
						|
-	mod_timer(port ? &port->multicast_query_timer :
 | 
						|
-			 &br->multicast_query_timer, time);
 | 
						|
+	mod_timer(&query->timer, time);
 | 
						|
 }
 | 
						|
 
 | 
						|
-static void br_multicast_port_query_expired(unsigned long data)
 | 
						|
+static void br_multicast_port_query_expired(struct net_bridge_port *port,
 | 
						|
+					    struct bridge_mcast_query *query)
 | 
						|
 {
 | 
						|
-	struct net_bridge_port *port = (void *)data;
 | 
						|
 	struct net_bridge *br = port->br;
 | 
						|
 
 | 
						|
 	spin_lock(&br->multicast_lock);
 | 
						|
@@ -828,25 +855,43 @@ static void br_multicast_port_query_expi
 | 
						|
 	    port->state == BR_STATE_BLOCKING)
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
-	if (port->multicast_startup_queries_sent <
 | 
						|
-	    br->multicast_startup_query_count)
 | 
						|
-		port->multicast_startup_queries_sent++;
 | 
						|
+	if (query->startup_sent < br->multicast_startup_query_count)
 | 
						|
+		query->startup_sent++;
 | 
						|
 
 | 
						|
-	br_multicast_send_query(port->br, port,
 | 
						|
-				port->multicast_startup_queries_sent);
 | 
						|
+	br_multicast_send_query(port->br, port, query);
 | 
						|
 
 | 
						|
 out:
 | 
						|
 	spin_unlock(&br->multicast_lock);
 | 
						|
 }
 | 
						|
 
 | 
						|
+static void br_ip4_multicast_port_query_expired(unsigned long data)
 | 
						|
+{
 | 
						|
+	struct net_bridge_port *port = (void *)data;
 | 
						|
+
 | 
						|
+	br_multicast_port_query_expired(port, &port->ip4_query);
 | 
						|
+}
 | 
						|
+
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+static void br_ip6_multicast_port_query_expired(unsigned long data)
 | 
						|
+{
 | 
						|
+	struct net_bridge_port *port = (void *)data;
 | 
						|
+
 | 
						|
+	br_multicast_port_query_expired(port, &port->ip6_query);
 | 
						|
+}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
 void br_multicast_add_port(struct net_bridge_port *port)
 | 
						|
 {
 | 
						|
 	port->multicast_router = 1;
 | 
						|
 
 | 
						|
 	setup_timer(&port->multicast_router_timer, br_multicast_router_expired,
 | 
						|
 		    (unsigned long)port);
 | 
						|
-	setup_timer(&port->multicast_query_timer,
 | 
						|
-		    br_multicast_port_query_expired, (unsigned long)port);
 | 
						|
+	setup_timer(&port->ip4_query.timer, br_ip4_multicast_port_query_expired,
 | 
						|
+		    (unsigned long)port);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	setup_timer(&port->ip6_query.timer, br_ip6_multicast_port_query_expired,
 | 
						|
+		    (unsigned long)port);
 | 
						|
+#endif
 | 
						|
 }
 | 
						|
 
 | 
						|
 void br_multicast_del_port(struct net_bridge_port *port)
 | 
						|
@@ -854,13 +899,13 @@ void br_multicast_del_port(struct net_br
 | 
						|
 	del_timer_sync(&port->multicast_router_timer);
 | 
						|
 }
 | 
						|
 
 | 
						|
-static void __br_multicast_enable_port(struct net_bridge_port *port)
 | 
						|
+static void br_multicast_enable(struct bridge_mcast_query *query)
 | 
						|
 {
 | 
						|
-	port->multicast_startup_queries_sent = 0;
 | 
						|
+	query->startup_sent = 0;
 | 
						|
 
 | 
						|
-	if (try_to_del_timer_sync(&port->multicast_query_timer) >= 0 ||
 | 
						|
-	    del_timer(&port->multicast_query_timer))
 | 
						|
-		mod_timer(&port->multicast_query_timer, jiffies);
 | 
						|
+	if (try_to_del_timer_sync(&query->timer) >= 0 ||
 | 
						|
+	    del_timer(&query->timer))
 | 
						|
+		mod_timer(&query->timer, jiffies);
 | 
						|
 }
 | 
						|
 
 | 
						|
 void br_multicast_enable_port(struct net_bridge_port *port)
 | 
						|
@@ -871,7 +916,10 @@ void br_multicast_enable_port(struct net
 | 
						|
 	if (br->multicast_disabled || !netif_running(br->dev))
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
-	__br_multicast_enable_port(port);
 | 
						|
+	br_multicast_enable(&port->ip4_query);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	br_multicast_enable(&port->ip6_query);
 | 
						|
+#endif
 | 
						|
 
 | 
						|
 out:
 | 
						|
 	spin_unlock(&br->multicast_lock);
 | 
						|
@@ -890,7 +938,10 @@ void br_multicast_disable_port(struct ne
 | 
						|
 	if (!hlist_unhashed(&port->rlist))
 | 
						|
 		hlist_del_init_rcu(&port->rlist);
 | 
						|
 	del_timer(&port->multicast_router_timer);
 | 
						|
-	del_timer(&port->multicast_query_timer);
 | 
						|
+	del_timer(&port->ip4_query.timer);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	del_timer(&port->ip6_query.timer);
 | 
						|
+#endif
 | 
						|
 	spin_unlock(&br->multicast_lock);
 | 
						|
 }
 | 
						|
 
 | 
						|
@@ -1015,6 +1066,17 @@ static int br_ip6_multicast_mld2_report(
 | 
						|
 }
 | 
						|
 #endif
 | 
						|
 
 | 
						|
+static void
 | 
						|
+br_multicast_update_querier_timer(struct net_bridge *br,
 | 
						|
+				  struct bridge_mcast_querier *querier,
 | 
						|
+				  unsigned long max_delay)
 | 
						|
+{
 | 
						|
+	if (!timer_pending(&querier->timer))
 | 
						|
+		querier->delay_time = jiffies + max_delay;
 | 
						|
+
 | 
						|
+	mod_timer(&querier->timer, jiffies + br->multicast_querier_interval);
 | 
						|
+}
 | 
						|
+
 | 
						|
 /*
 | 
						|
  * Add port to rotuer_list
 | 
						|
  *  list is maintained ordered by pointer value
 | 
						|
@@ -1065,12 +1127,13 @@ timer:
 | 
						|
 
 | 
						|
 static void br_multicast_query_received(struct net_bridge *br,
 | 
						|
 					struct net_bridge_port *port,
 | 
						|
-					int saddr)
 | 
						|
+					struct bridge_mcast_querier *querier,
 | 
						|
+					int saddr,
 | 
						|
+					unsigned long max_delay)
 | 
						|
 {
 | 
						|
 	if (saddr)
 | 
						|
-		mod_timer(&br->multicast_querier_timer,
 | 
						|
-			  jiffies + br->multicast_querier_interval);
 | 
						|
-	else if (timer_pending(&br->multicast_querier_timer))
 | 
						|
+		br_multicast_update_querier_timer(br, querier, max_delay);
 | 
						|
+	else if (timer_pending(&querier->timer))
 | 
						|
 		return;
 | 
						|
 
 | 
						|
 	br_multicast_mark_router(br, port);
 | 
						|
@@ -1097,8 +1160,6 @@ static int br_ip4_multicast_query(struct
 | 
						|
 	    (port && port->state == BR_STATE_DISABLED))
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
-	br_multicast_query_received(br, port, !!iph->saddr);
 | 
						|
-
 | 
						|
 	group = ih->group;
 | 
						|
 
 | 
						|
 	if (skb->len == sizeof(*ih)) {
 | 
						|
@@ -1122,6 +1183,9 @@ static int br_ip4_multicast_query(struct
 | 
						|
 			    IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
 | 
						|
 	}
 | 
						|
 
 | 
						|
+	br_multicast_query_received(br, port, &br->ip4_querier, !!iph->saddr,
 | 
						|
+				    max_delay);
 | 
						|
+
 | 
						|
 	if (!group)
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
@@ -1174,8 +1238,6 @@ static int br_ip6_multicast_query(struct
 | 
						|
 	    (port && port->state == BR_STATE_DISABLED))
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
-	br_multicast_query_received(br, port, !ipv6_addr_any(&ip6h->saddr));
 | 
						|
-
 | 
						|
 	/* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */
 | 
						|
 	if (!(ipv6_addr_type(&ip6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
 | 
						|
 		err = -EINVAL;
 | 
						|
@@ -1203,6 +1265,9 @@ static int br_ip6_multicast_query(struct
 | 
						|
 		max_delay = max(msecs_to_jiffies(MLDV2_MRC(ntohs(mld2q->mld2q_mrc))), 1UL);
 | 
						|
 	}
 | 
						|
 
 | 
						|
+	br_multicast_query_received(br, port, &br->ip6_querier,
 | 
						|
+				    !ipv6_addr_any(&ip6h->saddr), max_delay);
 | 
						|
+
 | 
						|
 	if (!group)
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
@@ -1235,7 +1300,9 @@ out:
 | 
						|
 
 | 
						|
 static void br_multicast_leave_group(struct net_bridge *br,
 | 
						|
 				     struct net_bridge_port *port,
 | 
						|
-				     struct br_ip *group)
 | 
						|
+				     struct br_ip *group,
 | 
						|
+				     struct bridge_mcast_querier *querier,
 | 
						|
+				     struct bridge_mcast_query *query)
 | 
						|
 {
 | 
						|
 	struct net_bridge_mdb_htable *mdb;
 | 
						|
 	struct net_bridge_mdb_entry *mp;
 | 
						|
@@ -1246,7 +1313,7 @@ static void br_multicast_leave_group(str
 | 
						|
 	spin_lock(&br->multicast_lock);
 | 
						|
 	if (!netif_running(br->dev) ||
 | 
						|
 	    (port && port->state == BR_STATE_DISABLED) ||
 | 
						|
-	    timer_pending(&br->multicast_querier_timer))
 | 
						|
+	    timer_pending(&querier->timer))
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
 	mdb = mlock_dereference(br->mdb, br);
 | 
						|
@@ -1254,6 +1321,31 @@ static void br_multicast_leave_group(str
 | 
						|
 	if (!mp)
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
+	if (br->multicast_querier) {
 | 
						|
+		__br_multicast_send_query(br, port, &mp->addr);
 | 
						|
+
 | 
						|
+		time = jiffies + br->multicast_last_member_count *
 | 
						|
+				 br->multicast_last_member_interval;
 | 
						|
+
 | 
						|
+		mod_timer(&query->timer, time);
 | 
						|
+
 | 
						|
+		for (p = mlock_dereference(mp->ports, br);
 | 
						|
+		     p != NULL;
 | 
						|
+		     p = mlock_dereference(p->next, br)) {
 | 
						|
+			if (p->port != port)
 | 
						|
+				continue;
 | 
						|
+
 | 
						|
+			if (!hlist_unhashed(&p->mglist) &&
 | 
						|
+			    (timer_pending(&p->timer) ?
 | 
						|
+			     time_after(p->timer.expires, time) :
 | 
						|
+			     try_to_del_timer_sync(&p->timer) >= 0)) {
 | 
						|
+				mod_timer(&p->timer, time);
 | 
						|
+			}
 | 
						|
+
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
 	if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) {
 | 
						|
 		struct net_bridge_port_group __rcu **pp;
 | 
						|
 
 | 
						|
@@ -1306,7 +1398,6 @@ static void br_multicast_leave_group(str
 | 
						|
 
 | 
						|
 		break;
 | 
						|
 	}
 | 
						|
-
 | 
						|
 out:
 | 
						|
 	spin_unlock(&br->multicast_lock);
 | 
						|
 }
 | 
						|
@@ -1317,6 +1408,8 @@ static void br_ip4_multicast_leave_group
 | 
						|
 					 __u16 vid)
 | 
						|
 {
 | 
						|
 	struct br_ip br_group;
 | 
						|
+	struct bridge_mcast_query *query = port ? &port->ip4_query :
 | 
						|
+						  &br->ip4_query;
 | 
						|
 
 | 
						|
 	if (ipv4_is_local_multicast(group))
 | 
						|
 		return;
 | 
						|
@@ -1325,7 +1418,7 @@ static void br_ip4_multicast_leave_group
 | 
						|
 	br_group.proto = htons(ETH_P_IP);
 | 
						|
 	br_group.vid = vid;
 | 
						|
 
 | 
						|
-	br_multicast_leave_group(br, port, &br_group);
 | 
						|
+	br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query);
 | 
						|
 }
 | 
						|
 
 | 
						|
 #if IS_ENABLED(CONFIG_IPV6)
 | 
						|
@@ -1335,15 +1428,18 @@ static void br_ip6_multicast_leave_group
 | 
						|
 					 __u16 vid)
 | 
						|
 {
 | 
						|
 	struct br_ip br_group;
 | 
						|
+	struct bridge_mcast_query *query = port ? &port->ip6_query :
 | 
						|
+						  &br->ip6_query;
 | 
						|
 
 | 
						|
-	if (!ipv6_is_transient_multicast(group))
 | 
						|
+
 | 
						|
+	if (ipv6_addr_is_ll_all_nodes(group))
 | 
						|
 		return;
 | 
						|
 
 | 
						|
 	br_group.u.ip6 = *group;
 | 
						|
 	br_group.proto = htons(ETH_P_IPV6);
 | 
						|
 	br_group.vid = vid;
 | 
						|
 
 | 
						|
-	br_multicast_leave_group(br, port, &br_group);
 | 
						|
+	br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query);
 | 
						|
 }
 | 
						|
 #endif
 | 
						|
 
 | 
						|
@@ -1473,8 +1569,14 @@ static int br_multicast_ipv6_rcv(struct 
 | 
						|
 	 *  - MLD has always Router Alert hop-by-hop option
 | 
						|
 	 *  - But we do not support jumbrograms.
 | 
						|
 	 */
 | 
						|
-	if (ip6h->version != 6 ||
 | 
						|
-	    ip6h->nexthdr != IPPROTO_HOPOPTS ||
 | 
						|
+	if (ip6h->version != 6)
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	/* Prevent flooding this packet if there is no listener present */
 | 
						|
+	if (!ipv6_addr_is_ll_all_nodes(&ip6h->daddr))
 | 
						|
+		BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
 | 
						|
+
 | 
						|
+	if (ip6h->nexthdr != IPPROTO_HOPOPTS ||
 | 
						|
 	    ip6h->payload_len == 0)
 | 
						|
 		return 0;
 | 
						|
 
 | 
						|
@@ -1605,19 +1707,32 @@ int br_multicast_rcv(struct net_bridge *
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
-static void br_multicast_query_expired(unsigned long data)
 | 
						|
+static void br_multicast_query_expired(struct net_bridge *br,
 | 
						|
+				       struct bridge_mcast_query *query)
 | 
						|
+{
 | 
						|
+	spin_lock(&br->multicast_lock);
 | 
						|
+	if (query->startup_sent < br->multicast_startup_query_count)
 | 
						|
+		query->startup_sent++;
 | 
						|
+
 | 
						|
+	br_multicast_send_query(br, NULL, query);
 | 
						|
+	spin_unlock(&br->multicast_lock);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void br_ip4_multicast_query_expired(unsigned long data)
 | 
						|
 {
 | 
						|
 	struct net_bridge *br = (void *)data;
 | 
						|
 
 | 
						|
-	spin_lock(&br->multicast_lock);
 | 
						|
-	if (br->multicast_startup_queries_sent <
 | 
						|
-	    br->multicast_startup_query_count)
 | 
						|
-		br->multicast_startup_queries_sent++;
 | 
						|
+	br_multicast_query_expired(br, &br->ip4_query);
 | 
						|
+}
 | 
						|
 
 | 
						|
-	br_multicast_send_query(br, NULL, br->multicast_startup_queries_sent);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+static void br_ip6_multicast_query_expired(unsigned long data)
 | 
						|
+{
 | 
						|
+	struct net_bridge *br = (void *)data;
 | 
						|
 
 | 
						|
-	spin_unlock(&br->multicast_lock);
 | 
						|
+	br_multicast_query_expired(br, &br->ip6_query);
 | 
						|
 }
 | 
						|
+#endif
 | 
						|
 
 | 
						|
 void br_multicast_init(struct net_bridge *br)
 | 
						|
 {
 | 
						|
@@ -1626,6 +1741,7 @@ void br_multicast_init(struct net_bridge
 | 
						|
 
 | 
						|
 	br->multicast_router = 1;
 | 
						|
 	br->multicast_querier = 0;
 | 
						|
+	br->multicast_query_use_ifaddr = 0;
 | 
						|
 	br->multicast_last_member_count = 2;
 | 
						|
 	br->multicast_startup_query_count = 2;
 | 
						|
 
 | 
						|
@@ -1636,23 +1752,43 @@ void br_multicast_init(struct net_bridge
 | 
						|
 	br->multicast_querier_interval = 255 * HZ;
 | 
						|
 	br->multicast_membership_interval = 260 * HZ;
 | 
						|
 
 | 
						|
+	br->ip4_querier.delay_time = 0;
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	br->ip6_querier.delay_time = 0;
 | 
						|
+#endif
 | 
						|
+
 | 
						|
 	spin_lock_init(&br->multicast_lock);
 | 
						|
 	setup_timer(&br->multicast_router_timer,
 | 
						|
 		    br_multicast_local_router_expired, 0);
 | 
						|
-	setup_timer(&br->multicast_querier_timer,
 | 
						|
-		    br_multicast_querier_expired, (unsigned long)br);
 | 
						|
-	setup_timer(&br->multicast_query_timer, br_multicast_query_expired,
 | 
						|
+	setup_timer(&br->ip4_querier.timer, br_ip4_multicast_querier_expired,
 | 
						|
 		    (unsigned long)br);
 | 
						|
+	setup_timer(&br->ip4_query.timer, br_ip4_multicast_query_expired,
 | 
						|
+		    (unsigned long)br);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	setup_timer(&br->ip6_querier.timer, br_ip6_multicast_querier_expired,
 | 
						|
+		    (unsigned long)br);
 | 
						|
+	setup_timer(&br->ip6_query.timer, br_ip6_multicast_query_expired,
 | 
						|
+		    (unsigned long)br);
 | 
						|
+#endif
 | 
						|
 }
 | 
						|
 
 | 
						|
-void br_multicast_open(struct net_bridge *br)
 | 
						|
+static void __br_multicast_open(struct net_bridge *br,
 | 
						|
+				struct bridge_mcast_query *query)
 | 
						|
 {
 | 
						|
-	br->multicast_startup_queries_sent = 0;
 | 
						|
+	query->startup_sent = 0;
 | 
						|
 
 | 
						|
 	if (br->multicast_disabled)
 | 
						|
 		return;
 | 
						|
 
 | 
						|
-	mod_timer(&br->multicast_query_timer, jiffies);
 | 
						|
+	mod_timer(&query->timer, jiffies);
 | 
						|
+}
 | 
						|
+
 | 
						|
+void br_multicast_open(struct net_bridge *br)
 | 
						|
+{
 | 
						|
+	__br_multicast_open(br, &br->ip4_query);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	__br_multicast_open(br, &br->ip6_query);
 | 
						|
+#endif
 | 
						|
 }
 | 
						|
 
 | 
						|
 void br_multicast_stop(struct net_bridge *br)
 | 
						|
@@ -1664,8 +1800,12 @@ void br_multicast_stop(struct net_bridge
 | 
						|
 	int i;
 | 
						|
 
 | 
						|
 	del_timer_sync(&br->multicast_router_timer);
 | 
						|
-	del_timer_sync(&br->multicast_querier_timer);
 | 
						|
-	del_timer_sync(&br->multicast_query_timer);
 | 
						|
+	del_timer_sync(&br->ip4_querier.timer);
 | 
						|
+	del_timer_sync(&br->ip4_query.timer);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	del_timer_sync(&br->ip6_querier.timer);
 | 
						|
+	del_timer_sync(&br->ip6_query.timer);
 | 
						|
+#endif
 | 
						|
 
 | 
						|
 	spin_lock_bh(&br->multicast_lock);
 | 
						|
 	mdb = mlock_dereference(br->mdb, br);
 | 
						|
@@ -1767,18 +1907,24 @@ unlock:
 | 
						|
 	return err;
 | 
						|
 }
 | 
						|
 
 | 
						|
-static void br_multicast_start_querier(struct net_bridge *br)
 | 
						|
+static void br_multicast_start_querier(struct net_bridge *br,
 | 
						|
+				       struct bridge_mcast_query *query)
 | 
						|
 {
 | 
						|
 	struct net_bridge_port *port;
 | 
						|
 
 | 
						|
-	br_multicast_open(br);
 | 
						|
+	__br_multicast_open(br, query);
 | 
						|
 
 | 
						|
 	list_for_each_entry(port, &br->port_list, list) {
 | 
						|
 		if (port->state == BR_STATE_DISABLED ||
 | 
						|
 		    port->state == BR_STATE_BLOCKING)
 | 
						|
 			continue;
 | 
						|
 
 | 
						|
-		__br_multicast_enable_port(port);
 | 
						|
+		if (query == &br->ip4_query)
 | 
						|
+			br_multicast_enable(&port->ip4_query);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+		else
 | 
						|
+			br_multicast_enable(&port->ip6_query);
 | 
						|
+#endif
 | 
						|
 	}
 | 
						|
 }
 | 
						|
 
 | 
						|
@@ -1813,7 +1959,10 @@ rollback:
 | 
						|
 			goto rollback;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	br_multicast_start_querier(br);
 | 
						|
+	br_multicast_start_querier(br, &br->ip4_query);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	br_multicast_start_querier(br, &br->ip6_query);
 | 
						|
+#endif
 | 
						|
 
 | 
						|
 unlock:
 | 
						|
 	spin_unlock_bh(&br->multicast_lock);
 | 
						|
@@ -1823,6 +1972,8 @@ unlock:
 | 
						|
 
 | 
						|
 int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
 | 
						|
 {
 | 
						|
+	unsigned long max_delay;
 | 
						|
+
 | 
						|
 	val = !!val;
 | 
						|
 
 | 
						|
 	spin_lock_bh(&br->multicast_lock);
 | 
						|
@@ -1830,8 +1981,22 @@ int br_multicast_set_querier(struct net_
 | 
						|
 		goto unlock;
 | 
						|
 
 | 
						|
 	br->multicast_querier = val;
 | 
						|
-	if (val)
 | 
						|
-		br_multicast_start_querier(br);
 | 
						|
+	if (!val)
 | 
						|
+		goto unlock;
 | 
						|
+
 | 
						|
+	max_delay = br->multicast_query_response_interval;
 | 
						|
+
 | 
						|
+	if (!timer_pending(&br->ip4_querier.timer))
 | 
						|
+		br->ip4_querier.delay_time = jiffies + max_delay;
 | 
						|
+
 | 
						|
+	br_multicast_start_querier(br, &br->ip4_query);
 | 
						|
+
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	if (!timer_pending(&br->ip6_querier.timer))
 | 
						|
+		br->ip6_querier.delay_time = jiffies + max_delay;
 | 
						|
+
 | 
						|
+	br_multicast_start_querier(br, &br->ip6_query);
 | 
						|
+#endif
 | 
						|
 
 | 
						|
 unlock:
 | 
						|
 	spin_unlock_bh(&br->multicast_lock);
 | 
						|
--- a/net/bridge/br_private.h
 | 
						|
+++ b/net/bridge/br_private.h
 | 
						|
@@ -66,6 +66,20 @@ struct br_ip
 | 
						|
 	__u16		vid;
 | 
						|
 };
 | 
						|
 
 | 
						|
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 | 
						|
+/* our own querier */
 | 
						|
+struct bridge_mcast_query {
 | 
						|
+	struct timer_list	timer;
 | 
						|
+	u32			startup_sent;
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* other querier */
 | 
						|
+struct bridge_mcast_querier {
 | 
						|
+	struct timer_list		timer;
 | 
						|
+	unsigned long			delay_time;
 | 
						|
+};
 | 
						|
+#endif
 | 
						|
+
 | 
						|
 struct net_port_vlans {
 | 
						|
 	u16				port_idx;
 | 
						|
 	u16				pvid;
 | 
						|
@@ -159,10 +173,12 @@ struct net_bridge_port
 | 
						|
 #define BR_ADMIN_COST		0x00000010
 | 
						|
 
 | 
						|
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 | 
						|
-	u32				multicast_startup_queries_sent;
 | 
						|
+	struct bridge_mcast_query	ip4_query;
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	struct bridge_mcast_query	ip6_query;
 | 
						|
+#endif /* IS_ENABLED(CONFIG_IPV6) */
 | 
						|
 	unsigned char			multicast_router;
 | 
						|
 	struct timer_list		multicast_router_timer;
 | 
						|
-	struct timer_list		multicast_query_timer;
 | 
						|
 	struct hlist_head		mglist;
 | 
						|
 	struct hlist_node		rlist;
 | 
						|
 #endif
 | 
						|
@@ -246,12 +262,12 @@ struct net_bridge
 | 
						|
 
 | 
						|
 	u8				multicast_disabled:1;
 | 
						|
 	u8				multicast_querier:1;
 | 
						|
+	u8				multicast_query_use_ifaddr:1;
 | 
						|
 
 | 
						|
 	u32				hash_elasticity;
 | 
						|
 	u32				hash_max;
 | 
						|
 
 | 
						|
 	u32				multicast_last_member_count;
 | 
						|
-	u32				multicast_startup_queries_sent;
 | 
						|
 	u32				multicast_startup_query_count;
 | 
						|
 
 | 
						|
 	unsigned long			multicast_last_member_interval;
 | 
						|
@@ -266,8 +282,12 @@ struct net_bridge
 | 
						|
 	struct hlist_head		router_list;
 | 
						|
 
 | 
						|
 	struct timer_list		multicast_router_timer;
 | 
						|
-	struct timer_list		multicast_querier_timer;
 | 
						|
-	struct timer_list		multicast_query_timer;
 | 
						|
+	struct bridge_mcast_querier	ip4_querier;
 | 
						|
+	struct bridge_mcast_query	ip4_query;
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	struct bridge_mcast_querier	ip6_querier;
 | 
						|
+	struct bridge_mcast_query	ip6_query;
 | 
						|
+#endif /* IS_ENABLED(CONFIG_IPV6) */
 | 
						|
 #endif
 | 
						|
 
 | 
						|
 	struct timer_list		hello_timer;
 | 
						|
@@ -477,22 +497,35 @@ extern void br_mdb_notify(struct net_dev
 | 
						|
 #define mlock_dereference(X, br) \
 | 
						|
 	rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
 | 
						|
 
 | 
						|
-#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
-#include <net/addrconf.h>
 | 
						|
-static inline int ipv6_is_transient_multicast(const struct in6_addr *addr)
 | 
						|
-{
 | 
						|
-	if (ipv6_addr_is_multicast(addr) && IPV6_ADDR_MC_FLAG_TRANSIENT(addr))
 | 
						|
-		return 1;
 | 
						|
-	return 0;
 | 
						|
-}
 | 
						|
-#endif
 | 
						|
-
 | 
						|
 static inline bool br_multicast_is_router(struct net_bridge *br)
 | 
						|
 {
 | 
						|
 	return br->multicast_router == 2 ||
 | 
						|
 	       (br->multicast_router == 1 &&
 | 
						|
 		timer_pending(&br->multicast_router_timer));
 | 
						|
 }
 | 
						|
+
 | 
						|
+static inline bool
 | 
						|
+__br_multicast_querier_exists(struct net_bridge *br,
 | 
						|
+			      struct bridge_mcast_querier *querier)
 | 
						|
+{
 | 
						|
+	return time_is_before_jiffies(querier->delay_time) &&
 | 
						|
+	       (br->multicast_querier || timer_pending(&querier->timer));
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline bool br_multicast_querier_exists(struct net_bridge *br,
 | 
						|
+					       struct ethhdr *eth)
 | 
						|
+{
 | 
						|
+	switch (eth->h_proto) {
 | 
						|
+	case (htons(ETH_P_IP)):
 | 
						|
+		return __br_multicast_querier_exists(br, &br->ip4_querier);
 | 
						|
+#if IS_ENABLED(CONFIG_IPV6)
 | 
						|
+	case (htons(ETH_P_IPV6)):
 | 
						|
+		return __br_multicast_querier_exists(br, &br->ip6_querier);
 | 
						|
+#endif
 | 
						|
+	default:
 | 
						|
+		return false;
 | 
						|
+	}
 | 
						|
+}
 | 
						|
 #else
 | 
						|
 static inline int br_multicast_rcv(struct net_bridge *br,
 | 
						|
 				   struct net_bridge_port *port,
 | 
						|
@@ -549,6 +582,11 @@ static inline bool br_multicast_is_route
 | 
						|
 {
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
+static inline bool br_multicast_querier_exists(struct net_bridge *br,
 | 
						|
+					       struct ethhdr *eth)
 | 
						|
+{
 | 
						|
+	return false;
 | 
						|
+}
 | 
						|
 static inline void br_mdb_init(void)
 | 
						|
 {
 | 
						|
 }
 | 
						|
--- a/net/bridge/br_sysfs_br.c
 | 
						|
+++ b/net/bridge/br_sysfs_br.c
 | 
						|
@@ -375,6 +375,31 @@ static ssize_t store_multicast_snooping(
 | 
						|
 static DEVICE_ATTR(multicast_snooping, S_IRUGO | S_IWUSR,
 | 
						|
 		   show_multicast_snooping, store_multicast_snooping);
 | 
						|
 
 | 
						|
+static ssize_t show_multicast_query_use_ifaddr(struct device *d,
 | 
						|
+				      struct device_attribute *attr,
 | 
						|
+				      char *buf)
 | 
						|
+{
 | 
						|
+	struct net_bridge *br = to_bridge(d);
 | 
						|
+	return sprintf(buf, "%d\n", br->multicast_query_use_ifaddr);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int set_query_use_ifaddr(struct net_bridge *br, unsigned long val)
 | 
						|
+{
 | 
						|
+	br->multicast_query_use_ifaddr = !!val;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static ssize_t
 | 
						|
+store_multicast_query_use_ifaddr(struct device *d,
 | 
						|
+				 struct device_attribute *attr,
 | 
						|
+				 const char *buf, size_t len)
 | 
						|
+{
 | 
						|
+	return store_bridge_parm(d, buf, len, set_query_use_ifaddr);
 | 
						|
+}
 | 
						|
+static DEVICE_ATTR(multicast_query_use_ifaddr, S_IRUGO | S_IWUSR,
 | 
						|
+		   show_multicast_query_use_ifaddr,
 | 
						|
+		   store_multicast_query_use_ifaddr);
 | 
						|
+
 | 
						|
 static ssize_t show_multicast_querier(struct device *d,
 | 
						|
 				      struct device_attribute *attr,
 | 
						|
 				      char *buf)
 | 
						|
@@ -734,6 +759,7 @@ static struct attribute *bridge_attrs[] 
 | 
						|
 	&dev_attr_multicast_router.attr,
 | 
						|
 	&dev_attr_multicast_snooping.attr,
 | 
						|
 	&dev_attr_multicast_querier.attr,
 | 
						|
+	&dev_attr_multicast_query_use_ifaddr.attr,
 | 
						|
 	&dev_attr_hash_elasticity.attr,
 | 
						|
 	&dev_attr_hash_max.attr,
 | 
						|
 	&dev_attr_multicast_last_member_count.attr,
 | 
						|
--- a/net/bridge/br_mdb.c
 | 
						|
+++ b/net/bridge/br_mdb.c
 | 
						|
@@ -9,6 +9,7 @@
 | 
						|
 #include <net/netlink.h>
 | 
						|
 #if IS_ENABLED(CONFIG_IPV6)
 | 
						|
 #include <net/ipv6.h>
 | 
						|
+#include <net/addrconf.h>
 | 
						|
 #endif
 | 
						|
 
 | 
						|
 #include "br_private.h"
 | 
						|
@@ -253,7 +254,7 @@ static bool is_valid_mdb_entry(struct br
 | 
						|
 			return false;
 | 
						|
 #if IS_ENABLED(CONFIG_IPV6)
 | 
						|
 	} else if (entry->addr.proto == htons(ETH_P_IPV6)) {
 | 
						|
-		if (!ipv6_is_transient_multicast(&entry->addr.u.ip6))
 | 
						|
+		if (ipv6_addr_is_ll_all_nodes(&entry->addr.u.ip6))
 | 
						|
 			return false;
 | 
						|
 #endif
 | 
						|
 	} else
 | 
						|
@@ -414,16 +415,20 @@ static int __br_mdb_del(struct net_bridg
 | 
						|
 	if (!netif_running(br->dev) || br->multicast_disabled)
 | 
						|
 		return -EINVAL;
 | 
						|
 
 | 
						|
-	if (timer_pending(&br->multicast_querier_timer))
 | 
						|
-		return -EBUSY;
 | 
						|
-
 | 
						|
 	ip.proto = entry->addr.proto;
 | 
						|
-	if (ip.proto == htons(ETH_P_IP))
 | 
						|
+	if (ip.proto == htons(ETH_P_IP)) {
 | 
						|
+		if (timer_pending(&br->ip4_querier.timer))
 | 
						|
+			return -EBUSY;
 | 
						|
+
 | 
						|
 		ip.u.ip4 = entry->addr.u.ip4;
 | 
						|
 #if IS_ENABLED(CONFIG_IPV6)
 | 
						|
-	else
 | 
						|
+	} else {
 | 
						|
+		if (timer_pending(&br->ip6_querier.timer))
 | 
						|
+			return -EBUSY;
 | 
						|
+
 | 
						|
 		ip.u.ip6 = entry->addr.u.ip6;
 | 
						|
 #endif
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	spin_lock_bh(&br->multicast_lock);
 | 
						|
 	mdb = mlock_dereference(br->mdb, br);
 |