aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/patches-3.10/070-net_bridge_backports.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/patches-3.10/070-net_bridge_backports.patch')
-rw-r--r--target/linux/generic/patches-3.10/070-net_bridge_backports.patch1098
1 files changed, 0 insertions, 1098 deletions
diff --git a/target/linux/generic/patches-3.10/070-net_bridge_backports.patch b/target/linux/generic/patches-3.10/070-net_bridge_backports.patch
deleted file mode 100644
index 48d6eda024..0000000000
--- a/target/linux/generic/patches-3.10/070-net_bridge_backports.patch
+++ /dev/null
@@ -1,1098 +0,0 @@
-commit f0b4eeced518c632210ef2aea44fc92cc9e86cce
-Author: Linus Lüssing <linus.luessing@web.de>
-Date: Mon Nov 17 12:20:28 2014 +0100
-
- bridge: fix netfilter/NF_BR_LOCAL_OUT for own, locally generated queries
-
- Ebtables on the OUTPUT chain (NF_BR_LOCAL_OUT) would not work as expected
- for both locally generated IGMP and MLD queries. The IP header specific
- filter options are off by 14 Bytes for netfilter (actual output on
- interfaces is fine).
-
- NF_HOOK() expects the skb->data to point to the IP header, not the
- ethernet one (while dev_queue_xmit() does not). Luckily there is an
- br_dev_queue_push_xmit() helper function already - let's just use that.
-
- Introduced by eb1d16414339a6e113d89e2cca2556005d7ce919
- ("bridge: Add core IGMP snooping support")
-
- Ebtables example:
-
- $ ebtables -I OUTPUT -p IPv6 -o eth1 --logical-out br0 \
- --log --log-level 6 --log-ip6 --log-prefix="~EBT: " -j DROP
-
- before (broken):
-
- ~EBT: IN= OUT=eth1 MAC source = 02:04:64:a4:39:c2 \
- MAC dest = 33:33:00:00:00:01 proto = 0x86dd IPv6 \
- SRC=64a4:39c2:86dd:6000:0000:0020:0001:fe80 IPv6 \
- DST=0000:0000:0000:0004:64ff:fea4:39c2:ff02, \
- IPv6 priority=0x3, Next Header=2
-
- after (working):
-
- ~EBT: IN= OUT=eth1 MAC source = 02:04:64:a4:39:c2 \
- MAC dest = 33:33:00:00:00:01 proto = 0x86dd IPv6 \
- SRC=fe80:0000:0000:0000:0004:64ff:fea4:39c2 IPv6 \
- DST=ff02:0000:0000:0000:0000:0000:0000:0001, \
- IPv6 priority=0x0, Next Header=0
-
- Signed-off-by: Linus Lüssing <linus.luessing@web.de>
- Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
- Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-
-commit 20a599bec95a52fa72432b2376a2ce47c5bb68fb
-Author: Linus Lüssing <linus.luessing@web.de>
-Date: Mon Mar 10 22:25:25 2014 +0100
-
- bridge: multicast: enable snooping on general queries only
-
- Without this check someone could easily create a denial of service
- by injecting multicast-specific queries to enable the bridge
- snooping part if no real querier issuing periodic general queries
- is present on the link which would result in the bridge wrongly
- shutting down ports for multicast traffic as the bridge did not learn
- about these listeners.
-
- With this patch the snooping code is enabled upon receiving valid,
- general queries only.
-
- Signed-off-by: Linus Lüssing <linus.luessing@web.de>
- Signed-off-by: David S. Miller <davem@davemloft.net>
-
-commit 9ed973cc40c588abeaa58aea0683ea665132d11d
-Author: Linus Lüssing <linus.luessing@web.de>
-Date: Mon Mar 10 22:25:24 2014 +0100
-
- bridge: multicast: add sanity check for general query destination
-
- General IGMP and MLD queries are supposed to have the multicast
- link-local all-nodes address as their destination according to RFC2236
- section 9, RFC3376 section 4.1.12/9.1, RFC2710 section 8 and RFC3810
- section 5.1.15.
-
- Without this check, such malformed IGMP/MLD queries can result in a
- denial of service: The queries are ignored by most IGMP/MLD listeners
- therefore they will not respond with an IGMP/MLD report. However,
- without this patch these malformed MLD queries would enable the
- snooping part in the bridge code, potentially shutting down the
- according ports towards these hosts for multicast traffic as the
- bridge did not learn about these listeners.
-
- Reported-by: Jan Stancek <jstancek@redhat.com>
- Signed-off-by: Linus Lüssing <linus.luessing@web.de>
- Signed-off-by: David S. Miller <davem@davemloft.net>
-
-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)
-@@ -781,46 +800,53 @@ static void __br_multicast_send_query(st
- return;
-
- if (port) {
-- __skb_push(skb, sizeof(struct ethhdr));
- skb->dev = port->dev;
- NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
-- dev_queue_xmit);
-+ br_dev_queue_push_xmit);
- } else
- netif_rx(skb);
- }
-
- 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 +854,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 +898,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 +915,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 +937,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 +1065,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 +1126,14 @@ timer:
-
- static void br_multicast_query_received(struct net_bridge *br,
- struct net_bridge_port *port,
-- int saddr)
--{
-- if (saddr)
-- mod_timer(&br->multicast_querier_timer,
-- jiffies + br->multicast_querier_interval);
-- else if (timer_pending(&br->multicast_querier_timer))
-+ struct bridge_mcast_querier *querier,
-+ int saddr,
-+ bool is_general_query,
-+ unsigned long max_delay)
-+{
-+ if (saddr && is_general_query)
-+ 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,17 @@ static int br_ip4_multicast_query(struct
- IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
- }
-
-+ /* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer
-+ * all-systems destination addresses (224.0.0.1) for general queries
-+ */
-+ if (!group && iph->daddr != htonl(INADDR_ALLHOSTS_GROUP)) {
-+ err = -EINVAL;
-+ goto out;
-+ }
-+
-+ br_multicast_query_received(br, port, &br->ip4_querier, !!iph->saddr,
-+ !group, max_delay);
-+
- if (!group)
- goto out;
-
-@@ -1166,6 +1238,7 @@ static int br_ip6_multicast_query(struct
- unsigned long max_delay;
- unsigned long now = jiffies;
- const struct in6_addr *group = NULL;
-+ bool is_general_query;
- int err = 0;
- u16 vid = 0;
-
-@@ -1174,8 +1247,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 +1274,20 @@ static int br_ip6_multicast_query(struct
- max_delay = max(msecs_to_jiffies(MLDV2_MRC(ntohs(mld2q->mld2q_mrc))), 1UL);
- }
-
-+ is_general_query = group && ipv6_addr_any(group);
-+
-+ /* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer
-+ * all-nodes destination address (ff02::1) for general queries
-+ */
-+ if (is_general_query && !ipv6_addr_is_ll_all_nodes(&ip6h->daddr)) {
-+ err = -EINVAL;
-+ goto out;
-+ }
-+
-+ br_multicast_query_received(br, port, &br->ip6_querier,
-+ !ipv6_addr_any(&ip6h->saddr),
-+ is_general_query, max_delay);
-+
- if (!group)
- goto out;
-
-@@ -1235,7 +1320,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 +1333,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 +1341,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 +1418,6 @@ static void br_multicast_leave_group(str
-
- break;
- }
--
- out:
- spin_unlock(&br->multicast_lock);
- }
-@@ -1317,6 +1428,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 +1438,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 +1448,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 +1589,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 +1727,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 +1761,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 +1772,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 +1820,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 +1927,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 +1979,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 +1992,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 +2001,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);