xref: /openbmc/linux/net/bridge/br_multicast.c (revision 9116ffbf1dd71f953ffda4198d01f82d3ca16df8)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2eb1d1641SHerbert Xu /*
3eb1d1641SHerbert Xu  * Bridge multicast support.
4eb1d1641SHerbert Xu  *
5eb1d1641SHerbert Xu  * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
6eb1d1641SHerbert Xu  */
7eb1d1641SHerbert Xu 
8eb1d1641SHerbert Xu #include <linux/err.h>
907f8ac4aSLinus Lüssing #include <linux/export.h>
10eb1d1641SHerbert Xu #include <linux/if_ether.h>
11eb1d1641SHerbert Xu #include <linux/igmp.h>
124b3087c7SLinus Lüssing #include <linux/in.h>
13eb1d1641SHerbert Xu #include <linux/jhash.h>
14eb1d1641SHerbert Xu #include <linux/kernel.h>
15b195167fSHerbert Xu #include <linux/log2.h>
16eb1d1641SHerbert Xu #include <linux/netdevice.h>
17eb1d1641SHerbert Xu #include <linux/netfilter_bridge.h>
18eb1d1641SHerbert Xu #include <linux/random.h>
19eb1d1641SHerbert Xu #include <linux/rculist.h>
20eb1d1641SHerbert Xu #include <linux/skbuff.h>
21eb1d1641SHerbert Xu #include <linux/slab.h>
22eb1d1641SHerbert Xu #include <linux/timer.h>
231c8ad5bfSCong Wang #include <linux/inetdevice.h>
2491b02d3dSNikolay Aleksandrov #include <linux/mroute.h>
25eb1d1641SHerbert Xu #include <net/ip.h>
26147c1e9bSNogah Frankel #include <net/switchdev.h>
27dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
284b3087c7SLinus Lüssing #include <linux/icmpv6.h>
2908b202b6SYOSHIFUJI Hideaki #include <net/ipv6.h>
3008b202b6SYOSHIFUJI Hideaki #include <net/mld.h>
31d4c4f07dSDavid S. Miller #include <net/ip6_checksum.h>
323c3769e6SLinus Lüssing #include <net/addrconf.h>
3308b202b6SYOSHIFUJI Hideaki #endif
34eb1d1641SHerbert Xu 
35eb1d1641SHerbert Xu #include "br_private.h"
36eb1d1641SHerbert Xu 
3719e3a9c9SNikolay Aleksandrov static const struct rhashtable_params br_mdb_rht_params = {
3819e3a9c9SNikolay Aleksandrov 	.head_offset = offsetof(struct net_bridge_mdb_entry, rhnode),
3919e3a9c9SNikolay Aleksandrov 	.key_offset = offsetof(struct net_bridge_mdb_entry, addr),
4019e3a9c9SNikolay Aleksandrov 	.key_len = sizeof(struct br_ip),
4119e3a9c9SNikolay Aleksandrov 	.automatic_shrinking = true,
4219e3a9c9SNikolay Aleksandrov };
4319e3a9c9SNikolay Aleksandrov 
44085b53c8SNikolay Aleksandrov static const struct rhashtable_params br_sg_port_rht_params = {
45085b53c8SNikolay Aleksandrov 	.head_offset = offsetof(struct net_bridge_port_group, rhnode),
46085b53c8SNikolay Aleksandrov 	.key_offset = offsetof(struct net_bridge_port_group, key),
47085b53c8SNikolay Aleksandrov 	.key_len = sizeof(struct net_bridge_port_group_sg_key),
48085b53c8SNikolay Aleksandrov 	.automatic_shrinking = true,
49085b53c8SNikolay Aleksandrov };
50085b53c8SNikolay Aleksandrov 
51cc0fdd80SLinus Lüssing static void br_multicast_start_querier(struct net_bridge *br,
5290010b36SLinus Lüssing 				       struct bridge_mcast_own_query *query);
53754bc547SSatish Ashok static void br_multicast_add_router(struct net_bridge *br,
54754bc547SSatish Ashok 				    struct net_bridge_port *port);
55bc8c20acSSatish Ashok static void br_ip4_multicast_leave_group(struct net_bridge *br,
56bc8c20acSSatish Ashok 					 struct net_bridge_port *port,
57bc8c20acSSatish Ashok 					 __be32 group,
586db6f0eaSFelix Fietkau 					 __u16 vid,
596db6f0eaSFelix Fietkau 					 const unsigned char *src);
6042c11ccfSNikolay Aleksandrov static void br_multicast_port_group_rexmit(struct timer_list *t);
616db6f0eaSFelix Fietkau 
62f12e7d95SNogah Frankel static void __del_port_router(struct net_bridge_port *p);
63bc8c20acSSatish Ashok #if IS_ENABLED(CONFIG_IPV6)
64bc8c20acSSatish Ashok static void br_ip6_multicast_leave_group(struct net_bridge *br,
65bc8c20acSSatish Ashok 					 struct net_bridge_port *port,
66bc8c20acSSatish Ashok 					 const struct in6_addr *group,
676db6f0eaSFelix Fietkau 					 __u16 vid, const unsigned char *src);
68bc8c20acSSatish Ashok #endif
69b0812368SNikolay Aleksandrov static struct net_bridge_port_group *
70b0812368SNikolay Aleksandrov __br_multicast_add_group(struct net_bridge *br,
71b0812368SNikolay Aleksandrov 			 struct net_bridge_port *port,
72b0812368SNikolay Aleksandrov 			 struct br_ip *group,
73b0812368SNikolay Aleksandrov 			 const unsigned char *src,
74b0812368SNikolay Aleksandrov 			 u8 filter_mode,
75*9116ffbfSNikolay Aleksandrov 			 bool igmpv2_mldv1,
76*9116ffbfSNikolay Aleksandrov 			 bool blocked);
778266a049SNikolay Aleksandrov static void br_multicast_find_del_pg(struct net_bridge *br,
788266a049SNikolay Aleksandrov 				     struct net_bridge_port_group *pg);
79c83b8fabSHerbert Xu 
80085b53c8SNikolay Aleksandrov static struct net_bridge_port_group *
81085b53c8SNikolay Aleksandrov br_sg_port_find(struct net_bridge *br,
82085b53c8SNikolay Aleksandrov 		struct net_bridge_port_group_sg_key *sg_p)
83085b53c8SNikolay Aleksandrov {
84085b53c8SNikolay Aleksandrov 	lockdep_assert_held_once(&br->multicast_lock);
85085b53c8SNikolay Aleksandrov 
86085b53c8SNikolay Aleksandrov 	return rhashtable_lookup_fast(&br->sg_port_tbl, sg_p,
87085b53c8SNikolay Aleksandrov 				      br_sg_port_rht_params);
88085b53c8SNikolay Aleksandrov }
89085b53c8SNikolay Aleksandrov 
9019e3a9c9SNikolay Aleksandrov static struct net_bridge_mdb_entry *br_mdb_ip_get_rcu(struct net_bridge *br,
91cfd56754SCong Wang 						      struct br_ip *dst)
927f285fa7SHerbert Xu {
9319e3a9c9SNikolay Aleksandrov 	return rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
947f285fa7SHerbert Xu }
957f285fa7SHerbert Xu 
9619e3a9c9SNikolay Aleksandrov struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge *br,
9719e3a9c9SNikolay Aleksandrov 					   struct br_ip *dst)
9819e3a9c9SNikolay Aleksandrov {
9919e3a9c9SNikolay Aleksandrov 	struct net_bridge_mdb_entry *ent;
10019e3a9c9SNikolay Aleksandrov 
10119e3a9c9SNikolay Aleksandrov 	lockdep_assert_held_once(&br->multicast_lock);
10219e3a9c9SNikolay Aleksandrov 
10319e3a9c9SNikolay Aleksandrov 	rcu_read_lock();
10419e3a9c9SNikolay Aleksandrov 	ent = rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
10519e3a9c9SNikolay Aleksandrov 	rcu_read_unlock();
10619e3a9c9SNikolay Aleksandrov 
10719e3a9c9SNikolay Aleksandrov 	return ent;
10819e3a9c9SNikolay Aleksandrov }
10919e3a9c9SNikolay Aleksandrov 
11019e3a9c9SNikolay Aleksandrov static struct net_bridge_mdb_entry *br_mdb_ip4_get(struct net_bridge *br,
11119e3a9c9SNikolay Aleksandrov 						   __be32 dst, __u16 vid)
112eb1d1641SHerbert Xu {
1138ef2a9a5SYOSHIFUJI Hideaki 	struct br_ip br_dst;
1140821ec55SHerbert Xu 
11519e3a9c9SNikolay Aleksandrov 	memset(&br_dst, 0, sizeof(br_dst));
116eab3227bSNikolay Aleksandrov 	br_dst.dst.ip4 = dst;
1178ef2a9a5SYOSHIFUJI Hideaki 	br_dst.proto = htons(ETH_P_IP);
118b0e9a30dSVlad Yasevich 	br_dst.vid = vid;
1198ef2a9a5SYOSHIFUJI Hideaki 
12019e3a9c9SNikolay Aleksandrov 	return br_mdb_ip_get(br, &br_dst);
1218ef2a9a5SYOSHIFUJI Hideaki }
1228ef2a9a5SYOSHIFUJI Hideaki 
123dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
12419e3a9c9SNikolay Aleksandrov static struct net_bridge_mdb_entry *br_mdb_ip6_get(struct net_bridge *br,
12519e3a9c9SNikolay Aleksandrov 						   const struct in6_addr *dst,
126b0e9a30dSVlad Yasevich 						   __u16 vid)
12708b202b6SYOSHIFUJI Hideaki {
12808b202b6SYOSHIFUJI Hideaki 	struct br_ip br_dst;
12908b202b6SYOSHIFUJI Hideaki 
13019e3a9c9SNikolay Aleksandrov 	memset(&br_dst, 0, sizeof(br_dst));
131eab3227bSNikolay Aleksandrov 	br_dst.dst.ip6 = *dst;
13208b202b6SYOSHIFUJI Hideaki 	br_dst.proto = htons(ETH_P_IPV6);
133b0e9a30dSVlad Yasevich 	br_dst.vid = vid;
13408b202b6SYOSHIFUJI Hideaki 
13519e3a9c9SNikolay Aleksandrov 	return br_mdb_ip_get(br, &br_dst);
13608b202b6SYOSHIFUJI Hideaki }
13708b202b6SYOSHIFUJI Hideaki #endif
13808b202b6SYOSHIFUJI Hideaki 
139eb1d1641SHerbert Xu struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
140fbca58a2SCong Wang 					struct sk_buff *skb, u16 vid)
141eb1d1641SHerbert Xu {
1428ef2a9a5SYOSHIFUJI Hideaki 	struct br_ip ip;
1438ef2a9a5SYOSHIFUJI Hideaki 
14413cefad2SNikolay Aleksandrov 	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
145eb1d1641SHerbert Xu 		return NULL;
146eb1d1641SHerbert Xu 
1478ef2a9a5SYOSHIFUJI Hideaki 	if (BR_INPUT_SKB_CB(skb)->igmp)
1488ef2a9a5SYOSHIFUJI Hideaki 		return NULL;
1498ef2a9a5SYOSHIFUJI Hideaki 
15019e3a9c9SNikolay Aleksandrov 	memset(&ip, 0, sizeof(ip));
1518ef2a9a5SYOSHIFUJI Hideaki 	ip.proto = skb->protocol;
152fbca58a2SCong Wang 	ip.vid = vid;
1538ef2a9a5SYOSHIFUJI Hideaki 
154eb1d1641SHerbert Xu 	switch (skb->protocol) {
155eb1d1641SHerbert Xu 	case htons(ETH_P_IP):
156eab3227bSNikolay Aleksandrov 		ip.dst.ip4 = ip_hdr(skb)->daddr;
1577d07a68cSNikolay Aleksandrov 		if (br->multicast_igmp_version == 3) {
1587d07a68cSNikolay Aleksandrov 			struct net_bridge_mdb_entry *mdb;
1597d07a68cSNikolay Aleksandrov 
1607d07a68cSNikolay Aleksandrov 			ip.src.ip4 = ip_hdr(skb)->saddr;
1617d07a68cSNikolay Aleksandrov 			mdb = br_mdb_ip_get_rcu(br, &ip);
1627d07a68cSNikolay Aleksandrov 			if (mdb)
1637d07a68cSNikolay Aleksandrov 				return mdb;
1647d07a68cSNikolay Aleksandrov 			ip.src.ip4 = 0;
1657d07a68cSNikolay Aleksandrov 		}
166eb1d1641SHerbert Xu 		break;
167dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
16808b202b6SYOSHIFUJI Hideaki 	case htons(ETH_P_IPV6):
169eab3227bSNikolay Aleksandrov 		ip.dst.ip6 = ipv6_hdr(skb)->daddr;
1707d07a68cSNikolay Aleksandrov 		if (br->multicast_mld_version == 2) {
1717d07a68cSNikolay Aleksandrov 			struct net_bridge_mdb_entry *mdb;
1727d07a68cSNikolay Aleksandrov 
1737d07a68cSNikolay Aleksandrov 			ip.src.ip6 = ipv6_hdr(skb)->saddr;
1747d07a68cSNikolay Aleksandrov 			mdb = br_mdb_ip_get_rcu(br, &ip);
1757d07a68cSNikolay Aleksandrov 			if (mdb)
1767d07a68cSNikolay Aleksandrov 				return mdb;
1777d07a68cSNikolay Aleksandrov 			memset(&ip.src.ip6, 0, sizeof(ip.src.ip6));
1787d07a68cSNikolay Aleksandrov 		}
17908b202b6SYOSHIFUJI Hideaki 		break;
18008b202b6SYOSHIFUJI Hideaki #endif
1818ef2a9a5SYOSHIFUJI Hideaki 	default:
1828ef2a9a5SYOSHIFUJI Hideaki 		return NULL;
183eb1d1641SHerbert Xu 	}
184eb1d1641SHerbert Xu 
18519e3a9c9SNikolay Aleksandrov 	return br_mdb_ip_get_rcu(br, &ip);
186eb1d1641SHerbert Xu }
187eb1d1641SHerbert Xu 
188b0812368SNikolay Aleksandrov static bool br_port_group_equal(struct net_bridge_port_group *p,
189b0812368SNikolay Aleksandrov 				struct net_bridge_port *port,
190b0812368SNikolay Aleksandrov 				const unsigned char *src)
191b0812368SNikolay Aleksandrov {
192b0812368SNikolay Aleksandrov 	if (p->key.port != port)
193b0812368SNikolay Aleksandrov 		return false;
194b0812368SNikolay Aleksandrov 
195b0812368SNikolay Aleksandrov 	if (!(port->flags & BR_MULTICAST_TO_UNICAST))
196b0812368SNikolay Aleksandrov 		return true;
197b0812368SNikolay Aleksandrov 
198b0812368SNikolay Aleksandrov 	return ether_addr_equal(src, p->eth_addr);
199b0812368SNikolay Aleksandrov }
200b0812368SNikolay Aleksandrov 
2018266a049SNikolay Aleksandrov static void __fwd_add_star_excl(struct net_bridge_port_group *pg,
2028266a049SNikolay Aleksandrov 				struct br_ip *sg_ip)
2038266a049SNikolay Aleksandrov {
2048266a049SNikolay Aleksandrov 	struct net_bridge_port_group_sg_key sg_key;
2058266a049SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
2068266a049SNikolay Aleksandrov 	struct net_bridge_port_group *src_pg;
2078266a049SNikolay Aleksandrov 
2088266a049SNikolay Aleksandrov 	memset(&sg_key, 0, sizeof(sg_key));
2098266a049SNikolay Aleksandrov 	sg_key.port = pg->key.port;
2108266a049SNikolay Aleksandrov 	sg_key.addr = *sg_ip;
2118266a049SNikolay Aleksandrov 	if (br_sg_port_find(br, &sg_key))
2128266a049SNikolay Aleksandrov 		return;
2138266a049SNikolay Aleksandrov 
2148266a049SNikolay Aleksandrov 	src_pg = __br_multicast_add_group(br, pg->key.port, sg_ip, pg->eth_addr,
215*9116ffbfSNikolay Aleksandrov 					  MCAST_INCLUDE, false, false);
2168266a049SNikolay Aleksandrov 	if (IS_ERR_OR_NULL(src_pg) ||
2178266a049SNikolay Aleksandrov 	    src_pg->rt_protocol != RTPROT_KERNEL)
2188266a049SNikolay Aleksandrov 		return;
2198266a049SNikolay Aleksandrov 
2208266a049SNikolay Aleksandrov 	src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL;
2218266a049SNikolay Aleksandrov }
2228266a049SNikolay Aleksandrov 
2238266a049SNikolay Aleksandrov static void __fwd_del_star_excl(struct net_bridge_port_group *pg,
2248266a049SNikolay Aleksandrov 				struct br_ip *sg_ip)
2258266a049SNikolay Aleksandrov {
2268266a049SNikolay Aleksandrov 	struct net_bridge_port_group_sg_key sg_key;
2278266a049SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
2288266a049SNikolay Aleksandrov 	struct net_bridge_port_group *src_pg;
2298266a049SNikolay Aleksandrov 
2308266a049SNikolay Aleksandrov 	memset(&sg_key, 0, sizeof(sg_key));
2318266a049SNikolay Aleksandrov 	sg_key.port = pg->key.port;
2328266a049SNikolay Aleksandrov 	sg_key.addr = *sg_ip;
2338266a049SNikolay Aleksandrov 	src_pg = br_sg_port_find(br, &sg_key);
2348266a049SNikolay Aleksandrov 	if (!src_pg || !(src_pg->flags & MDB_PG_FLAGS_STAR_EXCL) ||
2358266a049SNikolay Aleksandrov 	    src_pg->rt_protocol != RTPROT_KERNEL)
2368266a049SNikolay Aleksandrov 		return;
2378266a049SNikolay Aleksandrov 
2388266a049SNikolay Aleksandrov 	br_multicast_find_del_pg(br, src_pg);
2398266a049SNikolay Aleksandrov }
2408266a049SNikolay Aleksandrov 
2418266a049SNikolay Aleksandrov /* When a port group transitions to (or is added as) EXCLUDE we need to add it
2428266a049SNikolay Aleksandrov  * to all other ports' S,G entries which are not blocked by the current group
2438266a049SNikolay Aleksandrov  * for proper replication, the assumption is that any S,G blocked entries
2448266a049SNikolay Aleksandrov  * are already added so the S,G,port lookup should skip them.
2458266a049SNikolay Aleksandrov  * When a port group transitions from EXCLUDE -> INCLUDE mode or is being
2468266a049SNikolay Aleksandrov  * deleted we need to remove it from all ports' S,G entries where it was
2478266a049SNikolay Aleksandrov  * automatically installed before (i.e. where it's MDB_PG_FLAGS_STAR_EXCL).
2488266a049SNikolay Aleksandrov  */
2498266a049SNikolay Aleksandrov void br_multicast_star_g_handle_mode(struct net_bridge_port_group *pg,
2508266a049SNikolay Aleksandrov 				     u8 filter_mode)
2518266a049SNikolay Aleksandrov {
2528266a049SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
2538266a049SNikolay Aleksandrov 	struct net_bridge_port_group *pg_lst;
2548266a049SNikolay Aleksandrov 	struct net_bridge_mdb_entry *mp;
2558266a049SNikolay Aleksandrov 	struct br_ip sg_ip;
2568266a049SNikolay Aleksandrov 
2578266a049SNikolay Aleksandrov 	if (WARN_ON(!br_multicast_is_star_g(&pg->key.addr)))
2588266a049SNikolay Aleksandrov 		return;
2598266a049SNikolay Aleksandrov 
2608266a049SNikolay Aleksandrov 	mp = br_mdb_ip_get(br, &pg->key.addr);
2618266a049SNikolay Aleksandrov 	if (!mp)
2628266a049SNikolay Aleksandrov 		return;
2638266a049SNikolay Aleksandrov 
2648266a049SNikolay Aleksandrov 	memset(&sg_ip, 0, sizeof(sg_ip));
2658266a049SNikolay Aleksandrov 	sg_ip = pg->key.addr;
2668266a049SNikolay Aleksandrov 	for (pg_lst = mlock_dereference(mp->ports, br);
2678266a049SNikolay Aleksandrov 	     pg_lst;
2688266a049SNikolay Aleksandrov 	     pg_lst = mlock_dereference(pg_lst->next, br)) {
2698266a049SNikolay Aleksandrov 		struct net_bridge_group_src *src_ent;
2708266a049SNikolay Aleksandrov 
2718266a049SNikolay Aleksandrov 		if (pg_lst == pg)
2728266a049SNikolay Aleksandrov 			continue;
2738266a049SNikolay Aleksandrov 		hlist_for_each_entry(src_ent, &pg_lst->src_list, node) {
2748266a049SNikolay Aleksandrov 			if (!(src_ent->flags & BR_SGRP_F_INSTALLED))
2758266a049SNikolay Aleksandrov 				continue;
2768266a049SNikolay Aleksandrov 			sg_ip.src = src_ent->addr.src;
2778266a049SNikolay Aleksandrov 			switch (filter_mode) {
2788266a049SNikolay Aleksandrov 			case MCAST_INCLUDE:
2798266a049SNikolay Aleksandrov 				__fwd_del_star_excl(pg, &sg_ip);
2808266a049SNikolay Aleksandrov 				break;
2818266a049SNikolay Aleksandrov 			case MCAST_EXCLUDE:
2828266a049SNikolay Aleksandrov 				__fwd_add_star_excl(pg, &sg_ip);
2838266a049SNikolay Aleksandrov 				break;
2848266a049SNikolay Aleksandrov 			}
2858266a049SNikolay Aleksandrov 		}
2868266a049SNikolay Aleksandrov 	}
2878266a049SNikolay Aleksandrov }
2888266a049SNikolay Aleksandrov 
2898266a049SNikolay Aleksandrov static void br_multicast_sg_del_exclude_ports(struct net_bridge_mdb_entry *sgmp)
2908266a049SNikolay Aleksandrov {
2918266a049SNikolay Aleksandrov 	struct net_bridge_port_group __rcu **pp;
2928266a049SNikolay Aleksandrov 	struct net_bridge_port_group *p;
2938266a049SNikolay Aleksandrov 
2948266a049SNikolay Aleksandrov 	/* *,G exclude ports are only added to S,G entries */
2958266a049SNikolay Aleksandrov 	if (WARN_ON(br_multicast_is_star_g(&sgmp->addr)))
2968266a049SNikolay Aleksandrov 		return;
2978266a049SNikolay Aleksandrov 
2988266a049SNikolay Aleksandrov 	/* we need the STAR_EXCLUDE ports if there are non-STAR_EXCLUDE ports
2998266a049SNikolay Aleksandrov 	 * we should ignore perm entries since they're managed by user-space
3008266a049SNikolay Aleksandrov 	 */
3018266a049SNikolay Aleksandrov 	for (pp = &sgmp->ports;
3028266a049SNikolay Aleksandrov 	     (p = mlock_dereference(*pp, sgmp->br)) != NULL;
3038266a049SNikolay Aleksandrov 	     pp = &p->next)
3048266a049SNikolay Aleksandrov 		if (!(p->flags & (MDB_PG_FLAGS_STAR_EXCL |
3058266a049SNikolay Aleksandrov 				  MDB_PG_FLAGS_PERMANENT)))
3068266a049SNikolay Aleksandrov 			return;
3078266a049SNikolay Aleksandrov 
3088266a049SNikolay Aleksandrov 	for (pp = &sgmp->ports;
3098266a049SNikolay Aleksandrov 	     (p = mlock_dereference(*pp, sgmp->br)) != NULL;) {
3108266a049SNikolay Aleksandrov 		if (!(p->flags & MDB_PG_FLAGS_PERMANENT))
3118266a049SNikolay Aleksandrov 			br_multicast_del_pg(sgmp, p, pp);
3128266a049SNikolay Aleksandrov 		else
3138266a049SNikolay Aleksandrov 			pp = &p->next;
3148266a049SNikolay Aleksandrov 	}
3158266a049SNikolay Aleksandrov }
3168266a049SNikolay Aleksandrov 
3178266a049SNikolay Aleksandrov void br_multicast_sg_add_exclude_ports(struct net_bridge_mdb_entry *star_mp,
3188266a049SNikolay Aleksandrov 				       struct net_bridge_port_group *sg)
3198266a049SNikolay Aleksandrov {
3208266a049SNikolay Aleksandrov 	struct net_bridge_port_group_sg_key sg_key;
3218266a049SNikolay Aleksandrov 	struct net_bridge *br = star_mp->br;
3228266a049SNikolay Aleksandrov 	struct net_bridge_port_group *pg;
3238266a049SNikolay Aleksandrov 
3248266a049SNikolay Aleksandrov 	if (WARN_ON(br_multicast_is_star_g(&sg->key.addr)))
3258266a049SNikolay Aleksandrov 		return;
3268266a049SNikolay Aleksandrov 	if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr)))
3278266a049SNikolay Aleksandrov 		return;
3288266a049SNikolay Aleksandrov 
3298266a049SNikolay Aleksandrov 	memset(&sg_key, 0, sizeof(sg_key));
3308266a049SNikolay Aleksandrov 	sg_key.addr = sg->key.addr;
3318266a049SNikolay Aleksandrov 	/* we need to add all exclude ports to the S,G */
3328266a049SNikolay Aleksandrov 	for (pg = mlock_dereference(star_mp->ports, br);
3338266a049SNikolay Aleksandrov 	     pg;
3348266a049SNikolay Aleksandrov 	     pg = mlock_dereference(pg->next, br)) {
3358266a049SNikolay Aleksandrov 		struct net_bridge_port_group *src_pg;
3368266a049SNikolay Aleksandrov 
3378266a049SNikolay Aleksandrov 		if (pg == sg || pg->filter_mode == MCAST_INCLUDE)
3388266a049SNikolay Aleksandrov 			continue;
3398266a049SNikolay Aleksandrov 
3408266a049SNikolay Aleksandrov 		sg_key.port = pg->key.port;
3418266a049SNikolay Aleksandrov 		if (br_sg_port_find(br, &sg_key))
3428266a049SNikolay Aleksandrov 			continue;
3438266a049SNikolay Aleksandrov 
3448266a049SNikolay Aleksandrov 		src_pg = __br_multicast_add_group(br, pg->key.port,
3458266a049SNikolay Aleksandrov 						  &sg->key.addr,
3468266a049SNikolay Aleksandrov 						  sg->eth_addr,
347*9116ffbfSNikolay Aleksandrov 						  MCAST_INCLUDE, false, false);
3488266a049SNikolay Aleksandrov 		if (IS_ERR_OR_NULL(src_pg) ||
3498266a049SNikolay Aleksandrov 		    src_pg->rt_protocol != RTPROT_KERNEL)
3508266a049SNikolay Aleksandrov 			continue;
3518266a049SNikolay Aleksandrov 		src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL;
3528266a049SNikolay Aleksandrov 	}
3538266a049SNikolay Aleksandrov }
3548266a049SNikolay Aleksandrov 
355b0812368SNikolay Aleksandrov static void br_multicast_fwd_src_add(struct net_bridge_group_src *src)
356b0812368SNikolay Aleksandrov {
3578266a049SNikolay Aleksandrov 	struct net_bridge_mdb_entry *star_mp;
358b0812368SNikolay Aleksandrov 	struct net_bridge_port_group *sg;
359b0812368SNikolay Aleksandrov 	struct br_ip sg_ip;
360b0812368SNikolay Aleksandrov 
361b0812368SNikolay Aleksandrov 	if (src->flags & BR_SGRP_F_INSTALLED)
362b0812368SNikolay Aleksandrov 		return;
363b0812368SNikolay Aleksandrov 
364b0812368SNikolay Aleksandrov 	memset(&sg_ip, 0, sizeof(sg_ip));
365b0812368SNikolay Aleksandrov 	sg_ip = src->pg->key.addr;
366b0812368SNikolay Aleksandrov 	sg_ip.src = src->addr.src;
367b0812368SNikolay Aleksandrov 	sg = __br_multicast_add_group(src->br, src->pg->key.port, &sg_ip,
368*9116ffbfSNikolay Aleksandrov 				      src->pg->eth_addr, MCAST_INCLUDE, false,
369*9116ffbfSNikolay Aleksandrov 				      !timer_pending(&src->timer));
370b0812368SNikolay Aleksandrov 	if (IS_ERR_OR_NULL(sg))
371b0812368SNikolay Aleksandrov 		return;
372b0812368SNikolay Aleksandrov 	src->flags |= BR_SGRP_F_INSTALLED;
3738266a049SNikolay Aleksandrov 	sg->flags &= ~MDB_PG_FLAGS_STAR_EXCL;
374b0812368SNikolay Aleksandrov 
375b0812368SNikolay Aleksandrov 	/* if it was added by user-space as perm we can skip next steps */
376b0812368SNikolay Aleksandrov 	if (sg->rt_protocol != RTPROT_KERNEL &&
377b0812368SNikolay Aleksandrov 	    (sg->flags & MDB_PG_FLAGS_PERMANENT))
378b0812368SNikolay Aleksandrov 		return;
379b0812368SNikolay Aleksandrov 
380b0812368SNikolay Aleksandrov 	/* the kernel is now responsible for removing this S,G */
381b0812368SNikolay Aleksandrov 	del_timer(&sg->timer);
3828266a049SNikolay Aleksandrov 	star_mp = br_mdb_ip_get(src->br, &src->pg->key.addr);
3838266a049SNikolay Aleksandrov 	if (!star_mp)
3848266a049SNikolay Aleksandrov 		return;
3858266a049SNikolay Aleksandrov 
3868266a049SNikolay Aleksandrov 	br_multicast_sg_add_exclude_ports(star_mp, sg);
387b0812368SNikolay Aleksandrov }
388b0812368SNikolay Aleksandrov 
389b0812368SNikolay Aleksandrov static void br_multicast_fwd_src_remove(struct net_bridge_group_src *src)
390b0812368SNikolay Aleksandrov {
391b0812368SNikolay Aleksandrov 	struct net_bridge_port_group *p, *pg = src->pg;
392b0812368SNikolay Aleksandrov 	struct net_bridge_port_group __rcu **pp;
393b0812368SNikolay Aleksandrov 	struct net_bridge_mdb_entry *mp;
394b0812368SNikolay Aleksandrov 	struct br_ip sg_ip;
395b0812368SNikolay Aleksandrov 
396b0812368SNikolay Aleksandrov 	memset(&sg_ip, 0, sizeof(sg_ip));
397b0812368SNikolay Aleksandrov 	sg_ip = pg->key.addr;
398b0812368SNikolay Aleksandrov 	sg_ip.src = src->addr.src;
399b0812368SNikolay Aleksandrov 
400b0812368SNikolay Aleksandrov 	mp = br_mdb_ip_get(src->br, &sg_ip);
401b0812368SNikolay Aleksandrov 	if (!mp)
402b0812368SNikolay Aleksandrov 		return;
403b0812368SNikolay Aleksandrov 
404b0812368SNikolay Aleksandrov 	for (pp = &mp->ports;
405b0812368SNikolay Aleksandrov 	     (p = mlock_dereference(*pp, src->br)) != NULL;
406b0812368SNikolay Aleksandrov 	     pp = &p->next) {
407b0812368SNikolay Aleksandrov 		if (!br_port_group_equal(p, pg->key.port, pg->eth_addr))
408b0812368SNikolay Aleksandrov 			continue;
409b0812368SNikolay Aleksandrov 
410b0812368SNikolay Aleksandrov 		if (p->rt_protocol != RTPROT_KERNEL &&
411b0812368SNikolay Aleksandrov 		    (p->flags & MDB_PG_FLAGS_PERMANENT))
412b0812368SNikolay Aleksandrov 			break;
413b0812368SNikolay Aleksandrov 
414b0812368SNikolay Aleksandrov 		br_multicast_del_pg(mp, p, pp);
415b0812368SNikolay Aleksandrov 		break;
416b0812368SNikolay Aleksandrov 	}
417b0812368SNikolay Aleksandrov 	src->flags &= ~BR_SGRP_F_INSTALLED;
418b0812368SNikolay Aleksandrov }
419b0812368SNikolay Aleksandrov 
420*9116ffbfSNikolay Aleksandrov /* install S,G and based on src's timer enable or disable forwarding */
421b0812368SNikolay Aleksandrov static void br_multicast_fwd_src_handle(struct net_bridge_group_src *src)
422b0812368SNikolay Aleksandrov {
423*9116ffbfSNikolay Aleksandrov 	struct net_bridge_port_group_sg_key sg_key;
424*9116ffbfSNikolay Aleksandrov 	struct net_bridge_port_group *sg;
425*9116ffbfSNikolay Aleksandrov 	u8 old_flags;
426*9116ffbfSNikolay Aleksandrov 
427b0812368SNikolay Aleksandrov 	br_multicast_fwd_src_add(src);
428*9116ffbfSNikolay Aleksandrov 
429*9116ffbfSNikolay Aleksandrov 	memset(&sg_key, 0, sizeof(sg_key));
430*9116ffbfSNikolay Aleksandrov 	sg_key.addr = src->pg->key.addr;
431*9116ffbfSNikolay Aleksandrov 	sg_key.addr.src = src->addr.src;
432*9116ffbfSNikolay Aleksandrov 	sg_key.port = src->pg->key.port;
433*9116ffbfSNikolay Aleksandrov 
434*9116ffbfSNikolay Aleksandrov 	sg = br_sg_port_find(src->br, &sg_key);
435*9116ffbfSNikolay Aleksandrov 	if (!sg || (sg->flags & MDB_PG_FLAGS_PERMANENT))
436*9116ffbfSNikolay Aleksandrov 		return;
437*9116ffbfSNikolay Aleksandrov 
438*9116ffbfSNikolay Aleksandrov 	old_flags = sg->flags;
439*9116ffbfSNikolay Aleksandrov 	if (timer_pending(&src->timer))
440*9116ffbfSNikolay Aleksandrov 		sg->flags &= ~MDB_PG_FLAGS_BLOCKED;
441*9116ffbfSNikolay Aleksandrov 	else
442*9116ffbfSNikolay Aleksandrov 		sg->flags |= MDB_PG_FLAGS_BLOCKED;
443*9116ffbfSNikolay Aleksandrov 
444*9116ffbfSNikolay Aleksandrov 	if (old_flags != sg->flags) {
445*9116ffbfSNikolay Aleksandrov 		struct net_bridge_mdb_entry *sg_mp;
446*9116ffbfSNikolay Aleksandrov 
447*9116ffbfSNikolay Aleksandrov 		sg_mp = br_mdb_ip_get(src->br, &sg_key.addr);
448*9116ffbfSNikolay Aleksandrov 		if (!sg_mp)
449*9116ffbfSNikolay Aleksandrov 			return;
450*9116ffbfSNikolay Aleksandrov 		br_mdb_notify(src->br->dev, sg_mp, sg, RTM_NEWMDB);
451*9116ffbfSNikolay Aleksandrov 	}
452b0812368SNikolay Aleksandrov }
453b0812368SNikolay Aleksandrov 
454e12cec65SNikolay Aleksandrov static void br_multicast_destroy_mdb_entry(struct net_bridge_mcast_gc *gc)
455e12cec65SNikolay Aleksandrov {
456e12cec65SNikolay Aleksandrov 	struct net_bridge_mdb_entry *mp;
457e12cec65SNikolay Aleksandrov 
458e12cec65SNikolay Aleksandrov 	mp = container_of(gc, struct net_bridge_mdb_entry, mcast_gc);
459e12cec65SNikolay Aleksandrov 	WARN_ON(!hlist_unhashed(&mp->mdb_node));
460e12cec65SNikolay Aleksandrov 	WARN_ON(mp->ports);
461e12cec65SNikolay Aleksandrov 
462e12cec65SNikolay Aleksandrov 	del_timer_sync(&mp->timer);
463e12cec65SNikolay Aleksandrov 	kfree_rcu(mp, rcu);
464e12cec65SNikolay Aleksandrov }
465e12cec65SNikolay Aleksandrov 
466e12cec65SNikolay Aleksandrov static void br_multicast_del_mdb_entry(struct net_bridge_mdb_entry *mp)
467e12cec65SNikolay Aleksandrov {
468e12cec65SNikolay Aleksandrov 	struct net_bridge *br = mp->br;
469e12cec65SNikolay Aleksandrov 
470e12cec65SNikolay Aleksandrov 	rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode,
471e12cec65SNikolay Aleksandrov 			       br_mdb_rht_params);
472e12cec65SNikolay Aleksandrov 	hlist_del_init_rcu(&mp->mdb_node);
473e12cec65SNikolay Aleksandrov 	hlist_add_head(&mp->mcast_gc.gc_node, &br->mcast_gc_list);
474e12cec65SNikolay Aleksandrov 	queue_work(system_long_wq, &br->mcast_gc_work);
475e12cec65SNikolay Aleksandrov }
476e12cec65SNikolay Aleksandrov 
47788c1f37fSAllen Pais static void br_multicast_group_expired(struct timer_list *t)
478eb1d1641SHerbert Xu {
47988c1f37fSAllen Pais 	struct net_bridge_mdb_entry *mp = from_timer(mp, t, timer);
480eb1d1641SHerbert Xu 	struct net_bridge *br = mp->br;
481eb1d1641SHerbert Xu 
482eb1d1641SHerbert Xu 	spin_lock(&br->multicast_lock);
483b0812368SNikolay Aleksandrov 	if (hlist_unhashed(&mp->mdb_node) || !netif_running(br->dev) ||
484b0812368SNikolay Aleksandrov 	    timer_pending(&mp->timer))
485eb1d1641SHerbert Xu 		goto out;
486eb1d1641SHerbert Xu 
4871bc844eeSNikolay Aleksandrov 	br_multicast_host_leave(mp, true);
488eb1d1641SHerbert Xu 
489eb1d1641SHerbert Xu 	if (mp->ports)
490eb1d1641SHerbert Xu 		goto out;
491e12cec65SNikolay Aleksandrov 	br_multicast_del_mdb_entry(mp);
492eb1d1641SHerbert Xu out:
493eb1d1641SHerbert Xu 	spin_unlock(&br->multicast_lock);
494eb1d1641SHerbert Xu }
495eb1d1641SHerbert Xu 
496e12cec65SNikolay Aleksandrov static void br_multicast_destroy_group_src(struct net_bridge_mcast_gc *gc)
497e12cec65SNikolay Aleksandrov {
498e12cec65SNikolay Aleksandrov 	struct net_bridge_group_src *src;
499e12cec65SNikolay Aleksandrov 
500e12cec65SNikolay Aleksandrov 	src = container_of(gc, struct net_bridge_group_src, mcast_gc);
501e12cec65SNikolay Aleksandrov 	WARN_ON(!hlist_unhashed(&src->node));
502e12cec65SNikolay Aleksandrov 
503e12cec65SNikolay Aleksandrov 	del_timer_sync(&src->timer);
504e12cec65SNikolay Aleksandrov 	kfree_rcu(src, rcu);
505e12cec65SNikolay Aleksandrov }
506e12cec65SNikolay Aleksandrov 
5078b671779SNikolay Aleksandrov static void br_multicast_del_group_src(struct net_bridge_group_src *src)
5088b671779SNikolay Aleksandrov {
509085b53c8SNikolay Aleksandrov 	struct net_bridge *br = src->pg->key.port->br;
5108b671779SNikolay Aleksandrov 
511b0812368SNikolay Aleksandrov 	br_multicast_fwd_src_remove(src);
5128b671779SNikolay Aleksandrov 	hlist_del_init_rcu(&src->node);
5138b671779SNikolay Aleksandrov 	src->pg->src_ents--;
514e12cec65SNikolay Aleksandrov 	hlist_add_head(&src->mcast_gc.gc_node, &br->mcast_gc_list);
515e12cec65SNikolay Aleksandrov 	queue_work(system_long_wq, &br->mcast_gc_work);
516e12cec65SNikolay Aleksandrov }
517e12cec65SNikolay Aleksandrov 
518e12cec65SNikolay Aleksandrov static void br_multicast_destroy_port_group(struct net_bridge_mcast_gc *gc)
519e12cec65SNikolay Aleksandrov {
520e12cec65SNikolay Aleksandrov 	struct net_bridge_port_group *pg;
521e12cec65SNikolay Aleksandrov 
522e12cec65SNikolay Aleksandrov 	pg = container_of(gc, struct net_bridge_port_group, mcast_gc);
523e12cec65SNikolay Aleksandrov 	WARN_ON(!hlist_unhashed(&pg->mglist));
524e12cec65SNikolay Aleksandrov 	WARN_ON(!hlist_empty(&pg->src_list));
525e12cec65SNikolay Aleksandrov 
526e12cec65SNikolay Aleksandrov 	del_timer_sync(&pg->rexmit_timer);
527e12cec65SNikolay Aleksandrov 	del_timer_sync(&pg->timer);
528e12cec65SNikolay Aleksandrov 	kfree_rcu(pg, rcu);
5298b671779SNikolay Aleksandrov }
5308b671779SNikolay Aleksandrov 
531681590bdSNikolay Aleksandrov void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
532681590bdSNikolay Aleksandrov 			 struct net_bridge_port_group *pg,
533681590bdSNikolay Aleksandrov 			 struct net_bridge_port_group __rcu **pp)
534681590bdSNikolay Aleksandrov {
535085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
5368b671779SNikolay Aleksandrov 	struct net_bridge_group_src *ent;
5378b671779SNikolay Aleksandrov 	struct hlist_node *tmp;
538681590bdSNikolay Aleksandrov 
539085b53c8SNikolay Aleksandrov 	rhashtable_remove_fast(&br->sg_port_tbl, &pg->rhnode,
540085b53c8SNikolay Aleksandrov 			       br_sg_port_rht_params);
541681590bdSNikolay Aleksandrov 	rcu_assign_pointer(*pp, pg->next);
542681590bdSNikolay Aleksandrov 	hlist_del_init(&pg->mglist);
5438b671779SNikolay Aleksandrov 	hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
5448b671779SNikolay Aleksandrov 		br_multicast_del_group_src(ent);
54581f19838SNikolay Aleksandrov 	br_mdb_notify(br->dev, mp, pg, RTM_DELMDB);
5468266a049SNikolay Aleksandrov 	if (!br_multicast_is_star_g(&mp->addr))
5478266a049SNikolay Aleksandrov 		br_multicast_sg_del_exclude_ports(mp);
5488266a049SNikolay Aleksandrov 	else
5498266a049SNikolay Aleksandrov 		br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE);
550e12cec65SNikolay Aleksandrov 	hlist_add_head(&pg->mcast_gc.gc_node, &br->mcast_gc_list);
551e12cec65SNikolay Aleksandrov 	queue_work(system_long_wq, &br->mcast_gc_work);
552681590bdSNikolay Aleksandrov 
553681590bdSNikolay Aleksandrov 	if (!mp->ports && !mp->host_joined && netif_running(br->dev))
554681590bdSNikolay Aleksandrov 		mod_timer(&mp->timer, jiffies);
555681590bdSNikolay Aleksandrov }
556681590bdSNikolay Aleksandrov 
557681590bdSNikolay Aleksandrov static void br_multicast_find_del_pg(struct net_bridge *br,
558eb1d1641SHerbert Xu 				     struct net_bridge_port_group *pg)
559eb1d1641SHerbert Xu {
5608b671779SNikolay Aleksandrov 	struct net_bridge_port_group __rcu **pp;
561eb1d1641SHerbert Xu 	struct net_bridge_mdb_entry *mp;
562eb1d1641SHerbert Xu 	struct net_bridge_port_group *p;
563e8051688SEric Dumazet 
564085b53c8SNikolay Aleksandrov 	mp = br_mdb_ip_get(br, &pg->key.addr);
565eb1d1641SHerbert Xu 	if (WARN_ON(!mp))
566eb1d1641SHerbert Xu 		return;
567eb1d1641SHerbert Xu 
568e8051688SEric Dumazet 	for (pp = &mp->ports;
569e8051688SEric Dumazet 	     (p = mlock_dereference(*pp, br)) != NULL;
570e8051688SEric Dumazet 	     pp = &p->next) {
571eb1d1641SHerbert Xu 		if (p != pg)
572eb1d1641SHerbert Xu 			continue;
573eb1d1641SHerbert Xu 
574681590bdSNikolay Aleksandrov 		br_multicast_del_pg(mp, pg, pp);
575eb1d1641SHerbert Xu 		return;
576eb1d1641SHerbert Xu 	}
577eb1d1641SHerbert Xu 
578eb1d1641SHerbert Xu 	WARN_ON(1);
579eb1d1641SHerbert Xu }
580eb1d1641SHerbert Xu 
58188c1f37fSAllen Pais static void br_multicast_port_group_expired(struct timer_list *t)
582eb1d1641SHerbert Xu {
58388c1f37fSAllen Pais 	struct net_bridge_port_group *pg = from_timer(pg, t, timer);
584d6c33d67SNikolay Aleksandrov 	struct net_bridge_group_src *src_ent;
585085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
586d6c33d67SNikolay Aleksandrov 	struct hlist_node *tmp;
587d6c33d67SNikolay Aleksandrov 	bool changed;
588eb1d1641SHerbert Xu 
589eb1d1641SHerbert Xu 	spin_lock(&br->multicast_lock);
590eb1d1641SHerbert Xu 	if (!netif_running(br->dev) || timer_pending(&pg->timer) ||
5919d06b6d8SElad Raz 	    hlist_unhashed(&pg->mglist) || pg->flags & MDB_PG_FLAGS_PERMANENT)
592eb1d1641SHerbert Xu 		goto out;
593eb1d1641SHerbert Xu 
594d6c33d67SNikolay Aleksandrov 	changed = !!(pg->filter_mode == MCAST_EXCLUDE);
595d6c33d67SNikolay Aleksandrov 	pg->filter_mode = MCAST_INCLUDE;
596d6c33d67SNikolay Aleksandrov 	hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) {
597d6c33d67SNikolay Aleksandrov 		if (!timer_pending(&src_ent->timer)) {
598d6c33d67SNikolay Aleksandrov 			br_multicast_del_group_src(src_ent);
599d6c33d67SNikolay Aleksandrov 			changed = true;
600d6c33d67SNikolay Aleksandrov 		}
601d6c33d67SNikolay Aleksandrov 	}
602eb1d1641SHerbert Xu 
603d6c33d67SNikolay Aleksandrov 	if (hlist_empty(&pg->src_list)) {
604d6c33d67SNikolay Aleksandrov 		br_multicast_find_del_pg(br, pg);
605d6c33d67SNikolay Aleksandrov 	} else if (changed) {
606085b53c8SNikolay Aleksandrov 		struct net_bridge_mdb_entry *mp = br_mdb_ip_get(br, &pg->key.addr);
607d6c33d67SNikolay Aleksandrov 
6088266a049SNikolay Aleksandrov 		if (changed && br_multicast_is_star_g(&pg->key.addr))
6098266a049SNikolay Aleksandrov 			br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE);
6108266a049SNikolay Aleksandrov 
611d6c33d67SNikolay Aleksandrov 		if (WARN_ON(!mp))
612d6c33d67SNikolay Aleksandrov 			goto out;
613d6c33d67SNikolay Aleksandrov 		br_mdb_notify(br->dev, mp, pg, RTM_NEWMDB);
614d6c33d67SNikolay Aleksandrov 	}
615eb1d1641SHerbert Xu out:
616eb1d1641SHerbert Xu 	spin_unlock(&br->multicast_lock);
617eb1d1641SHerbert Xu }
618eb1d1641SHerbert Xu 
619e12cec65SNikolay Aleksandrov static void br_multicast_gc(struct hlist_head *head)
620e12cec65SNikolay Aleksandrov {
621e12cec65SNikolay Aleksandrov 	struct net_bridge_mcast_gc *gcent;
622e12cec65SNikolay Aleksandrov 	struct hlist_node *tmp;
623e12cec65SNikolay Aleksandrov 
624e12cec65SNikolay Aleksandrov 	hlist_for_each_entry_safe(gcent, tmp, head, gc_node) {
625e12cec65SNikolay Aleksandrov 		hlist_del_init(&gcent->gc_node);
626e12cec65SNikolay Aleksandrov 		gcent->destroy(gcent);
627e12cec65SNikolay Aleksandrov 	}
628e12cec65SNikolay Aleksandrov }
629e12cec65SNikolay Aleksandrov 
6308ef2a9a5SYOSHIFUJI Hideaki static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
631438ef2d0SNikolay Aleksandrov 						    struct net_bridge_port_group *pg,
632438ef2d0SNikolay Aleksandrov 						    __be32 ip_dst, __be32 group,
633438ef2d0SNikolay Aleksandrov 						    bool with_srcs, bool over_lmqt,
63442c11ccfSNikolay Aleksandrov 						    u8 sflag, u8 *igmp_type,
63542c11ccfSNikolay Aleksandrov 						    bool *need_rexmit)
636eb1d1641SHerbert Xu {
637085b53c8SNikolay Aleksandrov 	struct net_bridge_port *p = pg ? pg->key.port : NULL;
638438ef2d0SNikolay Aleksandrov 	struct net_bridge_group_src *ent;
639438ef2d0SNikolay Aleksandrov 	size_t pkt_size, igmp_hdr_size;
640438ef2d0SNikolay Aleksandrov 	unsigned long now = jiffies;
6415e923585SNikolay Aleksandrov 	struct igmpv3_query *ihv3;
642438ef2d0SNikolay Aleksandrov 	void *csum_start = NULL;
643438ef2d0SNikolay Aleksandrov 	__sum16 *csum = NULL;
644eb1d1641SHerbert Xu 	struct sk_buff *skb;
645eb1d1641SHerbert Xu 	struct igmphdr *ih;
646eb1d1641SHerbert Xu 	struct ethhdr *eth;
647438ef2d0SNikolay Aleksandrov 	unsigned long lmqt;
648eb1d1641SHerbert Xu 	struct iphdr *iph;
649438ef2d0SNikolay Aleksandrov 	u16 lmqt_srcs = 0;
650eb1d1641SHerbert Xu 
6515e923585SNikolay Aleksandrov 	igmp_hdr_size = sizeof(*ih);
652438ef2d0SNikolay Aleksandrov 	if (br->multicast_igmp_version == 3) {
6535e923585SNikolay Aleksandrov 		igmp_hdr_size = sizeof(*ihv3);
654438ef2d0SNikolay Aleksandrov 		if (pg && with_srcs) {
655438ef2d0SNikolay Aleksandrov 			lmqt = now + (br->multicast_last_member_interval *
656438ef2d0SNikolay Aleksandrov 				      br->multicast_last_member_count);
657438ef2d0SNikolay Aleksandrov 			hlist_for_each_entry(ent, &pg->src_list, node) {
658438ef2d0SNikolay Aleksandrov 				if (over_lmqt == time_after(ent->timer.expires,
659438ef2d0SNikolay Aleksandrov 							    lmqt) &&
660438ef2d0SNikolay Aleksandrov 				    ent->src_query_rexmit_cnt > 0)
661438ef2d0SNikolay Aleksandrov 					lmqt_srcs++;
662438ef2d0SNikolay Aleksandrov 			}
663438ef2d0SNikolay Aleksandrov 
664438ef2d0SNikolay Aleksandrov 			if (!lmqt_srcs)
665438ef2d0SNikolay Aleksandrov 				return NULL;
666438ef2d0SNikolay Aleksandrov 			igmp_hdr_size += lmqt_srcs * sizeof(__be32);
667438ef2d0SNikolay Aleksandrov 		}
668438ef2d0SNikolay Aleksandrov 	}
669438ef2d0SNikolay Aleksandrov 
670438ef2d0SNikolay Aleksandrov 	pkt_size = sizeof(*eth) + sizeof(*iph) + 4 + igmp_hdr_size;
671438ef2d0SNikolay Aleksandrov 	if ((p && pkt_size > p->dev->mtu) ||
672438ef2d0SNikolay Aleksandrov 	    pkt_size > br->dev->mtu)
673438ef2d0SNikolay Aleksandrov 		return NULL;
674438ef2d0SNikolay Aleksandrov 
675438ef2d0SNikolay Aleksandrov 	skb = netdev_alloc_skb_ip_align(br->dev, pkt_size);
676eb1d1641SHerbert Xu 	if (!skb)
677eb1d1641SHerbert Xu 		goto out;
678eb1d1641SHerbert Xu 
679eb1d1641SHerbert Xu 	skb->protocol = htons(ETH_P_IP);
680eb1d1641SHerbert Xu 
681eb1d1641SHerbert Xu 	skb_reset_mac_header(skb);
682eb1d1641SHerbert Xu 	eth = eth_hdr(skb);
683eb1d1641SHerbert Xu 
684e5a727f6SJoe Perches 	ether_addr_copy(eth->h_source, br->dev->dev_addr);
685438ef2d0SNikolay Aleksandrov 	ip_eth_mc_map(ip_dst, eth->h_dest);
686eb1d1641SHerbert Xu 	eth->h_proto = htons(ETH_P_IP);
687eb1d1641SHerbert Xu 	skb_put(skb, sizeof(*eth));
688eb1d1641SHerbert Xu 
689eb1d1641SHerbert Xu 	skb_set_network_header(skb, skb->len);
690eb1d1641SHerbert Xu 	iph = ip_hdr(skb);
691438ef2d0SNikolay Aleksandrov 	iph->tot_len = htons(pkt_size - sizeof(*eth));
692eb1d1641SHerbert Xu 
693eb1d1641SHerbert Xu 	iph->version = 4;
694eb1d1641SHerbert Xu 	iph->ihl = 6;
695eb1d1641SHerbert Xu 	iph->tos = 0xc0;
696eb1d1641SHerbert Xu 	iph->id = 0;
697eb1d1641SHerbert Xu 	iph->frag_off = htons(IP_DF);
698eb1d1641SHerbert Xu 	iph->ttl = 1;
699eb1d1641SHerbert Xu 	iph->protocol = IPPROTO_IGMP;
700675779adSNikolay Aleksandrov 	iph->saddr = br_opt_get(br, BROPT_MULTICAST_QUERY_USE_IFADDR) ?
7011c8ad5bfSCong Wang 		     inet_select_addr(br->dev, 0, RT_SCOPE_LINK) : 0;
702438ef2d0SNikolay Aleksandrov 	iph->daddr = ip_dst;
703eb1d1641SHerbert Xu 	((u8 *)&iph[1])[0] = IPOPT_RA;
704eb1d1641SHerbert Xu 	((u8 *)&iph[1])[1] = 4;
705eb1d1641SHerbert Xu 	((u8 *)&iph[1])[2] = 0;
706eb1d1641SHerbert Xu 	((u8 *)&iph[1])[3] = 0;
707eb1d1641SHerbert Xu 	ip_send_check(iph);
708eb1d1641SHerbert Xu 	skb_put(skb, 24);
709eb1d1641SHerbert Xu 
710eb1d1641SHerbert Xu 	skb_set_transport_header(skb, skb->len);
7111080ab95SNikolay Aleksandrov 	*igmp_type = IGMP_HOST_MEMBERSHIP_QUERY;
7125e923585SNikolay Aleksandrov 
7135e923585SNikolay Aleksandrov 	switch (br->multicast_igmp_version) {
7145e923585SNikolay Aleksandrov 	case 2:
7155e923585SNikolay Aleksandrov 		ih = igmp_hdr(skb);
716eb1d1641SHerbert Xu 		ih->type = IGMP_HOST_MEMBERSHIP_QUERY;
717eb1d1641SHerbert Xu 		ih->code = (group ? br->multicast_last_member_interval :
718eb1d1641SHerbert Xu 				    br->multicast_query_response_interval) /
719eb1d1641SHerbert Xu 			   (HZ / IGMP_TIMER_SCALE);
720eb1d1641SHerbert Xu 		ih->group = group;
721eb1d1641SHerbert Xu 		ih->csum = 0;
722438ef2d0SNikolay Aleksandrov 		csum = &ih->csum;
723438ef2d0SNikolay Aleksandrov 		csum_start = (void *)ih;
7245e923585SNikolay Aleksandrov 		break;
7255e923585SNikolay Aleksandrov 	case 3:
7265e923585SNikolay Aleksandrov 		ihv3 = igmpv3_query_hdr(skb);
7275e923585SNikolay Aleksandrov 		ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY;
7285e923585SNikolay Aleksandrov 		ihv3->code = (group ? br->multicast_last_member_interval :
7295e923585SNikolay Aleksandrov 				      br->multicast_query_response_interval) /
7305e923585SNikolay Aleksandrov 			     (HZ / IGMP_TIMER_SCALE);
7315e923585SNikolay Aleksandrov 		ihv3->group = group;
7325e923585SNikolay Aleksandrov 		ihv3->qqic = br->multicast_query_interval / HZ;
733438ef2d0SNikolay Aleksandrov 		ihv3->nsrcs = htons(lmqt_srcs);
7345e923585SNikolay Aleksandrov 		ihv3->resv = 0;
735438ef2d0SNikolay Aleksandrov 		ihv3->suppress = sflag;
7365e923585SNikolay Aleksandrov 		ihv3->qrv = 2;
7375e923585SNikolay Aleksandrov 		ihv3->csum = 0;
738438ef2d0SNikolay Aleksandrov 		csum = &ihv3->csum;
739438ef2d0SNikolay Aleksandrov 		csum_start = (void *)ihv3;
740438ef2d0SNikolay Aleksandrov 		if (!pg || !with_srcs)
741438ef2d0SNikolay Aleksandrov 			break;
742438ef2d0SNikolay Aleksandrov 
743438ef2d0SNikolay Aleksandrov 		lmqt_srcs = 0;
744438ef2d0SNikolay Aleksandrov 		hlist_for_each_entry(ent, &pg->src_list, node) {
745438ef2d0SNikolay Aleksandrov 			if (over_lmqt == time_after(ent->timer.expires,
746438ef2d0SNikolay Aleksandrov 						    lmqt) &&
747438ef2d0SNikolay Aleksandrov 			    ent->src_query_rexmit_cnt > 0) {
748deb96566SNikolay Aleksandrov 				ihv3->srcs[lmqt_srcs++] = ent->addr.src.ip4;
749438ef2d0SNikolay Aleksandrov 				ent->src_query_rexmit_cnt--;
75042c11ccfSNikolay Aleksandrov 				if (need_rexmit && ent->src_query_rexmit_cnt)
75142c11ccfSNikolay Aleksandrov 					*need_rexmit = true;
752438ef2d0SNikolay Aleksandrov 			}
753438ef2d0SNikolay Aleksandrov 		}
754438ef2d0SNikolay Aleksandrov 		if (WARN_ON(lmqt_srcs != ntohs(ihv3->nsrcs))) {
755438ef2d0SNikolay Aleksandrov 			kfree_skb(skb);
756438ef2d0SNikolay Aleksandrov 			return NULL;
757438ef2d0SNikolay Aleksandrov 		}
7585e923585SNikolay Aleksandrov 		break;
7595e923585SNikolay Aleksandrov 	}
760eb1d1641SHerbert Xu 
761438ef2d0SNikolay Aleksandrov 	if (WARN_ON(!csum || !csum_start)) {
762438ef2d0SNikolay Aleksandrov 		kfree_skb(skb);
763438ef2d0SNikolay Aleksandrov 		return NULL;
764438ef2d0SNikolay Aleksandrov 	}
765438ef2d0SNikolay Aleksandrov 
766438ef2d0SNikolay Aleksandrov 	*csum = ip_compute_csum(csum_start, igmp_hdr_size);
7675e923585SNikolay Aleksandrov 	skb_put(skb, igmp_hdr_size);
768eb1d1641SHerbert Xu 	__skb_pull(skb, sizeof(*eth));
769eb1d1641SHerbert Xu 
770eb1d1641SHerbert Xu out:
771eb1d1641SHerbert Xu 	return skb;
772eb1d1641SHerbert Xu }
773eb1d1641SHerbert Xu 
774dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
77508b202b6SYOSHIFUJI Hideaki static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
776438ef2d0SNikolay Aleksandrov 						    struct net_bridge_port_group *pg,
777438ef2d0SNikolay Aleksandrov 						    const struct in6_addr *ip6_dst,
778438ef2d0SNikolay Aleksandrov 						    const struct in6_addr *group,
779438ef2d0SNikolay Aleksandrov 						    bool with_srcs, bool over_llqt,
78042c11ccfSNikolay Aleksandrov 						    u8 sflag, u8 *igmp_type,
78142c11ccfSNikolay Aleksandrov 						    bool *need_rexmit)
78208b202b6SYOSHIFUJI Hideaki {
783085b53c8SNikolay Aleksandrov 	struct net_bridge_port *p = pg ? pg->key.port : NULL;
784438ef2d0SNikolay Aleksandrov 	struct net_bridge_group_src *ent;
785438ef2d0SNikolay Aleksandrov 	size_t pkt_size, mld_hdr_size;
786438ef2d0SNikolay Aleksandrov 	unsigned long now = jiffies;
787aa2ae3e7SNikolay Aleksandrov 	struct mld2_query *mld2q;
788438ef2d0SNikolay Aleksandrov 	void *csum_start = NULL;
789aa2ae3e7SNikolay Aleksandrov 	unsigned long interval;
790438ef2d0SNikolay Aleksandrov 	__sum16 *csum = NULL;
79108b202b6SYOSHIFUJI Hideaki 	struct ipv6hdr *ip6h;
79208b202b6SYOSHIFUJI Hideaki 	struct mld_msg *mldq;
793aa2ae3e7SNikolay Aleksandrov 	struct sk_buff *skb;
794438ef2d0SNikolay Aleksandrov 	unsigned long llqt;
79508b202b6SYOSHIFUJI Hideaki 	struct ethhdr *eth;
796438ef2d0SNikolay Aleksandrov 	u16 llqt_srcs = 0;
79708b202b6SYOSHIFUJI Hideaki 	u8 *hopopt;
79808b202b6SYOSHIFUJI Hideaki 
799aa2ae3e7SNikolay Aleksandrov 	mld_hdr_size = sizeof(*mldq);
800438ef2d0SNikolay Aleksandrov 	if (br->multicast_mld_version == 2) {
801aa2ae3e7SNikolay Aleksandrov 		mld_hdr_size = sizeof(*mld2q);
802438ef2d0SNikolay Aleksandrov 		if (pg && with_srcs) {
803438ef2d0SNikolay Aleksandrov 			llqt = now + (br->multicast_last_member_interval *
804438ef2d0SNikolay Aleksandrov 				      br->multicast_last_member_count);
805438ef2d0SNikolay Aleksandrov 			hlist_for_each_entry(ent, &pg->src_list, node) {
806438ef2d0SNikolay Aleksandrov 				if (over_llqt == time_after(ent->timer.expires,
807438ef2d0SNikolay Aleksandrov 							    llqt) &&
808438ef2d0SNikolay Aleksandrov 				    ent->src_query_rexmit_cnt > 0)
809438ef2d0SNikolay Aleksandrov 					llqt_srcs++;
810438ef2d0SNikolay Aleksandrov 			}
811438ef2d0SNikolay Aleksandrov 
812438ef2d0SNikolay Aleksandrov 			if (!llqt_srcs)
813438ef2d0SNikolay Aleksandrov 				return NULL;
814438ef2d0SNikolay Aleksandrov 			mld_hdr_size += llqt_srcs * sizeof(struct in6_addr);
815438ef2d0SNikolay Aleksandrov 		}
816438ef2d0SNikolay Aleksandrov 	}
817438ef2d0SNikolay Aleksandrov 
818438ef2d0SNikolay Aleksandrov 	pkt_size = sizeof(*eth) + sizeof(*ip6h) + 8 + mld_hdr_size;
819438ef2d0SNikolay Aleksandrov 	if ((p && pkt_size > p->dev->mtu) ||
820438ef2d0SNikolay Aleksandrov 	    pkt_size > br->dev->mtu)
821438ef2d0SNikolay Aleksandrov 		return NULL;
822438ef2d0SNikolay Aleksandrov 
823438ef2d0SNikolay Aleksandrov 	skb = netdev_alloc_skb_ip_align(br->dev, pkt_size);
82408b202b6SYOSHIFUJI Hideaki 	if (!skb)
82508b202b6SYOSHIFUJI Hideaki 		goto out;
82608b202b6SYOSHIFUJI Hideaki 
82708b202b6SYOSHIFUJI Hideaki 	skb->protocol = htons(ETH_P_IPV6);
82808b202b6SYOSHIFUJI Hideaki 
82908b202b6SYOSHIFUJI Hideaki 	/* Ethernet header */
83008b202b6SYOSHIFUJI Hideaki 	skb_reset_mac_header(skb);
83108b202b6SYOSHIFUJI Hideaki 	eth = eth_hdr(skb);
83208b202b6SYOSHIFUJI Hideaki 
833e5a727f6SJoe Perches 	ether_addr_copy(eth->h_source, br->dev->dev_addr);
83408b202b6SYOSHIFUJI Hideaki 	eth->h_proto = htons(ETH_P_IPV6);
83508b202b6SYOSHIFUJI Hideaki 	skb_put(skb, sizeof(*eth));
83608b202b6SYOSHIFUJI Hideaki 
83708b202b6SYOSHIFUJI Hideaki 	/* IPv6 header + HbH option */
83808b202b6SYOSHIFUJI Hideaki 	skb_set_network_header(skb, skb->len);
83908b202b6SYOSHIFUJI Hideaki 	ip6h = ipv6_hdr(skb);
84008b202b6SYOSHIFUJI Hideaki 
84108b202b6SYOSHIFUJI Hideaki 	*(__force __be32 *)ip6h = htonl(0x60000000);
842aa2ae3e7SNikolay Aleksandrov 	ip6h->payload_len = htons(8 + mld_hdr_size);
84308b202b6SYOSHIFUJI Hideaki 	ip6h->nexthdr = IPPROTO_HOPOPTS;
84408b202b6SYOSHIFUJI Hideaki 	ip6h->hop_limit = 1;
845438ef2d0SNikolay Aleksandrov 	ip6h->daddr = *ip6_dst;
846d1d81d4cSUlrich Weber 	if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0,
847d1d81d4cSUlrich Weber 			       &ip6h->saddr)) {
848d1d81d4cSUlrich Weber 		kfree_skb(skb);
849675779adSNikolay Aleksandrov 		br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, false);
850d1d81d4cSUlrich Weber 		return NULL;
851d1d81d4cSUlrich Weber 	}
8520888d5f3Sdaniel 
853675779adSNikolay Aleksandrov 	br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, true);
85436cff5a1SLinus Lüssing 	ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
85508b202b6SYOSHIFUJI Hideaki 
85608b202b6SYOSHIFUJI Hideaki 	hopopt = (u8 *)(ip6h + 1);
85708b202b6SYOSHIFUJI Hideaki 	hopopt[0] = IPPROTO_ICMPV6;		/* next hdr */
85808b202b6SYOSHIFUJI Hideaki 	hopopt[1] = 0;				/* length of HbH */
85908b202b6SYOSHIFUJI Hideaki 	hopopt[2] = IPV6_TLV_ROUTERALERT;	/* Router Alert */
86008b202b6SYOSHIFUJI Hideaki 	hopopt[3] = 2;				/* Length of RA Option */
86108b202b6SYOSHIFUJI Hideaki 	hopopt[4] = 0;				/* Type = 0x0000 (MLD) */
86208b202b6SYOSHIFUJI Hideaki 	hopopt[5] = 0;
8631de5a71cSEldad Zack 	hopopt[6] = IPV6_TLV_PAD1;		/* Pad1 */
8641de5a71cSEldad Zack 	hopopt[7] = IPV6_TLV_PAD1;		/* Pad1 */
86508b202b6SYOSHIFUJI Hideaki 
86608b202b6SYOSHIFUJI Hideaki 	skb_put(skb, sizeof(*ip6h) + 8);
86708b202b6SYOSHIFUJI Hideaki 
86808b202b6SYOSHIFUJI Hideaki 	/* ICMPv6 */
86908b202b6SYOSHIFUJI Hideaki 	skb_set_transport_header(skb, skb->len);
870438ef2d0SNikolay Aleksandrov 	interval = ipv6_addr_any(group) ?
87132de868cSLinus Lüssing 			br->multicast_query_response_interval :
87232de868cSLinus Lüssing 			br->multicast_last_member_interval;
8731080ab95SNikolay Aleksandrov 	*igmp_type = ICMPV6_MGM_QUERY;
874aa2ae3e7SNikolay Aleksandrov 	switch (br->multicast_mld_version) {
875aa2ae3e7SNikolay Aleksandrov 	case 1:
876aa2ae3e7SNikolay Aleksandrov 		mldq = (struct mld_msg *)icmp6_hdr(skb);
87708b202b6SYOSHIFUJI Hideaki 		mldq->mld_type = ICMPV6_MGM_QUERY;
87808b202b6SYOSHIFUJI Hideaki 		mldq->mld_code = 0;
87908b202b6SYOSHIFUJI Hideaki 		mldq->mld_cksum = 0;
88008b202b6SYOSHIFUJI Hideaki 		mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval));
88108b202b6SYOSHIFUJI Hideaki 		mldq->mld_reserved = 0;
882438ef2d0SNikolay Aleksandrov 		mldq->mld_mca = *group;
883438ef2d0SNikolay Aleksandrov 		csum = &mldq->mld_cksum;
884438ef2d0SNikolay Aleksandrov 		csum_start = (void *)mldq;
885aa2ae3e7SNikolay Aleksandrov 		break;
886aa2ae3e7SNikolay Aleksandrov 	case 2:
887aa2ae3e7SNikolay Aleksandrov 		mld2q = (struct mld2_query *)icmp6_hdr(skb);
88853631a5fSLance Richardson 		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval));
889aa2ae3e7SNikolay Aleksandrov 		mld2q->mld2q_type = ICMPV6_MGM_QUERY;
890aa2ae3e7SNikolay Aleksandrov 		mld2q->mld2q_code = 0;
891aa2ae3e7SNikolay Aleksandrov 		mld2q->mld2q_cksum = 0;
892aa2ae3e7SNikolay Aleksandrov 		mld2q->mld2q_resv1 = 0;
893aa2ae3e7SNikolay Aleksandrov 		mld2q->mld2q_resv2 = 0;
894438ef2d0SNikolay Aleksandrov 		mld2q->mld2q_suppress = sflag;
895aa2ae3e7SNikolay Aleksandrov 		mld2q->mld2q_qrv = 2;
896438ef2d0SNikolay Aleksandrov 		mld2q->mld2q_nsrcs = htons(llqt_srcs);
897aa2ae3e7SNikolay Aleksandrov 		mld2q->mld2q_qqic = br->multicast_query_interval / HZ;
898438ef2d0SNikolay Aleksandrov 		mld2q->mld2q_mca = *group;
899438ef2d0SNikolay Aleksandrov 		csum = &mld2q->mld2q_cksum;
900438ef2d0SNikolay Aleksandrov 		csum_start = (void *)mld2q;
901438ef2d0SNikolay Aleksandrov 		if (!pg || !with_srcs)
902438ef2d0SNikolay Aleksandrov 			break;
903438ef2d0SNikolay Aleksandrov 
904438ef2d0SNikolay Aleksandrov 		llqt_srcs = 0;
905438ef2d0SNikolay Aleksandrov 		hlist_for_each_entry(ent, &pg->src_list, node) {
906438ef2d0SNikolay Aleksandrov 			if (over_llqt == time_after(ent->timer.expires,
907438ef2d0SNikolay Aleksandrov 						    llqt) &&
908438ef2d0SNikolay Aleksandrov 			    ent->src_query_rexmit_cnt > 0) {
909deb96566SNikolay Aleksandrov 				mld2q->mld2q_srcs[llqt_srcs++] = ent->addr.src.ip6;
910438ef2d0SNikolay Aleksandrov 				ent->src_query_rexmit_cnt--;
91142c11ccfSNikolay Aleksandrov 				if (need_rexmit && ent->src_query_rexmit_cnt)
91242c11ccfSNikolay Aleksandrov 					*need_rexmit = true;
913438ef2d0SNikolay Aleksandrov 			}
914438ef2d0SNikolay Aleksandrov 		}
915438ef2d0SNikolay Aleksandrov 		if (WARN_ON(llqt_srcs != ntohs(mld2q->mld2q_nsrcs))) {
916438ef2d0SNikolay Aleksandrov 			kfree_skb(skb);
917438ef2d0SNikolay Aleksandrov 			return NULL;
918438ef2d0SNikolay Aleksandrov 		}
919aa2ae3e7SNikolay Aleksandrov 		break;
920aa2ae3e7SNikolay Aleksandrov 	}
92108b202b6SYOSHIFUJI Hideaki 
922438ef2d0SNikolay Aleksandrov 	if (WARN_ON(!csum || !csum_start)) {
923438ef2d0SNikolay Aleksandrov 		kfree_skb(skb);
924438ef2d0SNikolay Aleksandrov 		return NULL;
925438ef2d0SNikolay Aleksandrov 	}
926438ef2d0SNikolay Aleksandrov 
927438ef2d0SNikolay Aleksandrov 	*csum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, mld_hdr_size,
928438ef2d0SNikolay Aleksandrov 				IPPROTO_ICMPV6,
929438ef2d0SNikolay Aleksandrov 				csum_partial(csum_start, mld_hdr_size, 0));
930438ef2d0SNikolay Aleksandrov 	skb_put(skb, mld_hdr_size);
93108b202b6SYOSHIFUJI Hideaki 	__skb_pull(skb, sizeof(*eth));
93208b202b6SYOSHIFUJI Hideaki 
93308b202b6SYOSHIFUJI Hideaki out:
93408b202b6SYOSHIFUJI Hideaki 	return skb;
93508b202b6SYOSHIFUJI Hideaki }
93608b202b6SYOSHIFUJI Hideaki #endif
93708b202b6SYOSHIFUJI Hideaki 
9388ef2a9a5SYOSHIFUJI Hideaki static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
939438ef2d0SNikolay Aleksandrov 						struct net_bridge_port_group *pg,
940438ef2d0SNikolay Aleksandrov 						struct br_ip *ip_dst,
941438ef2d0SNikolay Aleksandrov 						struct br_ip *group,
942438ef2d0SNikolay Aleksandrov 						bool with_srcs, bool over_lmqt,
94342c11ccfSNikolay Aleksandrov 						u8 sflag, u8 *igmp_type,
94442c11ccfSNikolay Aleksandrov 						bool *need_rexmit)
9458ef2a9a5SYOSHIFUJI Hideaki {
946438ef2d0SNikolay Aleksandrov 	__be32 ip4_dst;
947438ef2d0SNikolay Aleksandrov 
948438ef2d0SNikolay Aleksandrov 	switch (group->proto) {
9498ef2a9a5SYOSHIFUJI Hideaki 	case htons(ETH_P_IP):
950eab3227bSNikolay Aleksandrov 		ip4_dst = ip_dst ? ip_dst->dst.ip4 : htonl(INADDR_ALLHOSTS_GROUP);
951438ef2d0SNikolay Aleksandrov 		return br_ip4_multicast_alloc_query(br, pg,
952eab3227bSNikolay Aleksandrov 						    ip4_dst, group->dst.ip4,
953438ef2d0SNikolay Aleksandrov 						    with_srcs, over_lmqt,
95442c11ccfSNikolay Aleksandrov 						    sflag, igmp_type,
95542c11ccfSNikolay Aleksandrov 						    need_rexmit);
956dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
957438ef2d0SNikolay Aleksandrov 	case htons(ETH_P_IPV6): {
958438ef2d0SNikolay Aleksandrov 		struct in6_addr ip6_dst;
959438ef2d0SNikolay Aleksandrov 
960438ef2d0SNikolay Aleksandrov 		if (ip_dst)
961eab3227bSNikolay Aleksandrov 			ip6_dst = ip_dst->dst.ip6;
962438ef2d0SNikolay Aleksandrov 		else
963438ef2d0SNikolay Aleksandrov 			ipv6_addr_set(&ip6_dst, htonl(0xff020000), 0, 0,
964438ef2d0SNikolay Aleksandrov 				      htonl(1));
965438ef2d0SNikolay Aleksandrov 
966438ef2d0SNikolay Aleksandrov 		return br_ip6_multicast_alloc_query(br, pg,
967eab3227bSNikolay Aleksandrov 						    &ip6_dst, &group->dst.ip6,
968438ef2d0SNikolay Aleksandrov 						    with_srcs, over_lmqt,
96942c11ccfSNikolay Aleksandrov 						    sflag, igmp_type,
97042c11ccfSNikolay Aleksandrov 						    need_rexmit);
971438ef2d0SNikolay Aleksandrov 	}
97208b202b6SYOSHIFUJI Hideaki #endif
9738ef2a9a5SYOSHIFUJI Hideaki 	}
9748ef2a9a5SYOSHIFUJI Hideaki 	return NULL;
9758ef2a9a5SYOSHIFUJI Hideaki }
9768ef2a9a5SYOSHIFUJI Hideaki 
977cfd56754SCong Wang struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
9785e923585SNikolay Aleksandrov 						    struct br_ip *group)
979eb1d1641SHerbert Xu {
980eb1d1641SHerbert Xu 	struct net_bridge_mdb_entry *mp;
9814c0833bcSTobias Klauser 	int err;
982eb1d1641SHerbert Xu 
98319e3a9c9SNikolay Aleksandrov 	mp = br_mdb_ip_get(br, group);
98419e3a9c9SNikolay Aleksandrov 	if (mp)
98519e3a9c9SNikolay Aleksandrov 		return mp;
986eb1d1641SHerbert Xu 
98719e3a9c9SNikolay Aleksandrov 	if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) {
98819e3a9c9SNikolay Aleksandrov 		br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
98919e3a9c9SNikolay Aleksandrov 		return ERR_PTR(-E2BIG);
990eb1d1641SHerbert Xu 	}
991eb1d1641SHerbert Xu 
992eb1d1641SHerbert Xu 	mp = kzalloc(sizeof(*mp), GFP_ATOMIC);
993eb1d1641SHerbert Xu 	if (unlikely(!mp))
9944c0833bcSTobias Klauser 		return ERR_PTR(-ENOMEM);
995eb1d1641SHerbert Xu 
996eb1d1641SHerbert Xu 	mp->br = br;
9978ef2a9a5SYOSHIFUJI Hideaki 	mp->addr = *group;
998e12cec65SNikolay Aleksandrov 	mp->mcast_gc.destroy = br_multicast_destroy_mdb_entry;
99988c1f37fSAllen Pais 	timer_setup(&mp->timer, br_multicast_group_expired, 0);
100019e3a9c9SNikolay Aleksandrov 	err = rhashtable_lookup_insert_fast(&br->mdb_hash_tbl, &mp->rhnode,
100119e3a9c9SNikolay Aleksandrov 					    br_mdb_rht_params);
100219e3a9c9SNikolay Aleksandrov 	if (err) {
100319e3a9c9SNikolay Aleksandrov 		kfree(mp);
100419e3a9c9SNikolay Aleksandrov 		mp = ERR_PTR(err);
100519e3a9c9SNikolay Aleksandrov 	} else {
100619e3a9c9SNikolay Aleksandrov 		hlist_add_head_rcu(&mp->mdb_node, &br->mdb_list);
100719e3a9c9SNikolay Aleksandrov 	}
10081faabf2aSEric Dumazet 
1009eb1d1641SHerbert Xu 	return mp;
1010eb1d1641SHerbert Xu }
1011eb1d1641SHerbert Xu 
10128b671779SNikolay Aleksandrov static void br_multicast_group_src_expired(struct timer_list *t)
10138b671779SNikolay Aleksandrov {
10148b671779SNikolay Aleksandrov 	struct net_bridge_group_src *src = from_timer(src, t, timer);
10158b671779SNikolay Aleksandrov 	struct net_bridge_port_group *pg;
10168b671779SNikolay Aleksandrov 	struct net_bridge *br = src->br;
10178b671779SNikolay Aleksandrov 
10188b671779SNikolay Aleksandrov 	spin_lock(&br->multicast_lock);
10198b671779SNikolay Aleksandrov 	if (hlist_unhashed(&src->node) || !netif_running(br->dev) ||
10208b671779SNikolay Aleksandrov 	    timer_pending(&src->timer))
10218b671779SNikolay Aleksandrov 		goto out;
10228b671779SNikolay Aleksandrov 
10238b671779SNikolay Aleksandrov 	pg = src->pg;
10248b671779SNikolay Aleksandrov 	if (pg->filter_mode == MCAST_INCLUDE) {
10258b671779SNikolay Aleksandrov 		br_multicast_del_group_src(src);
10268b671779SNikolay Aleksandrov 		if (!hlist_empty(&pg->src_list))
10278b671779SNikolay Aleksandrov 			goto out;
10288b671779SNikolay Aleksandrov 		br_multicast_find_del_pg(br, pg);
1029*9116ffbfSNikolay Aleksandrov 	} else {
1030*9116ffbfSNikolay Aleksandrov 		br_multicast_fwd_src_handle(src);
10318b671779SNikolay Aleksandrov 	}
1032*9116ffbfSNikolay Aleksandrov 
10338b671779SNikolay Aleksandrov out:
10348b671779SNikolay Aleksandrov 	spin_unlock(&br->multicast_lock);
10358b671779SNikolay Aleksandrov }
10368b671779SNikolay Aleksandrov 
10378b671779SNikolay Aleksandrov static struct net_bridge_group_src *
10388b671779SNikolay Aleksandrov br_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip)
10398b671779SNikolay Aleksandrov {
10408b671779SNikolay Aleksandrov 	struct net_bridge_group_src *ent;
10418b671779SNikolay Aleksandrov 
10428b671779SNikolay Aleksandrov 	switch (ip->proto) {
10438b671779SNikolay Aleksandrov 	case htons(ETH_P_IP):
10448b671779SNikolay Aleksandrov 		hlist_for_each_entry(ent, &pg->src_list, node)
1045deb96566SNikolay Aleksandrov 			if (ip->src.ip4 == ent->addr.src.ip4)
10468b671779SNikolay Aleksandrov 				return ent;
10478b671779SNikolay Aleksandrov 		break;
10488b671779SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
10498b671779SNikolay Aleksandrov 	case htons(ETH_P_IPV6):
10508b671779SNikolay Aleksandrov 		hlist_for_each_entry(ent, &pg->src_list, node)
1051deb96566SNikolay Aleksandrov 			if (!ipv6_addr_cmp(&ent->addr.src.ip6, &ip->src.ip6))
10528b671779SNikolay Aleksandrov 				return ent;
10538b671779SNikolay Aleksandrov 		break;
10548b671779SNikolay Aleksandrov #endif
10558b671779SNikolay Aleksandrov 	}
10568b671779SNikolay Aleksandrov 
10578b671779SNikolay Aleksandrov 	return NULL;
10588b671779SNikolay Aleksandrov }
10598b671779SNikolay Aleksandrov 
10608b671779SNikolay Aleksandrov static struct net_bridge_group_src *
10618b671779SNikolay Aleksandrov br_multicast_new_group_src(struct net_bridge_port_group *pg, struct br_ip *src_ip)
10628b671779SNikolay Aleksandrov {
10638b671779SNikolay Aleksandrov 	struct net_bridge_group_src *grp_src;
10648b671779SNikolay Aleksandrov 
10658b671779SNikolay Aleksandrov 	if (unlikely(pg->src_ents >= PG_SRC_ENT_LIMIT))
10668b671779SNikolay Aleksandrov 		return NULL;
10678b671779SNikolay Aleksandrov 
10688b671779SNikolay Aleksandrov 	switch (src_ip->proto) {
10698b671779SNikolay Aleksandrov 	case htons(ETH_P_IP):
1070deb96566SNikolay Aleksandrov 		if (ipv4_is_zeronet(src_ip->src.ip4) ||
1071deb96566SNikolay Aleksandrov 		    ipv4_is_multicast(src_ip->src.ip4))
10728b671779SNikolay Aleksandrov 			return NULL;
10738b671779SNikolay Aleksandrov 		break;
10748b671779SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
10758b671779SNikolay Aleksandrov 	case htons(ETH_P_IPV6):
1076deb96566SNikolay Aleksandrov 		if (ipv6_addr_any(&src_ip->src.ip6) ||
1077deb96566SNikolay Aleksandrov 		    ipv6_addr_is_multicast(&src_ip->src.ip6))
10788b671779SNikolay Aleksandrov 			return NULL;
10798b671779SNikolay Aleksandrov 		break;
10808b671779SNikolay Aleksandrov #endif
10818b671779SNikolay Aleksandrov 	}
10828b671779SNikolay Aleksandrov 
10838b671779SNikolay Aleksandrov 	grp_src = kzalloc(sizeof(*grp_src), GFP_ATOMIC);
10848b671779SNikolay Aleksandrov 	if (unlikely(!grp_src))
10858b671779SNikolay Aleksandrov 		return NULL;
10868b671779SNikolay Aleksandrov 
10878b671779SNikolay Aleksandrov 	grp_src->pg = pg;
1088085b53c8SNikolay Aleksandrov 	grp_src->br = pg->key.port->br;
10898b671779SNikolay Aleksandrov 	grp_src->addr = *src_ip;
1090e12cec65SNikolay Aleksandrov 	grp_src->mcast_gc.destroy = br_multicast_destroy_group_src;
10918b671779SNikolay Aleksandrov 	timer_setup(&grp_src->timer, br_multicast_group_src_expired, 0);
10928b671779SNikolay Aleksandrov 
10938b671779SNikolay Aleksandrov 	hlist_add_head_rcu(&grp_src->node, &pg->src_list);
10948b671779SNikolay Aleksandrov 	pg->src_ents++;
10958b671779SNikolay Aleksandrov 
10968b671779SNikolay Aleksandrov 	return grp_src;
10978b671779SNikolay Aleksandrov }
10988b671779SNikolay Aleksandrov 
1099cfd56754SCong Wang struct net_bridge_port_group *br_multicast_new_port_group(
1100cfd56754SCong Wang 			struct net_bridge_port *port,
1101cfd56754SCong Wang 			struct br_ip *group,
1102ccb1c31aSAmerigo Wang 			struct net_bridge_port_group __rcu *next,
11036db6f0eaSFelix Fietkau 			unsigned char flags,
11048b671779SNikolay Aleksandrov 			const unsigned char *src,
11058f8cb77eSNikolay Aleksandrov 			u8 filter_mode,
11068f8cb77eSNikolay Aleksandrov 			u8 rt_protocol)
1107cfd56754SCong Wang {
1108cfd56754SCong Wang 	struct net_bridge_port_group *p;
1109cfd56754SCong Wang 
1110cfd56754SCong Wang 	p = kzalloc(sizeof(*p), GFP_ATOMIC);
1111cfd56754SCong Wang 	if (unlikely(!p))
1112cfd56754SCong Wang 		return NULL;
1113cfd56754SCong Wang 
1114085b53c8SNikolay Aleksandrov 	p->key.addr = *group;
1115085b53c8SNikolay Aleksandrov 	p->key.port = port;
11169d06b6d8SElad Raz 	p->flags = flags;
11178b671779SNikolay Aleksandrov 	p->filter_mode = filter_mode;
11188f8cb77eSNikolay Aleksandrov 	p->rt_protocol = rt_protocol;
1119e12cec65SNikolay Aleksandrov 	p->mcast_gc.destroy = br_multicast_destroy_port_group;
11208b671779SNikolay Aleksandrov 	INIT_HLIST_HEAD(&p->src_list);
1121085b53c8SNikolay Aleksandrov 
1122085b53c8SNikolay Aleksandrov 	if (!br_multicast_is_star_g(group) &&
1123085b53c8SNikolay Aleksandrov 	    rhashtable_lookup_insert_fast(&port->br->sg_port_tbl, &p->rhnode,
1124085b53c8SNikolay Aleksandrov 					  br_sg_port_rht_params)) {
1125085b53c8SNikolay Aleksandrov 		kfree(p);
1126085b53c8SNikolay Aleksandrov 		return NULL;
1127085b53c8SNikolay Aleksandrov 	}
1128085b53c8SNikolay Aleksandrov 
1129eca2a43bSstephen hemminger 	rcu_assign_pointer(p->next, next);
113088c1f37fSAllen Pais 	timer_setup(&p->timer, br_multicast_port_group_expired, 0);
113142c11ccfSNikolay Aleksandrov 	timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0);
113242c11ccfSNikolay Aleksandrov 	hlist_add_head(&p->mglist, &port->mglist);
11336db6f0eaSFelix Fietkau 
11346db6f0eaSFelix Fietkau 	if (src)
11356db6f0eaSFelix Fietkau 		memcpy(p->eth_addr, src, ETH_ALEN);
11366db6f0eaSFelix Fietkau 	else
11371bfe45f4SMao Wenan 		eth_broadcast_addr(p->eth_addr);
11386db6f0eaSFelix Fietkau 
1139cfd56754SCong Wang 	return p;
1140cfd56754SCong Wang }
1141cfd56754SCong Wang 
11421bc844eeSNikolay Aleksandrov void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify)
11431bc844eeSNikolay Aleksandrov {
11441bc844eeSNikolay Aleksandrov 	if (!mp->host_joined) {
11451bc844eeSNikolay Aleksandrov 		mp->host_joined = true;
11461bc844eeSNikolay Aleksandrov 		if (notify)
114781f19838SNikolay Aleksandrov 			br_mdb_notify(mp->br->dev, mp, NULL, RTM_NEWMDB);
11481bc844eeSNikolay Aleksandrov 	}
11491bc844eeSNikolay Aleksandrov 	mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval);
11501bc844eeSNikolay Aleksandrov }
11511bc844eeSNikolay Aleksandrov 
11521bc844eeSNikolay Aleksandrov void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify)
11531bc844eeSNikolay Aleksandrov {
11541bc844eeSNikolay Aleksandrov 	if (!mp->host_joined)
11551bc844eeSNikolay Aleksandrov 		return;
11561bc844eeSNikolay Aleksandrov 
11571bc844eeSNikolay Aleksandrov 	mp->host_joined = false;
11581bc844eeSNikolay Aleksandrov 	if (notify)
115981f19838SNikolay Aleksandrov 		br_mdb_notify(mp->br->dev, mp, NULL, RTM_DELMDB);
11601bc844eeSNikolay Aleksandrov }
11611bc844eeSNikolay Aleksandrov 
1162b0812368SNikolay Aleksandrov static struct net_bridge_port_group *
1163b0812368SNikolay Aleksandrov __br_multicast_add_group(struct net_bridge *br,
11648ef2a9a5SYOSHIFUJI Hideaki 			 struct net_bridge_port *port,
11656db6f0eaSFelix Fietkau 			 struct br_ip *group,
11668b671779SNikolay Aleksandrov 			 const unsigned char *src,
11670436862eSNikolay Aleksandrov 			 u8 filter_mode,
1168*9116ffbfSNikolay Aleksandrov 			 bool igmpv2_mldv1,
1169*9116ffbfSNikolay Aleksandrov 			 bool blocked)
1170eb1d1641SHerbert Xu {
1171e8051688SEric Dumazet 	struct net_bridge_port_group __rcu **pp;
1172b0812368SNikolay Aleksandrov 	struct net_bridge_port_group *p = NULL;
11735e923585SNikolay Aleksandrov 	struct net_bridge_mdb_entry *mp;
1174454594f3SLinus Lüssing 	unsigned long now = jiffies;
1175eb1d1641SHerbert Xu 
1176eb1d1641SHerbert Xu 	if (!netif_running(br->dev) ||
1177eb1d1641SHerbert Xu 	    (port && port->state == BR_STATE_DISABLED))
1178eb1d1641SHerbert Xu 		goto out;
1179eb1d1641SHerbert Xu 
118019e3a9c9SNikolay Aleksandrov 	mp = br_multicast_new_group(br, group);
11814c0833bcSTobias Klauser 	if (IS_ERR(mp))
1182b0812368SNikolay Aleksandrov 		return ERR_PTR(PTR_ERR(mp));
1183eb1d1641SHerbert Xu 
1184eb1d1641SHerbert Xu 	if (!port) {
11851bc844eeSNikolay Aleksandrov 		br_multicast_host_join(mp, true);
1186eb1d1641SHerbert Xu 		goto out;
1187eb1d1641SHerbert Xu 	}
1188eb1d1641SHerbert Xu 
1189e8051688SEric Dumazet 	for (pp = &mp->ports;
1190e8051688SEric Dumazet 	     (p = mlock_dereference(*pp, br)) != NULL;
1191e8051688SEric Dumazet 	     pp = &p->next) {
11926db6f0eaSFelix Fietkau 		if (br_port_group_equal(p, port, src))
1193454594f3SLinus Lüssing 			goto found;
1194085b53c8SNikolay Aleksandrov 		if ((unsigned long)p->key.port < (unsigned long)port)
1195eb1d1641SHerbert Xu 			break;
1196eb1d1641SHerbert Xu 	}
1197eb1d1641SHerbert Xu 
11988f8cb77eSNikolay Aleksandrov 	p = br_multicast_new_port_group(port, group, *pp, 0, src, filter_mode,
11998f8cb77eSNikolay Aleksandrov 					RTPROT_KERNEL);
1200b0812368SNikolay Aleksandrov 	if (unlikely(!p)) {
1201b0812368SNikolay Aleksandrov 		p = ERR_PTR(-ENOMEM);
1202b0812368SNikolay Aleksandrov 		goto out;
1203b0812368SNikolay Aleksandrov 	}
1204eb1d1641SHerbert Xu 	rcu_assign_pointer(*pp, p);
1205*9116ffbfSNikolay Aleksandrov 	if (blocked)
1206*9116ffbfSNikolay Aleksandrov 		p->flags |= MDB_PG_FLAGS_BLOCKED;
120781f19838SNikolay Aleksandrov 	br_mdb_notify(br->dev, mp, p, RTM_NEWMDB);
1208eb1d1641SHerbert Xu 
1209454594f3SLinus Lüssing found:
12100436862eSNikolay Aleksandrov 	if (igmpv2_mldv1)
1211454594f3SLinus Lüssing 		mod_timer(&p->timer, now + br->multicast_membership_interval);
121281f19838SNikolay Aleksandrov 
1213eb1d1641SHerbert Xu out:
1214b0812368SNikolay Aleksandrov 	return p;
1215b0812368SNikolay Aleksandrov }
1216eb1d1641SHerbert Xu 
1217b0812368SNikolay Aleksandrov static int br_multicast_add_group(struct net_bridge *br,
1218b0812368SNikolay Aleksandrov 				  struct net_bridge_port *port,
1219b0812368SNikolay Aleksandrov 				  struct br_ip *group,
1220b0812368SNikolay Aleksandrov 				  const unsigned char *src,
1221b0812368SNikolay Aleksandrov 				  u8 filter_mode,
1222b0812368SNikolay Aleksandrov 				  bool igmpv2_mldv1)
1223b0812368SNikolay Aleksandrov {
1224b0812368SNikolay Aleksandrov 	struct net_bridge_port_group *pg;
1225b0812368SNikolay Aleksandrov 	int err;
1226b0812368SNikolay Aleksandrov 
1227b0812368SNikolay Aleksandrov 	spin_lock(&br->multicast_lock);
1228b0812368SNikolay Aleksandrov 	pg = __br_multicast_add_group(br, port, group, src, filter_mode,
1229*9116ffbfSNikolay Aleksandrov 				      igmpv2_mldv1, false);
1230b0812368SNikolay Aleksandrov 	/* NULL is considered valid for host joined groups */
1231b0812368SNikolay Aleksandrov 	err = IS_ERR(pg) ? PTR_ERR(pg) : 0;
1232eb1d1641SHerbert Xu 	spin_unlock(&br->multicast_lock);
1233b0812368SNikolay Aleksandrov 
1234eb1d1641SHerbert Xu 	return err;
1235eb1d1641SHerbert Xu }
1236eb1d1641SHerbert Xu 
12378ef2a9a5SYOSHIFUJI Hideaki static int br_ip4_multicast_add_group(struct net_bridge *br,
12388ef2a9a5SYOSHIFUJI Hideaki 				      struct net_bridge_port *port,
1239b0e9a30dSVlad Yasevich 				      __be32 group,
12406db6f0eaSFelix Fietkau 				      __u16 vid,
12418b671779SNikolay Aleksandrov 				      const unsigned char *src,
12428b671779SNikolay Aleksandrov 				      bool igmpv2)
12438ef2a9a5SYOSHIFUJI Hideaki {
12448ef2a9a5SYOSHIFUJI Hideaki 	struct br_ip br_group;
12458b671779SNikolay Aleksandrov 	u8 filter_mode;
12468ef2a9a5SYOSHIFUJI Hideaki 
12478ef2a9a5SYOSHIFUJI Hideaki 	if (ipv4_is_local_multicast(group))
12488ef2a9a5SYOSHIFUJI Hideaki 		return 0;
12498ef2a9a5SYOSHIFUJI Hideaki 
12501515a63fSNikolay Aleksandrov 	memset(&br_group, 0, sizeof(br_group));
1251eab3227bSNikolay Aleksandrov 	br_group.dst.ip4 = group;
12528ef2a9a5SYOSHIFUJI Hideaki 	br_group.proto = htons(ETH_P_IP);
1253b0e9a30dSVlad Yasevich 	br_group.vid = vid;
12548b671779SNikolay Aleksandrov 	filter_mode = igmpv2 ? MCAST_EXCLUDE : MCAST_INCLUDE;
12558ef2a9a5SYOSHIFUJI Hideaki 
12560436862eSNikolay Aleksandrov 	return br_multicast_add_group(br, port, &br_group, src, filter_mode,
12570436862eSNikolay Aleksandrov 				      igmpv2);
12588ef2a9a5SYOSHIFUJI Hideaki }
12598ef2a9a5SYOSHIFUJI Hideaki 
1260dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
126108b202b6SYOSHIFUJI Hideaki static int br_ip6_multicast_add_group(struct net_bridge *br,
126208b202b6SYOSHIFUJI Hideaki 				      struct net_bridge_port *port,
1263b0e9a30dSVlad Yasevich 				      const struct in6_addr *group,
12646db6f0eaSFelix Fietkau 				      __u16 vid,
12658b671779SNikolay Aleksandrov 				      const unsigned char *src,
12668b671779SNikolay Aleksandrov 				      bool mldv1)
126708b202b6SYOSHIFUJI Hideaki {
126808b202b6SYOSHIFUJI Hideaki 	struct br_ip br_group;
12698b671779SNikolay Aleksandrov 	u8 filter_mode;
127008b202b6SYOSHIFUJI Hideaki 
12713c3769e6SLinus Lüssing 	if (ipv6_addr_is_ll_all_nodes(group))
127208b202b6SYOSHIFUJI Hideaki 		return 0;
127308b202b6SYOSHIFUJI Hideaki 
127419e3a9c9SNikolay Aleksandrov 	memset(&br_group, 0, sizeof(br_group));
1275eab3227bSNikolay Aleksandrov 	br_group.dst.ip6 = *group;
12769cc6e0c4SLinus Lüssing 	br_group.proto = htons(ETH_P_IPV6);
1277b0e9a30dSVlad Yasevich 	br_group.vid = vid;
12788b671779SNikolay Aleksandrov 	filter_mode = mldv1 ? MCAST_EXCLUDE : MCAST_INCLUDE;
127908b202b6SYOSHIFUJI Hideaki 
12800436862eSNikolay Aleksandrov 	return br_multicast_add_group(br, port, &br_group, src, filter_mode,
12810436862eSNikolay Aleksandrov 				      mldv1);
128208b202b6SYOSHIFUJI Hideaki }
128308b202b6SYOSHIFUJI Hideaki #endif
128408b202b6SYOSHIFUJI Hideaki 
128588c1f37fSAllen Pais static void br_multicast_router_expired(struct timer_list *t)
1286eb1d1641SHerbert Xu {
128788c1f37fSAllen Pais 	struct net_bridge_port *port =
128888c1f37fSAllen Pais 			from_timer(port, t, multicast_router_timer);
1289eb1d1641SHerbert Xu 	struct net_bridge *br = port->br;
1290eb1d1641SHerbert Xu 
1291eb1d1641SHerbert Xu 	spin_lock(&br->multicast_lock);
1292a55d8246SNikolay Aleksandrov 	if (port->multicast_router == MDB_RTR_TYPE_DISABLED ||
1293a55d8246SNikolay Aleksandrov 	    port->multicast_router == MDB_RTR_TYPE_PERM ||
1294f12e7d95SNogah Frankel 	    timer_pending(&port->multicast_router_timer))
1295eb1d1641SHerbert Xu 		goto out;
1296eb1d1641SHerbert Xu 
1297f12e7d95SNogah Frankel 	__del_port_router(port);
1298eb1d1641SHerbert Xu out:
1299eb1d1641SHerbert Xu 	spin_unlock(&br->multicast_lock);
1300eb1d1641SHerbert Xu }
1301eb1d1641SHerbert Xu 
130277041420SYotam Gigi static void br_mc_router_state_change(struct net_bridge *p,
130377041420SYotam Gigi 				      bool is_mc_router)
130477041420SYotam Gigi {
130577041420SYotam Gigi 	struct switchdev_attr attr = {
130677041420SYotam Gigi 		.orig_dev = p->dev,
130777041420SYotam Gigi 		.id = SWITCHDEV_ATTR_ID_BRIDGE_MROUTER,
130877041420SYotam Gigi 		.flags = SWITCHDEV_F_DEFER,
130977041420SYotam Gigi 		.u.mrouter = is_mc_router,
131077041420SYotam Gigi 	};
131177041420SYotam Gigi 
131277041420SYotam Gigi 	switchdev_port_attr_set(p->dev, &attr);
131377041420SYotam Gigi }
131477041420SYotam Gigi 
131588c1f37fSAllen Pais static void br_multicast_local_router_expired(struct timer_list *t)
1316eb1d1641SHerbert Xu {
131788c1f37fSAllen Pais 	struct net_bridge *br = from_timer(br, t, multicast_router_timer);
131877041420SYotam Gigi 
131977041420SYotam Gigi 	spin_lock(&br->multicast_lock);
132077041420SYotam Gigi 	if (br->multicast_router == MDB_RTR_TYPE_DISABLED ||
132177041420SYotam Gigi 	    br->multicast_router == MDB_RTR_TYPE_PERM ||
132277041420SYotam Gigi 	    timer_pending(&br->multicast_router_timer))
132377041420SYotam Gigi 		goto out;
132477041420SYotam Gigi 
132577041420SYotam Gigi 	br_mc_router_state_change(br, false);
132677041420SYotam Gigi out:
132777041420SYotam Gigi 	spin_unlock(&br->multicast_lock);
1328eb1d1641SHerbert Xu }
1329eb1d1641SHerbert Xu 
1330cc0fdd80SLinus Lüssing static void br_multicast_querier_expired(struct net_bridge *br,
133190010b36SLinus Lüssing 					 struct bridge_mcast_own_query *query)
1332c83b8fabSHerbert Xu {
1333c83b8fabSHerbert Xu 	spin_lock(&br->multicast_lock);
133413cefad2SNikolay Aleksandrov 	if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED))
1335c83b8fabSHerbert Xu 		goto out;
1336c83b8fabSHerbert Xu 
1337cc0fdd80SLinus Lüssing 	br_multicast_start_querier(br, query);
1338c83b8fabSHerbert Xu 
1339c83b8fabSHerbert Xu out:
1340c83b8fabSHerbert Xu 	spin_unlock(&br->multicast_lock);
1341c83b8fabSHerbert Xu }
1342c83b8fabSHerbert Xu 
134388c1f37fSAllen Pais static void br_ip4_multicast_querier_expired(struct timer_list *t)
1344cc0fdd80SLinus Lüssing {
134588c1f37fSAllen Pais 	struct net_bridge *br = from_timer(br, t, ip4_other_query.timer);
1346cc0fdd80SLinus Lüssing 
134790010b36SLinus Lüssing 	br_multicast_querier_expired(br, &br->ip4_own_query);
1348cc0fdd80SLinus Lüssing }
1349cc0fdd80SLinus Lüssing 
1350cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
135188c1f37fSAllen Pais static void br_ip6_multicast_querier_expired(struct timer_list *t)
1352cc0fdd80SLinus Lüssing {
135388c1f37fSAllen Pais 	struct net_bridge *br = from_timer(br, t, ip6_other_query.timer);
1354cc0fdd80SLinus Lüssing 
135590010b36SLinus Lüssing 	br_multicast_querier_expired(br, &br->ip6_own_query);
1356cc0fdd80SLinus Lüssing }
1357cc0fdd80SLinus Lüssing #endif
1358cc0fdd80SLinus Lüssing 
1359dc4eb53aSLinus Lüssing static void br_multicast_select_own_querier(struct net_bridge *br,
1360dc4eb53aSLinus Lüssing 					    struct br_ip *ip,
1361dc4eb53aSLinus Lüssing 					    struct sk_buff *skb)
1362dc4eb53aSLinus Lüssing {
1363dc4eb53aSLinus Lüssing 	if (ip->proto == htons(ETH_P_IP))
1364deb96566SNikolay Aleksandrov 		br->ip4_querier.addr.src.ip4 = ip_hdr(skb)->saddr;
1365dc4eb53aSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
1366dc4eb53aSLinus Lüssing 	else
1367deb96566SNikolay Aleksandrov 		br->ip6_querier.addr.src.ip6 = ipv6_hdr(skb)->saddr;
1368dc4eb53aSLinus Lüssing #endif
1369dc4eb53aSLinus Lüssing }
1370dc4eb53aSLinus Lüssing 
13718ef2a9a5SYOSHIFUJI Hideaki static void __br_multicast_send_query(struct net_bridge *br,
13728ef2a9a5SYOSHIFUJI Hideaki 				      struct net_bridge_port *port,
1373438ef2d0SNikolay Aleksandrov 				      struct net_bridge_port_group *pg,
1374438ef2d0SNikolay Aleksandrov 				      struct br_ip *ip_dst,
1375438ef2d0SNikolay Aleksandrov 				      struct br_ip *group,
1376438ef2d0SNikolay Aleksandrov 				      bool with_srcs,
137742c11ccfSNikolay Aleksandrov 				      u8 sflag,
137842c11ccfSNikolay Aleksandrov 				      bool *need_rexmit)
1379eb1d1641SHerbert Xu {
1380438ef2d0SNikolay Aleksandrov 	bool over_lmqt = !!sflag;
1381eb1d1641SHerbert Xu 	struct sk_buff *skb;
13821080ab95SNikolay Aleksandrov 	u8 igmp_type;
1383eb1d1641SHerbert Xu 
1384438ef2d0SNikolay Aleksandrov again_under_lmqt:
1385438ef2d0SNikolay Aleksandrov 	skb = br_multicast_alloc_query(br, pg, ip_dst, group, with_srcs,
138642c11ccfSNikolay Aleksandrov 				       over_lmqt, sflag, &igmp_type,
138742c11ccfSNikolay Aleksandrov 				       need_rexmit);
1388eb1d1641SHerbert Xu 	if (!skb)
13898ef2a9a5SYOSHIFUJI Hideaki 		return;
1390eb1d1641SHerbert Xu 
1391eb1d1641SHerbert Xu 	if (port) {
1392eb1d1641SHerbert Xu 		skb->dev = port->dev;
1393a65056ecSNikolay Aleksandrov 		br_multicast_count(br, port, skb, igmp_type,
13941080ab95SNikolay Aleksandrov 				   BR_MCAST_DIR_TX);
139529a26a56SEric W. Biederman 		NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
139629a26a56SEric W. Biederman 			dev_net(port->dev), NULL, skb, NULL, skb->dev,
1397f0b4eeceSLinus Lüssing 			br_dev_queue_push_xmit);
1398438ef2d0SNikolay Aleksandrov 
1399438ef2d0SNikolay Aleksandrov 		if (over_lmqt && with_srcs && sflag) {
1400438ef2d0SNikolay Aleksandrov 			over_lmqt = false;
1401438ef2d0SNikolay Aleksandrov 			goto again_under_lmqt;
1402438ef2d0SNikolay Aleksandrov 		}
1403dc4eb53aSLinus Lüssing 	} else {
1404438ef2d0SNikolay Aleksandrov 		br_multicast_select_own_querier(br, group, skb);
1405a65056ecSNikolay Aleksandrov 		br_multicast_count(br, port, skb, igmp_type,
14061080ab95SNikolay Aleksandrov 				   BR_MCAST_DIR_RX);
1407eb1d1641SHerbert Xu 		netif_rx(skb);
14088ef2a9a5SYOSHIFUJI Hideaki 	}
1409dc4eb53aSLinus Lüssing }
1410eb1d1641SHerbert Xu 
14118ef2a9a5SYOSHIFUJI Hideaki static void br_multicast_send_query(struct net_bridge *br,
1412cc0fdd80SLinus Lüssing 				    struct net_bridge_port *port,
141390010b36SLinus Lüssing 				    struct bridge_mcast_own_query *own_query)
14148ef2a9a5SYOSHIFUJI Hideaki {
141590010b36SLinus Lüssing 	struct bridge_mcast_other_query *other_query = NULL;
14165e923585SNikolay Aleksandrov 	struct br_ip br_group;
14175e923585SNikolay Aleksandrov 	unsigned long time;
14188ef2a9a5SYOSHIFUJI Hideaki 
141913cefad2SNikolay Aleksandrov 	if (!netif_running(br->dev) ||
142013cefad2SNikolay Aleksandrov 	    !br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
1421675779adSNikolay Aleksandrov 	    !br_opt_get(br, BROPT_MULTICAST_QUERIER))
14228ef2a9a5SYOSHIFUJI Hideaki 		return;
14238ef2a9a5SYOSHIFUJI Hideaki 
1424eab3227bSNikolay Aleksandrov 	memset(&br_group.dst, 0, sizeof(br_group.dst));
14258ef2a9a5SYOSHIFUJI Hideaki 
142690010b36SLinus Lüssing 	if (port ? (own_query == &port->ip4_own_query) :
142790010b36SLinus Lüssing 		   (own_query == &br->ip4_own_query)) {
142890010b36SLinus Lüssing 		other_query = &br->ip4_other_query;
142908b202b6SYOSHIFUJI Hideaki 		br_group.proto = htons(ETH_P_IP);
1430dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
1431cc0fdd80SLinus Lüssing 	} else {
143290010b36SLinus Lüssing 		other_query = &br->ip6_other_query;
143308b202b6SYOSHIFUJI Hideaki 		br_group.proto = htons(ETH_P_IPV6);
143408b202b6SYOSHIFUJI Hideaki #endif
1435eb1d1641SHerbert Xu 	}
1436eb1d1641SHerbert Xu 
143790010b36SLinus Lüssing 	if (!other_query || timer_pending(&other_query->timer))
1438cc0fdd80SLinus Lüssing 		return;
1439cc0fdd80SLinus Lüssing 
144042c11ccfSNikolay Aleksandrov 	__br_multicast_send_query(br, port, NULL, NULL, &br_group, false, 0,
144142c11ccfSNikolay Aleksandrov 				  NULL);
1442cc0fdd80SLinus Lüssing 
1443cc0fdd80SLinus Lüssing 	time = jiffies;
144490010b36SLinus Lüssing 	time += own_query->startup_sent < br->multicast_startup_query_count ?
1445cc0fdd80SLinus Lüssing 		br->multicast_startup_query_interval :
1446cc0fdd80SLinus Lüssing 		br->multicast_query_interval;
144790010b36SLinus Lüssing 	mod_timer(&own_query->timer, time);
1448cc0fdd80SLinus Lüssing }
1449cc0fdd80SLinus Lüssing 
145090010b36SLinus Lüssing static void
145190010b36SLinus Lüssing br_multicast_port_query_expired(struct net_bridge_port *port,
145290010b36SLinus Lüssing 				struct bridge_mcast_own_query *query)
1453eb1d1641SHerbert Xu {
1454eb1d1641SHerbert Xu 	struct net_bridge *br = port->br;
1455eb1d1641SHerbert Xu 
1456eb1d1641SHerbert Xu 	spin_lock(&br->multicast_lock);
145702a780c0SDan Carpenter 	if (port->state == BR_STATE_DISABLED ||
145802a780c0SDan Carpenter 	    port->state == BR_STATE_BLOCKING)
1459eb1d1641SHerbert Xu 		goto out;
1460eb1d1641SHerbert Xu 
1461cc0fdd80SLinus Lüssing 	if (query->startup_sent < br->multicast_startup_query_count)
1462cc0fdd80SLinus Lüssing 		query->startup_sent++;
1463eb1d1641SHerbert Xu 
1464cc0fdd80SLinus Lüssing 	br_multicast_send_query(port->br, port, query);
1465eb1d1641SHerbert Xu 
1466eb1d1641SHerbert Xu out:
1467eb1d1641SHerbert Xu 	spin_unlock(&br->multicast_lock);
1468eb1d1641SHerbert Xu }
1469eb1d1641SHerbert Xu 
147088c1f37fSAllen Pais static void br_ip4_multicast_port_query_expired(struct timer_list *t)
1471cc0fdd80SLinus Lüssing {
147288c1f37fSAllen Pais 	struct net_bridge_port *port = from_timer(port, t, ip4_own_query.timer);
1473cc0fdd80SLinus Lüssing 
147490010b36SLinus Lüssing 	br_multicast_port_query_expired(port, &port->ip4_own_query);
1475cc0fdd80SLinus Lüssing }
1476cc0fdd80SLinus Lüssing 
1477cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
147888c1f37fSAllen Pais static void br_ip6_multicast_port_query_expired(struct timer_list *t)
1479cc0fdd80SLinus Lüssing {
148088c1f37fSAllen Pais 	struct net_bridge_port *port = from_timer(port, t, ip6_own_query.timer);
1481cc0fdd80SLinus Lüssing 
148290010b36SLinus Lüssing 	br_multicast_port_query_expired(port, &port->ip6_own_query);
1483cc0fdd80SLinus Lüssing }
1484cc0fdd80SLinus Lüssing #endif
1485cc0fdd80SLinus Lüssing 
148642c11ccfSNikolay Aleksandrov static void br_multicast_port_group_rexmit(struct timer_list *t)
148742c11ccfSNikolay Aleksandrov {
148842c11ccfSNikolay Aleksandrov 	struct net_bridge_port_group *pg = from_timer(pg, t, rexmit_timer);
148942c11ccfSNikolay Aleksandrov 	struct bridge_mcast_other_query *other_query = NULL;
1490085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
149142c11ccfSNikolay Aleksandrov 	bool need_rexmit = false;
149242c11ccfSNikolay Aleksandrov 
149342c11ccfSNikolay Aleksandrov 	spin_lock(&br->multicast_lock);
149442c11ccfSNikolay Aleksandrov 	if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) ||
149542c11ccfSNikolay Aleksandrov 	    !br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
149642c11ccfSNikolay Aleksandrov 	    !br_opt_get(br, BROPT_MULTICAST_QUERIER))
149742c11ccfSNikolay Aleksandrov 		goto out;
149842c11ccfSNikolay Aleksandrov 
1499085b53c8SNikolay Aleksandrov 	if (pg->key.addr.proto == htons(ETH_P_IP))
150042c11ccfSNikolay Aleksandrov 		other_query = &br->ip4_other_query;
150142c11ccfSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
150242c11ccfSNikolay Aleksandrov 	else
150342c11ccfSNikolay Aleksandrov 		other_query = &br->ip6_other_query;
150442c11ccfSNikolay Aleksandrov #endif
150542c11ccfSNikolay Aleksandrov 
150642c11ccfSNikolay Aleksandrov 	if (!other_query || timer_pending(&other_query->timer))
150742c11ccfSNikolay Aleksandrov 		goto out;
150842c11ccfSNikolay Aleksandrov 
150942c11ccfSNikolay Aleksandrov 	if (pg->grp_query_rexmit_cnt) {
151042c11ccfSNikolay Aleksandrov 		pg->grp_query_rexmit_cnt--;
1511085b53c8SNikolay Aleksandrov 		__br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr,
1512085b53c8SNikolay Aleksandrov 					  &pg->key.addr, false, 1, NULL);
151342c11ccfSNikolay Aleksandrov 	}
1514085b53c8SNikolay Aleksandrov 	__br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr,
1515085b53c8SNikolay Aleksandrov 				  &pg->key.addr, true, 0, &need_rexmit);
151642c11ccfSNikolay Aleksandrov 
151742c11ccfSNikolay Aleksandrov 	if (pg->grp_query_rexmit_cnt || need_rexmit)
151842c11ccfSNikolay Aleksandrov 		mod_timer(&pg->rexmit_timer, jiffies +
151942c11ccfSNikolay Aleksandrov 					     br->multicast_last_member_interval);
152042c11ccfSNikolay Aleksandrov out:
152142c11ccfSNikolay Aleksandrov 	spin_unlock(&br->multicast_lock);
152242c11ccfSNikolay Aleksandrov }
152342c11ccfSNikolay Aleksandrov 
1524147c1e9bSNogah Frankel static void br_mc_disabled_update(struct net_device *dev, bool value)
1525147c1e9bSNogah Frankel {
1526147c1e9bSNogah Frankel 	struct switchdev_attr attr = {
1527147c1e9bSNogah Frankel 		.orig_dev = dev,
1528147c1e9bSNogah Frankel 		.id = SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED,
1529147c1e9bSNogah Frankel 		.flags = SWITCHDEV_F_DEFER,
153013cefad2SNikolay Aleksandrov 		.u.mc_disabled = !value,
1531147c1e9bSNogah Frankel 	};
1532147c1e9bSNogah Frankel 
1533147c1e9bSNogah Frankel 	switchdev_port_attr_set(dev, &attr);
1534147c1e9bSNogah Frankel }
1535147c1e9bSNogah Frankel 
15361080ab95SNikolay Aleksandrov int br_multicast_add_port(struct net_bridge_port *port)
1537eb1d1641SHerbert Xu {
15387f0aec7aSNikolay Aleksandrov 	port->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
1539eb1d1641SHerbert Xu 
154088c1f37fSAllen Pais 	timer_setup(&port->multicast_router_timer,
154188c1f37fSAllen Pais 		    br_multicast_router_expired, 0);
154288c1f37fSAllen Pais 	timer_setup(&port->ip4_own_query.timer,
154388c1f37fSAllen Pais 		    br_ip4_multicast_port_query_expired, 0);
1544cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
154588c1f37fSAllen Pais 	timer_setup(&port->ip6_own_query.timer,
154688c1f37fSAllen Pais 		    br_ip6_multicast_port_query_expired, 0);
1547cc0fdd80SLinus Lüssing #endif
154813cefad2SNikolay Aleksandrov 	br_mc_disabled_update(port->dev,
154913cefad2SNikolay Aleksandrov 			      br_opt_get(port->br, BROPT_MULTICAST_ENABLED));
1550147c1e9bSNogah Frankel 
15511080ab95SNikolay Aleksandrov 	port->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats);
15521080ab95SNikolay Aleksandrov 	if (!port->mcast_stats)
15531080ab95SNikolay Aleksandrov 		return -ENOMEM;
15541080ab95SNikolay Aleksandrov 
15551080ab95SNikolay Aleksandrov 	return 0;
1556eb1d1641SHerbert Xu }
1557eb1d1641SHerbert Xu 
1558eb1d1641SHerbert Xu void br_multicast_del_port(struct net_bridge_port *port)
1559eb1d1641SHerbert Xu {
1560e10177abSSatish Ashok 	struct net_bridge *br = port->br;
1561e10177abSSatish Ashok 	struct net_bridge_port_group *pg;
1562e12cec65SNikolay Aleksandrov 	HLIST_HEAD(deleted_head);
1563e10177abSSatish Ashok 	struct hlist_node *n;
1564e10177abSSatish Ashok 
1565e10177abSSatish Ashok 	/* Take care of the remaining groups, only perm ones should be left */
1566e10177abSSatish Ashok 	spin_lock_bh(&br->multicast_lock);
1567e10177abSSatish Ashok 	hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
1568681590bdSNikolay Aleksandrov 		br_multicast_find_del_pg(br, pg);
1569e12cec65SNikolay Aleksandrov 	hlist_move_list(&br->mcast_gc_list, &deleted_head);
1570e10177abSSatish Ashok 	spin_unlock_bh(&br->multicast_lock);
1571e12cec65SNikolay Aleksandrov 	br_multicast_gc(&deleted_head);
1572eb1d1641SHerbert Xu 	del_timer_sync(&port->multicast_router_timer);
15731080ab95SNikolay Aleksandrov 	free_percpu(port->mcast_stats);
1574eb1d1641SHerbert Xu }
1575eb1d1641SHerbert Xu 
157690010b36SLinus Lüssing static void br_multicast_enable(struct bridge_mcast_own_query *query)
1577561f1103SHerbert Xu {
1578cc0fdd80SLinus Lüssing 	query->startup_sent = 0;
1579561f1103SHerbert Xu 
1580cc0fdd80SLinus Lüssing 	if (try_to_del_timer_sync(&query->timer) >= 0 ||
1581cc0fdd80SLinus Lüssing 	    del_timer(&query->timer))
1582cc0fdd80SLinus Lüssing 		mod_timer(&query->timer, jiffies);
1583561f1103SHerbert Xu }
1584561f1103SHerbert Xu 
15857cb3f921SNikolay Aleksandrov static void __br_multicast_enable_port(struct net_bridge_port *port)
1586eb1d1641SHerbert Xu {
1587eb1d1641SHerbert Xu 	struct net_bridge *br = port->br;
1588eb1d1641SHerbert Xu 
158913cefad2SNikolay Aleksandrov 	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED) || !netif_running(br->dev))
15907cb3f921SNikolay Aleksandrov 		return;
1591eb1d1641SHerbert Xu 
159290010b36SLinus Lüssing 	br_multicast_enable(&port->ip4_own_query);
1593cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
159490010b36SLinus Lüssing 	br_multicast_enable(&port->ip6_own_query);
1595cc0fdd80SLinus Lüssing #endif
15967f0aec7aSNikolay Aleksandrov 	if (port->multicast_router == MDB_RTR_TYPE_PERM &&
15977f0aec7aSNikolay Aleksandrov 	    hlist_unhashed(&port->rlist))
1598754bc547SSatish Ashok 		br_multicast_add_router(br, port);
15997cb3f921SNikolay Aleksandrov }
1600eb1d1641SHerbert Xu 
16017cb3f921SNikolay Aleksandrov void br_multicast_enable_port(struct net_bridge_port *port)
16027cb3f921SNikolay Aleksandrov {
16037cb3f921SNikolay Aleksandrov 	struct net_bridge *br = port->br;
16047cb3f921SNikolay Aleksandrov 
16057cb3f921SNikolay Aleksandrov 	spin_lock(&br->multicast_lock);
16067cb3f921SNikolay Aleksandrov 	__br_multicast_enable_port(port);
1607eb1d1641SHerbert Xu 	spin_unlock(&br->multicast_lock);
1608eb1d1641SHerbert Xu }
1609eb1d1641SHerbert Xu 
1610eb1d1641SHerbert Xu void br_multicast_disable_port(struct net_bridge_port *port)
1611eb1d1641SHerbert Xu {
1612eb1d1641SHerbert Xu 	struct net_bridge *br = port->br;
1613eb1d1641SHerbert Xu 	struct net_bridge_port_group *pg;
1614b67bfe0dSSasha Levin 	struct hlist_node *n;
1615eb1d1641SHerbert Xu 
1616eb1d1641SHerbert Xu 	spin_lock(&br->multicast_lock);
1617b67bfe0dSSasha Levin 	hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
16189d06b6d8SElad Raz 		if (!(pg->flags & MDB_PG_FLAGS_PERMANENT))
1619681590bdSNikolay Aleksandrov 			br_multicast_find_del_pg(br, pg);
1620eb1d1641SHerbert Xu 
1621f12e7d95SNogah Frankel 	__del_port_router(port);
1622f12e7d95SNogah Frankel 
1623eb1d1641SHerbert Xu 	del_timer(&port->multicast_router_timer);
162490010b36SLinus Lüssing 	del_timer(&port->ip4_own_query.timer);
1625cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
162690010b36SLinus Lüssing 	del_timer(&port->ip6_own_query.timer);
1627cc0fdd80SLinus Lüssing #endif
1628eb1d1641SHerbert Xu 	spin_unlock(&br->multicast_lock);
1629eb1d1641SHerbert Xu }
1630eb1d1641SHerbert Xu 
1631e6231bcaSNikolay Aleksandrov static int __grp_src_delete_marked(struct net_bridge_port_group *pg)
1632e6231bcaSNikolay Aleksandrov {
1633e6231bcaSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
1634e6231bcaSNikolay Aleksandrov 	struct hlist_node *tmp;
1635e6231bcaSNikolay Aleksandrov 	int deleted = 0;
1636e6231bcaSNikolay Aleksandrov 
1637e6231bcaSNikolay Aleksandrov 	hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
1638e6231bcaSNikolay Aleksandrov 		if (ent->flags & BR_SGRP_F_DELETE) {
1639e6231bcaSNikolay Aleksandrov 			br_multicast_del_group_src(ent);
1640e6231bcaSNikolay Aleksandrov 			deleted++;
1641e6231bcaSNikolay Aleksandrov 		}
1642e6231bcaSNikolay Aleksandrov 
1643e6231bcaSNikolay Aleksandrov 	return deleted;
1644e6231bcaSNikolay Aleksandrov }
1645e6231bcaSNikolay Aleksandrov 
1646b0812368SNikolay Aleksandrov static void __grp_src_mod_timer(struct net_bridge_group_src *src,
1647b0812368SNikolay Aleksandrov 				unsigned long expires)
1648b0812368SNikolay Aleksandrov {
1649b0812368SNikolay Aleksandrov 	mod_timer(&src->timer, expires);
1650b0812368SNikolay Aleksandrov 	br_multicast_fwd_src_handle(src);
1651b0812368SNikolay Aleksandrov }
1652b0812368SNikolay Aleksandrov 
16535bf1e00bSNikolay Aleksandrov static void __grp_src_query_marked_and_rexmit(struct net_bridge_port_group *pg)
16545bf1e00bSNikolay Aleksandrov {
16555bf1e00bSNikolay Aleksandrov 	struct bridge_mcast_other_query *other_query = NULL;
1656085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
16575bf1e00bSNikolay Aleksandrov 	u32 lmqc = br->multicast_last_member_count;
16585bf1e00bSNikolay Aleksandrov 	unsigned long lmqt, lmi, now = jiffies;
16595bf1e00bSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
16605bf1e00bSNikolay Aleksandrov 
16615bf1e00bSNikolay Aleksandrov 	if (!netif_running(br->dev) ||
16625bf1e00bSNikolay Aleksandrov 	    !br_opt_get(br, BROPT_MULTICAST_ENABLED))
16635bf1e00bSNikolay Aleksandrov 		return;
16645bf1e00bSNikolay Aleksandrov 
1665085b53c8SNikolay Aleksandrov 	if (pg->key.addr.proto == htons(ETH_P_IP))
16665bf1e00bSNikolay Aleksandrov 		other_query = &br->ip4_other_query;
16675bf1e00bSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
16685bf1e00bSNikolay Aleksandrov 	else
16695bf1e00bSNikolay Aleksandrov 		other_query = &br->ip6_other_query;
16705bf1e00bSNikolay Aleksandrov #endif
16715bf1e00bSNikolay Aleksandrov 
16725bf1e00bSNikolay Aleksandrov 	lmqt = now + br_multicast_lmqt(br);
16735bf1e00bSNikolay Aleksandrov 	hlist_for_each_entry(ent, &pg->src_list, node) {
16745bf1e00bSNikolay Aleksandrov 		if (ent->flags & BR_SGRP_F_SEND) {
16755bf1e00bSNikolay Aleksandrov 			ent->flags &= ~BR_SGRP_F_SEND;
16765bf1e00bSNikolay Aleksandrov 			if (ent->timer.expires > lmqt) {
16775bf1e00bSNikolay Aleksandrov 				if (br_opt_get(br, BROPT_MULTICAST_QUERIER) &&
16785bf1e00bSNikolay Aleksandrov 				    other_query &&
16795bf1e00bSNikolay Aleksandrov 				    !timer_pending(&other_query->timer))
16805bf1e00bSNikolay Aleksandrov 					ent->src_query_rexmit_cnt = lmqc;
1681b0812368SNikolay Aleksandrov 				__grp_src_mod_timer(ent, lmqt);
16825bf1e00bSNikolay Aleksandrov 			}
16835bf1e00bSNikolay Aleksandrov 		}
16845bf1e00bSNikolay Aleksandrov 	}
16855bf1e00bSNikolay Aleksandrov 
16865bf1e00bSNikolay Aleksandrov 	if (!br_opt_get(br, BROPT_MULTICAST_QUERIER) ||
16875bf1e00bSNikolay Aleksandrov 	    !other_query || timer_pending(&other_query->timer))
16885bf1e00bSNikolay Aleksandrov 		return;
16895bf1e00bSNikolay Aleksandrov 
1690085b53c8SNikolay Aleksandrov 	__br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr,
1691085b53c8SNikolay Aleksandrov 				  &pg->key.addr, true, 1, NULL);
16925bf1e00bSNikolay Aleksandrov 
16935bf1e00bSNikolay Aleksandrov 	lmi = now + br->multicast_last_member_interval;
16945bf1e00bSNikolay Aleksandrov 	if (!timer_pending(&pg->rexmit_timer) ||
16955bf1e00bSNikolay Aleksandrov 	    time_after(pg->rexmit_timer.expires, lmi))
16965bf1e00bSNikolay Aleksandrov 		mod_timer(&pg->rexmit_timer, lmi);
16975bf1e00bSNikolay Aleksandrov }
16985bf1e00bSNikolay Aleksandrov 
16995bf1e00bSNikolay Aleksandrov static void __grp_send_query_and_rexmit(struct net_bridge_port_group *pg)
17005bf1e00bSNikolay Aleksandrov {
17015bf1e00bSNikolay Aleksandrov 	struct bridge_mcast_other_query *other_query = NULL;
1702085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
17035bf1e00bSNikolay Aleksandrov 	unsigned long now = jiffies, lmi;
17045bf1e00bSNikolay Aleksandrov 
17055bf1e00bSNikolay Aleksandrov 	if (!netif_running(br->dev) ||
17065bf1e00bSNikolay Aleksandrov 	    !br_opt_get(br, BROPT_MULTICAST_ENABLED))
17075bf1e00bSNikolay Aleksandrov 		return;
17085bf1e00bSNikolay Aleksandrov 
1709085b53c8SNikolay Aleksandrov 	if (pg->key.addr.proto == htons(ETH_P_IP))
17105bf1e00bSNikolay Aleksandrov 		other_query = &br->ip4_other_query;
17115bf1e00bSNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
17125bf1e00bSNikolay Aleksandrov 	else
17135bf1e00bSNikolay Aleksandrov 		other_query = &br->ip6_other_query;
17145bf1e00bSNikolay Aleksandrov #endif
17155bf1e00bSNikolay Aleksandrov 
17165bf1e00bSNikolay Aleksandrov 	if (br_opt_get(br, BROPT_MULTICAST_QUERIER) &&
17175bf1e00bSNikolay Aleksandrov 	    other_query && !timer_pending(&other_query->timer)) {
17185bf1e00bSNikolay Aleksandrov 		lmi = now + br->multicast_last_member_interval;
17195bf1e00bSNikolay Aleksandrov 		pg->grp_query_rexmit_cnt = br->multicast_last_member_count - 1;
1720085b53c8SNikolay Aleksandrov 		__br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr,
1721085b53c8SNikolay Aleksandrov 					  &pg->key.addr, false, 0, NULL);
17225bf1e00bSNikolay Aleksandrov 		if (!timer_pending(&pg->rexmit_timer) ||
17235bf1e00bSNikolay Aleksandrov 		    time_after(pg->rexmit_timer.expires, lmi))
17245bf1e00bSNikolay Aleksandrov 			mod_timer(&pg->rexmit_timer, lmi);
17255bf1e00bSNikolay Aleksandrov 	}
17265bf1e00bSNikolay Aleksandrov 
17275bf1e00bSNikolay Aleksandrov 	if (pg->filter_mode == MCAST_EXCLUDE &&
17285bf1e00bSNikolay Aleksandrov 	    (!timer_pending(&pg->timer) ||
17295bf1e00bSNikolay Aleksandrov 	     time_after(pg->timer.expires, now + br_multicast_lmqt(br))))
17305bf1e00bSNikolay Aleksandrov 		mod_timer(&pg->timer, now + br_multicast_lmqt(br));
17315bf1e00bSNikolay Aleksandrov }
17325bf1e00bSNikolay Aleksandrov 
17330436862eSNikolay Aleksandrov /* State          Msg type      New state                Actions
17340436862eSNikolay Aleksandrov  * INCLUDE (A)    IS_IN (B)     INCLUDE (A+B)            (B)=GMI
17350436862eSNikolay Aleksandrov  * INCLUDE (A)    ALLOW (B)     INCLUDE (A+B)            (B)=GMI
17360436862eSNikolay Aleksandrov  * EXCLUDE (X,Y)  ALLOW (A)     EXCLUDE (X+A,Y-A)        (A)=GMI
17370436862eSNikolay Aleksandrov  */
17380436862eSNikolay Aleksandrov static bool br_multicast_isinc_allow(struct net_bridge_port_group *pg,
17390436862eSNikolay Aleksandrov 				     void *srcs, u32 nsrcs, size_t src_size)
17400436862eSNikolay Aleksandrov {
1741085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
17420436862eSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
17430436862eSNikolay Aleksandrov 	unsigned long now = jiffies;
17440436862eSNikolay Aleksandrov 	bool changed = false;
17450436862eSNikolay Aleksandrov 	struct br_ip src_ip;
17460436862eSNikolay Aleksandrov 	u32 src_idx;
17470436862eSNikolay Aleksandrov 
17480436862eSNikolay Aleksandrov 	memset(&src_ip, 0, sizeof(src_ip));
1749085b53c8SNikolay Aleksandrov 	src_ip.proto = pg->key.addr.proto;
17500436862eSNikolay Aleksandrov 	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
1751deb96566SNikolay Aleksandrov 		memcpy(&src_ip.src, srcs, src_size);
17520436862eSNikolay Aleksandrov 		ent = br_multicast_find_group_src(pg, &src_ip);
17530436862eSNikolay Aleksandrov 		if (!ent) {
17540436862eSNikolay Aleksandrov 			ent = br_multicast_new_group_src(pg, &src_ip);
17550436862eSNikolay Aleksandrov 			if (ent)
17560436862eSNikolay Aleksandrov 				changed = true;
17570436862eSNikolay Aleksandrov 		}
17580436862eSNikolay Aleksandrov 
17590436862eSNikolay Aleksandrov 		if (ent)
1760b0812368SNikolay Aleksandrov 			__grp_src_mod_timer(ent, now + br_multicast_gmi(br));
17610436862eSNikolay Aleksandrov 		srcs += src_size;
17620436862eSNikolay Aleksandrov 	}
17630436862eSNikolay Aleksandrov 
17640436862eSNikolay Aleksandrov 	return changed;
17650436862eSNikolay Aleksandrov }
17660436862eSNikolay Aleksandrov 
1767e6231bcaSNikolay Aleksandrov /* State          Msg type      New state                Actions
1768e6231bcaSNikolay Aleksandrov  * INCLUDE (A)    IS_EX (B)     EXCLUDE (A*B,B-A)        (B-A)=0
1769e6231bcaSNikolay Aleksandrov  *                                                       Delete (A-B)
1770e6231bcaSNikolay Aleksandrov  *                                                       Group Timer=GMI
1771e6231bcaSNikolay Aleksandrov  */
1772e6231bcaSNikolay Aleksandrov static void __grp_src_isexc_incl(struct net_bridge_port_group *pg,
1773e6231bcaSNikolay Aleksandrov 				 void *srcs, u32 nsrcs, size_t src_size)
1774e6231bcaSNikolay Aleksandrov {
1775e6231bcaSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
1776e6231bcaSNikolay Aleksandrov 	struct br_ip src_ip;
1777e6231bcaSNikolay Aleksandrov 	u32 src_idx;
1778e6231bcaSNikolay Aleksandrov 
1779e6231bcaSNikolay Aleksandrov 	hlist_for_each_entry(ent, &pg->src_list, node)
1780e6231bcaSNikolay Aleksandrov 		ent->flags |= BR_SGRP_F_DELETE;
1781e6231bcaSNikolay Aleksandrov 
1782e6231bcaSNikolay Aleksandrov 	memset(&src_ip, 0, sizeof(src_ip));
1783085b53c8SNikolay Aleksandrov 	src_ip.proto = pg->key.addr.proto;
1784e6231bcaSNikolay Aleksandrov 	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
1785deb96566SNikolay Aleksandrov 		memcpy(&src_ip.src, srcs, src_size);
1786e6231bcaSNikolay Aleksandrov 		ent = br_multicast_find_group_src(pg, &src_ip);
1787e6231bcaSNikolay Aleksandrov 		if (ent)
1788e6231bcaSNikolay Aleksandrov 			ent->flags &= ~BR_SGRP_F_DELETE;
1789e6231bcaSNikolay Aleksandrov 		else
1790b0812368SNikolay Aleksandrov 			ent = br_multicast_new_group_src(pg, &src_ip);
1791b0812368SNikolay Aleksandrov 		if (ent)
1792b0812368SNikolay Aleksandrov 			br_multicast_fwd_src_handle(ent);
1793e6231bcaSNikolay Aleksandrov 		srcs += src_size;
1794e6231bcaSNikolay Aleksandrov 	}
1795e6231bcaSNikolay Aleksandrov 
1796e6231bcaSNikolay Aleksandrov 	__grp_src_delete_marked(pg);
1797e6231bcaSNikolay Aleksandrov }
1798e6231bcaSNikolay Aleksandrov 
1799e6231bcaSNikolay Aleksandrov /* State          Msg type      New state                Actions
1800e6231bcaSNikolay Aleksandrov  * EXCLUDE (X,Y)  IS_EX (A)     EXCLUDE (A-Y,Y*A)        (A-X-Y)=GMI
1801e6231bcaSNikolay Aleksandrov  *                                                       Delete (X-A)
1802e6231bcaSNikolay Aleksandrov  *                                                       Delete (Y-A)
1803e6231bcaSNikolay Aleksandrov  *                                                       Group Timer=GMI
1804e6231bcaSNikolay Aleksandrov  */
1805e6231bcaSNikolay Aleksandrov static bool __grp_src_isexc_excl(struct net_bridge_port_group *pg,
1806e6231bcaSNikolay Aleksandrov 				 void *srcs, u32 nsrcs, size_t src_size)
1807e6231bcaSNikolay Aleksandrov {
1808085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
1809e6231bcaSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
1810e6231bcaSNikolay Aleksandrov 	unsigned long now = jiffies;
1811e6231bcaSNikolay Aleksandrov 	bool changed = false;
1812e6231bcaSNikolay Aleksandrov 	struct br_ip src_ip;
1813e6231bcaSNikolay Aleksandrov 	u32 src_idx;
1814e6231bcaSNikolay Aleksandrov 
1815e6231bcaSNikolay Aleksandrov 	hlist_for_each_entry(ent, &pg->src_list, node)
1816e6231bcaSNikolay Aleksandrov 		ent->flags |= BR_SGRP_F_DELETE;
1817e6231bcaSNikolay Aleksandrov 
1818e6231bcaSNikolay Aleksandrov 	memset(&src_ip, 0, sizeof(src_ip));
1819085b53c8SNikolay Aleksandrov 	src_ip.proto = pg->key.addr.proto;
1820e6231bcaSNikolay Aleksandrov 	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
1821deb96566SNikolay Aleksandrov 		memcpy(&src_ip.src, srcs, src_size);
1822e6231bcaSNikolay Aleksandrov 		ent = br_multicast_find_group_src(pg, &src_ip);
1823e6231bcaSNikolay Aleksandrov 		if (ent) {
1824e6231bcaSNikolay Aleksandrov 			ent->flags &= ~BR_SGRP_F_DELETE;
1825e6231bcaSNikolay Aleksandrov 		} else {
1826e6231bcaSNikolay Aleksandrov 			ent = br_multicast_new_group_src(pg, &src_ip);
1827e6231bcaSNikolay Aleksandrov 			if (ent) {
1828b0812368SNikolay Aleksandrov 				__grp_src_mod_timer(ent,
1829e6231bcaSNikolay Aleksandrov 						    now + br_multicast_gmi(br));
1830e6231bcaSNikolay Aleksandrov 				changed = true;
1831e6231bcaSNikolay Aleksandrov 			}
1832e6231bcaSNikolay Aleksandrov 		}
1833e6231bcaSNikolay Aleksandrov 		srcs += src_size;
1834e6231bcaSNikolay Aleksandrov 	}
1835e6231bcaSNikolay Aleksandrov 
1836e6231bcaSNikolay Aleksandrov 	if (__grp_src_delete_marked(pg))
1837e6231bcaSNikolay Aleksandrov 		changed = true;
1838e6231bcaSNikolay Aleksandrov 
1839e6231bcaSNikolay Aleksandrov 	return changed;
1840e6231bcaSNikolay Aleksandrov }
1841e6231bcaSNikolay Aleksandrov 
1842e6231bcaSNikolay Aleksandrov static bool br_multicast_isexc(struct net_bridge_port_group *pg,
1843e6231bcaSNikolay Aleksandrov 			       void *srcs, u32 nsrcs, size_t src_size)
1844e6231bcaSNikolay Aleksandrov {
1845085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
1846e6231bcaSNikolay Aleksandrov 	bool changed = false;
1847e6231bcaSNikolay Aleksandrov 
1848e6231bcaSNikolay Aleksandrov 	switch (pg->filter_mode) {
1849e6231bcaSNikolay Aleksandrov 	case MCAST_INCLUDE:
1850e6231bcaSNikolay Aleksandrov 		__grp_src_isexc_incl(pg, srcs, nsrcs, src_size);
18518266a049SNikolay Aleksandrov 		br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE);
1852e6231bcaSNikolay Aleksandrov 		changed = true;
1853e6231bcaSNikolay Aleksandrov 		break;
1854e6231bcaSNikolay Aleksandrov 	case MCAST_EXCLUDE:
1855e6231bcaSNikolay Aleksandrov 		changed = __grp_src_isexc_excl(pg, srcs, nsrcs, src_size);
1856e6231bcaSNikolay Aleksandrov 		break;
1857e6231bcaSNikolay Aleksandrov 	}
1858e6231bcaSNikolay Aleksandrov 
1859e6231bcaSNikolay Aleksandrov 	pg->filter_mode = MCAST_EXCLUDE;
1860e6231bcaSNikolay Aleksandrov 	mod_timer(&pg->timer, jiffies + br_multicast_gmi(br));
1861e6231bcaSNikolay Aleksandrov 
1862e6231bcaSNikolay Aleksandrov 	return changed;
1863e6231bcaSNikolay Aleksandrov }
1864e6231bcaSNikolay Aleksandrov 
18655bf1e00bSNikolay Aleksandrov /* State          Msg type      New state                Actions
18665bf1e00bSNikolay Aleksandrov  * INCLUDE (A)    TO_IN (B)     INCLUDE (A+B)            (B)=GMI
18675bf1e00bSNikolay Aleksandrov  *                                                       Send Q(G,A-B)
18685bf1e00bSNikolay Aleksandrov  */
18695bf1e00bSNikolay Aleksandrov static bool __grp_src_toin_incl(struct net_bridge_port_group *pg,
18705bf1e00bSNikolay Aleksandrov 				void *srcs, u32 nsrcs, size_t src_size)
18715bf1e00bSNikolay Aleksandrov {
1872085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
18735bf1e00bSNikolay Aleksandrov 	u32 src_idx, to_send = pg->src_ents;
18745bf1e00bSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
18755bf1e00bSNikolay Aleksandrov 	unsigned long now = jiffies;
18765bf1e00bSNikolay Aleksandrov 	bool changed = false;
18775bf1e00bSNikolay Aleksandrov 	struct br_ip src_ip;
18785bf1e00bSNikolay Aleksandrov 
18795bf1e00bSNikolay Aleksandrov 	hlist_for_each_entry(ent, &pg->src_list, node)
18805bf1e00bSNikolay Aleksandrov 		ent->flags |= BR_SGRP_F_SEND;
18815bf1e00bSNikolay Aleksandrov 
18825bf1e00bSNikolay Aleksandrov 	memset(&src_ip, 0, sizeof(src_ip));
1883085b53c8SNikolay Aleksandrov 	src_ip.proto = pg->key.addr.proto;
18845bf1e00bSNikolay Aleksandrov 	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
1885deb96566SNikolay Aleksandrov 		memcpy(&src_ip.src, srcs, src_size);
18865bf1e00bSNikolay Aleksandrov 		ent = br_multicast_find_group_src(pg, &src_ip);
18875bf1e00bSNikolay Aleksandrov 		if (ent) {
18885bf1e00bSNikolay Aleksandrov 			ent->flags &= ~BR_SGRP_F_SEND;
18895bf1e00bSNikolay Aleksandrov 			to_send--;
18905bf1e00bSNikolay Aleksandrov 		} else {
18915bf1e00bSNikolay Aleksandrov 			ent = br_multicast_new_group_src(pg, &src_ip);
18925bf1e00bSNikolay Aleksandrov 			if (ent)
18935bf1e00bSNikolay Aleksandrov 				changed = true;
18945bf1e00bSNikolay Aleksandrov 		}
18955bf1e00bSNikolay Aleksandrov 		if (ent)
1896b0812368SNikolay Aleksandrov 			__grp_src_mod_timer(ent, now + br_multicast_gmi(br));
18975bf1e00bSNikolay Aleksandrov 		srcs += src_size;
18985bf1e00bSNikolay Aleksandrov 	}
18995bf1e00bSNikolay Aleksandrov 
19005bf1e00bSNikolay Aleksandrov 	if (to_send)
19015bf1e00bSNikolay Aleksandrov 		__grp_src_query_marked_and_rexmit(pg);
19025bf1e00bSNikolay Aleksandrov 
19035bf1e00bSNikolay Aleksandrov 	return changed;
19045bf1e00bSNikolay Aleksandrov }
19055bf1e00bSNikolay Aleksandrov 
19065bf1e00bSNikolay Aleksandrov /* State          Msg type      New state                Actions
19075bf1e00bSNikolay Aleksandrov  * EXCLUDE (X,Y)  TO_IN (A)     EXCLUDE (X+A,Y-A)        (A)=GMI
19085bf1e00bSNikolay Aleksandrov  *                                                       Send Q(G,X-A)
19095bf1e00bSNikolay Aleksandrov  *                                                       Send Q(G)
19105bf1e00bSNikolay Aleksandrov  */
19115bf1e00bSNikolay Aleksandrov static bool __grp_src_toin_excl(struct net_bridge_port_group *pg,
19125bf1e00bSNikolay Aleksandrov 				void *srcs, u32 nsrcs, size_t src_size)
19135bf1e00bSNikolay Aleksandrov {
1914085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
19155bf1e00bSNikolay Aleksandrov 	u32 src_idx, to_send = pg->src_ents;
19165bf1e00bSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
19175bf1e00bSNikolay Aleksandrov 	unsigned long now = jiffies;
19185bf1e00bSNikolay Aleksandrov 	bool changed = false;
19195bf1e00bSNikolay Aleksandrov 	struct br_ip src_ip;
19205bf1e00bSNikolay Aleksandrov 
19215bf1e00bSNikolay Aleksandrov 	hlist_for_each_entry(ent, &pg->src_list, node)
19225bf1e00bSNikolay Aleksandrov 		if (timer_pending(&ent->timer))
19235bf1e00bSNikolay Aleksandrov 			ent->flags |= BR_SGRP_F_SEND;
19245bf1e00bSNikolay Aleksandrov 
19255bf1e00bSNikolay Aleksandrov 	memset(&src_ip, 0, sizeof(src_ip));
1926085b53c8SNikolay Aleksandrov 	src_ip.proto = pg->key.addr.proto;
19275bf1e00bSNikolay Aleksandrov 	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
1928deb96566SNikolay Aleksandrov 		memcpy(&src_ip.src, srcs, src_size);
19295bf1e00bSNikolay Aleksandrov 		ent = br_multicast_find_group_src(pg, &src_ip);
19305bf1e00bSNikolay Aleksandrov 		if (ent) {
19315bf1e00bSNikolay Aleksandrov 			if (timer_pending(&ent->timer)) {
19325bf1e00bSNikolay Aleksandrov 				ent->flags &= ~BR_SGRP_F_SEND;
19335bf1e00bSNikolay Aleksandrov 				to_send--;
19345bf1e00bSNikolay Aleksandrov 			}
19355bf1e00bSNikolay Aleksandrov 		} else {
19365bf1e00bSNikolay Aleksandrov 			ent = br_multicast_new_group_src(pg, &src_ip);
19375bf1e00bSNikolay Aleksandrov 			if (ent)
19385bf1e00bSNikolay Aleksandrov 				changed = true;
19395bf1e00bSNikolay Aleksandrov 		}
19405bf1e00bSNikolay Aleksandrov 		if (ent)
1941b0812368SNikolay Aleksandrov 			__grp_src_mod_timer(ent, now + br_multicast_gmi(br));
19425bf1e00bSNikolay Aleksandrov 		srcs += src_size;
19435bf1e00bSNikolay Aleksandrov 	}
19445bf1e00bSNikolay Aleksandrov 
19455bf1e00bSNikolay Aleksandrov 	if (to_send)
19465bf1e00bSNikolay Aleksandrov 		__grp_src_query_marked_and_rexmit(pg);
19475bf1e00bSNikolay Aleksandrov 
19485bf1e00bSNikolay Aleksandrov 	__grp_send_query_and_rexmit(pg);
19495bf1e00bSNikolay Aleksandrov 
19505bf1e00bSNikolay Aleksandrov 	return changed;
19515bf1e00bSNikolay Aleksandrov }
19525bf1e00bSNikolay Aleksandrov 
19535bf1e00bSNikolay Aleksandrov static bool br_multicast_toin(struct net_bridge_port_group *pg,
19545bf1e00bSNikolay Aleksandrov 			      void *srcs, u32 nsrcs, size_t src_size)
19555bf1e00bSNikolay Aleksandrov {
19565bf1e00bSNikolay Aleksandrov 	bool changed = false;
19575bf1e00bSNikolay Aleksandrov 
19585bf1e00bSNikolay Aleksandrov 	switch (pg->filter_mode) {
19595bf1e00bSNikolay Aleksandrov 	case MCAST_INCLUDE:
19605bf1e00bSNikolay Aleksandrov 		changed = __grp_src_toin_incl(pg, srcs, nsrcs, src_size);
19615bf1e00bSNikolay Aleksandrov 		break;
19625bf1e00bSNikolay Aleksandrov 	case MCAST_EXCLUDE:
19635bf1e00bSNikolay Aleksandrov 		changed = __grp_src_toin_excl(pg, srcs, nsrcs, src_size);
19645bf1e00bSNikolay Aleksandrov 		break;
19655bf1e00bSNikolay Aleksandrov 	}
19665bf1e00bSNikolay Aleksandrov 
19675bf1e00bSNikolay Aleksandrov 	return changed;
19685bf1e00bSNikolay Aleksandrov }
19695bf1e00bSNikolay Aleksandrov 
19705bf1e00bSNikolay Aleksandrov /* State          Msg type      New state                Actions
19715bf1e00bSNikolay Aleksandrov  * INCLUDE (A)    TO_EX (B)     EXCLUDE (A*B,B-A)        (B-A)=0
19725bf1e00bSNikolay Aleksandrov  *                                                       Delete (A-B)
19735bf1e00bSNikolay Aleksandrov  *                                                       Send Q(G,A*B)
19745bf1e00bSNikolay Aleksandrov  *                                                       Group Timer=GMI
19755bf1e00bSNikolay Aleksandrov  */
19765bf1e00bSNikolay Aleksandrov static void __grp_src_toex_incl(struct net_bridge_port_group *pg,
19775bf1e00bSNikolay Aleksandrov 				void *srcs, u32 nsrcs, size_t src_size)
19785bf1e00bSNikolay Aleksandrov {
19795bf1e00bSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
19805bf1e00bSNikolay Aleksandrov 	u32 src_idx, to_send = 0;
19815bf1e00bSNikolay Aleksandrov 	struct br_ip src_ip;
19825bf1e00bSNikolay Aleksandrov 
19835bf1e00bSNikolay Aleksandrov 	hlist_for_each_entry(ent, &pg->src_list, node)
19845bf1e00bSNikolay Aleksandrov 		ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE;
19855bf1e00bSNikolay Aleksandrov 
19865bf1e00bSNikolay Aleksandrov 	memset(&src_ip, 0, sizeof(src_ip));
1987085b53c8SNikolay Aleksandrov 	src_ip.proto = pg->key.addr.proto;
19885bf1e00bSNikolay Aleksandrov 	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
1989deb96566SNikolay Aleksandrov 		memcpy(&src_ip.src, srcs, src_size);
19905bf1e00bSNikolay Aleksandrov 		ent = br_multicast_find_group_src(pg, &src_ip);
19915bf1e00bSNikolay Aleksandrov 		if (ent) {
19925bf1e00bSNikolay Aleksandrov 			ent->flags = (ent->flags & ~BR_SGRP_F_DELETE) |
19935bf1e00bSNikolay Aleksandrov 				     BR_SGRP_F_SEND;
19945bf1e00bSNikolay Aleksandrov 			to_send++;
19955bf1e00bSNikolay Aleksandrov 		} else {
1996b0812368SNikolay Aleksandrov 			ent = br_multicast_new_group_src(pg, &src_ip);
19975bf1e00bSNikolay Aleksandrov 		}
1998b0812368SNikolay Aleksandrov 		if (ent)
1999b0812368SNikolay Aleksandrov 			br_multicast_fwd_src_handle(ent);
20005bf1e00bSNikolay Aleksandrov 		srcs += src_size;
20015bf1e00bSNikolay Aleksandrov 	}
20025bf1e00bSNikolay Aleksandrov 
20035bf1e00bSNikolay Aleksandrov 	__grp_src_delete_marked(pg);
20045bf1e00bSNikolay Aleksandrov 	if (to_send)
20055bf1e00bSNikolay Aleksandrov 		__grp_src_query_marked_and_rexmit(pg);
20065bf1e00bSNikolay Aleksandrov }
20075bf1e00bSNikolay Aleksandrov 
20085bf1e00bSNikolay Aleksandrov /* State          Msg type      New state                Actions
20095bf1e00bSNikolay Aleksandrov  * EXCLUDE (X,Y)  TO_EX (A)     EXCLUDE (A-Y,Y*A)        (A-X-Y)=Group Timer
20105bf1e00bSNikolay Aleksandrov  *                                                       Delete (X-A)
20115bf1e00bSNikolay Aleksandrov  *                                                       Delete (Y-A)
20125bf1e00bSNikolay Aleksandrov  *                                                       Send Q(G,A-Y)
20135bf1e00bSNikolay Aleksandrov  *                                                       Group Timer=GMI
20145bf1e00bSNikolay Aleksandrov  */
20155bf1e00bSNikolay Aleksandrov static bool __grp_src_toex_excl(struct net_bridge_port_group *pg,
20165bf1e00bSNikolay Aleksandrov 				void *srcs, u32 nsrcs, size_t src_size)
20175bf1e00bSNikolay Aleksandrov {
20185bf1e00bSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
20195bf1e00bSNikolay Aleksandrov 	u32 src_idx, to_send = 0;
20205bf1e00bSNikolay Aleksandrov 	bool changed = false;
20215bf1e00bSNikolay Aleksandrov 	struct br_ip src_ip;
20225bf1e00bSNikolay Aleksandrov 
20235bf1e00bSNikolay Aleksandrov 	hlist_for_each_entry(ent, &pg->src_list, node)
20245bf1e00bSNikolay Aleksandrov 		ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE;
20255bf1e00bSNikolay Aleksandrov 
20265bf1e00bSNikolay Aleksandrov 	memset(&src_ip, 0, sizeof(src_ip));
2027085b53c8SNikolay Aleksandrov 	src_ip.proto = pg->key.addr.proto;
20285bf1e00bSNikolay Aleksandrov 	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
2029deb96566SNikolay Aleksandrov 		memcpy(&src_ip.src, srcs, src_size);
20305bf1e00bSNikolay Aleksandrov 		ent = br_multicast_find_group_src(pg, &src_ip);
20315bf1e00bSNikolay Aleksandrov 		if (ent) {
20325bf1e00bSNikolay Aleksandrov 			ent->flags &= ~BR_SGRP_F_DELETE;
20335bf1e00bSNikolay Aleksandrov 		} else {
20345bf1e00bSNikolay Aleksandrov 			ent = br_multicast_new_group_src(pg, &src_ip);
20355bf1e00bSNikolay Aleksandrov 			if (ent) {
2036b0812368SNikolay Aleksandrov 				__grp_src_mod_timer(ent, pg->timer.expires);
20375bf1e00bSNikolay Aleksandrov 				changed = true;
20385bf1e00bSNikolay Aleksandrov 			}
20395bf1e00bSNikolay Aleksandrov 		}
20405bf1e00bSNikolay Aleksandrov 		if (ent && timer_pending(&ent->timer)) {
20415bf1e00bSNikolay Aleksandrov 			ent->flags |= BR_SGRP_F_SEND;
20425bf1e00bSNikolay Aleksandrov 			to_send++;
20435bf1e00bSNikolay Aleksandrov 		}
20445bf1e00bSNikolay Aleksandrov 		srcs += src_size;
20455bf1e00bSNikolay Aleksandrov 	}
20465bf1e00bSNikolay Aleksandrov 
20475bf1e00bSNikolay Aleksandrov 	if (__grp_src_delete_marked(pg))
20485bf1e00bSNikolay Aleksandrov 		changed = true;
20495bf1e00bSNikolay Aleksandrov 	if (to_send)
20505bf1e00bSNikolay Aleksandrov 		__grp_src_query_marked_and_rexmit(pg);
20515bf1e00bSNikolay Aleksandrov 
20525bf1e00bSNikolay Aleksandrov 	return changed;
20535bf1e00bSNikolay Aleksandrov }
20545bf1e00bSNikolay Aleksandrov 
20555bf1e00bSNikolay Aleksandrov static bool br_multicast_toex(struct net_bridge_port_group *pg,
20565bf1e00bSNikolay Aleksandrov 			      void *srcs, u32 nsrcs, size_t src_size)
20575bf1e00bSNikolay Aleksandrov {
2058085b53c8SNikolay Aleksandrov 	struct net_bridge *br = pg->key.port->br;
20595bf1e00bSNikolay Aleksandrov 	bool changed = false;
20605bf1e00bSNikolay Aleksandrov 
20615bf1e00bSNikolay Aleksandrov 	switch (pg->filter_mode) {
20625bf1e00bSNikolay Aleksandrov 	case MCAST_INCLUDE:
20635bf1e00bSNikolay Aleksandrov 		__grp_src_toex_incl(pg, srcs, nsrcs, src_size);
20648266a049SNikolay Aleksandrov 		br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE);
20655bf1e00bSNikolay Aleksandrov 		changed = true;
20665bf1e00bSNikolay Aleksandrov 		break;
20675bf1e00bSNikolay Aleksandrov 	case MCAST_EXCLUDE:
2068d5bf31ddSNikolay Aleksandrov 		changed = __grp_src_toex_excl(pg, srcs, nsrcs, src_size);
20695bf1e00bSNikolay Aleksandrov 		break;
20705bf1e00bSNikolay Aleksandrov 	}
20715bf1e00bSNikolay Aleksandrov 
20725bf1e00bSNikolay Aleksandrov 	pg->filter_mode = MCAST_EXCLUDE;
20735bf1e00bSNikolay Aleksandrov 	mod_timer(&pg->timer, jiffies + br_multicast_gmi(br));
20745bf1e00bSNikolay Aleksandrov 
20755bf1e00bSNikolay Aleksandrov 	return changed;
20765bf1e00bSNikolay Aleksandrov }
20775bf1e00bSNikolay Aleksandrov 
2078109865feSNikolay Aleksandrov /* State          Msg type      New state                Actions
2079109865feSNikolay Aleksandrov  * INCLUDE (A)    BLOCK (B)     INCLUDE (A)              Send Q(G,A*B)
2080109865feSNikolay Aleksandrov  */
2081109865feSNikolay Aleksandrov static void __grp_src_block_incl(struct net_bridge_port_group *pg,
2082109865feSNikolay Aleksandrov 				 void *srcs, u32 nsrcs, size_t src_size)
2083109865feSNikolay Aleksandrov {
2084109865feSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
2085109865feSNikolay Aleksandrov 	u32 src_idx, to_send = 0;
2086109865feSNikolay Aleksandrov 	struct br_ip src_ip;
2087109865feSNikolay Aleksandrov 
2088109865feSNikolay Aleksandrov 	hlist_for_each_entry(ent, &pg->src_list, node)
2089109865feSNikolay Aleksandrov 		ent->flags &= ~BR_SGRP_F_SEND;
2090109865feSNikolay Aleksandrov 
2091109865feSNikolay Aleksandrov 	memset(&src_ip, 0, sizeof(src_ip));
2092085b53c8SNikolay Aleksandrov 	src_ip.proto = pg->key.addr.proto;
2093109865feSNikolay Aleksandrov 	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
2094deb96566SNikolay Aleksandrov 		memcpy(&src_ip.src, srcs, src_size);
2095109865feSNikolay Aleksandrov 		ent = br_multicast_find_group_src(pg, &src_ip);
2096109865feSNikolay Aleksandrov 		if (ent) {
2097109865feSNikolay Aleksandrov 			ent->flags |= BR_SGRP_F_SEND;
2098109865feSNikolay Aleksandrov 			to_send++;
2099109865feSNikolay Aleksandrov 		}
2100109865feSNikolay Aleksandrov 		srcs += src_size;
2101109865feSNikolay Aleksandrov 	}
2102109865feSNikolay Aleksandrov 
2103109865feSNikolay Aleksandrov 	if (to_send)
2104109865feSNikolay Aleksandrov 		__grp_src_query_marked_and_rexmit(pg);
2105109865feSNikolay Aleksandrov 
2106109865feSNikolay Aleksandrov 	if (pg->filter_mode == MCAST_INCLUDE && hlist_empty(&pg->src_list))
2107085b53c8SNikolay Aleksandrov 		br_multicast_find_del_pg(pg->key.port->br, pg);
2108109865feSNikolay Aleksandrov }
2109109865feSNikolay Aleksandrov 
2110109865feSNikolay Aleksandrov /* State          Msg type      New state                Actions
2111109865feSNikolay Aleksandrov  * EXCLUDE (X,Y)  BLOCK (A)     EXCLUDE (X+(A-Y),Y)      (A-X-Y)=Group Timer
2112109865feSNikolay Aleksandrov  *                                                       Send Q(G,A-Y)
2113109865feSNikolay Aleksandrov  */
2114109865feSNikolay Aleksandrov static bool __grp_src_block_excl(struct net_bridge_port_group *pg,
2115109865feSNikolay Aleksandrov 				 void *srcs, u32 nsrcs, size_t src_size)
2116109865feSNikolay Aleksandrov {
2117109865feSNikolay Aleksandrov 	struct net_bridge_group_src *ent;
2118109865feSNikolay Aleksandrov 	u32 src_idx, to_send = 0;
2119109865feSNikolay Aleksandrov 	bool changed = false;
2120109865feSNikolay Aleksandrov 	struct br_ip src_ip;
2121109865feSNikolay Aleksandrov 
2122109865feSNikolay Aleksandrov 	hlist_for_each_entry(ent, &pg->src_list, node)
2123109865feSNikolay Aleksandrov 		ent->flags &= ~BR_SGRP_F_SEND;
2124109865feSNikolay Aleksandrov 
2125109865feSNikolay Aleksandrov 	memset(&src_ip, 0, sizeof(src_ip));
2126085b53c8SNikolay Aleksandrov 	src_ip.proto = pg->key.addr.proto;
2127109865feSNikolay Aleksandrov 	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
2128deb96566SNikolay Aleksandrov 		memcpy(&src_ip.src, srcs, src_size);
2129109865feSNikolay Aleksandrov 		ent = br_multicast_find_group_src(pg, &src_ip);
2130109865feSNikolay Aleksandrov 		if (!ent) {
2131109865feSNikolay Aleksandrov 			ent = br_multicast_new_group_src(pg, &src_ip);
2132109865feSNikolay Aleksandrov 			if (ent) {
2133b0812368SNikolay Aleksandrov 				__grp_src_mod_timer(ent, pg->timer.expires);
2134109865feSNikolay Aleksandrov 				changed = true;
2135109865feSNikolay Aleksandrov 			}
2136109865feSNikolay Aleksandrov 		}
2137109865feSNikolay Aleksandrov 		if (ent && timer_pending(&ent->timer)) {
2138109865feSNikolay Aleksandrov 			ent->flags |= BR_SGRP_F_SEND;
2139109865feSNikolay Aleksandrov 			to_send++;
2140109865feSNikolay Aleksandrov 		}
2141109865feSNikolay Aleksandrov 		srcs += src_size;
2142109865feSNikolay Aleksandrov 	}
2143109865feSNikolay Aleksandrov 
2144109865feSNikolay Aleksandrov 	if (to_send)
2145109865feSNikolay Aleksandrov 		__grp_src_query_marked_and_rexmit(pg);
2146109865feSNikolay Aleksandrov 
2147109865feSNikolay Aleksandrov 	return changed;
2148109865feSNikolay Aleksandrov }
2149109865feSNikolay Aleksandrov 
2150109865feSNikolay Aleksandrov static bool br_multicast_block(struct net_bridge_port_group *pg,
2151109865feSNikolay Aleksandrov 			       void *srcs, u32 nsrcs, size_t src_size)
2152109865feSNikolay Aleksandrov {
2153109865feSNikolay Aleksandrov 	bool changed = false;
2154109865feSNikolay Aleksandrov 
2155109865feSNikolay Aleksandrov 	switch (pg->filter_mode) {
2156109865feSNikolay Aleksandrov 	case MCAST_INCLUDE:
2157109865feSNikolay Aleksandrov 		__grp_src_block_incl(pg, srcs, nsrcs, src_size);
2158109865feSNikolay Aleksandrov 		break;
2159109865feSNikolay Aleksandrov 	case MCAST_EXCLUDE:
2160109865feSNikolay Aleksandrov 		changed = __grp_src_block_excl(pg, srcs, nsrcs, src_size);
2161109865feSNikolay Aleksandrov 		break;
2162109865feSNikolay Aleksandrov 	}
2163109865feSNikolay Aleksandrov 
2164109865feSNikolay Aleksandrov 	return changed;
2165109865feSNikolay Aleksandrov }
2166109865feSNikolay Aleksandrov 
21670436862eSNikolay Aleksandrov static struct net_bridge_port_group *
21680436862eSNikolay Aleksandrov br_multicast_find_port(struct net_bridge_mdb_entry *mp,
21690436862eSNikolay Aleksandrov 		       struct net_bridge_port *p,
21700436862eSNikolay Aleksandrov 		       const unsigned char *src)
21710436862eSNikolay Aleksandrov {
2172071445c6SNikolay Aleksandrov 	struct net_bridge *br __maybe_unused = mp->br;
21730436862eSNikolay Aleksandrov 	struct net_bridge_port_group *pg;
21740436862eSNikolay Aleksandrov 
21750436862eSNikolay Aleksandrov 	for (pg = mlock_dereference(mp->ports, br);
21760436862eSNikolay Aleksandrov 	     pg;
21770436862eSNikolay Aleksandrov 	     pg = mlock_dereference(pg->next, br))
21780436862eSNikolay Aleksandrov 		if (br_port_group_equal(pg, p, src))
21790436862eSNikolay Aleksandrov 			return pg;
21800436862eSNikolay Aleksandrov 
21810436862eSNikolay Aleksandrov 	return NULL;
21820436862eSNikolay Aleksandrov }
21830436862eSNikolay Aleksandrov 
21848ef2a9a5SYOSHIFUJI Hideaki static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
2185eb1d1641SHerbert Xu 					 struct net_bridge_port *port,
218606499098SVlad Yasevich 					 struct sk_buff *skb,
218706499098SVlad Yasevich 					 u16 vid)
2188eb1d1641SHerbert Xu {
21890436862eSNikolay Aleksandrov 	bool igmpv2 = br->multicast_igmp_version == 2;
21900436862eSNikolay Aleksandrov 	struct net_bridge_mdb_entry *mdst;
21910436862eSNikolay Aleksandrov 	struct net_bridge_port_group *pg;
21926db6f0eaSFelix Fietkau 	const unsigned char *src;
2193eb1d1641SHerbert Xu 	struct igmpv3_report *ih;
2194eb1d1641SHerbert Xu 	struct igmpv3_grec *grec;
21950436862eSNikolay Aleksandrov 	int i, len, num, type;
21960436862eSNikolay Aleksandrov 	bool changed = false;
2197eb1d1641SHerbert Xu 	__be32 group;
21980436862eSNikolay Aleksandrov 	int err = 0;
2199e57f6185SNikolay Aleksandrov 	u16 nsrcs;
2200eb1d1641SHerbert Xu 
2201eb1d1641SHerbert Xu 	ih = igmpv3_report_hdr(skb);
2202eb1d1641SHerbert Xu 	num = ntohs(ih->ngrec);
2203c2d4fbd2SLinus Lüssing 	len = skb_transport_offset(skb) + sizeof(*ih);
2204eb1d1641SHerbert Xu 
2205eb1d1641SHerbert Xu 	for (i = 0; i < num; i++) {
2206eb1d1641SHerbert Xu 		len += sizeof(*grec);
2207ba5ea614SLinus Lüssing 		if (!ip_mc_may_pull(skb, len))
2208eb1d1641SHerbert Xu 			return -EINVAL;
2209eb1d1641SHerbert Xu 
2210fd218cf9SHerbert Xu 		grec = (void *)(skb->data + len - sizeof(*grec));
2211eb1d1641SHerbert Xu 		group = grec->grec_mca;
2212eb1d1641SHerbert Xu 		type = grec->grec_type;
2213e57f6185SNikolay Aleksandrov 		nsrcs = ntohs(grec->grec_nsrcs);
2214eb1d1641SHerbert Xu 
2215e57f6185SNikolay Aleksandrov 		len += nsrcs * 4;
2216ba5ea614SLinus Lüssing 		if (!ip_mc_may_pull(skb, len))
2217eb1d1641SHerbert Xu 			return -EINVAL;
2218eb1d1641SHerbert Xu 
2219eb1d1641SHerbert Xu 		switch (type) {
2220eb1d1641SHerbert Xu 		case IGMPV3_MODE_IS_INCLUDE:
2221eb1d1641SHerbert Xu 		case IGMPV3_MODE_IS_EXCLUDE:
2222eb1d1641SHerbert Xu 		case IGMPV3_CHANGE_TO_INCLUDE:
2223eb1d1641SHerbert Xu 		case IGMPV3_CHANGE_TO_EXCLUDE:
2224eb1d1641SHerbert Xu 		case IGMPV3_ALLOW_NEW_SOURCES:
2225eb1d1641SHerbert Xu 		case IGMPV3_BLOCK_OLD_SOURCES:
2226eb1d1641SHerbert Xu 			break;
2227eb1d1641SHerbert Xu 
2228eb1d1641SHerbert Xu 		default:
2229eb1d1641SHerbert Xu 			continue;
2230eb1d1641SHerbert Xu 		}
2231eb1d1641SHerbert Xu 
22326db6f0eaSFelix Fietkau 		src = eth_hdr(skb)->h_source;
22330436862eSNikolay Aleksandrov 		if (nsrcs == 0 &&
22340436862eSNikolay Aleksandrov 		    (type == IGMPV3_CHANGE_TO_INCLUDE ||
22350436862eSNikolay Aleksandrov 		     type == IGMPV3_MODE_IS_INCLUDE)) {
22360436862eSNikolay Aleksandrov 			if (!port || igmpv2) {
22376db6f0eaSFelix Fietkau 				br_ip4_multicast_leave_group(br, port, group, vid, src);
22380436862eSNikolay Aleksandrov 				continue;
22390436862eSNikolay Aleksandrov 			}
2240bc8c20acSSatish Ashok 		} else {
22416db6f0eaSFelix Fietkau 			err = br_ip4_multicast_add_group(br, port, group, vid,
22420436862eSNikolay Aleksandrov 							 src, igmpv2);
2243eb1d1641SHerbert Xu 			if (err)
2244eb1d1641SHerbert Xu 				break;
2245eb1d1641SHerbert Xu 		}
22460436862eSNikolay Aleksandrov 
22470436862eSNikolay Aleksandrov 		if (!port || igmpv2)
22480436862eSNikolay Aleksandrov 			continue;
22490436862eSNikolay Aleksandrov 
22500436862eSNikolay Aleksandrov 		spin_lock_bh(&br->multicast_lock);
22510436862eSNikolay Aleksandrov 		mdst = br_mdb_ip4_get(br, group, vid);
22520436862eSNikolay Aleksandrov 		if (!mdst)
22530436862eSNikolay Aleksandrov 			goto unlock_continue;
22540436862eSNikolay Aleksandrov 		pg = br_multicast_find_port(mdst, port, src);
22550436862eSNikolay Aleksandrov 		if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT))
22560436862eSNikolay Aleksandrov 			goto unlock_continue;
22570436862eSNikolay Aleksandrov 		/* reload grec */
22580436862eSNikolay Aleksandrov 		grec = (void *)(skb->data + len - sizeof(*grec) - (nsrcs * 4));
22590436862eSNikolay Aleksandrov 		switch (type) {
22600436862eSNikolay Aleksandrov 		case IGMPV3_ALLOW_NEW_SOURCES:
22610436862eSNikolay Aleksandrov 			changed = br_multicast_isinc_allow(pg, grec->grec_src,
22620436862eSNikolay Aleksandrov 							   nsrcs, sizeof(__be32));
22630436862eSNikolay Aleksandrov 			break;
2264e6231bcaSNikolay Aleksandrov 		case IGMPV3_MODE_IS_INCLUDE:
2265e6231bcaSNikolay Aleksandrov 			changed = br_multicast_isinc_allow(pg, grec->grec_src, nsrcs,
2266e6231bcaSNikolay Aleksandrov 							   sizeof(__be32));
2267e6231bcaSNikolay Aleksandrov 			break;
2268e6231bcaSNikolay Aleksandrov 		case IGMPV3_MODE_IS_EXCLUDE:
2269e6231bcaSNikolay Aleksandrov 			changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
2270e6231bcaSNikolay Aleksandrov 						     sizeof(__be32));
2271e6231bcaSNikolay Aleksandrov 			break;
22725bf1e00bSNikolay Aleksandrov 		case IGMPV3_CHANGE_TO_INCLUDE:
22735bf1e00bSNikolay Aleksandrov 			changed = br_multicast_toin(pg, grec->grec_src, nsrcs,
22745bf1e00bSNikolay Aleksandrov 						    sizeof(__be32));
22755bf1e00bSNikolay Aleksandrov 			break;
22765bf1e00bSNikolay Aleksandrov 		case IGMPV3_CHANGE_TO_EXCLUDE:
22775bf1e00bSNikolay Aleksandrov 			changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
22785bf1e00bSNikolay Aleksandrov 						    sizeof(__be32));
22795bf1e00bSNikolay Aleksandrov 			break;
2280109865feSNikolay Aleksandrov 		case IGMPV3_BLOCK_OLD_SOURCES:
2281109865feSNikolay Aleksandrov 			changed = br_multicast_block(pg, grec->grec_src, nsrcs,
2282109865feSNikolay Aleksandrov 						     sizeof(__be32));
2283109865feSNikolay Aleksandrov 			break;
22840436862eSNikolay Aleksandrov 		}
22850436862eSNikolay Aleksandrov 		if (changed)
22860436862eSNikolay Aleksandrov 			br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);
22870436862eSNikolay Aleksandrov unlock_continue:
22880436862eSNikolay Aleksandrov 		spin_unlock_bh(&br->multicast_lock);
2289bc8c20acSSatish Ashok 	}
2290eb1d1641SHerbert Xu 
2291eb1d1641SHerbert Xu 	return err;
2292eb1d1641SHerbert Xu }
2293eb1d1641SHerbert Xu 
2294dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
229508b202b6SYOSHIFUJI Hideaki static int br_ip6_multicast_mld2_report(struct net_bridge *br,
229608b202b6SYOSHIFUJI Hideaki 					struct net_bridge_port *port,
229706499098SVlad Yasevich 					struct sk_buff *skb,
229806499098SVlad Yasevich 					u16 vid)
229908b202b6SYOSHIFUJI Hideaki {
23000436862eSNikolay Aleksandrov 	bool mldv1 = br->multicast_mld_version == 1;
23010436862eSNikolay Aleksandrov 	struct net_bridge_mdb_entry *mdst;
23020436862eSNikolay Aleksandrov 	struct net_bridge_port_group *pg;
2303ba5ea614SLinus Lüssing 	unsigned int nsrcs_offset;
23046db6f0eaSFelix Fietkau 	const unsigned char *src;
230508b202b6SYOSHIFUJI Hideaki 	struct icmp6hdr *icmp6h;
230608b202b6SYOSHIFUJI Hideaki 	struct mld2_grec *grec;
2307ba5ea614SLinus Lüssing 	unsigned int grec_len;
23080436862eSNikolay Aleksandrov 	bool changed = false;
23090436862eSNikolay Aleksandrov 	int i, len, num;
231008b202b6SYOSHIFUJI Hideaki 	int err = 0;
231108b202b6SYOSHIFUJI Hideaki 
2312ba5ea614SLinus Lüssing 	if (!ipv6_mc_may_pull(skb, sizeof(*icmp6h)))
231308b202b6SYOSHIFUJI Hideaki 		return -EINVAL;
231408b202b6SYOSHIFUJI Hideaki 
231508b202b6SYOSHIFUJI Hideaki 	icmp6h = icmp6_hdr(skb);
231608b202b6SYOSHIFUJI Hideaki 	num = ntohs(icmp6h->icmp6_dataun.un_data16[1]);
2317c2d4fbd2SLinus Lüssing 	len = skb_transport_offset(skb) + sizeof(*icmp6h);
231808b202b6SYOSHIFUJI Hideaki 
231908b202b6SYOSHIFUJI Hideaki 	for (i = 0; i < num; i++) {
2320e57f6185SNikolay Aleksandrov 		__be16 *_nsrcs, __nsrcs;
2321e57f6185SNikolay Aleksandrov 		u16 nsrcs;
232208b202b6SYOSHIFUJI Hideaki 
2323ba5ea614SLinus Lüssing 		nsrcs_offset = len + offsetof(struct mld2_grec, grec_nsrcs);
2324ba5ea614SLinus Lüssing 
2325ba5ea614SLinus Lüssing 		if (skb_transport_offset(skb) + ipv6_transport_len(skb) <
23265fc6266aSLinus Lüssing 		    nsrcs_offset + sizeof(__nsrcs))
2327ba5ea614SLinus Lüssing 			return -EINVAL;
2328ba5ea614SLinus Lüssing 
2329e57f6185SNikolay Aleksandrov 		_nsrcs = skb_header_pointer(skb, nsrcs_offset,
2330e57f6185SNikolay Aleksandrov 					    sizeof(__nsrcs), &__nsrcs);
2331e57f6185SNikolay Aleksandrov 		if (!_nsrcs)
233208b202b6SYOSHIFUJI Hideaki 			return -EINVAL;
233308b202b6SYOSHIFUJI Hideaki 
2334e57f6185SNikolay Aleksandrov 		nsrcs = ntohs(*_nsrcs);
2335e57f6185SNikolay Aleksandrov 		grec_len = struct_size(grec, grec_src, nsrcs);
2336ba5ea614SLinus Lüssing 
2337ba5ea614SLinus Lüssing 		if (!ipv6_mc_may_pull(skb, len + grec_len))
233808b202b6SYOSHIFUJI Hideaki 			return -EINVAL;
233908b202b6SYOSHIFUJI Hideaki 
234008b202b6SYOSHIFUJI Hideaki 		grec = (struct mld2_grec *)(skb->data + len);
2341ba5ea614SLinus Lüssing 		len += grec_len;
234208b202b6SYOSHIFUJI Hideaki 
234308b202b6SYOSHIFUJI Hideaki 		switch (grec->grec_type) {
234408b202b6SYOSHIFUJI Hideaki 		case MLD2_MODE_IS_INCLUDE:
234508b202b6SYOSHIFUJI Hideaki 		case MLD2_MODE_IS_EXCLUDE:
234608b202b6SYOSHIFUJI Hideaki 		case MLD2_CHANGE_TO_INCLUDE:
234708b202b6SYOSHIFUJI Hideaki 		case MLD2_CHANGE_TO_EXCLUDE:
234808b202b6SYOSHIFUJI Hideaki 		case MLD2_ALLOW_NEW_SOURCES:
234908b202b6SYOSHIFUJI Hideaki 		case MLD2_BLOCK_OLD_SOURCES:
235008b202b6SYOSHIFUJI Hideaki 			break;
235108b202b6SYOSHIFUJI Hideaki 
235208b202b6SYOSHIFUJI Hideaki 		default:
235308b202b6SYOSHIFUJI Hideaki 			continue;
235408b202b6SYOSHIFUJI Hideaki 		}
235508b202b6SYOSHIFUJI Hideaki 
23566db6f0eaSFelix Fietkau 		src = eth_hdr(skb)->h_source;
2357bc8c20acSSatish Ashok 		if ((grec->grec_type == MLD2_CHANGE_TO_INCLUDE ||
2358bc8c20acSSatish Ashok 		     grec->grec_type == MLD2_MODE_IS_INCLUDE) &&
2359e57f6185SNikolay Aleksandrov 		    nsrcs == 0) {
23600436862eSNikolay Aleksandrov 			if (!port || mldv1) {
23610436862eSNikolay Aleksandrov 				br_ip6_multicast_leave_group(br, port,
23620436862eSNikolay Aleksandrov 							     &grec->grec_mca,
23636db6f0eaSFelix Fietkau 							     vid, src);
23640436862eSNikolay Aleksandrov 				continue;
23650436862eSNikolay Aleksandrov 			}
2366bc8c20acSSatish Ashok 		} else {
2367bc8c20acSSatish Ashok 			err = br_ip6_multicast_add_group(br, port,
23686db6f0eaSFelix Fietkau 							 &grec->grec_mca, vid,
23690436862eSNikolay Aleksandrov 							 src, mldv1);
23709264251eSDavide Caratti 			if (err)
237108b202b6SYOSHIFUJI Hideaki 				break;
237208b202b6SYOSHIFUJI Hideaki 		}
23730436862eSNikolay Aleksandrov 
23740436862eSNikolay Aleksandrov 		if (!port || mldv1)
23750436862eSNikolay Aleksandrov 			continue;
23760436862eSNikolay Aleksandrov 
23770436862eSNikolay Aleksandrov 		spin_lock_bh(&br->multicast_lock);
23780436862eSNikolay Aleksandrov 		mdst = br_mdb_ip6_get(br, &grec->grec_mca, vid);
23790436862eSNikolay Aleksandrov 		if (!mdst)
23800436862eSNikolay Aleksandrov 			goto unlock_continue;
23810436862eSNikolay Aleksandrov 		pg = br_multicast_find_port(mdst, port, src);
23820436862eSNikolay Aleksandrov 		if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT))
23830436862eSNikolay Aleksandrov 			goto unlock_continue;
23840436862eSNikolay Aleksandrov 		switch (grec->grec_type) {
23850436862eSNikolay Aleksandrov 		case MLD2_ALLOW_NEW_SOURCES:
23860436862eSNikolay Aleksandrov 			changed = br_multicast_isinc_allow(pg, grec->grec_src,
23870436862eSNikolay Aleksandrov 							   nsrcs,
23880436862eSNikolay Aleksandrov 							   sizeof(struct in6_addr));
23890436862eSNikolay Aleksandrov 			break;
2390e6231bcaSNikolay Aleksandrov 		case MLD2_MODE_IS_INCLUDE:
2391e6231bcaSNikolay Aleksandrov 			changed = br_multicast_isinc_allow(pg, grec->grec_src, nsrcs,
2392e6231bcaSNikolay Aleksandrov 							   sizeof(struct in6_addr));
2393e6231bcaSNikolay Aleksandrov 			break;
2394e6231bcaSNikolay Aleksandrov 		case MLD2_MODE_IS_EXCLUDE:
2395e6231bcaSNikolay Aleksandrov 			changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
2396e6231bcaSNikolay Aleksandrov 						     sizeof(struct in6_addr));
2397e6231bcaSNikolay Aleksandrov 			break;
23985bf1e00bSNikolay Aleksandrov 		case MLD2_CHANGE_TO_INCLUDE:
23995bf1e00bSNikolay Aleksandrov 			changed = br_multicast_toin(pg, grec->grec_src, nsrcs,
24005bf1e00bSNikolay Aleksandrov 						    sizeof(struct in6_addr));
24015bf1e00bSNikolay Aleksandrov 			break;
24025bf1e00bSNikolay Aleksandrov 		case MLD2_CHANGE_TO_EXCLUDE:
24035bf1e00bSNikolay Aleksandrov 			changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
24045bf1e00bSNikolay Aleksandrov 						    sizeof(struct in6_addr));
24055bf1e00bSNikolay Aleksandrov 			break;
2406109865feSNikolay Aleksandrov 		case MLD2_BLOCK_OLD_SOURCES:
2407109865feSNikolay Aleksandrov 			changed = br_multicast_block(pg, grec->grec_src, nsrcs,
2408109865feSNikolay Aleksandrov 						     sizeof(struct in6_addr));
2409109865feSNikolay Aleksandrov 			break;
24100436862eSNikolay Aleksandrov 		}
24110436862eSNikolay Aleksandrov 		if (changed)
24120436862eSNikolay Aleksandrov 			br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);
24130436862eSNikolay Aleksandrov unlock_continue:
24140436862eSNikolay Aleksandrov 		spin_unlock_bh(&br->multicast_lock);
2415bc8c20acSSatish Ashok 	}
241608b202b6SYOSHIFUJI Hideaki 
241708b202b6SYOSHIFUJI Hideaki 	return err;
241808b202b6SYOSHIFUJI Hideaki }
241908b202b6SYOSHIFUJI Hideaki #endif
242008b202b6SYOSHIFUJI Hideaki 
2421dc4eb53aSLinus Lüssing static bool br_ip4_multicast_select_querier(struct net_bridge *br,
24222cd41431SLinus Lüssing 					    struct net_bridge_port *port,
2423dc4eb53aSLinus Lüssing 					    __be32 saddr)
2424dc4eb53aSLinus Lüssing {
2425dc4eb53aSLinus Lüssing 	if (!timer_pending(&br->ip4_own_query.timer) &&
2426dc4eb53aSLinus Lüssing 	    !timer_pending(&br->ip4_other_query.timer))
2427dc4eb53aSLinus Lüssing 		goto update;
2428dc4eb53aSLinus Lüssing 
2429deb96566SNikolay Aleksandrov 	if (!br->ip4_querier.addr.src.ip4)
2430dc4eb53aSLinus Lüssing 		goto update;
2431dc4eb53aSLinus Lüssing 
2432deb96566SNikolay Aleksandrov 	if (ntohl(saddr) <= ntohl(br->ip4_querier.addr.src.ip4))
2433dc4eb53aSLinus Lüssing 		goto update;
2434dc4eb53aSLinus Lüssing 
2435dc4eb53aSLinus Lüssing 	return false;
2436dc4eb53aSLinus Lüssing 
2437dc4eb53aSLinus Lüssing update:
2438deb96566SNikolay Aleksandrov 	br->ip4_querier.addr.src.ip4 = saddr;
2439dc4eb53aSLinus Lüssing 
24402cd41431SLinus Lüssing 	/* update protected by general multicast_lock by caller */
24412cd41431SLinus Lüssing 	rcu_assign_pointer(br->ip4_querier.port, port);
24422cd41431SLinus Lüssing 
2443dc4eb53aSLinus Lüssing 	return true;
2444dc4eb53aSLinus Lüssing }
2445dc4eb53aSLinus Lüssing 
2446dc4eb53aSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
2447dc4eb53aSLinus Lüssing static bool br_ip6_multicast_select_querier(struct net_bridge *br,
24482cd41431SLinus Lüssing 					    struct net_bridge_port *port,
2449dc4eb53aSLinus Lüssing 					    struct in6_addr *saddr)
2450dc4eb53aSLinus Lüssing {
2451dc4eb53aSLinus Lüssing 	if (!timer_pending(&br->ip6_own_query.timer) &&
2452dc4eb53aSLinus Lüssing 	    !timer_pending(&br->ip6_other_query.timer))
2453dc4eb53aSLinus Lüssing 		goto update;
2454dc4eb53aSLinus Lüssing 
2455deb96566SNikolay Aleksandrov 	if (ipv6_addr_cmp(saddr, &br->ip6_querier.addr.src.ip6) <= 0)
2456dc4eb53aSLinus Lüssing 		goto update;
2457dc4eb53aSLinus Lüssing 
2458dc4eb53aSLinus Lüssing 	return false;
2459dc4eb53aSLinus Lüssing 
2460dc4eb53aSLinus Lüssing update:
2461deb96566SNikolay Aleksandrov 	br->ip6_querier.addr.src.ip6 = *saddr;
2462dc4eb53aSLinus Lüssing 
24632cd41431SLinus Lüssing 	/* update protected by general multicast_lock by caller */
24642cd41431SLinus Lüssing 	rcu_assign_pointer(br->ip6_querier.port, port);
24652cd41431SLinus Lüssing 
2466dc4eb53aSLinus Lüssing 	return true;
2467dc4eb53aSLinus Lüssing }
2468dc4eb53aSLinus Lüssing #endif
2469dc4eb53aSLinus Lüssing 
2470dc4eb53aSLinus Lüssing static bool br_multicast_select_querier(struct net_bridge *br,
24712cd41431SLinus Lüssing 					struct net_bridge_port *port,
2472dc4eb53aSLinus Lüssing 					struct br_ip *saddr)
2473dc4eb53aSLinus Lüssing {
2474dc4eb53aSLinus Lüssing 	switch (saddr->proto) {
2475dc4eb53aSLinus Lüssing 	case htons(ETH_P_IP):
2476deb96566SNikolay Aleksandrov 		return br_ip4_multicast_select_querier(br, port, saddr->src.ip4);
2477dc4eb53aSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
2478dc4eb53aSLinus Lüssing 	case htons(ETH_P_IPV6):
2479deb96566SNikolay Aleksandrov 		return br_ip6_multicast_select_querier(br, port, &saddr->src.ip6);
2480dc4eb53aSLinus Lüssing #endif
2481dc4eb53aSLinus Lüssing 	}
2482dc4eb53aSLinus Lüssing 
2483dc4eb53aSLinus Lüssing 	return false;
2484dc4eb53aSLinus Lüssing }
2485dc4eb53aSLinus Lüssing 
2486cc0fdd80SLinus Lüssing static void
248790010b36SLinus Lüssing br_multicast_update_query_timer(struct net_bridge *br,
248890010b36SLinus Lüssing 				struct bridge_mcast_other_query *query,
2489b00589afSLinus Lüssing 				unsigned long max_delay)
2490b00589afSLinus Lüssing {
249190010b36SLinus Lüssing 	if (!timer_pending(&query->timer))
249290010b36SLinus Lüssing 		query->delay_time = jiffies + max_delay;
2493b00589afSLinus Lüssing 
249490010b36SLinus Lüssing 	mod_timer(&query->timer, jiffies + br->multicast_querier_interval);
2495b00589afSLinus Lüssing }
2496b00589afSLinus Lüssing 
24976d549648SNogah Frankel static void br_port_mc_router_state_change(struct net_bridge_port *p,
24986d549648SNogah Frankel 					   bool is_mc_router)
24996d549648SNogah Frankel {
25006d549648SNogah Frankel 	struct switchdev_attr attr = {
25016d549648SNogah Frankel 		.orig_dev = p->dev,
25026d549648SNogah Frankel 		.id = SWITCHDEV_ATTR_ID_PORT_MROUTER,
25036d549648SNogah Frankel 		.flags = SWITCHDEV_F_DEFER,
25046d549648SNogah Frankel 		.u.mrouter = is_mc_router,
25056d549648SNogah Frankel 	};
25066d549648SNogah Frankel 
25076d549648SNogah Frankel 	switchdev_port_attr_set(p->dev, &attr);
25086d549648SNogah Frankel }
25096d549648SNogah Frankel 
25107e80c124Sstephen hemminger /*
25117c77602fSCong Wang  * Add port to router_list
25127e80c124Sstephen hemminger  *  list is maintained ordered by pointer value
25137e80c124Sstephen hemminger  *  and locked by br->multicast_lock and RCU
25147e80c124Sstephen hemminger  */
25150909e117SHerbert Xu static void br_multicast_add_router(struct net_bridge *br,
25160909e117SHerbert Xu 				    struct net_bridge_port *port)
25170909e117SHerbert Xu {
2518dcdca2c4Sstephen hemminger 	struct net_bridge_port *p;
2519b67bfe0dSSasha Levin 	struct hlist_node *slot = NULL;
25200909e117SHerbert Xu 
25211a040eacSNikolay Aleksandrov 	if (!hlist_unhashed(&port->rlist))
25221a040eacSNikolay Aleksandrov 		return;
25231a040eacSNikolay Aleksandrov 
2524b67bfe0dSSasha Levin 	hlist_for_each_entry(p, &br->router_list, rlist) {
25257e80c124Sstephen hemminger 		if ((unsigned long) port >= (unsigned long) p)
25267e80c124Sstephen hemminger 			break;
2527b67bfe0dSSasha Levin 		slot = &p->rlist;
2528dcdca2c4Sstephen hemminger 	}
25290909e117SHerbert Xu 
25307e80c124Sstephen hemminger 	if (slot)
25311d023284SKen Helias 		hlist_add_behind_rcu(&port->rlist, slot);
2532dcdca2c4Sstephen hemminger 	else
2533dcdca2c4Sstephen hemminger 		hlist_add_head_rcu(&port->rlist, &br->router_list);
2534949f1e39SSatish Ashok 	br_rtr_notify(br->dev, port, RTM_NEWMDB);
25356d549648SNogah Frankel 	br_port_mc_router_state_change(port, true);
25360909e117SHerbert Xu }
25370909e117SHerbert Xu 
2538eb1d1641SHerbert Xu static void br_multicast_mark_router(struct net_bridge *br,
2539eb1d1641SHerbert Xu 				     struct net_bridge_port *port)
2540eb1d1641SHerbert Xu {
2541eb1d1641SHerbert Xu 	unsigned long now = jiffies;
2542eb1d1641SHerbert Xu 
2543eb1d1641SHerbert Xu 	if (!port) {
254477041420SYotam Gigi 		if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) {
254577041420SYotam Gigi 			if (!timer_pending(&br->multicast_router_timer))
254677041420SYotam Gigi 				br_mc_router_state_change(br, true);
2547eb1d1641SHerbert Xu 			mod_timer(&br->multicast_router_timer,
2548eb1d1641SHerbert Xu 				  now + br->multicast_querier_interval);
254977041420SYotam Gigi 		}
2550eb1d1641SHerbert Xu 		return;
2551eb1d1641SHerbert Xu 	}
2552eb1d1641SHerbert Xu 
2553a55d8246SNikolay Aleksandrov 	if (port->multicast_router == MDB_RTR_TYPE_DISABLED ||
2554a55d8246SNikolay Aleksandrov 	    port->multicast_router == MDB_RTR_TYPE_PERM)
2555eb1d1641SHerbert Xu 		return;
2556eb1d1641SHerbert Xu 
25570909e117SHerbert Xu 	br_multicast_add_router(br, port);
2558eb1d1641SHerbert Xu 
2559eb1d1641SHerbert Xu 	mod_timer(&port->multicast_router_timer,
2560eb1d1641SHerbert Xu 		  now + br->multicast_querier_interval);
2561eb1d1641SHerbert Xu }
2562eb1d1641SHerbert Xu 
2563eb1d1641SHerbert Xu static void br_multicast_query_received(struct net_bridge *br,
2564eb1d1641SHerbert Xu 					struct net_bridge_port *port,
256590010b36SLinus Lüssing 					struct bridge_mcast_other_query *query,
2566dc4eb53aSLinus Lüssing 					struct br_ip *saddr,
2567b00589afSLinus Lüssing 					unsigned long max_delay)
2568eb1d1641SHerbert Xu {
25692cd41431SLinus Lüssing 	if (!br_multicast_select_querier(br, port, saddr))
2570eb1d1641SHerbert Xu 		return;
2571eb1d1641SHerbert Xu 
2572dc4eb53aSLinus Lüssing 	br_multicast_update_query_timer(br, query, max_delay);
2573eb1d1641SHerbert Xu 	br_multicast_mark_router(br, port);
2574eb1d1641SHerbert Xu }
2575eb1d1641SHerbert Xu 
25769c2e955cSzhong jiang static void br_ip4_multicast_query(struct net_bridge *br,
2577eb1d1641SHerbert Xu 				   struct net_bridge_port *port,
257806499098SVlad Yasevich 				   struct sk_buff *skb,
257906499098SVlad Yasevich 				   u16 vid)
2580eb1d1641SHerbert Xu {
2581ba5ea614SLinus Lüssing 	unsigned int transport_len = ip_transport_len(skb);
2582b71d1d42SEric Dumazet 	const struct iphdr *iph = ip_hdr(skb);
2583eb1d1641SHerbert Xu 	struct igmphdr *ih = igmp_hdr(skb);
2584eb1d1641SHerbert Xu 	struct net_bridge_mdb_entry *mp;
2585eb1d1641SHerbert Xu 	struct igmpv3_query *ih3;
2586eb1d1641SHerbert Xu 	struct net_bridge_port_group *p;
2587e8051688SEric Dumazet 	struct net_bridge_port_group __rcu **pp;
2588dc4eb53aSLinus Lüssing 	struct br_ip saddr;
2589eb1d1641SHerbert Xu 	unsigned long max_delay;
2590eb1d1641SHerbert Xu 	unsigned long now = jiffies;
2591eb1d1641SHerbert Xu 	__be32 group;
2592eb1d1641SHerbert Xu 
2593eb1d1641SHerbert Xu 	spin_lock(&br->multicast_lock);
2594eb1d1641SHerbert Xu 	if (!netif_running(br->dev) ||
2595eb1d1641SHerbert Xu 	    (port && port->state == BR_STATE_DISABLED))
2596eb1d1641SHerbert Xu 		goto out;
2597eb1d1641SHerbert Xu 
2598eb1d1641SHerbert Xu 	group = ih->group;
2599eb1d1641SHerbert Xu 
2600ba5ea614SLinus Lüssing 	if (transport_len == sizeof(*ih)) {
2601eb1d1641SHerbert Xu 		max_delay = ih->code * (HZ / IGMP_TIMER_SCALE);
2602eb1d1641SHerbert Xu 
2603eb1d1641SHerbert Xu 		if (!max_delay) {
2604eb1d1641SHerbert Xu 			max_delay = 10 * HZ;
2605eb1d1641SHerbert Xu 			group = 0;
2606eb1d1641SHerbert Xu 		}
2607ba5ea614SLinus Lüssing 	} else if (transport_len >= sizeof(*ih3)) {
2608eb1d1641SHerbert Xu 		ih3 = igmpv3_query_hdr(skb);
260923550b83SNikolay Aleksandrov 		if (ih3->nsrcs ||
261023550b83SNikolay Aleksandrov 		    (br->multicast_igmp_version == 3 && group && ih3->suppress))
2611bec68ff1SYOSHIFUJI Hideaki 			goto out;
2612eb1d1641SHerbert Xu 
26130ba8c9ecSYOSHIFUJI Hideaki / 吉藤英明 		max_delay = ih3->code ?
26140ba8c9ecSYOSHIFUJI Hideaki / 吉藤英明 			    IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
26159afd85c9SLinus Lüssing 	} else {
26169ed973ccSLinus Lüssing 		goto out;
26179ed973ccSLinus Lüssing 	}
26189ed973ccSLinus Lüssing 
2619dc4eb53aSLinus Lüssing 	if (!group) {
2620dc4eb53aSLinus Lüssing 		saddr.proto = htons(ETH_P_IP);
2621deb96566SNikolay Aleksandrov 		saddr.src.ip4 = iph->saddr;
2622b00589afSLinus Lüssing 
2623dc4eb53aSLinus Lüssing 		br_multicast_query_received(br, port, &br->ip4_other_query,
2624dc4eb53aSLinus Lüssing 					    &saddr, max_delay);
2625eb1d1641SHerbert Xu 		goto out;
2626dc4eb53aSLinus Lüssing 	}
2627eb1d1641SHerbert Xu 
262819e3a9c9SNikolay Aleksandrov 	mp = br_mdb_ip4_get(br, group, vid);
2629eb1d1641SHerbert Xu 	if (!mp)
2630eb1d1641SHerbert Xu 		goto out;
2631eb1d1641SHerbert Xu 
2632eb1d1641SHerbert Xu 	max_delay *= br->multicast_last_member_count;
2633eb1d1641SHerbert Xu 
2634ff0fd34eSAndrew Lunn 	if (mp->host_joined &&
2635eb1d1641SHerbert Xu 	    (timer_pending(&mp->timer) ?
2636eb1d1641SHerbert Xu 	     time_after(mp->timer.expires, now + max_delay) :
2637eb1d1641SHerbert Xu 	     try_to_del_timer_sync(&mp->timer) >= 0))
2638eb1d1641SHerbert Xu 		mod_timer(&mp->timer, now + max_delay);
2639eb1d1641SHerbert Xu 
2640e8051688SEric Dumazet 	for (pp = &mp->ports;
2641e8051688SEric Dumazet 	     (p = mlock_dereference(*pp, br)) != NULL;
2642e8051688SEric Dumazet 	     pp = &p->next) {
2643eb1d1641SHerbert Xu 		if (timer_pending(&p->timer) ?
2644eb1d1641SHerbert Xu 		    time_after(p->timer.expires, now + max_delay) :
264523550b83SNikolay Aleksandrov 		    try_to_del_timer_sync(&p->timer) >= 0 &&
264623550b83SNikolay Aleksandrov 		    (br->multicast_igmp_version == 2 ||
264723550b83SNikolay Aleksandrov 		     p->filter_mode == MCAST_EXCLUDE))
264824f9cdcbSHerbert Xu 			mod_timer(&p->timer, now + max_delay);
2649eb1d1641SHerbert Xu 	}
2650eb1d1641SHerbert Xu 
2651eb1d1641SHerbert Xu out:
2652eb1d1641SHerbert Xu 	spin_unlock(&br->multicast_lock);
2653eb1d1641SHerbert Xu }
2654eb1d1641SHerbert Xu 
2655dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
265608b202b6SYOSHIFUJI Hideaki static int br_ip6_multicast_query(struct net_bridge *br,
265708b202b6SYOSHIFUJI Hideaki 				  struct net_bridge_port *port,
265806499098SVlad Yasevich 				  struct sk_buff *skb,
265906499098SVlad Yasevich 				  u16 vid)
266008b202b6SYOSHIFUJI Hideaki {
2661ba5ea614SLinus Lüssing 	unsigned int transport_len = ipv6_transport_len(skb);
2662eca2a43bSstephen hemminger 	struct mld_msg *mld;
266308b202b6SYOSHIFUJI Hideaki 	struct net_bridge_mdb_entry *mp;
266408b202b6SYOSHIFUJI Hideaki 	struct mld2_query *mld2q;
2665e8051688SEric Dumazet 	struct net_bridge_port_group *p;
2666e8051688SEric Dumazet 	struct net_bridge_port_group __rcu **pp;
2667dc4eb53aSLinus Lüssing 	struct br_ip saddr;
266808b202b6SYOSHIFUJI Hideaki 	unsigned long max_delay;
266908b202b6SYOSHIFUJI Hideaki 	unsigned long now = jiffies;
2670856ce5d0SLinus Lüssing 	unsigned int offset = skb_transport_offset(skb);
2671b71d1d42SEric Dumazet 	const struct in6_addr *group = NULL;
26729ed973ccSLinus Lüssing 	bool is_general_query;
267308b202b6SYOSHIFUJI Hideaki 	int err = 0;
267408b202b6SYOSHIFUJI Hideaki 
267508b202b6SYOSHIFUJI Hideaki 	spin_lock(&br->multicast_lock);
267608b202b6SYOSHIFUJI Hideaki 	if (!netif_running(br->dev) ||
267708b202b6SYOSHIFUJI Hideaki 	    (port && port->state == BR_STATE_DISABLED))
267808b202b6SYOSHIFUJI Hideaki 		goto out;
267908b202b6SYOSHIFUJI Hideaki 
2680ba5ea614SLinus Lüssing 	if (transport_len == sizeof(*mld)) {
2681856ce5d0SLinus Lüssing 		if (!pskb_may_pull(skb, offset + sizeof(*mld))) {
268208b202b6SYOSHIFUJI Hideaki 			err = -EINVAL;
268308b202b6SYOSHIFUJI Hideaki 			goto out;
268408b202b6SYOSHIFUJI Hideaki 		}
268508b202b6SYOSHIFUJI Hideaki 		mld = (struct mld_msg *) icmp6_hdr(skb);
26864715213dSLi RongQing 		max_delay = msecs_to_jiffies(ntohs(mld->mld_maxdelay));
268708b202b6SYOSHIFUJI Hideaki 		if (max_delay)
268808b202b6SYOSHIFUJI Hideaki 			group = &mld->mld_mca;
2689248ba8ecSLinus Lüssing 	} else {
2690856ce5d0SLinus Lüssing 		if (!pskb_may_pull(skb, offset + sizeof(*mld2q))) {
269108b202b6SYOSHIFUJI Hideaki 			err = -EINVAL;
269208b202b6SYOSHIFUJI Hideaki 			goto out;
269308b202b6SYOSHIFUJI Hideaki 		}
269408b202b6SYOSHIFUJI Hideaki 		mld2q = (struct mld2_query *)icmp6_hdr(skb);
269508b202b6SYOSHIFUJI Hideaki 		if (!mld2q->mld2q_nsrcs)
269608b202b6SYOSHIFUJI Hideaki 			group = &mld2q->mld2q_mca;
269723550b83SNikolay Aleksandrov 		if (br->multicast_mld_version == 2 &&
269823550b83SNikolay Aleksandrov 		    !ipv6_addr_any(&mld2q->mld2q_mca) &&
269923550b83SNikolay Aleksandrov 		    mld2q->mld2q_suppress)
270023550b83SNikolay Aleksandrov 			goto out;
2701e3f5b170SDaniel Borkmann 
2702e3f5b170SDaniel Borkmann 		max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL);
270308b202b6SYOSHIFUJI Hideaki 	}
270408b202b6SYOSHIFUJI Hideaki 
27059ed973ccSLinus Lüssing 	is_general_query = group && ipv6_addr_any(group);
27069ed973ccSLinus Lüssing 
2707dc4eb53aSLinus Lüssing 	if (is_general_query) {
2708dc4eb53aSLinus Lüssing 		saddr.proto = htons(ETH_P_IPV6);
2709deb96566SNikolay Aleksandrov 		saddr.src.ip6 = ipv6_hdr(skb)->saddr;
2710b00589afSLinus Lüssing 
2711dc4eb53aSLinus Lüssing 		br_multicast_query_received(br, port, &br->ip6_other_query,
2712dc4eb53aSLinus Lüssing 					    &saddr, max_delay);
271308b202b6SYOSHIFUJI Hideaki 		goto out;
27146c03ee8bSLinus Lüssing 	} else if (!group) {
27156c03ee8bSLinus Lüssing 		goto out;
2716dc4eb53aSLinus Lüssing 	}
271708b202b6SYOSHIFUJI Hideaki 
271819e3a9c9SNikolay Aleksandrov 	mp = br_mdb_ip6_get(br, group, vid);
271908b202b6SYOSHIFUJI Hideaki 	if (!mp)
272008b202b6SYOSHIFUJI Hideaki 		goto out;
272108b202b6SYOSHIFUJI Hideaki 
272208b202b6SYOSHIFUJI Hideaki 	max_delay *= br->multicast_last_member_count;
2723ff0fd34eSAndrew Lunn 	if (mp->host_joined &&
272408b202b6SYOSHIFUJI Hideaki 	    (timer_pending(&mp->timer) ?
272508b202b6SYOSHIFUJI Hideaki 	     time_after(mp->timer.expires, now + max_delay) :
272608b202b6SYOSHIFUJI Hideaki 	     try_to_del_timer_sync(&mp->timer) >= 0))
272708b202b6SYOSHIFUJI Hideaki 		mod_timer(&mp->timer, now + max_delay);
272808b202b6SYOSHIFUJI Hideaki 
2729e8051688SEric Dumazet 	for (pp = &mp->ports;
2730e8051688SEric Dumazet 	     (p = mlock_dereference(*pp, br)) != NULL;
2731e8051688SEric Dumazet 	     pp = &p->next) {
273208b202b6SYOSHIFUJI Hideaki 		if (timer_pending(&p->timer) ?
273308b202b6SYOSHIFUJI Hideaki 		    time_after(p->timer.expires, now + max_delay) :
273423550b83SNikolay Aleksandrov 		    try_to_del_timer_sync(&p->timer) >= 0 &&
273523550b83SNikolay Aleksandrov 		    (br->multicast_mld_version == 1 ||
273623550b83SNikolay Aleksandrov 		     p->filter_mode == MCAST_EXCLUDE))
273724f9cdcbSHerbert Xu 			mod_timer(&p->timer, now + max_delay);
273808b202b6SYOSHIFUJI Hideaki 	}
273908b202b6SYOSHIFUJI Hideaki 
274008b202b6SYOSHIFUJI Hideaki out:
274108b202b6SYOSHIFUJI Hideaki 	spin_unlock(&br->multicast_lock);
274208b202b6SYOSHIFUJI Hideaki 	return err;
274308b202b6SYOSHIFUJI Hideaki }
274408b202b6SYOSHIFUJI Hideaki #endif
274508b202b6SYOSHIFUJI Hideaki 
274690010b36SLinus Lüssing static void
274790010b36SLinus Lüssing br_multicast_leave_group(struct net_bridge *br,
2748eb1d1641SHerbert Xu 			 struct net_bridge_port *port,
2749cc0fdd80SLinus Lüssing 			 struct br_ip *group,
275090010b36SLinus Lüssing 			 struct bridge_mcast_other_query *other_query,
27516db6f0eaSFelix Fietkau 			 struct bridge_mcast_own_query *own_query,
27526db6f0eaSFelix Fietkau 			 const unsigned char *src)
2753eb1d1641SHerbert Xu {
2754eb1d1641SHerbert Xu 	struct net_bridge_mdb_entry *mp;
2755eb1d1641SHerbert Xu 	struct net_bridge_port_group *p;
2756eb1d1641SHerbert Xu 	unsigned long now;
2757eb1d1641SHerbert Xu 	unsigned long time;
2758eb1d1641SHerbert Xu 
2759eb1d1641SHerbert Xu 	spin_lock(&br->multicast_lock);
2760eb1d1641SHerbert Xu 	if (!netif_running(br->dev) ||
2761544586f7SSatish Ashok 	    (port && port->state == BR_STATE_DISABLED))
2762eb1d1641SHerbert Xu 		goto out;
2763eb1d1641SHerbert Xu 
276419e3a9c9SNikolay Aleksandrov 	mp = br_mdb_ip_get(br, group);
2765eb1d1641SHerbert Xu 	if (!mp)
2766eb1d1641SHerbert Xu 		goto out;
2767eb1d1641SHerbert Xu 
2768544586f7SSatish Ashok 	if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) {
2769544586f7SSatish Ashok 		struct net_bridge_port_group __rcu **pp;
2770544586f7SSatish Ashok 
2771544586f7SSatish Ashok 		for (pp = &mp->ports;
2772544586f7SSatish Ashok 		     (p = mlock_dereference(*pp, br)) != NULL;
2773544586f7SSatish Ashok 		     pp = &p->next) {
27746db6f0eaSFelix Fietkau 			if (!br_port_group_equal(p, port, src))
2775544586f7SSatish Ashok 				continue;
2776544586f7SSatish Ashok 
27775c725b6bSNikolay Aleksandrov 			if (p->flags & MDB_PG_FLAGS_PERMANENT)
27785c725b6bSNikolay Aleksandrov 				break;
27795c725b6bSNikolay Aleksandrov 
2780681590bdSNikolay Aleksandrov 			p->flags |= MDB_PG_FLAGS_FAST_LEAVE;
2781681590bdSNikolay Aleksandrov 			br_multicast_del_pg(mp, p, pp);
2782544586f7SSatish Ashok 		}
2783544586f7SSatish Ashok 		goto out;
2784544586f7SSatish Ashok 	}
2785544586f7SSatish Ashok 
2786544586f7SSatish Ashok 	if (timer_pending(&other_query->timer))
2787544586f7SSatish Ashok 		goto out;
2788544586f7SSatish Ashok 
2789675779adSNikolay Aleksandrov 	if (br_opt_get(br, BROPT_MULTICAST_QUERIER)) {
2790438ef2d0SNikolay Aleksandrov 		__br_multicast_send_query(br, port, NULL, NULL, &mp->addr,
279142c11ccfSNikolay Aleksandrov 					  false, 0, NULL);
27926b7df111SCong Wang 
27936b7df111SCong Wang 		time = jiffies + br->multicast_last_member_count *
27946b7df111SCong Wang 				 br->multicast_last_member_interval;
2795cc0fdd80SLinus Lüssing 
279690010b36SLinus Lüssing 		mod_timer(&own_query->timer, time);
27976b7df111SCong Wang 
27986b7df111SCong Wang 		for (p = mlock_dereference(mp->ports, br);
27996b7df111SCong Wang 		     p != NULL;
28006b7df111SCong Wang 		     p = mlock_dereference(p->next, br)) {
28016db6f0eaSFelix Fietkau 			if (!br_port_group_equal(p, port, src))
28026b7df111SCong Wang 				continue;
28036b7df111SCong Wang 
28046b7df111SCong Wang 			if (!hlist_unhashed(&p->mglist) &&
28056b7df111SCong Wang 			    (timer_pending(&p->timer) ?
28066b7df111SCong Wang 			     time_after(p->timer.expires, time) :
28076b7df111SCong Wang 			     try_to_del_timer_sync(&p->timer) >= 0)) {
28086b7df111SCong Wang 				mod_timer(&p->timer, time);
28096b7df111SCong Wang 			}
28106b7df111SCong Wang 
28116b7df111SCong Wang 			break;
28126b7df111SCong Wang 		}
28136b7df111SCong Wang 	}
28146b7df111SCong Wang 
2815eb1d1641SHerbert Xu 	now = jiffies;
2816eb1d1641SHerbert Xu 	time = now + br->multicast_last_member_count *
2817eb1d1641SHerbert Xu 		     br->multicast_last_member_interval;
2818eb1d1641SHerbert Xu 
2819eb1d1641SHerbert Xu 	if (!port) {
2820ff0fd34eSAndrew Lunn 		if (mp->host_joined &&
2821eb1d1641SHerbert Xu 		    (timer_pending(&mp->timer) ?
2822eb1d1641SHerbert Xu 		     time_after(mp->timer.expires, time) :
2823eb1d1641SHerbert Xu 		     try_to_del_timer_sync(&mp->timer) >= 0)) {
2824eb1d1641SHerbert Xu 			mod_timer(&mp->timer, time);
2825eb1d1641SHerbert Xu 		}
2826454594f3SLinus Lüssing 
2827454594f3SLinus Lüssing 		goto out;
2828454594f3SLinus Lüssing 	}
2829454594f3SLinus Lüssing 
2830454594f3SLinus Lüssing 	for (p = mlock_dereference(mp->ports, br);
2831454594f3SLinus Lüssing 	     p != NULL;
2832454594f3SLinus Lüssing 	     p = mlock_dereference(p->next, br)) {
2833085b53c8SNikolay Aleksandrov 		if (p->key.port != port)
2834454594f3SLinus Lüssing 			continue;
2835454594f3SLinus Lüssing 
2836454594f3SLinus Lüssing 		if (!hlist_unhashed(&p->mglist) &&
2837454594f3SLinus Lüssing 		    (timer_pending(&p->timer) ?
2838454594f3SLinus Lüssing 		     time_after(p->timer.expires, time) :
2839454594f3SLinus Lüssing 		     try_to_del_timer_sync(&p->timer) >= 0)) {
2840454594f3SLinus Lüssing 			mod_timer(&p->timer, time);
2841454594f3SLinus Lüssing 		}
2842454594f3SLinus Lüssing 
2843454594f3SLinus Lüssing 		break;
2844eb1d1641SHerbert Xu 	}
2845eb1d1641SHerbert Xu out:
2846eb1d1641SHerbert Xu 	spin_unlock(&br->multicast_lock);
2847eb1d1641SHerbert Xu }
2848eb1d1641SHerbert Xu 
28498ef2a9a5SYOSHIFUJI Hideaki static void br_ip4_multicast_leave_group(struct net_bridge *br,
28508ef2a9a5SYOSHIFUJI Hideaki 					 struct net_bridge_port *port,
2851b0e9a30dSVlad Yasevich 					 __be32 group,
28526db6f0eaSFelix Fietkau 					 __u16 vid,
28536db6f0eaSFelix Fietkau 					 const unsigned char *src)
28548ef2a9a5SYOSHIFUJI Hideaki {
28558ef2a9a5SYOSHIFUJI Hideaki 	struct br_ip br_group;
285690010b36SLinus Lüssing 	struct bridge_mcast_own_query *own_query;
28578ef2a9a5SYOSHIFUJI Hideaki 
28588ef2a9a5SYOSHIFUJI Hideaki 	if (ipv4_is_local_multicast(group))
28598ef2a9a5SYOSHIFUJI Hideaki 		return;
28608ef2a9a5SYOSHIFUJI Hideaki 
286190010b36SLinus Lüssing 	own_query = port ? &port->ip4_own_query : &br->ip4_own_query;
286290010b36SLinus Lüssing 
28631515a63fSNikolay Aleksandrov 	memset(&br_group, 0, sizeof(br_group));
2864eab3227bSNikolay Aleksandrov 	br_group.dst.ip4 = group;
28658ef2a9a5SYOSHIFUJI Hideaki 	br_group.proto = htons(ETH_P_IP);
2866b0e9a30dSVlad Yasevich 	br_group.vid = vid;
28678ef2a9a5SYOSHIFUJI Hideaki 
286890010b36SLinus Lüssing 	br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query,
28696db6f0eaSFelix Fietkau 				 own_query, src);
28708ef2a9a5SYOSHIFUJI Hideaki }
28718ef2a9a5SYOSHIFUJI Hideaki 
2872dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
287308b202b6SYOSHIFUJI Hideaki static void br_ip6_multicast_leave_group(struct net_bridge *br,
287408b202b6SYOSHIFUJI Hideaki 					 struct net_bridge_port *port,
2875b0e9a30dSVlad Yasevich 					 const struct in6_addr *group,
28766db6f0eaSFelix Fietkau 					 __u16 vid,
28776db6f0eaSFelix Fietkau 					 const unsigned char *src)
287808b202b6SYOSHIFUJI Hideaki {
287908b202b6SYOSHIFUJI Hideaki 	struct br_ip br_group;
288090010b36SLinus Lüssing 	struct bridge_mcast_own_query *own_query;
288108b202b6SYOSHIFUJI Hideaki 
28823c3769e6SLinus Lüssing 	if (ipv6_addr_is_ll_all_nodes(group))
288308b202b6SYOSHIFUJI Hideaki 		return;
288408b202b6SYOSHIFUJI Hideaki 
288590010b36SLinus Lüssing 	own_query = port ? &port->ip6_own_query : &br->ip6_own_query;
288690010b36SLinus Lüssing 
28871515a63fSNikolay Aleksandrov 	memset(&br_group, 0, sizeof(br_group));
2888eab3227bSNikolay Aleksandrov 	br_group.dst.ip6 = *group;
288908b202b6SYOSHIFUJI Hideaki 	br_group.proto = htons(ETH_P_IPV6);
2890b0e9a30dSVlad Yasevich 	br_group.vid = vid;
289108b202b6SYOSHIFUJI Hideaki 
289290010b36SLinus Lüssing 	br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query,
28936db6f0eaSFelix Fietkau 				 own_query, src);
289408b202b6SYOSHIFUJI Hideaki }
289508b202b6SYOSHIFUJI Hideaki #endif
28968ef2a9a5SYOSHIFUJI Hideaki 
28971080ab95SNikolay Aleksandrov static void br_multicast_err_count(const struct net_bridge *br,
28981080ab95SNikolay Aleksandrov 				   const struct net_bridge_port *p,
28991080ab95SNikolay Aleksandrov 				   __be16 proto)
29001080ab95SNikolay Aleksandrov {
29011080ab95SNikolay Aleksandrov 	struct bridge_mcast_stats __percpu *stats;
29021080ab95SNikolay Aleksandrov 	struct bridge_mcast_stats *pstats;
29031080ab95SNikolay Aleksandrov 
2904675779adSNikolay Aleksandrov 	if (!br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED))
29051080ab95SNikolay Aleksandrov 		return;
29061080ab95SNikolay Aleksandrov 
29071080ab95SNikolay Aleksandrov 	if (p)
29081080ab95SNikolay Aleksandrov 		stats = p->mcast_stats;
29091080ab95SNikolay Aleksandrov 	else
29101080ab95SNikolay Aleksandrov 		stats = br->mcast_stats;
29111080ab95SNikolay Aleksandrov 	if (WARN_ON(!stats))
29121080ab95SNikolay Aleksandrov 		return;
29131080ab95SNikolay Aleksandrov 
29141080ab95SNikolay Aleksandrov 	pstats = this_cpu_ptr(stats);
29151080ab95SNikolay Aleksandrov 
29161080ab95SNikolay Aleksandrov 	u64_stats_update_begin(&pstats->syncp);
29171080ab95SNikolay Aleksandrov 	switch (proto) {
29181080ab95SNikolay Aleksandrov 	case htons(ETH_P_IP):
29191080ab95SNikolay Aleksandrov 		pstats->mstats.igmp_parse_errors++;
29201080ab95SNikolay Aleksandrov 		break;
29211080ab95SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
29221080ab95SNikolay Aleksandrov 	case htons(ETH_P_IPV6):
29231080ab95SNikolay Aleksandrov 		pstats->mstats.mld_parse_errors++;
29241080ab95SNikolay Aleksandrov 		break;
29251080ab95SNikolay Aleksandrov #endif
29261080ab95SNikolay Aleksandrov 	}
29271080ab95SNikolay Aleksandrov 	u64_stats_update_end(&pstats->syncp);
29281080ab95SNikolay Aleksandrov }
29291080ab95SNikolay Aleksandrov 
293091b02d3dSNikolay Aleksandrov static void br_multicast_pim(struct net_bridge *br,
293191b02d3dSNikolay Aleksandrov 			     struct net_bridge_port *port,
293291b02d3dSNikolay Aleksandrov 			     const struct sk_buff *skb)
293391b02d3dSNikolay Aleksandrov {
293491b02d3dSNikolay Aleksandrov 	unsigned int offset = skb_transport_offset(skb);
293591b02d3dSNikolay Aleksandrov 	struct pimhdr *pimhdr, _pimhdr;
293691b02d3dSNikolay Aleksandrov 
293791b02d3dSNikolay Aleksandrov 	pimhdr = skb_header_pointer(skb, offset, sizeof(_pimhdr), &_pimhdr);
293891b02d3dSNikolay Aleksandrov 	if (!pimhdr || pim_hdr_version(pimhdr) != PIM_VERSION ||
293991b02d3dSNikolay Aleksandrov 	    pim_hdr_type(pimhdr) != PIM_TYPE_HELLO)
294091b02d3dSNikolay Aleksandrov 		return;
294191b02d3dSNikolay Aleksandrov 
294291b02d3dSNikolay Aleksandrov 	br_multicast_mark_router(br, port);
294391b02d3dSNikolay Aleksandrov }
294491b02d3dSNikolay Aleksandrov 
29454b3087c7SLinus Lüssing static int br_ip4_multicast_mrd_rcv(struct net_bridge *br,
29464b3087c7SLinus Lüssing 				    struct net_bridge_port *port,
29474b3087c7SLinus Lüssing 				    struct sk_buff *skb)
29484b3087c7SLinus Lüssing {
29494b3087c7SLinus Lüssing 	if (ip_hdr(skb)->protocol != IPPROTO_IGMP ||
29504b3087c7SLinus Lüssing 	    igmp_hdr(skb)->type != IGMP_MRDISC_ADV)
29514b3087c7SLinus Lüssing 		return -ENOMSG;
29524b3087c7SLinus Lüssing 
29534b3087c7SLinus Lüssing 	br_multicast_mark_router(br, port);
29544b3087c7SLinus Lüssing 
29554b3087c7SLinus Lüssing 	return 0;
29564b3087c7SLinus Lüssing }
29574b3087c7SLinus Lüssing 
2958eb1d1641SHerbert Xu static int br_multicast_ipv4_rcv(struct net_bridge *br,
2959eb1d1641SHerbert Xu 				 struct net_bridge_port *port,
296006499098SVlad Yasevich 				 struct sk_buff *skb,
296106499098SVlad Yasevich 				 u16 vid)
2962eb1d1641SHerbert Xu {
29636db6f0eaSFelix Fietkau 	const unsigned char *src;
2964eb1d1641SHerbert Xu 	struct igmphdr *ih;
2965eb1d1641SHerbert Xu 	int err;
2966eb1d1641SHerbert Xu 
2967ba5ea614SLinus Lüssing 	err = ip_mc_check_igmp(skb);
2968eb1d1641SHerbert Xu 
29699afd85c9SLinus Lüssing 	if (err == -ENOMSG) {
297091b02d3dSNikolay Aleksandrov 		if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) {
2971bd4265feSHerbert Xu 			BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
297291b02d3dSNikolay Aleksandrov 		} else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) {
297391b02d3dSNikolay Aleksandrov 			if (ip_hdr(skb)->protocol == IPPROTO_PIM)
297491b02d3dSNikolay Aleksandrov 				br_multicast_pim(br, port, skb);
29754b3087c7SLinus Lüssing 		} else if (ipv4_is_all_snoopers(ip_hdr(skb)->daddr)) {
297608e71623SLi RongQing 			br_ip4_multicast_mrd_rcv(br, port, skb);
29774b3087c7SLinus Lüssing 		}
29784b3087c7SLinus Lüssing 
2979eb1d1641SHerbert Xu 		return 0;
29809afd85c9SLinus Lüssing 	} else if (err < 0) {
29811080ab95SNikolay Aleksandrov 		br_multicast_err_count(br, port, skb->protocol);
29829afd85c9SLinus Lüssing 		return err;
2983bd4265feSHerbert Xu 	}
2984eb1d1641SHerbert Xu 
29859afd85c9SLinus Lüssing 	ih = igmp_hdr(skb);
29866db6f0eaSFelix Fietkau 	src = eth_hdr(skb)->h_source;
29871080ab95SNikolay Aleksandrov 	BR_INPUT_SKB_CB(skb)->igmp = ih->type;
2988eb1d1641SHerbert Xu 
2989eb1d1641SHerbert Xu 	switch (ih->type) {
2990eb1d1641SHerbert Xu 	case IGMP_HOST_MEMBERSHIP_REPORT:
2991eb1d1641SHerbert Xu 	case IGMPV2_HOST_MEMBERSHIP_REPORT:
299262b2bcb4SFernando Luis Vázquez Cao 		BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
29938b671779SNikolay Aleksandrov 		err = br_ip4_multicast_add_group(br, port, ih->group, vid, src,
29948b671779SNikolay Aleksandrov 						 true);
2995eb1d1641SHerbert Xu 		break;
2996eb1d1641SHerbert Xu 	case IGMPV3_HOST_MEMBERSHIP_REPORT:
2997ba5ea614SLinus Lüssing 		err = br_ip4_multicast_igmp3_report(br, port, skb, vid);
2998eb1d1641SHerbert Xu 		break;
2999eb1d1641SHerbert Xu 	case IGMP_HOST_MEMBERSHIP_QUERY:
3000ba5ea614SLinus Lüssing 		br_ip4_multicast_query(br, port, skb, vid);
3001eb1d1641SHerbert Xu 		break;
3002eb1d1641SHerbert Xu 	case IGMP_HOST_LEAVE_MESSAGE:
30036db6f0eaSFelix Fietkau 		br_ip4_multicast_leave_group(br, port, ih->group, vid, src);
3004eb1d1641SHerbert Xu 		break;
3005eb1d1641SHerbert Xu 	}
3006eb1d1641SHerbert Xu 
3007a65056ecSNikolay Aleksandrov 	br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
30081080ab95SNikolay Aleksandrov 			   BR_MCAST_DIR_RX);
30091080ab95SNikolay Aleksandrov 
3010eb1d1641SHerbert Xu 	return err;
3011eb1d1641SHerbert Xu }
3012eb1d1641SHerbert Xu 
3013dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
30144b3087c7SLinus Lüssing static int br_ip6_multicast_mrd_rcv(struct net_bridge *br,
30154b3087c7SLinus Lüssing 				    struct net_bridge_port *port,
30164b3087c7SLinus Lüssing 				    struct sk_buff *skb)
30174b3087c7SLinus Lüssing {
30184b3087c7SLinus Lüssing 	int ret;
30194b3087c7SLinus Lüssing 
30204b3087c7SLinus Lüssing 	if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6)
30214b3087c7SLinus Lüssing 		return -ENOMSG;
30224b3087c7SLinus Lüssing 
30234b3087c7SLinus Lüssing 	ret = ipv6_mc_check_icmpv6(skb);
30244b3087c7SLinus Lüssing 	if (ret < 0)
30254b3087c7SLinus Lüssing 		return ret;
30264b3087c7SLinus Lüssing 
30274b3087c7SLinus Lüssing 	if (icmp6_hdr(skb)->icmp6_type != ICMPV6_MRDISC_ADV)
30284b3087c7SLinus Lüssing 		return -ENOMSG;
30294b3087c7SLinus Lüssing 
30304b3087c7SLinus Lüssing 	br_multicast_mark_router(br, port);
30314b3087c7SLinus Lüssing 
30324b3087c7SLinus Lüssing 	return 0;
30334b3087c7SLinus Lüssing }
30344b3087c7SLinus Lüssing 
303508b202b6SYOSHIFUJI Hideaki static int br_multicast_ipv6_rcv(struct net_bridge *br,
303608b202b6SYOSHIFUJI Hideaki 				 struct net_bridge_port *port,
303706499098SVlad Yasevich 				 struct sk_buff *skb,
303806499098SVlad Yasevich 				 u16 vid)
303908b202b6SYOSHIFUJI Hideaki {
30406db6f0eaSFelix Fietkau 	const unsigned char *src;
30419afd85c9SLinus Lüssing 	struct mld_msg *mld;
304208b202b6SYOSHIFUJI Hideaki 	int err;
304308b202b6SYOSHIFUJI Hideaki 
3044ba5ea614SLinus Lüssing 	err = ipv6_mc_check_mld(skb);
304508b202b6SYOSHIFUJI Hideaki 
30469afd85c9SLinus Lüssing 	if (err == -ENOMSG) {
30479afd85c9SLinus Lüssing 		if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
30488fad9c39SLinus Lüssing 			BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
30494b3087c7SLinus Lüssing 
30504b3087c7SLinus Lüssing 		if (ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr)) {
30514b3087c7SLinus Lüssing 			err = br_ip6_multicast_mrd_rcv(br, port, skb);
30524b3087c7SLinus Lüssing 
30534b3087c7SLinus Lüssing 			if (err < 0 && err != -ENOMSG) {
30544b3087c7SLinus Lüssing 				br_multicast_err_count(br, port, skb->protocol);
30554b3087c7SLinus Lüssing 				return err;
30564b3087c7SLinus Lüssing 			}
30574b3087c7SLinus Lüssing 		}
30584b3087c7SLinus Lüssing 
305908b202b6SYOSHIFUJI Hideaki 		return 0;
30609afd85c9SLinus Lüssing 	} else if (err < 0) {
30611080ab95SNikolay Aleksandrov 		br_multicast_err_count(br, port, skb->protocol);
30629afd85c9SLinus Lüssing 		return err;
306308b202b6SYOSHIFUJI Hideaki 	}
306408b202b6SYOSHIFUJI Hideaki 
30659afd85c9SLinus Lüssing 	mld = (struct mld_msg *)skb_transport_header(skb);
30661080ab95SNikolay Aleksandrov 	BR_INPUT_SKB_CB(skb)->igmp = mld->mld_type;
306708b202b6SYOSHIFUJI Hideaki 
30689afd85c9SLinus Lüssing 	switch (mld->mld_type) {
306908b202b6SYOSHIFUJI Hideaki 	case ICMPV6_MGM_REPORT:
30706db6f0eaSFelix Fietkau 		src = eth_hdr(skb)->h_source;
3071fc2af6c7SFernando Luis Vázquez Cao 		BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
30726db6f0eaSFelix Fietkau 		err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid,
30738b671779SNikolay Aleksandrov 						 src, true);
307408b202b6SYOSHIFUJI Hideaki 		break;
307508b202b6SYOSHIFUJI Hideaki 	case ICMPV6_MLD2_REPORT:
3076ba5ea614SLinus Lüssing 		err = br_ip6_multicast_mld2_report(br, port, skb, vid);
307708b202b6SYOSHIFUJI Hideaki 		break;
307808b202b6SYOSHIFUJI Hideaki 	case ICMPV6_MGM_QUERY:
3079ba5ea614SLinus Lüssing 		err = br_ip6_multicast_query(br, port, skb, vid);
308008b202b6SYOSHIFUJI Hideaki 		break;
308108b202b6SYOSHIFUJI Hideaki 	case ICMPV6_MGM_REDUCTION:
30826db6f0eaSFelix Fietkau 		src = eth_hdr(skb)->h_source;
30836db6f0eaSFelix Fietkau 		br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid, src);
30849afd85c9SLinus Lüssing 		break;
308508b202b6SYOSHIFUJI Hideaki 	}
308608b202b6SYOSHIFUJI Hideaki 
3087a65056ecSNikolay Aleksandrov 	br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
30881080ab95SNikolay Aleksandrov 			   BR_MCAST_DIR_RX);
30891080ab95SNikolay Aleksandrov 
309008b202b6SYOSHIFUJI Hideaki 	return err;
309108b202b6SYOSHIFUJI Hideaki }
309208b202b6SYOSHIFUJI Hideaki #endif
309308b202b6SYOSHIFUJI Hideaki 
3094eb1d1641SHerbert Xu int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
309506499098SVlad Yasevich 		     struct sk_buff *skb, u16 vid)
3096eb1d1641SHerbert Xu {
30971080ab95SNikolay Aleksandrov 	int ret = 0;
30981080ab95SNikolay Aleksandrov 
30991fafc7a9SYOSHIFUJI Hideaki / 吉藤英明 	BR_INPUT_SKB_CB(skb)->igmp = 0;
31001fafc7a9SYOSHIFUJI Hideaki / 吉藤英明 	BR_INPUT_SKB_CB(skb)->mrouters_only = 0;
31011fafc7a9SYOSHIFUJI Hideaki / 吉藤英明 
310213cefad2SNikolay Aleksandrov 	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
3103eb1d1641SHerbert Xu 		return 0;
3104eb1d1641SHerbert Xu 
3105eb1d1641SHerbert Xu 	switch (skb->protocol) {
3106eb1d1641SHerbert Xu 	case htons(ETH_P_IP):
31071080ab95SNikolay Aleksandrov 		ret = br_multicast_ipv4_rcv(br, port, skb, vid);
31081080ab95SNikolay Aleksandrov 		break;
3109dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6)
311008b202b6SYOSHIFUJI Hideaki 	case htons(ETH_P_IPV6):
31111080ab95SNikolay Aleksandrov 		ret = br_multicast_ipv6_rcv(br, port, skb, vid);
31121080ab95SNikolay Aleksandrov 		break;
311308b202b6SYOSHIFUJI Hideaki #endif
3114eb1d1641SHerbert Xu 	}
3115eb1d1641SHerbert Xu 
31161080ab95SNikolay Aleksandrov 	return ret;
3117eb1d1641SHerbert Xu }
3118eb1d1641SHerbert Xu 
3119cc0fdd80SLinus Lüssing static void br_multicast_query_expired(struct net_bridge *br,
31202cd41431SLinus Lüssing 				       struct bridge_mcast_own_query *query,
31212cd41431SLinus Lüssing 				       struct bridge_mcast_querier *querier)
3122cc0fdd80SLinus Lüssing {
3123cc0fdd80SLinus Lüssing 	spin_lock(&br->multicast_lock);
3124cc0fdd80SLinus Lüssing 	if (query->startup_sent < br->multicast_startup_query_count)
3125cc0fdd80SLinus Lüssing 		query->startup_sent++;
3126cc0fdd80SLinus Lüssing 
312771d9f614SEric Dumazet 	RCU_INIT_POINTER(querier->port, NULL);
3128cc0fdd80SLinus Lüssing 	br_multicast_send_query(br, NULL, query);
3129cc0fdd80SLinus Lüssing 	spin_unlock(&br->multicast_lock);
3130cc0fdd80SLinus Lüssing }
3131cc0fdd80SLinus Lüssing 
313288c1f37fSAllen Pais static void br_ip4_multicast_query_expired(struct timer_list *t)
3133eb1d1641SHerbert Xu {
313488c1f37fSAllen Pais 	struct net_bridge *br = from_timer(br, t, ip4_own_query.timer);
3135eb1d1641SHerbert Xu 
31362cd41431SLinus Lüssing 	br_multicast_query_expired(br, &br->ip4_own_query, &br->ip4_querier);
3137eb1d1641SHerbert Xu }
3138eb1d1641SHerbert Xu 
3139cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
314088c1f37fSAllen Pais static void br_ip6_multicast_query_expired(struct timer_list *t)
3141cc0fdd80SLinus Lüssing {
314288c1f37fSAllen Pais 	struct net_bridge *br = from_timer(br, t, ip6_own_query.timer);
3143cc0fdd80SLinus Lüssing 
31442cd41431SLinus Lüssing 	br_multicast_query_expired(br, &br->ip6_own_query, &br->ip6_querier);
3145cc0fdd80SLinus Lüssing }
3146cc0fdd80SLinus Lüssing #endif
3147cc0fdd80SLinus Lüssing 
3148e12cec65SNikolay Aleksandrov static void br_multicast_gc_work(struct work_struct *work)
31498b671779SNikolay Aleksandrov {
31508b671779SNikolay Aleksandrov 	struct net_bridge *br = container_of(work, struct net_bridge,
3151e12cec65SNikolay Aleksandrov 					     mcast_gc_work);
31528b671779SNikolay Aleksandrov 	HLIST_HEAD(deleted_head);
31538b671779SNikolay Aleksandrov 
31548b671779SNikolay Aleksandrov 	spin_lock_bh(&br->multicast_lock);
3155e12cec65SNikolay Aleksandrov 	hlist_move_list(&br->mcast_gc_list, &deleted_head);
31568b671779SNikolay Aleksandrov 	spin_unlock_bh(&br->multicast_lock);
31578b671779SNikolay Aleksandrov 
3158e12cec65SNikolay Aleksandrov 	br_multicast_gc(&deleted_head);
31598b671779SNikolay Aleksandrov }
31608b671779SNikolay Aleksandrov 
3161eb1d1641SHerbert Xu void br_multicast_init(struct net_bridge *br)
3162eb1d1641SHerbert Xu {
3163d08c6bc0SNikolay Aleksandrov 	br->hash_max = BR_MULTICAST_DEFAULT_HASH_MAX;
3164eb1d1641SHerbert Xu 
31657f0aec7aSNikolay Aleksandrov 	br->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
3166eb1d1641SHerbert Xu 	br->multicast_last_member_count = 2;
3167eb1d1641SHerbert Xu 	br->multicast_startup_query_count = 2;
3168eb1d1641SHerbert Xu 
3169eb1d1641SHerbert Xu 	br->multicast_last_member_interval = HZ;
3170eb1d1641SHerbert Xu 	br->multicast_query_response_interval = 10 * HZ;
3171eb1d1641SHerbert Xu 	br->multicast_startup_query_interval = 125 * HZ / 4;
3172eb1d1641SHerbert Xu 	br->multicast_query_interval = 125 * HZ;
3173eb1d1641SHerbert Xu 	br->multicast_querier_interval = 255 * HZ;
3174eb1d1641SHerbert Xu 	br->multicast_membership_interval = 260 * HZ;
3175eb1d1641SHerbert Xu 
317690010b36SLinus Lüssing 	br->ip4_other_query.delay_time = 0;
31772cd41431SLinus Lüssing 	br->ip4_querier.port = NULL;
3178aa2ae3e7SNikolay Aleksandrov 	br->multicast_igmp_version = 2;
3179cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
3180aa2ae3e7SNikolay Aleksandrov 	br->multicast_mld_version = 1;
318190010b36SLinus Lüssing 	br->ip6_other_query.delay_time = 0;
31822cd41431SLinus Lüssing 	br->ip6_querier.port = NULL;
3183cc0fdd80SLinus Lüssing #endif
31846919622aSIdo Schimmel 	br_opt_toggle(br, BROPT_MULTICAST_ENABLED, true);
3185675779adSNikolay Aleksandrov 	br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, true);
3186b00589afSLinus Lüssing 
3187eb1d1641SHerbert Xu 	spin_lock_init(&br->multicast_lock);
318888c1f37fSAllen Pais 	timer_setup(&br->multicast_router_timer,
318988c1f37fSAllen Pais 		    br_multicast_local_router_expired, 0);
319088c1f37fSAllen Pais 	timer_setup(&br->ip4_other_query.timer,
319188c1f37fSAllen Pais 		    br_ip4_multicast_querier_expired, 0);
319288c1f37fSAllen Pais 	timer_setup(&br->ip4_own_query.timer,
319388c1f37fSAllen Pais 		    br_ip4_multicast_query_expired, 0);
3194cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
319588c1f37fSAllen Pais 	timer_setup(&br->ip6_other_query.timer,
319688c1f37fSAllen Pais 		    br_ip6_multicast_querier_expired, 0);
319788c1f37fSAllen Pais 	timer_setup(&br->ip6_own_query.timer,
319888c1f37fSAllen Pais 		    br_ip6_multicast_query_expired, 0);
3199cc0fdd80SLinus Lüssing #endif
320019e3a9c9SNikolay Aleksandrov 	INIT_HLIST_HEAD(&br->mdb_list);
3201e12cec65SNikolay Aleksandrov 	INIT_HLIST_HEAD(&br->mcast_gc_list);
3202e12cec65SNikolay Aleksandrov 	INIT_WORK(&br->mcast_gc_work, br_multicast_gc_work);
3203eb1d1641SHerbert Xu }
3204eb1d1641SHerbert Xu 
32054effd28cSLinus Lüssing static void br_ip4_multicast_join_snoopers(struct net_bridge *br)
32064effd28cSLinus Lüssing {
32074effd28cSLinus Lüssing 	struct in_device *in_dev = in_dev_get(br->dev);
32084effd28cSLinus Lüssing 
32094effd28cSLinus Lüssing 	if (!in_dev)
32104effd28cSLinus Lüssing 		return;
32114effd28cSLinus Lüssing 
32129fb20801SFlorian Fainelli 	__ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
32134effd28cSLinus Lüssing 	in_dev_put(in_dev);
32144effd28cSLinus Lüssing }
32154effd28cSLinus Lüssing 
32164effd28cSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
32174effd28cSLinus Lüssing static void br_ip6_multicast_join_snoopers(struct net_bridge *br)
32184effd28cSLinus Lüssing {
32194effd28cSLinus Lüssing 	struct in6_addr addr;
32204effd28cSLinus Lüssing 
32214effd28cSLinus Lüssing 	ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a));
32224effd28cSLinus Lüssing 	ipv6_dev_mc_inc(br->dev, &addr);
32234effd28cSLinus Lüssing }
32244effd28cSLinus Lüssing #else
32254effd28cSLinus Lüssing static inline void br_ip6_multicast_join_snoopers(struct net_bridge *br)
32264effd28cSLinus Lüssing {
32274effd28cSLinus Lüssing }
32284effd28cSLinus Lüssing #endif
32294effd28cSLinus Lüssing 
32304effd28cSLinus Lüssing static void br_multicast_join_snoopers(struct net_bridge *br)
32314effd28cSLinus Lüssing {
32324effd28cSLinus Lüssing 	br_ip4_multicast_join_snoopers(br);
32334effd28cSLinus Lüssing 	br_ip6_multicast_join_snoopers(br);
32344effd28cSLinus Lüssing }
32354effd28cSLinus Lüssing 
32364effd28cSLinus Lüssing static void br_ip4_multicast_leave_snoopers(struct net_bridge *br)
32374effd28cSLinus Lüssing {
32384effd28cSLinus Lüssing 	struct in_device *in_dev = in_dev_get(br->dev);
32394effd28cSLinus Lüssing 
32404effd28cSLinus Lüssing 	if (WARN_ON(!in_dev))
32414effd28cSLinus Lüssing 		return;
32424effd28cSLinus Lüssing 
32439fb20801SFlorian Fainelli 	__ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
32444effd28cSLinus Lüssing 	in_dev_put(in_dev);
32454effd28cSLinus Lüssing }
32464effd28cSLinus Lüssing 
32474effd28cSLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
32484effd28cSLinus Lüssing static void br_ip6_multicast_leave_snoopers(struct net_bridge *br)
32494effd28cSLinus Lüssing {
32504effd28cSLinus Lüssing 	struct in6_addr addr;
32514effd28cSLinus Lüssing 
32524effd28cSLinus Lüssing 	ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a));
32534effd28cSLinus Lüssing 	ipv6_dev_mc_dec(br->dev, &addr);
32544effd28cSLinus Lüssing }
32554effd28cSLinus Lüssing #else
32564effd28cSLinus Lüssing static inline void br_ip6_multicast_leave_snoopers(struct net_bridge *br)
32574effd28cSLinus Lüssing {
32584effd28cSLinus Lüssing }
32594effd28cSLinus Lüssing #endif
32604effd28cSLinus Lüssing 
32614effd28cSLinus Lüssing static void br_multicast_leave_snoopers(struct net_bridge *br)
32624effd28cSLinus Lüssing {
32634effd28cSLinus Lüssing 	br_ip4_multicast_leave_snoopers(br);
32644effd28cSLinus Lüssing 	br_ip6_multicast_leave_snoopers(br);
32654effd28cSLinus Lüssing }
32664effd28cSLinus Lüssing 
3267cc0fdd80SLinus Lüssing static void __br_multicast_open(struct net_bridge *br,
326890010b36SLinus Lüssing 				struct bridge_mcast_own_query *query)
3269eb1d1641SHerbert Xu {
3270cc0fdd80SLinus Lüssing 	query->startup_sent = 0;
3271eb1d1641SHerbert Xu 
327213cefad2SNikolay Aleksandrov 	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
3273eb1d1641SHerbert Xu 		return;
3274eb1d1641SHerbert Xu 
3275cc0fdd80SLinus Lüssing 	mod_timer(&query->timer, jiffies);
3276cc0fdd80SLinus Lüssing }
3277cc0fdd80SLinus Lüssing 
3278cc0fdd80SLinus Lüssing void br_multicast_open(struct net_bridge *br)
3279cc0fdd80SLinus Lüssing {
32804effd28cSLinus Lüssing 	if (br_opt_get(br, BROPT_MULTICAST_ENABLED))
32814effd28cSLinus Lüssing 		br_multicast_join_snoopers(br);
32824effd28cSLinus Lüssing 
328390010b36SLinus Lüssing 	__br_multicast_open(br, &br->ip4_own_query);
3284cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
328590010b36SLinus Lüssing 	__br_multicast_open(br, &br->ip6_own_query);
3286cc0fdd80SLinus Lüssing #endif
3287eb1d1641SHerbert Xu }
3288eb1d1641SHerbert Xu 
3289eb1d1641SHerbert Xu void br_multicast_stop(struct net_bridge *br)
3290eb1d1641SHerbert Xu {
3291eb1d1641SHerbert Xu 	del_timer_sync(&br->multicast_router_timer);
329290010b36SLinus Lüssing 	del_timer_sync(&br->ip4_other_query.timer);
329390010b36SLinus Lüssing 	del_timer_sync(&br->ip4_own_query.timer);
3294cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
329590010b36SLinus Lüssing 	del_timer_sync(&br->ip6_other_query.timer);
329690010b36SLinus Lüssing 	del_timer_sync(&br->ip6_own_query.timer);
3297cc0fdd80SLinus Lüssing #endif
32984effd28cSLinus Lüssing 
32994effd28cSLinus Lüssing 	if (br_opt_get(br, BROPT_MULTICAST_ENABLED))
33004effd28cSLinus Lüssing 		br_multicast_leave_snoopers(br);
3301e10177abSSatish Ashok }
3302e10177abSSatish Ashok 
3303e10177abSSatish Ashok void br_multicast_dev_del(struct net_bridge *br)
3304e10177abSSatish Ashok {
3305e10177abSSatish Ashok 	struct net_bridge_mdb_entry *mp;
33068b671779SNikolay Aleksandrov 	HLIST_HEAD(deleted_head);
330719e3a9c9SNikolay Aleksandrov 	struct hlist_node *tmp;
3308eb1d1641SHerbert Xu 
3309eb1d1641SHerbert Xu 	spin_lock_bh(&br->multicast_lock);
3310e12cec65SNikolay Aleksandrov 	hlist_for_each_entry_safe(mp, tmp, &br->mdb_list, mdb_node)
3311e12cec65SNikolay Aleksandrov 		br_multicast_del_mdb_entry(mp);
3312e12cec65SNikolay Aleksandrov 	hlist_move_list(&br->mcast_gc_list, &deleted_head);
3313eb1d1641SHerbert Xu 	spin_unlock_bh(&br->multicast_lock);
331419e3a9c9SNikolay Aleksandrov 
3315e12cec65SNikolay Aleksandrov 	br_multicast_gc(&deleted_head);
3316e12cec65SNikolay Aleksandrov 	cancel_work_sync(&br->mcast_gc_work);
33178b671779SNikolay Aleksandrov 
33184329596cSNikolay Aleksandrov 	rcu_barrier();
3319eb1d1641SHerbert Xu }
33200909e117SHerbert Xu 
33210909e117SHerbert Xu int br_multicast_set_router(struct net_bridge *br, unsigned long val)
33220909e117SHerbert Xu {
33236ae4ae8eSLinus Lüssing 	int err = -EINVAL;
33240909e117SHerbert Xu 
33250909e117SHerbert Xu 	spin_lock_bh(&br->multicast_lock);
33260909e117SHerbert Xu 
33270909e117SHerbert Xu 	switch (val) {
33287f0aec7aSNikolay Aleksandrov 	case MDB_RTR_TYPE_DISABLED:
33297f0aec7aSNikolay Aleksandrov 	case MDB_RTR_TYPE_PERM:
333077041420SYotam Gigi 		br_mc_router_state_change(br, val == MDB_RTR_TYPE_PERM);
33310909e117SHerbert Xu 		del_timer(&br->multicast_router_timer);
333277041420SYotam Gigi 		br->multicast_router = val;
333377041420SYotam Gigi 		err = 0;
333477041420SYotam Gigi 		break;
33357f0aec7aSNikolay Aleksandrov 	case MDB_RTR_TYPE_TEMP_QUERY:
333677041420SYotam Gigi 		if (br->multicast_router != MDB_RTR_TYPE_TEMP_QUERY)
333777041420SYotam Gigi 			br_mc_router_state_change(br, false);
33380909e117SHerbert Xu 		br->multicast_router = val;
33390909e117SHerbert Xu 		err = 0;
33400909e117SHerbert Xu 		break;
33410909e117SHerbert Xu 	}
33420909e117SHerbert Xu 
33430909e117SHerbert Xu 	spin_unlock_bh(&br->multicast_lock);
33440909e117SHerbert Xu 
33450909e117SHerbert Xu 	return err;
33460909e117SHerbert Xu }
33470909e117SHerbert Xu 
33487f0aec7aSNikolay Aleksandrov static void __del_port_router(struct net_bridge_port *p)
33497f0aec7aSNikolay Aleksandrov {
33507f0aec7aSNikolay Aleksandrov 	if (hlist_unhashed(&p->rlist))
33517f0aec7aSNikolay Aleksandrov 		return;
33527f0aec7aSNikolay Aleksandrov 	hlist_del_init_rcu(&p->rlist);
33537f0aec7aSNikolay Aleksandrov 	br_rtr_notify(p->br->dev, p, RTM_DELMDB);
33546d549648SNogah Frankel 	br_port_mc_router_state_change(p, false);
3355f12e7d95SNogah Frankel 
3356f12e7d95SNogah Frankel 	/* don't allow timer refresh */
3357f12e7d95SNogah Frankel 	if (p->multicast_router == MDB_RTR_TYPE_TEMP)
3358f12e7d95SNogah Frankel 		p->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
33597f0aec7aSNikolay Aleksandrov }
33607f0aec7aSNikolay Aleksandrov 
33610909e117SHerbert Xu int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
33620909e117SHerbert Xu {
33630909e117SHerbert Xu 	struct net_bridge *br = p->br;
3364a55d8246SNikolay Aleksandrov 	unsigned long now = jiffies;
33656ae4ae8eSLinus Lüssing 	int err = -EINVAL;
33660909e117SHerbert Xu 
33670909e117SHerbert Xu 	spin_lock(&br->multicast_lock);
33684950cfd1SNikolay Aleksandrov 	if (p->multicast_router == val) {
3369a55d8246SNikolay Aleksandrov 		/* Refresh the temp router port timer */
3370a55d8246SNikolay Aleksandrov 		if (p->multicast_router == MDB_RTR_TYPE_TEMP)
3371a55d8246SNikolay Aleksandrov 			mod_timer(&p->multicast_router_timer,
3372a55d8246SNikolay Aleksandrov 				  now + br->multicast_querier_interval);
33734950cfd1SNikolay Aleksandrov 		err = 0;
33744950cfd1SNikolay Aleksandrov 		goto unlock;
33754950cfd1SNikolay Aleksandrov 	}
33760909e117SHerbert Xu 	switch (val) {
33777f0aec7aSNikolay Aleksandrov 	case MDB_RTR_TYPE_DISABLED:
33787f0aec7aSNikolay Aleksandrov 		p->multicast_router = MDB_RTR_TYPE_DISABLED;
33797f0aec7aSNikolay Aleksandrov 		__del_port_router(p);
33800909e117SHerbert Xu 		del_timer(&p->multicast_router_timer);
33810909e117SHerbert Xu 		break;
33827f0aec7aSNikolay Aleksandrov 	case MDB_RTR_TYPE_TEMP_QUERY:
33837f0aec7aSNikolay Aleksandrov 		p->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
33847f0aec7aSNikolay Aleksandrov 		__del_port_router(p);
33857f0aec7aSNikolay Aleksandrov 		break;
33867f0aec7aSNikolay Aleksandrov 	case MDB_RTR_TYPE_PERM:
33877f0aec7aSNikolay Aleksandrov 		p->multicast_router = MDB_RTR_TYPE_PERM;
33887f0aec7aSNikolay Aleksandrov 		del_timer(&p->multicast_router_timer);
33890909e117SHerbert Xu 		br_multicast_add_router(br, p);
33900909e117SHerbert Xu 		break;
3391a55d8246SNikolay Aleksandrov 	case MDB_RTR_TYPE_TEMP:
3392a55d8246SNikolay Aleksandrov 		p->multicast_router = MDB_RTR_TYPE_TEMP;
3393a55d8246SNikolay Aleksandrov 		br_multicast_mark_router(br, p);
3394a55d8246SNikolay Aleksandrov 		break;
33957f0aec7aSNikolay Aleksandrov 	default:
33967f0aec7aSNikolay Aleksandrov 		goto unlock;
33970909e117SHerbert Xu 	}
33987f0aec7aSNikolay Aleksandrov 	err = 0;
33997f0aec7aSNikolay Aleksandrov unlock:
34000909e117SHerbert Xu 	spin_unlock(&br->multicast_lock);
34010909e117SHerbert Xu 
34020909e117SHerbert Xu 	return err;
34030909e117SHerbert Xu }
3404561f1103SHerbert Xu 
3405cc0fdd80SLinus Lüssing static void br_multicast_start_querier(struct net_bridge *br,
340690010b36SLinus Lüssing 				       struct bridge_mcast_own_query *query)
3407561f1103SHerbert Xu {
3408561f1103SHerbert Xu 	struct net_bridge_port *port;
340974857216SHerbert Xu 
3410cc0fdd80SLinus Lüssing 	__br_multicast_open(br, query);
341174857216SHerbert Xu 
3412c5b493ceSNikolay Aleksandrov 	rcu_read_lock();
3413c5b493ceSNikolay Aleksandrov 	list_for_each_entry_rcu(port, &br->port_list, list) {
341474857216SHerbert Xu 		if (port->state == BR_STATE_DISABLED ||
341574857216SHerbert Xu 		    port->state == BR_STATE_BLOCKING)
341674857216SHerbert Xu 			continue;
341774857216SHerbert Xu 
341890010b36SLinus Lüssing 		if (query == &br->ip4_own_query)
341990010b36SLinus Lüssing 			br_multicast_enable(&port->ip4_own_query);
3420cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
3421cc0fdd80SLinus Lüssing 		else
342290010b36SLinus Lüssing 			br_multicast_enable(&port->ip6_own_query);
3423cc0fdd80SLinus Lüssing #endif
342474857216SHerbert Xu 	}
3425c5b493ceSNikolay Aleksandrov 	rcu_read_unlock();
342674857216SHerbert Xu }
342774857216SHerbert Xu 
342874857216SHerbert Xu int br_multicast_toggle(struct net_bridge *br, unsigned long val)
342974857216SHerbert Xu {
34307cb3f921SNikolay Aleksandrov 	struct net_bridge_port *port;
3431561f1103SHerbert Xu 
3432ef5e0d82SAndrey Vagin 	spin_lock_bh(&br->multicast_lock);
343313cefad2SNikolay Aleksandrov 	if (!!br_opt_get(br, BROPT_MULTICAST_ENABLED) == !!val)
3434561f1103SHerbert Xu 		goto unlock;
3435561f1103SHerbert Xu 
343613cefad2SNikolay Aleksandrov 	br_mc_disabled_update(br->dev, val);
343713cefad2SNikolay Aleksandrov 	br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val);
34384effd28cSLinus Lüssing 	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) {
34394effd28cSLinus Lüssing 		br_multicast_leave_snoopers(br);
3440561f1103SHerbert Xu 		goto unlock;
34414effd28cSLinus Lüssing 	}
3442561f1103SHerbert Xu 
34433a7fda06SHerbert Xu 	if (!netif_running(br->dev))
34443a7fda06SHerbert Xu 		goto unlock;
34453a7fda06SHerbert Xu 
34467cb3f921SNikolay Aleksandrov 	br_multicast_open(br);
34477cb3f921SNikolay Aleksandrov 	list_for_each_entry(port, &br->port_list, list)
34487cb3f921SNikolay Aleksandrov 		__br_multicast_enable_port(port);
3449561f1103SHerbert Xu 
3450561f1103SHerbert Xu unlock:
3451ef5e0d82SAndrey Vagin 	spin_unlock_bh(&br->multicast_lock);
3452561f1103SHerbert Xu 
3453a26d94bfSYueHaibing 	return 0;
3454561f1103SHerbert Xu }
3455b195167fSHerbert Xu 
34569341b988SIdo Schimmel bool br_multicast_enabled(const struct net_device *dev)
34579341b988SIdo Schimmel {
34589341b988SIdo Schimmel 	struct net_bridge *br = netdev_priv(dev);
34599341b988SIdo Schimmel 
346013cefad2SNikolay Aleksandrov 	return !!br_opt_get(br, BROPT_MULTICAST_ENABLED);
34619341b988SIdo Schimmel }
34629341b988SIdo Schimmel EXPORT_SYMBOL_GPL(br_multicast_enabled);
34639341b988SIdo Schimmel 
34640912bda4SYotam Gigi bool br_multicast_router(const struct net_device *dev)
34650912bda4SYotam Gigi {
34660912bda4SYotam Gigi 	struct net_bridge *br = netdev_priv(dev);
34670912bda4SYotam Gigi 	bool is_router;
34680912bda4SYotam Gigi 
34690912bda4SYotam Gigi 	spin_lock_bh(&br->multicast_lock);
34700912bda4SYotam Gigi 	is_router = br_multicast_is_router(br);
34710912bda4SYotam Gigi 	spin_unlock_bh(&br->multicast_lock);
34720912bda4SYotam Gigi 	return is_router;
34730912bda4SYotam Gigi }
34740912bda4SYotam Gigi EXPORT_SYMBOL_GPL(br_multicast_router);
34750912bda4SYotam Gigi 
3476c5c23260SHerbert Xu int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
3477c5c23260SHerbert Xu {
3478b00589afSLinus Lüssing 	unsigned long max_delay;
3479b00589afSLinus Lüssing 
3480c5c23260SHerbert Xu 	val = !!val;
3481c5c23260SHerbert Xu 
3482c5c23260SHerbert Xu 	spin_lock_bh(&br->multicast_lock);
3483675779adSNikolay Aleksandrov 	if (br_opt_get(br, BROPT_MULTICAST_QUERIER) == val)
3484c5c23260SHerbert Xu 		goto unlock;
3485c5c23260SHerbert Xu 
3486675779adSNikolay Aleksandrov 	br_opt_toggle(br, BROPT_MULTICAST_QUERIER, !!val);
3487b00589afSLinus Lüssing 	if (!val)
3488b00589afSLinus Lüssing 		goto unlock;
3489b00589afSLinus Lüssing 
3490b00589afSLinus Lüssing 	max_delay = br->multicast_query_response_interval;
3491b00589afSLinus Lüssing 
349290010b36SLinus Lüssing 	if (!timer_pending(&br->ip4_other_query.timer))
349390010b36SLinus Lüssing 		br->ip4_other_query.delay_time = jiffies + max_delay;
3494cc0fdd80SLinus Lüssing 
349590010b36SLinus Lüssing 	br_multicast_start_querier(br, &br->ip4_own_query);
3496cc0fdd80SLinus Lüssing 
3497cc0fdd80SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
349890010b36SLinus Lüssing 	if (!timer_pending(&br->ip6_other_query.timer))
349990010b36SLinus Lüssing 		br->ip6_other_query.delay_time = jiffies + max_delay;
3500cc0fdd80SLinus Lüssing 
350190010b36SLinus Lüssing 	br_multicast_start_querier(br, &br->ip6_own_query);
3502cc0fdd80SLinus Lüssing #endif
3503c5c23260SHerbert Xu 
3504c5c23260SHerbert Xu unlock:
3505c5c23260SHerbert Xu 	spin_unlock_bh(&br->multicast_lock);
3506c5c23260SHerbert Xu 
3507c5c23260SHerbert Xu 	return 0;
3508c5c23260SHerbert Xu }
3509c5c23260SHerbert Xu 
35105e923585SNikolay Aleksandrov int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val)
35115e923585SNikolay Aleksandrov {
35125e923585SNikolay Aleksandrov 	/* Currently we support only version 2 and 3 */
35135e923585SNikolay Aleksandrov 	switch (val) {
35145e923585SNikolay Aleksandrov 	case 2:
35155e923585SNikolay Aleksandrov 	case 3:
35165e923585SNikolay Aleksandrov 		break;
35175e923585SNikolay Aleksandrov 	default:
35185e923585SNikolay Aleksandrov 		return -EINVAL;
35195e923585SNikolay Aleksandrov 	}
35205e923585SNikolay Aleksandrov 
35215e923585SNikolay Aleksandrov 	spin_lock_bh(&br->multicast_lock);
35225e923585SNikolay Aleksandrov 	br->multicast_igmp_version = val;
35235e923585SNikolay Aleksandrov 	spin_unlock_bh(&br->multicast_lock);
35245e923585SNikolay Aleksandrov 
35255e923585SNikolay Aleksandrov 	return 0;
35265e923585SNikolay Aleksandrov }
35275e923585SNikolay Aleksandrov 
3528aa2ae3e7SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
3529aa2ae3e7SNikolay Aleksandrov int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val)
3530aa2ae3e7SNikolay Aleksandrov {
3531aa2ae3e7SNikolay Aleksandrov 	/* Currently we support version 1 and 2 */
3532aa2ae3e7SNikolay Aleksandrov 	switch (val) {
3533aa2ae3e7SNikolay Aleksandrov 	case 1:
3534aa2ae3e7SNikolay Aleksandrov 	case 2:
3535aa2ae3e7SNikolay Aleksandrov 		break;
3536aa2ae3e7SNikolay Aleksandrov 	default:
3537aa2ae3e7SNikolay Aleksandrov 		return -EINVAL;
3538aa2ae3e7SNikolay Aleksandrov 	}
3539aa2ae3e7SNikolay Aleksandrov 
3540aa2ae3e7SNikolay Aleksandrov 	spin_lock_bh(&br->multicast_lock);
3541aa2ae3e7SNikolay Aleksandrov 	br->multicast_mld_version = val;
3542aa2ae3e7SNikolay Aleksandrov 	spin_unlock_bh(&br->multicast_lock);
3543aa2ae3e7SNikolay Aleksandrov 
3544aa2ae3e7SNikolay Aleksandrov 	return 0;
3545aa2ae3e7SNikolay Aleksandrov }
3546aa2ae3e7SNikolay Aleksandrov #endif
3547aa2ae3e7SNikolay Aleksandrov 
354807f8ac4aSLinus Lüssing /**
354907f8ac4aSLinus Lüssing  * br_multicast_list_adjacent - Returns snooped multicast addresses
355007f8ac4aSLinus Lüssing  * @dev:	The bridge port adjacent to which to retrieve addresses
355107f8ac4aSLinus Lüssing  * @br_ip_list:	The list to store found, snooped multicast IP addresses in
355207f8ac4aSLinus Lüssing  *
355307f8ac4aSLinus Lüssing  * Creates a list of IP addresses (struct br_ip_list) sensed by the multicast
355407f8ac4aSLinus Lüssing  * snooping feature on all bridge ports of dev's bridge device, excluding
355507f8ac4aSLinus Lüssing  * the addresses from dev itself.
355607f8ac4aSLinus Lüssing  *
355707f8ac4aSLinus Lüssing  * Returns the number of items added to br_ip_list.
355807f8ac4aSLinus Lüssing  *
355907f8ac4aSLinus Lüssing  * Notes:
356007f8ac4aSLinus Lüssing  * - br_ip_list needs to be initialized by caller
356107f8ac4aSLinus Lüssing  * - br_ip_list might contain duplicates in the end
356207f8ac4aSLinus Lüssing  *   (needs to be taken care of by caller)
356307f8ac4aSLinus Lüssing  * - br_ip_list needs to be freed by caller
356407f8ac4aSLinus Lüssing  */
356507f8ac4aSLinus Lüssing int br_multicast_list_adjacent(struct net_device *dev,
356607f8ac4aSLinus Lüssing 			       struct list_head *br_ip_list)
356707f8ac4aSLinus Lüssing {
356807f8ac4aSLinus Lüssing 	struct net_bridge *br;
356907f8ac4aSLinus Lüssing 	struct net_bridge_port *port;
357007f8ac4aSLinus Lüssing 	struct net_bridge_port_group *group;
357107f8ac4aSLinus Lüssing 	struct br_ip_list *entry;
357207f8ac4aSLinus Lüssing 	int count = 0;
357307f8ac4aSLinus Lüssing 
357407f8ac4aSLinus Lüssing 	rcu_read_lock();
357535f861e3SJulian Wiedmann 	if (!br_ip_list || !netif_is_bridge_port(dev))
357607f8ac4aSLinus Lüssing 		goto unlock;
357707f8ac4aSLinus Lüssing 
357807f8ac4aSLinus Lüssing 	port = br_port_get_rcu(dev);
357907f8ac4aSLinus Lüssing 	if (!port || !port->br)
358007f8ac4aSLinus Lüssing 		goto unlock;
358107f8ac4aSLinus Lüssing 
358207f8ac4aSLinus Lüssing 	br = port->br;
358307f8ac4aSLinus Lüssing 
358407f8ac4aSLinus Lüssing 	list_for_each_entry_rcu(port, &br->port_list, list) {
358507f8ac4aSLinus Lüssing 		if (!port->dev || port->dev == dev)
358607f8ac4aSLinus Lüssing 			continue;
358707f8ac4aSLinus Lüssing 
358807f8ac4aSLinus Lüssing 		hlist_for_each_entry_rcu(group, &port->mglist, mglist) {
358907f8ac4aSLinus Lüssing 			entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
359007f8ac4aSLinus Lüssing 			if (!entry)
359107f8ac4aSLinus Lüssing 				goto unlock;
359207f8ac4aSLinus Lüssing 
3593085b53c8SNikolay Aleksandrov 			entry->addr = group->key.addr;
359407f8ac4aSLinus Lüssing 			list_add(&entry->list, br_ip_list);
359507f8ac4aSLinus Lüssing 			count++;
359607f8ac4aSLinus Lüssing 		}
359707f8ac4aSLinus Lüssing 	}
359807f8ac4aSLinus Lüssing 
359907f8ac4aSLinus Lüssing unlock:
360007f8ac4aSLinus Lüssing 	rcu_read_unlock();
360107f8ac4aSLinus Lüssing 	return count;
360207f8ac4aSLinus Lüssing }
360307f8ac4aSLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_list_adjacent);
36042cd41431SLinus Lüssing 
36052cd41431SLinus Lüssing /**
3606c34963e2SLinus Lüssing  * br_multicast_has_querier_anywhere - Checks for a querier on a bridge
3607c34963e2SLinus Lüssing  * @dev: The bridge port providing the bridge on which to check for a querier
3608c34963e2SLinus Lüssing  * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
3609c34963e2SLinus Lüssing  *
3610c34963e2SLinus Lüssing  * Checks whether the given interface has a bridge on top and if so returns
3611c34963e2SLinus Lüssing  * true if a valid querier exists anywhere on the bridged link layer.
3612c34963e2SLinus Lüssing  * Otherwise returns false.
3613c34963e2SLinus Lüssing  */
3614c34963e2SLinus Lüssing bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto)
3615c34963e2SLinus Lüssing {
3616c34963e2SLinus Lüssing 	struct net_bridge *br;
3617c34963e2SLinus Lüssing 	struct net_bridge_port *port;
3618c34963e2SLinus Lüssing 	struct ethhdr eth;
3619c34963e2SLinus Lüssing 	bool ret = false;
3620c34963e2SLinus Lüssing 
3621c34963e2SLinus Lüssing 	rcu_read_lock();
362235f861e3SJulian Wiedmann 	if (!netif_is_bridge_port(dev))
3623c34963e2SLinus Lüssing 		goto unlock;
3624c34963e2SLinus Lüssing 
3625c34963e2SLinus Lüssing 	port = br_port_get_rcu(dev);
3626c34963e2SLinus Lüssing 	if (!port || !port->br)
3627c34963e2SLinus Lüssing 		goto unlock;
3628c34963e2SLinus Lüssing 
3629c34963e2SLinus Lüssing 	br = port->br;
3630c34963e2SLinus Lüssing 
3631c34963e2SLinus Lüssing 	memset(&eth, 0, sizeof(eth));
3632c34963e2SLinus Lüssing 	eth.h_proto = htons(proto);
3633c34963e2SLinus Lüssing 
3634c34963e2SLinus Lüssing 	ret = br_multicast_querier_exists(br, &eth);
3635c34963e2SLinus Lüssing 
3636c34963e2SLinus Lüssing unlock:
3637c34963e2SLinus Lüssing 	rcu_read_unlock();
3638c34963e2SLinus Lüssing 	return ret;
3639c34963e2SLinus Lüssing }
3640c34963e2SLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_has_querier_anywhere);
3641c34963e2SLinus Lüssing 
3642c34963e2SLinus Lüssing /**
36432cd41431SLinus Lüssing  * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port
36442cd41431SLinus Lüssing  * @dev: The bridge port adjacent to which to check for a querier
36452cd41431SLinus Lüssing  * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
36462cd41431SLinus Lüssing  *
36472cd41431SLinus Lüssing  * Checks whether the given interface has a bridge on top and if so returns
36482cd41431SLinus Lüssing  * true if a selected querier is behind one of the other ports of this
36492cd41431SLinus Lüssing  * bridge. Otherwise returns false.
36502cd41431SLinus Lüssing  */
36512cd41431SLinus Lüssing bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
36522cd41431SLinus Lüssing {
36532cd41431SLinus Lüssing 	struct net_bridge *br;
36542cd41431SLinus Lüssing 	struct net_bridge_port *port;
36552cd41431SLinus Lüssing 	bool ret = false;
36562cd41431SLinus Lüssing 
36572cd41431SLinus Lüssing 	rcu_read_lock();
365835f861e3SJulian Wiedmann 	if (!netif_is_bridge_port(dev))
36592cd41431SLinus Lüssing 		goto unlock;
36602cd41431SLinus Lüssing 
36612cd41431SLinus Lüssing 	port = br_port_get_rcu(dev);
36622cd41431SLinus Lüssing 	if (!port || !port->br)
36632cd41431SLinus Lüssing 		goto unlock;
36642cd41431SLinus Lüssing 
36652cd41431SLinus Lüssing 	br = port->br;
36662cd41431SLinus Lüssing 
36672cd41431SLinus Lüssing 	switch (proto) {
36682cd41431SLinus Lüssing 	case ETH_P_IP:
36692cd41431SLinus Lüssing 		if (!timer_pending(&br->ip4_other_query.timer) ||
36702cd41431SLinus Lüssing 		    rcu_dereference(br->ip4_querier.port) == port)
36712cd41431SLinus Lüssing 			goto unlock;
36722cd41431SLinus Lüssing 		break;
36733993c4e1SLinus Lüssing #if IS_ENABLED(CONFIG_IPV6)
36742cd41431SLinus Lüssing 	case ETH_P_IPV6:
36752cd41431SLinus Lüssing 		if (!timer_pending(&br->ip6_other_query.timer) ||
36762cd41431SLinus Lüssing 		    rcu_dereference(br->ip6_querier.port) == port)
36772cd41431SLinus Lüssing 			goto unlock;
36782cd41431SLinus Lüssing 		break;
36793993c4e1SLinus Lüssing #endif
36802cd41431SLinus Lüssing 	default:
36812cd41431SLinus Lüssing 		goto unlock;
36822cd41431SLinus Lüssing 	}
36832cd41431SLinus Lüssing 
36842cd41431SLinus Lüssing 	ret = true;
36852cd41431SLinus Lüssing unlock:
36862cd41431SLinus Lüssing 	rcu_read_unlock();
36872cd41431SLinus Lüssing 	return ret;
36882cd41431SLinus Lüssing }
36892cd41431SLinus Lüssing EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent);
36901080ab95SNikolay Aleksandrov 
36911080ab95SNikolay Aleksandrov static void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
3692a65056ecSNikolay Aleksandrov 			       const struct sk_buff *skb, u8 type, u8 dir)
36931080ab95SNikolay Aleksandrov {
36941080ab95SNikolay Aleksandrov 	struct bridge_mcast_stats *pstats = this_cpu_ptr(stats);
3695a65056ecSNikolay Aleksandrov 	__be16 proto = skb->protocol;
3696a65056ecSNikolay Aleksandrov 	unsigned int t_len;
36971080ab95SNikolay Aleksandrov 
36981080ab95SNikolay Aleksandrov 	u64_stats_update_begin(&pstats->syncp);
36991080ab95SNikolay Aleksandrov 	switch (proto) {
37001080ab95SNikolay Aleksandrov 	case htons(ETH_P_IP):
3701a65056ecSNikolay Aleksandrov 		t_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb);
37021080ab95SNikolay Aleksandrov 		switch (type) {
37031080ab95SNikolay Aleksandrov 		case IGMP_HOST_MEMBERSHIP_REPORT:
37041080ab95SNikolay Aleksandrov 			pstats->mstats.igmp_v1reports[dir]++;
37051080ab95SNikolay Aleksandrov 			break;
37061080ab95SNikolay Aleksandrov 		case IGMPV2_HOST_MEMBERSHIP_REPORT:
37071080ab95SNikolay Aleksandrov 			pstats->mstats.igmp_v2reports[dir]++;
37081080ab95SNikolay Aleksandrov 			break;
37091080ab95SNikolay Aleksandrov 		case IGMPV3_HOST_MEMBERSHIP_REPORT:
37101080ab95SNikolay Aleksandrov 			pstats->mstats.igmp_v3reports[dir]++;
37111080ab95SNikolay Aleksandrov 			break;
37121080ab95SNikolay Aleksandrov 		case IGMP_HOST_MEMBERSHIP_QUERY:
3713a65056ecSNikolay Aleksandrov 			if (t_len != sizeof(struct igmphdr)) {
3714a65056ecSNikolay Aleksandrov 				pstats->mstats.igmp_v3queries[dir]++;
3715a65056ecSNikolay Aleksandrov 			} else {
3716a65056ecSNikolay Aleksandrov 				unsigned int offset = skb_transport_offset(skb);
3717a65056ecSNikolay Aleksandrov 				struct igmphdr *ih, _ihdr;
3718a65056ecSNikolay Aleksandrov 
3719a65056ecSNikolay Aleksandrov 				ih = skb_header_pointer(skb, offset,
3720a65056ecSNikolay Aleksandrov 							sizeof(_ihdr), &_ihdr);
3721a65056ecSNikolay Aleksandrov 				if (!ih)
3722a65056ecSNikolay Aleksandrov 					break;
3723a65056ecSNikolay Aleksandrov 				if (!ih->code)
3724a65056ecSNikolay Aleksandrov 					pstats->mstats.igmp_v1queries[dir]++;
3725a65056ecSNikolay Aleksandrov 				else
3726a65056ecSNikolay Aleksandrov 					pstats->mstats.igmp_v2queries[dir]++;
3727a65056ecSNikolay Aleksandrov 			}
37281080ab95SNikolay Aleksandrov 			break;
37291080ab95SNikolay Aleksandrov 		case IGMP_HOST_LEAVE_MESSAGE:
37301080ab95SNikolay Aleksandrov 			pstats->mstats.igmp_leaves[dir]++;
37311080ab95SNikolay Aleksandrov 			break;
37321080ab95SNikolay Aleksandrov 		}
37331080ab95SNikolay Aleksandrov 		break;
37341080ab95SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
37351080ab95SNikolay Aleksandrov 	case htons(ETH_P_IPV6):
3736a65056ecSNikolay Aleksandrov 		t_len = ntohs(ipv6_hdr(skb)->payload_len) +
3737a65056ecSNikolay Aleksandrov 			sizeof(struct ipv6hdr);
3738a65056ecSNikolay Aleksandrov 		t_len -= skb_network_header_len(skb);
37391080ab95SNikolay Aleksandrov 		switch (type) {
37401080ab95SNikolay Aleksandrov 		case ICMPV6_MGM_REPORT:
37411080ab95SNikolay Aleksandrov 			pstats->mstats.mld_v1reports[dir]++;
37421080ab95SNikolay Aleksandrov 			break;
37431080ab95SNikolay Aleksandrov 		case ICMPV6_MLD2_REPORT:
37441080ab95SNikolay Aleksandrov 			pstats->mstats.mld_v2reports[dir]++;
37451080ab95SNikolay Aleksandrov 			break;
37461080ab95SNikolay Aleksandrov 		case ICMPV6_MGM_QUERY:
3747a65056ecSNikolay Aleksandrov 			if (t_len != sizeof(struct mld_msg))
3748a65056ecSNikolay Aleksandrov 				pstats->mstats.mld_v2queries[dir]++;
3749a65056ecSNikolay Aleksandrov 			else
3750a65056ecSNikolay Aleksandrov 				pstats->mstats.mld_v1queries[dir]++;
37511080ab95SNikolay Aleksandrov 			break;
37521080ab95SNikolay Aleksandrov 		case ICMPV6_MGM_REDUCTION:
37531080ab95SNikolay Aleksandrov 			pstats->mstats.mld_leaves[dir]++;
37541080ab95SNikolay Aleksandrov 			break;
37551080ab95SNikolay Aleksandrov 		}
37561080ab95SNikolay Aleksandrov 		break;
37571080ab95SNikolay Aleksandrov #endif /* CONFIG_IPV6 */
37581080ab95SNikolay Aleksandrov 	}
37591080ab95SNikolay Aleksandrov 	u64_stats_update_end(&pstats->syncp);
37601080ab95SNikolay Aleksandrov }
37611080ab95SNikolay Aleksandrov 
37621080ab95SNikolay Aleksandrov void br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p,
3763a65056ecSNikolay Aleksandrov 			const struct sk_buff *skb, u8 type, u8 dir)
37641080ab95SNikolay Aleksandrov {
37651080ab95SNikolay Aleksandrov 	struct bridge_mcast_stats __percpu *stats;
37661080ab95SNikolay Aleksandrov 
37671080ab95SNikolay Aleksandrov 	/* if multicast_disabled is true then igmp type can't be set */
3768675779adSNikolay Aleksandrov 	if (!type || !br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED))
37691080ab95SNikolay Aleksandrov 		return;
37701080ab95SNikolay Aleksandrov 
37711080ab95SNikolay Aleksandrov 	if (p)
37721080ab95SNikolay Aleksandrov 		stats = p->mcast_stats;
37731080ab95SNikolay Aleksandrov 	else
37741080ab95SNikolay Aleksandrov 		stats = br->mcast_stats;
37751080ab95SNikolay Aleksandrov 	if (WARN_ON(!stats))
37761080ab95SNikolay Aleksandrov 		return;
37771080ab95SNikolay Aleksandrov 
3778a65056ecSNikolay Aleksandrov 	br_mcast_stats_add(stats, skb, type, dir);
37791080ab95SNikolay Aleksandrov }
37801080ab95SNikolay Aleksandrov 
37811080ab95SNikolay Aleksandrov int br_multicast_init_stats(struct net_bridge *br)
37821080ab95SNikolay Aleksandrov {
37831080ab95SNikolay Aleksandrov 	br->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats);
37841080ab95SNikolay Aleksandrov 	if (!br->mcast_stats)
37851080ab95SNikolay Aleksandrov 		return -ENOMEM;
37861080ab95SNikolay Aleksandrov 
37871080ab95SNikolay Aleksandrov 	return 0;
37881080ab95SNikolay Aleksandrov }
37891080ab95SNikolay Aleksandrov 
3790b6fe0440SIdo Schimmel void br_multicast_uninit_stats(struct net_bridge *br)
3791b6fe0440SIdo Schimmel {
3792b6fe0440SIdo Schimmel 	free_percpu(br->mcast_stats);
3793b6fe0440SIdo Schimmel }
3794b6fe0440SIdo Schimmel 
3795b3b6a84cSArnd Bergmann /* noinline for https://bugs.llvm.org/show_bug.cgi?id=45802#c9 */
3796b3b6a84cSArnd Bergmann static noinline_for_stack void mcast_stats_add_dir(u64 *dst, u64 *src)
37971080ab95SNikolay Aleksandrov {
37981080ab95SNikolay Aleksandrov 	dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX];
37991080ab95SNikolay Aleksandrov 	dst[BR_MCAST_DIR_TX] += src[BR_MCAST_DIR_TX];
38001080ab95SNikolay Aleksandrov }
38011080ab95SNikolay Aleksandrov 
38021080ab95SNikolay Aleksandrov void br_multicast_get_stats(const struct net_bridge *br,
38031080ab95SNikolay Aleksandrov 			    const struct net_bridge_port *p,
38041080ab95SNikolay Aleksandrov 			    struct br_mcast_stats *dest)
38051080ab95SNikolay Aleksandrov {
38061080ab95SNikolay Aleksandrov 	struct bridge_mcast_stats __percpu *stats;
38071080ab95SNikolay Aleksandrov 	struct br_mcast_stats tdst;
38081080ab95SNikolay Aleksandrov 	int i;
38091080ab95SNikolay Aleksandrov 
38101080ab95SNikolay Aleksandrov 	memset(dest, 0, sizeof(*dest));
38111080ab95SNikolay Aleksandrov 	if (p)
38121080ab95SNikolay Aleksandrov 		stats = p->mcast_stats;
38131080ab95SNikolay Aleksandrov 	else
38141080ab95SNikolay Aleksandrov 		stats = br->mcast_stats;
38151080ab95SNikolay Aleksandrov 	if (WARN_ON(!stats))
38161080ab95SNikolay Aleksandrov 		return;
38171080ab95SNikolay Aleksandrov 
38181080ab95SNikolay Aleksandrov 	memset(&tdst, 0, sizeof(tdst));
38191080ab95SNikolay Aleksandrov 	for_each_possible_cpu(i) {
38201080ab95SNikolay Aleksandrov 		struct bridge_mcast_stats *cpu_stats = per_cpu_ptr(stats, i);
38211080ab95SNikolay Aleksandrov 		struct br_mcast_stats temp;
38221080ab95SNikolay Aleksandrov 		unsigned int start;
38231080ab95SNikolay Aleksandrov 
38241080ab95SNikolay Aleksandrov 		do {
38251080ab95SNikolay Aleksandrov 			start = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
38261080ab95SNikolay Aleksandrov 			memcpy(&temp, &cpu_stats->mstats, sizeof(temp));
38271080ab95SNikolay Aleksandrov 		} while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start));
38281080ab95SNikolay Aleksandrov 
3829a65056ecSNikolay Aleksandrov 		mcast_stats_add_dir(tdst.igmp_v1queries, temp.igmp_v1queries);
3830a65056ecSNikolay Aleksandrov 		mcast_stats_add_dir(tdst.igmp_v2queries, temp.igmp_v2queries);
3831a65056ecSNikolay Aleksandrov 		mcast_stats_add_dir(tdst.igmp_v3queries, temp.igmp_v3queries);
38321080ab95SNikolay Aleksandrov 		mcast_stats_add_dir(tdst.igmp_leaves, temp.igmp_leaves);
38331080ab95SNikolay Aleksandrov 		mcast_stats_add_dir(tdst.igmp_v1reports, temp.igmp_v1reports);
38341080ab95SNikolay Aleksandrov 		mcast_stats_add_dir(tdst.igmp_v2reports, temp.igmp_v2reports);
38351080ab95SNikolay Aleksandrov 		mcast_stats_add_dir(tdst.igmp_v3reports, temp.igmp_v3reports);
38361080ab95SNikolay Aleksandrov 		tdst.igmp_parse_errors += temp.igmp_parse_errors;
38371080ab95SNikolay Aleksandrov 
3838a65056ecSNikolay Aleksandrov 		mcast_stats_add_dir(tdst.mld_v1queries, temp.mld_v1queries);
3839a65056ecSNikolay Aleksandrov 		mcast_stats_add_dir(tdst.mld_v2queries, temp.mld_v2queries);
38401080ab95SNikolay Aleksandrov 		mcast_stats_add_dir(tdst.mld_leaves, temp.mld_leaves);
38411080ab95SNikolay Aleksandrov 		mcast_stats_add_dir(tdst.mld_v1reports, temp.mld_v1reports);
38421080ab95SNikolay Aleksandrov 		mcast_stats_add_dir(tdst.mld_v2reports, temp.mld_v2reports);
38431080ab95SNikolay Aleksandrov 		tdst.mld_parse_errors += temp.mld_parse_errors;
38441080ab95SNikolay Aleksandrov 	}
38451080ab95SNikolay Aleksandrov 	memcpy(dest, &tdst, sizeof(*dest));
38461080ab95SNikolay Aleksandrov }
384719e3a9c9SNikolay Aleksandrov 
384819e3a9c9SNikolay Aleksandrov int br_mdb_hash_init(struct net_bridge *br)
384919e3a9c9SNikolay Aleksandrov {
3850085b53c8SNikolay Aleksandrov 	int err;
3851085b53c8SNikolay Aleksandrov 
3852085b53c8SNikolay Aleksandrov 	err = rhashtable_init(&br->sg_port_tbl, &br_sg_port_rht_params);
3853085b53c8SNikolay Aleksandrov 	if (err)
3854085b53c8SNikolay Aleksandrov 		return err;
3855085b53c8SNikolay Aleksandrov 
3856085b53c8SNikolay Aleksandrov 	err = rhashtable_init(&br->mdb_hash_tbl, &br_mdb_rht_params);
3857085b53c8SNikolay Aleksandrov 	if (err) {
3858085b53c8SNikolay Aleksandrov 		rhashtable_destroy(&br->sg_port_tbl);
3859085b53c8SNikolay Aleksandrov 		return err;
3860085b53c8SNikolay Aleksandrov 	}
3861085b53c8SNikolay Aleksandrov 
3862085b53c8SNikolay Aleksandrov 	return 0;
386319e3a9c9SNikolay Aleksandrov }
386419e3a9c9SNikolay Aleksandrov 
386519e3a9c9SNikolay Aleksandrov void br_mdb_hash_fini(struct net_bridge *br)
386619e3a9c9SNikolay Aleksandrov {
3867085b53c8SNikolay Aleksandrov 	rhashtable_destroy(&br->sg_port_tbl);
386819e3a9c9SNikolay Aleksandrov 	rhashtable_destroy(&br->mdb_hash_tbl);
386919e3a9c9SNikolay Aleksandrov }
3870