diff options
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.patch | 1098 |
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 48d6eda..0000000 --- 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); |