|
@@ -0,0 +1,848 @@
|
|
|
|
|
+--- 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);
|