kernel: backport upstream bridge multicast snooping fixes

Signed-off-by: Felix Fietkau <nbd@openwrt.org>

SVN-Revision: 41817
lede-17.01
Felix Fietkau 2014-07-24 09:12:59 +00:00
parent 22419ae4cb
commit dd7650f67c
7 changed files with 899 additions and 49 deletions

View File

@ -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);

View File

@ -15,7 +15,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org>
--- a/net/bridge/br_input.c --- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c +++ b/net/bridge/br_input.c
@@ -139,10 +139,14 @@ drop: @@ -140,10 +140,14 @@ drop:
static int br_handle_local_finish(struct sk_buff *skb) static int br_handle_local_finish(struct sk_buff *skb)
{ {
struct net_bridge_port *p = br_port_get_rcu(skb->dev); struct net_bridge_port *p = br_port_get_rcu(skb->dev);
@ -33,7 +33,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org>
return 0; /* process further */ return 0; /* process further */
} }
@@ -212,6 +216,18 @@ rx_handler_result_t br_handle_frame(stru @@ -213,6 +217,18 @@ rx_handler_result_t br_handle_frame(stru
forward: forward:
switch (p->state) { switch (p->state) {

View File

@ -1,13 +1,13 @@
--- a/net/bridge/br_private.h --- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h +++ b/net/bridge/br_private.h
@@ -157,6 +157,7 @@ struct net_bridge_port @@ -171,6 +171,7 @@ struct net_bridge_port
#define BR_ROOT_BLOCK 0x00000004 #define BR_ROOT_BLOCK 0x00000004
#define BR_MULTICAST_FAST_LEAVE 0x00000008 #define BR_MULTICAST_FAST_LEAVE 0x00000008
#define BR_ADMIN_COST 0x00000010 #define BR_ADMIN_COST 0x00000010
+#define BR_ISOLATE_MODE 0x00000020 +#define BR_ISOLATE_MODE 0x00000020
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
u32 multicast_startup_queries_sent; struct bridge_mcast_query ip4_query;
--- a/net/bridge/br_sysfs_if.c --- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c
@@ -159,6 +159,22 @@ BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPI @@ -159,6 +159,22 @@ BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPI
@ -43,7 +43,7 @@
--- a/net/bridge/br_input.c --- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c +++ b/net/bridge/br_input.c
@@ -114,8 +114,8 @@ int br_handle_frame_finish(struct sk_buf @@ -115,8 +115,8 @@ int br_handle_frame_finish(struct sk_buf
skb2 = skb; skb2 = skb;
br->dev->stats.multicast++; br->dev->stats.multicast++;

View File

@ -38,7 +38,7 @@
netif_receive_skb); netif_receive_skb);
} }
@@ -209,7 +209,7 @@ rx_handler_result_t br_handle_frame(stru @@ -210,7 +210,7 @@ rx_handler_result_t br_handle_frame(stru
} }
/* Deliver packet to local host only */ /* Deliver packet to local host only */
@ -47,7 +47,7 @@
NULL, br_handle_local_finish)) { NULL, br_handle_local_finish)) {
return RX_HANDLER_CONSUMED; /* consumed by filter */ return RX_HANDLER_CONSUMED; /* consumed by filter */
} else { } else {
@@ -224,7 +224,7 @@ forward: @@ -225,7 +225,7 @@ forward:
if (ether_addr_equal(p->br->dev->dev_addr, dest)) if (ether_addr_equal(p->br->dev->dev_addr, dest))
skb->pkt_type = PACKET_HOST; skb->pkt_type = PACKET_HOST;
@ -56,7 +56,7 @@
br_handle_local_finish)) br_handle_local_finish))
break; break;
@@ -246,7 +246,7 @@ forward: @@ -247,7 +247,7 @@ forward:
if (ether_addr_equal(p->br->dev->dev_addr, dest)) if (ether_addr_equal(p->br->dev->dev_addr, dest))
skb->pkt_type = PACKET_HOST; skb->pkt_type = PACKET_HOST;
@ -67,7 +67,7 @@
default: default:
--- a/net/bridge/br_multicast.c --- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c
@@ -783,7 +783,7 @@ static void __br_multicast_send_query(st @@ -802,7 +802,7 @@ static void __br_multicast_send_query(st
if (port) { if (port) {
__skb_push(skb, sizeof(struct ethhdr)); __skb_push(skb, sizeof(struct ethhdr));
skb->dev = port->dev; skb->dev = port->dev;
@ -96,7 +96,7 @@
if (vlan_tx_tag_present(skb)) if (vlan_tx_tag_present(skb))
--- a/net/bridge/br_private.h --- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h +++ b/net/bridge/br_private.h
@@ -696,15 +696,29 @@ static inline u16 br_get_pvid(const stru @@ -734,15 +734,29 @@ static inline u16 br_get_pvid(const stru
/* br_netfilter.c */ /* br_netfilter.c */
#ifdef CONFIG_BRIDGE_NETFILTER #ifdef CONFIG_BRIDGE_NETFILTER

View File

@ -1,6 +1,6 @@
--- a/net/bridge/br_multicast.c --- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c
@@ -630,7 +630,8 @@ struct net_bridge_port_group *br_multica @@ -634,7 +634,8 @@ struct net_bridge_port_group *br_multica
struct net_bridge_port *port, struct net_bridge_port *port,
struct br_ip *group, struct br_ip *group,
struct net_bridge_port_group __rcu *next, struct net_bridge_port_group __rcu *next,
@ -10,7 +10,7 @@
{ {
struct net_bridge_port_group *p; struct net_bridge_port_group *p;
@@ -645,12 +646,33 @@ struct net_bridge_port_group *br_multica @@ -649,12 +650,33 @@ struct net_bridge_port_group *br_multica
hlist_add_head(&p->mglist, &port->mglist); hlist_add_head(&p->mglist, &port->mglist);
setup_timer(&p->timer, br_multicast_port_group_expired, setup_timer(&p->timer, br_multicast_port_group_expired,
(unsigned long)p); (unsigned long)p);
@ -45,7 +45,7 @@
{ {
struct net_bridge_mdb_entry *mp; struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p; struct net_bridge_port_group *p;
@@ -677,13 +699,13 @@ static int br_multicast_add_group(struct @@ -681,13 +703,13 @@ static int br_multicast_add_group(struct
for (pp = &mp->ports; for (pp = &mp->ports;
(p = mlock_dereference(*pp, br)) != NULL; (p = mlock_dereference(*pp, br)) != NULL;
pp = &p->next) { pp = &p->next) {
@ -61,7 +61,7 @@
if (unlikely(!p)) if (unlikely(!p))
goto err; goto err;
rcu_assign_pointer(*pp, p); rcu_assign_pointer(*pp, p);
@@ -702,7 +724,7 @@ err: @@ -706,7 +728,7 @@ err:
static int br_ip4_multicast_add_group(struct net_bridge *br, static int br_ip4_multicast_add_group(struct net_bridge *br,
struct net_bridge_port *port, struct net_bridge_port *port,
__be32 group, __be32 group,
@ -70,7 +70,7 @@
{ {
struct br_ip br_group; struct br_ip br_group;
@@ -713,14 +735,14 @@ static int br_ip4_multicast_add_group(st @@ -717,14 +739,14 @@ static int br_ip4_multicast_add_group(st
br_group.proto = htons(ETH_P_IP); br_group.proto = htons(ETH_P_IP);
br_group.vid = vid; br_group.vid = vid;
@ -87,7 +87,7 @@
{ {
struct br_ip br_group; struct br_ip br_group;
@@ -731,7 +753,7 @@ static int br_ip6_multicast_add_group(st @@ -735,7 +757,7 @@ static int br_ip6_multicast_add_group(st
br_group.proto = htons(ETH_P_IPV6); br_group.proto = htons(ETH_P_IPV6);
br_group.vid = vid; br_group.vid = vid;
@ -96,7 +96,7 @@
} }
#endif #endif
@@ -898,6 +920,7 @@ static int br_ip4_multicast_igmp3_report @@ -949,6 +971,7 @@ static int br_ip4_multicast_igmp3_report
struct net_bridge_port *port, struct net_bridge_port *port,
struct sk_buff *skb) struct sk_buff *skb)
{ {
@ -104,7 +104,7 @@
struct igmpv3_report *ih; struct igmpv3_report *ih;
struct igmpv3_grec *grec; struct igmpv3_grec *grec;
int i; int i;
@@ -943,7 +966,7 @@ static int br_ip4_multicast_igmp3_report @@ -994,7 +1017,7 @@ static int br_ip4_multicast_igmp3_report
continue; continue;
} }
@ -113,7 +113,7 @@
if (err) if (err)
break; break;
} }
@@ -956,6 +979,7 @@ static int br_ip6_multicast_mld2_report( @@ -1007,6 +1030,7 @@ static int br_ip6_multicast_mld2_report(
struct net_bridge_port *port, struct net_bridge_port *port,
struct sk_buff *skb) struct sk_buff *skb)
{ {
@ -121,7 +121,7 @@
struct icmp6hdr *icmp6h; struct icmp6hdr *icmp6h;
struct mld2_grec *grec; struct mld2_grec *grec;
int i; int i;
@@ -1006,7 +1030,7 @@ static int br_ip6_multicast_mld2_report( @@ -1057,7 +1081,7 @@ static int br_ip6_multicast_mld2_report(
} }
err = br_ip6_multicast_add_group(br, port, &grec->grec_mca, err = br_ip6_multicast_add_group(br, port, &grec->grec_mca,
@ -130,17 +130,17 @@
if (!err) if (!err)
break; break;
} }
@@ -1235,7 +1259,8 @@ out: @@ -1302,7 +1326,8 @@ static void br_multicast_leave_group(str
static void br_multicast_leave_group(struct net_bridge *br,
struct net_bridge_port *port, struct net_bridge_port *port,
- struct br_ip *group) struct br_ip *group,
+ struct br_ip *group, struct bridge_mcast_querier *querier,
- struct bridge_mcast_query *query)
+ struct bridge_mcast_query *query,
+ const unsigned char *src) + const unsigned char *src)
{ {
struct net_bridge_mdb_htable *mdb; struct net_bridge_mdb_htable *mdb;
struct net_bridge_mdb_entry *mp; struct net_bridge_mdb_entry *mp;
@@ -1260,7 +1285,7 @@ static void br_multicast_leave_group(str @@ -1352,7 +1377,7 @@ static void br_multicast_leave_group(str
for (pp = &mp->ports; for (pp = &mp->ports;
(p = mlock_dereference(*pp, br)) != NULL; (p = mlock_dereference(*pp, br)) != NULL;
pp = &p->next) { pp = &p->next) {
@ -149,7 +149,7 @@
continue; continue;
rcu_assign_pointer(*pp, p->next); rcu_assign_pointer(*pp, p->next);
@@ -1294,7 +1319,7 @@ static void br_multicast_leave_group(str @@ -1386,7 +1411,7 @@ static void br_multicast_leave_group(str
for (p = mlock_dereference(mp->ports, br); for (p = mlock_dereference(mp->ports, br);
p != NULL; p != NULL;
p = mlock_dereference(p->next, br)) { p = mlock_dereference(p->next, br)) {
@ -158,7 +158,7 @@
continue; continue;
if (!hlist_unhashed(&p->mglist) && if (!hlist_unhashed(&p->mglist) &&
@@ -1313,8 +1338,8 @@ out: @@ -1404,8 +1429,8 @@ out:
static void br_ip4_multicast_leave_group(struct net_bridge *br, static void br_ip4_multicast_leave_group(struct net_bridge *br,
struct net_bridge_port *port, struct net_bridge_port *port,
@ -168,13 +168,14 @@
+ const unsigned char *src) + const unsigned char *src)
{ {
struct br_ip br_group; struct br_ip br_group;
struct bridge_mcast_query *query = port ? &port->ip4_query :
@@ -1325,14 +1350,14 @@ static void br_ip4_multicast_leave_group @@ -1418,14 +1443,15 @@ static void br_ip4_multicast_leave_group
br_group.proto = htons(ETH_P_IP); br_group.proto = htons(ETH_P_IP);
br_group.vid = vid; br_group.vid = vid;
- br_multicast_leave_group(br, port, &br_group); - br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query);
+ br_multicast_leave_group(br, port, &br_group, src); + br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query,
+ src);
} }
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
@ -185,17 +186,18 @@
+ __u16 vid, const unsigned char *src) + __u16 vid, const unsigned char *src)
{ {
struct br_ip br_group; struct br_ip br_group;
struct bridge_mcast_query *query = port ? &port->ip6_query :
@@ -1343,7 +1368,7 @@ static void br_ip6_multicast_leave_group @@ -1439,7 +1465,8 @@ static void br_ip6_multicast_leave_group
br_group.proto = htons(ETH_P_IPV6); br_group.proto = htons(ETH_P_IPV6);
br_group.vid = vid; br_group.vid = vid;
- br_multicast_leave_group(br, port, &br_group); - br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query);
+ br_multicast_leave_group(br, port, &br_group, src); + br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query,
+ src);
} }
#endif #endif
@@ -1351,6 +1376,7 @@ static int br_multicast_ipv4_rcv(struct @@ -1447,6 +1474,7 @@ static int br_multicast_ipv4_rcv(struct
struct net_bridge_port *port, struct net_bridge_port *port,
struct sk_buff *skb) struct sk_buff *skb)
{ {
@ -203,7 +205,7 @@
struct sk_buff *skb2 = skb; struct sk_buff *skb2 = skb;
const struct iphdr *iph; const struct iphdr *iph;
struct igmphdr *ih; struct igmphdr *ih;
@@ -1426,7 +1452,7 @@ static int br_multicast_ipv4_rcv(struct @@ -1522,7 +1550,7 @@ static int br_multicast_ipv4_rcv(struct
case IGMP_HOST_MEMBERSHIP_REPORT: case IGMP_HOST_MEMBERSHIP_REPORT:
case IGMPV2_HOST_MEMBERSHIP_REPORT: case IGMPV2_HOST_MEMBERSHIP_REPORT:
BR_INPUT_SKB_CB(skb)->mrouters_only = 1; BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
@ -212,7 +214,7 @@
break; break;
case IGMPV3_HOST_MEMBERSHIP_REPORT: case IGMPV3_HOST_MEMBERSHIP_REPORT:
err = br_ip4_multicast_igmp3_report(br, port, skb2); err = br_ip4_multicast_igmp3_report(br, port, skb2);
@@ -1435,7 +1461,7 @@ static int br_multicast_ipv4_rcv(struct @@ -1531,7 +1559,7 @@ static int br_multicast_ipv4_rcv(struct
err = br_ip4_multicast_query(br, port, skb2); err = br_ip4_multicast_query(br, port, skb2);
break; break;
case IGMP_HOST_LEAVE_MESSAGE: case IGMP_HOST_LEAVE_MESSAGE:
@ -221,7 +223,7 @@
break; break;
} }
@@ -1452,6 +1478,7 @@ static int br_multicast_ipv6_rcv(struct @@ -1548,6 +1576,7 @@ static int br_multicast_ipv6_rcv(struct
struct net_bridge_port *port, struct net_bridge_port *port,
struct sk_buff *skb) struct sk_buff *skb)
{ {
@ -229,7 +231,7 @@
struct sk_buff *skb2; struct sk_buff *skb2;
const struct ipv6hdr *ip6h; const struct ipv6hdr *ip6h;
u8 icmp6_type; u8 icmp6_type;
@@ -1557,7 +1584,8 @@ static int br_multicast_ipv6_rcv(struct @@ -1659,7 +1688,8 @@ static int br_multicast_ipv6_rcv(struct
} }
mld = (struct mld_msg *)skb_transport_header(skb2); mld = (struct mld_msg *)skb_transport_header(skb2);
BR_INPUT_SKB_CB(skb)->mrouters_only = 1; BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
@ -239,7 +241,7 @@
break; break;
} }
case ICMPV6_MLD2_REPORT: case ICMPV6_MLD2_REPORT:
@@ -1574,7 +1602,7 @@ static int br_multicast_ipv6_rcv(struct @@ -1676,7 +1706,7 @@ static int br_multicast_ipv6_rcv(struct
goto out; goto out;
} }
mld = (struct mld_msg *)skb_transport_header(skb2); mld = (struct mld_msg *)skb_transport_header(skb2);
@ -250,7 +252,7 @@
--- a/net/bridge/br_private.h --- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h +++ b/net/bridge/br_private.h
@@ -101,6 +101,9 @@ struct net_bridge_port_group { @@ -115,6 +115,9 @@ struct net_bridge_port_group {
struct timer_list timer; struct timer_list timer;
struct br_ip addr; struct br_ip addr;
unsigned char state; unsigned char state;
@ -260,15 +262,15 @@
}; };
struct net_bridge_mdb_entry struct net_bridge_mdb_entry
@@ -158,6 +161,7 @@ struct net_bridge_port @@ -172,6 +175,7 @@ struct net_bridge_port
#define BR_MULTICAST_FAST_LEAVE 0x00000008 #define BR_MULTICAST_FAST_LEAVE 0x00000008
#define BR_ADMIN_COST 0x00000010 #define BR_ADMIN_COST 0x00000010
#define BR_ISOLATE_MODE 0x00000020 #define BR_ISOLATE_MODE 0x00000020
+#define BR_MULTICAST_TO_UCAST 0x00000040 +#define BR_MULTICAST_TO_UCAST 0x00000040
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
u32 multicast_startup_queries_sent; struct bridge_mcast_query ip4_query;
@@ -469,7 +473,8 @@ extern struct net_bridge_port_group *br_ @@ -489,7 +493,8 @@ extern struct net_bridge_port_group *br_
struct net_bridge_port *port, struct net_bridge_port *port,
struct br_ip *group, struct br_ip *group,
struct net_bridge_port_group *next, struct net_bridge_port_group *next,
@ -280,7 +282,7 @@
extern void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, extern void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
--- a/net/bridge/br_mdb.c --- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c
@@ -340,7 +340,7 @@ static int br_mdb_add_group(struct net_b @@ -341,7 +341,7 @@ static int br_mdb_add_group(struct net_b
break; break;
} }