kernel: backport upstream bridge multicast snooping fixes
Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 41817
This commit is contained in:
		
							
								
								
									
										848
									
								
								target/linux/generic/patches-3.10/070-net_bridge_backports.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										848
									
								
								target/linux/generic/patches-3.10/070-net_bridge_backports.patch
									
									
									
									
									
										Normal 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); | ||||||
| @@ -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) { | ||||||
|   | |||||||
| @@ -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++; | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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; | ||||||
|  	} |  	} | ||||||
|   |   | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau