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 | ||||
| +++ 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) | ||||
|  { | ||||
|  	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 */ | ||||
|  } | ||||
|   | ||||
| @@ -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: | ||||
|  	switch (p->state) { | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| --- a/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_MULTICAST_FAST_LEAVE	0x00000008 | ||||
|  #define BR_ADMIN_COST		0x00000010 | ||||
| +#define BR_ISOLATE_MODE		0x00000020 | ||||
|   | ||||
|  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING | ||||
|  	u32				multicast_startup_queries_sent; | ||||
|  	struct bridge_mcast_query	ip4_query; | ||||
| --- a/net/bridge/br_sysfs_if.c | ||||
| +++ b/net/bridge/br_sysfs_if.c | ||||
| @@ -159,6 +159,22 @@ BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPI | ||||
| @@ -43,7 +43,7 @@ | ||||
|   | ||||
| --- a/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; | ||||
|   | ||||
|  		br->dev->stats.multicast++; | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
|  	  Ethernet bridge, which means that the different Ethernet segments it | ||||
| --- a/net/ipv6/Makefile | ||||
| +++ b/net/ipv6/Makefile | ||||
| @@ -44,3 +44,4 @@ obj-y += addrconf_core.o exthdrs_core.o | ||||
| @@ -44,3 +44,4 @@ obj-y += addrconf_core.o exthdrs_core.o  | ||||
|  obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload) | ||||
|   | ||||
|  obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o | ||||
|   | ||||
| @@ -38,7 +38,7 @@ | ||||
|  		       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 */ | ||||
| @@ -47,7 +47,7 @@ | ||||
|  			    NULL, br_handle_local_finish)) { | ||||
|  			return RX_HANDLER_CONSUMED; /* consumed by filter */ | ||||
|  		} else { | ||||
| @@ -224,7 +224,7 @@ forward: | ||||
| @@ -225,7 +225,7 @@ forward: | ||||
|  		if (ether_addr_equal(p->br->dev->dev_addr, dest)) | ||||
|  			skb->pkt_type = PACKET_HOST; | ||||
|   | ||||
| @@ -56,7 +56,7 @@ | ||||
|  			br_handle_local_finish)) | ||||
|  			break; | ||||
|   | ||||
| @@ -246,7 +246,7 @@ forward: | ||||
| @@ -247,7 +247,7 @@ forward: | ||||
|  		if (ether_addr_equal(p->br->dev->dev_addr, dest)) | ||||
|  			skb->pkt_type = PACKET_HOST; | ||||
|   | ||||
| @@ -67,7 +67,7 @@ | ||||
|  	default: | ||||
| --- a/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) { | ||||
|  		__skb_push(skb, sizeof(struct ethhdr)); | ||||
|  		skb->dev = port->dev; | ||||
| @@ -96,7 +96,7 @@ | ||||
|  	if (vlan_tx_tag_present(skb)) | ||||
| --- a/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 */ | ||||
|  #ifdef CONFIG_BRIDGE_NETFILTER | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- a/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 br_ip *group, | ||||
|  			struct net_bridge_port_group __rcu *next, | ||||
| @@ -10,7 +10,7 @@ | ||||
|  { | ||||
|  	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); | ||||
|  	setup_timer(&p->timer, br_multicast_port_group_expired, | ||||
|  		    (unsigned long)p); | ||||
| @@ -45,7 +45,7 @@ | ||||
|  { | ||||
|  	struct net_bridge_mdb_entry *mp; | ||||
|  	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; | ||||
|  	     (p = mlock_dereference(*pp, br)) != NULL; | ||||
|  	     pp = &p->next) { | ||||
| @@ -61,7 +61,7 @@ | ||||
|  	if (unlikely(!p)) | ||||
|  		goto err; | ||||
|  	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, | ||||
|  				      struct net_bridge_port *port, | ||||
|  				      __be32 group, | ||||
| @@ -70,7 +70,7 @@ | ||||
|  { | ||||
|  	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.vid = vid; | ||||
|   | ||||
| @@ -87,7 +87,7 @@ | ||||
|  { | ||||
|  	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.vid = vid; | ||||
|   | ||||
| @@ -96,7 +96,7 @@ | ||||
|  } | ||||
|  #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 sk_buff *skb) | ||||
|  { | ||||
| @@ -104,7 +104,7 @@ | ||||
|  	struct igmpv3_report *ih; | ||||
|  	struct igmpv3_grec *grec; | ||||
|  	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; | ||||
|  		} | ||||
|   | ||||
| @@ -113,7 +113,7 @@ | ||||
|  		if (err) | ||||
|  			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 sk_buff *skb) | ||||
|  { | ||||
| @@ -121,7 +121,7 @@ | ||||
|  	struct icmp6hdr *icmp6h; | ||||
|  	struct mld2_grec *grec; | ||||
|  	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, | ||||
| @@ -130,17 +130,17 @@ | ||||
|  		if (!err) | ||||
|  			break; | ||||
|  	} | ||||
| @@ -1235,7 +1259,8 @@ out: | ||||
|   | ||||
|  static void br_multicast_leave_group(struct net_bridge *br, | ||||
| @@ -1302,7 +1326,8 @@ static void br_multicast_leave_group(str | ||||
|  				     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) | ||||
|  { | ||||
|  	struct net_bridge_mdb_htable *mdb; | ||||
|  	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; | ||||
|  		     (p = mlock_dereference(*pp, br)) != NULL; | ||||
|  		     pp = &p->next) { | ||||
| @@ -149,7 +149,7 @@ | ||||
|  				continue; | ||||
|   | ||||
|  			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); | ||||
|  	     p != NULL; | ||||
|  	     p = mlock_dereference(p->next, br)) { | ||||
| @@ -158,7 +158,7 @@ | ||||
|  			continue; | ||||
|   | ||||
|  		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, | ||||
|  					 struct net_bridge_port *port, | ||||
| @@ -168,13 +168,14 @@ | ||||
| +					 const unsigned char *src) | ||||
|  { | ||||
|  	struct br_ip br_group; | ||||
|   | ||||
| @@ -1325,14 +1350,14 @@ static void br_ip4_multicast_leave_group | ||||
|  	struct bridge_mcast_query *query = port ? &port->ip4_query : | ||||
| @@ -1418,14 +1443,15 @@ 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, src); | ||||
| -	br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query); | ||||
| +	br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query, | ||||
| +				 src); | ||||
|  } | ||||
|   | ||||
|  #if IS_ENABLED(CONFIG_IPV6) | ||||
| @@ -185,17 +186,18 @@ | ||||
| +					 __u16 vid, const unsigned char *src) | ||||
|  { | ||||
|  	struct br_ip br_group; | ||||
|   | ||||
| @@ -1343,7 +1368,7 @@ static void br_ip6_multicast_leave_group | ||||
|  	struct bridge_mcast_query *query = port ? &port->ip6_query : | ||||
| @@ -1439,7 +1465,8 @@ static void br_ip6_multicast_leave_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, src); | ||||
| -	br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query); | ||||
| +	br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query, | ||||
| +				 src); | ||||
|  } | ||||
|  #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 sk_buff *skb) | ||||
|  { | ||||
| @@ -203,7 +205,7 @@ | ||||
|  	struct sk_buff *skb2 = skb; | ||||
|  	const struct iphdr *iph; | ||||
|  	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 IGMPV2_HOST_MEMBERSHIP_REPORT: | ||||
|  		BR_INPUT_SKB_CB(skb)->mrouters_only = 1; | ||||
| @@ -212,7 +214,7 @@ | ||||
|  		break; | ||||
|  	case IGMPV3_HOST_MEMBERSHIP_REPORT: | ||||
|  		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); | ||||
|  		break; | ||||
|  	case IGMP_HOST_LEAVE_MESSAGE: | ||||
| @@ -221,7 +223,7 @@ | ||||
|  		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 sk_buff *skb) | ||||
|  { | ||||
| @@ -229,7 +231,7 @@ | ||||
|  	struct sk_buff *skb2; | ||||
|  	const struct ipv6hdr *ip6h; | ||||
|  	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); | ||||
|  		BR_INPUT_SKB_CB(skb)->mrouters_only = 1; | ||||
| @@ -239,7 +241,7 @@ | ||||
|  		break; | ||||
|  	    } | ||||
|  	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; | ||||
|  		} | ||||
|  		mld = (struct mld_msg *)skb_transport_header(skb2); | ||||
| @@ -250,7 +252,7 @@ | ||||
|   | ||||
| --- a/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 br_ip			addr; | ||||
|  	unsigned char			state; | ||||
| @@ -260,15 +262,15 @@ | ||||
|  }; | ||||
|   | ||||
|  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_ADMIN_COST		0x00000010 | ||||
|  #define BR_ISOLATE_MODE		0x00000020 | ||||
| +#define BR_MULTICAST_TO_UCAST	0x00000040 | ||||
|   | ||||
|  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING | ||||
|  	u32				multicast_startup_queries_sent; | ||||
| @@ -469,7 +473,8 @@ extern struct net_bridge_port_group *br_ | ||||
|  	struct bridge_mcast_query	ip4_query; | ||||
| @@ -489,7 +493,8 @@ extern struct net_bridge_port_group *br_ | ||||
|  				struct net_bridge_port *port, | ||||
|  				struct br_ip *group, | ||||
|  				struct net_bridge_port_group *next, | ||||
| @@ -280,7 +282,7 @@ | ||||
|  extern void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, | ||||
| --- a/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; | ||||
|  	} | ||||
|   | ||||
|   | ||||
| @@ -24,7 +24,7 @@ commont qdiscs. | ||||
|  	} | ||||
| --- a/net/sched/sch_fifo.c | ||||
| +++ b/net/sched/sch_fifo.c | ||||
| @@ -29,17 +29,21 @@ static int bfifo_enqueue(struct sk_buff | ||||
| @@ -29,17 +29,21 @@ static int bfifo_enqueue(struct sk_buff  | ||||
|   | ||||
|  static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) | ||||
|  { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau