162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Bridge multicast support.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/export.h>
1062306a36Sopenharmony_ci#include <linux/if_ether.h>
1162306a36Sopenharmony_ci#include <linux/igmp.h>
1262306a36Sopenharmony_ci#include <linux/in.h>
1362306a36Sopenharmony_ci#include <linux/jhash.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/log2.h>
1662306a36Sopenharmony_ci#include <linux/netdevice.h>
1762306a36Sopenharmony_ci#include <linux/netfilter_bridge.h>
1862306a36Sopenharmony_ci#include <linux/random.h>
1962306a36Sopenharmony_ci#include <linux/rculist.h>
2062306a36Sopenharmony_ci#include <linux/skbuff.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/timer.h>
2362306a36Sopenharmony_ci#include <linux/inetdevice.h>
2462306a36Sopenharmony_ci#include <linux/mroute.h>
2562306a36Sopenharmony_ci#include <net/ip.h>
2662306a36Sopenharmony_ci#include <net/switchdev.h>
2762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
2862306a36Sopenharmony_ci#include <linux/icmpv6.h>
2962306a36Sopenharmony_ci#include <net/ipv6.h>
3062306a36Sopenharmony_ci#include <net/mld.h>
3162306a36Sopenharmony_ci#include <net/ip6_checksum.h>
3262306a36Sopenharmony_ci#include <net/addrconf.h>
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci#include <trace/events/bridge.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include "br_private.h"
3762306a36Sopenharmony_ci#include "br_private_mcast_eht.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const struct rhashtable_params br_mdb_rht_params = {
4062306a36Sopenharmony_ci	.head_offset = offsetof(struct net_bridge_mdb_entry, rhnode),
4162306a36Sopenharmony_ci	.key_offset = offsetof(struct net_bridge_mdb_entry, addr),
4262306a36Sopenharmony_ci	.key_len = sizeof(struct br_ip),
4362306a36Sopenharmony_ci	.automatic_shrinking = true,
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic const struct rhashtable_params br_sg_port_rht_params = {
4762306a36Sopenharmony_ci	.head_offset = offsetof(struct net_bridge_port_group, rhnode),
4862306a36Sopenharmony_ci	.key_offset = offsetof(struct net_bridge_port_group, key),
4962306a36Sopenharmony_ci	.key_len = sizeof(struct net_bridge_port_group_sg_key),
5062306a36Sopenharmony_ci	.automatic_shrinking = true,
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void br_multicast_start_querier(struct net_bridge_mcast *brmctx,
5462306a36Sopenharmony_ci				       struct bridge_mcast_own_query *query);
5562306a36Sopenharmony_cistatic void br_ip4_multicast_add_router(struct net_bridge_mcast *brmctx,
5662306a36Sopenharmony_ci					struct net_bridge_mcast_port *pmctx);
5762306a36Sopenharmony_cistatic void br_ip4_multicast_leave_group(struct net_bridge_mcast *brmctx,
5862306a36Sopenharmony_ci					 struct net_bridge_mcast_port *pmctx,
5962306a36Sopenharmony_ci					 __be32 group,
6062306a36Sopenharmony_ci					 __u16 vid,
6162306a36Sopenharmony_ci					 const unsigned char *src);
6262306a36Sopenharmony_cistatic void br_multicast_port_group_rexmit(struct timer_list *t);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void
6562306a36Sopenharmony_cibr_multicast_rport_del_notify(struct net_bridge_mcast_port *pmctx, bool deleted);
6662306a36Sopenharmony_cistatic void br_ip6_multicast_add_router(struct net_bridge_mcast *brmctx,
6762306a36Sopenharmony_ci					struct net_bridge_mcast_port *pmctx);
6862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
6962306a36Sopenharmony_cistatic void br_ip6_multicast_leave_group(struct net_bridge_mcast *brmctx,
7062306a36Sopenharmony_ci					 struct net_bridge_mcast_port *pmctx,
7162306a36Sopenharmony_ci					 const struct in6_addr *group,
7262306a36Sopenharmony_ci					 __u16 vid, const unsigned char *src);
7362306a36Sopenharmony_ci#endif
7462306a36Sopenharmony_cistatic struct net_bridge_port_group *
7562306a36Sopenharmony_ci__br_multicast_add_group(struct net_bridge_mcast *brmctx,
7662306a36Sopenharmony_ci			 struct net_bridge_mcast_port *pmctx,
7762306a36Sopenharmony_ci			 struct br_ip *group,
7862306a36Sopenharmony_ci			 const unsigned char *src,
7962306a36Sopenharmony_ci			 u8 filter_mode,
8062306a36Sopenharmony_ci			 bool igmpv2_mldv1,
8162306a36Sopenharmony_ci			 bool blocked);
8262306a36Sopenharmony_cistatic void br_multicast_find_del_pg(struct net_bridge *br,
8362306a36Sopenharmony_ci				     struct net_bridge_port_group *pg);
8462306a36Sopenharmony_cistatic void __br_multicast_stop(struct net_bridge_mcast *brmctx);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int br_mc_disabled_update(struct net_device *dev, bool value,
8762306a36Sopenharmony_ci				 struct netlink_ext_ack *extack);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic struct net_bridge_port_group *
9062306a36Sopenharmony_cibr_sg_port_find(struct net_bridge *br,
9162306a36Sopenharmony_ci		struct net_bridge_port_group_sg_key *sg_p)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	lockdep_assert_held_once(&br->multicast_lock);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return rhashtable_lookup_fast(&br->sg_port_tbl, sg_p,
9662306a36Sopenharmony_ci				      br_sg_port_rht_params);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic struct net_bridge_mdb_entry *br_mdb_ip_get_rcu(struct net_bridge *br,
10062306a36Sopenharmony_ci						      struct br_ip *dst)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	return rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge *br,
10662306a36Sopenharmony_ci					   struct br_ip *dst)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct net_bridge_mdb_entry *ent;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	lockdep_assert_held_once(&br->multicast_lock);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	rcu_read_lock();
11362306a36Sopenharmony_ci	ent = rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
11462306a36Sopenharmony_ci	rcu_read_unlock();
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return ent;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic struct net_bridge_mdb_entry *br_mdb_ip4_get(struct net_bridge *br,
12062306a36Sopenharmony_ci						   __be32 dst, __u16 vid)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct br_ip br_dst;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	memset(&br_dst, 0, sizeof(br_dst));
12562306a36Sopenharmony_ci	br_dst.dst.ip4 = dst;
12662306a36Sopenharmony_ci	br_dst.proto = htons(ETH_P_IP);
12762306a36Sopenharmony_ci	br_dst.vid = vid;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return br_mdb_ip_get(br, &br_dst);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
13362306a36Sopenharmony_cistatic struct net_bridge_mdb_entry *br_mdb_ip6_get(struct net_bridge *br,
13462306a36Sopenharmony_ci						   const struct in6_addr *dst,
13562306a36Sopenharmony_ci						   __u16 vid)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct br_ip br_dst;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	memset(&br_dst, 0, sizeof(br_dst));
14062306a36Sopenharmony_ci	br_dst.dst.ip6 = *dst;
14162306a36Sopenharmony_ci	br_dst.proto = htons(ETH_P_IPV6);
14262306a36Sopenharmony_ci	br_dst.vid = vid;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return br_mdb_ip_get(br, &br_dst);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci#endif
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistruct net_bridge_mdb_entry *br_mdb_get(struct net_bridge_mcast *brmctx,
14962306a36Sopenharmony_ci					struct sk_buff *skb, u16 vid)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct net_bridge *br = brmctx->br;
15262306a36Sopenharmony_ci	struct br_ip ip;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
15562306a36Sopenharmony_ci	    br_multicast_ctx_vlan_global_disabled(brmctx))
15662306a36Sopenharmony_ci		return NULL;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (BR_INPUT_SKB_CB(skb)->igmp)
15962306a36Sopenharmony_ci		return NULL;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	memset(&ip, 0, sizeof(ip));
16262306a36Sopenharmony_ci	ip.proto = skb->protocol;
16362306a36Sopenharmony_ci	ip.vid = vid;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	switch (skb->protocol) {
16662306a36Sopenharmony_ci	case htons(ETH_P_IP):
16762306a36Sopenharmony_ci		ip.dst.ip4 = ip_hdr(skb)->daddr;
16862306a36Sopenharmony_ci		if (brmctx->multicast_igmp_version == 3) {
16962306a36Sopenharmony_ci			struct net_bridge_mdb_entry *mdb;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci			ip.src.ip4 = ip_hdr(skb)->saddr;
17262306a36Sopenharmony_ci			mdb = br_mdb_ip_get_rcu(br, &ip);
17362306a36Sopenharmony_ci			if (mdb)
17462306a36Sopenharmony_ci				return mdb;
17562306a36Sopenharmony_ci			ip.src.ip4 = 0;
17662306a36Sopenharmony_ci		}
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
17962306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
18062306a36Sopenharmony_ci		ip.dst.ip6 = ipv6_hdr(skb)->daddr;
18162306a36Sopenharmony_ci		if (brmctx->multicast_mld_version == 2) {
18262306a36Sopenharmony_ci			struct net_bridge_mdb_entry *mdb;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci			ip.src.ip6 = ipv6_hdr(skb)->saddr;
18562306a36Sopenharmony_ci			mdb = br_mdb_ip_get_rcu(br, &ip);
18662306a36Sopenharmony_ci			if (mdb)
18762306a36Sopenharmony_ci				return mdb;
18862306a36Sopenharmony_ci			memset(&ip.src.ip6, 0, sizeof(ip.src.ip6));
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci#endif
19262306a36Sopenharmony_ci	default:
19362306a36Sopenharmony_ci		ip.proto = 0;
19462306a36Sopenharmony_ci		ether_addr_copy(ip.dst.mac_addr, eth_hdr(skb)->h_dest);
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return br_mdb_ip_get_rcu(br, &ip);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/* IMPORTANT: this function must be used only when the contexts cannot be
20162306a36Sopenharmony_ci * passed down (e.g. timer) and must be used for read-only purposes because
20262306a36Sopenharmony_ci * the vlan snooping option can change, so it can return any context
20362306a36Sopenharmony_ci * (non-vlan or vlan). Its initial intended purpose is to read timer values
20462306a36Sopenharmony_ci * from the *current* context based on the option. At worst that could lead
20562306a36Sopenharmony_ci * to inconsistent timers when the contexts are changed, i.e. src timer
20662306a36Sopenharmony_ci * which needs to re-arm with a specific delay taken from the old context
20762306a36Sopenharmony_ci */
20862306a36Sopenharmony_cistatic struct net_bridge_mcast_port *
20962306a36Sopenharmony_cibr_multicast_pg_to_port_ctx(const struct net_bridge_port_group *pg)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx = &pg->key.port->multicast_ctx;
21262306a36Sopenharmony_ci	struct net_bridge_vlan *vlan;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	lockdep_assert_held_once(&pg->key.port->br->multicast_lock);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* if vlan snooping is disabled use the port's multicast context */
21762306a36Sopenharmony_ci	if (!pg->key.addr.vid ||
21862306a36Sopenharmony_ci	    !br_opt_get(pg->key.port->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED))
21962306a36Sopenharmony_ci		goto out;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* locking is tricky here, due to different rules for multicast and
22262306a36Sopenharmony_ci	 * vlans we need to take rcu to find the vlan and make sure it has
22362306a36Sopenharmony_ci	 * the BR_VLFLAG_MCAST_ENABLED flag set, it can only change under
22462306a36Sopenharmony_ci	 * multicast_lock which must be already held here, so the vlan's pmctx
22562306a36Sopenharmony_ci	 * can safely be used on return
22662306a36Sopenharmony_ci	 */
22762306a36Sopenharmony_ci	rcu_read_lock();
22862306a36Sopenharmony_ci	vlan = br_vlan_find(nbp_vlan_group_rcu(pg->key.port), pg->key.addr.vid);
22962306a36Sopenharmony_ci	if (vlan && !br_multicast_port_ctx_vlan_disabled(&vlan->port_mcast_ctx))
23062306a36Sopenharmony_ci		pmctx = &vlan->port_mcast_ctx;
23162306a36Sopenharmony_ci	else
23262306a36Sopenharmony_ci		pmctx = NULL;
23362306a36Sopenharmony_ci	rcu_read_unlock();
23462306a36Sopenharmony_ciout:
23562306a36Sopenharmony_ci	return pmctx;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic struct net_bridge_mcast_port *
23962306a36Sopenharmony_cibr_multicast_port_vid_to_port_ctx(struct net_bridge_port *port, u16 vid)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx = NULL;
24262306a36Sopenharmony_ci	struct net_bridge_vlan *vlan;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	lockdep_assert_held_once(&port->br->multicast_lock);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (!br_opt_get(port->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED))
24762306a36Sopenharmony_ci		return NULL;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* Take RCU to access the vlan. */
25062306a36Sopenharmony_ci	rcu_read_lock();
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	vlan = br_vlan_find(nbp_vlan_group_rcu(port), vid);
25362306a36Sopenharmony_ci	if (vlan && !br_multicast_port_ctx_vlan_disabled(&vlan->port_mcast_ctx))
25462306a36Sopenharmony_ci		pmctx = &vlan->port_mcast_ctx;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	rcu_read_unlock();
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return pmctx;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci/* when snooping we need to check if the contexts should be used
26262306a36Sopenharmony_ci * in the following order:
26362306a36Sopenharmony_ci * - if pmctx is non-NULL (port), check if it should be used
26462306a36Sopenharmony_ci * - if pmctx is NULL (bridge), check if brmctx should be used
26562306a36Sopenharmony_ci */
26662306a36Sopenharmony_cistatic bool
26762306a36Sopenharmony_cibr_multicast_ctx_should_use(const struct net_bridge_mcast *brmctx,
26862306a36Sopenharmony_ci			    const struct net_bridge_mcast_port *pmctx)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	if (!netif_running(brmctx->br->dev))
27162306a36Sopenharmony_ci		return false;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (pmctx)
27462306a36Sopenharmony_ci		return !br_multicast_port_ctx_state_disabled(pmctx);
27562306a36Sopenharmony_ci	else
27662306a36Sopenharmony_ci		return !br_multicast_ctx_vlan_disabled(brmctx);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic bool br_port_group_equal(struct net_bridge_port_group *p,
28062306a36Sopenharmony_ci				struct net_bridge_port *port,
28162306a36Sopenharmony_ci				const unsigned char *src)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	if (p->key.port != port)
28462306a36Sopenharmony_ci		return false;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!(port->flags & BR_MULTICAST_TO_UNICAST))
28762306a36Sopenharmony_ci		return true;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return ether_addr_equal(src, p->eth_addr);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void __fwd_add_star_excl(struct net_bridge_mcast_port *pmctx,
29362306a36Sopenharmony_ci				struct net_bridge_port_group *pg,
29462306a36Sopenharmony_ci				struct br_ip *sg_ip)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct net_bridge_port_group_sg_key sg_key;
29762306a36Sopenharmony_ci	struct net_bridge_port_group *src_pg;
29862306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	memset(&sg_key, 0, sizeof(sg_key));
30162306a36Sopenharmony_ci	brmctx = br_multicast_port_ctx_get_global(pmctx);
30262306a36Sopenharmony_ci	sg_key.port = pg->key.port;
30362306a36Sopenharmony_ci	sg_key.addr = *sg_ip;
30462306a36Sopenharmony_ci	if (br_sg_port_find(brmctx->br, &sg_key))
30562306a36Sopenharmony_ci		return;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	src_pg = __br_multicast_add_group(brmctx, pmctx,
30862306a36Sopenharmony_ci					  sg_ip, pg->eth_addr,
30962306a36Sopenharmony_ci					  MCAST_INCLUDE, false, false);
31062306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(src_pg) ||
31162306a36Sopenharmony_ci	    src_pg->rt_protocol != RTPROT_KERNEL)
31262306a36Sopenharmony_ci		return;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic void __fwd_del_star_excl(struct net_bridge_port_group *pg,
31862306a36Sopenharmony_ci				struct br_ip *sg_ip)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct net_bridge_port_group_sg_key sg_key;
32162306a36Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
32262306a36Sopenharmony_ci	struct net_bridge_port_group *src_pg;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	memset(&sg_key, 0, sizeof(sg_key));
32562306a36Sopenharmony_ci	sg_key.port = pg->key.port;
32662306a36Sopenharmony_ci	sg_key.addr = *sg_ip;
32762306a36Sopenharmony_ci	src_pg = br_sg_port_find(br, &sg_key);
32862306a36Sopenharmony_ci	if (!src_pg || !(src_pg->flags & MDB_PG_FLAGS_STAR_EXCL) ||
32962306a36Sopenharmony_ci	    src_pg->rt_protocol != RTPROT_KERNEL)
33062306a36Sopenharmony_ci		return;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	br_multicast_find_del_pg(br, src_pg);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci/* When a port group transitions to (or is added as) EXCLUDE we need to add it
33662306a36Sopenharmony_ci * to all other ports' S,G entries which are not blocked by the current group
33762306a36Sopenharmony_ci * for proper replication, the assumption is that any S,G blocked entries
33862306a36Sopenharmony_ci * are already added so the S,G,port lookup should skip them.
33962306a36Sopenharmony_ci * When a port group transitions from EXCLUDE -> INCLUDE mode or is being
34062306a36Sopenharmony_ci * deleted we need to remove it from all ports' S,G entries where it was
34162306a36Sopenharmony_ci * automatically installed before (i.e. where it's MDB_PG_FLAGS_STAR_EXCL).
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_civoid br_multicast_star_g_handle_mode(struct net_bridge_port_group *pg,
34462306a36Sopenharmony_ci				     u8 filter_mode)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
34762306a36Sopenharmony_ci	struct net_bridge_port_group *pg_lst;
34862306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx;
34962306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
35062306a36Sopenharmony_ci	struct br_ip sg_ip;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (WARN_ON(!br_multicast_is_star_g(&pg->key.addr)))
35362306a36Sopenharmony_ci		return;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	mp = br_mdb_ip_get(br, &pg->key.addr);
35662306a36Sopenharmony_ci	if (!mp)
35762306a36Sopenharmony_ci		return;
35862306a36Sopenharmony_ci	pmctx = br_multicast_pg_to_port_ctx(pg);
35962306a36Sopenharmony_ci	if (!pmctx)
36062306a36Sopenharmony_ci		return;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	memset(&sg_ip, 0, sizeof(sg_ip));
36362306a36Sopenharmony_ci	sg_ip = pg->key.addr;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	for (pg_lst = mlock_dereference(mp->ports, br);
36662306a36Sopenharmony_ci	     pg_lst;
36762306a36Sopenharmony_ci	     pg_lst = mlock_dereference(pg_lst->next, br)) {
36862306a36Sopenharmony_ci		struct net_bridge_group_src *src_ent;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		if (pg_lst == pg)
37162306a36Sopenharmony_ci			continue;
37262306a36Sopenharmony_ci		hlist_for_each_entry(src_ent, &pg_lst->src_list, node) {
37362306a36Sopenharmony_ci			if (!(src_ent->flags & BR_SGRP_F_INSTALLED))
37462306a36Sopenharmony_ci				continue;
37562306a36Sopenharmony_ci			sg_ip.src = src_ent->addr.src;
37662306a36Sopenharmony_ci			switch (filter_mode) {
37762306a36Sopenharmony_ci			case MCAST_INCLUDE:
37862306a36Sopenharmony_ci				__fwd_del_star_excl(pg, &sg_ip);
37962306a36Sopenharmony_ci				break;
38062306a36Sopenharmony_ci			case MCAST_EXCLUDE:
38162306a36Sopenharmony_ci				__fwd_add_star_excl(pmctx, pg, &sg_ip);
38262306a36Sopenharmony_ci				break;
38362306a36Sopenharmony_ci			}
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/* called when adding a new S,G with host_joined == false by default */
38962306a36Sopenharmony_cistatic void br_multicast_sg_host_state(struct net_bridge_mdb_entry *star_mp,
39062306a36Sopenharmony_ci				       struct net_bridge_port_group *sg)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	struct net_bridge_mdb_entry *sg_mp;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr)))
39562306a36Sopenharmony_ci		return;
39662306a36Sopenharmony_ci	if (!star_mp->host_joined)
39762306a36Sopenharmony_ci		return;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	sg_mp = br_mdb_ip_get(star_mp->br, &sg->key.addr);
40062306a36Sopenharmony_ci	if (!sg_mp)
40162306a36Sopenharmony_ci		return;
40262306a36Sopenharmony_ci	sg_mp->host_joined = true;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci/* set the host_joined state of all of *,G's S,G entries */
40662306a36Sopenharmony_cistatic void br_multicast_star_g_host_state(struct net_bridge_mdb_entry *star_mp)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct net_bridge *br = star_mp->br;
40962306a36Sopenharmony_ci	struct net_bridge_mdb_entry *sg_mp;
41062306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
41162306a36Sopenharmony_ci	struct br_ip sg_ip;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr)))
41462306a36Sopenharmony_ci		return;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	memset(&sg_ip, 0, sizeof(sg_ip));
41762306a36Sopenharmony_ci	sg_ip = star_mp->addr;
41862306a36Sopenharmony_ci	for (pg = mlock_dereference(star_mp->ports, br);
41962306a36Sopenharmony_ci	     pg;
42062306a36Sopenharmony_ci	     pg = mlock_dereference(pg->next, br)) {
42162306a36Sopenharmony_ci		struct net_bridge_group_src *src_ent;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		hlist_for_each_entry(src_ent, &pg->src_list, node) {
42462306a36Sopenharmony_ci			if (!(src_ent->flags & BR_SGRP_F_INSTALLED))
42562306a36Sopenharmony_ci				continue;
42662306a36Sopenharmony_ci			sg_ip.src = src_ent->addr.src;
42762306a36Sopenharmony_ci			sg_mp = br_mdb_ip_get(br, &sg_ip);
42862306a36Sopenharmony_ci			if (!sg_mp)
42962306a36Sopenharmony_ci				continue;
43062306a36Sopenharmony_ci			sg_mp->host_joined = star_mp->host_joined;
43162306a36Sopenharmony_ci		}
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void br_multicast_sg_del_exclude_ports(struct net_bridge_mdb_entry *sgmp)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
43862306a36Sopenharmony_ci	struct net_bridge_port_group *p;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* *,G exclude ports are only added to S,G entries */
44162306a36Sopenharmony_ci	if (WARN_ON(br_multicast_is_star_g(&sgmp->addr)))
44262306a36Sopenharmony_ci		return;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/* we need the STAR_EXCLUDE ports if there are non-STAR_EXCLUDE ports
44562306a36Sopenharmony_ci	 * we should ignore perm entries since they're managed by user-space
44662306a36Sopenharmony_ci	 */
44762306a36Sopenharmony_ci	for (pp = &sgmp->ports;
44862306a36Sopenharmony_ci	     (p = mlock_dereference(*pp, sgmp->br)) != NULL;
44962306a36Sopenharmony_ci	     pp = &p->next)
45062306a36Sopenharmony_ci		if (!(p->flags & (MDB_PG_FLAGS_STAR_EXCL |
45162306a36Sopenharmony_ci				  MDB_PG_FLAGS_PERMANENT)))
45262306a36Sopenharmony_ci			return;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* currently the host can only have joined the *,G which means
45562306a36Sopenharmony_ci	 * we treat it as EXCLUDE {}, so for an S,G it's considered a
45662306a36Sopenharmony_ci	 * STAR_EXCLUDE entry and we can safely leave it
45762306a36Sopenharmony_ci	 */
45862306a36Sopenharmony_ci	sgmp->host_joined = false;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	for (pp = &sgmp->ports;
46162306a36Sopenharmony_ci	     (p = mlock_dereference(*pp, sgmp->br)) != NULL;) {
46262306a36Sopenharmony_ci		if (!(p->flags & MDB_PG_FLAGS_PERMANENT))
46362306a36Sopenharmony_ci			br_multicast_del_pg(sgmp, p, pp);
46462306a36Sopenharmony_ci		else
46562306a36Sopenharmony_ci			pp = &p->next;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_civoid br_multicast_sg_add_exclude_ports(struct net_bridge_mdb_entry *star_mp,
47062306a36Sopenharmony_ci				       struct net_bridge_port_group *sg)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct net_bridge_port_group_sg_key sg_key;
47362306a36Sopenharmony_ci	struct net_bridge *br = star_mp->br;
47462306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx;
47562306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
47662306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (WARN_ON(br_multicast_is_star_g(&sg->key.addr)))
47962306a36Sopenharmony_ci		return;
48062306a36Sopenharmony_ci	if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr)))
48162306a36Sopenharmony_ci		return;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	br_multicast_sg_host_state(star_mp, sg);
48462306a36Sopenharmony_ci	memset(&sg_key, 0, sizeof(sg_key));
48562306a36Sopenharmony_ci	sg_key.addr = sg->key.addr;
48662306a36Sopenharmony_ci	/* we need to add all exclude ports to the S,G */
48762306a36Sopenharmony_ci	for (pg = mlock_dereference(star_mp->ports, br);
48862306a36Sopenharmony_ci	     pg;
48962306a36Sopenharmony_ci	     pg = mlock_dereference(pg->next, br)) {
49062306a36Sopenharmony_ci		struct net_bridge_port_group *src_pg;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		if (pg == sg || pg->filter_mode == MCAST_INCLUDE)
49362306a36Sopenharmony_ci			continue;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		sg_key.port = pg->key.port;
49662306a36Sopenharmony_ci		if (br_sg_port_find(br, &sg_key))
49762306a36Sopenharmony_ci			continue;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		pmctx = br_multicast_pg_to_port_ctx(pg);
50062306a36Sopenharmony_ci		if (!pmctx)
50162306a36Sopenharmony_ci			continue;
50262306a36Sopenharmony_ci		brmctx = br_multicast_port_ctx_get_global(pmctx);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		src_pg = __br_multicast_add_group(brmctx, pmctx,
50562306a36Sopenharmony_ci						  &sg->key.addr,
50662306a36Sopenharmony_ci						  sg->eth_addr,
50762306a36Sopenharmony_ci						  MCAST_INCLUDE, false, false);
50862306a36Sopenharmony_ci		if (IS_ERR_OR_NULL(src_pg) ||
50962306a36Sopenharmony_ci		    src_pg->rt_protocol != RTPROT_KERNEL)
51062306a36Sopenharmony_ci			continue;
51162306a36Sopenharmony_ci		src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic void br_multicast_fwd_src_add(struct net_bridge_group_src *src)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct net_bridge_mdb_entry *star_mp;
51862306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx;
51962306a36Sopenharmony_ci	struct net_bridge_port_group *sg;
52062306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx;
52162306a36Sopenharmony_ci	struct br_ip sg_ip;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (src->flags & BR_SGRP_F_INSTALLED)
52462306a36Sopenharmony_ci		return;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	memset(&sg_ip, 0, sizeof(sg_ip));
52762306a36Sopenharmony_ci	pmctx = br_multicast_pg_to_port_ctx(src->pg);
52862306a36Sopenharmony_ci	if (!pmctx)
52962306a36Sopenharmony_ci		return;
53062306a36Sopenharmony_ci	brmctx = br_multicast_port_ctx_get_global(pmctx);
53162306a36Sopenharmony_ci	sg_ip = src->pg->key.addr;
53262306a36Sopenharmony_ci	sg_ip.src = src->addr.src;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	sg = __br_multicast_add_group(brmctx, pmctx, &sg_ip,
53562306a36Sopenharmony_ci				      src->pg->eth_addr, MCAST_INCLUDE, false,
53662306a36Sopenharmony_ci				      !timer_pending(&src->timer));
53762306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(sg))
53862306a36Sopenharmony_ci		return;
53962306a36Sopenharmony_ci	src->flags |= BR_SGRP_F_INSTALLED;
54062306a36Sopenharmony_ci	sg->flags &= ~MDB_PG_FLAGS_STAR_EXCL;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/* if it was added by user-space as perm we can skip next steps */
54362306a36Sopenharmony_ci	if (sg->rt_protocol != RTPROT_KERNEL &&
54462306a36Sopenharmony_ci	    (sg->flags & MDB_PG_FLAGS_PERMANENT))
54562306a36Sopenharmony_ci		return;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* the kernel is now responsible for removing this S,G */
54862306a36Sopenharmony_ci	del_timer(&sg->timer);
54962306a36Sopenharmony_ci	star_mp = br_mdb_ip_get(src->br, &src->pg->key.addr);
55062306a36Sopenharmony_ci	if (!star_mp)
55162306a36Sopenharmony_ci		return;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	br_multicast_sg_add_exclude_ports(star_mp, sg);
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic void br_multicast_fwd_src_remove(struct net_bridge_group_src *src,
55762306a36Sopenharmony_ci					bool fastleave)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	struct net_bridge_port_group *p, *pg = src->pg;
56062306a36Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
56162306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
56262306a36Sopenharmony_ci	struct br_ip sg_ip;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	memset(&sg_ip, 0, sizeof(sg_ip));
56562306a36Sopenharmony_ci	sg_ip = pg->key.addr;
56662306a36Sopenharmony_ci	sg_ip.src = src->addr.src;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	mp = br_mdb_ip_get(src->br, &sg_ip);
56962306a36Sopenharmony_ci	if (!mp)
57062306a36Sopenharmony_ci		return;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	for (pp = &mp->ports;
57362306a36Sopenharmony_ci	     (p = mlock_dereference(*pp, src->br)) != NULL;
57462306a36Sopenharmony_ci	     pp = &p->next) {
57562306a36Sopenharmony_ci		if (!br_port_group_equal(p, pg->key.port, pg->eth_addr))
57662306a36Sopenharmony_ci			continue;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		if (p->rt_protocol != RTPROT_KERNEL &&
57962306a36Sopenharmony_ci		    (p->flags & MDB_PG_FLAGS_PERMANENT) &&
58062306a36Sopenharmony_ci		    !(src->flags & BR_SGRP_F_USER_ADDED))
58162306a36Sopenharmony_ci			break;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		if (fastleave)
58462306a36Sopenharmony_ci			p->flags |= MDB_PG_FLAGS_FAST_LEAVE;
58562306a36Sopenharmony_ci		br_multicast_del_pg(mp, p, pp);
58662306a36Sopenharmony_ci		break;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci	src->flags &= ~BR_SGRP_F_INSTALLED;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci/* install S,G and based on src's timer enable or disable forwarding */
59262306a36Sopenharmony_cistatic void br_multicast_fwd_src_handle(struct net_bridge_group_src *src)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct net_bridge_port_group_sg_key sg_key;
59562306a36Sopenharmony_ci	struct net_bridge_port_group *sg;
59662306a36Sopenharmony_ci	u8 old_flags;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	br_multicast_fwd_src_add(src);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	memset(&sg_key, 0, sizeof(sg_key));
60162306a36Sopenharmony_ci	sg_key.addr = src->pg->key.addr;
60262306a36Sopenharmony_ci	sg_key.addr.src = src->addr.src;
60362306a36Sopenharmony_ci	sg_key.port = src->pg->key.port;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	sg = br_sg_port_find(src->br, &sg_key);
60662306a36Sopenharmony_ci	if (!sg || (sg->flags & MDB_PG_FLAGS_PERMANENT))
60762306a36Sopenharmony_ci		return;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	old_flags = sg->flags;
61062306a36Sopenharmony_ci	if (timer_pending(&src->timer))
61162306a36Sopenharmony_ci		sg->flags &= ~MDB_PG_FLAGS_BLOCKED;
61262306a36Sopenharmony_ci	else
61362306a36Sopenharmony_ci		sg->flags |= MDB_PG_FLAGS_BLOCKED;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (old_flags != sg->flags) {
61662306a36Sopenharmony_ci		struct net_bridge_mdb_entry *sg_mp;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		sg_mp = br_mdb_ip_get(src->br, &sg_key.addr);
61962306a36Sopenharmony_ci		if (!sg_mp)
62062306a36Sopenharmony_ci			return;
62162306a36Sopenharmony_ci		br_mdb_notify(src->br->dev, sg_mp, sg, RTM_NEWMDB);
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic void br_multicast_destroy_mdb_entry(struct net_bridge_mcast_gc *gc)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	mp = container_of(gc, struct net_bridge_mdb_entry, mcast_gc);
63062306a36Sopenharmony_ci	WARN_ON(!hlist_unhashed(&mp->mdb_node));
63162306a36Sopenharmony_ci	WARN_ON(mp->ports);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	timer_shutdown_sync(&mp->timer);
63462306a36Sopenharmony_ci	kfree_rcu(mp, rcu);
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic void br_multicast_del_mdb_entry(struct net_bridge_mdb_entry *mp)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct net_bridge *br = mp->br;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode,
64262306a36Sopenharmony_ci			       br_mdb_rht_params);
64362306a36Sopenharmony_ci	hlist_del_init_rcu(&mp->mdb_node);
64462306a36Sopenharmony_ci	hlist_add_head(&mp->mcast_gc.gc_node, &br->mcast_gc_list);
64562306a36Sopenharmony_ci	queue_work(system_long_wq, &br->mcast_gc_work);
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic void br_multicast_group_expired(struct timer_list *t)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp = from_timer(mp, t, timer);
65162306a36Sopenharmony_ci	struct net_bridge *br = mp->br;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	spin_lock(&br->multicast_lock);
65462306a36Sopenharmony_ci	if (hlist_unhashed(&mp->mdb_node) || !netif_running(br->dev) ||
65562306a36Sopenharmony_ci	    timer_pending(&mp->timer))
65662306a36Sopenharmony_ci		goto out;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	br_multicast_host_leave(mp, true);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (mp->ports)
66162306a36Sopenharmony_ci		goto out;
66262306a36Sopenharmony_ci	br_multicast_del_mdb_entry(mp);
66362306a36Sopenharmony_ciout:
66462306a36Sopenharmony_ci	spin_unlock(&br->multicast_lock);
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic void br_multicast_destroy_group_src(struct net_bridge_mcast_gc *gc)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct net_bridge_group_src *src;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	src = container_of(gc, struct net_bridge_group_src, mcast_gc);
67262306a36Sopenharmony_ci	WARN_ON(!hlist_unhashed(&src->node));
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	timer_shutdown_sync(&src->timer);
67562306a36Sopenharmony_ci	kfree_rcu(src, rcu);
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_civoid __br_multicast_del_group_src(struct net_bridge_group_src *src)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	struct net_bridge *br = src->pg->key.port->br;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	hlist_del_init_rcu(&src->node);
68362306a36Sopenharmony_ci	src->pg->src_ents--;
68462306a36Sopenharmony_ci	hlist_add_head(&src->mcast_gc.gc_node, &br->mcast_gc_list);
68562306a36Sopenharmony_ci	queue_work(system_long_wq, &br->mcast_gc_work);
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_civoid br_multicast_del_group_src(struct net_bridge_group_src *src,
68962306a36Sopenharmony_ci				bool fastleave)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	br_multicast_fwd_src_remove(src, fastleave);
69262306a36Sopenharmony_ci	__br_multicast_del_group_src(src);
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic int
69662306a36Sopenharmony_cibr_multicast_port_ngroups_inc_one(struct net_bridge_mcast_port *pmctx,
69762306a36Sopenharmony_ci				  struct netlink_ext_ack *extack,
69862306a36Sopenharmony_ci				  const char *what)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	u32 max = READ_ONCE(pmctx->mdb_max_entries);
70162306a36Sopenharmony_ci	u32 n = READ_ONCE(pmctx->mdb_n_entries);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (max && n >= max) {
70462306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT_MOD(extack, "%s is already in %u groups, and mcast_max_groups=%u",
70562306a36Sopenharmony_ci				       what, n, max);
70662306a36Sopenharmony_ci		return -E2BIG;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	WRITE_ONCE(pmctx->mdb_n_entries, n + 1);
71062306a36Sopenharmony_ci	return 0;
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic void br_multicast_port_ngroups_dec_one(struct net_bridge_mcast_port *pmctx)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	u32 n = READ_ONCE(pmctx->mdb_n_entries);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	WARN_ON_ONCE(n == 0);
71862306a36Sopenharmony_ci	WRITE_ONCE(pmctx->mdb_n_entries, n - 1);
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic int br_multicast_port_ngroups_inc(struct net_bridge_port *port,
72262306a36Sopenharmony_ci					 const struct br_ip *group,
72362306a36Sopenharmony_ci					 struct netlink_ext_ack *extack)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx;
72662306a36Sopenharmony_ci	int err;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	lockdep_assert_held_once(&port->br->multicast_lock);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* Always count on the port context. */
73162306a36Sopenharmony_ci	err = br_multicast_port_ngroups_inc_one(&port->multicast_ctx, extack,
73262306a36Sopenharmony_ci						"Port");
73362306a36Sopenharmony_ci	if (err) {
73462306a36Sopenharmony_ci		trace_br_mdb_full(port->dev, group);
73562306a36Sopenharmony_ci		return err;
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	/* Only count on the VLAN context if VID is given, and if snooping on
73962306a36Sopenharmony_ci	 * that VLAN is enabled.
74062306a36Sopenharmony_ci	 */
74162306a36Sopenharmony_ci	if (!group->vid)
74262306a36Sopenharmony_ci		return 0;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	pmctx = br_multicast_port_vid_to_port_ctx(port, group->vid);
74562306a36Sopenharmony_ci	if (!pmctx)
74662306a36Sopenharmony_ci		return 0;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	err = br_multicast_port_ngroups_inc_one(pmctx, extack, "Port-VLAN");
74962306a36Sopenharmony_ci	if (err) {
75062306a36Sopenharmony_ci		trace_br_mdb_full(port->dev, group);
75162306a36Sopenharmony_ci		goto dec_one_out;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cidec_one_out:
75762306a36Sopenharmony_ci	br_multicast_port_ngroups_dec_one(&port->multicast_ctx);
75862306a36Sopenharmony_ci	return err;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic void br_multicast_port_ngroups_dec(struct net_bridge_port *port, u16 vid)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	lockdep_assert_held_once(&port->br->multicast_lock);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (vid) {
76862306a36Sopenharmony_ci		pmctx = br_multicast_port_vid_to_port_ctx(port, vid);
76962306a36Sopenharmony_ci		if (pmctx)
77062306a36Sopenharmony_ci			br_multicast_port_ngroups_dec_one(pmctx);
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci	br_multicast_port_ngroups_dec_one(&port->multicast_ctx);
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ciu32 br_multicast_ngroups_get(const struct net_bridge_mcast_port *pmctx)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	return READ_ONCE(pmctx->mdb_n_entries);
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_civoid br_multicast_ngroups_set_max(struct net_bridge_mcast_port *pmctx, u32 max)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	WRITE_ONCE(pmctx->mdb_max_entries, max);
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ciu32 br_multicast_ngroups_get_max(const struct net_bridge_mcast_port *pmctx)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	return READ_ONCE(pmctx->mdb_max_entries);
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic void br_multicast_destroy_port_group(struct net_bridge_mcast_gc *gc)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	pg = container_of(gc, struct net_bridge_port_group, mcast_gc);
79562306a36Sopenharmony_ci	WARN_ON(!hlist_unhashed(&pg->mglist));
79662306a36Sopenharmony_ci	WARN_ON(!hlist_empty(&pg->src_list));
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	timer_shutdown_sync(&pg->rexmit_timer);
79962306a36Sopenharmony_ci	timer_shutdown_sync(&pg->timer);
80062306a36Sopenharmony_ci	kfree_rcu(pg, rcu);
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_civoid br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
80462306a36Sopenharmony_ci			 struct net_bridge_port_group *pg,
80562306a36Sopenharmony_ci			 struct net_bridge_port_group __rcu **pp)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
80862306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
80962306a36Sopenharmony_ci	struct hlist_node *tmp;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	rcu_assign_pointer(*pp, pg->next);
81262306a36Sopenharmony_ci	hlist_del_init(&pg->mglist);
81362306a36Sopenharmony_ci	br_multicast_eht_clean_sets(pg);
81462306a36Sopenharmony_ci	hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
81562306a36Sopenharmony_ci		br_multicast_del_group_src(ent, false);
81662306a36Sopenharmony_ci	br_mdb_notify(br->dev, mp, pg, RTM_DELMDB);
81762306a36Sopenharmony_ci	if (!br_multicast_is_star_g(&mp->addr)) {
81862306a36Sopenharmony_ci		rhashtable_remove_fast(&br->sg_port_tbl, &pg->rhnode,
81962306a36Sopenharmony_ci				       br_sg_port_rht_params);
82062306a36Sopenharmony_ci		br_multicast_sg_del_exclude_ports(mp);
82162306a36Sopenharmony_ci	} else {
82262306a36Sopenharmony_ci		br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE);
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci	br_multicast_port_ngroups_dec(pg->key.port, pg->key.addr.vid);
82562306a36Sopenharmony_ci	hlist_add_head(&pg->mcast_gc.gc_node, &br->mcast_gc_list);
82662306a36Sopenharmony_ci	queue_work(system_long_wq, &br->mcast_gc_work);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (!mp->ports && !mp->host_joined && netif_running(br->dev))
82962306a36Sopenharmony_ci		mod_timer(&mp->timer, jiffies);
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic void br_multicast_find_del_pg(struct net_bridge *br,
83362306a36Sopenharmony_ci				     struct net_bridge_port_group *pg)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
83662306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
83762306a36Sopenharmony_ci	struct net_bridge_port_group *p;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	mp = br_mdb_ip_get(br, &pg->key.addr);
84062306a36Sopenharmony_ci	if (WARN_ON(!mp))
84162306a36Sopenharmony_ci		return;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	for (pp = &mp->ports;
84462306a36Sopenharmony_ci	     (p = mlock_dereference(*pp, br)) != NULL;
84562306a36Sopenharmony_ci	     pp = &p->next) {
84662306a36Sopenharmony_ci		if (p != pg)
84762306a36Sopenharmony_ci			continue;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		br_multicast_del_pg(mp, pg, pp);
85062306a36Sopenharmony_ci		return;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	WARN_ON(1);
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic void br_multicast_port_group_expired(struct timer_list *t)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct net_bridge_port_group *pg = from_timer(pg, t, timer);
85962306a36Sopenharmony_ci	struct net_bridge_group_src *src_ent;
86062306a36Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
86162306a36Sopenharmony_ci	struct hlist_node *tmp;
86262306a36Sopenharmony_ci	bool changed;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	spin_lock(&br->multicast_lock);
86562306a36Sopenharmony_ci	if (!netif_running(br->dev) || timer_pending(&pg->timer) ||
86662306a36Sopenharmony_ci	    hlist_unhashed(&pg->mglist) || pg->flags & MDB_PG_FLAGS_PERMANENT)
86762306a36Sopenharmony_ci		goto out;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	changed = !!(pg->filter_mode == MCAST_EXCLUDE);
87062306a36Sopenharmony_ci	pg->filter_mode = MCAST_INCLUDE;
87162306a36Sopenharmony_ci	hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) {
87262306a36Sopenharmony_ci		if (!timer_pending(&src_ent->timer)) {
87362306a36Sopenharmony_ci			br_multicast_del_group_src(src_ent, false);
87462306a36Sopenharmony_ci			changed = true;
87562306a36Sopenharmony_ci		}
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (hlist_empty(&pg->src_list)) {
87962306a36Sopenharmony_ci		br_multicast_find_del_pg(br, pg);
88062306a36Sopenharmony_ci	} else if (changed) {
88162306a36Sopenharmony_ci		struct net_bridge_mdb_entry *mp = br_mdb_ip_get(br, &pg->key.addr);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci		if (changed && br_multicast_is_star_g(&pg->key.addr))
88462306a36Sopenharmony_ci			br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci		if (WARN_ON(!mp))
88762306a36Sopenharmony_ci			goto out;
88862306a36Sopenharmony_ci		br_mdb_notify(br->dev, mp, pg, RTM_NEWMDB);
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ciout:
89162306a36Sopenharmony_ci	spin_unlock(&br->multicast_lock);
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic void br_multicast_gc(struct hlist_head *head)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	struct net_bridge_mcast_gc *gcent;
89762306a36Sopenharmony_ci	struct hlist_node *tmp;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	hlist_for_each_entry_safe(gcent, tmp, head, gc_node) {
90062306a36Sopenharmony_ci		hlist_del_init(&gcent->gc_node);
90162306a36Sopenharmony_ci		gcent->destroy(gcent);
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic void __br_multicast_query_handle_vlan(struct net_bridge_mcast *brmctx,
90662306a36Sopenharmony_ci					     struct net_bridge_mcast_port *pmctx,
90762306a36Sopenharmony_ci					     struct sk_buff *skb)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	struct net_bridge_vlan *vlan = NULL;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	if (pmctx && br_multicast_port_ctx_is_vlan(pmctx))
91262306a36Sopenharmony_ci		vlan = pmctx->vlan;
91362306a36Sopenharmony_ci	else if (br_multicast_ctx_is_vlan(brmctx))
91462306a36Sopenharmony_ci		vlan = brmctx->vlan;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if (vlan && !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)) {
91762306a36Sopenharmony_ci		u16 vlan_proto;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci		if (br_vlan_get_proto(brmctx->br->dev, &vlan_proto) != 0)
92062306a36Sopenharmony_ci			return;
92162306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(vlan_proto), vlan->vid);
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brmctx,
92662306a36Sopenharmony_ci						    struct net_bridge_mcast_port *pmctx,
92762306a36Sopenharmony_ci						    struct net_bridge_port_group *pg,
92862306a36Sopenharmony_ci						    __be32 ip_dst, __be32 group,
92962306a36Sopenharmony_ci						    bool with_srcs, bool over_lmqt,
93062306a36Sopenharmony_ci						    u8 sflag, u8 *igmp_type,
93162306a36Sopenharmony_ci						    bool *need_rexmit)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct net_bridge_port *p = pg ? pg->key.port : NULL;
93462306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
93562306a36Sopenharmony_ci	size_t pkt_size, igmp_hdr_size;
93662306a36Sopenharmony_ci	unsigned long now = jiffies;
93762306a36Sopenharmony_ci	struct igmpv3_query *ihv3;
93862306a36Sopenharmony_ci	void *csum_start = NULL;
93962306a36Sopenharmony_ci	__sum16 *csum = NULL;
94062306a36Sopenharmony_ci	struct sk_buff *skb;
94162306a36Sopenharmony_ci	struct igmphdr *ih;
94262306a36Sopenharmony_ci	struct ethhdr *eth;
94362306a36Sopenharmony_ci	unsigned long lmqt;
94462306a36Sopenharmony_ci	struct iphdr *iph;
94562306a36Sopenharmony_ci	u16 lmqt_srcs = 0;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	igmp_hdr_size = sizeof(*ih);
94862306a36Sopenharmony_ci	if (brmctx->multicast_igmp_version == 3) {
94962306a36Sopenharmony_ci		igmp_hdr_size = sizeof(*ihv3);
95062306a36Sopenharmony_ci		if (pg && with_srcs) {
95162306a36Sopenharmony_ci			lmqt = now + (brmctx->multicast_last_member_interval *
95262306a36Sopenharmony_ci				      brmctx->multicast_last_member_count);
95362306a36Sopenharmony_ci			hlist_for_each_entry(ent, &pg->src_list, node) {
95462306a36Sopenharmony_ci				if (over_lmqt == time_after(ent->timer.expires,
95562306a36Sopenharmony_ci							    lmqt) &&
95662306a36Sopenharmony_ci				    ent->src_query_rexmit_cnt > 0)
95762306a36Sopenharmony_ci					lmqt_srcs++;
95862306a36Sopenharmony_ci			}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci			if (!lmqt_srcs)
96162306a36Sopenharmony_ci				return NULL;
96262306a36Sopenharmony_ci			igmp_hdr_size += lmqt_srcs * sizeof(__be32);
96362306a36Sopenharmony_ci		}
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	pkt_size = sizeof(*eth) + sizeof(*iph) + 4 + igmp_hdr_size;
96762306a36Sopenharmony_ci	if ((p && pkt_size > p->dev->mtu) ||
96862306a36Sopenharmony_ci	    pkt_size > brmctx->br->dev->mtu)
96962306a36Sopenharmony_ci		return NULL;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(brmctx->br->dev, pkt_size);
97262306a36Sopenharmony_ci	if (!skb)
97362306a36Sopenharmony_ci		goto out;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	__br_multicast_query_handle_vlan(brmctx, pmctx, skb);
97662306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	skb_reset_mac_header(skb);
97962306a36Sopenharmony_ci	eth = eth_hdr(skb);
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	ether_addr_copy(eth->h_source, brmctx->br->dev->dev_addr);
98262306a36Sopenharmony_ci	ip_eth_mc_map(ip_dst, eth->h_dest);
98362306a36Sopenharmony_ci	eth->h_proto = htons(ETH_P_IP);
98462306a36Sopenharmony_ci	skb_put(skb, sizeof(*eth));
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	skb_set_network_header(skb, skb->len);
98762306a36Sopenharmony_ci	iph = ip_hdr(skb);
98862306a36Sopenharmony_ci	iph->tot_len = htons(pkt_size - sizeof(*eth));
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	iph->version = 4;
99162306a36Sopenharmony_ci	iph->ihl = 6;
99262306a36Sopenharmony_ci	iph->tos = 0xc0;
99362306a36Sopenharmony_ci	iph->id = 0;
99462306a36Sopenharmony_ci	iph->frag_off = htons(IP_DF);
99562306a36Sopenharmony_ci	iph->ttl = 1;
99662306a36Sopenharmony_ci	iph->protocol = IPPROTO_IGMP;
99762306a36Sopenharmony_ci	iph->saddr = br_opt_get(brmctx->br, BROPT_MULTICAST_QUERY_USE_IFADDR) ?
99862306a36Sopenharmony_ci		     inet_select_addr(brmctx->br->dev, 0, RT_SCOPE_LINK) : 0;
99962306a36Sopenharmony_ci	iph->daddr = ip_dst;
100062306a36Sopenharmony_ci	((u8 *)&iph[1])[0] = IPOPT_RA;
100162306a36Sopenharmony_ci	((u8 *)&iph[1])[1] = 4;
100262306a36Sopenharmony_ci	((u8 *)&iph[1])[2] = 0;
100362306a36Sopenharmony_ci	((u8 *)&iph[1])[3] = 0;
100462306a36Sopenharmony_ci	ip_send_check(iph);
100562306a36Sopenharmony_ci	skb_put(skb, 24);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	skb_set_transport_header(skb, skb->len);
100862306a36Sopenharmony_ci	*igmp_type = IGMP_HOST_MEMBERSHIP_QUERY;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	switch (brmctx->multicast_igmp_version) {
101162306a36Sopenharmony_ci	case 2:
101262306a36Sopenharmony_ci		ih = igmp_hdr(skb);
101362306a36Sopenharmony_ci		ih->type = IGMP_HOST_MEMBERSHIP_QUERY;
101462306a36Sopenharmony_ci		ih->code = (group ? brmctx->multicast_last_member_interval :
101562306a36Sopenharmony_ci				    brmctx->multicast_query_response_interval) /
101662306a36Sopenharmony_ci			   (HZ / IGMP_TIMER_SCALE);
101762306a36Sopenharmony_ci		ih->group = group;
101862306a36Sopenharmony_ci		ih->csum = 0;
101962306a36Sopenharmony_ci		csum = &ih->csum;
102062306a36Sopenharmony_ci		csum_start = (void *)ih;
102162306a36Sopenharmony_ci		break;
102262306a36Sopenharmony_ci	case 3:
102362306a36Sopenharmony_ci		ihv3 = igmpv3_query_hdr(skb);
102462306a36Sopenharmony_ci		ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY;
102562306a36Sopenharmony_ci		ihv3->code = (group ? brmctx->multicast_last_member_interval :
102662306a36Sopenharmony_ci				      brmctx->multicast_query_response_interval) /
102762306a36Sopenharmony_ci			     (HZ / IGMP_TIMER_SCALE);
102862306a36Sopenharmony_ci		ihv3->group = group;
102962306a36Sopenharmony_ci		ihv3->qqic = brmctx->multicast_query_interval / HZ;
103062306a36Sopenharmony_ci		ihv3->nsrcs = htons(lmqt_srcs);
103162306a36Sopenharmony_ci		ihv3->resv = 0;
103262306a36Sopenharmony_ci		ihv3->suppress = sflag;
103362306a36Sopenharmony_ci		ihv3->qrv = 2;
103462306a36Sopenharmony_ci		ihv3->csum = 0;
103562306a36Sopenharmony_ci		csum = &ihv3->csum;
103662306a36Sopenharmony_ci		csum_start = (void *)ihv3;
103762306a36Sopenharmony_ci		if (!pg || !with_srcs)
103862306a36Sopenharmony_ci			break;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		lmqt_srcs = 0;
104162306a36Sopenharmony_ci		hlist_for_each_entry(ent, &pg->src_list, node) {
104262306a36Sopenharmony_ci			if (over_lmqt == time_after(ent->timer.expires,
104362306a36Sopenharmony_ci						    lmqt) &&
104462306a36Sopenharmony_ci			    ent->src_query_rexmit_cnt > 0) {
104562306a36Sopenharmony_ci				ihv3->srcs[lmqt_srcs++] = ent->addr.src.ip4;
104662306a36Sopenharmony_ci				ent->src_query_rexmit_cnt--;
104762306a36Sopenharmony_ci				if (need_rexmit && ent->src_query_rexmit_cnt)
104862306a36Sopenharmony_ci					*need_rexmit = true;
104962306a36Sopenharmony_ci			}
105062306a36Sopenharmony_ci		}
105162306a36Sopenharmony_ci		if (WARN_ON(lmqt_srcs != ntohs(ihv3->nsrcs))) {
105262306a36Sopenharmony_ci			kfree_skb(skb);
105362306a36Sopenharmony_ci			return NULL;
105462306a36Sopenharmony_ci		}
105562306a36Sopenharmony_ci		break;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (WARN_ON(!csum || !csum_start)) {
105962306a36Sopenharmony_ci		kfree_skb(skb);
106062306a36Sopenharmony_ci		return NULL;
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	*csum = ip_compute_csum(csum_start, igmp_hdr_size);
106462306a36Sopenharmony_ci	skb_put(skb, igmp_hdr_size);
106562306a36Sopenharmony_ci	__skb_pull(skb, sizeof(*eth));
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ciout:
106862306a36Sopenharmony_ci	return skb;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
107262306a36Sopenharmony_cistatic struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brmctx,
107362306a36Sopenharmony_ci						    struct net_bridge_mcast_port *pmctx,
107462306a36Sopenharmony_ci						    struct net_bridge_port_group *pg,
107562306a36Sopenharmony_ci						    const struct in6_addr *ip6_dst,
107662306a36Sopenharmony_ci						    const struct in6_addr *group,
107762306a36Sopenharmony_ci						    bool with_srcs, bool over_llqt,
107862306a36Sopenharmony_ci						    u8 sflag, u8 *igmp_type,
107962306a36Sopenharmony_ci						    bool *need_rexmit)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	struct net_bridge_port *p = pg ? pg->key.port : NULL;
108262306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
108362306a36Sopenharmony_ci	size_t pkt_size, mld_hdr_size;
108462306a36Sopenharmony_ci	unsigned long now = jiffies;
108562306a36Sopenharmony_ci	struct mld2_query *mld2q;
108662306a36Sopenharmony_ci	void *csum_start = NULL;
108762306a36Sopenharmony_ci	unsigned long interval;
108862306a36Sopenharmony_ci	__sum16 *csum = NULL;
108962306a36Sopenharmony_ci	struct ipv6hdr *ip6h;
109062306a36Sopenharmony_ci	struct mld_msg *mldq;
109162306a36Sopenharmony_ci	struct sk_buff *skb;
109262306a36Sopenharmony_ci	unsigned long llqt;
109362306a36Sopenharmony_ci	struct ethhdr *eth;
109462306a36Sopenharmony_ci	u16 llqt_srcs = 0;
109562306a36Sopenharmony_ci	u8 *hopopt;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	mld_hdr_size = sizeof(*mldq);
109862306a36Sopenharmony_ci	if (brmctx->multicast_mld_version == 2) {
109962306a36Sopenharmony_ci		mld_hdr_size = sizeof(*mld2q);
110062306a36Sopenharmony_ci		if (pg && with_srcs) {
110162306a36Sopenharmony_ci			llqt = now + (brmctx->multicast_last_member_interval *
110262306a36Sopenharmony_ci				      brmctx->multicast_last_member_count);
110362306a36Sopenharmony_ci			hlist_for_each_entry(ent, &pg->src_list, node) {
110462306a36Sopenharmony_ci				if (over_llqt == time_after(ent->timer.expires,
110562306a36Sopenharmony_ci							    llqt) &&
110662306a36Sopenharmony_ci				    ent->src_query_rexmit_cnt > 0)
110762306a36Sopenharmony_ci					llqt_srcs++;
110862306a36Sopenharmony_ci			}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci			if (!llqt_srcs)
111162306a36Sopenharmony_ci				return NULL;
111262306a36Sopenharmony_ci			mld_hdr_size += llqt_srcs * sizeof(struct in6_addr);
111362306a36Sopenharmony_ci		}
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	pkt_size = sizeof(*eth) + sizeof(*ip6h) + 8 + mld_hdr_size;
111762306a36Sopenharmony_ci	if ((p && pkt_size > p->dev->mtu) ||
111862306a36Sopenharmony_ci	    pkt_size > brmctx->br->dev->mtu)
111962306a36Sopenharmony_ci		return NULL;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(brmctx->br->dev, pkt_size);
112262306a36Sopenharmony_ci	if (!skb)
112362306a36Sopenharmony_ci		goto out;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	__br_multicast_query_handle_vlan(brmctx, pmctx, skb);
112662306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_IPV6);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	/* Ethernet header */
112962306a36Sopenharmony_ci	skb_reset_mac_header(skb);
113062306a36Sopenharmony_ci	eth = eth_hdr(skb);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	ether_addr_copy(eth->h_source, brmctx->br->dev->dev_addr);
113362306a36Sopenharmony_ci	eth->h_proto = htons(ETH_P_IPV6);
113462306a36Sopenharmony_ci	skb_put(skb, sizeof(*eth));
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	/* IPv6 header + HbH option */
113762306a36Sopenharmony_ci	skb_set_network_header(skb, skb->len);
113862306a36Sopenharmony_ci	ip6h = ipv6_hdr(skb);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	*(__force __be32 *)ip6h = htonl(0x60000000);
114162306a36Sopenharmony_ci	ip6h->payload_len = htons(8 + mld_hdr_size);
114262306a36Sopenharmony_ci	ip6h->nexthdr = IPPROTO_HOPOPTS;
114362306a36Sopenharmony_ci	ip6h->hop_limit = 1;
114462306a36Sopenharmony_ci	ip6h->daddr = *ip6_dst;
114562306a36Sopenharmony_ci	if (ipv6_dev_get_saddr(dev_net(brmctx->br->dev), brmctx->br->dev,
114662306a36Sopenharmony_ci			       &ip6h->daddr, 0, &ip6h->saddr)) {
114762306a36Sopenharmony_ci		kfree_skb(skb);
114862306a36Sopenharmony_ci		br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, false);
114962306a36Sopenharmony_ci		return NULL;
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, true);
115362306a36Sopenharmony_ci	ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	hopopt = (u8 *)(ip6h + 1);
115662306a36Sopenharmony_ci	hopopt[0] = IPPROTO_ICMPV6;		/* next hdr */
115762306a36Sopenharmony_ci	hopopt[1] = 0;				/* length of HbH */
115862306a36Sopenharmony_ci	hopopt[2] = IPV6_TLV_ROUTERALERT;	/* Router Alert */
115962306a36Sopenharmony_ci	hopopt[3] = 2;				/* Length of RA Option */
116062306a36Sopenharmony_ci	hopopt[4] = 0;				/* Type = 0x0000 (MLD) */
116162306a36Sopenharmony_ci	hopopt[5] = 0;
116262306a36Sopenharmony_ci	hopopt[6] = IPV6_TLV_PAD1;		/* Pad1 */
116362306a36Sopenharmony_ci	hopopt[7] = IPV6_TLV_PAD1;		/* Pad1 */
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	skb_put(skb, sizeof(*ip6h) + 8);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	/* ICMPv6 */
116862306a36Sopenharmony_ci	skb_set_transport_header(skb, skb->len);
116962306a36Sopenharmony_ci	interval = ipv6_addr_any(group) ?
117062306a36Sopenharmony_ci			brmctx->multicast_query_response_interval :
117162306a36Sopenharmony_ci			brmctx->multicast_last_member_interval;
117262306a36Sopenharmony_ci	*igmp_type = ICMPV6_MGM_QUERY;
117362306a36Sopenharmony_ci	switch (brmctx->multicast_mld_version) {
117462306a36Sopenharmony_ci	case 1:
117562306a36Sopenharmony_ci		mldq = (struct mld_msg *)icmp6_hdr(skb);
117662306a36Sopenharmony_ci		mldq->mld_type = ICMPV6_MGM_QUERY;
117762306a36Sopenharmony_ci		mldq->mld_code = 0;
117862306a36Sopenharmony_ci		mldq->mld_cksum = 0;
117962306a36Sopenharmony_ci		mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval));
118062306a36Sopenharmony_ci		mldq->mld_reserved = 0;
118162306a36Sopenharmony_ci		mldq->mld_mca = *group;
118262306a36Sopenharmony_ci		csum = &mldq->mld_cksum;
118362306a36Sopenharmony_ci		csum_start = (void *)mldq;
118462306a36Sopenharmony_ci		break;
118562306a36Sopenharmony_ci	case 2:
118662306a36Sopenharmony_ci		mld2q = (struct mld2_query *)icmp6_hdr(skb);
118762306a36Sopenharmony_ci		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval));
118862306a36Sopenharmony_ci		mld2q->mld2q_type = ICMPV6_MGM_QUERY;
118962306a36Sopenharmony_ci		mld2q->mld2q_code = 0;
119062306a36Sopenharmony_ci		mld2q->mld2q_cksum = 0;
119162306a36Sopenharmony_ci		mld2q->mld2q_resv1 = 0;
119262306a36Sopenharmony_ci		mld2q->mld2q_resv2 = 0;
119362306a36Sopenharmony_ci		mld2q->mld2q_suppress = sflag;
119462306a36Sopenharmony_ci		mld2q->mld2q_qrv = 2;
119562306a36Sopenharmony_ci		mld2q->mld2q_nsrcs = htons(llqt_srcs);
119662306a36Sopenharmony_ci		mld2q->mld2q_qqic = brmctx->multicast_query_interval / HZ;
119762306a36Sopenharmony_ci		mld2q->mld2q_mca = *group;
119862306a36Sopenharmony_ci		csum = &mld2q->mld2q_cksum;
119962306a36Sopenharmony_ci		csum_start = (void *)mld2q;
120062306a36Sopenharmony_ci		if (!pg || !with_srcs)
120162306a36Sopenharmony_ci			break;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci		llqt_srcs = 0;
120462306a36Sopenharmony_ci		hlist_for_each_entry(ent, &pg->src_list, node) {
120562306a36Sopenharmony_ci			if (over_llqt == time_after(ent->timer.expires,
120662306a36Sopenharmony_ci						    llqt) &&
120762306a36Sopenharmony_ci			    ent->src_query_rexmit_cnt > 0) {
120862306a36Sopenharmony_ci				mld2q->mld2q_srcs[llqt_srcs++] = ent->addr.src.ip6;
120962306a36Sopenharmony_ci				ent->src_query_rexmit_cnt--;
121062306a36Sopenharmony_ci				if (need_rexmit && ent->src_query_rexmit_cnt)
121162306a36Sopenharmony_ci					*need_rexmit = true;
121262306a36Sopenharmony_ci			}
121362306a36Sopenharmony_ci		}
121462306a36Sopenharmony_ci		if (WARN_ON(llqt_srcs != ntohs(mld2q->mld2q_nsrcs))) {
121562306a36Sopenharmony_ci			kfree_skb(skb);
121662306a36Sopenharmony_ci			return NULL;
121762306a36Sopenharmony_ci		}
121862306a36Sopenharmony_ci		break;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	if (WARN_ON(!csum || !csum_start)) {
122262306a36Sopenharmony_ci		kfree_skb(skb);
122362306a36Sopenharmony_ci		return NULL;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	*csum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, mld_hdr_size,
122762306a36Sopenharmony_ci				IPPROTO_ICMPV6,
122862306a36Sopenharmony_ci				csum_partial(csum_start, mld_hdr_size, 0));
122962306a36Sopenharmony_ci	skb_put(skb, mld_hdr_size);
123062306a36Sopenharmony_ci	__skb_pull(skb, sizeof(*eth));
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ciout:
123362306a36Sopenharmony_ci	return skb;
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci#endif
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic struct sk_buff *br_multicast_alloc_query(struct net_bridge_mcast *brmctx,
123862306a36Sopenharmony_ci						struct net_bridge_mcast_port *pmctx,
123962306a36Sopenharmony_ci						struct net_bridge_port_group *pg,
124062306a36Sopenharmony_ci						struct br_ip *ip_dst,
124162306a36Sopenharmony_ci						struct br_ip *group,
124262306a36Sopenharmony_ci						bool with_srcs, bool over_lmqt,
124362306a36Sopenharmony_ci						u8 sflag, u8 *igmp_type,
124462306a36Sopenharmony_ci						bool *need_rexmit)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	__be32 ip4_dst;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	switch (group->proto) {
124962306a36Sopenharmony_ci	case htons(ETH_P_IP):
125062306a36Sopenharmony_ci		ip4_dst = ip_dst ? ip_dst->dst.ip4 : htonl(INADDR_ALLHOSTS_GROUP);
125162306a36Sopenharmony_ci		return br_ip4_multicast_alloc_query(brmctx, pmctx, pg,
125262306a36Sopenharmony_ci						    ip4_dst, group->dst.ip4,
125362306a36Sopenharmony_ci						    with_srcs, over_lmqt,
125462306a36Sopenharmony_ci						    sflag, igmp_type,
125562306a36Sopenharmony_ci						    need_rexmit);
125662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
125762306a36Sopenharmony_ci	case htons(ETH_P_IPV6): {
125862306a36Sopenharmony_ci		struct in6_addr ip6_dst;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci		if (ip_dst)
126162306a36Sopenharmony_ci			ip6_dst = ip_dst->dst.ip6;
126262306a36Sopenharmony_ci		else
126362306a36Sopenharmony_ci			ipv6_addr_set(&ip6_dst, htonl(0xff020000), 0, 0,
126462306a36Sopenharmony_ci				      htonl(1));
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci		return br_ip6_multicast_alloc_query(brmctx, pmctx, pg,
126762306a36Sopenharmony_ci						    &ip6_dst, &group->dst.ip6,
126862306a36Sopenharmony_ci						    with_srcs, over_lmqt,
126962306a36Sopenharmony_ci						    sflag, igmp_type,
127062306a36Sopenharmony_ci						    need_rexmit);
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci#endif
127362306a36Sopenharmony_ci	}
127462306a36Sopenharmony_ci	return NULL;
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistruct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
127862306a36Sopenharmony_ci						    struct br_ip *group)
127962306a36Sopenharmony_ci{
128062306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
128162306a36Sopenharmony_ci	int err;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	mp = br_mdb_ip_get(br, group);
128462306a36Sopenharmony_ci	if (mp)
128562306a36Sopenharmony_ci		return mp;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) {
128862306a36Sopenharmony_ci		trace_br_mdb_full(br->dev, group);
128962306a36Sopenharmony_ci		br_mc_disabled_update(br->dev, false, NULL);
129062306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
129162306a36Sopenharmony_ci		return ERR_PTR(-E2BIG);
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	mp = kzalloc(sizeof(*mp), GFP_ATOMIC);
129562306a36Sopenharmony_ci	if (unlikely(!mp))
129662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	mp->br = br;
129962306a36Sopenharmony_ci	mp->addr = *group;
130062306a36Sopenharmony_ci	mp->mcast_gc.destroy = br_multicast_destroy_mdb_entry;
130162306a36Sopenharmony_ci	timer_setup(&mp->timer, br_multicast_group_expired, 0);
130262306a36Sopenharmony_ci	err = rhashtable_lookup_insert_fast(&br->mdb_hash_tbl, &mp->rhnode,
130362306a36Sopenharmony_ci					    br_mdb_rht_params);
130462306a36Sopenharmony_ci	if (err) {
130562306a36Sopenharmony_ci		kfree(mp);
130662306a36Sopenharmony_ci		mp = ERR_PTR(err);
130762306a36Sopenharmony_ci	} else {
130862306a36Sopenharmony_ci		hlist_add_head_rcu(&mp->mdb_node, &br->mdb_list);
130962306a36Sopenharmony_ci	}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	return mp;
131262306a36Sopenharmony_ci}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cistatic void br_multicast_group_src_expired(struct timer_list *t)
131562306a36Sopenharmony_ci{
131662306a36Sopenharmony_ci	struct net_bridge_group_src *src = from_timer(src, t, timer);
131762306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
131862306a36Sopenharmony_ci	struct net_bridge *br = src->br;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	spin_lock(&br->multicast_lock);
132162306a36Sopenharmony_ci	if (hlist_unhashed(&src->node) || !netif_running(br->dev) ||
132262306a36Sopenharmony_ci	    timer_pending(&src->timer))
132362306a36Sopenharmony_ci		goto out;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	pg = src->pg;
132662306a36Sopenharmony_ci	if (pg->filter_mode == MCAST_INCLUDE) {
132762306a36Sopenharmony_ci		br_multicast_del_group_src(src, false);
132862306a36Sopenharmony_ci		if (!hlist_empty(&pg->src_list))
132962306a36Sopenharmony_ci			goto out;
133062306a36Sopenharmony_ci		br_multicast_find_del_pg(br, pg);
133162306a36Sopenharmony_ci	} else {
133262306a36Sopenharmony_ci		br_multicast_fwd_src_handle(src);
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ciout:
133662306a36Sopenharmony_ci	spin_unlock(&br->multicast_lock);
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_cistruct net_bridge_group_src *
134062306a36Sopenharmony_cibr_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	switch (ip->proto) {
134562306a36Sopenharmony_ci	case htons(ETH_P_IP):
134662306a36Sopenharmony_ci		hlist_for_each_entry(ent, &pg->src_list, node)
134762306a36Sopenharmony_ci			if (ip->src.ip4 == ent->addr.src.ip4)
134862306a36Sopenharmony_ci				return ent;
134962306a36Sopenharmony_ci		break;
135062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
135162306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
135262306a36Sopenharmony_ci		hlist_for_each_entry(ent, &pg->src_list, node)
135362306a36Sopenharmony_ci			if (!ipv6_addr_cmp(&ent->addr.src.ip6, &ip->src.ip6))
135462306a36Sopenharmony_ci				return ent;
135562306a36Sopenharmony_ci		break;
135662306a36Sopenharmony_ci#endif
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	return NULL;
136062306a36Sopenharmony_ci}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_cistruct net_bridge_group_src *
136362306a36Sopenharmony_cibr_multicast_new_group_src(struct net_bridge_port_group *pg, struct br_ip *src_ip)
136462306a36Sopenharmony_ci{
136562306a36Sopenharmony_ci	struct net_bridge_group_src *grp_src;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	if (unlikely(pg->src_ents >= PG_SRC_ENT_LIMIT))
136862306a36Sopenharmony_ci		return NULL;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	switch (src_ip->proto) {
137162306a36Sopenharmony_ci	case htons(ETH_P_IP):
137262306a36Sopenharmony_ci		if (ipv4_is_zeronet(src_ip->src.ip4) ||
137362306a36Sopenharmony_ci		    ipv4_is_multicast(src_ip->src.ip4))
137462306a36Sopenharmony_ci			return NULL;
137562306a36Sopenharmony_ci		break;
137662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
137762306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
137862306a36Sopenharmony_ci		if (ipv6_addr_any(&src_ip->src.ip6) ||
137962306a36Sopenharmony_ci		    ipv6_addr_is_multicast(&src_ip->src.ip6))
138062306a36Sopenharmony_ci			return NULL;
138162306a36Sopenharmony_ci		break;
138262306a36Sopenharmony_ci#endif
138362306a36Sopenharmony_ci	}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	grp_src = kzalloc(sizeof(*grp_src), GFP_ATOMIC);
138662306a36Sopenharmony_ci	if (unlikely(!grp_src))
138762306a36Sopenharmony_ci		return NULL;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	grp_src->pg = pg;
139062306a36Sopenharmony_ci	grp_src->br = pg->key.port->br;
139162306a36Sopenharmony_ci	grp_src->addr = *src_ip;
139262306a36Sopenharmony_ci	grp_src->mcast_gc.destroy = br_multicast_destroy_group_src;
139362306a36Sopenharmony_ci	timer_setup(&grp_src->timer, br_multicast_group_src_expired, 0);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	hlist_add_head_rcu(&grp_src->node, &pg->src_list);
139662306a36Sopenharmony_ci	pg->src_ents++;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	return grp_src;
139962306a36Sopenharmony_ci}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistruct net_bridge_port_group *br_multicast_new_port_group(
140262306a36Sopenharmony_ci			struct net_bridge_port *port,
140362306a36Sopenharmony_ci			const struct br_ip *group,
140462306a36Sopenharmony_ci			struct net_bridge_port_group __rcu *next,
140562306a36Sopenharmony_ci			unsigned char flags,
140662306a36Sopenharmony_ci			const unsigned char *src,
140762306a36Sopenharmony_ci			u8 filter_mode,
140862306a36Sopenharmony_ci			u8 rt_protocol,
140962306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
141062306a36Sopenharmony_ci{
141162306a36Sopenharmony_ci	struct net_bridge_port_group *p;
141262306a36Sopenharmony_ci	int err;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	err = br_multicast_port_ngroups_inc(port, group, extack);
141562306a36Sopenharmony_ci	if (err)
141662306a36Sopenharmony_ci		return NULL;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	p = kzalloc(sizeof(*p), GFP_ATOMIC);
141962306a36Sopenharmony_ci	if (unlikely(!p)) {
142062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new port group");
142162306a36Sopenharmony_ci		goto dec_out;
142262306a36Sopenharmony_ci	}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	p->key.addr = *group;
142562306a36Sopenharmony_ci	p->key.port = port;
142662306a36Sopenharmony_ci	p->flags = flags;
142762306a36Sopenharmony_ci	p->filter_mode = filter_mode;
142862306a36Sopenharmony_ci	p->rt_protocol = rt_protocol;
142962306a36Sopenharmony_ci	p->eht_host_tree = RB_ROOT;
143062306a36Sopenharmony_ci	p->eht_set_tree = RB_ROOT;
143162306a36Sopenharmony_ci	p->mcast_gc.destroy = br_multicast_destroy_port_group;
143262306a36Sopenharmony_ci	INIT_HLIST_HEAD(&p->src_list);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	if (!br_multicast_is_star_g(group) &&
143562306a36Sopenharmony_ci	    rhashtable_lookup_insert_fast(&port->br->sg_port_tbl, &p->rhnode,
143662306a36Sopenharmony_ci					  br_sg_port_rht_params)) {
143762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Couldn't insert new port group");
143862306a36Sopenharmony_ci		goto free_out;
143962306a36Sopenharmony_ci	}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	rcu_assign_pointer(p->next, next);
144262306a36Sopenharmony_ci	timer_setup(&p->timer, br_multicast_port_group_expired, 0);
144362306a36Sopenharmony_ci	timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0);
144462306a36Sopenharmony_ci	hlist_add_head(&p->mglist, &port->mglist);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	if (src)
144762306a36Sopenharmony_ci		memcpy(p->eth_addr, src, ETH_ALEN);
144862306a36Sopenharmony_ci	else
144962306a36Sopenharmony_ci		eth_broadcast_addr(p->eth_addr);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	return p;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cifree_out:
145462306a36Sopenharmony_ci	kfree(p);
145562306a36Sopenharmony_cidec_out:
145662306a36Sopenharmony_ci	br_multicast_port_ngroups_dec(port, group->vid);
145762306a36Sopenharmony_ci	return NULL;
145862306a36Sopenharmony_ci}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_civoid br_multicast_del_port_group(struct net_bridge_port_group *p)
146162306a36Sopenharmony_ci{
146262306a36Sopenharmony_ci	struct net_bridge_port *port = p->key.port;
146362306a36Sopenharmony_ci	__u16 vid = p->key.addr.vid;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	hlist_del_init(&p->mglist);
146662306a36Sopenharmony_ci	if (!br_multicast_is_star_g(&p->key.addr))
146762306a36Sopenharmony_ci		rhashtable_remove_fast(&port->br->sg_port_tbl, &p->rhnode,
146862306a36Sopenharmony_ci				       br_sg_port_rht_params);
146962306a36Sopenharmony_ci	kfree(p);
147062306a36Sopenharmony_ci	br_multicast_port_ngroups_dec(port, vid);
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_civoid br_multicast_host_join(const struct net_bridge_mcast *brmctx,
147462306a36Sopenharmony_ci			    struct net_bridge_mdb_entry *mp, bool notify)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	if (!mp->host_joined) {
147762306a36Sopenharmony_ci		mp->host_joined = true;
147862306a36Sopenharmony_ci		if (br_multicast_is_star_g(&mp->addr))
147962306a36Sopenharmony_ci			br_multicast_star_g_host_state(mp);
148062306a36Sopenharmony_ci		if (notify)
148162306a36Sopenharmony_ci			br_mdb_notify(mp->br->dev, mp, NULL, RTM_NEWMDB);
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	if (br_group_is_l2(&mp->addr))
148562306a36Sopenharmony_ci		return;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	mod_timer(&mp->timer, jiffies + brmctx->multicast_membership_interval);
148862306a36Sopenharmony_ci}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_civoid br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify)
149162306a36Sopenharmony_ci{
149262306a36Sopenharmony_ci	if (!mp->host_joined)
149362306a36Sopenharmony_ci		return;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	mp->host_joined = false;
149662306a36Sopenharmony_ci	if (br_multicast_is_star_g(&mp->addr))
149762306a36Sopenharmony_ci		br_multicast_star_g_host_state(mp);
149862306a36Sopenharmony_ci	if (notify)
149962306a36Sopenharmony_ci		br_mdb_notify(mp->br->dev, mp, NULL, RTM_DELMDB);
150062306a36Sopenharmony_ci}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_cistatic struct net_bridge_port_group *
150362306a36Sopenharmony_ci__br_multicast_add_group(struct net_bridge_mcast *brmctx,
150462306a36Sopenharmony_ci			 struct net_bridge_mcast_port *pmctx,
150562306a36Sopenharmony_ci			 struct br_ip *group,
150662306a36Sopenharmony_ci			 const unsigned char *src,
150762306a36Sopenharmony_ci			 u8 filter_mode,
150862306a36Sopenharmony_ci			 bool igmpv2_mldv1,
150962306a36Sopenharmony_ci			 bool blocked)
151062306a36Sopenharmony_ci{
151162306a36Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
151262306a36Sopenharmony_ci	struct net_bridge_port_group *p = NULL;
151362306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
151462306a36Sopenharmony_ci	unsigned long now = jiffies;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	if (!br_multicast_ctx_should_use(brmctx, pmctx))
151762306a36Sopenharmony_ci		goto out;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	mp = br_multicast_new_group(brmctx->br, group);
152062306a36Sopenharmony_ci	if (IS_ERR(mp))
152162306a36Sopenharmony_ci		return ERR_CAST(mp);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (!pmctx) {
152462306a36Sopenharmony_ci		br_multicast_host_join(brmctx, mp, true);
152562306a36Sopenharmony_ci		goto out;
152662306a36Sopenharmony_ci	}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	for (pp = &mp->ports;
152962306a36Sopenharmony_ci	     (p = mlock_dereference(*pp, brmctx->br)) != NULL;
153062306a36Sopenharmony_ci	     pp = &p->next) {
153162306a36Sopenharmony_ci		if (br_port_group_equal(p, pmctx->port, src))
153262306a36Sopenharmony_ci			goto found;
153362306a36Sopenharmony_ci		if ((unsigned long)p->key.port < (unsigned long)pmctx->port)
153462306a36Sopenharmony_ci			break;
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	p = br_multicast_new_port_group(pmctx->port, group, *pp, 0, src,
153862306a36Sopenharmony_ci					filter_mode, RTPROT_KERNEL, NULL);
153962306a36Sopenharmony_ci	if (unlikely(!p)) {
154062306a36Sopenharmony_ci		p = ERR_PTR(-ENOMEM);
154162306a36Sopenharmony_ci		goto out;
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci	rcu_assign_pointer(*pp, p);
154462306a36Sopenharmony_ci	if (blocked)
154562306a36Sopenharmony_ci		p->flags |= MDB_PG_FLAGS_BLOCKED;
154662306a36Sopenharmony_ci	br_mdb_notify(brmctx->br->dev, mp, p, RTM_NEWMDB);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_cifound:
154962306a36Sopenharmony_ci	if (igmpv2_mldv1)
155062306a36Sopenharmony_ci		mod_timer(&p->timer,
155162306a36Sopenharmony_ci			  now + brmctx->multicast_membership_interval);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ciout:
155462306a36Sopenharmony_ci	return p;
155562306a36Sopenharmony_ci}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_cistatic int br_multicast_add_group(struct net_bridge_mcast *brmctx,
155862306a36Sopenharmony_ci				  struct net_bridge_mcast_port *pmctx,
155962306a36Sopenharmony_ci				  struct br_ip *group,
156062306a36Sopenharmony_ci				  const unsigned char *src,
156162306a36Sopenharmony_ci				  u8 filter_mode,
156262306a36Sopenharmony_ci				  bool igmpv2_mldv1)
156362306a36Sopenharmony_ci{
156462306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
156562306a36Sopenharmony_ci	int err;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
156862306a36Sopenharmony_ci	pg = __br_multicast_add_group(brmctx, pmctx, group, src, filter_mode,
156962306a36Sopenharmony_ci				      igmpv2_mldv1, false);
157062306a36Sopenharmony_ci	/* NULL is considered valid for host joined groups */
157162306a36Sopenharmony_ci	err = PTR_ERR_OR_ZERO(pg);
157262306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	return err;
157562306a36Sopenharmony_ci}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_cistatic int br_ip4_multicast_add_group(struct net_bridge_mcast *brmctx,
157862306a36Sopenharmony_ci				      struct net_bridge_mcast_port *pmctx,
157962306a36Sopenharmony_ci				      __be32 group,
158062306a36Sopenharmony_ci				      __u16 vid,
158162306a36Sopenharmony_ci				      const unsigned char *src,
158262306a36Sopenharmony_ci				      bool igmpv2)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	struct br_ip br_group;
158562306a36Sopenharmony_ci	u8 filter_mode;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	if (ipv4_is_local_multicast(group))
158862306a36Sopenharmony_ci		return 0;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	memset(&br_group, 0, sizeof(br_group));
159162306a36Sopenharmony_ci	br_group.dst.ip4 = group;
159262306a36Sopenharmony_ci	br_group.proto = htons(ETH_P_IP);
159362306a36Sopenharmony_ci	br_group.vid = vid;
159462306a36Sopenharmony_ci	filter_mode = igmpv2 ? MCAST_EXCLUDE : MCAST_INCLUDE;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	return br_multicast_add_group(brmctx, pmctx, &br_group, src,
159762306a36Sopenharmony_ci				      filter_mode, igmpv2);
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
160162306a36Sopenharmony_cistatic int br_ip6_multicast_add_group(struct net_bridge_mcast *brmctx,
160262306a36Sopenharmony_ci				      struct net_bridge_mcast_port *pmctx,
160362306a36Sopenharmony_ci				      const struct in6_addr *group,
160462306a36Sopenharmony_ci				      __u16 vid,
160562306a36Sopenharmony_ci				      const unsigned char *src,
160662306a36Sopenharmony_ci				      bool mldv1)
160762306a36Sopenharmony_ci{
160862306a36Sopenharmony_ci	struct br_ip br_group;
160962306a36Sopenharmony_ci	u8 filter_mode;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	if (ipv6_addr_is_ll_all_nodes(group))
161262306a36Sopenharmony_ci		return 0;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	memset(&br_group, 0, sizeof(br_group));
161562306a36Sopenharmony_ci	br_group.dst.ip6 = *group;
161662306a36Sopenharmony_ci	br_group.proto = htons(ETH_P_IPV6);
161762306a36Sopenharmony_ci	br_group.vid = vid;
161862306a36Sopenharmony_ci	filter_mode = mldv1 ? MCAST_EXCLUDE : MCAST_INCLUDE;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	return br_multicast_add_group(brmctx, pmctx, &br_group, src,
162162306a36Sopenharmony_ci				      filter_mode, mldv1);
162262306a36Sopenharmony_ci}
162362306a36Sopenharmony_ci#endif
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_cistatic bool br_multicast_rport_del(struct hlist_node *rlist)
162662306a36Sopenharmony_ci{
162762306a36Sopenharmony_ci	if (hlist_unhashed(rlist))
162862306a36Sopenharmony_ci		return false;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	hlist_del_init_rcu(rlist);
163162306a36Sopenharmony_ci	return true;
163262306a36Sopenharmony_ci}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_cistatic bool br_ip4_multicast_rport_del(struct net_bridge_mcast_port *pmctx)
163562306a36Sopenharmony_ci{
163662306a36Sopenharmony_ci	return br_multicast_rport_del(&pmctx->ip4_rlist);
163762306a36Sopenharmony_ci}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_cistatic bool br_ip6_multicast_rport_del(struct net_bridge_mcast_port *pmctx)
164062306a36Sopenharmony_ci{
164162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
164262306a36Sopenharmony_ci	return br_multicast_rport_del(&pmctx->ip6_rlist);
164362306a36Sopenharmony_ci#else
164462306a36Sopenharmony_ci	return false;
164562306a36Sopenharmony_ci#endif
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistatic void br_multicast_router_expired(struct net_bridge_mcast_port *pmctx,
164962306a36Sopenharmony_ci					struct timer_list *t,
165062306a36Sopenharmony_ci					struct hlist_node *rlist)
165162306a36Sopenharmony_ci{
165262306a36Sopenharmony_ci	struct net_bridge *br = pmctx->port->br;
165362306a36Sopenharmony_ci	bool del;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	spin_lock(&br->multicast_lock);
165662306a36Sopenharmony_ci	if (pmctx->multicast_router == MDB_RTR_TYPE_DISABLED ||
165762306a36Sopenharmony_ci	    pmctx->multicast_router == MDB_RTR_TYPE_PERM ||
165862306a36Sopenharmony_ci	    timer_pending(t))
165962306a36Sopenharmony_ci		goto out;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	del = br_multicast_rport_del(rlist);
166262306a36Sopenharmony_ci	br_multicast_rport_del_notify(pmctx, del);
166362306a36Sopenharmony_ciout:
166462306a36Sopenharmony_ci	spin_unlock(&br->multicast_lock);
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_cistatic void br_ip4_multicast_router_expired(struct timer_list *t)
166862306a36Sopenharmony_ci{
166962306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx = from_timer(pmctx, t,
167062306a36Sopenharmony_ci							 ip4_mc_router_timer);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	br_multicast_router_expired(pmctx, t, &pmctx->ip4_rlist);
167362306a36Sopenharmony_ci}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
167662306a36Sopenharmony_cistatic void br_ip6_multicast_router_expired(struct timer_list *t)
167762306a36Sopenharmony_ci{
167862306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx = from_timer(pmctx, t,
167962306a36Sopenharmony_ci							 ip6_mc_router_timer);
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	br_multicast_router_expired(pmctx, t, &pmctx->ip6_rlist);
168262306a36Sopenharmony_ci}
168362306a36Sopenharmony_ci#endif
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_cistatic void br_mc_router_state_change(struct net_bridge *p,
168662306a36Sopenharmony_ci				      bool is_mc_router)
168762306a36Sopenharmony_ci{
168862306a36Sopenharmony_ci	struct switchdev_attr attr = {
168962306a36Sopenharmony_ci		.orig_dev = p->dev,
169062306a36Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_MROUTER,
169162306a36Sopenharmony_ci		.flags = SWITCHDEV_F_DEFER,
169262306a36Sopenharmony_ci		.u.mrouter = is_mc_router,
169362306a36Sopenharmony_ci	};
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	switchdev_port_attr_set(p->dev, &attr, NULL);
169662306a36Sopenharmony_ci}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_cistatic void br_multicast_local_router_expired(struct net_bridge_mcast *brmctx,
169962306a36Sopenharmony_ci					      struct timer_list *timer)
170062306a36Sopenharmony_ci{
170162306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
170262306a36Sopenharmony_ci	if (brmctx->multicast_router == MDB_RTR_TYPE_DISABLED ||
170362306a36Sopenharmony_ci	    brmctx->multicast_router == MDB_RTR_TYPE_PERM ||
170462306a36Sopenharmony_ci	    br_ip4_multicast_is_router(brmctx) ||
170562306a36Sopenharmony_ci	    br_ip6_multicast_is_router(brmctx))
170662306a36Sopenharmony_ci		goto out;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	br_mc_router_state_change(brmctx->br, false);
170962306a36Sopenharmony_ciout:
171062306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
171162306a36Sopenharmony_ci}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_cistatic void br_ip4_multicast_local_router_expired(struct timer_list *t)
171462306a36Sopenharmony_ci{
171562306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
171662306a36Sopenharmony_ci						     ip4_mc_router_timer);
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	br_multicast_local_router_expired(brmctx, t);
171962306a36Sopenharmony_ci}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
172262306a36Sopenharmony_cistatic void br_ip6_multicast_local_router_expired(struct timer_list *t)
172362306a36Sopenharmony_ci{
172462306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
172562306a36Sopenharmony_ci						     ip6_mc_router_timer);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	br_multicast_local_router_expired(brmctx, t);
172862306a36Sopenharmony_ci}
172962306a36Sopenharmony_ci#endif
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_cistatic void br_multicast_querier_expired(struct net_bridge_mcast *brmctx,
173262306a36Sopenharmony_ci					 struct bridge_mcast_own_query *query)
173362306a36Sopenharmony_ci{
173462306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
173562306a36Sopenharmony_ci	if (!netif_running(brmctx->br->dev) ||
173662306a36Sopenharmony_ci	    br_multicast_ctx_vlan_global_disabled(brmctx) ||
173762306a36Sopenharmony_ci	    !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED))
173862306a36Sopenharmony_ci		goto out;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	br_multicast_start_querier(brmctx, query);
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ciout:
174362306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic void br_ip4_multicast_querier_expired(struct timer_list *t)
174762306a36Sopenharmony_ci{
174862306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
174962306a36Sopenharmony_ci						     ip4_other_query.timer);
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	br_multicast_querier_expired(brmctx, &brmctx->ip4_own_query);
175262306a36Sopenharmony_ci}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
175562306a36Sopenharmony_cistatic void br_ip6_multicast_querier_expired(struct timer_list *t)
175662306a36Sopenharmony_ci{
175762306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
175862306a36Sopenharmony_ci						     ip6_other_query.timer);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	br_multicast_querier_expired(brmctx, &brmctx->ip6_own_query);
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ci#endif
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_cistatic void br_multicast_query_delay_expired(struct timer_list *t)
176562306a36Sopenharmony_ci{
176662306a36Sopenharmony_ci}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cistatic void br_multicast_select_own_querier(struct net_bridge_mcast *brmctx,
176962306a36Sopenharmony_ci					    struct br_ip *ip,
177062306a36Sopenharmony_ci					    struct sk_buff *skb)
177162306a36Sopenharmony_ci{
177262306a36Sopenharmony_ci	if (ip->proto == htons(ETH_P_IP))
177362306a36Sopenharmony_ci		brmctx->ip4_querier.addr.src.ip4 = ip_hdr(skb)->saddr;
177462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
177562306a36Sopenharmony_ci	else
177662306a36Sopenharmony_ci		brmctx->ip6_querier.addr.src.ip6 = ipv6_hdr(skb)->saddr;
177762306a36Sopenharmony_ci#endif
177862306a36Sopenharmony_ci}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_cistatic void __br_multicast_send_query(struct net_bridge_mcast *brmctx,
178162306a36Sopenharmony_ci				      struct net_bridge_mcast_port *pmctx,
178262306a36Sopenharmony_ci				      struct net_bridge_port_group *pg,
178362306a36Sopenharmony_ci				      struct br_ip *ip_dst,
178462306a36Sopenharmony_ci				      struct br_ip *group,
178562306a36Sopenharmony_ci				      bool with_srcs,
178662306a36Sopenharmony_ci				      u8 sflag,
178762306a36Sopenharmony_ci				      bool *need_rexmit)
178862306a36Sopenharmony_ci{
178962306a36Sopenharmony_ci	bool over_lmqt = !!sflag;
179062306a36Sopenharmony_ci	struct sk_buff *skb;
179162306a36Sopenharmony_ci	u8 igmp_type;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	if (!br_multicast_ctx_should_use(brmctx, pmctx) ||
179462306a36Sopenharmony_ci	    !br_multicast_ctx_matches_vlan_snooping(brmctx))
179562306a36Sopenharmony_ci		return;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ciagain_under_lmqt:
179862306a36Sopenharmony_ci	skb = br_multicast_alloc_query(brmctx, pmctx, pg, ip_dst, group,
179962306a36Sopenharmony_ci				       with_srcs, over_lmqt, sflag, &igmp_type,
180062306a36Sopenharmony_ci				       need_rexmit);
180162306a36Sopenharmony_ci	if (!skb)
180262306a36Sopenharmony_ci		return;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	if (pmctx) {
180562306a36Sopenharmony_ci		skb->dev = pmctx->port->dev;
180662306a36Sopenharmony_ci		br_multicast_count(brmctx->br, pmctx->port, skb, igmp_type,
180762306a36Sopenharmony_ci				   BR_MCAST_DIR_TX);
180862306a36Sopenharmony_ci		NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
180962306a36Sopenharmony_ci			dev_net(pmctx->port->dev), NULL, skb, NULL, skb->dev,
181062306a36Sopenharmony_ci			br_dev_queue_push_xmit);
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci		if (over_lmqt && with_srcs && sflag) {
181362306a36Sopenharmony_ci			over_lmqt = false;
181462306a36Sopenharmony_ci			goto again_under_lmqt;
181562306a36Sopenharmony_ci		}
181662306a36Sopenharmony_ci	} else {
181762306a36Sopenharmony_ci		br_multicast_select_own_querier(brmctx, group, skb);
181862306a36Sopenharmony_ci		br_multicast_count(brmctx->br, NULL, skb, igmp_type,
181962306a36Sopenharmony_ci				   BR_MCAST_DIR_RX);
182062306a36Sopenharmony_ci		netif_rx(skb);
182162306a36Sopenharmony_ci	}
182262306a36Sopenharmony_ci}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_cistatic void br_multicast_read_querier(const struct bridge_mcast_querier *querier,
182562306a36Sopenharmony_ci				      struct bridge_mcast_querier *dest)
182662306a36Sopenharmony_ci{
182762306a36Sopenharmony_ci	unsigned int seq;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	memset(dest, 0, sizeof(*dest));
183062306a36Sopenharmony_ci	do {
183162306a36Sopenharmony_ci		seq = read_seqcount_begin(&querier->seq);
183262306a36Sopenharmony_ci		dest->port_ifidx = querier->port_ifidx;
183362306a36Sopenharmony_ci		memcpy(&dest->addr, &querier->addr, sizeof(struct br_ip));
183462306a36Sopenharmony_ci	} while (read_seqcount_retry(&querier->seq, seq));
183562306a36Sopenharmony_ci}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_cistatic void br_multicast_update_querier(struct net_bridge_mcast *brmctx,
183862306a36Sopenharmony_ci					struct bridge_mcast_querier *querier,
183962306a36Sopenharmony_ci					int ifindex,
184062306a36Sopenharmony_ci					struct br_ip *saddr)
184162306a36Sopenharmony_ci{
184262306a36Sopenharmony_ci	write_seqcount_begin(&querier->seq);
184362306a36Sopenharmony_ci	querier->port_ifidx = ifindex;
184462306a36Sopenharmony_ci	memcpy(&querier->addr, saddr, sizeof(*saddr));
184562306a36Sopenharmony_ci	write_seqcount_end(&querier->seq);
184662306a36Sopenharmony_ci}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_cistatic void br_multicast_send_query(struct net_bridge_mcast *brmctx,
184962306a36Sopenharmony_ci				    struct net_bridge_mcast_port *pmctx,
185062306a36Sopenharmony_ci				    struct bridge_mcast_own_query *own_query)
185162306a36Sopenharmony_ci{
185262306a36Sopenharmony_ci	struct bridge_mcast_other_query *other_query = NULL;
185362306a36Sopenharmony_ci	struct bridge_mcast_querier *querier;
185462306a36Sopenharmony_ci	struct br_ip br_group;
185562306a36Sopenharmony_ci	unsigned long time;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	if (!br_multicast_ctx_should_use(brmctx, pmctx) ||
185862306a36Sopenharmony_ci	    !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED) ||
185962306a36Sopenharmony_ci	    !brmctx->multicast_querier)
186062306a36Sopenharmony_ci		return;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	memset(&br_group.dst, 0, sizeof(br_group.dst));
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	if (pmctx ? (own_query == &pmctx->ip4_own_query) :
186562306a36Sopenharmony_ci		    (own_query == &brmctx->ip4_own_query)) {
186662306a36Sopenharmony_ci		querier = &brmctx->ip4_querier;
186762306a36Sopenharmony_ci		other_query = &brmctx->ip4_other_query;
186862306a36Sopenharmony_ci		br_group.proto = htons(ETH_P_IP);
186962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
187062306a36Sopenharmony_ci	} else {
187162306a36Sopenharmony_ci		querier = &brmctx->ip6_querier;
187262306a36Sopenharmony_ci		other_query = &brmctx->ip6_other_query;
187362306a36Sopenharmony_ci		br_group.proto = htons(ETH_P_IPV6);
187462306a36Sopenharmony_ci#endif
187562306a36Sopenharmony_ci	}
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	if (!other_query || timer_pending(&other_query->timer))
187862306a36Sopenharmony_ci		return;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	/* we're about to select ourselves as querier */
188162306a36Sopenharmony_ci	if (!pmctx && querier->port_ifidx) {
188262306a36Sopenharmony_ci		struct br_ip zeroip = {};
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci		br_multicast_update_querier(brmctx, querier, 0, &zeroip);
188562306a36Sopenharmony_ci	}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	__br_multicast_send_query(brmctx, pmctx, NULL, NULL, &br_group, false,
188862306a36Sopenharmony_ci				  0, NULL);
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	time = jiffies;
189162306a36Sopenharmony_ci	time += own_query->startup_sent < brmctx->multicast_startup_query_count ?
189262306a36Sopenharmony_ci		brmctx->multicast_startup_query_interval :
189362306a36Sopenharmony_ci		brmctx->multicast_query_interval;
189462306a36Sopenharmony_ci	mod_timer(&own_query->timer, time);
189562306a36Sopenharmony_ci}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_cistatic void
189862306a36Sopenharmony_cibr_multicast_port_query_expired(struct net_bridge_mcast_port *pmctx,
189962306a36Sopenharmony_ci				struct bridge_mcast_own_query *query)
190062306a36Sopenharmony_ci{
190162306a36Sopenharmony_ci	struct net_bridge *br = pmctx->port->br;
190262306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	spin_lock(&br->multicast_lock);
190562306a36Sopenharmony_ci	if (br_multicast_port_ctx_state_stopped(pmctx))
190662306a36Sopenharmony_ci		goto out;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	brmctx = br_multicast_port_ctx_get_global(pmctx);
190962306a36Sopenharmony_ci	if (query->startup_sent < brmctx->multicast_startup_query_count)
191062306a36Sopenharmony_ci		query->startup_sent++;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	br_multicast_send_query(brmctx, pmctx, query);
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ciout:
191562306a36Sopenharmony_ci	spin_unlock(&br->multicast_lock);
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_cistatic void br_ip4_multicast_port_query_expired(struct timer_list *t)
191962306a36Sopenharmony_ci{
192062306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx = from_timer(pmctx, t,
192162306a36Sopenharmony_ci							 ip4_own_query.timer);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	br_multicast_port_query_expired(pmctx, &pmctx->ip4_own_query);
192462306a36Sopenharmony_ci}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
192762306a36Sopenharmony_cistatic void br_ip6_multicast_port_query_expired(struct timer_list *t)
192862306a36Sopenharmony_ci{
192962306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx = from_timer(pmctx, t,
193062306a36Sopenharmony_ci							 ip6_own_query.timer);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	br_multicast_port_query_expired(pmctx, &pmctx->ip6_own_query);
193362306a36Sopenharmony_ci}
193462306a36Sopenharmony_ci#endif
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_cistatic void br_multicast_port_group_rexmit(struct timer_list *t)
193762306a36Sopenharmony_ci{
193862306a36Sopenharmony_ci	struct net_bridge_port_group *pg = from_timer(pg, t, rexmit_timer);
193962306a36Sopenharmony_ci	struct bridge_mcast_other_query *other_query = NULL;
194062306a36Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
194162306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx;
194262306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx;
194362306a36Sopenharmony_ci	bool need_rexmit = false;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	spin_lock(&br->multicast_lock);
194662306a36Sopenharmony_ci	if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) ||
194762306a36Sopenharmony_ci	    !br_opt_get(br, BROPT_MULTICAST_ENABLED))
194862306a36Sopenharmony_ci		goto out;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	pmctx = br_multicast_pg_to_port_ctx(pg);
195162306a36Sopenharmony_ci	if (!pmctx)
195262306a36Sopenharmony_ci		goto out;
195362306a36Sopenharmony_ci	brmctx = br_multicast_port_ctx_get_global(pmctx);
195462306a36Sopenharmony_ci	if (!brmctx->multicast_querier)
195562306a36Sopenharmony_ci		goto out;
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	if (pg->key.addr.proto == htons(ETH_P_IP))
195862306a36Sopenharmony_ci		other_query = &brmctx->ip4_other_query;
195962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
196062306a36Sopenharmony_ci	else
196162306a36Sopenharmony_ci		other_query = &brmctx->ip6_other_query;
196262306a36Sopenharmony_ci#endif
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	if (!other_query || timer_pending(&other_query->timer))
196562306a36Sopenharmony_ci		goto out;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	if (pg->grp_query_rexmit_cnt) {
196862306a36Sopenharmony_ci		pg->grp_query_rexmit_cnt--;
196962306a36Sopenharmony_ci		__br_multicast_send_query(brmctx, pmctx, pg, &pg->key.addr,
197062306a36Sopenharmony_ci					  &pg->key.addr, false, 1, NULL);
197162306a36Sopenharmony_ci	}
197262306a36Sopenharmony_ci	__br_multicast_send_query(brmctx, pmctx, pg, &pg->key.addr,
197362306a36Sopenharmony_ci				  &pg->key.addr, true, 0, &need_rexmit);
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	if (pg->grp_query_rexmit_cnt || need_rexmit)
197662306a36Sopenharmony_ci		mod_timer(&pg->rexmit_timer, jiffies +
197762306a36Sopenharmony_ci					     brmctx->multicast_last_member_interval);
197862306a36Sopenharmony_ciout:
197962306a36Sopenharmony_ci	spin_unlock(&br->multicast_lock);
198062306a36Sopenharmony_ci}
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_cistatic int br_mc_disabled_update(struct net_device *dev, bool value,
198362306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
198462306a36Sopenharmony_ci{
198562306a36Sopenharmony_ci	struct switchdev_attr attr = {
198662306a36Sopenharmony_ci		.orig_dev = dev,
198762306a36Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED,
198862306a36Sopenharmony_ci		.flags = SWITCHDEV_F_DEFER,
198962306a36Sopenharmony_ci		.u.mc_disabled = !value,
199062306a36Sopenharmony_ci	};
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	return switchdev_port_attr_set(dev, &attr, extack);
199362306a36Sopenharmony_ci}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_civoid br_multicast_port_ctx_init(struct net_bridge_port *port,
199662306a36Sopenharmony_ci				struct net_bridge_vlan *vlan,
199762306a36Sopenharmony_ci				struct net_bridge_mcast_port *pmctx)
199862306a36Sopenharmony_ci{
199962306a36Sopenharmony_ci	pmctx->port = port;
200062306a36Sopenharmony_ci	pmctx->vlan = vlan;
200162306a36Sopenharmony_ci	pmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
200262306a36Sopenharmony_ci	timer_setup(&pmctx->ip4_mc_router_timer,
200362306a36Sopenharmony_ci		    br_ip4_multicast_router_expired, 0);
200462306a36Sopenharmony_ci	timer_setup(&pmctx->ip4_own_query.timer,
200562306a36Sopenharmony_ci		    br_ip4_multicast_port_query_expired, 0);
200662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
200762306a36Sopenharmony_ci	timer_setup(&pmctx->ip6_mc_router_timer,
200862306a36Sopenharmony_ci		    br_ip6_multicast_router_expired, 0);
200962306a36Sopenharmony_ci	timer_setup(&pmctx->ip6_own_query.timer,
201062306a36Sopenharmony_ci		    br_ip6_multicast_port_query_expired, 0);
201162306a36Sopenharmony_ci#endif
201262306a36Sopenharmony_ci}
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_civoid br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx)
201562306a36Sopenharmony_ci{
201662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
201762306a36Sopenharmony_ci	del_timer_sync(&pmctx->ip6_mc_router_timer);
201862306a36Sopenharmony_ci#endif
201962306a36Sopenharmony_ci	del_timer_sync(&pmctx->ip4_mc_router_timer);
202062306a36Sopenharmony_ci}
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ciint br_multicast_add_port(struct net_bridge_port *port)
202362306a36Sopenharmony_ci{
202462306a36Sopenharmony_ci	int err;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	port->multicast_eht_hosts_limit = BR_MCAST_DEFAULT_EHT_HOSTS_LIMIT;
202762306a36Sopenharmony_ci	br_multicast_port_ctx_init(port, NULL, &port->multicast_ctx);
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	err = br_mc_disabled_update(port->dev,
203062306a36Sopenharmony_ci				    br_opt_get(port->br,
203162306a36Sopenharmony_ci					       BROPT_MULTICAST_ENABLED),
203262306a36Sopenharmony_ci				    NULL);
203362306a36Sopenharmony_ci	if (err && err != -EOPNOTSUPP)
203462306a36Sopenharmony_ci		return err;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	port->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats);
203762306a36Sopenharmony_ci	if (!port->mcast_stats)
203862306a36Sopenharmony_ci		return -ENOMEM;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	return 0;
204162306a36Sopenharmony_ci}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_civoid br_multicast_del_port(struct net_bridge_port *port)
204462306a36Sopenharmony_ci{
204562306a36Sopenharmony_ci	struct net_bridge *br = port->br;
204662306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
204762306a36Sopenharmony_ci	HLIST_HEAD(deleted_head);
204862306a36Sopenharmony_ci	struct hlist_node *n;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	/* Take care of the remaining groups, only perm ones should be left */
205162306a36Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
205262306a36Sopenharmony_ci	hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
205362306a36Sopenharmony_ci		br_multicast_find_del_pg(br, pg);
205462306a36Sopenharmony_ci	hlist_move_list(&br->mcast_gc_list, &deleted_head);
205562306a36Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
205662306a36Sopenharmony_ci	br_multicast_gc(&deleted_head);
205762306a36Sopenharmony_ci	br_multicast_port_ctx_deinit(&port->multicast_ctx);
205862306a36Sopenharmony_ci	free_percpu(port->mcast_stats);
205962306a36Sopenharmony_ci}
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_cistatic void br_multicast_enable(struct bridge_mcast_own_query *query)
206262306a36Sopenharmony_ci{
206362306a36Sopenharmony_ci	query->startup_sent = 0;
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	if (try_to_del_timer_sync(&query->timer) >= 0 ||
206662306a36Sopenharmony_ci	    del_timer(&query->timer))
206762306a36Sopenharmony_ci		mod_timer(&query->timer, jiffies);
206862306a36Sopenharmony_ci}
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_cistatic void __br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx)
207162306a36Sopenharmony_ci{
207262306a36Sopenharmony_ci	struct net_bridge *br = pmctx->port->br;
207362306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx;
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	brmctx = br_multicast_port_ctx_get_global(pmctx);
207662306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
207762306a36Sopenharmony_ci	    !netif_running(br->dev))
207862306a36Sopenharmony_ci		return;
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	br_multicast_enable(&pmctx->ip4_own_query);
208162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
208262306a36Sopenharmony_ci	br_multicast_enable(&pmctx->ip6_own_query);
208362306a36Sopenharmony_ci#endif
208462306a36Sopenharmony_ci	if (pmctx->multicast_router == MDB_RTR_TYPE_PERM) {
208562306a36Sopenharmony_ci		br_ip4_multicast_add_router(brmctx, pmctx);
208662306a36Sopenharmony_ci		br_ip6_multicast_add_router(brmctx, pmctx);
208762306a36Sopenharmony_ci	}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	if (br_multicast_port_ctx_is_vlan(pmctx)) {
209062306a36Sopenharmony_ci		struct net_bridge_port_group *pg;
209162306a36Sopenharmony_ci		u32 n = 0;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci		/* The mcast_n_groups counter might be wrong. First,
209462306a36Sopenharmony_ci		 * BR_VLFLAG_MCAST_ENABLED is toggled before temporary entries
209562306a36Sopenharmony_ci		 * are flushed, thus mcast_n_groups after the toggle does not
209662306a36Sopenharmony_ci		 * reflect the true values. And second, permanent entries added
209762306a36Sopenharmony_ci		 * while BR_VLFLAG_MCAST_ENABLED was disabled, are not reflected
209862306a36Sopenharmony_ci		 * either. Thus we have to refresh the counter.
209962306a36Sopenharmony_ci		 */
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci		hlist_for_each_entry(pg, &pmctx->port->mglist, mglist) {
210262306a36Sopenharmony_ci			if (pg->key.addr.vid == pmctx->vlan->vid)
210362306a36Sopenharmony_ci				n++;
210462306a36Sopenharmony_ci		}
210562306a36Sopenharmony_ci		WRITE_ONCE(pmctx->mdb_n_entries, n);
210662306a36Sopenharmony_ci	}
210762306a36Sopenharmony_ci}
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_civoid br_multicast_enable_port(struct net_bridge_port *port)
211062306a36Sopenharmony_ci{
211162306a36Sopenharmony_ci	struct net_bridge *br = port->br;
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
211462306a36Sopenharmony_ci	__br_multicast_enable_port_ctx(&port->multicast_ctx);
211562306a36Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
211662306a36Sopenharmony_ci}
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_cistatic void __br_multicast_disable_port_ctx(struct net_bridge_mcast_port *pmctx)
211962306a36Sopenharmony_ci{
212062306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
212162306a36Sopenharmony_ci	struct hlist_node *n;
212262306a36Sopenharmony_ci	bool del = false;
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	hlist_for_each_entry_safe(pg, n, &pmctx->port->mglist, mglist)
212562306a36Sopenharmony_ci		if (!(pg->flags & MDB_PG_FLAGS_PERMANENT) &&
212662306a36Sopenharmony_ci		    (!br_multicast_port_ctx_is_vlan(pmctx) ||
212762306a36Sopenharmony_ci		     pg->key.addr.vid == pmctx->vlan->vid))
212862306a36Sopenharmony_ci			br_multicast_find_del_pg(pmctx->port->br, pg);
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	del |= br_ip4_multicast_rport_del(pmctx);
213162306a36Sopenharmony_ci	del_timer(&pmctx->ip4_mc_router_timer);
213262306a36Sopenharmony_ci	del_timer(&pmctx->ip4_own_query.timer);
213362306a36Sopenharmony_ci	del |= br_ip6_multicast_rport_del(pmctx);
213462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
213562306a36Sopenharmony_ci	del_timer(&pmctx->ip6_mc_router_timer);
213662306a36Sopenharmony_ci	del_timer(&pmctx->ip6_own_query.timer);
213762306a36Sopenharmony_ci#endif
213862306a36Sopenharmony_ci	br_multicast_rport_del_notify(pmctx, del);
213962306a36Sopenharmony_ci}
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_civoid br_multicast_disable_port(struct net_bridge_port *port)
214262306a36Sopenharmony_ci{
214362306a36Sopenharmony_ci	spin_lock_bh(&port->br->multicast_lock);
214462306a36Sopenharmony_ci	__br_multicast_disable_port_ctx(&port->multicast_ctx);
214562306a36Sopenharmony_ci	spin_unlock_bh(&port->br->multicast_lock);
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cistatic int __grp_src_delete_marked(struct net_bridge_port_group *pg)
214962306a36Sopenharmony_ci{
215062306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
215162306a36Sopenharmony_ci	struct hlist_node *tmp;
215262306a36Sopenharmony_ci	int deleted = 0;
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
215562306a36Sopenharmony_ci		if (ent->flags & BR_SGRP_F_DELETE) {
215662306a36Sopenharmony_ci			br_multicast_del_group_src(ent, false);
215762306a36Sopenharmony_ci			deleted++;
215862306a36Sopenharmony_ci		}
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	return deleted;
216162306a36Sopenharmony_ci}
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_cistatic void __grp_src_mod_timer(struct net_bridge_group_src *src,
216462306a36Sopenharmony_ci				unsigned long expires)
216562306a36Sopenharmony_ci{
216662306a36Sopenharmony_ci	mod_timer(&src->timer, expires);
216762306a36Sopenharmony_ci	br_multicast_fwd_src_handle(src);
216862306a36Sopenharmony_ci}
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_cistatic void __grp_src_query_marked_and_rexmit(struct net_bridge_mcast *brmctx,
217162306a36Sopenharmony_ci					      struct net_bridge_mcast_port *pmctx,
217262306a36Sopenharmony_ci					      struct net_bridge_port_group *pg)
217362306a36Sopenharmony_ci{
217462306a36Sopenharmony_ci	struct bridge_mcast_other_query *other_query = NULL;
217562306a36Sopenharmony_ci	u32 lmqc = brmctx->multicast_last_member_count;
217662306a36Sopenharmony_ci	unsigned long lmqt, lmi, now = jiffies;
217762306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	if (!netif_running(brmctx->br->dev) ||
218062306a36Sopenharmony_ci	    !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED))
218162306a36Sopenharmony_ci		return;
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	if (pg->key.addr.proto == htons(ETH_P_IP))
218462306a36Sopenharmony_ci		other_query = &brmctx->ip4_other_query;
218562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
218662306a36Sopenharmony_ci	else
218762306a36Sopenharmony_ci		other_query = &brmctx->ip6_other_query;
218862306a36Sopenharmony_ci#endif
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	lmqt = now + br_multicast_lmqt(brmctx);
219162306a36Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node) {
219262306a36Sopenharmony_ci		if (ent->flags & BR_SGRP_F_SEND) {
219362306a36Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_SEND;
219462306a36Sopenharmony_ci			if (ent->timer.expires > lmqt) {
219562306a36Sopenharmony_ci				if (brmctx->multicast_querier &&
219662306a36Sopenharmony_ci				    other_query &&
219762306a36Sopenharmony_ci				    !timer_pending(&other_query->timer))
219862306a36Sopenharmony_ci					ent->src_query_rexmit_cnt = lmqc;
219962306a36Sopenharmony_ci				__grp_src_mod_timer(ent, lmqt);
220062306a36Sopenharmony_ci			}
220162306a36Sopenharmony_ci		}
220262306a36Sopenharmony_ci	}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	if (!brmctx->multicast_querier ||
220562306a36Sopenharmony_ci	    !other_query || timer_pending(&other_query->timer))
220662306a36Sopenharmony_ci		return;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	__br_multicast_send_query(brmctx, pmctx, pg, &pg->key.addr,
220962306a36Sopenharmony_ci				  &pg->key.addr, true, 1, NULL);
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	lmi = now + brmctx->multicast_last_member_interval;
221262306a36Sopenharmony_ci	if (!timer_pending(&pg->rexmit_timer) ||
221362306a36Sopenharmony_ci	    time_after(pg->rexmit_timer.expires, lmi))
221462306a36Sopenharmony_ci		mod_timer(&pg->rexmit_timer, lmi);
221562306a36Sopenharmony_ci}
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_cistatic void __grp_send_query_and_rexmit(struct net_bridge_mcast *brmctx,
221862306a36Sopenharmony_ci					struct net_bridge_mcast_port *pmctx,
221962306a36Sopenharmony_ci					struct net_bridge_port_group *pg)
222062306a36Sopenharmony_ci{
222162306a36Sopenharmony_ci	struct bridge_mcast_other_query *other_query = NULL;
222262306a36Sopenharmony_ci	unsigned long now = jiffies, lmi;
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	if (!netif_running(brmctx->br->dev) ||
222562306a36Sopenharmony_ci	    !br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED))
222662306a36Sopenharmony_ci		return;
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	if (pg->key.addr.proto == htons(ETH_P_IP))
222962306a36Sopenharmony_ci		other_query = &brmctx->ip4_other_query;
223062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
223162306a36Sopenharmony_ci	else
223262306a36Sopenharmony_ci		other_query = &brmctx->ip6_other_query;
223362306a36Sopenharmony_ci#endif
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	if (brmctx->multicast_querier &&
223662306a36Sopenharmony_ci	    other_query && !timer_pending(&other_query->timer)) {
223762306a36Sopenharmony_ci		lmi = now + brmctx->multicast_last_member_interval;
223862306a36Sopenharmony_ci		pg->grp_query_rexmit_cnt = brmctx->multicast_last_member_count - 1;
223962306a36Sopenharmony_ci		__br_multicast_send_query(brmctx, pmctx, pg, &pg->key.addr,
224062306a36Sopenharmony_ci					  &pg->key.addr, false, 0, NULL);
224162306a36Sopenharmony_ci		if (!timer_pending(&pg->rexmit_timer) ||
224262306a36Sopenharmony_ci		    time_after(pg->rexmit_timer.expires, lmi))
224362306a36Sopenharmony_ci			mod_timer(&pg->rexmit_timer, lmi);
224462306a36Sopenharmony_ci	}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	if (pg->filter_mode == MCAST_EXCLUDE &&
224762306a36Sopenharmony_ci	    (!timer_pending(&pg->timer) ||
224862306a36Sopenharmony_ci	     time_after(pg->timer.expires, now + br_multicast_lmqt(brmctx))))
224962306a36Sopenharmony_ci		mod_timer(&pg->timer, now + br_multicast_lmqt(brmctx));
225062306a36Sopenharmony_ci}
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci/* State          Msg type      New state                Actions
225362306a36Sopenharmony_ci * INCLUDE (A)    IS_IN (B)     INCLUDE (A+B)            (B)=GMI
225462306a36Sopenharmony_ci * INCLUDE (A)    ALLOW (B)     INCLUDE (A+B)            (B)=GMI
225562306a36Sopenharmony_ci * EXCLUDE (X,Y)  ALLOW (A)     EXCLUDE (X+A,Y-A)        (A)=GMI
225662306a36Sopenharmony_ci */
225762306a36Sopenharmony_cistatic bool br_multicast_isinc_allow(const struct net_bridge_mcast *brmctx,
225862306a36Sopenharmony_ci				     struct net_bridge_port_group *pg, void *h_addr,
225962306a36Sopenharmony_ci				     void *srcs, u32 nsrcs, size_t addr_size,
226062306a36Sopenharmony_ci				     int grec_type)
226162306a36Sopenharmony_ci{
226262306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
226362306a36Sopenharmony_ci	unsigned long now = jiffies;
226462306a36Sopenharmony_ci	bool changed = false;
226562306a36Sopenharmony_ci	struct br_ip src_ip;
226662306a36Sopenharmony_ci	u32 src_idx;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
226962306a36Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
227062306a36Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
227162306a36Sopenharmony_ci		memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
227262306a36Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
227362306a36Sopenharmony_ci		if (!ent) {
227462306a36Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
227562306a36Sopenharmony_ci			if (ent)
227662306a36Sopenharmony_ci				changed = true;
227762306a36Sopenharmony_ci		}
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci		if (ent)
228062306a36Sopenharmony_ci			__grp_src_mod_timer(ent, now + br_multicast_gmi(brmctx));
228162306a36Sopenharmony_ci	}
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
228462306a36Sopenharmony_ci				    grec_type))
228562306a36Sopenharmony_ci		changed = true;
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	return changed;
228862306a36Sopenharmony_ci}
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci/* State          Msg type      New state                Actions
229162306a36Sopenharmony_ci * INCLUDE (A)    IS_EX (B)     EXCLUDE (A*B,B-A)        (B-A)=0
229262306a36Sopenharmony_ci *                                                       Delete (A-B)
229362306a36Sopenharmony_ci *                                                       Group Timer=GMI
229462306a36Sopenharmony_ci */
229562306a36Sopenharmony_cistatic void __grp_src_isexc_incl(const struct net_bridge_mcast *brmctx,
229662306a36Sopenharmony_ci				 struct net_bridge_port_group *pg, void *h_addr,
229762306a36Sopenharmony_ci				 void *srcs, u32 nsrcs, size_t addr_size,
229862306a36Sopenharmony_ci				 int grec_type)
229962306a36Sopenharmony_ci{
230062306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
230162306a36Sopenharmony_ci	struct br_ip src_ip;
230262306a36Sopenharmony_ci	u32 src_idx;
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
230562306a36Sopenharmony_ci		ent->flags |= BR_SGRP_F_DELETE;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
230862306a36Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
230962306a36Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
231062306a36Sopenharmony_ci		memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
231162306a36Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
231262306a36Sopenharmony_ci		if (ent)
231362306a36Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_DELETE;
231462306a36Sopenharmony_ci		else
231562306a36Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
231662306a36Sopenharmony_ci		if (ent)
231762306a36Sopenharmony_ci			br_multicast_fwd_src_handle(ent);
231862306a36Sopenharmony_ci	}
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
232162306a36Sopenharmony_ci				grec_type);
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	__grp_src_delete_marked(pg);
232462306a36Sopenharmony_ci}
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci/* State          Msg type      New state                Actions
232762306a36Sopenharmony_ci * EXCLUDE (X,Y)  IS_EX (A)     EXCLUDE (A-Y,Y*A)        (A-X-Y)=GMI
232862306a36Sopenharmony_ci *                                                       Delete (X-A)
232962306a36Sopenharmony_ci *                                                       Delete (Y-A)
233062306a36Sopenharmony_ci *                                                       Group Timer=GMI
233162306a36Sopenharmony_ci */
233262306a36Sopenharmony_cistatic bool __grp_src_isexc_excl(const struct net_bridge_mcast *brmctx,
233362306a36Sopenharmony_ci				 struct net_bridge_port_group *pg, void *h_addr,
233462306a36Sopenharmony_ci				 void *srcs, u32 nsrcs, size_t addr_size,
233562306a36Sopenharmony_ci				 int grec_type)
233662306a36Sopenharmony_ci{
233762306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
233862306a36Sopenharmony_ci	unsigned long now = jiffies;
233962306a36Sopenharmony_ci	bool changed = false;
234062306a36Sopenharmony_ci	struct br_ip src_ip;
234162306a36Sopenharmony_ci	u32 src_idx;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
234462306a36Sopenharmony_ci		ent->flags |= BR_SGRP_F_DELETE;
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
234762306a36Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
234862306a36Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
234962306a36Sopenharmony_ci		memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
235062306a36Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
235162306a36Sopenharmony_ci		if (ent) {
235262306a36Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_DELETE;
235362306a36Sopenharmony_ci		} else {
235462306a36Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
235562306a36Sopenharmony_ci			if (ent) {
235662306a36Sopenharmony_ci				__grp_src_mod_timer(ent,
235762306a36Sopenharmony_ci						    now + br_multicast_gmi(brmctx));
235862306a36Sopenharmony_ci				changed = true;
235962306a36Sopenharmony_ci			}
236062306a36Sopenharmony_ci		}
236162306a36Sopenharmony_ci	}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
236462306a36Sopenharmony_ci				    grec_type))
236562306a36Sopenharmony_ci		changed = true;
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci	if (__grp_src_delete_marked(pg))
236862306a36Sopenharmony_ci		changed = true;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	return changed;
237162306a36Sopenharmony_ci}
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_cistatic bool br_multicast_isexc(const struct net_bridge_mcast *brmctx,
237462306a36Sopenharmony_ci			       struct net_bridge_port_group *pg, void *h_addr,
237562306a36Sopenharmony_ci			       void *srcs, u32 nsrcs, size_t addr_size,
237662306a36Sopenharmony_ci			       int grec_type)
237762306a36Sopenharmony_ci{
237862306a36Sopenharmony_ci	bool changed = false;
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	switch (pg->filter_mode) {
238162306a36Sopenharmony_ci	case MCAST_INCLUDE:
238262306a36Sopenharmony_ci		__grp_src_isexc_incl(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
238362306a36Sopenharmony_ci				     grec_type);
238462306a36Sopenharmony_ci		br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE);
238562306a36Sopenharmony_ci		changed = true;
238662306a36Sopenharmony_ci		break;
238762306a36Sopenharmony_ci	case MCAST_EXCLUDE:
238862306a36Sopenharmony_ci		changed = __grp_src_isexc_excl(brmctx, pg, h_addr, srcs, nsrcs,
238962306a36Sopenharmony_ci					       addr_size, grec_type);
239062306a36Sopenharmony_ci		break;
239162306a36Sopenharmony_ci	}
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	pg->filter_mode = MCAST_EXCLUDE;
239462306a36Sopenharmony_ci	mod_timer(&pg->timer, jiffies + br_multicast_gmi(brmctx));
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	return changed;
239762306a36Sopenharmony_ci}
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci/* State          Msg type      New state                Actions
240062306a36Sopenharmony_ci * INCLUDE (A)    TO_IN (B)     INCLUDE (A+B)            (B)=GMI
240162306a36Sopenharmony_ci *                                                       Send Q(G,A-B)
240262306a36Sopenharmony_ci */
240362306a36Sopenharmony_cistatic bool __grp_src_toin_incl(struct net_bridge_mcast *brmctx,
240462306a36Sopenharmony_ci				struct net_bridge_mcast_port *pmctx,
240562306a36Sopenharmony_ci				struct net_bridge_port_group *pg, void *h_addr,
240662306a36Sopenharmony_ci				void *srcs, u32 nsrcs, size_t addr_size,
240762306a36Sopenharmony_ci				int grec_type)
240862306a36Sopenharmony_ci{
240962306a36Sopenharmony_ci	u32 src_idx, to_send = pg->src_ents;
241062306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
241162306a36Sopenharmony_ci	unsigned long now = jiffies;
241262306a36Sopenharmony_ci	bool changed = false;
241362306a36Sopenharmony_ci	struct br_ip src_ip;
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
241662306a36Sopenharmony_ci		ent->flags |= BR_SGRP_F_SEND;
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
241962306a36Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
242062306a36Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
242162306a36Sopenharmony_ci		memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
242262306a36Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
242362306a36Sopenharmony_ci		if (ent) {
242462306a36Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_SEND;
242562306a36Sopenharmony_ci			to_send--;
242662306a36Sopenharmony_ci		} else {
242762306a36Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
242862306a36Sopenharmony_ci			if (ent)
242962306a36Sopenharmony_ci				changed = true;
243062306a36Sopenharmony_ci		}
243162306a36Sopenharmony_ci		if (ent)
243262306a36Sopenharmony_ci			__grp_src_mod_timer(ent, now + br_multicast_gmi(brmctx));
243362306a36Sopenharmony_ci	}
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
243662306a36Sopenharmony_ci				    grec_type))
243762306a36Sopenharmony_ci		changed = true;
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	if (to_send)
244062306a36Sopenharmony_ci		__grp_src_query_marked_and_rexmit(brmctx, pmctx, pg);
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_ci	return changed;
244362306a36Sopenharmony_ci}
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci/* State          Msg type      New state                Actions
244662306a36Sopenharmony_ci * EXCLUDE (X,Y)  TO_IN (A)     EXCLUDE (X+A,Y-A)        (A)=GMI
244762306a36Sopenharmony_ci *                                                       Send Q(G,X-A)
244862306a36Sopenharmony_ci *                                                       Send Q(G)
244962306a36Sopenharmony_ci */
245062306a36Sopenharmony_cistatic bool __grp_src_toin_excl(struct net_bridge_mcast *brmctx,
245162306a36Sopenharmony_ci				struct net_bridge_mcast_port *pmctx,
245262306a36Sopenharmony_ci				struct net_bridge_port_group *pg, void *h_addr,
245362306a36Sopenharmony_ci				void *srcs, u32 nsrcs, size_t addr_size,
245462306a36Sopenharmony_ci				int grec_type)
245562306a36Sopenharmony_ci{
245662306a36Sopenharmony_ci	u32 src_idx, to_send = pg->src_ents;
245762306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
245862306a36Sopenharmony_ci	unsigned long now = jiffies;
245962306a36Sopenharmony_ci	bool changed = false;
246062306a36Sopenharmony_ci	struct br_ip src_ip;
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
246362306a36Sopenharmony_ci		if (timer_pending(&ent->timer))
246462306a36Sopenharmony_ci			ent->flags |= BR_SGRP_F_SEND;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
246762306a36Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
246862306a36Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
246962306a36Sopenharmony_ci		memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
247062306a36Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
247162306a36Sopenharmony_ci		if (ent) {
247262306a36Sopenharmony_ci			if (timer_pending(&ent->timer)) {
247362306a36Sopenharmony_ci				ent->flags &= ~BR_SGRP_F_SEND;
247462306a36Sopenharmony_ci				to_send--;
247562306a36Sopenharmony_ci			}
247662306a36Sopenharmony_ci		} else {
247762306a36Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
247862306a36Sopenharmony_ci			if (ent)
247962306a36Sopenharmony_ci				changed = true;
248062306a36Sopenharmony_ci		}
248162306a36Sopenharmony_ci		if (ent)
248262306a36Sopenharmony_ci			__grp_src_mod_timer(ent, now + br_multicast_gmi(brmctx));
248362306a36Sopenharmony_ci	}
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
248662306a36Sopenharmony_ci				    grec_type))
248762306a36Sopenharmony_ci		changed = true;
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	if (to_send)
249062306a36Sopenharmony_ci		__grp_src_query_marked_and_rexmit(brmctx, pmctx, pg);
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	__grp_send_query_and_rexmit(brmctx, pmctx, pg);
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci	return changed;
249562306a36Sopenharmony_ci}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_cistatic bool br_multicast_toin(struct net_bridge_mcast *brmctx,
249862306a36Sopenharmony_ci			      struct net_bridge_mcast_port *pmctx,
249962306a36Sopenharmony_ci			      struct net_bridge_port_group *pg, void *h_addr,
250062306a36Sopenharmony_ci			      void *srcs, u32 nsrcs, size_t addr_size,
250162306a36Sopenharmony_ci			      int grec_type)
250262306a36Sopenharmony_ci{
250362306a36Sopenharmony_ci	bool changed = false;
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	switch (pg->filter_mode) {
250662306a36Sopenharmony_ci	case MCAST_INCLUDE:
250762306a36Sopenharmony_ci		changed = __grp_src_toin_incl(brmctx, pmctx, pg, h_addr, srcs,
250862306a36Sopenharmony_ci					      nsrcs, addr_size, grec_type);
250962306a36Sopenharmony_ci		break;
251062306a36Sopenharmony_ci	case MCAST_EXCLUDE:
251162306a36Sopenharmony_ci		changed = __grp_src_toin_excl(brmctx, pmctx, pg, h_addr, srcs,
251262306a36Sopenharmony_ci					      nsrcs, addr_size, grec_type);
251362306a36Sopenharmony_ci		break;
251462306a36Sopenharmony_ci	}
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	if (br_multicast_eht_should_del_pg(pg)) {
251762306a36Sopenharmony_ci		pg->flags |= MDB_PG_FLAGS_FAST_LEAVE;
251862306a36Sopenharmony_ci		br_multicast_find_del_pg(pg->key.port->br, pg);
251962306a36Sopenharmony_ci		/* a notification has already been sent and we shouldn't
252062306a36Sopenharmony_ci		 * access pg after the delete so we have to return false
252162306a36Sopenharmony_ci		 */
252262306a36Sopenharmony_ci		changed = false;
252362306a36Sopenharmony_ci	}
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	return changed;
252662306a36Sopenharmony_ci}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci/* State          Msg type      New state                Actions
252962306a36Sopenharmony_ci * INCLUDE (A)    TO_EX (B)     EXCLUDE (A*B,B-A)        (B-A)=0
253062306a36Sopenharmony_ci *                                                       Delete (A-B)
253162306a36Sopenharmony_ci *                                                       Send Q(G,A*B)
253262306a36Sopenharmony_ci *                                                       Group Timer=GMI
253362306a36Sopenharmony_ci */
253462306a36Sopenharmony_cistatic void __grp_src_toex_incl(struct net_bridge_mcast *brmctx,
253562306a36Sopenharmony_ci				struct net_bridge_mcast_port *pmctx,
253662306a36Sopenharmony_ci				struct net_bridge_port_group *pg, void *h_addr,
253762306a36Sopenharmony_ci				void *srcs, u32 nsrcs, size_t addr_size,
253862306a36Sopenharmony_ci				int grec_type)
253962306a36Sopenharmony_ci{
254062306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
254162306a36Sopenharmony_ci	u32 src_idx, to_send = 0;
254262306a36Sopenharmony_ci	struct br_ip src_ip;
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
254562306a36Sopenharmony_ci		ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE;
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
254862306a36Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
254962306a36Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
255062306a36Sopenharmony_ci		memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
255162306a36Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
255262306a36Sopenharmony_ci		if (ent) {
255362306a36Sopenharmony_ci			ent->flags = (ent->flags & ~BR_SGRP_F_DELETE) |
255462306a36Sopenharmony_ci				     BR_SGRP_F_SEND;
255562306a36Sopenharmony_ci			to_send++;
255662306a36Sopenharmony_ci		} else {
255762306a36Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
255862306a36Sopenharmony_ci		}
255962306a36Sopenharmony_ci		if (ent)
256062306a36Sopenharmony_ci			br_multicast_fwd_src_handle(ent);
256162306a36Sopenharmony_ci	}
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
256462306a36Sopenharmony_ci				grec_type);
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	__grp_src_delete_marked(pg);
256762306a36Sopenharmony_ci	if (to_send)
256862306a36Sopenharmony_ci		__grp_src_query_marked_and_rexmit(brmctx, pmctx, pg);
256962306a36Sopenharmony_ci}
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci/* State          Msg type      New state                Actions
257262306a36Sopenharmony_ci * EXCLUDE (X,Y)  TO_EX (A)     EXCLUDE (A-Y,Y*A)        (A-X-Y)=Group Timer
257362306a36Sopenharmony_ci *                                                       Delete (X-A)
257462306a36Sopenharmony_ci *                                                       Delete (Y-A)
257562306a36Sopenharmony_ci *                                                       Send Q(G,A-Y)
257662306a36Sopenharmony_ci *                                                       Group Timer=GMI
257762306a36Sopenharmony_ci */
257862306a36Sopenharmony_cistatic bool __grp_src_toex_excl(struct net_bridge_mcast *brmctx,
257962306a36Sopenharmony_ci				struct net_bridge_mcast_port *pmctx,
258062306a36Sopenharmony_ci				struct net_bridge_port_group *pg, void *h_addr,
258162306a36Sopenharmony_ci				void *srcs, u32 nsrcs, size_t addr_size,
258262306a36Sopenharmony_ci				int grec_type)
258362306a36Sopenharmony_ci{
258462306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
258562306a36Sopenharmony_ci	u32 src_idx, to_send = 0;
258662306a36Sopenharmony_ci	bool changed = false;
258762306a36Sopenharmony_ci	struct br_ip src_ip;
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
259062306a36Sopenharmony_ci		ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE;
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
259362306a36Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
259462306a36Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
259562306a36Sopenharmony_ci		memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
259662306a36Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
259762306a36Sopenharmony_ci		if (ent) {
259862306a36Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_DELETE;
259962306a36Sopenharmony_ci		} else {
260062306a36Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
260162306a36Sopenharmony_ci			if (ent) {
260262306a36Sopenharmony_ci				__grp_src_mod_timer(ent, pg->timer.expires);
260362306a36Sopenharmony_ci				changed = true;
260462306a36Sopenharmony_ci			}
260562306a36Sopenharmony_ci		}
260662306a36Sopenharmony_ci		if (ent && timer_pending(&ent->timer)) {
260762306a36Sopenharmony_ci			ent->flags |= BR_SGRP_F_SEND;
260862306a36Sopenharmony_ci			to_send++;
260962306a36Sopenharmony_ci		}
261062306a36Sopenharmony_ci	}
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
261362306a36Sopenharmony_ci				    grec_type))
261462306a36Sopenharmony_ci		changed = true;
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	if (__grp_src_delete_marked(pg))
261762306a36Sopenharmony_ci		changed = true;
261862306a36Sopenharmony_ci	if (to_send)
261962306a36Sopenharmony_ci		__grp_src_query_marked_and_rexmit(brmctx, pmctx, pg);
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci	return changed;
262262306a36Sopenharmony_ci}
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_cistatic bool br_multicast_toex(struct net_bridge_mcast *brmctx,
262562306a36Sopenharmony_ci			      struct net_bridge_mcast_port *pmctx,
262662306a36Sopenharmony_ci			      struct net_bridge_port_group *pg, void *h_addr,
262762306a36Sopenharmony_ci			      void *srcs, u32 nsrcs, size_t addr_size,
262862306a36Sopenharmony_ci			      int grec_type)
262962306a36Sopenharmony_ci{
263062306a36Sopenharmony_ci	bool changed = false;
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci	switch (pg->filter_mode) {
263362306a36Sopenharmony_ci	case MCAST_INCLUDE:
263462306a36Sopenharmony_ci		__grp_src_toex_incl(brmctx, pmctx, pg, h_addr, srcs, nsrcs,
263562306a36Sopenharmony_ci				    addr_size, grec_type);
263662306a36Sopenharmony_ci		br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE);
263762306a36Sopenharmony_ci		changed = true;
263862306a36Sopenharmony_ci		break;
263962306a36Sopenharmony_ci	case MCAST_EXCLUDE:
264062306a36Sopenharmony_ci		changed = __grp_src_toex_excl(brmctx, pmctx, pg, h_addr, srcs,
264162306a36Sopenharmony_ci					      nsrcs, addr_size, grec_type);
264262306a36Sopenharmony_ci		break;
264362306a36Sopenharmony_ci	}
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	pg->filter_mode = MCAST_EXCLUDE;
264662306a36Sopenharmony_ci	mod_timer(&pg->timer, jiffies + br_multicast_gmi(brmctx));
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	return changed;
264962306a36Sopenharmony_ci}
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci/* State          Msg type      New state                Actions
265262306a36Sopenharmony_ci * INCLUDE (A)    BLOCK (B)     INCLUDE (A)              Send Q(G,A*B)
265362306a36Sopenharmony_ci */
265462306a36Sopenharmony_cistatic bool __grp_src_block_incl(struct net_bridge_mcast *brmctx,
265562306a36Sopenharmony_ci				 struct net_bridge_mcast_port *pmctx,
265662306a36Sopenharmony_ci				 struct net_bridge_port_group *pg, void *h_addr,
265762306a36Sopenharmony_ci				 void *srcs, u32 nsrcs, size_t addr_size, int grec_type)
265862306a36Sopenharmony_ci{
265962306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
266062306a36Sopenharmony_ci	u32 src_idx, to_send = 0;
266162306a36Sopenharmony_ci	bool changed = false;
266262306a36Sopenharmony_ci	struct br_ip src_ip;
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
266562306a36Sopenharmony_ci		ent->flags &= ~BR_SGRP_F_SEND;
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
266862306a36Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
266962306a36Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
267062306a36Sopenharmony_ci		memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
267162306a36Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
267262306a36Sopenharmony_ci		if (ent) {
267362306a36Sopenharmony_ci			ent->flags |= BR_SGRP_F_SEND;
267462306a36Sopenharmony_ci			to_send++;
267562306a36Sopenharmony_ci		}
267662306a36Sopenharmony_ci	}
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
267962306a36Sopenharmony_ci				    grec_type))
268062306a36Sopenharmony_ci		changed = true;
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	if (to_send)
268362306a36Sopenharmony_ci		__grp_src_query_marked_and_rexmit(brmctx, pmctx, pg);
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	return changed;
268662306a36Sopenharmony_ci}
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci/* State          Msg type      New state                Actions
268962306a36Sopenharmony_ci * EXCLUDE (X,Y)  BLOCK (A)     EXCLUDE (X+(A-Y),Y)      (A-X-Y)=Group Timer
269062306a36Sopenharmony_ci *                                                       Send Q(G,A-Y)
269162306a36Sopenharmony_ci */
269262306a36Sopenharmony_cistatic bool __grp_src_block_excl(struct net_bridge_mcast *brmctx,
269362306a36Sopenharmony_ci				 struct net_bridge_mcast_port *pmctx,
269462306a36Sopenharmony_ci				 struct net_bridge_port_group *pg, void *h_addr,
269562306a36Sopenharmony_ci				 void *srcs, u32 nsrcs, size_t addr_size, int grec_type)
269662306a36Sopenharmony_ci{
269762306a36Sopenharmony_ci	struct net_bridge_group_src *ent;
269862306a36Sopenharmony_ci	u32 src_idx, to_send = 0;
269962306a36Sopenharmony_ci	bool changed = false;
270062306a36Sopenharmony_ci	struct br_ip src_ip;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
270362306a36Sopenharmony_ci		ent->flags &= ~BR_SGRP_F_SEND;
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
270662306a36Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
270762306a36Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
270862306a36Sopenharmony_ci		memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
270962306a36Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
271062306a36Sopenharmony_ci		if (!ent) {
271162306a36Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
271262306a36Sopenharmony_ci			if (ent) {
271362306a36Sopenharmony_ci				__grp_src_mod_timer(ent, pg->timer.expires);
271462306a36Sopenharmony_ci				changed = true;
271562306a36Sopenharmony_ci			}
271662306a36Sopenharmony_ci		}
271762306a36Sopenharmony_ci		if (ent && timer_pending(&ent->timer)) {
271862306a36Sopenharmony_ci			ent->flags |= BR_SGRP_F_SEND;
271962306a36Sopenharmony_ci			to_send++;
272062306a36Sopenharmony_ci		}
272162306a36Sopenharmony_ci	}
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	if (br_multicast_eht_handle(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
272462306a36Sopenharmony_ci				    grec_type))
272562306a36Sopenharmony_ci		changed = true;
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	if (to_send)
272862306a36Sopenharmony_ci		__grp_src_query_marked_and_rexmit(brmctx, pmctx, pg);
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	return changed;
273162306a36Sopenharmony_ci}
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_cistatic bool br_multicast_block(struct net_bridge_mcast *brmctx,
273462306a36Sopenharmony_ci			       struct net_bridge_mcast_port *pmctx,
273562306a36Sopenharmony_ci			       struct net_bridge_port_group *pg, void *h_addr,
273662306a36Sopenharmony_ci			       void *srcs, u32 nsrcs, size_t addr_size, int grec_type)
273762306a36Sopenharmony_ci{
273862306a36Sopenharmony_ci	bool changed = false;
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	switch (pg->filter_mode) {
274162306a36Sopenharmony_ci	case MCAST_INCLUDE:
274262306a36Sopenharmony_ci		changed = __grp_src_block_incl(brmctx, pmctx, pg, h_addr, srcs,
274362306a36Sopenharmony_ci					       nsrcs, addr_size, grec_type);
274462306a36Sopenharmony_ci		break;
274562306a36Sopenharmony_ci	case MCAST_EXCLUDE:
274662306a36Sopenharmony_ci		changed = __grp_src_block_excl(brmctx, pmctx, pg, h_addr, srcs,
274762306a36Sopenharmony_ci					       nsrcs, addr_size, grec_type);
274862306a36Sopenharmony_ci		break;
274962306a36Sopenharmony_ci	}
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	if ((pg->filter_mode == MCAST_INCLUDE && hlist_empty(&pg->src_list)) ||
275262306a36Sopenharmony_ci	    br_multicast_eht_should_del_pg(pg)) {
275362306a36Sopenharmony_ci		if (br_multicast_eht_should_del_pg(pg))
275462306a36Sopenharmony_ci			pg->flags |= MDB_PG_FLAGS_FAST_LEAVE;
275562306a36Sopenharmony_ci		br_multicast_find_del_pg(pg->key.port->br, pg);
275662306a36Sopenharmony_ci		/* a notification has already been sent and we shouldn't
275762306a36Sopenharmony_ci		 * access pg after the delete so we have to return false
275862306a36Sopenharmony_ci		 */
275962306a36Sopenharmony_ci		changed = false;
276062306a36Sopenharmony_ci	}
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	return changed;
276362306a36Sopenharmony_ci}
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_cistatic struct net_bridge_port_group *
276662306a36Sopenharmony_cibr_multicast_find_port(struct net_bridge_mdb_entry *mp,
276762306a36Sopenharmony_ci		       struct net_bridge_port *p,
276862306a36Sopenharmony_ci		       const unsigned char *src)
276962306a36Sopenharmony_ci{
277062306a36Sopenharmony_ci	struct net_bridge *br __maybe_unused = mp->br;
277162306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci	for (pg = mlock_dereference(mp->ports, br);
277462306a36Sopenharmony_ci	     pg;
277562306a36Sopenharmony_ci	     pg = mlock_dereference(pg->next, br))
277662306a36Sopenharmony_ci		if (br_port_group_equal(pg, p, src))
277762306a36Sopenharmony_ci			return pg;
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci	return NULL;
278062306a36Sopenharmony_ci}
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_cistatic int br_ip4_multicast_igmp3_report(struct net_bridge_mcast *brmctx,
278362306a36Sopenharmony_ci					 struct net_bridge_mcast_port *pmctx,
278462306a36Sopenharmony_ci					 struct sk_buff *skb,
278562306a36Sopenharmony_ci					 u16 vid)
278662306a36Sopenharmony_ci{
278762306a36Sopenharmony_ci	bool igmpv2 = brmctx->multicast_igmp_version == 2;
278862306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mdst;
278962306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
279062306a36Sopenharmony_ci	const unsigned char *src;
279162306a36Sopenharmony_ci	struct igmpv3_report *ih;
279262306a36Sopenharmony_ci	struct igmpv3_grec *grec;
279362306a36Sopenharmony_ci	int i, len, num, type;
279462306a36Sopenharmony_ci	__be32 group, *h_addr;
279562306a36Sopenharmony_ci	bool changed = false;
279662306a36Sopenharmony_ci	int err = 0;
279762306a36Sopenharmony_ci	u16 nsrcs;
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	ih = igmpv3_report_hdr(skb);
280062306a36Sopenharmony_ci	num = ntohs(ih->ngrec);
280162306a36Sopenharmony_ci	len = skb_transport_offset(skb) + sizeof(*ih);
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
280462306a36Sopenharmony_ci		len += sizeof(*grec);
280562306a36Sopenharmony_ci		if (!ip_mc_may_pull(skb, len))
280662306a36Sopenharmony_ci			return -EINVAL;
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci		grec = (void *)(skb->data + len - sizeof(*grec));
280962306a36Sopenharmony_ci		group = grec->grec_mca;
281062306a36Sopenharmony_ci		type = grec->grec_type;
281162306a36Sopenharmony_ci		nsrcs = ntohs(grec->grec_nsrcs);
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci		len += nsrcs * 4;
281462306a36Sopenharmony_ci		if (!ip_mc_may_pull(skb, len))
281562306a36Sopenharmony_ci			return -EINVAL;
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci		switch (type) {
281862306a36Sopenharmony_ci		case IGMPV3_MODE_IS_INCLUDE:
281962306a36Sopenharmony_ci		case IGMPV3_MODE_IS_EXCLUDE:
282062306a36Sopenharmony_ci		case IGMPV3_CHANGE_TO_INCLUDE:
282162306a36Sopenharmony_ci		case IGMPV3_CHANGE_TO_EXCLUDE:
282262306a36Sopenharmony_ci		case IGMPV3_ALLOW_NEW_SOURCES:
282362306a36Sopenharmony_ci		case IGMPV3_BLOCK_OLD_SOURCES:
282462306a36Sopenharmony_ci			break;
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci		default:
282762306a36Sopenharmony_ci			continue;
282862306a36Sopenharmony_ci		}
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci		src = eth_hdr(skb)->h_source;
283162306a36Sopenharmony_ci		if (nsrcs == 0 &&
283262306a36Sopenharmony_ci		    (type == IGMPV3_CHANGE_TO_INCLUDE ||
283362306a36Sopenharmony_ci		     type == IGMPV3_MODE_IS_INCLUDE)) {
283462306a36Sopenharmony_ci			if (!pmctx || igmpv2) {
283562306a36Sopenharmony_ci				br_ip4_multicast_leave_group(brmctx, pmctx,
283662306a36Sopenharmony_ci							     group, vid, src);
283762306a36Sopenharmony_ci				continue;
283862306a36Sopenharmony_ci			}
283962306a36Sopenharmony_ci		} else {
284062306a36Sopenharmony_ci			err = br_ip4_multicast_add_group(brmctx, pmctx, group,
284162306a36Sopenharmony_ci							 vid, src, igmpv2);
284262306a36Sopenharmony_ci			if (err)
284362306a36Sopenharmony_ci				break;
284462306a36Sopenharmony_ci		}
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci		if (!pmctx || igmpv2)
284762306a36Sopenharmony_ci			continue;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci		spin_lock(&brmctx->br->multicast_lock);
285062306a36Sopenharmony_ci		if (!br_multicast_ctx_should_use(brmctx, pmctx))
285162306a36Sopenharmony_ci			goto unlock_continue;
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci		mdst = br_mdb_ip4_get(brmctx->br, group, vid);
285462306a36Sopenharmony_ci		if (!mdst)
285562306a36Sopenharmony_ci			goto unlock_continue;
285662306a36Sopenharmony_ci		pg = br_multicast_find_port(mdst, pmctx->port, src);
285762306a36Sopenharmony_ci		if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT))
285862306a36Sopenharmony_ci			goto unlock_continue;
285962306a36Sopenharmony_ci		/* reload grec and host addr */
286062306a36Sopenharmony_ci		grec = (void *)(skb->data + len - sizeof(*grec) - (nsrcs * 4));
286162306a36Sopenharmony_ci		h_addr = &ip_hdr(skb)->saddr;
286262306a36Sopenharmony_ci		switch (type) {
286362306a36Sopenharmony_ci		case IGMPV3_ALLOW_NEW_SOURCES:
286462306a36Sopenharmony_ci			changed = br_multicast_isinc_allow(brmctx, pg, h_addr,
286562306a36Sopenharmony_ci							   grec->grec_src,
286662306a36Sopenharmony_ci							   nsrcs, sizeof(__be32), type);
286762306a36Sopenharmony_ci			break;
286862306a36Sopenharmony_ci		case IGMPV3_MODE_IS_INCLUDE:
286962306a36Sopenharmony_ci			changed = br_multicast_isinc_allow(brmctx, pg, h_addr,
287062306a36Sopenharmony_ci							   grec->grec_src,
287162306a36Sopenharmony_ci							   nsrcs, sizeof(__be32), type);
287262306a36Sopenharmony_ci			break;
287362306a36Sopenharmony_ci		case IGMPV3_MODE_IS_EXCLUDE:
287462306a36Sopenharmony_ci			changed = br_multicast_isexc(brmctx, pg, h_addr,
287562306a36Sopenharmony_ci						     grec->grec_src,
287662306a36Sopenharmony_ci						     nsrcs, sizeof(__be32), type);
287762306a36Sopenharmony_ci			break;
287862306a36Sopenharmony_ci		case IGMPV3_CHANGE_TO_INCLUDE:
287962306a36Sopenharmony_ci			changed = br_multicast_toin(brmctx, pmctx, pg, h_addr,
288062306a36Sopenharmony_ci						    grec->grec_src,
288162306a36Sopenharmony_ci						    nsrcs, sizeof(__be32), type);
288262306a36Sopenharmony_ci			break;
288362306a36Sopenharmony_ci		case IGMPV3_CHANGE_TO_EXCLUDE:
288462306a36Sopenharmony_ci			changed = br_multicast_toex(brmctx, pmctx, pg, h_addr,
288562306a36Sopenharmony_ci						    grec->grec_src,
288662306a36Sopenharmony_ci						    nsrcs, sizeof(__be32), type);
288762306a36Sopenharmony_ci			break;
288862306a36Sopenharmony_ci		case IGMPV3_BLOCK_OLD_SOURCES:
288962306a36Sopenharmony_ci			changed = br_multicast_block(brmctx, pmctx, pg, h_addr,
289062306a36Sopenharmony_ci						     grec->grec_src,
289162306a36Sopenharmony_ci						     nsrcs, sizeof(__be32), type);
289262306a36Sopenharmony_ci			break;
289362306a36Sopenharmony_ci		}
289462306a36Sopenharmony_ci		if (changed)
289562306a36Sopenharmony_ci			br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_NEWMDB);
289662306a36Sopenharmony_ciunlock_continue:
289762306a36Sopenharmony_ci		spin_unlock(&brmctx->br->multicast_lock);
289862306a36Sopenharmony_ci	}
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_ci	return err;
290162306a36Sopenharmony_ci}
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
290462306a36Sopenharmony_cistatic int br_ip6_multicast_mld2_report(struct net_bridge_mcast *brmctx,
290562306a36Sopenharmony_ci					struct net_bridge_mcast_port *pmctx,
290662306a36Sopenharmony_ci					struct sk_buff *skb,
290762306a36Sopenharmony_ci					u16 vid)
290862306a36Sopenharmony_ci{
290962306a36Sopenharmony_ci	bool mldv1 = brmctx->multicast_mld_version == 1;
291062306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mdst;
291162306a36Sopenharmony_ci	struct net_bridge_port_group *pg;
291262306a36Sopenharmony_ci	unsigned int nsrcs_offset;
291362306a36Sopenharmony_ci	struct mld2_report *mld2r;
291462306a36Sopenharmony_ci	const unsigned char *src;
291562306a36Sopenharmony_ci	struct in6_addr *h_addr;
291662306a36Sopenharmony_ci	struct mld2_grec *grec;
291762306a36Sopenharmony_ci	unsigned int grec_len;
291862306a36Sopenharmony_ci	bool changed = false;
291962306a36Sopenharmony_ci	int i, len, num;
292062306a36Sopenharmony_ci	int err = 0;
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_ci	if (!ipv6_mc_may_pull(skb, sizeof(*mld2r)))
292362306a36Sopenharmony_ci		return -EINVAL;
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_ci	mld2r = (struct mld2_report *)icmp6_hdr(skb);
292662306a36Sopenharmony_ci	num = ntohs(mld2r->mld2r_ngrec);
292762306a36Sopenharmony_ci	len = skb_transport_offset(skb) + sizeof(*mld2r);
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
293062306a36Sopenharmony_ci		__be16 *_nsrcs, __nsrcs;
293162306a36Sopenharmony_ci		u16 nsrcs;
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci		nsrcs_offset = len + offsetof(struct mld2_grec, grec_nsrcs);
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci		if (skb_transport_offset(skb) + ipv6_transport_len(skb) <
293662306a36Sopenharmony_ci		    nsrcs_offset + sizeof(__nsrcs))
293762306a36Sopenharmony_ci			return -EINVAL;
293862306a36Sopenharmony_ci
293962306a36Sopenharmony_ci		_nsrcs = skb_header_pointer(skb, nsrcs_offset,
294062306a36Sopenharmony_ci					    sizeof(__nsrcs), &__nsrcs);
294162306a36Sopenharmony_ci		if (!_nsrcs)
294262306a36Sopenharmony_ci			return -EINVAL;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci		nsrcs = ntohs(*_nsrcs);
294562306a36Sopenharmony_ci		grec_len = struct_size(grec, grec_src, nsrcs);
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci		if (!ipv6_mc_may_pull(skb, len + grec_len))
294862306a36Sopenharmony_ci			return -EINVAL;
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci		grec = (struct mld2_grec *)(skb->data + len);
295162306a36Sopenharmony_ci		len += grec_len;
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci		switch (grec->grec_type) {
295462306a36Sopenharmony_ci		case MLD2_MODE_IS_INCLUDE:
295562306a36Sopenharmony_ci		case MLD2_MODE_IS_EXCLUDE:
295662306a36Sopenharmony_ci		case MLD2_CHANGE_TO_INCLUDE:
295762306a36Sopenharmony_ci		case MLD2_CHANGE_TO_EXCLUDE:
295862306a36Sopenharmony_ci		case MLD2_ALLOW_NEW_SOURCES:
295962306a36Sopenharmony_ci		case MLD2_BLOCK_OLD_SOURCES:
296062306a36Sopenharmony_ci			break;
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_ci		default:
296362306a36Sopenharmony_ci			continue;
296462306a36Sopenharmony_ci		}
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci		src = eth_hdr(skb)->h_source;
296762306a36Sopenharmony_ci		if ((grec->grec_type == MLD2_CHANGE_TO_INCLUDE ||
296862306a36Sopenharmony_ci		     grec->grec_type == MLD2_MODE_IS_INCLUDE) &&
296962306a36Sopenharmony_ci		    nsrcs == 0) {
297062306a36Sopenharmony_ci			if (!pmctx || mldv1) {
297162306a36Sopenharmony_ci				br_ip6_multicast_leave_group(brmctx, pmctx,
297262306a36Sopenharmony_ci							     &grec->grec_mca,
297362306a36Sopenharmony_ci							     vid, src);
297462306a36Sopenharmony_ci				continue;
297562306a36Sopenharmony_ci			}
297662306a36Sopenharmony_ci		} else {
297762306a36Sopenharmony_ci			err = br_ip6_multicast_add_group(brmctx, pmctx,
297862306a36Sopenharmony_ci							 &grec->grec_mca, vid,
297962306a36Sopenharmony_ci							 src, mldv1);
298062306a36Sopenharmony_ci			if (err)
298162306a36Sopenharmony_ci				break;
298262306a36Sopenharmony_ci		}
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci		if (!pmctx || mldv1)
298562306a36Sopenharmony_ci			continue;
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci		spin_lock(&brmctx->br->multicast_lock);
298862306a36Sopenharmony_ci		if (!br_multicast_ctx_should_use(brmctx, pmctx))
298962306a36Sopenharmony_ci			goto unlock_continue;
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci		mdst = br_mdb_ip6_get(brmctx->br, &grec->grec_mca, vid);
299262306a36Sopenharmony_ci		if (!mdst)
299362306a36Sopenharmony_ci			goto unlock_continue;
299462306a36Sopenharmony_ci		pg = br_multicast_find_port(mdst, pmctx->port, src);
299562306a36Sopenharmony_ci		if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT))
299662306a36Sopenharmony_ci			goto unlock_continue;
299762306a36Sopenharmony_ci		h_addr = &ipv6_hdr(skb)->saddr;
299862306a36Sopenharmony_ci		switch (grec->grec_type) {
299962306a36Sopenharmony_ci		case MLD2_ALLOW_NEW_SOURCES:
300062306a36Sopenharmony_ci			changed = br_multicast_isinc_allow(brmctx, pg, h_addr,
300162306a36Sopenharmony_ci							   grec->grec_src, nsrcs,
300262306a36Sopenharmony_ci							   sizeof(struct in6_addr),
300362306a36Sopenharmony_ci							   grec->grec_type);
300462306a36Sopenharmony_ci			break;
300562306a36Sopenharmony_ci		case MLD2_MODE_IS_INCLUDE:
300662306a36Sopenharmony_ci			changed = br_multicast_isinc_allow(brmctx, pg, h_addr,
300762306a36Sopenharmony_ci							   grec->grec_src, nsrcs,
300862306a36Sopenharmony_ci							   sizeof(struct in6_addr),
300962306a36Sopenharmony_ci							   grec->grec_type);
301062306a36Sopenharmony_ci			break;
301162306a36Sopenharmony_ci		case MLD2_MODE_IS_EXCLUDE:
301262306a36Sopenharmony_ci			changed = br_multicast_isexc(brmctx, pg, h_addr,
301362306a36Sopenharmony_ci						     grec->grec_src, nsrcs,
301462306a36Sopenharmony_ci						     sizeof(struct in6_addr),
301562306a36Sopenharmony_ci						     grec->grec_type);
301662306a36Sopenharmony_ci			break;
301762306a36Sopenharmony_ci		case MLD2_CHANGE_TO_INCLUDE:
301862306a36Sopenharmony_ci			changed = br_multicast_toin(brmctx, pmctx, pg, h_addr,
301962306a36Sopenharmony_ci						    grec->grec_src, nsrcs,
302062306a36Sopenharmony_ci						    sizeof(struct in6_addr),
302162306a36Sopenharmony_ci						    grec->grec_type);
302262306a36Sopenharmony_ci			break;
302362306a36Sopenharmony_ci		case MLD2_CHANGE_TO_EXCLUDE:
302462306a36Sopenharmony_ci			changed = br_multicast_toex(brmctx, pmctx, pg, h_addr,
302562306a36Sopenharmony_ci						    grec->grec_src, nsrcs,
302662306a36Sopenharmony_ci						    sizeof(struct in6_addr),
302762306a36Sopenharmony_ci						    grec->grec_type);
302862306a36Sopenharmony_ci			break;
302962306a36Sopenharmony_ci		case MLD2_BLOCK_OLD_SOURCES:
303062306a36Sopenharmony_ci			changed = br_multicast_block(brmctx, pmctx, pg, h_addr,
303162306a36Sopenharmony_ci						     grec->grec_src, nsrcs,
303262306a36Sopenharmony_ci						     sizeof(struct in6_addr),
303362306a36Sopenharmony_ci						     grec->grec_type);
303462306a36Sopenharmony_ci			break;
303562306a36Sopenharmony_ci		}
303662306a36Sopenharmony_ci		if (changed)
303762306a36Sopenharmony_ci			br_mdb_notify(brmctx->br->dev, mdst, pg, RTM_NEWMDB);
303862306a36Sopenharmony_ciunlock_continue:
303962306a36Sopenharmony_ci		spin_unlock(&brmctx->br->multicast_lock);
304062306a36Sopenharmony_ci	}
304162306a36Sopenharmony_ci
304262306a36Sopenharmony_ci	return err;
304362306a36Sopenharmony_ci}
304462306a36Sopenharmony_ci#endif
304562306a36Sopenharmony_ci
304662306a36Sopenharmony_cistatic bool br_multicast_select_querier(struct net_bridge_mcast *brmctx,
304762306a36Sopenharmony_ci					struct net_bridge_mcast_port *pmctx,
304862306a36Sopenharmony_ci					struct br_ip *saddr)
304962306a36Sopenharmony_ci{
305062306a36Sopenharmony_ci	int port_ifidx = pmctx ? pmctx->port->dev->ifindex : 0;
305162306a36Sopenharmony_ci	struct timer_list *own_timer, *other_timer;
305262306a36Sopenharmony_ci	struct bridge_mcast_querier *querier;
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci	switch (saddr->proto) {
305562306a36Sopenharmony_ci	case htons(ETH_P_IP):
305662306a36Sopenharmony_ci		querier = &brmctx->ip4_querier;
305762306a36Sopenharmony_ci		own_timer = &brmctx->ip4_own_query.timer;
305862306a36Sopenharmony_ci		other_timer = &brmctx->ip4_other_query.timer;
305962306a36Sopenharmony_ci		if (!querier->addr.src.ip4 ||
306062306a36Sopenharmony_ci		    ntohl(saddr->src.ip4) <= ntohl(querier->addr.src.ip4))
306162306a36Sopenharmony_ci			goto update;
306262306a36Sopenharmony_ci		break;
306362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
306462306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
306562306a36Sopenharmony_ci		querier = &brmctx->ip6_querier;
306662306a36Sopenharmony_ci		own_timer = &brmctx->ip6_own_query.timer;
306762306a36Sopenharmony_ci		other_timer = &brmctx->ip6_other_query.timer;
306862306a36Sopenharmony_ci		if (ipv6_addr_cmp(&saddr->src.ip6, &querier->addr.src.ip6) <= 0)
306962306a36Sopenharmony_ci			goto update;
307062306a36Sopenharmony_ci		break;
307162306a36Sopenharmony_ci#endif
307262306a36Sopenharmony_ci	default:
307362306a36Sopenharmony_ci		return false;
307462306a36Sopenharmony_ci	}
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci	if (!timer_pending(own_timer) && !timer_pending(other_timer))
307762306a36Sopenharmony_ci		goto update;
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci	return false;
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ciupdate:
308262306a36Sopenharmony_ci	br_multicast_update_querier(brmctx, querier, port_ifidx, saddr);
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci	return true;
308562306a36Sopenharmony_ci}
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_cistatic struct net_bridge_port *
308862306a36Sopenharmony_ci__br_multicast_get_querier_port(struct net_bridge *br,
308962306a36Sopenharmony_ci				const struct bridge_mcast_querier *querier)
309062306a36Sopenharmony_ci{
309162306a36Sopenharmony_ci	int port_ifidx = READ_ONCE(querier->port_ifidx);
309262306a36Sopenharmony_ci	struct net_bridge_port *p;
309362306a36Sopenharmony_ci	struct net_device *dev;
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	if (port_ifidx == 0)
309662306a36Sopenharmony_ci		return NULL;
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	dev = dev_get_by_index_rcu(dev_net(br->dev), port_ifidx);
309962306a36Sopenharmony_ci	if (!dev)
310062306a36Sopenharmony_ci		return NULL;
310162306a36Sopenharmony_ci	p = br_port_get_rtnl_rcu(dev);
310262306a36Sopenharmony_ci	if (!p || p->br != br)
310362306a36Sopenharmony_ci		return NULL;
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci	return p;
310662306a36Sopenharmony_ci}
310762306a36Sopenharmony_ci
310862306a36Sopenharmony_cisize_t br_multicast_querier_state_size(void)
310962306a36Sopenharmony_ci{
311062306a36Sopenharmony_ci	return nla_total_size(0) +		/* nest attribute */
311162306a36Sopenharmony_ci	       nla_total_size(sizeof(__be32)) + /* BRIDGE_QUERIER_IP_ADDRESS */
311262306a36Sopenharmony_ci	       nla_total_size(sizeof(int)) +    /* BRIDGE_QUERIER_IP_PORT */
311362306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* BRIDGE_QUERIER_IP_OTHER_TIMER */
311462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
311562306a36Sopenharmony_ci	       nla_total_size(sizeof(struct in6_addr)) + /* BRIDGE_QUERIER_IPV6_ADDRESS */
311662306a36Sopenharmony_ci	       nla_total_size(sizeof(int)) +		 /* BRIDGE_QUERIER_IPV6_PORT */
311762306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) +	 /* BRIDGE_QUERIER_IPV6_OTHER_TIMER */
311862306a36Sopenharmony_ci#endif
311962306a36Sopenharmony_ci	       0;
312062306a36Sopenharmony_ci}
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_ci/* protected by rtnl or rcu */
312362306a36Sopenharmony_ciint br_multicast_dump_querier_state(struct sk_buff *skb,
312462306a36Sopenharmony_ci				    const struct net_bridge_mcast *brmctx,
312562306a36Sopenharmony_ci				    int nest_attr)
312662306a36Sopenharmony_ci{
312762306a36Sopenharmony_ci	struct bridge_mcast_querier querier = {};
312862306a36Sopenharmony_ci	struct net_bridge_port *p;
312962306a36Sopenharmony_ci	struct nlattr *nest;
313062306a36Sopenharmony_ci
313162306a36Sopenharmony_ci	if (!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED) ||
313262306a36Sopenharmony_ci	    br_multicast_ctx_vlan_global_disabled(brmctx))
313362306a36Sopenharmony_ci		return 0;
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	nest = nla_nest_start(skb, nest_attr);
313662306a36Sopenharmony_ci	if (!nest)
313762306a36Sopenharmony_ci		return -EMSGSIZE;
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_ci	rcu_read_lock();
314062306a36Sopenharmony_ci	if (!brmctx->multicast_querier &&
314162306a36Sopenharmony_ci	    !timer_pending(&brmctx->ip4_other_query.timer))
314262306a36Sopenharmony_ci		goto out_v6;
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci	br_multicast_read_querier(&brmctx->ip4_querier, &querier);
314562306a36Sopenharmony_ci	if (nla_put_in_addr(skb, BRIDGE_QUERIER_IP_ADDRESS,
314662306a36Sopenharmony_ci			    querier.addr.src.ip4)) {
314762306a36Sopenharmony_ci		rcu_read_unlock();
314862306a36Sopenharmony_ci		goto out_err;
314962306a36Sopenharmony_ci	}
315062306a36Sopenharmony_ci
315162306a36Sopenharmony_ci	p = __br_multicast_get_querier_port(brmctx->br, &querier);
315262306a36Sopenharmony_ci	if (timer_pending(&brmctx->ip4_other_query.timer) &&
315362306a36Sopenharmony_ci	    (nla_put_u64_64bit(skb, BRIDGE_QUERIER_IP_OTHER_TIMER,
315462306a36Sopenharmony_ci			       br_timer_value(&brmctx->ip4_other_query.timer),
315562306a36Sopenharmony_ci			       BRIDGE_QUERIER_PAD) ||
315662306a36Sopenharmony_ci	     (p && nla_put_u32(skb, BRIDGE_QUERIER_IP_PORT, p->dev->ifindex)))) {
315762306a36Sopenharmony_ci		rcu_read_unlock();
315862306a36Sopenharmony_ci		goto out_err;
315962306a36Sopenharmony_ci	}
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ciout_v6:
316262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
316362306a36Sopenharmony_ci	if (!brmctx->multicast_querier &&
316462306a36Sopenharmony_ci	    !timer_pending(&brmctx->ip6_other_query.timer))
316562306a36Sopenharmony_ci		goto out;
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci	br_multicast_read_querier(&brmctx->ip6_querier, &querier);
316862306a36Sopenharmony_ci	if (nla_put_in6_addr(skb, BRIDGE_QUERIER_IPV6_ADDRESS,
316962306a36Sopenharmony_ci			     &querier.addr.src.ip6)) {
317062306a36Sopenharmony_ci		rcu_read_unlock();
317162306a36Sopenharmony_ci		goto out_err;
317262306a36Sopenharmony_ci	}
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci	p = __br_multicast_get_querier_port(brmctx->br, &querier);
317562306a36Sopenharmony_ci	if (timer_pending(&brmctx->ip6_other_query.timer) &&
317662306a36Sopenharmony_ci	    (nla_put_u64_64bit(skb, BRIDGE_QUERIER_IPV6_OTHER_TIMER,
317762306a36Sopenharmony_ci			       br_timer_value(&brmctx->ip6_other_query.timer),
317862306a36Sopenharmony_ci			       BRIDGE_QUERIER_PAD) ||
317962306a36Sopenharmony_ci	     (p && nla_put_u32(skb, BRIDGE_QUERIER_IPV6_PORT,
318062306a36Sopenharmony_ci			       p->dev->ifindex)))) {
318162306a36Sopenharmony_ci		rcu_read_unlock();
318262306a36Sopenharmony_ci		goto out_err;
318362306a36Sopenharmony_ci	}
318462306a36Sopenharmony_ciout:
318562306a36Sopenharmony_ci#endif
318662306a36Sopenharmony_ci	rcu_read_unlock();
318762306a36Sopenharmony_ci	nla_nest_end(skb, nest);
318862306a36Sopenharmony_ci	if (!nla_len(nest))
318962306a36Sopenharmony_ci		nla_nest_cancel(skb, nest);
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	return 0;
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ciout_err:
319462306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
319562306a36Sopenharmony_ci	return -EMSGSIZE;
319662306a36Sopenharmony_ci}
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_cistatic void
319962306a36Sopenharmony_cibr_multicast_update_query_timer(struct net_bridge_mcast *brmctx,
320062306a36Sopenharmony_ci				struct bridge_mcast_other_query *query,
320162306a36Sopenharmony_ci				unsigned long max_delay)
320262306a36Sopenharmony_ci{
320362306a36Sopenharmony_ci	if (!timer_pending(&query->timer))
320462306a36Sopenharmony_ci		mod_timer(&query->delay_timer, jiffies + max_delay);
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci	mod_timer(&query->timer, jiffies + brmctx->multicast_querier_interval);
320762306a36Sopenharmony_ci}
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_cistatic void br_port_mc_router_state_change(struct net_bridge_port *p,
321062306a36Sopenharmony_ci					   bool is_mc_router)
321162306a36Sopenharmony_ci{
321262306a36Sopenharmony_ci	struct switchdev_attr attr = {
321362306a36Sopenharmony_ci		.orig_dev = p->dev,
321462306a36Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_PORT_MROUTER,
321562306a36Sopenharmony_ci		.flags = SWITCHDEV_F_DEFER,
321662306a36Sopenharmony_ci		.u.mrouter = is_mc_router,
321762306a36Sopenharmony_ci	};
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci	switchdev_port_attr_set(p->dev, &attr, NULL);
322062306a36Sopenharmony_ci}
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_cistatic struct net_bridge_port *
322362306a36Sopenharmony_cibr_multicast_rport_from_node(struct net_bridge_mcast *brmctx,
322462306a36Sopenharmony_ci			     struct hlist_head *mc_router_list,
322562306a36Sopenharmony_ci			     struct hlist_node *rlist)
322662306a36Sopenharmony_ci{
322762306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx;
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
323062306a36Sopenharmony_ci	if (mc_router_list == &brmctx->ip6_mc_router_list)
323162306a36Sopenharmony_ci		pmctx = hlist_entry(rlist, struct net_bridge_mcast_port,
323262306a36Sopenharmony_ci				    ip6_rlist);
323362306a36Sopenharmony_ci	else
323462306a36Sopenharmony_ci#endif
323562306a36Sopenharmony_ci		pmctx = hlist_entry(rlist, struct net_bridge_mcast_port,
323662306a36Sopenharmony_ci				    ip4_rlist);
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	return pmctx->port;
323962306a36Sopenharmony_ci}
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_cistatic struct hlist_node *
324262306a36Sopenharmony_cibr_multicast_get_rport_slot(struct net_bridge_mcast *brmctx,
324362306a36Sopenharmony_ci			    struct net_bridge_port *port,
324462306a36Sopenharmony_ci			    struct hlist_head *mc_router_list)
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci{
324762306a36Sopenharmony_ci	struct hlist_node *slot = NULL;
324862306a36Sopenharmony_ci	struct net_bridge_port *p;
324962306a36Sopenharmony_ci	struct hlist_node *rlist;
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ci	hlist_for_each(rlist, mc_router_list) {
325262306a36Sopenharmony_ci		p = br_multicast_rport_from_node(brmctx, mc_router_list, rlist);
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci		if ((unsigned long)port >= (unsigned long)p)
325562306a36Sopenharmony_ci			break;
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci		slot = rlist;
325862306a36Sopenharmony_ci	}
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci	return slot;
326162306a36Sopenharmony_ci}
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_cistatic bool br_multicast_no_router_otherpf(struct net_bridge_mcast_port *pmctx,
326462306a36Sopenharmony_ci					   struct hlist_node *rnode)
326562306a36Sopenharmony_ci{
326662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
326762306a36Sopenharmony_ci	if (rnode != &pmctx->ip6_rlist)
326862306a36Sopenharmony_ci		return hlist_unhashed(&pmctx->ip6_rlist);
326962306a36Sopenharmony_ci	else
327062306a36Sopenharmony_ci		return hlist_unhashed(&pmctx->ip4_rlist);
327162306a36Sopenharmony_ci#else
327262306a36Sopenharmony_ci	return true;
327362306a36Sopenharmony_ci#endif
327462306a36Sopenharmony_ci}
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci/* Add port to router_list
327762306a36Sopenharmony_ci *  list is maintained ordered by pointer value
327862306a36Sopenharmony_ci *  and locked by br->multicast_lock and RCU
327962306a36Sopenharmony_ci */
328062306a36Sopenharmony_cistatic void br_multicast_add_router(struct net_bridge_mcast *brmctx,
328162306a36Sopenharmony_ci				    struct net_bridge_mcast_port *pmctx,
328262306a36Sopenharmony_ci				    struct hlist_node *rlist,
328362306a36Sopenharmony_ci				    struct hlist_head *mc_router_list)
328462306a36Sopenharmony_ci{
328562306a36Sopenharmony_ci	struct hlist_node *slot;
328662306a36Sopenharmony_ci
328762306a36Sopenharmony_ci	if (!hlist_unhashed(rlist))
328862306a36Sopenharmony_ci		return;
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	slot = br_multicast_get_rport_slot(brmctx, pmctx->port, mc_router_list);
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci	if (slot)
329362306a36Sopenharmony_ci		hlist_add_behind_rcu(rlist, slot);
329462306a36Sopenharmony_ci	else
329562306a36Sopenharmony_ci		hlist_add_head_rcu(rlist, mc_router_list);
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_ci	/* For backwards compatibility for now, only notify if we
329862306a36Sopenharmony_ci	 * switched from no IPv4/IPv6 multicast router to a new
329962306a36Sopenharmony_ci	 * IPv4 or IPv6 multicast router.
330062306a36Sopenharmony_ci	 */
330162306a36Sopenharmony_ci	if (br_multicast_no_router_otherpf(pmctx, rlist)) {
330262306a36Sopenharmony_ci		br_rtr_notify(pmctx->port->br->dev, pmctx, RTM_NEWMDB);
330362306a36Sopenharmony_ci		br_port_mc_router_state_change(pmctx->port, true);
330462306a36Sopenharmony_ci	}
330562306a36Sopenharmony_ci}
330662306a36Sopenharmony_ci
330762306a36Sopenharmony_ci/* Add port to router_list
330862306a36Sopenharmony_ci *  list is maintained ordered by pointer value
330962306a36Sopenharmony_ci *  and locked by br->multicast_lock and RCU
331062306a36Sopenharmony_ci */
331162306a36Sopenharmony_cistatic void br_ip4_multicast_add_router(struct net_bridge_mcast *brmctx,
331262306a36Sopenharmony_ci					struct net_bridge_mcast_port *pmctx)
331362306a36Sopenharmony_ci{
331462306a36Sopenharmony_ci	br_multicast_add_router(brmctx, pmctx, &pmctx->ip4_rlist,
331562306a36Sopenharmony_ci				&brmctx->ip4_mc_router_list);
331662306a36Sopenharmony_ci}
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_ci/* Add port to router_list
331962306a36Sopenharmony_ci *  list is maintained ordered by pointer value
332062306a36Sopenharmony_ci *  and locked by br->multicast_lock and RCU
332162306a36Sopenharmony_ci */
332262306a36Sopenharmony_cistatic void br_ip6_multicast_add_router(struct net_bridge_mcast *brmctx,
332362306a36Sopenharmony_ci					struct net_bridge_mcast_port *pmctx)
332462306a36Sopenharmony_ci{
332562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
332662306a36Sopenharmony_ci	br_multicast_add_router(brmctx, pmctx, &pmctx->ip6_rlist,
332762306a36Sopenharmony_ci				&brmctx->ip6_mc_router_list);
332862306a36Sopenharmony_ci#endif
332962306a36Sopenharmony_ci}
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_cistatic void br_multicast_mark_router(struct net_bridge_mcast *brmctx,
333262306a36Sopenharmony_ci				     struct net_bridge_mcast_port *pmctx,
333362306a36Sopenharmony_ci				     struct timer_list *timer,
333462306a36Sopenharmony_ci				     struct hlist_node *rlist,
333562306a36Sopenharmony_ci				     struct hlist_head *mc_router_list)
333662306a36Sopenharmony_ci{
333762306a36Sopenharmony_ci	unsigned long now = jiffies;
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	if (!br_multicast_ctx_should_use(brmctx, pmctx))
334062306a36Sopenharmony_ci		return;
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci	if (!pmctx) {
334362306a36Sopenharmony_ci		if (brmctx->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) {
334462306a36Sopenharmony_ci			if (!br_ip4_multicast_is_router(brmctx) &&
334562306a36Sopenharmony_ci			    !br_ip6_multicast_is_router(brmctx))
334662306a36Sopenharmony_ci				br_mc_router_state_change(brmctx->br, true);
334762306a36Sopenharmony_ci			mod_timer(timer, now + brmctx->multicast_querier_interval);
334862306a36Sopenharmony_ci		}
334962306a36Sopenharmony_ci		return;
335062306a36Sopenharmony_ci	}
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	if (pmctx->multicast_router == MDB_RTR_TYPE_DISABLED ||
335362306a36Sopenharmony_ci	    pmctx->multicast_router == MDB_RTR_TYPE_PERM)
335462306a36Sopenharmony_ci		return;
335562306a36Sopenharmony_ci
335662306a36Sopenharmony_ci	br_multicast_add_router(brmctx, pmctx, rlist, mc_router_list);
335762306a36Sopenharmony_ci	mod_timer(timer, now + brmctx->multicast_querier_interval);
335862306a36Sopenharmony_ci}
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_cistatic void br_ip4_multicast_mark_router(struct net_bridge_mcast *brmctx,
336162306a36Sopenharmony_ci					 struct net_bridge_mcast_port *pmctx)
336262306a36Sopenharmony_ci{
336362306a36Sopenharmony_ci	struct timer_list *timer = &brmctx->ip4_mc_router_timer;
336462306a36Sopenharmony_ci	struct hlist_node *rlist = NULL;
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_ci	if (pmctx) {
336762306a36Sopenharmony_ci		timer = &pmctx->ip4_mc_router_timer;
336862306a36Sopenharmony_ci		rlist = &pmctx->ip4_rlist;
336962306a36Sopenharmony_ci	}
337062306a36Sopenharmony_ci
337162306a36Sopenharmony_ci	br_multicast_mark_router(brmctx, pmctx, timer, rlist,
337262306a36Sopenharmony_ci				 &brmctx->ip4_mc_router_list);
337362306a36Sopenharmony_ci}
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_cistatic void br_ip6_multicast_mark_router(struct net_bridge_mcast *brmctx,
337662306a36Sopenharmony_ci					 struct net_bridge_mcast_port *pmctx)
337762306a36Sopenharmony_ci{
337862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
337962306a36Sopenharmony_ci	struct timer_list *timer = &brmctx->ip6_mc_router_timer;
338062306a36Sopenharmony_ci	struct hlist_node *rlist = NULL;
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci	if (pmctx) {
338362306a36Sopenharmony_ci		timer = &pmctx->ip6_mc_router_timer;
338462306a36Sopenharmony_ci		rlist = &pmctx->ip6_rlist;
338562306a36Sopenharmony_ci	}
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_ci	br_multicast_mark_router(brmctx, pmctx, timer, rlist,
338862306a36Sopenharmony_ci				 &brmctx->ip6_mc_router_list);
338962306a36Sopenharmony_ci#endif
339062306a36Sopenharmony_ci}
339162306a36Sopenharmony_ci
339262306a36Sopenharmony_cistatic void
339362306a36Sopenharmony_cibr_ip4_multicast_query_received(struct net_bridge_mcast *brmctx,
339462306a36Sopenharmony_ci				struct net_bridge_mcast_port *pmctx,
339562306a36Sopenharmony_ci				struct bridge_mcast_other_query *query,
339662306a36Sopenharmony_ci				struct br_ip *saddr,
339762306a36Sopenharmony_ci				unsigned long max_delay)
339862306a36Sopenharmony_ci{
339962306a36Sopenharmony_ci	if (!br_multicast_select_querier(brmctx, pmctx, saddr))
340062306a36Sopenharmony_ci		return;
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci	br_multicast_update_query_timer(brmctx, query, max_delay);
340362306a36Sopenharmony_ci	br_ip4_multicast_mark_router(brmctx, pmctx);
340462306a36Sopenharmony_ci}
340562306a36Sopenharmony_ci
340662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
340762306a36Sopenharmony_cistatic void
340862306a36Sopenharmony_cibr_ip6_multicast_query_received(struct net_bridge_mcast *brmctx,
340962306a36Sopenharmony_ci				struct net_bridge_mcast_port *pmctx,
341062306a36Sopenharmony_ci				struct bridge_mcast_other_query *query,
341162306a36Sopenharmony_ci				struct br_ip *saddr,
341262306a36Sopenharmony_ci				unsigned long max_delay)
341362306a36Sopenharmony_ci{
341462306a36Sopenharmony_ci	if (!br_multicast_select_querier(brmctx, pmctx, saddr))
341562306a36Sopenharmony_ci		return;
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	br_multicast_update_query_timer(brmctx, query, max_delay);
341862306a36Sopenharmony_ci	br_ip6_multicast_mark_router(brmctx, pmctx);
341962306a36Sopenharmony_ci}
342062306a36Sopenharmony_ci#endif
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_cistatic void br_ip4_multicast_query(struct net_bridge_mcast *brmctx,
342362306a36Sopenharmony_ci				   struct net_bridge_mcast_port *pmctx,
342462306a36Sopenharmony_ci				   struct sk_buff *skb,
342562306a36Sopenharmony_ci				   u16 vid)
342662306a36Sopenharmony_ci{
342762306a36Sopenharmony_ci	unsigned int transport_len = ip_transport_len(skb);
342862306a36Sopenharmony_ci	const struct iphdr *iph = ip_hdr(skb);
342962306a36Sopenharmony_ci	struct igmphdr *ih = igmp_hdr(skb);
343062306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
343162306a36Sopenharmony_ci	struct igmpv3_query *ih3;
343262306a36Sopenharmony_ci	struct net_bridge_port_group *p;
343362306a36Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
343462306a36Sopenharmony_ci	struct br_ip saddr = {};
343562306a36Sopenharmony_ci	unsigned long max_delay;
343662306a36Sopenharmony_ci	unsigned long now = jiffies;
343762306a36Sopenharmony_ci	__be32 group;
343862306a36Sopenharmony_ci
343962306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
344062306a36Sopenharmony_ci	if (!br_multicast_ctx_should_use(brmctx, pmctx))
344162306a36Sopenharmony_ci		goto out;
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_ci	group = ih->group;
344462306a36Sopenharmony_ci
344562306a36Sopenharmony_ci	if (transport_len == sizeof(*ih)) {
344662306a36Sopenharmony_ci		max_delay = ih->code * (HZ / IGMP_TIMER_SCALE);
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci		if (!max_delay) {
344962306a36Sopenharmony_ci			max_delay = 10 * HZ;
345062306a36Sopenharmony_ci			group = 0;
345162306a36Sopenharmony_ci		}
345262306a36Sopenharmony_ci	} else if (transport_len >= sizeof(*ih3)) {
345362306a36Sopenharmony_ci		ih3 = igmpv3_query_hdr(skb);
345462306a36Sopenharmony_ci		if (ih3->nsrcs ||
345562306a36Sopenharmony_ci		    (brmctx->multicast_igmp_version == 3 && group &&
345662306a36Sopenharmony_ci		     ih3->suppress))
345762306a36Sopenharmony_ci			goto out;
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci		max_delay = ih3->code ?
346062306a36Sopenharmony_ci			    IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
346162306a36Sopenharmony_ci	} else {
346262306a36Sopenharmony_ci		goto out;
346362306a36Sopenharmony_ci	}
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci	if (!group) {
346662306a36Sopenharmony_ci		saddr.proto = htons(ETH_P_IP);
346762306a36Sopenharmony_ci		saddr.src.ip4 = iph->saddr;
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci		br_ip4_multicast_query_received(brmctx, pmctx,
347062306a36Sopenharmony_ci						&brmctx->ip4_other_query,
347162306a36Sopenharmony_ci						&saddr, max_delay);
347262306a36Sopenharmony_ci		goto out;
347362306a36Sopenharmony_ci	}
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_ci	mp = br_mdb_ip4_get(brmctx->br, group, vid);
347662306a36Sopenharmony_ci	if (!mp)
347762306a36Sopenharmony_ci		goto out;
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci	max_delay *= brmctx->multicast_last_member_count;
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ci	if (mp->host_joined &&
348262306a36Sopenharmony_ci	    (timer_pending(&mp->timer) ?
348362306a36Sopenharmony_ci	     time_after(mp->timer.expires, now + max_delay) :
348462306a36Sopenharmony_ci	     try_to_del_timer_sync(&mp->timer) >= 0))
348562306a36Sopenharmony_ci		mod_timer(&mp->timer, now + max_delay);
348662306a36Sopenharmony_ci
348762306a36Sopenharmony_ci	for (pp = &mp->ports;
348862306a36Sopenharmony_ci	     (p = mlock_dereference(*pp, brmctx->br)) != NULL;
348962306a36Sopenharmony_ci	     pp = &p->next) {
349062306a36Sopenharmony_ci		if (timer_pending(&p->timer) ?
349162306a36Sopenharmony_ci		    time_after(p->timer.expires, now + max_delay) :
349262306a36Sopenharmony_ci		    try_to_del_timer_sync(&p->timer) >= 0 &&
349362306a36Sopenharmony_ci		    (brmctx->multicast_igmp_version == 2 ||
349462306a36Sopenharmony_ci		     p->filter_mode == MCAST_EXCLUDE))
349562306a36Sopenharmony_ci			mod_timer(&p->timer, now + max_delay);
349662306a36Sopenharmony_ci	}
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ciout:
349962306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
350062306a36Sopenharmony_ci}
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
350362306a36Sopenharmony_cistatic int br_ip6_multicast_query(struct net_bridge_mcast *brmctx,
350462306a36Sopenharmony_ci				  struct net_bridge_mcast_port *pmctx,
350562306a36Sopenharmony_ci				  struct sk_buff *skb,
350662306a36Sopenharmony_ci				  u16 vid)
350762306a36Sopenharmony_ci{
350862306a36Sopenharmony_ci	unsigned int transport_len = ipv6_transport_len(skb);
350962306a36Sopenharmony_ci	struct mld_msg *mld;
351062306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
351162306a36Sopenharmony_ci	struct mld2_query *mld2q;
351262306a36Sopenharmony_ci	struct net_bridge_port_group *p;
351362306a36Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
351462306a36Sopenharmony_ci	struct br_ip saddr = {};
351562306a36Sopenharmony_ci	unsigned long max_delay;
351662306a36Sopenharmony_ci	unsigned long now = jiffies;
351762306a36Sopenharmony_ci	unsigned int offset = skb_transport_offset(skb);
351862306a36Sopenharmony_ci	const struct in6_addr *group = NULL;
351962306a36Sopenharmony_ci	bool is_general_query;
352062306a36Sopenharmony_ci	int err = 0;
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
352362306a36Sopenharmony_ci	if (!br_multicast_ctx_should_use(brmctx, pmctx))
352462306a36Sopenharmony_ci		goto out;
352562306a36Sopenharmony_ci
352662306a36Sopenharmony_ci	if (transport_len == sizeof(*mld)) {
352762306a36Sopenharmony_ci		if (!pskb_may_pull(skb, offset + sizeof(*mld))) {
352862306a36Sopenharmony_ci			err = -EINVAL;
352962306a36Sopenharmony_ci			goto out;
353062306a36Sopenharmony_ci		}
353162306a36Sopenharmony_ci		mld = (struct mld_msg *) icmp6_hdr(skb);
353262306a36Sopenharmony_ci		max_delay = msecs_to_jiffies(ntohs(mld->mld_maxdelay));
353362306a36Sopenharmony_ci		if (max_delay)
353462306a36Sopenharmony_ci			group = &mld->mld_mca;
353562306a36Sopenharmony_ci	} else {
353662306a36Sopenharmony_ci		if (!pskb_may_pull(skb, offset + sizeof(*mld2q))) {
353762306a36Sopenharmony_ci			err = -EINVAL;
353862306a36Sopenharmony_ci			goto out;
353962306a36Sopenharmony_ci		}
354062306a36Sopenharmony_ci		mld2q = (struct mld2_query *)icmp6_hdr(skb);
354162306a36Sopenharmony_ci		if (!mld2q->mld2q_nsrcs)
354262306a36Sopenharmony_ci			group = &mld2q->mld2q_mca;
354362306a36Sopenharmony_ci		if (brmctx->multicast_mld_version == 2 &&
354462306a36Sopenharmony_ci		    !ipv6_addr_any(&mld2q->mld2q_mca) &&
354562306a36Sopenharmony_ci		    mld2q->mld2q_suppress)
354662306a36Sopenharmony_ci			goto out;
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci		max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL);
354962306a36Sopenharmony_ci	}
355062306a36Sopenharmony_ci
355162306a36Sopenharmony_ci	is_general_query = group && ipv6_addr_any(group);
355262306a36Sopenharmony_ci
355362306a36Sopenharmony_ci	if (is_general_query) {
355462306a36Sopenharmony_ci		saddr.proto = htons(ETH_P_IPV6);
355562306a36Sopenharmony_ci		saddr.src.ip6 = ipv6_hdr(skb)->saddr;
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_ci		br_ip6_multicast_query_received(brmctx, pmctx,
355862306a36Sopenharmony_ci						&brmctx->ip6_other_query,
355962306a36Sopenharmony_ci						&saddr, max_delay);
356062306a36Sopenharmony_ci		goto out;
356162306a36Sopenharmony_ci	} else if (!group) {
356262306a36Sopenharmony_ci		goto out;
356362306a36Sopenharmony_ci	}
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_ci	mp = br_mdb_ip6_get(brmctx->br, group, vid);
356662306a36Sopenharmony_ci	if (!mp)
356762306a36Sopenharmony_ci		goto out;
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci	max_delay *= brmctx->multicast_last_member_count;
357062306a36Sopenharmony_ci	if (mp->host_joined &&
357162306a36Sopenharmony_ci	    (timer_pending(&mp->timer) ?
357262306a36Sopenharmony_ci	     time_after(mp->timer.expires, now + max_delay) :
357362306a36Sopenharmony_ci	     try_to_del_timer_sync(&mp->timer) >= 0))
357462306a36Sopenharmony_ci		mod_timer(&mp->timer, now + max_delay);
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_ci	for (pp = &mp->ports;
357762306a36Sopenharmony_ci	     (p = mlock_dereference(*pp, brmctx->br)) != NULL;
357862306a36Sopenharmony_ci	     pp = &p->next) {
357962306a36Sopenharmony_ci		if (timer_pending(&p->timer) ?
358062306a36Sopenharmony_ci		    time_after(p->timer.expires, now + max_delay) :
358162306a36Sopenharmony_ci		    try_to_del_timer_sync(&p->timer) >= 0 &&
358262306a36Sopenharmony_ci		    (brmctx->multicast_mld_version == 1 ||
358362306a36Sopenharmony_ci		     p->filter_mode == MCAST_EXCLUDE))
358462306a36Sopenharmony_ci			mod_timer(&p->timer, now + max_delay);
358562306a36Sopenharmony_ci	}
358662306a36Sopenharmony_ci
358762306a36Sopenharmony_ciout:
358862306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
358962306a36Sopenharmony_ci	return err;
359062306a36Sopenharmony_ci}
359162306a36Sopenharmony_ci#endif
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_cistatic void
359462306a36Sopenharmony_cibr_multicast_leave_group(struct net_bridge_mcast *brmctx,
359562306a36Sopenharmony_ci			 struct net_bridge_mcast_port *pmctx,
359662306a36Sopenharmony_ci			 struct br_ip *group,
359762306a36Sopenharmony_ci			 struct bridge_mcast_other_query *other_query,
359862306a36Sopenharmony_ci			 struct bridge_mcast_own_query *own_query,
359962306a36Sopenharmony_ci			 const unsigned char *src)
360062306a36Sopenharmony_ci{
360162306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
360262306a36Sopenharmony_ci	struct net_bridge_port_group *p;
360362306a36Sopenharmony_ci	unsigned long now;
360462306a36Sopenharmony_ci	unsigned long time;
360562306a36Sopenharmony_ci
360662306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
360762306a36Sopenharmony_ci	if (!br_multicast_ctx_should_use(brmctx, pmctx))
360862306a36Sopenharmony_ci		goto out;
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ci	mp = br_mdb_ip_get(brmctx->br, group);
361162306a36Sopenharmony_ci	if (!mp)
361262306a36Sopenharmony_ci		goto out;
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci	if (pmctx && (pmctx->port->flags & BR_MULTICAST_FAST_LEAVE)) {
361562306a36Sopenharmony_ci		struct net_bridge_port_group __rcu **pp;
361662306a36Sopenharmony_ci
361762306a36Sopenharmony_ci		for (pp = &mp->ports;
361862306a36Sopenharmony_ci		     (p = mlock_dereference(*pp, brmctx->br)) != NULL;
361962306a36Sopenharmony_ci		     pp = &p->next) {
362062306a36Sopenharmony_ci			if (!br_port_group_equal(p, pmctx->port, src))
362162306a36Sopenharmony_ci				continue;
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci			if (p->flags & MDB_PG_FLAGS_PERMANENT)
362462306a36Sopenharmony_ci				break;
362562306a36Sopenharmony_ci
362662306a36Sopenharmony_ci			p->flags |= MDB_PG_FLAGS_FAST_LEAVE;
362762306a36Sopenharmony_ci			br_multicast_del_pg(mp, p, pp);
362862306a36Sopenharmony_ci		}
362962306a36Sopenharmony_ci		goto out;
363062306a36Sopenharmony_ci	}
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_ci	if (timer_pending(&other_query->timer))
363362306a36Sopenharmony_ci		goto out;
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci	if (brmctx->multicast_querier) {
363662306a36Sopenharmony_ci		__br_multicast_send_query(brmctx, pmctx, NULL, NULL, &mp->addr,
363762306a36Sopenharmony_ci					  false, 0, NULL);
363862306a36Sopenharmony_ci
363962306a36Sopenharmony_ci		time = jiffies + brmctx->multicast_last_member_count *
364062306a36Sopenharmony_ci				 brmctx->multicast_last_member_interval;
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci		mod_timer(&own_query->timer, time);
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci		for (p = mlock_dereference(mp->ports, brmctx->br);
364562306a36Sopenharmony_ci		     p != NULL && pmctx != NULL;
364662306a36Sopenharmony_ci		     p = mlock_dereference(p->next, brmctx->br)) {
364762306a36Sopenharmony_ci			if (!br_port_group_equal(p, pmctx->port, src))
364862306a36Sopenharmony_ci				continue;
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_ci			if (!hlist_unhashed(&p->mglist) &&
365162306a36Sopenharmony_ci			    (timer_pending(&p->timer) ?
365262306a36Sopenharmony_ci			     time_after(p->timer.expires, time) :
365362306a36Sopenharmony_ci			     try_to_del_timer_sync(&p->timer) >= 0)) {
365462306a36Sopenharmony_ci				mod_timer(&p->timer, time);
365562306a36Sopenharmony_ci			}
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_ci			break;
365862306a36Sopenharmony_ci		}
365962306a36Sopenharmony_ci	}
366062306a36Sopenharmony_ci
366162306a36Sopenharmony_ci	now = jiffies;
366262306a36Sopenharmony_ci	time = now + brmctx->multicast_last_member_count *
366362306a36Sopenharmony_ci		     brmctx->multicast_last_member_interval;
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci	if (!pmctx) {
366662306a36Sopenharmony_ci		if (mp->host_joined &&
366762306a36Sopenharmony_ci		    (timer_pending(&mp->timer) ?
366862306a36Sopenharmony_ci		     time_after(mp->timer.expires, time) :
366962306a36Sopenharmony_ci		     try_to_del_timer_sync(&mp->timer) >= 0)) {
367062306a36Sopenharmony_ci			mod_timer(&mp->timer, time);
367162306a36Sopenharmony_ci		}
367262306a36Sopenharmony_ci
367362306a36Sopenharmony_ci		goto out;
367462306a36Sopenharmony_ci	}
367562306a36Sopenharmony_ci
367662306a36Sopenharmony_ci	for (p = mlock_dereference(mp->ports, brmctx->br);
367762306a36Sopenharmony_ci	     p != NULL;
367862306a36Sopenharmony_ci	     p = mlock_dereference(p->next, brmctx->br)) {
367962306a36Sopenharmony_ci		if (p->key.port != pmctx->port)
368062306a36Sopenharmony_ci			continue;
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_ci		if (!hlist_unhashed(&p->mglist) &&
368362306a36Sopenharmony_ci		    (timer_pending(&p->timer) ?
368462306a36Sopenharmony_ci		     time_after(p->timer.expires, time) :
368562306a36Sopenharmony_ci		     try_to_del_timer_sync(&p->timer) >= 0)) {
368662306a36Sopenharmony_ci			mod_timer(&p->timer, time);
368762306a36Sopenharmony_ci		}
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci		break;
369062306a36Sopenharmony_ci	}
369162306a36Sopenharmony_ciout:
369262306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
369362306a36Sopenharmony_ci}
369462306a36Sopenharmony_ci
369562306a36Sopenharmony_cistatic void br_ip4_multicast_leave_group(struct net_bridge_mcast *brmctx,
369662306a36Sopenharmony_ci					 struct net_bridge_mcast_port *pmctx,
369762306a36Sopenharmony_ci					 __be32 group,
369862306a36Sopenharmony_ci					 __u16 vid,
369962306a36Sopenharmony_ci					 const unsigned char *src)
370062306a36Sopenharmony_ci{
370162306a36Sopenharmony_ci	struct br_ip br_group;
370262306a36Sopenharmony_ci	struct bridge_mcast_own_query *own_query;
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_ci	if (ipv4_is_local_multicast(group))
370562306a36Sopenharmony_ci		return;
370662306a36Sopenharmony_ci
370762306a36Sopenharmony_ci	own_query = pmctx ? &pmctx->ip4_own_query : &brmctx->ip4_own_query;
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_ci	memset(&br_group, 0, sizeof(br_group));
371062306a36Sopenharmony_ci	br_group.dst.ip4 = group;
371162306a36Sopenharmony_ci	br_group.proto = htons(ETH_P_IP);
371262306a36Sopenharmony_ci	br_group.vid = vid;
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci	br_multicast_leave_group(brmctx, pmctx, &br_group,
371562306a36Sopenharmony_ci				 &brmctx->ip4_other_query,
371662306a36Sopenharmony_ci				 own_query, src);
371762306a36Sopenharmony_ci}
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
372062306a36Sopenharmony_cistatic void br_ip6_multicast_leave_group(struct net_bridge_mcast *brmctx,
372162306a36Sopenharmony_ci					 struct net_bridge_mcast_port *pmctx,
372262306a36Sopenharmony_ci					 const struct in6_addr *group,
372362306a36Sopenharmony_ci					 __u16 vid,
372462306a36Sopenharmony_ci					 const unsigned char *src)
372562306a36Sopenharmony_ci{
372662306a36Sopenharmony_ci	struct br_ip br_group;
372762306a36Sopenharmony_ci	struct bridge_mcast_own_query *own_query;
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	if (ipv6_addr_is_ll_all_nodes(group))
373062306a36Sopenharmony_ci		return;
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci	own_query = pmctx ? &pmctx->ip6_own_query : &brmctx->ip6_own_query;
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci	memset(&br_group, 0, sizeof(br_group));
373562306a36Sopenharmony_ci	br_group.dst.ip6 = *group;
373662306a36Sopenharmony_ci	br_group.proto = htons(ETH_P_IPV6);
373762306a36Sopenharmony_ci	br_group.vid = vid;
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_ci	br_multicast_leave_group(brmctx, pmctx, &br_group,
374062306a36Sopenharmony_ci				 &brmctx->ip6_other_query,
374162306a36Sopenharmony_ci				 own_query, src);
374262306a36Sopenharmony_ci}
374362306a36Sopenharmony_ci#endif
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_cistatic void br_multicast_err_count(const struct net_bridge *br,
374662306a36Sopenharmony_ci				   const struct net_bridge_port *p,
374762306a36Sopenharmony_ci				   __be16 proto)
374862306a36Sopenharmony_ci{
374962306a36Sopenharmony_ci	struct bridge_mcast_stats __percpu *stats;
375062306a36Sopenharmony_ci	struct bridge_mcast_stats *pstats;
375162306a36Sopenharmony_ci
375262306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED))
375362306a36Sopenharmony_ci		return;
375462306a36Sopenharmony_ci
375562306a36Sopenharmony_ci	if (p)
375662306a36Sopenharmony_ci		stats = p->mcast_stats;
375762306a36Sopenharmony_ci	else
375862306a36Sopenharmony_ci		stats = br->mcast_stats;
375962306a36Sopenharmony_ci	if (WARN_ON(!stats))
376062306a36Sopenharmony_ci		return;
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci	pstats = this_cpu_ptr(stats);
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ci	u64_stats_update_begin(&pstats->syncp);
376562306a36Sopenharmony_ci	switch (proto) {
376662306a36Sopenharmony_ci	case htons(ETH_P_IP):
376762306a36Sopenharmony_ci		pstats->mstats.igmp_parse_errors++;
376862306a36Sopenharmony_ci		break;
376962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
377062306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
377162306a36Sopenharmony_ci		pstats->mstats.mld_parse_errors++;
377262306a36Sopenharmony_ci		break;
377362306a36Sopenharmony_ci#endif
377462306a36Sopenharmony_ci	}
377562306a36Sopenharmony_ci	u64_stats_update_end(&pstats->syncp);
377662306a36Sopenharmony_ci}
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_cistatic void br_multicast_pim(struct net_bridge_mcast *brmctx,
377962306a36Sopenharmony_ci			     struct net_bridge_mcast_port *pmctx,
378062306a36Sopenharmony_ci			     const struct sk_buff *skb)
378162306a36Sopenharmony_ci{
378262306a36Sopenharmony_ci	unsigned int offset = skb_transport_offset(skb);
378362306a36Sopenharmony_ci	struct pimhdr *pimhdr, _pimhdr;
378462306a36Sopenharmony_ci
378562306a36Sopenharmony_ci	pimhdr = skb_header_pointer(skb, offset, sizeof(_pimhdr), &_pimhdr);
378662306a36Sopenharmony_ci	if (!pimhdr || pim_hdr_version(pimhdr) != PIM_VERSION ||
378762306a36Sopenharmony_ci	    pim_hdr_type(pimhdr) != PIM_TYPE_HELLO)
378862306a36Sopenharmony_ci		return;
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
379162306a36Sopenharmony_ci	br_ip4_multicast_mark_router(brmctx, pmctx);
379262306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
379362306a36Sopenharmony_ci}
379462306a36Sopenharmony_ci
379562306a36Sopenharmony_cistatic int br_ip4_multicast_mrd_rcv(struct net_bridge_mcast *brmctx,
379662306a36Sopenharmony_ci				    struct net_bridge_mcast_port *pmctx,
379762306a36Sopenharmony_ci				    struct sk_buff *skb)
379862306a36Sopenharmony_ci{
379962306a36Sopenharmony_ci	if (ip_hdr(skb)->protocol != IPPROTO_IGMP ||
380062306a36Sopenharmony_ci	    igmp_hdr(skb)->type != IGMP_MRDISC_ADV)
380162306a36Sopenharmony_ci		return -ENOMSG;
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
380462306a36Sopenharmony_ci	br_ip4_multicast_mark_router(brmctx, pmctx);
380562306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
380662306a36Sopenharmony_ci
380762306a36Sopenharmony_ci	return 0;
380862306a36Sopenharmony_ci}
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_cistatic int br_multicast_ipv4_rcv(struct net_bridge_mcast *brmctx,
381162306a36Sopenharmony_ci				 struct net_bridge_mcast_port *pmctx,
381262306a36Sopenharmony_ci				 struct sk_buff *skb,
381362306a36Sopenharmony_ci				 u16 vid)
381462306a36Sopenharmony_ci{
381562306a36Sopenharmony_ci	struct net_bridge_port *p = pmctx ? pmctx->port : NULL;
381662306a36Sopenharmony_ci	const unsigned char *src;
381762306a36Sopenharmony_ci	struct igmphdr *ih;
381862306a36Sopenharmony_ci	int err;
381962306a36Sopenharmony_ci
382062306a36Sopenharmony_ci	err = ip_mc_check_igmp(skb);
382162306a36Sopenharmony_ci
382262306a36Sopenharmony_ci	if (err == -ENOMSG) {
382362306a36Sopenharmony_ci		if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) {
382462306a36Sopenharmony_ci			BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
382562306a36Sopenharmony_ci		} else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) {
382662306a36Sopenharmony_ci			if (ip_hdr(skb)->protocol == IPPROTO_PIM)
382762306a36Sopenharmony_ci				br_multicast_pim(brmctx, pmctx, skb);
382862306a36Sopenharmony_ci		} else if (ipv4_is_all_snoopers(ip_hdr(skb)->daddr)) {
382962306a36Sopenharmony_ci			br_ip4_multicast_mrd_rcv(brmctx, pmctx, skb);
383062306a36Sopenharmony_ci		}
383162306a36Sopenharmony_ci
383262306a36Sopenharmony_ci		return 0;
383362306a36Sopenharmony_ci	} else if (err < 0) {
383462306a36Sopenharmony_ci		br_multicast_err_count(brmctx->br, p, skb->protocol);
383562306a36Sopenharmony_ci		return err;
383662306a36Sopenharmony_ci	}
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	ih = igmp_hdr(skb);
383962306a36Sopenharmony_ci	src = eth_hdr(skb)->h_source;
384062306a36Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->igmp = ih->type;
384162306a36Sopenharmony_ci
384262306a36Sopenharmony_ci	switch (ih->type) {
384362306a36Sopenharmony_ci	case IGMP_HOST_MEMBERSHIP_REPORT:
384462306a36Sopenharmony_ci	case IGMPV2_HOST_MEMBERSHIP_REPORT:
384562306a36Sopenharmony_ci		BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
384662306a36Sopenharmony_ci		err = br_ip4_multicast_add_group(brmctx, pmctx, ih->group, vid,
384762306a36Sopenharmony_ci						 src, true);
384862306a36Sopenharmony_ci		break;
384962306a36Sopenharmony_ci	case IGMPV3_HOST_MEMBERSHIP_REPORT:
385062306a36Sopenharmony_ci		err = br_ip4_multicast_igmp3_report(brmctx, pmctx, skb, vid);
385162306a36Sopenharmony_ci		break;
385262306a36Sopenharmony_ci	case IGMP_HOST_MEMBERSHIP_QUERY:
385362306a36Sopenharmony_ci		br_ip4_multicast_query(brmctx, pmctx, skb, vid);
385462306a36Sopenharmony_ci		break;
385562306a36Sopenharmony_ci	case IGMP_HOST_LEAVE_MESSAGE:
385662306a36Sopenharmony_ci		br_ip4_multicast_leave_group(brmctx, pmctx, ih->group, vid, src);
385762306a36Sopenharmony_ci		break;
385862306a36Sopenharmony_ci	}
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_ci	br_multicast_count(brmctx->br, p, skb, BR_INPUT_SKB_CB(skb)->igmp,
386162306a36Sopenharmony_ci			   BR_MCAST_DIR_RX);
386262306a36Sopenharmony_ci
386362306a36Sopenharmony_ci	return err;
386462306a36Sopenharmony_ci}
386562306a36Sopenharmony_ci
386662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
386762306a36Sopenharmony_cistatic void br_ip6_multicast_mrd_rcv(struct net_bridge_mcast *brmctx,
386862306a36Sopenharmony_ci				     struct net_bridge_mcast_port *pmctx,
386962306a36Sopenharmony_ci				     struct sk_buff *skb)
387062306a36Sopenharmony_ci{
387162306a36Sopenharmony_ci	if (icmp6_hdr(skb)->icmp6_type != ICMPV6_MRDISC_ADV)
387262306a36Sopenharmony_ci		return;
387362306a36Sopenharmony_ci
387462306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
387562306a36Sopenharmony_ci	br_ip6_multicast_mark_router(brmctx, pmctx);
387662306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
387762306a36Sopenharmony_ci}
387862306a36Sopenharmony_ci
387962306a36Sopenharmony_cistatic int br_multicast_ipv6_rcv(struct net_bridge_mcast *brmctx,
388062306a36Sopenharmony_ci				 struct net_bridge_mcast_port *pmctx,
388162306a36Sopenharmony_ci				 struct sk_buff *skb,
388262306a36Sopenharmony_ci				 u16 vid)
388362306a36Sopenharmony_ci{
388462306a36Sopenharmony_ci	struct net_bridge_port *p = pmctx ? pmctx->port : NULL;
388562306a36Sopenharmony_ci	const unsigned char *src;
388662306a36Sopenharmony_ci	struct mld_msg *mld;
388762306a36Sopenharmony_ci	int err;
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci	err = ipv6_mc_check_mld(skb);
389062306a36Sopenharmony_ci
389162306a36Sopenharmony_ci	if (err == -ENOMSG || err == -ENODATA) {
389262306a36Sopenharmony_ci		if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
389362306a36Sopenharmony_ci			BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
389462306a36Sopenharmony_ci		if (err == -ENODATA &&
389562306a36Sopenharmony_ci		    ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr))
389662306a36Sopenharmony_ci			br_ip6_multicast_mrd_rcv(brmctx, pmctx, skb);
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_ci		return 0;
389962306a36Sopenharmony_ci	} else if (err < 0) {
390062306a36Sopenharmony_ci		br_multicast_err_count(brmctx->br, p, skb->protocol);
390162306a36Sopenharmony_ci		return err;
390262306a36Sopenharmony_ci	}
390362306a36Sopenharmony_ci
390462306a36Sopenharmony_ci	mld = (struct mld_msg *)skb_transport_header(skb);
390562306a36Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->igmp = mld->mld_type;
390662306a36Sopenharmony_ci
390762306a36Sopenharmony_ci	switch (mld->mld_type) {
390862306a36Sopenharmony_ci	case ICMPV6_MGM_REPORT:
390962306a36Sopenharmony_ci		src = eth_hdr(skb)->h_source;
391062306a36Sopenharmony_ci		BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
391162306a36Sopenharmony_ci		err = br_ip6_multicast_add_group(brmctx, pmctx, &mld->mld_mca,
391262306a36Sopenharmony_ci						 vid, src, true);
391362306a36Sopenharmony_ci		break;
391462306a36Sopenharmony_ci	case ICMPV6_MLD2_REPORT:
391562306a36Sopenharmony_ci		err = br_ip6_multicast_mld2_report(brmctx, pmctx, skb, vid);
391662306a36Sopenharmony_ci		break;
391762306a36Sopenharmony_ci	case ICMPV6_MGM_QUERY:
391862306a36Sopenharmony_ci		err = br_ip6_multicast_query(brmctx, pmctx, skb, vid);
391962306a36Sopenharmony_ci		break;
392062306a36Sopenharmony_ci	case ICMPV6_MGM_REDUCTION:
392162306a36Sopenharmony_ci		src = eth_hdr(skb)->h_source;
392262306a36Sopenharmony_ci		br_ip6_multicast_leave_group(brmctx, pmctx, &mld->mld_mca, vid,
392362306a36Sopenharmony_ci					     src);
392462306a36Sopenharmony_ci		break;
392562306a36Sopenharmony_ci	}
392662306a36Sopenharmony_ci
392762306a36Sopenharmony_ci	br_multicast_count(brmctx->br, p, skb, BR_INPUT_SKB_CB(skb)->igmp,
392862306a36Sopenharmony_ci			   BR_MCAST_DIR_RX);
392962306a36Sopenharmony_ci
393062306a36Sopenharmony_ci	return err;
393162306a36Sopenharmony_ci}
393262306a36Sopenharmony_ci#endif
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_ciint br_multicast_rcv(struct net_bridge_mcast **brmctx,
393562306a36Sopenharmony_ci		     struct net_bridge_mcast_port **pmctx,
393662306a36Sopenharmony_ci		     struct net_bridge_vlan *vlan,
393762306a36Sopenharmony_ci		     struct sk_buff *skb, u16 vid)
393862306a36Sopenharmony_ci{
393962306a36Sopenharmony_ci	int ret = 0;
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->igmp = 0;
394262306a36Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->mrouters_only = 0;
394362306a36Sopenharmony_ci
394462306a36Sopenharmony_ci	if (!br_opt_get((*brmctx)->br, BROPT_MULTICAST_ENABLED))
394562306a36Sopenharmony_ci		return 0;
394662306a36Sopenharmony_ci
394762306a36Sopenharmony_ci	if (br_opt_get((*brmctx)->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && vlan) {
394862306a36Sopenharmony_ci		const struct net_bridge_vlan *masterv;
394962306a36Sopenharmony_ci
395062306a36Sopenharmony_ci		/* the vlan has the master flag set only when transmitting
395162306a36Sopenharmony_ci		 * through the bridge device
395262306a36Sopenharmony_ci		 */
395362306a36Sopenharmony_ci		if (br_vlan_is_master(vlan)) {
395462306a36Sopenharmony_ci			masterv = vlan;
395562306a36Sopenharmony_ci			*brmctx = &vlan->br_mcast_ctx;
395662306a36Sopenharmony_ci			*pmctx = NULL;
395762306a36Sopenharmony_ci		} else {
395862306a36Sopenharmony_ci			masterv = vlan->brvlan;
395962306a36Sopenharmony_ci			*brmctx = &vlan->brvlan->br_mcast_ctx;
396062306a36Sopenharmony_ci			*pmctx = &vlan->port_mcast_ctx;
396162306a36Sopenharmony_ci		}
396262306a36Sopenharmony_ci
396362306a36Sopenharmony_ci		if (!(masterv->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED))
396462306a36Sopenharmony_ci			return 0;
396562306a36Sopenharmony_ci	}
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_ci	switch (skb->protocol) {
396862306a36Sopenharmony_ci	case htons(ETH_P_IP):
396962306a36Sopenharmony_ci		ret = br_multicast_ipv4_rcv(*brmctx, *pmctx, skb, vid);
397062306a36Sopenharmony_ci		break;
397162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
397262306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
397362306a36Sopenharmony_ci		ret = br_multicast_ipv6_rcv(*brmctx, *pmctx, skb, vid);
397462306a36Sopenharmony_ci		break;
397562306a36Sopenharmony_ci#endif
397662306a36Sopenharmony_ci	}
397762306a36Sopenharmony_ci
397862306a36Sopenharmony_ci	return ret;
397962306a36Sopenharmony_ci}
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_cistatic void br_multicast_query_expired(struct net_bridge_mcast *brmctx,
398262306a36Sopenharmony_ci				       struct bridge_mcast_own_query *query,
398362306a36Sopenharmony_ci				       struct bridge_mcast_querier *querier)
398462306a36Sopenharmony_ci{
398562306a36Sopenharmony_ci	spin_lock(&brmctx->br->multicast_lock);
398662306a36Sopenharmony_ci	if (br_multicast_ctx_vlan_disabled(brmctx))
398762306a36Sopenharmony_ci		goto out;
398862306a36Sopenharmony_ci
398962306a36Sopenharmony_ci	if (query->startup_sent < brmctx->multicast_startup_query_count)
399062306a36Sopenharmony_ci		query->startup_sent++;
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_ci	br_multicast_send_query(brmctx, NULL, query);
399362306a36Sopenharmony_ciout:
399462306a36Sopenharmony_ci	spin_unlock(&brmctx->br->multicast_lock);
399562306a36Sopenharmony_ci}
399662306a36Sopenharmony_ci
399762306a36Sopenharmony_cistatic void br_ip4_multicast_query_expired(struct timer_list *t)
399862306a36Sopenharmony_ci{
399962306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
400062306a36Sopenharmony_ci						     ip4_own_query.timer);
400162306a36Sopenharmony_ci
400262306a36Sopenharmony_ci	br_multicast_query_expired(brmctx, &brmctx->ip4_own_query,
400362306a36Sopenharmony_ci				   &brmctx->ip4_querier);
400462306a36Sopenharmony_ci}
400562306a36Sopenharmony_ci
400662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
400762306a36Sopenharmony_cistatic void br_ip6_multicast_query_expired(struct timer_list *t)
400862306a36Sopenharmony_ci{
400962306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
401062306a36Sopenharmony_ci						     ip6_own_query.timer);
401162306a36Sopenharmony_ci
401262306a36Sopenharmony_ci	br_multicast_query_expired(brmctx, &brmctx->ip6_own_query,
401362306a36Sopenharmony_ci				   &brmctx->ip6_querier);
401462306a36Sopenharmony_ci}
401562306a36Sopenharmony_ci#endif
401662306a36Sopenharmony_ci
401762306a36Sopenharmony_cistatic void br_multicast_gc_work(struct work_struct *work)
401862306a36Sopenharmony_ci{
401962306a36Sopenharmony_ci	struct net_bridge *br = container_of(work, struct net_bridge,
402062306a36Sopenharmony_ci					     mcast_gc_work);
402162306a36Sopenharmony_ci	HLIST_HEAD(deleted_head);
402262306a36Sopenharmony_ci
402362306a36Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
402462306a36Sopenharmony_ci	hlist_move_list(&br->mcast_gc_list, &deleted_head);
402562306a36Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
402662306a36Sopenharmony_ci
402762306a36Sopenharmony_ci	br_multicast_gc(&deleted_head);
402862306a36Sopenharmony_ci}
402962306a36Sopenharmony_ci
403062306a36Sopenharmony_civoid br_multicast_ctx_init(struct net_bridge *br,
403162306a36Sopenharmony_ci			   struct net_bridge_vlan *vlan,
403262306a36Sopenharmony_ci			   struct net_bridge_mcast *brmctx)
403362306a36Sopenharmony_ci{
403462306a36Sopenharmony_ci	brmctx->br = br;
403562306a36Sopenharmony_ci	brmctx->vlan = vlan;
403662306a36Sopenharmony_ci	brmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
403762306a36Sopenharmony_ci	brmctx->multicast_last_member_count = 2;
403862306a36Sopenharmony_ci	brmctx->multicast_startup_query_count = 2;
403962306a36Sopenharmony_ci
404062306a36Sopenharmony_ci	brmctx->multicast_last_member_interval = HZ;
404162306a36Sopenharmony_ci	brmctx->multicast_query_response_interval = 10 * HZ;
404262306a36Sopenharmony_ci	brmctx->multicast_startup_query_interval = 125 * HZ / 4;
404362306a36Sopenharmony_ci	brmctx->multicast_query_interval = 125 * HZ;
404462306a36Sopenharmony_ci	brmctx->multicast_querier_interval = 255 * HZ;
404562306a36Sopenharmony_ci	brmctx->multicast_membership_interval = 260 * HZ;
404662306a36Sopenharmony_ci
404762306a36Sopenharmony_ci	brmctx->ip4_querier.port_ifidx = 0;
404862306a36Sopenharmony_ci	seqcount_spinlock_init(&brmctx->ip4_querier.seq, &br->multicast_lock);
404962306a36Sopenharmony_ci	brmctx->multicast_igmp_version = 2;
405062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
405162306a36Sopenharmony_ci	brmctx->multicast_mld_version = 1;
405262306a36Sopenharmony_ci	brmctx->ip6_querier.port_ifidx = 0;
405362306a36Sopenharmony_ci	seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock);
405462306a36Sopenharmony_ci#endif
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_ci	timer_setup(&brmctx->ip4_mc_router_timer,
405762306a36Sopenharmony_ci		    br_ip4_multicast_local_router_expired, 0);
405862306a36Sopenharmony_ci	timer_setup(&brmctx->ip4_other_query.timer,
405962306a36Sopenharmony_ci		    br_ip4_multicast_querier_expired, 0);
406062306a36Sopenharmony_ci	timer_setup(&brmctx->ip4_other_query.delay_timer,
406162306a36Sopenharmony_ci		    br_multicast_query_delay_expired, 0);
406262306a36Sopenharmony_ci	timer_setup(&brmctx->ip4_own_query.timer,
406362306a36Sopenharmony_ci		    br_ip4_multicast_query_expired, 0);
406462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
406562306a36Sopenharmony_ci	timer_setup(&brmctx->ip6_mc_router_timer,
406662306a36Sopenharmony_ci		    br_ip6_multicast_local_router_expired, 0);
406762306a36Sopenharmony_ci	timer_setup(&brmctx->ip6_other_query.timer,
406862306a36Sopenharmony_ci		    br_ip6_multicast_querier_expired, 0);
406962306a36Sopenharmony_ci	timer_setup(&brmctx->ip6_other_query.delay_timer,
407062306a36Sopenharmony_ci		    br_multicast_query_delay_expired, 0);
407162306a36Sopenharmony_ci	timer_setup(&brmctx->ip6_own_query.timer,
407262306a36Sopenharmony_ci		    br_ip6_multicast_query_expired, 0);
407362306a36Sopenharmony_ci#endif
407462306a36Sopenharmony_ci}
407562306a36Sopenharmony_ci
407662306a36Sopenharmony_civoid br_multicast_ctx_deinit(struct net_bridge_mcast *brmctx)
407762306a36Sopenharmony_ci{
407862306a36Sopenharmony_ci	__br_multicast_stop(brmctx);
407962306a36Sopenharmony_ci}
408062306a36Sopenharmony_ci
408162306a36Sopenharmony_civoid br_multicast_init(struct net_bridge *br)
408262306a36Sopenharmony_ci{
408362306a36Sopenharmony_ci	br->hash_max = BR_MULTICAST_DEFAULT_HASH_MAX;
408462306a36Sopenharmony_ci
408562306a36Sopenharmony_ci	br_multicast_ctx_init(br, NULL, &br->multicast_ctx);
408662306a36Sopenharmony_ci
408762306a36Sopenharmony_ci	br_opt_toggle(br, BROPT_MULTICAST_ENABLED, true);
408862306a36Sopenharmony_ci	br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, true);
408962306a36Sopenharmony_ci
409062306a36Sopenharmony_ci	spin_lock_init(&br->multicast_lock);
409162306a36Sopenharmony_ci	INIT_HLIST_HEAD(&br->mdb_list);
409262306a36Sopenharmony_ci	INIT_HLIST_HEAD(&br->mcast_gc_list);
409362306a36Sopenharmony_ci	INIT_WORK(&br->mcast_gc_work, br_multicast_gc_work);
409462306a36Sopenharmony_ci}
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_cistatic void br_ip4_multicast_join_snoopers(struct net_bridge *br)
409762306a36Sopenharmony_ci{
409862306a36Sopenharmony_ci	struct in_device *in_dev = in_dev_get(br->dev);
409962306a36Sopenharmony_ci
410062306a36Sopenharmony_ci	if (!in_dev)
410162306a36Sopenharmony_ci		return;
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_ci	__ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
410462306a36Sopenharmony_ci	in_dev_put(in_dev);
410562306a36Sopenharmony_ci}
410662306a36Sopenharmony_ci
410762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
410862306a36Sopenharmony_cistatic void br_ip6_multicast_join_snoopers(struct net_bridge *br)
410962306a36Sopenharmony_ci{
411062306a36Sopenharmony_ci	struct in6_addr addr;
411162306a36Sopenharmony_ci
411262306a36Sopenharmony_ci	ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a));
411362306a36Sopenharmony_ci	ipv6_dev_mc_inc(br->dev, &addr);
411462306a36Sopenharmony_ci}
411562306a36Sopenharmony_ci#else
411662306a36Sopenharmony_cistatic inline void br_ip6_multicast_join_snoopers(struct net_bridge *br)
411762306a36Sopenharmony_ci{
411862306a36Sopenharmony_ci}
411962306a36Sopenharmony_ci#endif
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_civoid br_multicast_join_snoopers(struct net_bridge *br)
412262306a36Sopenharmony_ci{
412362306a36Sopenharmony_ci	br_ip4_multicast_join_snoopers(br);
412462306a36Sopenharmony_ci	br_ip6_multicast_join_snoopers(br);
412562306a36Sopenharmony_ci}
412662306a36Sopenharmony_ci
412762306a36Sopenharmony_cistatic void br_ip4_multicast_leave_snoopers(struct net_bridge *br)
412862306a36Sopenharmony_ci{
412962306a36Sopenharmony_ci	struct in_device *in_dev = in_dev_get(br->dev);
413062306a36Sopenharmony_ci
413162306a36Sopenharmony_ci	if (WARN_ON(!in_dev))
413262306a36Sopenharmony_ci		return;
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci	__ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
413562306a36Sopenharmony_ci	in_dev_put(in_dev);
413662306a36Sopenharmony_ci}
413762306a36Sopenharmony_ci
413862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
413962306a36Sopenharmony_cistatic void br_ip6_multicast_leave_snoopers(struct net_bridge *br)
414062306a36Sopenharmony_ci{
414162306a36Sopenharmony_ci	struct in6_addr addr;
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_ci	ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a));
414462306a36Sopenharmony_ci	ipv6_dev_mc_dec(br->dev, &addr);
414562306a36Sopenharmony_ci}
414662306a36Sopenharmony_ci#else
414762306a36Sopenharmony_cistatic inline void br_ip6_multicast_leave_snoopers(struct net_bridge *br)
414862306a36Sopenharmony_ci{
414962306a36Sopenharmony_ci}
415062306a36Sopenharmony_ci#endif
415162306a36Sopenharmony_ci
415262306a36Sopenharmony_civoid br_multicast_leave_snoopers(struct net_bridge *br)
415362306a36Sopenharmony_ci{
415462306a36Sopenharmony_ci	br_ip4_multicast_leave_snoopers(br);
415562306a36Sopenharmony_ci	br_ip6_multicast_leave_snoopers(br);
415662306a36Sopenharmony_ci}
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_cistatic void __br_multicast_open_query(struct net_bridge *br,
415962306a36Sopenharmony_ci				      struct bridge_mcast_own_query *query)
416062306a36Sopenharmony_ci{
416162306a36Sopenharmony_ci	query->startup_sent = 0;
416262306a36Sopenharmony_ci
416362306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
416462306a36Sopenharmony_ci		return;
416562306a36Sopenharmony_ci
416662306a36Sopenharmony_ci	mod_timer(&query->timer, jiffies);
416762306a36Sopenharmony_ci}
416862306a36Sopenharmony_ci
416962306a36Sopenharmony_cistatic void __br_multicast_open(struct net_bridge_mcast *brmctx)
417062306a36Sopenharmony_ci{
417162306a36Sopenharmony_ci	__br_multicast_open_query(brmctx->br, &brmctx->ip4_own_query);
417262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
417362306a36Sopenharmony_ci	__br_multicast_open_query(brmctx->br, &brmctx->ip6_own_query);
417462306a36Sopenharmony_ci#endif
417562306a36Sopenharmony_ci}
417662306a36Sopenharmony_ci
417762306a36Sopenharmony_civoid br_multicast_open(struct net_bridge *br)
417862306a36Sopenharmony_ci{
417962306a36Sopenharmony_ci	ASSERT_RTNL();
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
418262306a36Sopenharmony_ci		struct net_bridge_vlan_group *vg;
418362306a36Sopenharmony_ci		struct net_bridge_vlan *vlan;
418462306a36Sopenharmony_ci
418562306a36Sopenharmony_ci		vg = br_vlan_group(br);
418662306a36Sopenharmony_ci		if (vg) {
418762306a36Sopenharmony_ci			list_for_each_entry(vlan, &vg->vlan_list, vlist) {
418862306a36Sopenharmony_ci				struct net_bridge_mcast *brmctx;
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_ci				brmctx = &vlan->br_mcast_ctx;
419162306a36Sopenharmony_ci				if (br_vlan_is_brentry(vlan) &&
419262306a36Sopenharmony_ci				    !br_multicast_ctx_vlan_disabled(brmctx))
419362306a36Sopenharmony_ci					__br_multicast_open(&vlan->br_mcast_ctx);
419462306a36Sopenharmony_ci			}
419562306a36Sopenharmony_ci		}
419662306a36Sopenharmony_ci	} else {
419762306a36Sopenharmony_ci		__br_multicast_open(&br->multicast_ctx);
419862306a36Sopenharmony_ci	}
419962306a36Sopenharmony_ci}
420062306a36Sopenharmony_ci
420162306a36Sopenharmony_cistatic void __br_multicast_stop(struct net_bridge_mcast *brmctx)
420262306a36Sopenharmony_ci{
420362306a36Sopenharmony_ci	del_timer_sync(&brmctx->ip4_mc_router_timer);
420462306a36Sopenharmony_ci	del_timer_sync(&brmctx->ip4_other_query.timer);
420562306a36Sopenharmony_ci	del_timer_sync(&brmctx->ip4_other_query.delay_timer);
420662306a36Sopenharmony_ci	del_timer_sync(&brmctx->ip4_own_query.timer);
420762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
420862306a36Sopenharmony_ci	del_timer_sync(&brmctx->ip6_mc_router_timer);
420962306a36Sopenharmony_ci	del_timer_sync(&brmctx->ip6_other_query.timer);
421062306a36Sopenharmony_ci	del_timer_sync(&brmctx->ip6_other_query.delay_timer);
421162306a36Sopenharmony_ci	del_timer_sync(&brmctx->ip6_own_query.timer);
421262306a36Sopenharmony_ci#endif
421362306a36Sopenharmony_ci}
421462306a36Sopenharmony_ci
421562306a36Sopenharmony_civoid br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
421662306a36Sopenharmony_ci{
421762306a36Sopenharmony_ci	struct net_bridge *br;
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_ci	/* it's okay to check for the flag without the multicast lock because it
422062306a36Sopenharmony_ci	 * can only change under RTNL -> multicast_lock, we need the latter to
422162306a36Sopenharmony_ci	 * sync with timers and packets
422262306a36Sopenharmony_ci	 */
422362306a36Sopenharmony_ci	if (on == !!(vlan->priv_flags & BR_VLFLAG_MCAST_ENABLED))
422462306a36Sopenharmony_ci		return;
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	if (br_vlan_is_master(vlan)) {
422762306a36Sopenharmony_ci		br = vlan->br;
422862306a36Sopenharmony_ci
422962306a36Sopenharmony_ci		if (!br_vlan_is_brentry(vlan) ||
423062306a36Sopenharmony_ci		    (on &&
423162306a36Sopenharmony_ci		     br_multicast_ctx_vlan_global_disabled(&vlan->br_mcast_ctx)))
423262306a36Sopenharmony_ci			return;
423362306a36Sopenharmony_ci
423462306a36Sopenharmony_ci		spin_lock_bh(&br->multicast_lock);
423562306a36Sopenharmony_ci		vlan->priv_flags ^= BR_VLFLAG_MCAST_ENABLED;
423662306a36Sopenharmony_ci		spin_unlock_bh(&br->multicast_lock);
423762306a36Sopenharmony_ci
423862306a36Sopenharmony_ci		if (on)
423962306a36Sopenharmony_ci			__br_multicast_open(&vlan->br_mcast_ctx);
424062306a36Sopenharmony_ci		else
424162306a36Sopenharmony_ci			__br_multicast_stop(&vlan->br_mcast_ctx);
424262306a36Sopenharmony_ci	} else {
424362306a36Sopenharmony_ci		struct net_bridge_mcast *brmctx;
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci		brmctx = br_multicast_port_ctx_get_global(&vlan->port_mcast_ctx);
424662306a36Sopenharmony_ci		if (on && br_multicast_ctx_vlan_global_disabled(brmctx))
424762306a36Sopenharmony_ci			return;
424862306a36Sopenharmony_ci
424962306a36Sopenharmony_ci		br = vlan->port->br;
425062306a36Sopenharmony_ci		spin_lock_bh(&br->multicast_lock);
425162306a36Sopenharmony_ci		vlan->priv_flags ^= BR_VLFLAG_MCAST_ENABLED;
425262306a36Sopenharmony_ci		if (on)
425362306a36Sopenharmony_ci			__br_multicast_enable_port_ctx(&vlan->port_mcast_ctx);
425462306a36Sopenharmony_ci		else
425562306a36Sopenharmony_ci			__br_multicast_disable_port_ctx(&vlan->port_mcast_ctx);
425662306a36Sopenharmony_ci		spin_unlock_bh(&br->multicast_lock);
425762306a36Sopenharmony_ci	}
425862306a36Sopenharmony_ci}
425962306a36Sopenharmony_ci
426062306a36Sopenharmony_cistatic void br_multicast_toggle_vlan(struct net_bridge_vlan *vlan, bool on)
426162306a36Sopenharmony_ci{
426262306a36Sopenharmony_ci	struct net_bridge_port *p;
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_ci	if (WARN_ON_ONCE(!br_vlan_is_master(vlan)))
426562306a36Sopenharmony_ci		return;
426662306a36Sopenharmony_ci
426762306a36Sopenharmony_ci	list_for_each_entry(p, &vlan->br->port_list, list) {
426862306a36Sopenharmony_ci		struct net_bridge_vlan *vport;
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_ci		vport = br_vlan_find(nbp_vlan_group(p), vlan->vid);
427162306a36Sopenharmony_ci		if (!vport)
427262306a36Sopenharmony_ci			continue;
427362306a36Sopenharmony_ci		br_multicast_toggle_one_vlan(vport, on);
427462306a36Sopenharmony_ci	}
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci	if (br_vlan_is_brentry(vlan))
427762306a36Sopenharmony_ci		br_multicast_toggle_one_vlan(vlan, on);
427862306a36Sopenharmony_ci}
427962306a36Sopenharmony_ci
428062306a36Sopenharmony_ciint br_multicast_toggle_vlan_snooping(struct net_bridge *br, bool on,
428162306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
428262306a36Sopenharmony_ci{
428362306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
428462306a36Sopenharmony_ci	struct net_bridge_vlan *vlan;
428562306a36Sopenharmony_ci	struct net_bridge_port *p;
428662306a36Sopenharmony_ci
428762306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) == on)
428862306a36Sopenharmony_ci		return 0;
428962306a36Sopenharmony_ci
429062306a36Sopenharmony_ci	if (on && !br_opt_get(br, BROPT_VLAN_ENABLED)) {
429162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Cannot enable multicast vlan snooping with vlan filtering disabled");
429262306a36Sopenharmony_ci		return -EINVAL;
429362306a36Sopenharmony_ci	}
429462306a36Sopenharmony_ci
429562306a36Sopenharmony_ci	vg = br_vlan_group(br);
429662306a36Sopenharmony_ci	if (!vg)
429762306a36Sopenharmony_ci		return 0;
429862306a36Sopenharmony_ci
429962306a36Sopenharmony_ci	br_opt_toggle(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED, on);
430062306a36Sopenharmony_ci
430162306a36Sopenharmony_ci	/* disable/enable non-vlan mcast contexts based on vlan snooping */
430262306a36Sopenharmony_ci	if (on)
430362306a36Sopenharmony_ci		__br_multicast_stop(&br->multicast_ctx);
430462306a36Sopenharmony_ci	else
430562306a36Sopenharmony_ci		__br_multicast_open(&br->multicast_ctx);
430662306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
430762306a36Sopenharmony_ci		if (on)
430862306a36Sopenharmony_ci			br_multicast_disable_port(p);
430962306a36Sopenharmony_ci		else
431062306a36Sopenharmony_ci			br_multicast_enable_port(p);
431162306a36Sopenharmony_ci	}
431262306a36Sopenharmony_ci
431362306a36Sopenharmony_ci	list_for_each_entry(vlan, &vg->vlan_list, vlist)
431462306a36Sopenharmony_ci		br_multicast_toggle_vlan(vlan, on);
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci	return 0;
431762306a36Sopenharmony_ci}
431862306a36Sopenharmony_ci
431962306a36Sopenharmony_cibool br_multicast_toggle_global_vlan(struct net_bridge_vlan *vlan, bool on)
432062306a36Sopenharmony_ci{
432162306a36Sopenharmony_ci	ASSERT_RTNL();
432262306a36Sopenharmony_ci
432362306a36Sopenharmony_ci	/* BR_VLFLAG_GLOBAL_MCAST_ENABLED relies on eventual consistency and
432462306a36Sopenharmony_ci	 * requires only RTNL to change
432562306a36Sopenharmony_ci	 */
432662306a36Sopenharmony_ci	if (on == !!(vlan->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED))
432762306a36Sopenharmony_ci		return false;
432862306a36Sopenharmony_ci
432962306a36Sopenharmony_ci	vlan->priv_flags ^= BR_VLFLAG_GLOBAL_MCAST_ENABLED;
433062306a36Sopenharmony_ci	br_multicast_toggle_vlan(vlan, on);
433162306a36Sopenharmony_ci
433262306a36Sopenharmony_ci	return true;
433362306a36Sopenharmony_ci}
433462306a36Sopenharmony_ci
433562306a36Sopenharmony_civoid br_multicast_stop(struct net_bridge *br)
433662306a36Sopenharmony_ci{
433762306a36Sopenharmony_ci	ASSERT_RTNL();
433862306a36Sopenharmony_ci
433962306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
434062306a36Sopenharmony_ci		struct net_bridge_vlan_group *vg;
434162306a36Sopenharmony_ci		struct net_bridge_vlan *vlan;
434262306a36Sopenharmony_ci
434362306a36Sopenharmony_ci		vg = br_vlan_group(br);
434462306a36Sopenharmony_ci		if (vg) {
434562306a36Sopenharmony_ci			list_for_each_entry(vlan, &vg->vlan_list, vlist) {
434662306a36Sopenharmony_ci				struct net_bridge_mcast *brmctx;
434762306a36Sopenharmony_ci
434862306a36Sopenharmony_ci				brmctx = &vlan->br_mcast_ctx;
434962306a36Sopenharmony_ci				if (br_vlan_is_brentry(vlan) &&
435062306a36Sopenharmony_ci				    !br_multicast_ctx_vlan_disabled(brmctx))
435162306a36Sopenharmony_ci					__br_multicast_stop(&vlan->br_mcast_ctx);
435262306a36Sopenharmony_ci			}
435362306a36Sopenharmony_ci		}
435462306a36Sopenharmony_ci	} else {
435562306a36Sopenharmony_ci		__br_multicast_stop(&br->multicast_ctx);
435662306a36Sopenharmony_ci	}
435762306a36Sopenharmony_ci}
435862306a36Sopenharmony_ci
435962306a36Sopenharmony_civoid br_multicast_dev_del(struct net_bridge *br)
436062306a36Sopenharmony_ci{
436162306a36Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
436262306a36Sopenharmony_ci	HLIST_HEAD(deleted_head);
436362306a36Sopenharmony_ci	struct hlist_node *tmp;
436462306a36Sopenharmony_ci
436562306a36Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
436662306a36Sopenharmony_ci	hlist_for_each_entry_safe(mp, tmp, &br->mdb_list, mdb_node)
436762306a36Sopenharmony_ci		br_multicast_del_mdb_entry(mp);
436862306a36Sopenharmony_ci	hlist_move_list(&br->mcast_gc_list, &deleted_head);
436962306a36Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
437062306a36Sopenharmony_ci
437162306a36Sopenharmony_ci	br_multicast_ctx_deinit(&br->multicast_ctx);
437262306a36Sopenharmony_ci	br_multicast_gc(&deleted_head);
437362306a36Sopenharmony_ci	cancel_work_sync(&br->mcast_gc_work);
437462306a36Sopenharmony_ci
437562306a36Sopenharmony_ci	rcu_barrier();
437662306a36Sopenharmony_ci}
437762306a36Sopenharmony_ci
437862306a36Sopenharmony_ciint br_multicast_set_router(struct net_bridge_mcast *brmctx, unsigned long val)
437962306a36Sopenharmony_ci{
438062306a36Sopenharmony_ci	int err = -EINVAL;
438162306a36Sopenharmony_ci
438262306a36Sopenharmony_ci	spin_lock_bh(&brmctx->br->multicast_lock);
438362306a36Sopenharmony_ci
438462306a36Sopenharmony_ci	switch (val) {
438562306a36Sopenharmony_ci	case MDB_RTR_TYPE_DISABLED:
438662306a36Sopenharmony_ci	case MDB_RTR_TYPE_PERM:
438762306a36Sopenharmony_ci		br_mc_router_state_change(brmctx->br, val == MDB_RTR_TYPE_PERM);
438862306a36Sopenharmony_ci		del_timer(&brmctx->ip4_mc_router_timer);
438962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
439062306a36Sopenharmony_ci		del_timer(&brmctx->ip6_mc_router_timer);
439162306a36Sopenharmony_ci#endif
439262306a36Sopenharmony_ci		brmctx->multicast_router = val;
439362306a36Sopenharmony_ci		err = 0;
439462306a36Sopenharmony_ci		break;
439562306a36Sopenharmony_ci	case MDB_RTR_TYPE_TEMP_QUERY:
439662306a36Sopenharmony_ci		if (brmctx->multicast_router != MDB_RTR_TYPE_TEMP_QUERY)
439762306a36Sopenharmony_ci			br_mc_router_state_change(brmctx->br, false);
439862306a36Sopenharmony_ci		brmctx->multicast_router = val;
439962306a36Sopenharmony_ci		err = 0;
440062306a36Sopenharmony_ci		break;
440162306a36Sopenharmony_ci	}
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_ci	spin_unlock_bh(&brmctx->br->multicast_lock);
440462306a36Sopenharmony_ci
440562306a36Sopenharmony_ci	return err;
440662306a36Sopenharmony_ci}
440762306a36Sopenharmony_ci
440862306a36Sopenharmony_cistatic void
440962306a36Sopenharmony_cibr_multicast_rport_del_notify(struct net_bridge_mcast_port *pmctx, bool deleted)
441062306a36Sopenharmony_ci{
441162306a36Sopenharmony_ci	if (!deleted)
441262306a36Sopenharmony_ci		return;
441362306a36Sopenharmony_ci
441462306a36Sopenharmony_ci	/* For backwards compatibility for now, only notify if there is
441562306a36Sopenharmony_ci	 * no multicast router anymore for both IPv4 and IPv6.
441662306a36Sopenharmony_ci	 */
441762306a36Sopenharmony_ci	if (!hlist_unhashed(&pmctx->ip4_rlist))
441862306a36Sopenharmony_ci		return;
441962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
442062306a36Sopenharmony_ci	if (!hlist_unhashed(&pmctx->ip6_rlist))
442162306a36Sopenharmony_ci		return;
442262306a36Sopenharmony_ci#endif
442362306a36Sopenharmony_ci
442462306a36Sopenharmony_ci	br_rtr_notify(pmctx->port->br->dev, pmctx, RTM_DELMDB);
442562306a36Sopenharmony_ci	br_port_mc_router_state_change(pmctx->port, false);
442662306a36Sopenharmony_ci
442762306a36Sopenharmony_ci	/* don't allow timer refresh */
442862306a36Sopenharmony_ci	if (pmctx->multicast_router == MDB_RTR_TYPE_TEMP)
442962306a36Sopenharmony_ci		pmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
443062306a36Sopenharmony_ci}
443162306a36Sopenharmony_ci
443262306a36Sopenharmony_ciint br_multicast_set_port_router(struct net_bridge_mcast_port *pmctx,
443362306a36Sopenharmony_ci				 unsigned long val)
443462306a36Sopenharmony_ci{
443562306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx;
443662306a36Sopenharmony_ci	unsigned long now = jiffies;
443762306a36Sopenharmony_ci	int err = -EINVAL;
443862306a36Sopenharmony_ci	bool del = false;
443962306a36Sopenharmony_ci
444062306a36Sopenharmony_ci	brmctx = br_multicast_port_ctx_get_global(pmctx);
444162306a36Sopenharmony_ci	spin_lock_bh(&brmctx->br->multicast_lock);
444262306a36Sopenharmony_ci	if (pmctx->multicast_router == val) {
444362306a36Sopenharmony_ci		/* Refresh the temp router port timer */
444462306a36Sopenharmony_ci		if (pmctx->multicast_router == MDB_RTR_TYPE_TEMP) {
444562306a36Sopenharmony_ci			mod_timer(&pmctx->ip4_mc_router_timer,
444662306a36Sopenharmony_ci				  now + brmctx->multicast_querier_interval);
444762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
444862306a36Sopenharmony_ci			mod_timer(&pmctx->ip6_mc_router_timer,
444962306a36Sopenharmony_ci				  now + brmctx->multicast_querier_interval);
445062306a36Sopenharmony_ci#endif
445162306a36Sopenharmony_ci		}
445262306a36Sopenharmony_ci		err = 0;
445362306a36Sopenharmony_ci		goto unlock;
445462306a36Sopenharmony_ci	}
445562306a36Sopenharmony_ci	switch (val) {
445662306a36Sopenharmony_ci	case MDB_RTR_TYPE_DISABLED:
445762306a36Sopenharmony_ci		pmctx->multicast_router = MDB_RTR_TYPE_DISABLED;
445862306a36Sopenharmony_ci		del |= br_ip4_multicast_rport_del(pmctx);
445962306a36Sopenharmony_ci		del_timer(&pmctx->ip4_mc_router_timer);
446062306a36Sopenharmony_ci		del |= br_ip6_multicast_rport_del(pmctx);
446162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
446262306a36Sopenharmony_ci		del_timer(&pmctx->ip6_mc_router_timer);
446362306a36Sopenharmony_ci#endif
446462306a36Sopenharmony_ci		br_multicast_rport_del_notify(pmctx, del);
446562306a36Sopenharmony_ci		break;
446662306a36Sopenharmony_ci	case MDB_RTR_TYPE_TEMP_QUERY:
446762306a36Sopenharmony_ci		pmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
446862306a36Sopenharmony_ci		del |= br_ip4_multicast_rport_del(pmctx);
446962306a36Sopenharmony_ci		del |= br_ip6_multicast_rport_del(pmctx);
447062306a36Sopenharmony_ci		br_multicast_rport_del_notify(pmctx, del);
447162306a36Sopenharmony_ci		break;
447262306a36Sopenharmony_ci	case MDB_RTR_TYPE_PERM:
447362306a36Sopenharmony_ci		pmctx->multicast_router = MDB_RTR_TYPE_PERM;
447462306a36Sopenharmony_ci		del_timer(&pmctx->ip4_mc_router_timer);
447562306a36Sopenharmony_ci		br_ip4_multicast_add_router(brmctx, pmctx);
447662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
447762306a36Sopenharmony_ci		del_timer(&pmctx->ip6_mc_router_timer);
447862306a36Sopenharmony_ci#endif
447962306a36Sopenharmony_ci		br_ip6_multicast_add_router(brmctx, pmctx);
448062306a36Sopenharmony_ci		break;
448162306a36Sopenharmony_ci	case MDB_RTR_TYPE_TEMP:
448262306a36Sopenharmony_ci		pmctx->multicast_router = MDB_RTR_TYPE_TEMP;
448362306a36Sopenharmony_ci		br_ip4_multicast_mark_router(brmctx, pmctx);
448462306a36Sopenharmony_ci		br_ip6_multicast_mark_router(brmctx, pmctx);
448562306a36Sopenharmony_ci		break;
448662306a36Sopenharmony_ci	default:
448762306a36Sopenharmony_ci		goto unlock;
448862306a36Sopenharmony_ci	}
448962306a36Sopenharmony_ci	err = 0;
449062306a36Sopenharmony_ciunlock:
449162306a36Sopenharmony_ci	spin_unlock_bh(&brmctx->br->multicast_lock);
449262306a36Sopenharmony_ci
449362306a36Sopenharmony_ci	return err;
449462306a36Sopenharmony_ci}
449562306a36Sopenharmony_ci
449662306a36Sopenharmony_ciint br_multicast_set_vlan_router(struct net_bridge_vlan *v, u8 mcast_router)
449762306a36Sopenharmony_ci{
449862306a36Sopenharmony_ci	int err;
449962306a36Sopenharmony_ci
450062306a36Sopenharmony_ci	if (br_vlan_is_master(v))
450162306a36Sopenharmony_ci		err = br_multicast_set_router(&v->br_mcast_ctx, mcast_router);
450262306a36Sopenharmony_ci	else
450362306a36Sopenharmony_ci		err = br_multicast_set_port_router(&v->port_mcast_ctx,
450462306a36Sopenharmony_ci						   mcast_router);
450562306a36Sopenharmony_ci
450662306a36Sopenharmony_ci	return err;
450762306a36Sopenharmony_ci}
450862306a36Sopenharmony_ci
450962306a36Sopenharmony_cistatic void br_multicast_start_querier(struct net_bridge_mcast *brmctx,
451062306a36Sopenharmony_ci				       struct bridge_mcast_own_query *query)
451162306a36Sopenharmony_ci{
451262306a36Sopenharmony_ci	struct net_bridge_port *port;
451362306a36Sopenharmony_ci
451462306a36Sopenharmony_ci	if (!br_multicast_ctx_matches_vlan_snooping(brmctx))
451562306a36Sopenharmony_ci		return;
451662306a36Sopenharmony_ci
451762306a36Sopenharmony_ci	__br_multicast_open_query(brmctx->br, query);
451862306a36Sopenharmony_ci
451962306a36Sopenharmony_ci	rcu_read_lock();
452062306a36Sopenharmony_ci	list_for_each_entry_rcu(port, &brmctx->br->port_list, list) {
452162306a36Sopenharmony_ci		struct bridge_mcast_own_query *ip4_own_query;
452262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
452362306a36Sopenharmony_ci		struct bridge_mcast_own_query *ip6_own_query;
452462306a36Sopenharmony_ci#endif
452562306a36Sopenharmony_ci
452662306a36Sopenharmony_ci		if (br_multicast_port_ctx_state_stopped(&port->multicast_ctx))
452762306a36Sopenharmony_ci			continue;
452862306a36Sopenharmony_ci
452962306a36Sopenharmony_ci		if (br_multicast_ctx_is_vlan(brmctx)) {
453062306a36Sopenharmony_ci			struct net_bridge_vlan *vlan;
453162306a36Sopenharmony_ci
453262306a36Sopenharmony_ci			vlan = br_vlan_find(nbp_vlan_group_rcu(port),
453362306a36Sopenharmony_ci					    brmctx->vlan->vid);
453462306a36Sopenharmony_ci			if (!vlan ||
453562306a36Sopenharmony_ci			    br_multicast_port_ctx_state_stopped(&vlan->port_mcast_ctx))
453662306a36Sopenharmony_ci				continue;
453762306a36Sopenharmony_ci
453862306a36Sopenharmony_ci			ip4_own_query = &vlan->port_mcast_ctx.ip4_own_query;
453962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
454062306a36Sopenharmony_ci			ip6_own_query = &vlan->port_mcast_ctx.ip6_own_query;
454162306a36Sopenharmony_ci#endif
454262306a36Sopenharmony_ci		} else {
454362306a36Sopenharmony_ci			ip4_own_query = &port->multicast_ctx.ip4_own_query;
454462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
454562306a36Sopenharmony_ci			ip6_own_query = &port->multicast_ctx.ip6_own_query;
454662306a36Sopenharmony_ci#endif
454762306a36Sopenharmony_ci		}
454862306a36Sopenharmony_ci
454962306a36Sopenharmony_ci		if (query == &brmctx->ip4_own_query)
455062306a36Sopenharmony_ci			br_multicast_enable(ip4_own_query);
455162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
455262306a36Sopenharmony_ci		else
455362306a36Sopenharmony_ci			br_multicast_enable(ip6_own_query);
455462306a36Sopenharmony_ci#endif
455562306a36Sopenharmony_ci	}
455662306a36Sopenharmony_ci	rcu_read_unlock();
455762306a36Sopenharmony_ci}
455862306a36Sopenharmony_ci
455962306a36Sopenharmony_ciint br_multicast_toggle(struct net_bridge *br, unsigned long val,
456062306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
456162306a36Sopenharmony_ci{
456262306a36Sopenharmony_ci	struct net_bridge_port *port;
456362306a36Sopenharmony_ci	bool change_snoopers = false;
456462306a36Sopenharmony_ci	int err = 0;
456562306a36Sopenharmony_ci
456662306a36Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
456762306a36Sopenharmony_ci	if (!!br_opt_get(br, BROPT_MULTICAST_ENABLED) == !!val)
456862306a36Sopenharmony_ci		goto unlock;
456962306a36Sopenharmony_ci
457062306a36Sopenharmony_ci	err = br_mc_disabled_update(br->dev, val, extack);
457162306a36Sopenharmony_ci	if (err == -EOPNOTSUPP)
457262306a36Sopenharmony_ci		err = 0;
457362306a36Sopenharmony_ci	if (err)
457462306a36Sopenharmony_ci		goto unlock;
457562306a36Sopenharmony_ci
457662306a36Sopenharmony_ci	br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val);
457762306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) {
457862306a36Sopenharmony_ci		change_snoopers = true;
457962306a36Sopenharmony_ci		goto unlock;
458062306a36Sopenharmony_ci	}
458162306a36Sopenharmony_ci
458262306a36Sopenharmony_ci	if (!netif_running(br->dev))
458362306a36Sopenharmony_ci		goto unlock;
458462306a36Sopenharmony_ci
458562306a36Sopenharmony_ci	br_multicast_open(br);
458662306a36Sopenharmony_ci	list_for_each_entry(port, &br->port_list, list)
458762306a36Sopenharmony_ci		__br_multicast_enable_port_ctx(&port->multicast_ctx);
458862306a36Sopenharmony_ci
458962306a36Sopenharmony_ci	change_snoopers = true;
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_ciunlock:
459262306a36Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
459362306a36Sopenharmony_ci
459462306a36Sopenharmony_ci	/* br_multicast_join_snoopers has the potential to cause
459562306a36Sopenharmony_ci	 * an MLD Report/Leave to be delivered to br_multicast_rcv,
459662306a36Sopenharmony_ci	 * which would in turn call br_multicast_add_group, which would
459762306a36Sopenharmony_ci	 * attempt to acquire multicast_lock. This function should be
459862306a36Sopenharmony_ci	 * called after the lock has been released to avoid deadlocks on
459962306a36Sopenharmony_ci	 * multicast_lock.
460062306a36Sopenharmony_ci	 *
460162306a36Sopenharmony_ci	 * br_multicast_leave_snoopers does not have the problem since
460262306a36Sopenharmony_ci	 * br_multicast_rcv first checks BROPT_MULTICAST_ENABLED, and
460362306a36Sopenharmony_ci	 * returns without calling br_multicast_ipv4/6_rcv if it's not
460462306a36Sopenharmony_ci	 * enabled. Moved both functions out just for symmetry.
460562306a36Sopenharmony_ci	 */
460662306a36Sopenharmony_ci	if (change_snoopers) {
460762306a36Sopenharmony_ci		if (br_opt_get(br, BROPT_MULTICAST_ENABLED))
460862306a36Sopenharmony_ci			br_multicast_join_snoopers(br);
460962306a36Sopenharmony_ci		else
461062306a36Sopenharmony_ci			br_multicast_leave_snoopers(br);
461162306a36Sopenharmony_ci	}
461262306a36Sopenharmony_ci
461362306a36Sopenharmony_ci	return err;
461462306a36Sopenharmony_ci}
461562306a36Sopenharmony_ci
461662306a36Sopenharmony_cibool br_multicast_enabled(const struct net_device *dev)
461762306a36Sopenharmony_ci{
461862306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
461962306a36Sopenharmony_ci
462062306a36Sopenharmony_ci	return !!br_opt_get(br, BROPT_MULTICAST_ENABLED);
462162306a36Sopenharmony_ci}
462262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_enabled);
462362306a36Sopenharmony_ci
462462306a36Sopenharmony_cibool br_multicast_router(const struct net_device *dev)
462562306a36Sopenharmony_ci{
462662306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
462762306a36Sopenharmony_ci	bool is_router;
462862306a36Sopenharmony_ci
462962306a36Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
463062306a36Sopenharmony_ci	is_router = br_multicast_is_router(&br->multicast_ctx, NULL);
463162306a36Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
463262306a36Sopenharmony_ci	return is_router;
463362306a36Sopenharmony_ci}
463462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_router);
463562306a36Sopenharmony_ci
463662306a36Sopenharmony_ciint br_multicast_set_querier(struct net_bridge_mcast *brmctx, unsigned long val)
463762306a36Sopenharmony_ci{
463862306a36Sopenharmony_ci	unsigned long max_delay;
463962306a36Sopenharmony_ci
464062306a36Sopenharmony_ci	val = !!val;
464162306a36Sopenharmony_ci
464262306a36Sopenharmony_ci	spin_lock_bh(&brmctx->br->multicast_lock);
464362306a36Sopenharmony_ci	if (brmctx->multicast_querier == val)
464462306a36Sopenharmony_ci		goto unlock;
464562306a36Sopenharmony_ci
464662306a36Sopenharmony_ci	WRITE_ONCE(brmctx->multicast_querier, val);
464762306a36Sopenharmony_ci	if (!val)
464862306a36Sopenharmony_ci		goto unlock;
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_ci	max_delay = brmctx->multicast_query_response_interval;
465162306a36Sopenharmony_ci
465262306a36Sopenharmony_ci	if (!timer_pending(&brmctx->ip4_other_query.timer))
465362306a36Sopenharmony_ci		mod_timer(&brmctx->ip4_other_query.delay_timer,
465462306a36Sopenharmony_ci			  jiffies + max_delay);
465562306a36Sopenharmony_ci
465662306a36Sopenharmony_ci	br_multicast_start_querier(brmctx, &brmctx->ip4_own_query);
465762306a36Sopenharmony_ci
465862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
465962306a36Sopenharmony_ci	if (!timer_pending(&brmctx->ip6_other_query.timer))
466062306a36Sopenharmony_ci		mod_timer(&brmctx->ip6_other_query.delay_timer,
466162306a36Sopenharmony_ci			  jiffies + max_delay);
466262306a36Sopenharmony_ci
466362306a36Sopenharmony_ci	br_multicast_start_querier(brmctx, &brmctx->ip6_own_query);
466462306a36Sopenharmony_ci#endif
466562306a36Sopenharmony_ci
466662306a36Sopenharmony_ciunlock:
466762306a36Sopenharmony_ci	spin_unlock_bh(&brmctx->br->multicast_lock);
466862306a36Sopenharmony_ci
466962306a36Sopenharmony_ci	return 0;
467062306a36Sopenharmony_ci}
467162306a36Sopenharmony_ci
467262306a36Sopenharmony_ciint br_multicast_set_igmp_version(struct net_bridge_mcast *brmctx,
467362306a36Sopenharmony_ci				  unsigned long val)
467462306a36Sopenharmony_ci{
467562306a36Sopenharmony_ci	/* Currently we support only version 2 and 3 */
467662306a36Sopenharmony_ci	switch (val) {
467762306a36Sopenharmony_ci	case 2:
467862306a36Sopenharmony_ci	case 3:
467962306a36Sopenharmony_ci		break;
468062306a36Sopenharmony_ci	default:
468162306a36Sopenharmony_ci		return -EINVAL;
468262306a36Sopenharmony_ci	}
468362306a36Sopenharmony_ci
468462306a36Sopenharmony_ci	spin_lock_bh(&brmctx->br->multicast_lock);
468562306a36Sopenharmony_ci	brmctx->multicast_igmp_version = val;
468662306a36Sopenharmony_ci	spin_unlock_bh(&brmctx->br->multicast_lock);
468762306a36Sopenharmony_ci
468862306a36Sopenharmony_ci	return 0;
468962306a36Sopenharmony_ci}
469062306a36Sopenharmony_ci
469162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
469262306a36Sopenharmony_ciint br_multicast_set_mld_version(struct net_bridge_mcast *brmctx,
469362306a36Sopenharmony_ci				 unsigned long val)
469462306a36Sopenharmony_ci{
469562306a36Sopenharmony_ci	/* Currently we support version 1 and 2 */
469662306a36Sopenharmony_ci	switch (val) {
469762306a36Sopenharmony_ci	case 1:
469862306a36Sopenharmony_ci	case 2:
469962306a36Sopenharmony_ci		break;
470062306a36Sopenharmony_ci	default:
470162306a36Sopenharmony_ci		return -EINVAL;
470262306a36Sopenharmony_ci	}
470362306a36Sopenharmony_ci
470462306a36Sopenharmony_ci	spin_lock_bh(&brmctx->br->multicast_lock);
470562306a36Sopenharmony_ci	brmctx->multicast_mld_version = val;
470662306a36Sopenharmony_ci	spin_unlock_bh(&brmctx->br->multicast_lock);
470762306a36Sopenharmony_ci
470862306a36Sopenharmony_ci	return 0;
470962306a36Sopenharmony_ci}
471062306a36Sopenharmony_ci#endif
471162306a36Sopenharmony_ci
471262306a36Sopenharmony_civoid br_multicast_set_query_intvl(struct net_bridge_mcast *brmctx,
471362306a36Sopenharmony_ci				  unsigned long val)
471462306a36Sopenharmony_ci{
471562306a36Sopenharmony_ci	unsigned long intvl_jiffies = clock_t_to_jiffies(val);
471662306a36Sopenharmony_ci
471762306a36Sopenharmony_ci	if (intvl_jiffies < BR_MULTICAST_QUERY_INTVL_MIN) {
471862306a36Sopenharmony_ci		br_info(brmctx->br,
471962306a36Sopenharmony_ci			"trying to set multicast query interval below minimum, setting to %lu (%ums)\n",
472062306a36Sopenharmony_ci			jiffies_to_clock_t(BR_MULTICAST_QUERY_INTVL_MIN),
472162306a36Sopenharmony_ci			jiffies_to_msecs(BR_MULTICAST_QUERY_INTVL_MIN));
472262306a36Sopenharmony_ci		intvl_jiffies = BR_MULTICAST_QUERY_INTVL_MIN;
472362306a36Sopenharmony_ci	}
472462306a36Sopenharmony_ci
472562306a36Sopenharmony_ci	brmctx->multicast_query_interval = intvl_jiffies;
472662306a36Sopenharmony_ci}
472762306a36Sopenharmony_ci
472862306a36Sopenharmony_civoid br_multicast_set_startup_query_intvl(struct net_bridge_mcast *brmctx,
472962306a36Sopenharmony_ci					  unsigned long val)
473062306a36Sopenharmony_ci{
473162306a36Sopenharmony_ci	unsigned long intvl_jiffies = clock_t_to_jiffies(val);
473262306a36Sopenharmony_ci
473362306a36Sopenharmony_ci	if (intvl_jiffies < BR_MULTICAST_STARTUP_QUERY_INTVL_MIN) {
473462306a36Sopenharmony_ci		br_info(brmctx->br,
473562306a36Sopenharmony_ci			"trying to set multicast startup query interval below minimum, setting to %lu (%ums)\n",
473662306a36Sopenharmony_ci			jiffies_to_clock_t(BR_MULTICAST_STARTUP_QUERY_INTVL_MIN),
473762306a36Sopenharmony_ci			jiffies_to_msecs(BR_MULTICAST_STARTUP_QUERY_INTVL_MIN));
473862306a36Sopenharmony_ci		intvl_jiffies = BR_MULTICAST_STARTUP_QUERY_INTVL_MIN;
473962306a36Sopenharmony_ci	}
474062306a36Sopenharmony_ci
474162306a36Sopenharmony_ci	brmctx->multicast_startup_query_interval = intvl_jiffies;
474262306a36Sopenharmony_ci}
474362306a36Sopenharmony_ci
474462306a36Sopenharmony_ci/**
474562306a36Sopenharmony_ci * br_multicast_list_adjacent - Returns snooped multicast addresses
474662306a36Sopenharmony_ci * @dev:	The bridge port adjacent to which to retrieve addresses
474762306a36Sopenharmony_ci * @br_ip_list:	The list to store found, snooped multicast IP addresses in
474862306a36Sopenharmony_ci *
474962306a36Sopenharmony_ci * Creates a list of IP addresses (struct br_ip_list) sensed by the multicast
475062306a36Sopenharmony_ci * snooping feature on all bridge ports of dev's bridge device, excluding
475162306a36Sopenharmony_ci * the addresses from dev itself.
475262306a36Sopenharmony_ci *
475362306a36Sopenharmony_ci * Returns the number of items added to br_ip_list.
475462306a36Sopenharmony_ci *
475562306a36Sopenharmony_ci * Notes:
475662306a36Sopenharmony_ci * - br_ip_list needs to be initialized by caller
475762306a36Sopenharmony_ci * - br_ip_list might contain duplicates in the end
475862306a36Sopenharmony_ci *   (needs to be taken care of by caller)
475962306a36Sopenharmony_ci * - br_ip_list needs to be freed by caller
476062306a36Sopenharmony_ci */
476162306a36Sopenharmony_ciint br_multicast_list_adjacent(struct net_device *dev,
476262306a36Sopenharmony_ci			       struct list_head *br_ip_list)
476362306a36Sopenharmony_ci{
476462306a36Sopenharmony_ci	struct net_bridge *br;
476562306a36Sopenharmony_ci	struct net_bridge_port *port;
476662306a36Sopenharmony_ci	struct net_bridge_port_group *group;
476762306a36Sopenharmony_ci	struct br_ip_list *entry;
476862306a36Sopenharmony_ci	int count = 0;
476962306a36Sopenharmony_ci
477062306a36Sopenharmony_ci	rcu_read_lock();
477162306a36Sopenharmony_ci	if (!br_ip_list || !netif_is_bridge_port(dev))
477262306a36Sopenharmony_ci		goto unlock;
477362306a36Sopenharmony_ci
477462306a36Sopenharmony_ci	port = br_port_get_rcu(dev);
477562306a36Sopenharmony_ci	if (!port || !port->br)
477662306a36Sopenharmony_ci		goto unlock;
477762306a36Sopenharmony_ci
477862306a36Sopenharmony_ci	br = port->br;
477962306a36Sopenharmony_ci
478062306a36Sopenharmony_ci	list_for_each_entry_rcu(port, &br->port_list, list) {
478162306a36Sopenharmony_ci		if (!port->dev || port->dev == dev)
478262306a36Sopenharmony_ci			continue;
478362306a36Sopenharmony_ci
478462306a36Sopenharmony_ci		hlist_for_each_entry_rcu(group, &port->mglist, mglist) {
478562306a36Sopenharmony_ci			entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
478662306a36Sopenharmony_ci			if (!entry)
478762306a36Sopenharmony_ci				goto unlock;
478862306a36Sopenharmony_ci
478962306a36Sopenharmony_ci			entry->addr = group->key.addr;
479062306a36Sopenharmony_ci			list_add(&entry->list, br_ip_list);
479162306a36Sopenharmony_ci			count++;
479262306a36Sopenharmony_ci		}
479362306a36Sopenharmony_ci	}
479462306a36Sopenharmony_ci
479562306a36Sopenharmony_ciunlock:
479662306a36Sopenharmony_ci	rcu_read_unlock();
479762306a36Sopenharmony_ci	return count;
479862306a36Sopenharmony_ci}
479962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_list_adjacent);
480062306a36Sopenharmony_ci
480162306a36Sopenharmony_ci/**
480262306a36Sopenharmony_ci * br_multicast_has_querier_anywhere - Checks for a querier on a bridge
480362306a36Sopenharmony_ci * @dev: The bridge port providing the bridge on which to check for a querier
480462306a36Sopenharmony_ci * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
480562306a36Sopenharmony_ci *
480662306a36Sopenharmony_ci * Checks whether the given interface has a bridge on top and if so returns
480762306a36Sopenharmony_ci * true if a valid querier exists anywhere on the bridged link layer.
480862306a36Sopenharmony_ci * Otherwise returns false.
480962306a36Sopenharmony_ci */
481062306a36Sopenharmony_cibool br_multicast_has_querier_anywhere(struct net_device *dev, int proto)
481162306a36Sopenharmony_ci{
481262306a36Sopenharmony_ci	struct net_bridge *br;
481362306a36Sopenharmony_ci	struct net_bridge_port *port;
481462306a36Sopenharmony_ci	struct ethhdr eth;
481562306a36Sopenharmony_ci	bool ret = false;
481662306a36Sopenharmony_ci
481762306a36Sopenharmony_ci	rcu_read_lock();
481862306a36Sopenharmony_ci	if (!netif_is_bridge_port(dev))
481962306a36Sopenharmony_ci		goto unlock;
482062306a36Sopenharmony_ci
482162306a36Sopenharmony_ci	port = br_port_get_rcu(dev);
482262306a36Sopenharmony_ci	if (!port || !port->br)
482362306a36Sopenharmony_ci		goto unlock;
482462306a36Sopenharmony_ci
482562306a36Sopenharmony_ci	br = port->br;
482662306a36Sopenharmony_ci
482762306a36Sopenharmony_ci	memset(&eth, 0, sizeof(eth));
482862306a36Sopenharmony_ci	eth.h_proto = htons(proto);
482962306a36Sopenharmony_ci
483062306a36Sopenharmony_ci	ret = br_multicast_querier_exists(&br->multicast_ctx, &eth, NULL);
483162306a36Sopenharmony_ci
483262306a36Sopenharmony_ciunlock:
483362306a36Sopenharmony_ci	rcu_read_unlock();
483462306a36Sopenharmony_ci	return ret;
483562306a36Sopenharmony_ci}
483662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_has_querier_anywhere);
483762306a36Sopenharmony_ci
483862306a36Sopenharmony_ci/**
483962306a36Sopenharmony_ci * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port
484062306a36Sopenharmony_ci * @dev: The bridge port adjacent to which to check for a querier
484162306a36Sopenharmony_ci * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
484262306a36Sopenharmony_ci *
484362306a36Sopenharmony_ci * Checks whether the given interface has a bridge on top and if so returns
484462306a36Sopenharmony_ci * true if a selected querier is behind one of the other ports of this
484562306a36Sopenharmony_ci * bridge. Otherwise returns false.
484662306a36Sopenharmony_ci */
484762306a36Sopenharmony_cibool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
484862306a36Sopenharmony_ci{
484962306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx;
485062306a36Sopenharmony_ci	struct net_bridge *br;
485162306a36Sopenharmony_ci	struct net_bridge_port *port;
485262306a36Sopenharmony_ci	bool ret = false;
485362306a36Sopenharmony_ci	int port_ifidx;
485462306a36Sopenharmony_ci
485562306a36Sopenharmony_ci	rcu_read_lock();
485662306a36Sopenharmony_ci	if (!netif_is_bridge_port(dev))
485762306a36Sopenharmony_ci		goto unlock;
485862306a36Sopenharmony_ci
485962306a36Sopenharmony_ci	port = br_port_get_rcu(dev);
486062306a36Sopenharmony_ci	if (!port || !port->br)
486162306a36Sopenharmony_ci		goto unlock;
486262306a36Sopenharmony_ci
486362306a36Sopenharmony_ci	br = port->br;
486462306a36Sopenharmony_ci	brmctx = &br->multicast_ctx;
486562306a36Sopenharmony_ci
486662306a36Sopenharmony_ci	switch (proto) {
486762306a36Sopenharmony_ci	case ETH_P_IP:
486862306a36Sopenharmony_ci		port_ifidx = brmctx->ip4_querier.port_ifidx;
486962306a36Sopenharmony_ci		if (!timer_pending(&brmctx->ip4_other_query.timer) ||
487062306a36Sopenharmony_ci		    port_ifidx == port->dev->ifindex)
487162306a36Sopenharmony_ci			goto unlock;
487262306a36Sopenharmony_ci		break;
487362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
487462306a36Sopenharmony_ci	case ETH_P_IPV6:
487562306a36Sopenharmony_ci		port_ifidx = brmctx->ip6_querier.port_ifidx;
487662306a36Sopenharmony_ci		if (!timer_pending(&brmctx->ip6_other_query.timer) ||
487762306a36Sopenharmony_ci		    port_ifidx == port->dev->ifindex)
487862306a36Sopenharmony_ci			goto unlock;
487962306a36Sopenharmony_ci		break;
488062306a36Sopenharmony_ci#endif
488162306a36Sopenharmony_ci	default:
488262306a36Sopenharmony_ci		goto unlock;
488362306a36Sopenharmony_ci	}
488462306a36Sopenharmony_ci
488562306a36Sopenharmony_ci	ret = true;
488662306a36Sopenharmony_ciunlock:
488762306a36Sopenharmony_ci	rcu_read_unlock();
488862306a36Sopenharmony_ci	return ret;
488962306a36Sopenharmony_ci}
489062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent);
489162306a36Sopenharmony_ci
489262306a36Sopenharmony_ci/**
489362306a36Sopenharmony_ci * br_multicast_has_router_adjacent - Checks for a router behind a bridge port
489462306a36Sopenharmony_ci * @dev: The bridge port adjacent to which to check for a multicast router
489562306a36Sopenharmony_ci * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
489662306a36Sopenharmony_ci *
489762306a36Sopenharmony_ci * Checks whether the given interface has a bridge on top and if so returns
489862306a36Sopenharmony_ci * true if a multicast router is behind one of the other ports of this
489962306a36Sopenharmony_ci * bridge. Otherwise returns false.
490062306a36Sopenharmony_ci */
490162306a36Sopenharmony_cibool br_multicast_has_router_adjacent(struct net_device *dev, int proto)
490262306a36Sopenharmony_ci{
490362306a36Sopenharmony_ci	struct net_bridge_mcast_port *pmctx;
490462306a36Sopenharmony_ci	struct net_bridge_mcast *brmctx;
490562306a36Sopenharmony_ci	struct net_bridge_port *port;
490662306a36Sopenharmony_ci	bool ret = false;
490762306a36Sopenharmony_ci
490862306a36Sopenharmony_ci	rcu_read_lock();
490962306a36Sopenharmony_ci	port = br_port_get_check_rcu(dev);
491062306a36Sopenharmony_ci	if (!port)
491162306a36Sopenharmony_ci		goto unlock;
491262306a36Sopenharmony_ci
491362306a36Sopenharmony_ci	brmctx = &port->br->multicast_ctx;
491462306a36Sopenharmony_ci	switch (proto) {
491562306a36Sopenharmony_ci	case ETH_P_IP:
491662306a36Sopenharmony_ci		hlist_for_each_entry_rcu(pmctx, &brmctx->ip4_mc_router_list,
491762306a36Sopenharmony_ci					 ip4_rlist) {
491862306a36Sopenharmony_ci			if (pmctx->port == port)
491962306a36Sopenharmony_ci				continue;
492062306a36Sopenharmony_ci
492162306a36Sopenharmony_ci			ret = true;
492262306a36Sopenharmony_ci			goto unlock;
492362306a36Sopenharmony_ci		}
492462306a36Sopenharmony_ci		break;
492562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
492662306a36Sopenharmony_ci	case ETH_P_IPV6:
492762306a36Sopenharmony_ci		hlist_for_each_entry_rcu(pmctx, &brmctx->ip6_mc_router_list,
492862306a36Sopenharmony_ci					 ip6_rlist) {
492962306a36Sopenharmony_ci			if (pmctx->port == port)
493062306a36Sopenharmony_ci				continue;
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci			ret = true;
493362306a36Sopenharmony_ci			goto unlock;
493462306a36Sopenharmony_ci		}
493562306a36Sopenharmony_ci		break;
493662306a36Sopenharmony_ci#endif
493762306a36Sopenharmony_ci	default:
493862306a36Sopenharmony_ci		/* when compiled without IPv6 support, be conservative and
493962306a36Sopenharmony_ci		 * always assume presence of an IPv6 multicast router
494062306a36Sopenharmony_ci		 */
494162306a36Sopenharmony_ci		ret = true;
494262306a36Sopenharmony_ci	}
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ciunlock:
494562306a36Sopenharmony_ci	rcu_read_unlock();
494662306a36Sopenharmony_ci	return ret;
494762306a36Sopenharmony_ci}
494862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_has_router_adjacent);
494962306a36Sopenharmony_ci
495062306a36Sopenharmony_cistatic void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
495162306a36Sopenharmony_ci			       const struct sk_buff *skb, u8 type, u8 dir)
495262306a36Sopenharmony_ci{
495362306a36Sopenharmony_ci	struct bridge_mcast_stats *pstats = this_cpu_ptr(stats);
495462306a36Sopenharmony_ci	__be16 proto = skb->protocol;
495562306a36Sopenharmony_ci	unsigned int t_len;
495662306a36Sopenharmony_ci
495762306a36Sopenharmony_ci	u64_stats_update_begin(&pstats->syncp);
495862306a36Sopenharmony_ci	switch (proto) {
495962306a36Sopenharmony_ci	case htons(ETH_P_IP):
496062306a36Sopenharmony_ci		t_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb);
496162306a36Sopenharmony_ci		switch (type) {
496262306a36Sopenharmony_ci		case IGMP_HOST_MEMBERSHIP_REPORT:
496362306a36Sopenharmony_ci			pstats->mstats.igmp_v1reports[dir]++;
496462306a36Sopenharmony_ci			break;
496562306a36Sopenharmony_ci		case IGMPV2_HOST_MEMBERSHIP_REPORT:
496662306a36Sopenharmony_ci			pstats->mstats.igmp_v2reports[dir]++;
496762306a36Sopenharmony_ci			break;
496862306a36Sopenharmony_ci		case IGMPV3_HOST_MEMBERSHIP_REPORT:
496962306a36Sopenharmony_ci			pstats->mstats.igmp_v3reports[dir]++;
497062306a36Sopenharmony_ci			break;
497162306a36Sopenharmony_ci		case IGMP_HOST_MEMBERSHIP_QUERY:
497262306a36Sopenharmony_ci			if (t_len != sizeof(struct igmphdr)) {
497362306a36Sopenharmony_ci				pstats->mstats.igmp_v3queries[dir]++;
497462306a36Sopenharmony_ci			} else {
497562306a36Sopenharmony_ci				unsigned int offset = skb_transport_offset(skb);
497662306a36Sopenharmony_ci				struct igmphdr *ih, _ihdr;
497762306a36Sopenharmony_ci
497862306a36Sopenharmony_ci				ih = skb_header_pointer(skb, offset,
497962306a36Sopenharmony_ci							sizeof(_ihdr), &_ihdr);
498062306a36Sopenharmony_ci				if (!ih)
498162306a36Sopenharmony_ci					break;
498262306a36Sopenharmony_ci				if (!ih->code)
498362306a36Sopenharmony_ci					pstats->mstats.igmp_v1queries[dir]++;
498462306a36Sopenharmony_ci				else
498562306a36Sopenharmony_ci					pstats->mstats.igmp_v2queries[dir]++;
498662306a36Sopenharmony_ci			}
498762306a36Sopenharmony_ci			break;
498862306a36Sopenharmony_ci		case IGMP_HOST_LEAVE_MESSAGE:
498962306a36Sopenharmony_ci			pstats->mstats.igmp_leaves[dir]++;
499062306a36Sopenharmony_ci			break;
499162306a36Sopenharmony_ci		}
499262306a36Sopenharmony_ci		break;
499362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
499462306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
499562306a36Sopenharmony_ci		t_len = ntohs(ipv6_hdr(skb)->payload_len) +
499662306a36Sopenharmony_ci			sizeof(struct ipv6hdr);
499762306a36Sopenharmony_ci		t_len -= skb_network_header_len(skb);
499862306a36Sopenharmony_ci		switch (type) {
499962306a36Sopenharmony_ci		case ICMPV6_MGM_REPORT:
500062306a36Sopenharmony_ci			pstats->mstats.mld_v1reports[dir]++;
500162306a36Sopenharmony_ci			break;
500262306a36Sopenharmony_ci		case ICMPV6_MLD2_REPORT:
500362306a36Sopenharmony_ci			pstats->mstats.mld_v2reports[dir]++;
500462306a36Sopenharmony_ci			break;
500562306a36Sopenharmony_ci		case ICMPV6_MGM_QUERY:
500662306a36Sopenharmony_ci			if (t_len != sizeof(struct mld_msg))
500762306a36Sopenharmony_ci				pstats->mstats.mld_v2queries[dir]++;
500862306a36Sopenharmony_ci			else
500962306a36Sopenharmony_ci				pstats->mstats.mld_v1queries[dir]++;
501062306a36Sopenharmony_ci			break;
501162306a36Sopenharmony_ci		case ICMPV6_MGM_REDUCTION:
501262306a36Sopenharmony_ci			pstats->mstats.mld_leaves[dir]++;
501362306a36Sopenharmony_ci			break;
501462306a36Sopenharmony_ci		}
501562306a36Sopenharmony_ci		break;
501662306a36Sopenharmony_ci#endif /* CONFIG_IPV6 */
501762306a36Sopenharmony_ci	}
501862306a36Sopenharmony_ci	u64_stats_update_end(&pstats->syncp);
501962306a36Sopenharmony_ci}
502062306a36Sopenharmony_ci
502162306a36Sopenharmony_civoid br_multicast_count(struct net_bridge *br,
502262306a36Sopenharmony_ci			const struct net_bridge_port *p,
502362306a36Sopenharmony_ci			const struct sk_buff *skb, u8 type, u8 dir)
502462306a36Sopenharmony_ci{
502562306a36Sopenharmony_ci	struct bridge_mcast_stats __percpu *stats;
502662306a36Sopenharmony_ci
502762306a36Sopenharmony_ci	/* if multicast_disabled is true then igmp type can't be set */
502862306a36Sopenharmony_ci	if (!type || !br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED))
502962306a36Sopenharmony_ci		return;
503062306a36Sopenharmony_ci
503162306a36Sopenharmony_ci	if (p)
503262306a36Sopenharmony_ci		stats = p->mcast_stats;
503362306a36Sopenharmony_ci	else
503462306a36Sopenharmony_ci		stats = br->mcast_stats;
503562306a36Sopenharmony_ci	if (WARN_ON(!stats))
503662306a36Sopenharmony_ci		return;
503762306a36Sopenharmony_ci
503862306a36Sopenharmony_ci	br_mcast_stats_add(stats, skb, type, dir);
503962306a36Sopenharmony_ci}
504062306a36Sopenharmony_ci
504162306a36Sopenharmony_ciint br_multicast_init_stats(struct net_bridge *br)
504262306a36Sopenharmony_ci{
504362306a36Sopenharmony_ci	br->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats);
504462306a36Sopenharmony_ci	if (!br->mcast_stats)
504562306a36Sopenharmony_ci		return -ENOMEM;
504662306a36Sopenharmony_ci
504762306a36Sopenharmony_ci	return 0;
504862306a36Sopenharmony_ci}
504962306a36Sopenharmony_ci
505062306a36Sopenharmony_civoid br_multicast_uninit_stats(struct net_bridge *br)
505162306a36Sopenharmony_ci{
505262306a36Sopenharmony_ci	free_percpu(br->mcast_stats);
505362306a36Sopenharmony_ci}
505462306a36Sopenharmony_ci
505562306a36Sopenharmony_ci/* noinline for https://bugs.llvm.org/show_bug.cgi?id=45802#c9 */
505662306a36Sopenharmony_cistatic noinline_for_stack void mcast_stats_add_dir(u64 *dst, u64 *src)
505762306a36Sopenharmony_ci{
505862306a36Sopenharmony_ci	dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX];
505962306a36Sopenharmony_ci	dst[BR_MCAST_DIR_TX] += src[BR_MCAST_DIR_TX];
506062306a36Sopenharmony_ci}
506162306a36Sopenharmony_ci
506262306a36Sopenharmony_civoid br_multicast_get_stats(const struct net_bridge *br,
506362306a36Sopenharmony_ci			    const struct net_bridge_port *p,
506462306a36Sopenharmony_ci			    struct br_mcast_stats *dest)
506562306a36Sopenharmony_ci{
506662306a36Sopenharmony_ci	struct bridge_mcast_stats __percpu *stats;
506762306a36Sopenharmony_ci	struct br_mcast_stats tdst;
506862306a36Sopenharmony_ci	int i;
506962306a36Sopenharmony_ci
507062306a36Sopenharmony_ci	memset(dest, 0, sizeof(*dest));
507162306a36Sopenharmony_ci	if (p)
507262306a36Sopenharmony_ci		stats = p->mcast_stats;
507362306a36Sopenharmony_ci	else
507462306a36Sopenharmony_ci		stats = br->mcast_stats;
507562306a36Sopenharmony_ci	if (WARN_ON(!stats))
507662306a36Sopenharmony_ci		return;
507762306a36Sopenharmony_ci
507862306a36Sopenharmony_ci	memset(&tdst, 0, sizeof(tdst));
507962306a36Sopenharmony_ci	for_each_possible_cpu(i) {
508062306a36Sopenharmony_ci		struct bridge_mcast_stats *cpu_stats = per_cpu_ptr(stats, i);
508162306a36Sopenharmony_ci		struct br_mcast_stats temp;
508262306a36Sopenharmony_ci		unsigned int start;
508362306a36Sopenharmony_ci
508462306a36Sopenharmony_ci		do {
508562306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&cpu_stats->syncp);
508662306a36Sopenharmony_ci			memcpy(&temp, &cpu_stats->mstats, sizeof(temp));
508762306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&cpu_stats->syncp, start));
508862306a36Sopenharmony_ci
508962306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v1queries, temp.igmp_v1queries);
509062306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v2queries, temp.igmp_v2queries);
509162306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v3queries, temp.igmp_v3queries);
509262306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_leaves, temp.igmp_leaves);
509362306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v1reports, temp.igmp_v1reports);
509462306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v2reports, temp.igmp_v2reports);
509562306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v3reports, temp.igmp_v3reports);
509662306a36Sopenharmony_ci		tdst.igmp_parse_errors += temp.igmp_parse_errors;
509762306a36Sopenharmony_ci
509862306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_v1queries, temp.mld_v1queries);
509962306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_v2queries, temp.mld_v2queries);
510062306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_leaves, temp.mld_leaves);
510162306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_v1reports, temp.mld_v1reports);
510262306a36Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_v2reports, temp.mld_v2reports);
510362306a36Sopenharmony_ci		tdst.mld_parse_errors += temp.mld_parse_errors;
510462306a36Sopenharmony_ci	}
510562306a36Sopenharmony_ci	memcpy(dest, &tdst, sizeof(*dest));
510662306a36Sopenharmony_ci}
510762306a36Sopenharmony_ci
510862306a36Sopenharmony_ciint br_mdb_hash_init(struct net_bridge *br)
510962306a36Sopenharmony_ci{
511062306a36Sopenharmony_ci	int err;
511162306a36Sopenharmony_ci
511262306a36Sopenharmony_ci	err = rhashtable_init(&br->sg_port_tbl, &br_sg_port_rht_params);
511362306a36Sopenharmony_ci	if (err)
511462306a36Sopenharmony_ci		return err;
511562306a36Sopenharmony_ci
511662306a36Sopenharmony_ci	err = rhashtable_init(&br->mdb_hash_tbl, &br_mdb_rht_params);
511762306a36Sopenharmony_ci	if (err) {
511862306a36Sopenharmony_ci		rhashtable_destroy(&br->sg_port_tbl);
511962306a36Sopenharmony_ci		return err;
512062306a36Sopenharmony_ci	}
512162306a36Sopenharmony_ci
512262306a36Sopenharmony_ci	return 0;
512362306a36Sopenharmony_ci}
512462306a36Sopenharmony_ci
512562306a36Sopenharmony_civoid br_mdb_hash_fini(struct net_bridge *br)
512662306a36Sopenharmony_ci{
512762306a36Sopenharmony_ci	rhashtable_destroy(&br->sg_port_tbl);
512862306a36Sopenharmony_ci	rhashtable_destroy(&br->mdb_hash_tbl);
512962306a36Sopenharmony_ci}
5130