18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Bridge multicast support.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/export.h>
108c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
118c2ecf20Sopenharmony_ci#include <linux/igmp.h>
128c2ecf20Sopenharmony_ci#include <linux/in.h>
138c2ecf20Sopenharmony_ci#include <linux/jhash.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/log2.h>
168c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
178c2ecf20Sopenharmony_ci#include <linux/netfilter_bridge.h>
188c2ecf20Sopenharmony_ci#include <linux/random.h>
198c2ecf20Sopenharmony_ci#include <linux/rculist.h>
208c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/timer.h>
238c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
248c2ecf20Sopenharmony_ci#include <linux/mroute.h>
258c2ecf20Sopenharmony_ci#include <net/ip.h>
268c2ecf20Sopenharmony_ci#include <net/switchdev.h>
278c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
288c2ecf20Sopenharmony_ci#include <linux/icmpv6.h>
298c2ecf20Sopenharmony_ci#include <net/ipv6.h>
308c2ecf20Sopenharmony_ci#include <net/mld.h>
318c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h>
328c2ecf20Sopenharmony_ci#include <net/addrconf.h>
338c2ecf20Sopenharmony_ci#endif
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "br_private.h"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic const struct rhashtable_params br_mdb_rht_params = {
388c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct net_bridge_mdb_entry, rhnode),
398c2ecf20Sopenharmony_ci	.key_offset = offsetof(struct net_bridge_mdb_entry, addr),
408c2ecf20Sopenharmony_ci	.key_len = sizeof(struct br_ip),
418c2ecf20Sopenharmony_ci	.automatic_shrinking = true,
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic const struct rhashtable_params br_sg_port_rht_params = {
458c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct net_bridge_port_group, rhnode),
468c2ecf20Sopenharmony_ci	.key_offset = offsetof(struct net_bridge_port_group, key),
478c2ecf20Sopenharmony_ci	.key_len = sizeof(struct net_bridge_port_group_sg_key),
488c2ecf20Sopenharmony_ci	.automatic_shrinking = true,
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void br_multicast_start_querier(struct net_bridge *br,
528c2ecf20Sopenharmony_ci				       struct bridge_mcast_own_query *query);
538c2ecf20Sopenharmony_cistatic void br_multicast_add_router(struct net_bridge *br,
548c2ecf20Sopenharmony_ci				    struct net_bridge_port *port);
558c2ecf20Sopenharmony_cistatic void br_ip4_multicast_leave_group(struct net_bridge *br,
568c2ecf20Sopenharmony_ci					 struct net_bridge_port *port,
578c2ecf20Sopenharmony_ci					 __be32 group,
588c2ecf20Sopenharmony_ci					 __u16 vid,
598c2ecf20Sopenharmony_ci					 const unsigned char *src);
608c2ecf20Sopenharmony_cistatic void br_multicast_port_group_rexmit(struct timer_list *t);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void __del_port_router(struct net_bridge_port *p);
638c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
648c2ecf20Sopenharmony_cistatic void br_ip6_multicast_leave_group(struct net_bridge *br,
658c2ecf20Sopenharmony_ci					 struct net_bridge_port *port,
668c2ecf20Sopenharmony_ci					 const struct in6_addr *group,
678c2ecf20Sopenharmony_ci					 __u16 vid, const unsigned char *src);
688c2ecf20Sopenharmony_ci#endif
698c2ecf20Sopenharmony_cistatic struct net_bridge_port_group *
708c2ecf20Sopenharmony_ci__br_multicast_add_group(struct net_bridge *br,
718c2ecf20Sopenharmony_ci			 struct net_bridge_port *port,
728c2ecf20Sopenharmony_ci			 struct br_ip *group,
738c2ecf20Sopenharmony_ci			 const unsigned char *src,
748c2ecf20Sopenharmony_ci			 u8 filter_mode,
758c2ecf20Sopenharmony_ci			 bool igmpv2_mldv1,
768c2ecf20Sopenharmony_ci			 bool blocked);
778c2ecf20Sopenharmony_cistatic void br_multicast_find_del_pg(struct net_bridge *br,
788c2ecf20Sopenharmony_ci				     struct net_bridge_port_group *pg);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic struct net_bridge_port_group *
818c2ecf20Sopenharmony_cibr_sg_port_find(struct net_bridge *br,
828c2ecf20Sopenharmony_ci		struct net_bridge_port_group_sg_key *sg_p)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	lockdep_assert_held_once(&br->multicast_lock);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return rhashtable_lookup_fast(&br->sg_port_tbl, sg_p,
878c2ecf20Sopenharmony_ci				      br_sg_port_rht_params);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic struct net_bridge_mdb_entry *br_mdb_ip_get_rcu(struct net_bridge *br,
918c2ecf20Sopenharmony_ci						      struct br_ip *dst)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	return rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge *br,
978c2ecf20Sopenharmony_ci					   struct br_ip *dst)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *ent;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	lockdep_assert_held_once(&br->multicast_lock);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	rcu_read_lock();
1048c2ecf20Sopenharmony_ci	ent = rhashtable_lookup(&br->mdb_hash_tbl, dst, br_mdb_rht_params);
1058c2ecf20Sopenharmony_ci	rcu_read_unlock();
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return ent;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic struct net_bridge_mdb_entry *br_mdb_ip4_get(struct net_bridge *br,
1118c2ecf20Sopenharmony_ci						   __be32 dst, __u16 vid)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct br_ip br_dst;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	memset(&br_dst, 0, sizeof(br_dst));
1168c2ecf20Sopenharmony_ci	br_dst.dst.ip4 = dst;
1178c2ecf20Sopenharmony_ci	br_dst.proto = htons(ETH_P_IP);
1188c2ecf20Sopenharmony_ci	br_dst.vid = vid;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return br_mdb_ip_get(br, &br_dst);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1248c2ecf20Sopenharmony_cistatic struct net_bridge_mdb_entry *br_mdb_ip6_get(struct net_bridge *br,
1258c2ecf20Sopenharmony_ci						   const struct in6_addr *dst,
1268c2ecf20Sopenharmony_ci						   __u16 vid)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct br_ip br_dst;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	memset(&br_dst, 0, sizeof(br_dst));
1318c2ecf20Sopenharmony_ci	br_dst.dst.ip6 = *dst;
1328c2ecf20Sopenharmony_ci	br_dst.proto = htons(ETH_P_IPV6);
1338c2ecf20Sopenharmony_ci	br_dst.vid = vid;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return br_mdb_ip_get(br, &br_dst);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci#endif
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistruct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br,
1408c2ecf20Sopenharmony_ci					struct sk_buff *skb, u16 vid)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct br_ip ip;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
1458c2ecf20Sopenharmony_ci		return NULL;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (BR_INPUT_SKB_CB(skb)->igmp)
1488c2ecf20Sopenharmony_ci		return NULL;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	memset(&ip, 0, sizeof(ip));
1518c2ecf20Sopenharmony_ci	ip.proto = skb->protocol;
1528c2ecf20Sopenharmony_ci	ip.vid = vid;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	switch (skb->protocol) {
1558c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
1568c2ecf20Sopenharmony_ci		ip.dst.ip4 = ip_hdr(skb)->daddr;
1578c2ecf20Sopenharmony_ci		if (br->multicast_igmp_version == 3) {
1588c2ecf20Sopenharmony_ci			struct net_bridge_mdb_entry *mdb;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci			ip.src.ip4 = ip_hdr(skb)->saddr;
1618c2ecf20Sopenharmony_ci			mdb = br_mdb_ip_get_rcu(br, &ip);
1628c2ecf20Sopenharmony_ci			if (mdb)
1638c2ecf20Sopenharmony_ci				return mdb;
1648c2ecf20Sopenharmony_ci			ip.src.ip4 = 0;
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1688c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
1698c2ecf20Sopenharmony_ci		ip.dst.ip6 = ipv6_hdr(skb)->daddr;
1708c2ecf20Sopenharmony_ci		if (br->multicast_mld_version == 2) {
1718c2ecf20Sopenharmony_ci			struct net_bridge_mdb_entry *mdb;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci			ip.src.ip6 = ipv6_hdr(skb)->saddr;
1748c2ecf20Sopenharmony_ci			mdb = br_mdb_ip_get_rcu(br, &ip);
1758c2ecf20Sopenharmony_ci			if (mdb)
1768c2ecf20Sopenharmony_ci				return mdb;
1778c2ecf20Sopenharmony_ci			memset(&ip.src.ip6, 0, sizeof(ip.src.ip6));
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci		break;
1808c2ecf20Sopenharmony_ci#endif
1818c2ecf20Sopenharmony_ci	default:
1828c2ecf20Sopenharmony_ci		return NULL;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return br_mdb_ip_get_rcu(br, &ip);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic bool br_port_group_equal(struct net_bridge_port_group *p,
1898c2ecf20Sopenharmony_ci				struct net_bridge_port *port,
1908c2ecf20Sopenharmony_ci				const unsigned char *src)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	if (p->key.port != port)
1938c2ecf20Sopenharmony_ci		return false;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (!(port->flags & BR_MULTICAST_TO_UNICAST))
1968c2ecf20Sopenharmony_ci		return true;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return ether_addr_equal(src, p->eth_addr);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic void __fwd_add_star_excl(struct net_bridge_port_group *pg,
2028c2ecf20Sopenharmony_ci				struct br_ip *sg_ip)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct net_bridge_port_group_sg_key sg_key;
2058c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
2068c2ecf20Sopenharmony_ci	struct net_bridge_port_group *src_pg;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	memset(&sg_key, 0, sizeof(sg_key));
2098c2ecf20Sopenharmony_ci	sg_key.port = pg->key.port;
2108c2ecf20Sopenharmony_ci	sg_key.addr = *sg_ip;
2118c2ecf20Sopenharmony_ci	if (br_sg_port_find(br, &sg_key))
2128c2ecf20Sopenharmony_ci		return;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	src_pg = __br_multicast_add_group(br, pg->key.port, sg_ip, pg->eth_addr,
2158c2ecf20Sopenharmony_ci					  MCAST_INCLUDE, false, false);
2168c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(src_pg) ||
2178c2ecf20Sopenharmony_ci	    src_pg->rt_protocol != RTPROT_KERNEL)
2188c2ecf20Sopenharmony_ci		return;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void __fwd_del_star_excl(struct net_bridge_port_group *pg,
2248c2ecf20Sopenharmony_ci				struct br_ip *sg_ip)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct net_bridge_port_group_sg_key sg_key;
2278c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
2288c2ecf20Sopenharmony_ci	struct net_bridge_port_group *src_pg;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	memset(&sg_key, 0, sizeof(sg_key));
2318c2ecf20Sopenharmony_ci	sg_key.port = pg->key.port;
2328c2ecf20Sopenharmony_ci	sg_key.addr = *sg_ip;
2338c2ecf20Sopenharmony_ci	src_pg = br_sg_port_find(br, &sg_key);
2348c2ecf20Sopenharmony_ci	if (!src_pg || !(src_pg->flags & MDB_PG_FLAGS_STAR_EXCL) ||
2358c2ecf20Sopenharmony_ci	    src_pg->rt_protocol != RTPROT_KERNEL)
2368c2ecf20Sopenharmony_ci		return;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	br_multicast_find_del_pg(br, src_pg);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/* When a port group transitions to (or is added as) EXCLUDE we need to add it
2428c2ecf20Sopenharmony_ci * to all other ports' S,G entries which are not blocked by the current group
2438c2ecf20Sopenharmony_ci * for proper replication, the assumption is that any S,G blocked entries
2448c2ecf20Sopenharmony_ci * are already added so the S,G,port lookup should skip them.
2458c2ecf20Sopenharmony_ci * When a port group transitions from EXCLUDE -> INCLUDE mode or is being
2468c2ecf20Sopenharmony_ci * deleted we need to remove it from all ports' S,G entries where it was
2478c2ecf20Sopenharmony_ci * automatically installed before (i.e. where it's MDB_PG_FLAGS_STAR_EXCL).
2488c2ecf20Sopenharmony_ci */
2498c2ecf20Sopenharmony_civoid br_multicast_star_g_handle_mode(struct net_bridge_port_group *pg,
2508c2ecf20Sopenharmony_ci				     u8 filter_mode)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
2538c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg_lst;
2548c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
2558c2ecf20Sopenharmony_ci	struct br_ip sg_ip;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (WARN_ON(!br_multicast_is_star_g(&pg->key.addr)))
2588c2ecf20Sopenharmony_ci		return;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	mp = br_mdb_ip_get(br, &pg->key.addr);
2618c2ecf20Sopenharmony_ci	if (!mp)
2628c2ecf20Sopenharmony_ci		return;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	memset(&sg_ip, 0, sizeof(sg_ip));
2658c2ecf20Sopenharmony_ci	sg_ip = pg->key.addr;
2668c2ecf20Sopenharmony_ci	for (pg_lst = mlock_dereference(mp->ports, br);
2678c2ecf20Sopenharmony_ci	     pg_lst;
2688c2ecf20Sopenharmony_ci	     pg_lst = mlock_dereference(pg_lst->next, br)) {
2698c2ecf20Sopenharmony_ci		struct net_bridge_group_src *src_ent;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		if (pg_lst == pg)
2728c2ecf20Sopenharmony_ci			continue;
2738c2ecf20Sopenharmony_ci		hlist_for_each_entry(src_ent, &pg_lst->src_list, node) {
2748c2ecf20Sopenharmony_ci			if (!(src_ent->flags & BR_SGRP_F_INSTALLED))
2758c2ecf20Sopenharmony_ci				continue;
2768c2ecf20Sopenharmony_ci			sg_ip.src = src_ent->addr.src;
2778c2ecf20Sopenharmony_ci			switch (filter_mode) {
2788c2ecf20Sopenharmony_ci			case MCAST_INCLUDE:
2798c2ecf20Sopenharmony_ci				__fwd_del_star_excl(pg, &sg_ip);
2808c2ecf20Sopenharmony_ci				break;
2818c2ecf20Sopenharmony_ci			case MCAST_EXCLUDE:
2828c2ecf20Sopenharmony_ci				__fwd_add_star_excl(pg, &sg_ip);
2838c2ecf20Sopenharmony_ci				break;
2848c2ecf20Sopenharmony_ci			}
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/* called when adding a new S,G with host_joined == false by default */
2908c2ecf20Sopenharmony_cistatic void br_multicast_sg_host_state(struct net_bridge_mdb_entry *star_mp,
2918c2ecf20Sopenharmony_ci				       struct net_bridge_port_group *sg)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *sg_mp;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr)))
2968c2ecf20Sopenharmony_ci		return;
2978c2ecf20Sopenharmony_ci	if (!star_mp->host_joined)
2988c2ecf20Sopenharmony_ci		return;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	sg_mp = br_mdb_ip_get(star_mp->br, &sg->key.addr);
3018c2ecf20Sopenharmony_ci	if (!sg_mp)
3028c2ecf20Sopenharmony_ci		return;
3038c2ecf20Sopenharmony_ci	sg_mp->host_joined = true;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/* set the host_joined state of all of *,G's S,G entries */
3078c2ecf20Sopenharmony_cistatic void br_multicast_star_g_host_state(struct net_bridge_mdb_entry *star_mp)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct net_bridge *br = star_mp->br;
3108c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *sg_mp;
3118c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
3128c2ecf20Sopenharmony_ci	struct br_ip sg_ip;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr)))
3158c2ecf20Sopenharmony_ci		return;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	memset(&sg_ip, 0, sizeof(sg_ip));
3188c2ecf20Sopenharmony_ci	sg_ip = star_mp->addr;
3198c2ecf20Sopenharmony_ci	for (pg = mlock_dereference(star_mp->ports, br);
3208c2ecf20Sopenharmony_ci	     pg;
3218c2ecf20Sopenharmony_ci	     pg = mlock_dereference(pg->next, br)) {
3228c2ecf20Sopenharmony_ci		struct net_bridge_group_src *src_ent;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		hlist_for_each_entry(src_ent, &pg->src_list, node) {
3258c2ecf20Sopenharmony_ci			if (!(src_ent->flags & BR_SGRP_F_INSTALLED))
3268c2ecf20Sopenharmony_ci				continue;
3278c2ecf20Sopenharmony_ci			sg_ip.src = src_ent->addr.src;
3288c2ecf20Sopenharmony_ci			sg_mp = br_mdb_ip_get(br, &sg_ip);
3298c2ecf20Sopenharmony_ci			if (!sg_mp)
3308c2ecf20Sopenharmony_ci				continue;
3318c2ecf20Sopenharmony_ci			sg_mp->host_joined = star_mp->host_joined;
3328c2ecf20Sopenharmony_ci		}
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void br_multicast_sg_del_exclude_ports(struct net_bridge_mdb_entry *sgmp)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
3398c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* *,G exclude ports are only added to S,G entries */
3428c2ecf20Sopenharmony_ci	if (WARN_ON(br_multicast_is_star_g(&sgmp->addr)))
3438c2ecf20Sopenharmony_ci		return;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* we need the STAR_EXCLUDE ports if there are non-STAR_EXCLUDE ports
3468c2ecf20Sopenharmony_ci	 * we should ignore perm entries since they're managed by user-space
3478c2ecf20Sopenharmony_ci	 */
3488c2ecf20Sopenharmony_ci	for (pp = &sgmp->ports;
3498c2ecf20Sopenharmony_ci	     (p = mlock_dereference(*pp, sgmp->br)) != NULL;
3508c2ecf20Sopenharmony_ci	     pp = &p->next)
3518c2ecf20Sopenharmony_ci		if (!(p->flags & (MDB_PG_FLAGS_STAR_EXCL |
3528c2ecf20Sopenharmony_ci				  MDB_PG_FLAGS_PERMANENT)))
3538c2ecf20Sopenharmony_ci			return;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* currently the host can only have joined the *,G which means
3568c2ecf20Sopenharmony_ci	 * we treat it as EXCLUDE {}, so for an S,G it's considered a
3578c2ecf20Sopenharmony_ci	 * STAR_EXCLUDE entry and we can safely leave it
3588c2ecf20Sopenharmony_ci	 */
3598c2ecf20Sopenharmony_ci	sgmp->host_joined = false;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	for (pp = &sgmp->ports;
3628c2ecf20Sopenharmony_ci	     (p = mlock_dereference(*pp, sgmp->br)) != NULL;) {
3638c2ecf20Sopenharmony_ci		if (!(p->flags & MDB_PG_FLAGS_PERMANENT))
3648c2ecf20Sopenharmony_ci			br_multicast_del_pg(sgmp, p, pp);
3658c2ecf20Sopenharmony_ci		else
3668c2ecf20Sopenharmony_ci			pp = &p->next;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_civoid br_multicast_sg_add_exclude_ports(struct net_bridge_mdb_entry *star_mp,
3718c2ecf20Sopenharmony_ci				       struct net_bridge_port_group *sg)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct net_bridge_port_group_sg_key sg_key;
3748c2ecf20Sopenharmony_ci	struct net_bridge *br = star_mp->br;
3758c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (WARN_ON(br_multicast_is_star_g(&sg->key.addr)))
3788c2ecf20Sopenharmony_ci		return;
3798c2ecf20Sopenharmony_ci	if (WARN_ON(!br_multicast_is_star_g(&star_mp->addr)))
3808c2ecf20Sopenharmony_ci		return;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	br_multicast_sg_host_state(star_mp, sg);
3838c2ecf20Sopenharmony_ci	memset(&sg_key, 0, sizeof(sg_key));
3848c2ecf20Sopenharmony_ci	sg_key.addr = sg->key.addr;
3858c2ecf20Sopenharmony_ci	/* we need to add all exclude ports to the S,G */
3868c2ecf20Sopenharmony_ci	for (pg = mlock_dereference(star_mp->ports, br);
3878c2ecf20Sopenharmony_ci	     pg;
3888c2ecf20Sopenharmony_ci	     pg = mlock_dereference(pg->next, br)) {
3898c2ecf20Sopenharmony_ci		struct net_bridge_port_group *src_pg;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		if (pg == sg || pg->filter_mode == MCAST_INCLUDE)
3928c2ecf20Sopenharmony_ci			continue;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		sg_key.port = pg->key.port;
3958c2ecf20Sopenharmony_ci		if (br_sg_port_find(br, &sg_key))
3968c2ecf20Sopenharmony_ci			continue;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		src_pg = __br_multicast_add_group(br, pg->key.port,
3998c2ecf20Sopenharmony_ci						  &sg->key.addr,
4008c2ecf20Sopenharmony_ci						  sg->eth_addr,
4018c2ecf20Sopenharmony_ci						  MCAST_INCLUDE, false, false);
4028c2ecf20Sopenharmony_ci		if (IS_ERR_OR_NULL(src_pg) ||
4038c2ecf20Sopenharmony_ci		    src_pg->rt_protocol != RTPROT_KERNEL)
4048c2ecf20Sopenharmony_ci			continue;
4058c2ecf20Sopenharmony_ci		src_pg->flags |= MDB_PG_FLAGS_STAR_EXCL;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic void br_multicast_fwd_src_add(struct net_bridge_group_src *src)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *star_mp;
4128c2ecf20Sopenharmony_ci	struct net_bridge_port_group *sg;
4138c2ecf20Sopenharmony_ci	struct br_ip sg_ip;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (src->flags & BR_SGRP_F_INSTALLED)
4168c2ecf20Sopenharmony_ci		return;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	memset(&sg_ip, 0, sizeof(sg_ip));
4198c2ecf20Sopenharmony_ci	sg_ip = src->pg->key.addr;
4208c2ecf20Sopenharmony_ci	sg_ip.src = src->addr.src;
4218c2ecf20Sopenharmony_ci	sg = __br_multicast_add_group(src->br, src->pg->key.port, &sg_ip,
4228c2ecf20Sopenharmony_ci				      src->pg->eth_addr, MCAST_INCLUDE, false,
4238c2ecf20Sopenharmony_ci				      !timer_pending(&src->timer));
4248c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(sg))
4258c2ecf20Sopenharmony_ci		return;
4268c2ecf20Sopenharmony_ci	src->flags |= BR_SGRP_F_INSTALLED;
4278c2ecf20Sopenharmony_ci	sg->flags &= ~MDB_PG_FLAGS_STAR_EXCL;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* if it was added by user-space as perm we can skip next steps */
4308c2ecf20Sopenharmony_ci	if (sg->rt_protocol != RTPROT_KERNEL &&
4318c2ecf20Sopenharmony_ci	    (sg->flags & MDB_PG_FLAGS_PERMANENT))
4328c2ecf20Sopenharmony_ci		return;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* the kernel is now responsible for removing this S,G */
4358c2ecf20Sopenharmony_ci	del_timer(&sg->timer);
4368c2ecf20Sopenharmony_ci	star_mp = br_mdb_ip_get(src->br, &src->pg->key.addr);
4378c2ecf20Sopenharmony_ci	if (!star_mp)
4388c2ecf20Sopenharmony_ci		return;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	br_multicast_sg_add_exclude_ports(star_mp, sg);
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic void br_multicast_fwd_src_remove(struct net_bridge_group_src *src)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p, *pg = src->pg;
4468c2ecf20Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
4478c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
4488c2ecf20Sopenharmony_ci	struct br_ip sg_ip;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	memset(&sg_ip, 0, sizeof(sg_ip));
4518c2ecf20Sopenharmony_ci	sg_ip = pg->key.addr;
4528c2ecf20Sopenharmony_ci	sg_ip.src = src->addr.src;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	mp = br_mdb_ip_get(src->br, &sg_ip);
4558c2ecf20Sopenharmony_ci	if (!mp)
4568c2ecf20Sopenharmony_ci		return;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	for (pp = &mp->ports;
4598c2ecf20Sopenharmony_ci	     (p = mlock_dereference(*pp, src->br)) != NULL;
4608c2ecf20Sopenharmony_ci	     pp = &p->next) {
4618c2ecf20Sopenharmony_ci		if (!br_port_group_equal(p, pg->key.port, pg->eth_addr))
4628c2ecf20Sopenharmony_ci			continue;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		if (p->rt_protocol != RTPROT_KERNEL &&
4658c2ecf20Sopenharmony_ci		    (p->flags & MDB_PG_FLAGS_PERMANENT))
4668c2ecf20Sopenharmony_ci			break;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		br_multicast_del_pg(mp, p, pp);
4698c2ecf20Sopenharmony_ci		break;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci	src->flags &= ~BR_SGRP_F_INSTALLED;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/* install S,G and based on src's timer enable or disable forwarding */
4758c2ecf20Sopenharmony_cistatic void br_multicast_fwd_src_handle(struct net_bridge_group_src *src)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct net_bridge_port_group_sg_key sg_key;
4788c2ecf20Sopenharmony_ci	struct net_bridge_port_group *sg;
4798c2ecf20Sopenharmony_ci	u8 old_flags;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	br_multicast_fwd_src_add(src);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	memset(&sg_key, 0, sizeof(sg_key));
4848c2ecf20Sopenharmony_ci	sg_key.addr = src->pg->key.addr;
4858c2ecf20Sopenharmony_ci	sg_key.addr.src = src->addr.src;
4868c2ecf20Sopenharmony_ci	sg_key.port = src->pg->key.port;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	sg = br_sg_port_find(src->br, &sg_key);
4898c2ecf20Sopenharmony_ci	if (!sg || (sg->flags & MDB_PG_FLAGS_PERMANENT))
4908c2ecf20Sopenharmony_ci		return;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	old_flags = sg->flags;
4938c2ecf20Sopenharmony_ci	if (timer_pending(&src->timer))
4948c2ecf20Sopenharmony_ci		sg->flags &= ~MDB_PG_FLAGS_BLOCKED;
4958c2ecf20Sopenharmony_ci	else
4968c2ecf20Sopenharmony_ci		sg->flags |= MDB_PG_FLAGS_BLOCKED;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (old_flags != sg->flags) {
4998c2ecf20Sopenharmony_ci		struct net_bridge_mdb_entry *sg_mp;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		sg_mp = br_mdb_ip_get(src->br, &sg_key.addr);
5028c2ecf20Sopenharmony_ci		if (!sg_mp)
5038c2ecf20Sopenharmony_ci			return;
5048c2ecf20Sopenharmony_ci		br_mdb_notify(src->br->dev, sg_mp, sg, RTM_NEWMDB);
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void br_multicast_destroy_mdb_entry(struct net_bridge_mcast_gc *gc)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	mp = container_of(gc, struct net_bridge_mdb_entry, mcast_gc);
5138c2ecf20Sopenharmony_ci	WARN_ON(!hlist_unhashed(&mp->mdb_node));
5148c2ecf20Sopenharmony_ci	WARN_ON(mp->ports);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	del_timer_sync(&mp->timer);
5178c2ecf20Sopenharmony_ci	kfree_rcu(mp, rcu);
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic void br_multicast_del_mdb_entry(struct net_bridge_mdb_entry *mp)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct net_bridge *br = mp->br;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	rhashtable_remove_fast(&br->mdb_hash_tbl, &mp->rhnode,
5258c2ecf20Sopenharmony_ci			       br_mdb_rht_params);
5268c2ecf20Sopenharmony_ci	hlist_del_init_rcu(&mp->mdb_node);
5278c2ecf20Sopenharmony_ci	hlist_add_head(&mp->mcast_gc.gc_node, &br->mcast_gc_list);
5288c2ecf20Sopenharmony_ci	queue_work(system_long_wq, &br->mcast_gc_work);
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic void br_multicast_group_expired(struct timer_list *t)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp = from_timer(mp, t, timer);
5348c2ecf20Sopenharmony_ci	struct net_bridge *br = mp->br;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
5378c2ecf20Sopenharmony_ci	if (hlist_unhashed(&mp->mdb_node) || !netif_running(br->dev) ||
5388c2ecf20Sopenharmony_ci	    timer_pending(&mp->timer))
5398c2ecf20Sopenharmony_ci		goto out;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	br_multicast_host_leave(mp, true);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	if (mp->ports)
5448c2ecf20Sopenharmony_ci		goto out;
5458c2ecf20Sopenharmony_ci	br_multicast_del_mdb_entry(mp);
5468c2ecf20Sopenharmony_ciout:
5478c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void br_multicast_destroy_group_src(struct net_bridge_mcast_gc *gc)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct net_bridge_group_src *src;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	src = container_of(gc, struct net_bridge_group_src, mcast_gc);
5558c2ecf20Sopenharmony_ci	WARN_ON(!hlist_unhashed(&src->node));
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	del_timer_sync(&src->timer);
5588c2ecf20Sopenharmony_ci	kfree_rcu(src, rcu);
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic void br_multicast_del_group_src(struct net_bridge_group_src *src)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct net_bridge *br = src->pg->key.port->br;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	br_multicast_fwd_src_remove(src);
5668c2ecf20Sopenharmony_ci	hlist_del_init_rcu(&src->node);
5678c2ecf20Sopenharmony_ci	src->pg->src_ents--;
5688c2ecf20Sopenharmony_ci	hlist_add_head(&src->mcast_gc.gc_node, &br->mcast_gc_list);
5698c2ecf20Sopenharmony_ci	queue_work(system_long_wq, &br->mcast_gc_work);
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic void br_multicast_destroy_port_group(struct net_bridge_mcast_gc *gc)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	pg = container_of(gc, struct net_bridge_port_group, mcast_gc);
5778c2ecf20Sopenharmony_ci	WARN_ON(!hlist_unhashed(&pg->mglist));
5788c2ecf20Sopenharmony_ci	WARN_ON(!hlist_empty(&pg->src_list));
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	del_timer_sync(&pg->rexmit_timer);
5818c2ecf20Sopenharmony_ci	del_timer_sync(&pg->timer);
5828c2ecf20Sopenharmony_ci	kfree_rcu(pg, rcu);
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_civoid br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
5868c2ecf20Sopenharmony_ci			 struct net_bridge_port_group *pg,
5878c2ecf20Sopenharmony_ci			 struct net_bridge_port_group __rcu **pp)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
5908c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
5918c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	rcu_assign_pointer(*pp, pg->next);
5948c2ecf20Sopenharmony_ci	hlist_del_init(&pg->mglist);
5958c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
5968c2ecf20Sopenharmony_ci		br_multicast_del_group_src(ent);
5978c2ecf20Sopenharmony_ci	br_mdb_notify(br->dev, mp, pg, RTM_DELMDB);
5988c2ecf20Sopenharmony_ci	if (!br_multicast_is_star_g(&mp->addr)) {
5998c2ecf20Sopenharmony_ci		rhashtable_remove_fast(&br->sg_port_tbl, &pg->rhnode,
6008c2ecf20Sopenharmony_ci				       br_sg_port_rht_params);
6018c2ecf20Sopenharmony_ci		br_multicast_sg_del_exclude_ports(mp);
6028c2ecf20Sopenharmony_ci	} else {
6038c2ecf20Sopenharmony_ci		br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE);
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci	hlist_add_head(&pg->mcast_gc.gc_node, &br->mcast_gc_list);
6068c2ecf20Sopenharmony_ci	queue_work(system_long_wq, &br->mcast_gc_work);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (!mp->ports && !mp->host_joined && netif_running(br->dev))
6098c2ecf20Sopenharmony_ci		mod_timer(&mp->timer, jiffies);
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic void br_multicast_find_del_pg(struct net_bridge *br,
6138c2ecf20Sopenharmony_ci				     struct net_bridge_port_group *pg)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
6168c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
6178c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	mp = br_mdb_ip_get(br, &pg->key.addr);
6208c2ecf20Sopenharmony_ci	if (WARN_ON(!mp))
6218c2ecf20Sopenharmony_ci		return;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	for (pp = &mp->ports;
6248c2ecf20Sopenharmony_ci	     (p = mlock_dereference(*pp, br)) != NULL;
6258c2ecf20Sopenharmony_ci	     pp = &p->next) {
6268c2ecf20Sopenharmony_ci		if (p != pg)
6278c2ecf20Sopenharmony_ci			continue;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci		br_multicast_del_pg(mp, pg, pp);
6308c2ecf20Sopenharmony_ci		return;
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	WARN_ON(1);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic void br_multicast_port_group_expired(struct timer_list *t)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg = from_timer(pg, t, timer);
6398c2ecf20Sopenharmony_ci	struct net_bridge_group_src *src_ent;
6408c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
6418c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
6428c2ecf20Sopenharmony_ci	bool changed;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
6458c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) || timer_pending(&pg->timer) ||
6468c2ecf20Sopenharmony_ci	    hlist_unhashed(&pg->mglist) || pg->flags & MDB_PG_FLAGS_PERMANENT)
6478c2ecf20Sopenharmony_ci		goto out;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	changed = !!(pg->filter_mode == MCAST_EXCLUDE);
6508c2ecf20Sopenharmony_ci	pg->filter_mode = MCAST_INCLUDE;
6518c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) {
6528c2ecf20Sopenharmony_ci		if (!timer_pending(&src_ent->timer)) {
6538c2ecf20Sopenharmony_ci			br_multicast_del_group_src(src_ent);
6548c2ecf20Sopenharmony_ci			changed = true;
6558c2ecf20Sopenharmony_ci		}
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (hlist_empty(&pg->src_list)) {
6598c2ecf20Sopenharmony_ci		br_multicast_find_del_pg(br, pg);
6608c2ecf20Sopenharmony_ci	} else if (changed) {
6618c2ecf20Sopenharmony_ci		struct net_bridge_mdb_entry *mp = br_mdb_ip_get(br, &pg->key.addr);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci		if (changed && br_multicast_is_star_g(&pg->key.addr))
6648c2ecf20Sopenharmony_ci			br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		if (WARN_ON(!mp))
6678c2ecf20Sopenharmony_ci			goto out;
6688c2ecf20Sopenharmony_ci		br_mdb_notify(br->dev, mp, pg, RTM_NEWMDB);
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ciout:
6718c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic void br_multicast_gc(struct hlist_head *head)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	struct net_bridge_mcast_gc *gcent;
6778c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(gcent, tmp, head, gc_node) {
6808c2ecf20Sopenharmony_ci		hlist_del_init(&gcent->gc_node);
6818c2ecf20Sopenharmony_ci		gcent->destroy(gcent);
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
6868c2ecf20Sopenharmony_ci						    struct net_bridge_port_group *pg,
6878c2ecf20Sopenharmony_ci						    __be32 ip_dst, __be32 group,
6888c2ecf20Sopenharmony_ci						    bool with_srcs, bool over_lmqt,
6898c2ecf20Sopenharmony_ci						    u8 sflag, u8 *igmp_type,
6908c2ecf20Sopenharmony_ci						    bool *need_rexmit)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	struct net_bridge_port *p = pg ? pg->key.port : NULL;
6938c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
6948c2ecf20Sopenharmony_ci	size_t pkt_size, igmp_hdr_size;
6958c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
6968c2ecf20Sopenharmony_ci	struct igmpv3_query *ihv3;
6978c2ecf20Sopenharmony_ci	void *csum_start = NULL;
6988c2ecf20Sopenharmony_ci	__sum16 *csum = NULL;
6998c2ecf20Sopenharmony_ci	struct sk_buff *skb;
7008c2ecf20Sopenharmony_ci	struct igmphdr *ih;
7018c2ecf20Sopenharmony_ci	struct ethhdr *eth;
7028c2ecf20Sopenharmony_ci	unsigned long lmqt;
7038c2ecf20Sopenharmony_ci	struct iphdr *iph;
7048c2ecf20Sopenharmony_ci	u16 lmqt_srcs = 0;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	igmp_hdr_size = sizeof(*ih);
7078c2ecf20Sopenharmony_ci	if (br->multicast_igmp_version == 3) {
7088c2ecf20Sopenharmony_ci		igmp_hdr_size = sizeof(*ihv3);
7098c2ecf20Sopenharmony_ci		if (pg && with_srcs) {
7108c2ecf20Sopenharmony_ci			lmqt = now + (br->multicast_last_member_interval *
7118c2ecf20Sopenharmony_ci				      br->multicast_last_member_count);
7128c2ecf20Sopenharmony_ci			hlist_for_each_entry(ent, &pg->src_list, node) {
7138c2ecf20Sopenharmony_ci				if (over_lmqt == time_after(ent->timer.expires,
7148c2ecf20Sopenharmony_ci							    lmqt) &&
7158c2ecf20Sopenharmony_ci				    ent->src_query_rexmit_cnt > 0)
7168c2ecf20Sopenharmony_ci					lmqt_srcs++;
7178c2ecf20Sopenharmony_ci			}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci			if (!lmqt_srcs)
7208c2ecf20Sopenharmony_ci				return NULL;
7218c2ecf20Sopenharmony_ci			igmp_hdr_size += lmqt_srcs * sizeof(__be32);
7228c2ecf20Sopenharmony_ci		}
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	pkt_size = sizeof(*eth) + sizeof(*iph) + 4 + igmp_hdr_size;
7268c2ecf20Sopenharmony_ci	if ((p && pkt_size > p->dev->mtu) ||
7278c2ecf20Sopenharmony_ci	    pkt_size > br->dev->mtu)
7288c2ecf20Sopenharmony_ci		return NULL;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(br->dev, pkt_size);
7318c2ecf20Sopenharmony_ci	if (!skb)
7328c2ecf20Sopenharmony_ci		goto out;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
7378c2ecf20Sopenharmony_ci	eth = eth_hdr(skb);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	ether_addr_copy(eth->h_source, br->dev->dev_addr);
7408c2ecf20Sopenharmony_ci	ip_eth_mc_map(ip_dst, eth->h_dest);
7418c2ecf20Sopenharmony_ci	eth->h_proto = htons(ETH_P_IP);
7428c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(*eth));
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	skb_set_network_header(skb, skb->len);
7458c2ecf20Sopenharmony_ci	iph = ip_hdr(skb);
7468c2ecf20Sopenharmony_ci	iph->tot_len = htons(pkt_size - sizeof(*eth));
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	iph->version = 4;
7498c2ecf20Sopenharmony_ci	iph->ihl = 6;
7508c2ecf20Sopenharmony_ci	iph->tos = 0xc0;
7518c2ecf20Sopenharmony_ci	iph->id = 0;
7528c2ecf20Sopenharmony_ci	iph->frag_off = htons(IP_DF);
7538c2ecf20Sopenharmony_ci	iph->ttl = 1;
7548c2ecf20Sopenharmony_ci	iph->protocol = IPPROTO_IGMP;
7558c2ecf20Sopenharmony_ci	iph->saddr = br_opt_get(br, BROPT_MULTICAST_QUERY_USE_IFADDR) ?
7568c2ecf20Sopenharmony_ci		     inet_select_addr(br->dev, 0, RT_SCOPE_LINK) : 0;
7578c2ecf20Sopenharmony_ci	iph->daddr = ip_dst;
7588c2ecf20Sopenharmony_ci	((u8 *)&iph[1])[0] = IPOPT_RA;
7598c2ecf20Sopenharmony_ci	((u8 *)&iph[1])[1] = 4;
7608c2ecf20Sopenharmony_ci	((u8 *)&iph[1])[2] = 0;
7618c2ecf20Sopenharmony_ci	((u8 *)&iph[1])[3] = 0;
7628c2ecf20Sopenharmony_ci	ip_send_check(iph);
7638c2ecf20Sopenharmony_ci	skb_put(skb, 24);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	skb_set_transport_header(skb, skb->len);
7668c2ecf20Sopenharmony_ci	*igmp_type = IGMP_HOST_MEMBERSHIP_QUERY;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	switch (br->multicast_igmp_version) {
7698c2ecf20Sopenharmony_ci	case 2:
7708c2ecf20Sopenharmony_ci		ih = igmp_hdr(skb);
7718c2ecf20Sopenharmony_ci		ih->type = IGMP_HOST_MEMBERSHIP_QUERY;
7728c2ecf20Sopenharmony_ci		ih->code = (group ? br->multicast_last_member_interval :
7738c2ecf20Sopenharmony_ci				    br->multicast_query_response_interval) /
7748c2ecf20Sopenharmony_ci			   (HZ / IGMP_TIMER_SCALE);
7758c2ecf20Sopenharmony_ci		ih->group = group;
7768c2ecf20Sopenharmony_ci		ih->csum = 0;
7778c2ecf20Sopenharmony_ci		csum = &ih->csum;
7788c2ecf20Sopenharmony_ci		csum_start = (void *)ih;
7798c2ecf20Sopenharmony_ci		break;
7808c2ecf20Sopenharmony_ci	case 3:
7818c2ecf20Sopenharmony_ci		ihv3 = igmpv3_query_hdr(skb);
7828c2ecf20Sopenharmony_ci		ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY;
7838c2ecf20Sopenharmony_ci		ihv3->code = (group ? br->multicast_last_member_interval :
7848c2ecf20Sopenharmony_ci				      br->multicast_query_response_interval) /
7858c2ecf20Sopenharmony_ci			     (HZ / IGMP_TIMER_SCALE);
7868c2ecf20Sopenharmony_ci		ihv3->group = group;
7878c2ecf20Sopenharmony_ci		ihv3->qqic = br->multicast_query_interval / HZ;
7888c2ecf20Sopenharmony_ci		ihv3->nsrcs = htons(lmqt_srcs);
7898c2ecf20Sopenharmony_ci		ihv3->resv = 0;
7908c2ecf20Sopenharmony_ci		ihv3->suppress = sflag;
7918c2ecf20Sopenharmony_ci		ihv3->qrv = 2;
7928c2ecf20Sopenharmony_ci		ihv3->csum = 0;
7938c2ecf20Sopenharmony_ci		csum = &ihv3->csum;
7948c2ecf20Sopenharmony_ci		csum_start = (void *)ihv3;
7958c2ecf20Sopenharmony_ci		if (!pg || !with_srcs)
7968c2ecf20Sopenharmony_ci			break;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci		lmqt_srcs = 0;
7998c2ecf20Sopenharmony_ci		hlist_for_each_entry(ent, &pg->src_list, node) {
8008c2ecf20Sopenharmony_ci			if (over_lmqt == time_after(ent->timer.expires,
8018c2ecf20Sopenharmony_ci						    lmqt) &&
8028c2ecf20Sopenharmony_ci			    ent->src_query_rexmit_cnt > 0) {
8038c2ecf20Sopenharmony_ci				ihv3->srcs[lmqt_srcs++] = ent->addr.src.ip4;
8048c2ecf20Sopenharmony_ci				ent->src_query_rexmit_cnt--;
8058c2ecf20Sopenharmony_ci				if (need_rexmit && ent->src_query_rexmit_cnt)
8068c2ecf20Sopenharmony_ci					*need_rexmit = true;
8078c2ecf20Sopenharmony_ci			}
8088c2ecf20Sopenharmony_ci		}
8098c2ecf20Sopenharmony_ci		if (WARN_ON(lmqt_srcs != ntohs(ihv3->nsrcs))) {
8108c2ecf20Sopenharmony_ci			kfree_skb(skb);
8118c2ecf20Sopenharmony_ci			return NULL;
8128c2ecf20Sopenharmony_ci		}
8138c2ecf20Sopenharmony_ci		break;
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (WARN_ON(!csum || !csum_start)) {
8178c2ecf20Sopenharmony_ci		kfree_skb(skb);
8188c2ecf20Sopenharmony_ci		return NULL;
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	*csum = ip_compute_csum(csum_start, igmp_hdr_size);
8228c2ecf20Sopenharmony_ci	skb_put(skb, igmp_hdr_size);
8238c2ecf20Sopenharmony_ci	__skb_pull(skb, sizeof(*eth));
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ciout:
8268c2ecf20Sopenharmony_ci	return skb;
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
8308c2ecf20Sopenharmony_cistatic struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
8318c2ecf20Sopenharmony_ci						    struct net_bridge_port_group *pg,
8328c2ecf20Sopenharmony_ci						    const struct in6_addr *ip6_dst,
8338c2ecf20Sopenharmony_ci						    const struct in6_addr *group,
8348c2ecf20Sopenharmony_ci						    bool with_srcs, bool over_llqt,
8358c2ecf20Sopenharmony_ci						    u8 sflag, u8 *igmp_type,
8368c2ecf20Sopenharmony_ci						    bool *need_rexmit)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct net_bridge_port *p = pg ? pg->key.port : NULL;
8398c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
8408c2ecf20Sopenharmony_ci	size_t pkt_size, mld_hdr_size;
8418c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
8428c2ecf20Sopenharmony_ci	struct mld2_query *mld2q;
8438c2ecf20Sopenharmony_ci	void *csum_start = NULL;
8448c2ecf20Sopenharmony_ci	unsigned long interval;
8458c2ecf20Sopenharmony_ci	__sum16 *csum = NULL;
8468c2ecf20Sopenharmony_ci	struct ipv6hdr *ip6h;
8478c2ecf20Sopenharmony_ci	struct mld_msg *mldq;
8488c2ecf20Sopenharmony_ci	struct sk_buff *skb;
8498c2ecf20Sopenharmony_ci	unsigned long llqt;
8508c2ecf20Sopenharmony_ci	struct ethhdr *eth;
8518c2ecf20Sopenharmony_ci	u16 llqt_srcs = 0;
8528c2ecf20Sopenharmony_ci	u8 *hopopt;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	mld_hdr_size = sizeof(*mldq);
8558c2ecf20Sopenharmony_ci	if (br->multicast_mld_version == 2) {
8568c2ecf20Sopenharmony_ci		mld_hdr_size = sizeof(*mld2q);
8578c2ecf20Sopenharmony_ci		if (pg && with_srcs) {
8588c2ecf20Sopenharmony_ci			llqt = now + (br->multicast_last_member_interval *
8598c2ecf20Sopenharmony_ci				      br->multicast_last_member_count);
8608c2ecf20Sopenharmony_ci			hlist_for_each_entry(ent, &pg->src_list, node) {
8618c2ecf20Sopenharmony_ci				if (over_llqt == time_after(ent->timer.expires,
8628c2ecf20Sopenharmony_ci							    llqt) &&
8638c2ecf20Sopenharmony_ci				    ent->src_query_rexmit_cnt > 0)
8648c2ecf20Sopenharmony_ci					llqt_srcs++;
8658c2ecf20Sopenharmony_ci			}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci			if (!llqt_srcs)
8688c2ecf20Sopenharmony_ci				return NULL;
8698c2ecf20Sopenharmony_ci			mld_hdr_size += llqt_srcs * sizeof(struct in6_addr);
8708c2ecf20Sopenharmony_ci		}
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	pkt_size = sizeof(*eth) + sizeof(*ip6h) + 8 + mld_hdr_size;
8748c2ecf20Sopenharmony_ci	if ((p && pkt_size > p->dev->mtu) ||
8758c2ecf20Sopenharmony_ci	    pkt_size > br->dev->mtu)
8768c2ecf20Sopenharmony_ci		return NULL;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(br->dev, pkt_size);
8798c2ecf20Sopenharmony_ci	if (!skb)
8808c2ecf20Sopenharmony_ci		goto out;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IPV6);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	/* Ethernet header */
8858c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
8868c2ecf20Sopenharmony_ci	eth = eth_hdr(skb);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	ether_addr_copy(eth->h_source, br->dev->dev_addr);
8898c2ecf20Sopenharmony_ci	eth->h_proto = htons(ETH_P_IPV6);
8908c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(*eth));
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	/* IPv6 header + HbH option */
8938c2ecf20Sopenharmony_ci	skb_set_network_header(skb, skb->len);
8948c2ecf20Sopenharmony_ci	ip6h = ipv6_hdr(skb);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	*(__force __be32 *)ip6h = htonl(0x60000000);
8978c2ecf20Sopenharmony_ci	ip6h->payload_len = htons(8 + mld_hdr_size);
8988c2ecf20Sopenharmony_ci	ip6h->nexthdr = IPPROTO_HOPOPTS;
8998c2ecf20Sopenharmony_ci	ip6h->hop_limit = 1;
9008c2ecf20Sopenharmony_ci	ip6h->daddr = *ip6_dst;
9018c2ecf20Sopenharmony_ci	if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0,
9028c2ecf20Sopenharmony_ci			       &ip6h->saddr)) {
9038c2ecf20Sopenharmony_ci		kfree_skb(skb);
9048c2ecf20Sopenharmony_ci		br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, false);
9058c2ecf20Sopenharmony_ci		return NULL;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, true);
9098c2ecf20Sopenharmony_ci	ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	hopopt = (u8 *)(ip6h + 1);
9128c2ecf20Sopenharmony_ci	hopopt[0] = IPPROTO_ICMPV6;		/* next hdr */
9138c2ecf20Sopenharmony_ci	hopopt[1] = 0;				/* length of HbH */
9148c2ecf20Sopenharmony_ci	hopopt[2] = IPV6_TLV_ROUTERALERT;	/* Router Alert */
9158c2ecf20Sopenharmony_ci	hopopt[3] = 2;				/* Length of RA Option */
9168c2ecf20Sopenharmony_ci	hopopt[4] = 0;				/* Type = 0x0000 (MLD) */
9178c2ecf20Sopenharmony_ci	hopopt[5] = 0;
9188c2ecf20Sopenharmony_ci	hopopt[6] = IPV6_TLV_PAD1;		/* Pad1 */
9198c2ecf20Sopenharmony_ci	hopopt[7] = IPV6_TLV_PAD1;		/* Pad1 */
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	skb_put(skb, sizeof(*ip6h) + 8);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	/* ICMPv6 */
9248c2ecf20Sopenharmony_ci	skb_set_transport_header(skb, skb->len);
9258c2ecf20Sopenharmony_ci	interval = ipv6_addr_any(group) ?
9268c2ecf20Sopenharmony_ci			br->multicast_query_response_interval :
9278c2ecf20Sopenharmony_ci			br->multicast_last_member_interval;
9288c2ecf20Sopenharmony_ci	*igmp_type = ICMPV6_MGM_QUERY;
9298c2ecf20Sopenharmony_ci	switch (br->multicast_mld_version) {
9308c2ecf20Sopenharmony_ci	case 1:
9318c2ecf20Sopenharmony_ci		mldq = (struct mld_msg *)icmp6_hdr(skb);
9328c2ecf20Sopenharmony_ci		mldq->mld_type = ICMPV6_MGM_QUERY;
9338c2ecf20Sopenharmony_ci		mldq->mld_code = 0;
9348c2ecf20Sopenharmony_ci		mldq->mld_cksum = 0;
9358c2ecf20Sopenharmony_ci		mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval));
9368c2ecf20Sopenharmony_ci		mldq->mld_reserved = 0;
9378c2ecf20Sopenharmony_ci		mldq->mld_mca = *group;
9388c2ecf20Sopenharmony_ci		csum = &mldq->mld_cksum;
9398c2ecf20Sopenharmony_ci		csum_start = (void *)mldq;
9408c2ecf20Sopenharmony_ci		break;
9418c2ecf20Sopenharmony_ci	case 2:
9428c2ecf20Sopenharmony_ci		mld2q = (struct mld2_query *)icmp6_hdr(skb);
9438c2ecf20Sopenharmony_ci		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval));
9448c2ecf20Sopenharmony_ci		mld2q->mld2q_type = ICMPV6_MGM_QUERY;
9458c2ecf20Sopenharmony_ci		mld2q->mld2q_code = 0;
9468c2ecf20Sopenharmony_ci		mld2q->mld2q_cksum = 0;
9478c2ecf20Sopenharmony_ci		mld2q->mld2q_resv1 = 0;
9488c2ecf20Sopenharmony_ci		mld2q->mld2q_resv2 = 0;
9498c2ecf20Sopenharmony_ci		mld2q->mld2q_suppress = sflag;
9508c2ecf20Sopenharmony_ci		mld2q->mld2q_qrv = 2;
9518c2ecf20Sopenharmony_ci		mld2q->mld2q_nsrcs = htons(llqt_srcs);
9528c2ecf20Sopenharmony_ci		mld2q->mld2q_qqic = br->multicast_query_interval / HZ;
9538c2ecf20Sopenharmony_ci		mld2q->mld2q_mca = *group;
9548c2ecf20Sopenharmony_ci		csum = &mld2q->mld2q_cksum;
9558c2ecf20Sopenharmony_ci		csum_start = (void *)mld2q;
9568c2ecf20Sopenharmony_ci		if (!pg || !with_srcs)
9578c2ecf20Sopenharmony_ci			break;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci		llqt_srcs = 0;
9608c2ecf20Sopenharmony_ci		hlist_for_each_entry(ent, &pg->src_list, node) {
9618c2ecf20Sopenharmony_ci			if (over_llqt == time_after(ent->timer.expires,
9628c2ecf20Sopenharmony_ci						    llqt) &&
9638c2ecf20Sopenharmony_ci			    ent->src_query_rexmit_cnt > 0) {
9648c2ecf20Sopenharmony_ci				mld2q->mld2q_srcs[llqt_srcs++] = ent->addr.src.ip6;
9658c2ecf20Sopenharmony_ci				ent->src_query_rexmit_cnt--;
9668c2ecf20Sopenharmony_ci				if (need_rexmit && ent->src_query_rexmit_cnt)
9678c2ecf20Sopenharmony_ci					*need_rexmit = true;
9688c2ecf20Sopenharmony_ci			}
9698c2ecf20Sopenharmony_ci		}
9708c2ecf20Sopenharmony_ci		if (WARN_ON(llqt_srcs != ntohs(mld2q->mld2q_nsrcs))) {
9718c2ecf20Sopenharmony_ci			kfree_skb(skb);
9728c2ecf20Sopenharmony_ci			return NULL;
9738c2ecf20Sopenharmony_ci		}
9748c2ecf20Sopenharmony_ci		break;
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	if (WARN_ON(!csum || !csum_start)) {
9788c2ecf20Sopenharmony_ci		kfree_skb(skb);
9798c2ecf20Sopenharmony_ci		return NULL;
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	*csum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, mld_hdr_size,
9838c2ecf20Sopenharmony_ci				IPPROTO_ICMPV6,
9848c2ecf20Sopenharmony_ci				csum_partial(csum_start, mld_hdr_size, 0));
9858c2ecf20Sopenharmony_ci	skb_put(skb, mld_hdr_size);
9868c2ecf20Sopenharmony_ci	__skb_pull(skb, sizeof(*eth));
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ciout:
9898c2ecf20Sopenharmony_ci	return skb;
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_ci#endif
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_cistatic struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
9948c2ecf20Sopenharmony_ci						struct net_bridge_port_group *pg,
9958c2ecf20Sopenharmony_ci						struct br_ip *ip_dst,
9968c2ecf20Sopenharmony_ci						struct br_ip *group,
9978c2ecf20Sopenharmony_ci						bool with_srcs, bool over_lmqt,
9988c2ecf20Sopenharmony_ci						u8 sflag, u8 *igmp_type,
9998c2ecf20Sopenharmony_ci						bool *need_rexmit)
10008c2ecf20Sopenharmony_ci{
10018c2ecf20Sopenharmony_ci	__be32 ip4_dst;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	switch (group->proto) {
10048c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
10058c2ecf20Sopenharmony_ci		ip4_dst = ip_dst ? ip_dst->dst.ip4 : htonl(INADDR_ALLHOSTS_GROUP);
10068c2ecf20Sopenharmony_ci		return br_ip4_multicast_alloc_query(br, pg,
10078c2ecf20Sopenharmony_ci						    ip4_dst, group->dst.ip4,
10088c2ecf20Sopenharmony_ci						    with_srcs, over_lmqt,
10098c2ecf20Sopenharmony_ci						    sflag, igmp_type,
10108c2ecf20Sopenharmony_ci						    need_rexmit);
10118c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
10128c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6): {
10138c2ecf20Sopenharmony_ci		struct in6_addr ip6_dst;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci		if (ip_dst)
10168c2ecf20Sopenharmony_ci			ip6_dst = ip_dst->dst.ip6;
10178c2ecf20Sopenharmony_ci		else
10188c2ecf20Sopenharmony_ci			ipv6_addr_set(&ip6_dst, htonl(0xff020000), 0, 0,
10198c2ecf20Sopenharmony_ci				      htonl(1));
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci		return br_ip6_multicast_alloc_query(br, pg,
10228c2ecf20Sopenharmony_ci						    &ip6_dst, &group->dst.ip6,
10238c2ecf20Sopenharmony_ci						    with_srcs, over_lmqt,
10248c2ecf20Sopenharmony_ci						    sflag, igmp_type,
10258c2ecf20Sopenharmony_ci						    need_rexmit);
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci#endif
10288c2ecf20Sopenharmony_ci	}
10298c2ecf20Sopenharmony_ci	return NULL;
10308c2ecf20Sopenharmony_ci}
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cistruct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
10338c2ecf20Sopenharmony_ci						    struct br_ip *group)
10348c2ecf20Sopenharmony_ci{
10358c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
10368c2ecf20Sopenharmony_ci	int err;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	mp = br_mdb_ip_get(br, group);
10398c2ecf20Sopenharmony_ci	if (mp)
10408c2ecf20Sopenharmony_ci		return mp;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) {
10438c2ecf20Sopenharmony_ci		br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
10448c2ecf20Sopenharmony_ci		return ERR_PTR(-E2BIG);
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	mp = kzalloc(sizeof(*mp), GFP_ATOMIC);
10488c2ecf20Sopenharmony_ci	if (unlikely(!mp))
10498c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	mp->br = br;
10528c2ecf20Sopenharmony_ci	mp->addr = *group;
10538c2ecf20Sopenharmony_ci	mp->mcast_gc.destroy = br_multicast_destroy_mdb_entry;
10548c2ecf20Sopenharmony_ci	timer_setup(&mp->timer, br_multicast_group_expired, 0);
10558c2ecf20Sopenharmony_ci	err = rhashtable_lookup_insert_fast(&br->mdb_hash_tbl, &mp->rhnode,
10568c2ecf20Sopenharmony_ci					    br_mdb_rht_params);
10578c2ecf20Sopenharmony_ci	if (err) {
10588c2ecf20Sopenharmony_ci		kfree(mp);
10598c2ecf20Sopenharmony_ci		mp = ERR_PTR(err);
10608c2ecf20Sopenharmony_ci	} else {
10618c2ecf20Sopenharmony_ci		hlist_add_head_rcu(&mp->mdb_node, &br->mdb_list);
10628c2ecf20Sopenharmony_ci	}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	return mp;
10658c2ecf20Sopenharmony_ci}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistatic void br_multicast_group_src_expired(struct timer_list *t)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	struct net_bridge_group_src *src = from_timer(src, t, timer);
10708c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
10718c2ecf20Sopenharmony_ci	struct net_bridge *br = src->br;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
10748c2ecf20Sopenharmony_ci	if (hlist_unhashed(&src->node) || !netif_running(br->dev) ||
10758c2ecf20Sopenharmony_ci	    timer_pending(&src->timer))
10768c2ecf20Sopenharmony_ci		goto out;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	pg = src->pg;
10798c2ecf20Sopenharmony_ci	if (pg->filter_mode == MCAST_INCLUDE) {
10808c2ecf20Sopenharmony_ci		br_multicast_del_group_src(src);
10818c2ecf20Sopenharmony_ci		if (!hlist_empty(&pg->src_list))
10828c2ecf20Sopenharmony_ci			goto out;
10838c2ecf20Sopenharmony_ci		br_multicast_find_del_pg(br, pg);
10848c2ecf20Sopenharmony_ci	} else {
10858c2ecf20Sopenharmony_ci		br_multicast_fwd_src_handle(src);
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ciout:
10898c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
10908c2ecf20Sopenharmony_ci}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_cistatic struct net_bridge_group_src *
10938c2ecf20Sopenharmony_cibr_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	switch (ip->proto) {
10988c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
10998c2ecf20Sopenharmony_ci		hlist_for_each_entry(ent, &pg->src_list, node)
11008c2ecf20Sopenharmony_ci			if (ip->src.ip4 == ent->addr.src.ip4)
11018c2ecf20Sopenharmony_ci				return ent;
11028c2ecf20Sopenharmony_ci		break;
11038c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
11048c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
11058c2ecf20Sopenharmony_ci		hlist_for_each_entry(ent, &pg->src_list, node)
11068c2ecf20Sopenharmony_ci			if (!ipv6_addr_cmp(&ent->addr.src.ip6, &ip->src.ip6))
11078c2ecf20Sopenharmony_ci				return ent;
11088c2ecf20Sopenharmony_ci		break;
11098c2ecf20Sopenharmony_ci#endif
11108c2ecf20Sopenharmony_ci	}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	return NULL;
11138c2ecf20Sopenharmony_ci}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_cistatic struct net_bridge_group_src *
11168c2ecf20Sopenharmony_cibr_multicast_new_group_src(struct net_bridge_port_group *pg, struct br_ip *src_ip)
11178c2ecf20Sopenharmony_ci{
11188c2ecf20Sopenharmony_ci	struct net_bridge_group_src *grp_src;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (unlikely(pg->src_ents >= PG_SRC_ENT_LIMIT))
11218c2ecf20Sopenharmony_ci		return NULL;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	switch (src_ip->proto) {
11248c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
11258c2ecf20Sopenharmony_ci		if (ipv4_is_zeronet(src_ip->src.ip4) ||
11268c2ecf20Sopenharmony_ci		    ipv4_is_multicast(src_ip->src.ip4))
11278c2ecf20Sopenharmony_ci			return NULL;
11288c2ecf20Sopenharmony_ci		break;
11298c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
11308c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
11318c2ecf20Sopenharmony_ci		if (ipv6_addr_any(&src_ip->src.ip6) ||
11328c2ecf20Sopenharmony_ci		    ipv6_addr_is_multicast(&src_ip->src.ip6))
11338c2ecf20Sopenharmony_ci			return NULL;
11348c2ecf20Sopenharmony_ci		break;
11358c2ecf20Sopenharmony_ci#endif
11368c2ecf20Sopenharmony_ci	}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	grp_src = kzalloc(sizeof(*grp_src), GFP_ATOMIC);
11398c2ecf20Sopenharmony_ci	if (unlikely(!grp_src))
11408c2ecf20Sopenharmony_ci		return NULL;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	grp_src->pg = pg;
11438c2ecf20Sopenharmony_ci	grp_src->br = pg->key.port->br;
11448c2ecf20Sopenharmony_ci	grp_src->addr = *src_ip;
11458c2ecf20Sopenharmony_ci	grp_src->mcast_gc.destroy = br_multicast_destroy_group_src;
11468c2ecf20Sopenharmony_ci	timer_setup(&grp_src->timer, br_multicast_group_src_expired, 0);
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	hlist_add_head_rcu(&grp_src->node, &pg->src_list);
11498c2ecf20Sopenharmony_ci	pg->src_ents++;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	return grp_src;
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_cistruct net_bridge_port_group *br_multicast_new_port_group(
11558c2ecf20Sopenharmony_ci			struct net_bridge_port *port,
11568c2ecf20Sopenharmony_ci			struct br_ip *group,
11578c2ecf20Sopenharmony_ci			struct net_bridge_port_group __rcu *next,
11588c2ecf20Sopenharmony_ci			unsigned char flags,
11598c2ecf20Sopenharmony_ci			const unsigned char *src,
11608c2ecf20Sopenharmony_ci			u8 filter_mode,
11618c2ecf20Sopenharmony_ci			u8 rt_protocol)
11628c2ecf20Sopenharmony_ci{
11638c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	p = kzalloc(sizeof(*p), GFP_ATOMIC);
11668c2ecf20Sopenharmony_ci	if (unlikely(!p))
11678c2ecf20Sopenharmony_ci		return NULL;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	p->key.addr = *group;
11708c2ecf20Sopenharmony_ci	p->key.port = port;
11718c2ecf20Sopenharmony_ci	p->flags = flags;
11728c2ecf20Sopenharmony_ci	p->filter_mode = filter_mode;
11738c2ecf20Sopenharmony_ci	p->rt_protocol = rt_protocol;
11748c2ecf20Sopenharmony_ci	p->mcast_gc.destroy = br_multicast_destroy_port_group;
11758c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&p->src_list);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	if (!br_multicast_is_star_g(group) &&
11788c2ecf20Sopenharmony_ci	    rhashtable_lookup_insert_fast(&port->br->sg_port_tbl, &p->rhnode,
11798c2ecf20Sopenharmony_ci					  br_sg_port_rht_params)) {
11808c2ecf20Sopenharmony_ci		kfree(p);
11818c2ecf20Sopenharmony_ci		return NULL;
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	rcu_assign_pointer(p->next, next);
11858c2ecf20Sopenharmony_ci	timer_setup(&p->timer, br_multicast_port_group_expired, 0);
11868c2ecf20Sopenharmony_ci	timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0);
11878c2ecf20Sopenharmony_ci	hlist_add_head(&p->mglist, &port->mglist);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	if (src)
11908c2ecf20Sopenharmony_ci		memcpy(p->eth_addr, src, ETH_ALEN);
11918c2ecf20Sopenharmony_ci	else
11928c2ecf20Sopenharmony_ci		eth_broadcast_addr(p->eth_addr);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	return p;
11958c2ecf20Sopenharmony_ci}
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_civoid br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify)
11988c2ecf20Sopenharmony_ci{
11998c2ecf20Sopenharmony_ci	if (!mp->host_joined) {
12008c2ecf20Sopenharmony_ci		mp->host_joined = true;
12018c2ecf20Sopenharmony_ci		if (br_multicast_is_star_g(&mp->addr))
12028c2ecf20Sopenharmony_ci			br_multicast_star_g_host_state(mp);
12038c2ecf20Sopenharmony_ci		if (notify)
12048c2ecf20Sopenharmony_ci			br_mdb_notify(mp->br->dev, mp, NULL, RTM_NEWMDB);
12058c2ecf20Sopenharmony_ci	}
12068c2ecf20Sopenharmony_ci	mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval);
12078c2ecf20Sopenharmony_ci}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_civoid br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify)
12108c2ecf20Sopenharmony_ci{
12118c2ecf20Sopenharmony_ci	if (!mp->host_joined)
12128c2ecf20Sopenharmony_ci		return;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	mp->host_joined = false;
12158c2ecf20Sopenharmony_ci	if (br_multicast_is_star_g(&mp->addr))
12168c2ecf20Sopenharmony_ci		br_multicast_star_g_host_state(mp);
12178c2ecf20Sopenharmony_ci	if (notify)
12188c2ecf20Sopenharmony_ci		br_mdb_notify(mp->br->dev, mp, NULL, RTM_DELMDB);
12198c2ecf20Sopenharmony_ci}
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_cistatic struct net_bridge_port_group *
12228c2ecf20Sopenharmony_ci__br_multicast_add_group(struct net_bridge *br,
12238c2ecf20Sopenharmony_ci			 struct net_bridge_port *port,
12248c2ecf20Sopenharmony_ci			 struct br_ip *group,
12258c2ecf20Sopenharmony_ci			 const unsigned char *src,
12268c2ecf20Sopenharmony_ci			 u8 filter_mode,
12278c2ecf20Sopenharmony_ci			 bool igmpv2_mldv1,
12288c2ecf20Sopenharmony_ci			 bool blocked)
12298c2ecf20Sopenharmony_ci{
12308c2ecf20Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
12318c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p = NULL;
12328c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
12338c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) ||
12368c2ecf20Sopenharmony_ci	    (port && port->state == BR_STATE_DISABLED))
12378c2ecf20Sopenharmony_ci		goto out;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	mp = br_multicast_new_group(br, group);
12408c2ecf20Sopenharmony_ci	if (IS_ERR(mp))
12418c2ecf20Sopenharmony_ci		return ERR_PTR(PTR_ERR(mp));
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	if (!port) {
12448c2ecf20Sopenharmony_ci		br_multicast_host_join(mp, true);
12458c2ecf20Sopenharmony_ci		goto out;
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	for (pp = &mp->ports;
12498c2ecf20Sopenharmony_ci	     (p = mlock_dereference(*pp, br)) != NULL;
12508c2ecf20Sopenharmony_ci	     pp = &p->next) {
12518c2ecf20Sopenharmony_ci		if (br_port_group_equal(p, port, src))
12528c2ecf20Sopenharmony_ci			goto found;
12538c2ecf20Sopenharmony_ci		if ((unsigned long)p->key.port < (unsigned long)port)
12548c2ecf20Sopenharmony_ci			break;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	p = br_multicast_new_port_group(port, group, *pp, 0, src, filter_mode,
12588c2ecf20Sopenharmony_ci					RTPROT_KERNEL);
12598c2ecf20Sopenharmony_ci	if (unlikely(!p)) {
12608c2ecf20Sopenharmony_ci		p = ERR_PTR(-ENOMEM);
12618c2ecf20Sopenharmony_ci		goto out;
12628c2ecf20Sopenharmony_ci	}
12638c2ecf20Sopenharmony_ci	rcu_assign_pointer(*pp, p);
12648c2ecf20Sopenharmony_ci	if (blocked)
12658c2ecf20Sopenharmony_ci		p->flags |= MDB_PG_FLAGS_BLOCKED;
12668c2ecf20Sopenharmony_ci	br_mdb_notify(br->dev, mp, p, RTM_NEWMDB);
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_cifound:
12698c2ecf20Sopenharmony_ci	if (igmpv2_mldv1)
12708c2ecf20Sopenharmony_ci		mod_timer(&p->timer, now + br->multicast_membership_interval);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ciout:
12738c2ecf20Sopenharmony_ci	return p;
12748c2ecf20Sopenharmony_ci}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_cistatic int br_multicast_add_group(struct net_bridge *br,
12778c2ecf20Sopenharmony_ci				  struct net_bridge_port *port,
12788c2ecf20Sopenharmony_ci				  struct br_ip *group,
12798c2ecf20Sopenharmony_ci				  const unsigned char *src,
12808c2ecf20Sopenharmony_ci				  u8 filter_mode,
12818c2ecf20Sopenharmony_ci				  bool igmpv2_mldv1)
12828c2ecf20Sopenharmony_ci{
12838c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
12848c2ecf20Sopenharmony_ci	int err;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
12878c2ecf20Sopenharmony_ci	pg = __br_multicast_add_group(br, port, group, src, filter_mode,
12888c2ecf20Sopenharmony_ci				      igmpv2_mldv1, false);
12898c2ecf20Sopenharmony_ci	/* NULL is considered valid for host joined groups */
12908c2ecf20Sopenharmony_ci	err = IS_ERR(pg) ? PTR_ERR(pg) : 0;
12918c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	return err;
12948c2ecf20Sopenharmony_ci}
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_cistatic int br_ip4_multicast_add_group(struct net_bridge *br,
12978c2ecf20Sopenharmony_ci				      struct net_bridge_port *port,
12988c2ecf20Sopenharmony_ci				      __be32 group,
12998c2ecf20Sopenharmony_ci				      __u16 vid,
13008c2ecf20Sopenharmony_ci				      const unsigned char *src,
13018c2ecf20Sopenharmony_ci				      bool igmpv2)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	struct br_ip br_group;
13048c2ecf20Sopenharmony_ci	u8 filter_mode;
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	if (ipv4_is_local_multicast(group))
13078c2ecf20Sopenharmony_ci		return 0;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	memset(&br_group, 0, sizeof(br_group));
13108c2ecf20Sopenharmony_ci	br_group.dst.ip4 = group;
13118c2ecf20Sopenharmony_ci	br_group.proto = htons(ETH_P_IP);
13128c2ecf20Sopenharmony_ci	br_group.vid = vid;
13138c2ecf20Sopenharmony_ci	filter_mode = igmpv2 ? MCAST_EXCLUDE : MCAST_INCLUDE;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	return br_multicast_add_group(br, port, &br_group, src, filter_mode,
13168c2ecf20Sopenharmony_ci				      igmpv2);
13178c2ecf20Sopenharmony_ci}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
13208c2ecf20Sopenharmony_cistatic int br_ip6_multicast_add_group(struct net_bridge *br,
13218c2ecf20Sopenharmony_ci				      struct net_bridge_port *port,
13228c2ecf20Sopenharmony_ci				      const struct in6_addr *group,
13238c2ecf20Sopenharmony_ci				      __u16 vid,
13248c2ecf20Sopenharmony_ci				      const unsigned char *src,
13258c2ecf20Sopenharmony_ci				      bool mldv1)
13268c2ecf20Sopenharmony_ci{
13278c2ecf20Sopenharmony_ci	struct br_ip br_group;
13288c2ecf20Sopenharmony_ci	u8 filter_mode;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	if (ipv6_addr_is_ll_all_nodes(group))
13318c2ecf20Sopenharmony_ci		return 0;
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	memset(&br_group, 0, sizeof(br_group));
13348c2ecf20Sopenharmony_ci	br_group.dst.ip6 = *group;
13358c2ecf20Sopenharmony_ci	br_group.proto = htons(ETH_P_IPV6);
13368c2ecf20Sopenharmony_ci	br_group.vid = vid;
13378c2ecf20Sopenharmony_ci	filter_mode = mldv1 ? MCAST_EXCLUDE : MCAST_INCLUDE;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	return br_multicast_add_group(br, port, &br_group, src, filter_mode,
13408c2ecf20Sopenharmony_ci				      mldv1);
13418c2ecf20Sopenharmony_ci}
13428c2ecf20Sopenharmony_ci#endif
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_cistatic void br_multicast_router_expired(struct timer_list *t)
13458c2ecf20Sopenharmony_ci{
13468c2ecf20Sopenharmony_ci	struct net_bridge_port *port =
13478c2ecf20Sopenharmony_ci			from_timer(port, t, multicast_router_timer);
13488c2ecf20Sopenharmony_ci	struct net_bridge *br = port->br;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
13518c2ecf20Sopenharmony_ci	if (port->multicast_router == MDB_RTR_TYPE_DISABLED ||
13528c2ecf20Sopenharmony_ci	    port->multicast_router == MDB_RTR_TYPE_PERM ||
13538c2ecf20Sopenharmony_ci	    timer_pending(&port->multicast_router_timer))
13548c2ecf20Sopenharmony_ci		goto out;
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	__del_port_router(port);
13578c2ecf20Sopenharmony_ciout:
13588c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
13598c2ecf20Sopenharmony_ci}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_cistatic void br_mc_router_state_change(struct net_bridge *p,
13628c2ecf20Sopenharmony_ci				      bool is_mc_router)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	struct switchdev_attr attr = {
13658c2ecf20Sopenharmony_ci		.orig_dev = p->dev,
13668c2ecf20Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_MROUTER,
13678c2ecf20Sopenharmony_ci		.flags = SWITCHDEV_F_DEFER,
13688c2ecf20Sopenharmony_ci		.u.mrouter = is_mc_router,
13698c2ecf20Sopenharmony_ci	};
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	switchdev_port_attr_set(p->dev, &attr);
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cistatic void br_multicast_local_router_expired(struct timer_list *t)
13758c2ecf20Sopenharmony_ci{
13768c2ecf20Sopenharmony_ci	struct net_bridge *br = from_timer(br, t, multicast_router_timer);
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
13798c2ecf20Sopenharmony_ci	if (br->multicast_router == MDB_RTR_TYPE_DISABLED ||
13808c2ecf20Sopenharmony_ci	    br->multicast_router == MDB_RTR_TYPE_PERM ||
13818c2ecf20Sopenharmony_ci	    timer_pending(&br->multicast_router_timer))
13828c2ecf20Sopenharmony_ci		goto out;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	br_mc_router_state_change(br, false);
13858c2ecf20Sopenharmony_ciout:
13868c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_cistatic void br_multicast_querier_expired(struct net_bridge *br,
13908c2ecf20Sopenharmony_ci					 struct bridge_mcast_own_query *query)
13918c2ecf20Sopenharmony_ci{
13928c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
13938c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED))
13948c2ecf20Sopenharmony_ci		goto out;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	br_multicast_start_querier(br, query);
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ciout:
13998c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_cistatic void br_ip4_multicast_querier_expired(struct timer_list *t)
14038c2ecf20Sopenharmony_ci{
14048c2ecf20Sopenharmony_ci	struct net_bridge *br = from_timer(br, t, ip4_other_query.timer);
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	br_multicast_querier_expired(br, &br->ip4_own_query);
14078c2ecf20Sopenharmony_ci}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
14108c2ecf20Sopenharmony_cistatic void br_ip6_multicast_querier_expired(struct timer_list *t)
14118c2ecf20Sopenharmony_ci{
14128c2ecf20Sopenharmony_ci	struct net_bridge *br = from_timer(br, t, ip6_other_query.timer);
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	br_multicast_querier_expired(br, &br->ip6_own_query);
14158c2ecf20Sopenharmony_ci}
14168c2ecf20Sopenharmony_ci#endif
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_cistatic void br_multicast_select_own_querier(struct net_bridge *br,
14198c2ecf20Sopenharmony_ci					    struct br_ip *ip,
14208c2ecf20Sopenharmony_ci					    struct sk_buff *skb)
14218c2ecf20Sopenharmony_ci{
14228c2ecf20Sopenharmony_ci	if (ip->proto == htons(ETH_P_IP))
14238c2ecf20Sopenharmony_ci		br->ip4_querier.addr.src.ip4 = ip_hdr(skb)->saddr;
14248c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
14258c2ecf20Sopenharmony_ci	else
14268c2ecf20Sopenharmony_ci		br->ip6_querier.addr.src.ip6 = ipv6_hdr(skb)->saddr;
14278c2ecf20Sopenharmony_ci#endif
14288c2ecf20Sopenharmony_ci}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_cistatic void __br_multicast_send_query(struct net_bridge *br,
14318c2ecf20Sopenharmony_ci				      struct net_bridge_port *port,
14328c2ecf20Sopenharmony_ci				      struct net_bridge_port_group *pg,
14338c2ecf20Sopenharmony_ci				      struct br_ip *ip_dst,
14348c2ecf20Sopenharmony_ci				      struct br_ip *group,
14358c2ecf20Sopenharmony_ci				      bool with_srcs,
14368c2ecf20Sopenharmony_ci				      u8 sflag,
14378c2ecf20Sopenharmony_ci				      bool *need_rexmit)
14388c2ecf20Sopenharmony_ci{
14398c2ecf20Sopenharmony_ci	bool over_lmqt = !!sflag;
14408c2ecf20Sopenharmony_ci	struct sk_buff *skb;
14418c2ecf20Sopenharmony_ci	u8 igmp_type;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ciagain_under_lmqt:
14448c2ecf20Sopenharmony_ci	skb = br_multicast_alloc_query(br, pg, ip_dst, group, with_srcs,
14458c2ecf20Sopenharmony_ci				       over_lmqt, sflag, &igmp_type,
14468c2ecf20Sopenharmony_ci				       need_rexmit);
14478c2ecf20Sopenharmony_ci	if (!skb)
14488c2ecf20Sopenharmony_ci		return;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	if (port) {
14518c2ecf20Sopenharmony_ci		skb->dev = port->dev;
14528c2ecf20Sopenharmony_ci		br_multicast_count(br, port, skb, igmp_type,
14538c2ecf20Sopenharmony_ci				   BR_MCAST_DIR_TX);
14548c2ecf20Sopenharmony_ci		NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
14558c2ecf20Sopenharmony_ci			dev_net(port->dev), NULL, skb, NULL, skb->dev,
14568c2ecf20Sopenharmony_ci			br_dev_queue_push_xmit);
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci		if (over_lmqt && with_srcs && sflag) {
14598c2ecf20Sopenharmony_ci			over_lmqt = false;
14608c2ecf20Sopenharmony_ci			goto again_under_lmqt;
14618c2ecf20Sopenharmony_ci		}
14628c2ecf20Sopenharmony_ci	} else {
14638c2ecf20Sopenharmony_ci		br_multicast_select_own_querier(br, group, skb);
14648c2ecf20Sopenharmony_ci		br_multicast_count(br, port, skb, igmp_type,
14658c2ecf20Sopenharmony_ci				   BR_MCAST_DIR_RX);
14668c2ecf20Sopenharmony_ci		netif_rx(skb);
14678c2ecf20Sopenharmony_ci	}
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_cistatic void br_multicast_send_query(struct net_bridge *br,
14718c2ecf20Sopenharmony_ci				    struct net_bridge_port *port,
14728c2ecf20Sopenharmony_ci				    struct bridge_mcast_own_query *own_query)
14738c2ecf20Sopenharmony_ci{
14748c2ecf20Sopenharmony_ci	struct bridge_mcast_other_query *other_query = NULL;
14758c2ecf20Sopenharmony_ci	struct br_ip br_group;
14768c2ecf20Sopenharmony_ci	unsigned long time;
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) ||
14798c2ecf20Sopenharmony_ci	    !br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
14808c2ecf20Sopenharmony_ci	    !br_opt_get(br, BROPT_MULTICAST_QUERIER))
14818c2ecf20Sopenharmony_ci		return;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	memset(&br_group.dst, 0, sizeof(br_group.dst));
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	if (port ? (own_query == &port->ip4_own_query) :
14868c2ecf20Sopenharmony_ci		   (own_query == &br->ip4_own_query)) {
14878c2ecf20Sopenharmony_ci		other_query = &br->ip4_other_query;
14888c2ecf20Sopenharmony_ci		br_group.proto = htons(ETH_P_IP);
14898c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
14908c2ecf20Sopenharmony_ci	} else {
14918c2ecf20Sopenharmony_ci		other_query = &br->ip6_other_query;
14928c2ecf20Sopenharmony_ci		br_group.proto = htons(ETH_P_IPV6);
14938c2ecf20Sopenharmony_ci#endif
14948c2ecf20Sopenharmony_ci	}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	if (!other_query || timer_pending(&other_query->timer))
14978c2ecf20Sopenharmony_ci		return;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	__br_multicast_send_query(br, port, NULL, NULL, &br_group, false, 0,
15008c2ecf20Sopenharmony_ci				  NULL);
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	time = jiffies;
15038c2ecf20Sopenharmony_ci	time += own_query->startup_sent < br->multicast_startup_query_count ?
15048c2ecf20Sopenharmony_ci		br->multicast_startup_query_interval :
15058c2ecf20Sopenharmony_ci		br->multicast_query_interval;
15068c2ecf20Sopenharmony_ci	mod_timer(&own_query->timer, time);
15078c2ecf20Sopenharmony_ci}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_cistatic void
15108c2ecf20Sopenharmony_cibr_multicast_port_query_expired(struct net_bridge_port *port,
15118c2ecf20Sopenharmony_ci				struct bridge_mcast_own_query *query)
15128c2ecf20Sopenharmony_ci{
15138c2ecf20Sopenharmony_ci	struct net_bridge *br = port->br;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
15168c2ecf20Sopenharmony_ci	if (port->state == BR_STATE_DISABLED ||
15178c2ecf20Sopenharmony_ci	    port->state == BR_STATE_BLOCKING)
15188c2ecf20Sopenharmony_ci		goto out;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	if (query->startup_sent < br->multicast_startup_query_count)
15218c2ecf20Sopenharmony_ci		query->startup_sent++;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	br_multicast_send_query(port->br, port, query);
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ciout:
15268c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
15278c2ecf20Sopenharmony_ci}
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_cistatic void br_ip4_multicast_port_query_expired(struct timer_list *t)
15308c2ecf20Sopenharmony_ci{
15318c2ecf20Sopenharmony_ci	struct net_bridge_port *port = from_timer(port, t, ip4_own_query.timer);
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	br_multicast_port_query_expired(port, &port->ip4_own_query);
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
15378c2ecf20Sopenharmony_cistatic void br_ip6_multicast_port_query_expired(struct timer_list *t)
15388c2ecf20Sopenharmony_ci{
15398c2ecf20Sopenharmony_ci	struct net_bridge_port *port = from_timer(port, t, ip6_own_query.timer);
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	br_multicast_port_query_expired(port, &port->ip6_own_query);
15428c2ecf20Sopenharmony_ci}
15438c2ecf20Sopenharmony_ci#endif
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_cistatic void br_multicast_port_group_rexmit(struct timer_list *t)
15468c2ecf20Sopenharmony_ci{
15478c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg = from_timer(pg, t, rexmit_timer);
15488c2ecf20Sopenharmony_ci	struct bridge_mcast_other_query *other_query = NULL;
15498c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
15508c2ecf20Sopenharmony_ci	bool need_rexmit = false;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
15538c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) ||
15548c2ecf20Sopenharmony_ci	    !br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
15558c2ecf20Sopenharmony_ci	    !br_opt_get(br, BROPT_MULTICAST_QUERIER))
15568c2ecf20Sopenharmony_ci		goto out;
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	if (pg->key.addr.proto == htons(ETH_P_IP))
15598c2ecf20Sopenharmony_ci		other_query = &br->ip4_other_query;
15608c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
15618c2ecf20Sopenharmony_ci	else
15628c2ecf20Sopenharmony_ci		other_query = &br->ip6_other_query;
15638c2ecf20Sopenharmony_ci#endif
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	if (!other_query || timer_pending(&other_query->timer))
15668c2ecf20Sopenharmony_ci		goto out;
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	if (pg->grp_query_rexmit_cnt) {
15698c2ecf20Sopenharmony_ci		pg->grp_query_rexmit_cnt--;
15708c2ecf20Sopenharmony_ci		__br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr,
15718c2ecf20Sopenharmony_ci					  &pg->key.addr, false, 1, NULL);
15728c2ecf20Sopenharmony_ci	}
15738c2ecf20Sopenharmony_ci	__br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr,
15748c2ecf20Sopenharmony_ci				  &pg->key.addr, true, 0, &need_rexmit);
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	if (pg->grp_query_rexmit_cnt || need_rexmit)
15778c2ecf20Sopenharmony_ci		mod_timer(&pg->rexmit_timer, jiffies +
15788c2ecf20Sopenharmony_ci					     br->multicast_last_member_interval);
15798c2ecf20Sopenharmony_ciout:
15808c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
15818c2ecf20Sopenharmony_ci}
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_cistatic void br_mc_disabled_update(struct net_device *dev, bool value)
15848c2ecf20Sopenharmony_ci{
15858c2ecf20Sopenharmony_ci	struct switchdev_attr attr = {
15868c2ecf20Sopenharmony_ci		.orig_dev = dev,
15878c2ecf20Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED,
15888c2ecf20Sopenharmony_ci		.flags = SWITCHDEV_F_DEFER,
15898c2ecf20Sopenharmony_ci		.u.mc_disabled = !value,
15908c2ecf20Sopenharmony_ci	};
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	switchdev_port_attr_set(dev, &attr);
15938c2ecf20Sopenharmony_ci}
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ciint br_multicast_add_port(struct net_bridge_port *port)
15968c2ecf20Sopenharmony_ci{
15978c2ecf20Sopenharmony_ci	port->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	timer_setup(&port->multicast_router_timer,
16008c2ecf20Sopenharmony_ci		    br_multicast_router_expired, 0);
16018c2ecf20Sopenharmony_ci	timer_setup(&port->ip4_own_query.timer,
16028c2ecf20Sopenharmony_ci		    br_ip4_multicast_port_query_expired, 0);
16038c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
16048c2ecf20Sopenharmony_ci	timer_setup(&port->ip6_own_query.timer,
16058c2ecf20Sopenharmony_ci		    br_ip6_multicast_port_query_expired, 0);
16068c2ecf20Sopenharmony_ci#endif
16078c2ecf20Sopenharmony_ci	br_mc_disabled_update(port->dev,
16088c2ecf20Sopenharmony_ci			      br_opt_get(port->br, BROPT_MULTICAST_ENABLED));
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	port->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats);
16118c2ecf20Sopenharmony_ci	if (!port->mcast_stats)
16128c2ecf20Sopenharmony_ci		return -ENOMEM;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	return 0;
16158c2ecf20Sopenharmony_ci}
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_civoid br_multicast_del_port(struct net_bridge_port *port)
16188c2ecf20Sopenharmony_ci{
16198c2ecf20Sopenharmony_ci	struct net_bridge *br = port->br;
16208c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
16218c2ecf20Sopenharmony_ci	HLIST_HEAD(deleted_head);
16228c2ecf20Sopenharmony_ci	struct hlist_node *n;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	/* Take care of the remaining groups, only perm ones should be left */
16258c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
16268c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
16278c2ecf20Sopenharmony_ci		br_multicast_find_del_pg(br, pg);
16288c2ecf20Sopenharmony_ci	hlist_move_list(&br->mcast_gc_list, &deleted_head);
16298c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
16308c2ecf20Sopenharmony_ci	br_multicast_gc(&deleted_head);
16318c2ecf20Sopenharmony_ci	del_timer_sync(&port->multicast_router_timer);
16328c2ecf20Sopenharmony_ci	free_percpu(port->mcast_stats);
16338c2ecf20Sopenharmony_ci}
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_cistatic void br_multicast_enable(struct bridge_mcast_own_query *query)
16368c2ecf20Sopenharmony_ci{
16378c2ecf20Sopenharmony_ci	query->startup_sent = 0;
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci	if (try_to_del_timer_sync(&query->timer) >= 0 ||
16408c2ecf20Sopenharmony_ci	    del_timer(&query->timer))
16418c2ecf20Sopenharmony_ci		mod_timer(&query->timer, jiffies);
16428c2ecf20Sopenharmony_ci}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_cistatic void __br_multicast_enable_port(struct net_bridge_port *port)
16458c2ecf20Sopenharmony_ci{
16468c2ecf20Sopenharmony_ci	struct net_bridge *br = port->br;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED) || !netif_running(br->dev))
16498c2ecf20Sopenharmony_ci		return;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	br_multicast_enable(&port->ip4_own_query);
16528c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
16538c2ecf20Sopenharmony_ci	br_multicast_enable(&port->ip6_own_query);
16548c2ecf20Sopenharmony_ci#endif
16558c2ecf20Sopenharmony_ci	if (port->multicast_router == MDB_RTR_TYPE_PERM &&
16568c2ecf20Sopenharmony_ci	    hlist_unhashed(&port->rlist))
16578c2ecf20Sopenharmony_ci		br_multicast_add_router(br, port);
16588c2ecf20Sopenharmony_ci}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_civoid br_multicast_enable_port(struct net_bridge_port *port)
16618c2ecf20Sopenharmony_ci{
16628c2ecf20Sopenharmony_ci	struct net_bridge *br = port->br;
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
16658c2ecf20Sopenharmony_ci	__br_multicast_enable_port(port);
16668c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
16678c2ecf20Sopenharmony_ci}
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_civoid br_multicast_disable_port(struct net_bridge_port *port)
16708c2ecf20Sopenharmony_ci{
16718c2ecf20Sopenharmony_ci	struct net_bridge *br = port->br;
16728c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
16738c2ecf20Sopenharmony_ci	struct hlist_node *n;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
16768c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(pg, n, &port->mglist, mglist)
16778c2ecf20Sopenharmony_ci		if (!(pg->flags & MDB_PG_FLAGS_PERMANENT))
16788c2ecf20Sopenharmony_ci			br_multicast_find_del_pg(br, pg);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	__del_port_router(port);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	del_timer(&port->multicast_router_timer);
16838c2ecf20Sopenharmony_ci	del_timer(&port->ip4_own_query.timer);
16848c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
16858c2ecf20Sopenharmony_ci	del_timer(&port->ip6_own_query.timer);
16868c2ecf20Sopenharmony_ci#endif
16878c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
16888c2ecf20Sopenharmony_ci}
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_cistatic int __grp_src_delete_marked(struct net_bridge_port_group *pg)
16918c2ecf20Sopenharmony_ci{
16928c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
16938c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
16948c2ecf20Sopenharmony_ci	int deleted = 0;
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
16978c2ecf20Sopenharmony_ci		if (ent->flags & BR_SGRP_F_DELETE) {
16988c2ecf20Sopenharmony_ci			br_multicast_del_group_src(ent);
16998c2ecf20Sopenharmony_ci			deleted++;
17008c2ecf20Sopenharmony_ci		}
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	return deleted;
17038c2ecf20Sopenharmony_ci}
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_cistatic void __grp_src_mod_timer(struct net_bridge_group_src *src,
17068c2ecf20Sopenharmony_ci				unsigned long expires)
17078c2ecf20Sopenharmony_ci{
17088c2ecf20Sopenharmony_ci	mod_timer(&src->timer, expires);
17098c2ecf20Sopenharmony_ci	br_multicast_fwd_src_handle(src);
17108c2ecf20Sopenharmony_ci}
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_cistatic void __grp_src_query_marked_and_rexmit(struct net_bridge_port_group *pg)
17138c2ecf20Sopenharmony_ci{
17148c2ecf20Sopenharmony_ci	struct bridge_mcast_other_query *other_query = NULL;
17158c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
17168c2ecf20Sopenharmony_ci	u32 lmqc = br->multicast_last_member_count;
17178c2ecf20Sopenharmony_ci	unsigned long lmqt, lmi, now = jiffies;
17188c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) ||
17218c2ecf20Sopenharmony_ci	    !br_opt_get(br, BROPT_MULTICAST_ENABLED))
17228c2ecf20Sopenharmony_ci		return;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	if (pg->key.addr.proto == htons(ETH_P_IP))
17258c2ecf20Sopenharmony_ci		other_query = &br->ip4_other_query;
17268c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
17278c2ecf20Sopenharmony_ci	else
17288c2ecf20Sopenharmony_ci		other_query = &br->ip6_other_query;
17298c2ecf20Sopenharmony_ci#endif
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	lmqt = now + br_multicast_lmqt(br);
17328c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node) {
17338c2ecf20Sopenharmony_ci		if (ent->flags & BR_SGRP_F_SEND) {
17348c2ecf20Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_SEND;
17358c2ecf20Sopenharmony_ci			if (ent->timer.expires > lmqt) {
17368c2ecf20Sopenharmony_ci				if (br_opt_get(br, BROPT_MULTICAST_QUERIER) &&
17378c2ecf20Sopenharmony_ci				    other_query &&
17388c2ecf20Sopenharmony_ci				    !timer_pending(&other_query->timer))
17398c2ecf20Sopenharmony_ci					ent->src_query_rexmit_cnt = lmqc;
17408c2ecf20Sopenharmony_ci				__grp_src_mod_timer(ent, lmqt);
17418c2ecf20Sopenharmony_ci			}
17428c2ecf20Sopenharmony_ci		}
17438c2ecf20Sopenharmony_ci	}
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_QUERIER) ||
17468c2ecf20Sopenharmony_ci	    !other_query || timer_pending(&other_query->timer))
17478c2ecf20Sopenharmony_ci		return;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	__br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr,
17508c2ecf20Sopenharmony_ci				  &pg->key.addr, true, 1, NULL);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	lmi = now + br->multicast_last_member_interval;
17538c2ecf20Sopenharmony_ci	if (!timer_pending(&pg->rexmit_timer) ||
17548c2ecf20Sopenharmony_ci	    time_after(pg->rexmit_timer.expires, lmi))
17558c2ecf20Sopenharmony_ci		mod_timer(&pg->rexmit_timer, lmi);
17568c2ecf20Sopenharmony_ci}
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_cistatic void __grp_send_query_and_rexmit(struct net_bridge_port_group *pg)
17598c2ecf20Sopenharmony_ci{
17608c2ecf20Sopenharmony_ci	struct bridge_mcast_other_query *other_query = NULL;
17618c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
17628c2ecf20Sopenharmony_ci	unsigned long now = jiffies, lmi;
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) ||
17658c2ecf20Sopenharmony_ci	    !br_opt_get(br, BROPT_MULTICAST_ENABLED))
17668c2ecf20Sopenharmony_ci		return;
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci	if (pg->key.addr.proto == htons(ETH_P_IP))
17698c2ecf20Sopenharmony_ci		other_query = &br->ip4_other_query;
17708c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
17718c2ecf20Sopenharmony_ci	else
17728c2ecf20Sopenharmony_ci		other_query = &br->ip6_other_query;
17738c2ecf20Sopenharmony_ci#endif
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	if (br_opt_get(br, BROPT_MULTICAST_QUERIER) &&
17768c2ecf20Sopenharmony_ci	    other_query && !timer_pending(&other_query->timer)) {
17778c2ecf20Sopenharmony_ci		lmi = now + br->multicast_last_member_interval;
17788c2ecf20Sopenharmony_ci		pg->grp_query_rexmit_cnt = br->multicast_last_member_count - 1;
17798c2ecf20Sopenharmony_ci		__br_multicast_send_query(br, pg->key.port, pg, &pg->key.addr,
17808c2ecf20Sopenharmony_ci					  &pg->key.addr, false, 0, NULL);
17818c2ecf20Sopenharmony_ci		if (!timer_pending(&pg->rexmit_timer) ||
17828c2ecf20Sopenharmony_ci		    time_after(pg->rexmit_timer.expires, lmi))
17838c2ecf20Sopenharmony_ci			mod_timer(&pg->rexmit_timer, lmi);
17848c2ecf20Sopenharmony_ci	}
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	if (pg->filter_mode == MCAST_EXCLUDE &&
17878c2ecf20Sopenharmony_ci	    (!timer_pending(&pg->timer) ||
17888c2ecf20Sopenharmony_ci	     time_after(pg->timer.expires, now + br_multicast_lmqt(br))))
17898c2ecf20Sopenharmony_ci		mod_timer(&pg->timer, now + br_multicast_lmqt(br));
17908c2ecf20Sopenharmony_ci}
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci/* State          Msg type      New state                Actions
17938c2ecf20Sopenharmony_ci * INCLUDE (A)    IS_IN (B)     INCLUDE (A+B)            (B)=GMI
17948c2ecf20Sopenharmony_ci * INCLUDE (A)    ALLOW (B)     INCLUDE (A+B)            (B)=GMI
17958c2ecf20Sopenharmony_ci * EXCLUDE (X,Y)  ALLOW (A)     EXCLUDE (X+A,Y-A)        (A)=GMI
17968c2ecf20Sopenharmony_ci */
17978c2ecf20Sopenharmony_cistatic bool br_multicast_isinc_allow(struct net_bridge_port_group *pg,
17988c2ecf20Sopenharmony_ci				     void *srcs, u32 nsrcs, size_t src_size)
17998c2ecf20Sopenharmony_ci{
18008c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
18018c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
18028c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
18038c2ecf20Sopenharmony_ci	bool changed = false;
18048c2ecf20Sopenharmony_ci	struct br_ip src_ip;
18058c2ecf20Sopenharmony_ci	u32 src_idx;
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
18088c2ecf20Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
18098c2ecf20Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
18108c2ecf20Sopenharmony_ci		memcpy(&src_ip.src, srcs, src_size);
18118c2ecf20Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
18128c2ecf20Sopenharmony_ci		if (!ent) {
18138c2ecf20Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
18148c2ecf20Sopenharmony_ci			if (ent)
18158c2ecf20Sopenharmony_ci				changed = true;
18168c2ecf20Sopenharmony_ci		}
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci		if (ent)
18198c2ecf20Sopenharmony_ci			__grp_src_mod_timer(ent, now + br_multicast_gmi(br));
18208c2ecf20Sopenharmony_ci		srcs += src_size;
18218c2ecf20Sopenharmony_ci	}
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	return changed;
18248c2ecf20Sopenharmony_ci}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci/* State          Msg type      New state                Actions
18278c2ecf20Sopenharmony_ci * INCLUDE (A)    IS_EX (B)     EXCLUDE (A*B,B-A)        (B-A)=0
18288c2ecf20Sopenharmony_ci *                                                       Delete (A-B)
18298c2ecf20Sopenharmony_ci *                                                       Group Timer=GMI
18308c2ecf20Sopenharmony_ci */
18318c2ecf20Sopenharmony_cistatic void __grp_src_isexc_incl(struct net_bridge_port_group *pg,
18328c2ecf20Sopenharmony_ci				 void *srcs, u32 nsrcs, size_t src_size)
18338c2ecf20Sopenharmony_ci{
18348c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
18358c2ecf20Sopenharmony_ci	struct br_ip src_ip;
18368c2ecf20Sopenharmony_ci	u32 src_idx;
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
18398c2ecf20Sopenharmony_ci		ent->flags |= BR_SGRP_F_DELETE;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
18428c2ecf20Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
18438c2ecf20Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
18448c2ecf20Sopenharmony_ci		memcpy(&src_ip.src, srcs, src_size);
18458c2ecf20Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
18468c2ecf20Sopenharmony_ci		if (ent)
18478c2ecf20Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_DELETE;
18488c2ecf20Sopenharmony_ci		else
18498c2ecf20Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
18508c2ecf20Sopenharmony_ci		if (ent)
18518c2ecf20Sopenharmony_ci			br_multicast_fwd_src_handle(ent);
18528c2ecf20Sopenharmony_ci		srcs += src_size;
18538c2ecf20Sopenharmony_ci	}
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	__grp_src_delete_marked(pg);
18568c2ecf20Sopenharmony_ci}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci/* State          Msg type      New state                Actions
18598c2ecf20Sopenharmony_ci * EXCLUDE (X,Y)  IS_EX (A)     EXCLUDE (A-Y,Y*A)        (A-X-Y)=GMI
18608c2ecf20Sopenharmony_ci *                                                       Delete (X-A)
18618c2ecf20Sopenharmony_ci *                                                       Delete (Y-A)
18628c2ecf20Sopenharmony_ci *                                                       Group Timer=GMI
18638c2ecf20Sopenharmony_ci */
18648c2ecf20Sopenharmony_cistatic bool __grp_src_isexc_excl(struct net_bridge_port_group *pg,
18658c2ecf20Sopenharmony_ci				 void *srcs, u32 nsrcs, size_t src_size)
18668c2ecf20Sopenharmony_ci{
18678c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
18688c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
18698c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
18708c2ecf20Sopenharmony_ci	bool changed = false;
18718c2ecf20Sopenharmony_ci	struct br_ip src_ip;
18728c2ecf20Sopenharmony_ci	u32 src_idx;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
18758c2ecf20Sopenharmony_ci		ent->flags |= BR_SGRP_F_DELETE;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
18788c2ecf20Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
18798c2ecf20Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
18808c2ecf20Sopenharmony_ci		memcpy(&src_ip.src, srcs, src_size);
18818c2ecf20Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
18828c2ecf20Sopenharmony_ci		if (ent) {
18838c2ecf20Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_DELETE;
18848c2ecf20Sopenharmony_ci		} else {
18858c2ecf20Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
18868c2ecf20Sopenharmony_ci			if (ent) {
18878c2ecf20Sopenharmony_ci				__grp_src_mod_timer(ent,
18888c2ecf20Sopenharmony_ci						    now + br_multicast_gmi(br));
18898c2ecf20Sopenharmony_ci				changed = true;
18908c2ecf20Sopenharmony_ci			}
18918c2ecf20Sopenharmony_ci		}
18928c2ecf20Sopenharmony_ci		srcs += src_size;
18938c2ecf20Sopenharmony_ci	}
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	if (__grp_src_delete_marked(pg))
18968c2ecf20Sopenharmony_ci		changed = true;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	return changed;
18998c2ecf20Sopenharmony_ci}
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_cistatic bool br_multicast_isexc(struct net_bridge_port_group *pg,
19028c2ecf20Sopenharmony_ci			       void *srcs, u32 nsrcs, size_t src_size)
19038c2ecf20Sopenharmony_ci{
19048c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
19058c2ecf20Sopenharmony_ci	bool changed = false;
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	switch (pg->filter_mode) {
19088c2ecf20Sopenharmony_ci	case MCAST_INCLUDE:
19098c2ecf20Sopenharmony_ci		__grp_src_isexc_incl(pg, srcs, nsrcs, src_size);
19108c2ecf20Sopenharmony_ci		br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE);
19118c2ecf20Sopenharmony_ci		changed = true;
19128c2ecf20Sopenharmony_ci		break;
19138c2ecf20Sopenharmony_ci	case MCAST_EXCLUDE:
19148c2ecf20Sopenharmony_ci		changed = __grp_src_isexc_excl(pg, srcs, nsrcs, src_size);
19158c2ecf20Sopenharmony_ci		break;
19168c2ecf20Sopenharmony_ci	}
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	pg->filter_mode = MCAST_EXCLUDE;
19198c2ecf20Sopenharmony_ci	mod_timer(&pg->timer, jiffies + br_multicast_gmi(br));
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	return changed;
19228c2ecf20Sopenharmony_ci}
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci/* State          Msg type      New state                Actions
19258c2ecf20Sopenharmony_ci * INCLUDE (A)    TO_IN (B)     INCLUDE (A+B)            (B)=GMI
19268c2ecf20Sopenharmony_ci *                                                       Send Q(G,A-B)
19278c2ecf20Sopenharmony_ci */
19288c2ecf20Sopenharmony_cistatic bool __grp_src_toin_incl(struct net_bridge_port_group *pg,
19298c2ecf20Sopenharmony_ci				void *srcs, u32 nsrcs, size_t src_size)
19308c2ecf20Sopenharmony_ci{
19318c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
19328c2ecf20Sopenharmony_ci	u32 src_idx, to_send = pg->src_ents;
19338c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
19348c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
19358c2ecf20Sopenharmony_ci	bool changed = false;
19368c2ecf20Sopenharmony_ci	struct br_ip src_ip;
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
19398c2ecf20Sopenharmony_ci		ent->flags |= BR_SGRP_F_SEND;
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
19428c2ecf20Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
19438c2ecf20Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
19448c2ecf20Sopenharmony_ci		memcpy(&src_ip.src, srcs, src_size);
19458c2ecf20Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
19468c2ecf20Sopenharmony_ci		if (ent) {
19478c2ecf20Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_SEND;
19488c2ecf20Sopenharmony_ci			to_send--;
19498c2ecf20Sopenharmony_ci		} else {
19508c2ecf20Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
19518c2ecf20Sopenharmony_ci			if (ent)
19528c2ecf20Sopenharmony_ci				changed = true;
19538c2ecf20Sopenharmony_ci		}
19548c2ecf20Sopenharmony_ci		if (ent)
19558c2ecf20Sopenharmony_ci			__grp_src_mod_timer(ent, now + br_multicast_gmi(br));
19568c2ecf20Sopenharmony_ci		srcs += src_size;
19578c2ecf20Sopenharmony_ci	}
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	if (to_send)
19608c2ecf20Sopenharmony_ci		__grp_src_query_marked_and_rexmit(pg);
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci	return changed;
19638c2ecf20Sopenharmony_ci}
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci/* State          Msg type      New state                Actions
19668c2ecf20Sopenharmony_ci * EXCLUDE (X,Y)  TO_IN (A)     EXCLUDE (X+A,Y-A)        (A)=GMI
19678c2ecf20Sopenharmony_ci *                                                       Send Q(G,X-A)
19688c2ecf20Sopenharmony_ci *                                                       Send Q(G)
19698c2ecf20Sopenharmony_ci */
19708c2ecf20Sopenharmony_cistatic bool __grp_src_toin_excl(struct net_bridge_port_group *pg,
19718c2ecf20Sopenharmony_ci				void *srcs, u32 nsrcs, size_t src_size)
19728c2ecf20Sopenharmony_ci{
19738c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
19748c2ecf20Sopenharmony_ci	u32 src_idx, to_send = pg->src_ents;
19758c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
19768c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
19778c2ecf20Sopenharmony_ci	bool changed = false;
19788c2ecf20Sopenharmony_ci	struct br_ip src_ip;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
19818c2ecf20Sopenharmony_ci		if (timer_pending(&ent->timer))
19828c2ecf20Sopenharmony_ci			ent->flags |= BR_SGRP_F_SEND;
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
19858c2ecf20Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
19868c2ecf20Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
19878c2ecf20Sopenharmony_ci		memcpy(&src_ip.src, srcs, src_size);
19888c2ecf20Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
19898c2ecf20Sopenharmony_ci		if (ent) {
19908c2ecf20Sopenharmony_ci			if (timer_pending(&ent->timer)) {
19918c2ecf20Sopenharmony_ci				ent->flags &= ~BR_SGRP_F_SEND;
19928c2ecf20Sopenharmony_ci				to_send--;
19938c2ecf20Sopenharmony_ci			}
19948c2ecf20Sopenharmony_ci		} else {
19958c2ecf20Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
19968c2ecf20Sopenharmony_ci			if (ent)
19978c2ecf20Sopenharmony_ci				changed = true;
19988c2ecf20Sopenharmony_ci		}
19998c2ecf20Sopenharmony_ci		if (ent)
20008c2ecf20Sopenharmony_ci			__grp_src_mod_timer(ent, now + br_multicast_gmi(br));
20018c2ecf20Sopenharmony_ci		srcs += src_size;
20028c2ecf20Sopenharmony_ci	}
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	if (to_send)
20058c2ecf20Sopenharmony_ci		__grp_src_query_marked_and_rexmit(pg);
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	__grp_send_query_and_rexmit(pg);
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	return changed;
20108c2ecf20Sopenharmony_ci}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_cistatic bool br_multicast_toin(struct net_bridge_port_group *pg,
20138c2ecf20Sopenharmony_ci			      void *srcs, u32 nsrcs, size_t src_size)
20148c2ecf20Sopenharmony_ci{
20158c2ecf20Sopenharmony_ci	bool changed = false;
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	switch (pg->filter_mode) {
20188c2ecf20Sopenharmony_ci	case MCAST_INCLUDE:
20198c2ecf20Sopenharmony_ci		changed = __grp_src_toin_incl(pg, srcs, nsrcs, src_size);
20208c2ecf20Sopenharmony_ci		break;
20218c2ecf20Sopenharmony_ci	case MCAST_EXCLUDE:
20228c2ecf20Sopenharmony_ci		changed = __grp_src_toin_excl(pg, srcs, nsrcs, src_size);
20238c2ecf20Sopenharmony_ci		break;
20248c2ecf20Sopenharmony_ci	}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	return changed;
20278c2ecf20Sopenharmony_ci}
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci/* State          Msg type      New state                Actions
20308c2ecf20Sopenharmony_ci * INCLUDE (A)    TO_EX (B)     EXCLUDE (A*B,B-A)        (B-A)=0
20318c2ecf20Sopenharmony_ci *                                                       Delete (A-B)
20328c2ecf20Sopenharmony_ci *                                                       Send Q(G,A*B)
20338c2ecf20Sopenharmony_ci *                                                       Group Timer=GMI
20348c2ecf20Sopenharmony_ci */
20358c2ecf20Sopenharmony_cistatic void __grp_src_toex_incl(struct net_bridge_port_group *pg,
20368c2ecf20Sopenharmony_ci				void *srcs, u32 nsrcs, size_t src_size)
20378c2ecf20Sopenharmony_ci{
20388c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
20398c2ecf20Sopenharmony_ci	u32 src_idx, to_send = 0;
20408c2ecf20Sopenharmony_ci	struct br_ip src_ip;
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
20438c2ecf20Sopenharmony_ci		ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE;
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
20468c2ecf20Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
20478c2ecf20Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
20488c2ecf20Sopenharmony_ci		memcpy(&src_ip.src, srcs, src_size);
20498c2ecf20Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
20508c2ecf20Sopenharmony_ci		if (ent) {
20518c2ecf20Sopenharmony_ci			ent->flags = (ent->flags & ~BR_SGRP_F_DELETE) |
20528c2ecf20Sopenharmony_ci				     BR_SGRP_F_SEND;
20538c2ecf20Sopenharmony_ci			to_send++;
20548c2ecf20Sopenharmony_ci		} else {
20558c2ecf20Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
20568c2ecf20Sopenharmony_ci		}
20578c2ecf20Sopenharmony_ci		if (ent)
20588c2ecf20Sopenharmony_ci			br_multicast_fwd_src_handle(ent);
20598c2ecf20Sopenharmony_ci		srcs += src_size;
20608c2ecf20Sopenharmony_ci	}
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	__grp_src_delete_marked(pg);
20638c2ecf20Sopenharmony_ci	if (to_send)
20648c2ecf20Sopenharmony_ci		__grp_src_query_marked_and_rexmit(pg);
20658c2ecf20Sopenharmony_ci}
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci/* State          Msg type      New state                Actions
20688c2ecf20Sopenharmony_ci * EXCLUDE (X,Y)  TO_EX (A)     EXCLUDE (A-Y,Y*A)        (A-X-Y)=Group Timer
20698c2ecf20Sopenharmony_ci *                                                       Delete (X-A)
20708c2ecf20Sopenharmony_ci *                                                       Delete (Y-A)
20718c2ecf20Sopenharmony_ci *                                                       Send Q(G,A-Y)
20728c2ecf20Sopenharmony_ci *                                                       Group Timer=GMI
20738c2ecf20Sopenharmony_ci */
20748c2ecf20Sopenharmony_cistatic bool __grp_src_toex_excl(struct net_bridge_port_group *pg,
20758c2ecf20Sopenharmony_ci				void *srcs, u32 nsrcs, size_t src_size)
20768c2ecf20Sopenharmony_ci{
20778c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
20788c2ecf20Sopenharmony_ci	u32 src_idx, to_send = 0;
20798c2ecf20Sopenharmony_ci	bool changed = false;
20808c2ecf20Sopenharmony_ci	struct br_ip src_ip;
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
20838c2ecf20Sopenharmony_ci		ent->flags = (ent->flags & ~BR_SGRP_F_SEND) | BR_SGRP_F_DELETE;
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
20868c2ecf20Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
20878c2ecf20Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
20888c2ecf20Sopenharmony_ci		memcpy(&src_ip.src, srcs, src_size);
20898c2ecf20Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
20908c2ecf20Sopenharmony_ci		if (ent) {
20918c2ecf20Sopenharmony_ci			ent->flags &= ~BR_SGRP_F_DELETE;
20928c2ecf20Sopenharmony_ci		} else {
20938c2ecf20Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
20948c2ecf20Sopenharmony_ci			if (ent) {
20958c2ecf20Sopenharmony_ci				__grp_src_mod_timer(ent, pg->timer.expires);
20968c2ecf20Sopenharmony_ci				changed = true;
20978c2ecf20Sopenharmony_ci			}
20988c2ecf20Sopenharmony_ci		}
20998c2ecf20Sopenharmony_ci		if (ent && timer_pending(&ent->timer)) {
21008c2ecf20Sopenharmony_ci			ent->flags |= BR_SGRP_F_SEND;
21018c2ecf20Sopenharmony_ci			to_send++;
21028c2ecf20Sopenharmony_ci		}
21038c2ecf20Sopenharmony_ci		srcs += src_size;
21048c2ecf20Sopenharmony_ci	}
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	if (__grp_src_delete_marked(pg))
21078c2ecf20Sopenharmony_ci		changed = true;
21088c2ecf20Sopenharmony_ci	if (to_send)
21098c2ecf20Sopenharmony_ci		__grp_src_query_marked_and_rexmit(pg);
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	return changed;
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_cistatic bool br_multicast_toex(struct net_bridge_port_group *pg,
21158c2ecf20Sopenharmony_ci			      void *srcs, u32 nsrcs, size_t src_size)
21168c2ecf20Sopenharmony_ci{
21178c2ecf20Sopenharmony_ci	struct net_bridge *br = pg->key.port->br;
21188c2ecf20Sopenharmony_ci	bool changed = false;
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_ci	switch (pg->filter_mode) {
21218c2ecf20Sopenharmony_ci	case MCAST_INCLUDE:
21228c2ecf20Sopenharmony_ci		__grp_src_toex_incl(pg, srcs, nsrcs, src_size);
21238c2ecf20Sopenharmony_ci		br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE);
21248c2ecf20Sopenharmony_ci		changed = true;
21258c2ecf20Sopenharmony_ci		break;
21268c2ecf20Sopenharmony_ci	case MCAST_EXCLUDE:
21278c2ecf20Sopenharmony_ci		changed = __grp_src_toex_excl(pg, srcs, nsrcs, src_size);
21288c2ecf20Sopenharmony_ci		break;
21298c2ecf20Sopenharmony_ci	}
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	pg->filter_mode = MCAST_EXCLUDE;
21328c2ecf20Sopenharmony_ci	mod_timer(&pg->timer, jiffies + br_multicast_gmi(br));
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	return changed;
21358c2ecf20Sopenharmony_ci}
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci/* State          Msg type      New state                Actions
21388c2ecf20Sopenharmony_ci * INCLUDE (A)    BLOCK (B)     INCLUDE (A)              Send Q(G,A*B)
21398c2ecf20Sopenharmony_ci */
21408c2ecf20Sopenharmony_cistatic void __grp_src_block_incl(struct net_bridge_port_group *pg,
21418c2ecf20Sopenharmony_ci				 void *srcs, u32 nsrcs, size_t src_size)
21428c2ecf20Sopenharmony_ci{
21438c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
21448c2ecf20Sopenharmony_ci	u32 src_idx, to_send = 0;
21458c2ecf20Sopenharmony_ci	struct br_ip src_ip;
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
21488c2ecf20Sopenharmony_ci		ent->flags &= ~BR_SGRP_F_SEND;
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
21518c2ecf20Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
21528c2ecf20Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
21538c2ecf20Sopenharmony_ci		memcpy(&src_ip.src, srcs, src_size);
21548c2ecf20Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
21558c2ecf20Sopenharmony_ci		if (ent) {
21568c2ecf20Sopenharmony_ci			ent->flags |= BR_SGRP_F_SEND;
21578c2ecf20Sopenharmony_ci			to_send++;
21588c2ecf20Sopenharmony_ci		}
21598c2ecf20Sopenharmony_ci		srcs += src_size;
21608c2ecf20Sopenharmony_ci	}
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_ci	if (to_send)
21638c2ecf20Sopenharmony_ci		__grp_src_query_marked_and_rexmit(pg);
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci	if (pg->filter_mode == MCAST_INCLUDE && hlist_empty(&pg->src_list))
21668c2ecf20Sopenharmony_ci		br_multicast_find_del_pg(pg->key.port->br, pg);
21678c2ecf20Sopenharmony_ci}
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci/* State          Msg type      New state                Actions
21708c2ecf20Sopenharmony_ci * EXCLUDE (X,Y)  BLOCK (A)     EXCLUDE (X+(A-Y),Y)      (A-X-Y)=Group Timer
21718c2ecf20Sopenharmony_ci *                                                       Send Q(G,A-Y)
21728c2ecf20Sopenharmony_ci */
21738c2ecf20Sopenharmony_cistatic bool __grp_src_block_excl(struct net_bridge_port_group *pg,
21748c2ecf20Sopenharmony_ci				 void *srcs, u32 nsrcs, size_t src_size)
21758c2ecf20Sopenharmony_ci{
21768c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
21778c2ecf20Sopenharmony_ci	u32 src_idx, to_send = 0;
21788c2ecf20Sopenharmony_ci	bool changed = false;
21798c2ecf20Sopenharmony_ci	struct br_ip src_ip;
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node)
21828c2ecf20Sopenharmony_ci		ent->flags &= ~BR_SGRP_F_SEND;
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	memset(&src_ip, 0, sizeof(src_ip));
21858c2ecf20Sopenharmony_ci	src_ip.proto = pg->key.addr.proto;
21868c2ecf20Sopenharmony_ci	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
21878c2ecf20Sopenharmony_ci		memcpy(&src_ip.src, srcs, src_size);
21888c2ecf20Sopenharmony_ci		ent = br_multicast_find_group_src(pg, &src_ip);
21898c2ecf20Sopenharmony_ci		if (!ent) {
21908c2ecf20Sopenharmony_ci			ent = br_multicast_new_group_src(pg, &src_ip);
21918c2ecf20Sopenharmony_ci			if (ent) {
21928c2ecf20Sopenharmony_ci				__grp_src_mod_timer(ent, pg->timer.expires);
21938c2ecf20Sopenharmony_ci				changed = true;
21948c2ecf20Sopenharmony_ci			}
21958c2ecf20Sopenharmony_ci		}
21968c2ecf20Sopenharmony_ci		if (ent && timer_pending(&ent->timer)) {
21978c2ecf20Sopenharmony_ci			ent->flags |= BR_SGRP_F_SEND;
21988c2ecf20Sopenharmony_ci			to_send++;
21998c2ecf20Sopenharmony_ci		}
22008c2ecf20Sopenharmony_ci		srcs += src_size;
22018c2ecf20Sopenharmony_ci	}
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ci	if (to_send)
22048c2ecf20Sopenharmony_ci		__grp_src_query_marked_and_rexmit(pg);
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	return changed;
22078c2ecf20Sopenharmony_ci}
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_cistatic bool br_multicast_block(struct net_bridge_port_group *pg,
22108c2ecf20Sopenharmony_ci			       void *srcs, u32 nsrcs, size_t src_size)
22118c2ecf20Sopenharmony_ci{
22128c2ecf20Sopenharmony_ci	bool changed = false;
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci	switch (pg->filter_mode) {
22158c2ecf20Sopenharmony_ci	case MCAST_INCLUDE:
22168c2ecf20Sopenharmony_ci		__grp_src_block_incl(pg, srcs, nsrcs, src_size);
22178c2ecf20Sopenharmony_ci		break;
22188c2ecf20Sopenharmony_ci	case MCAST_EXCLUDE:
22198c2ecf20Sopenharmony_ci		changed = __grp_src_block_excl(pg, srcs, nsrcs, src_size);
22208c2ecf20Sopenharmony_ci		break;
22218c2ecf20Sopenharmony_ci	}
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	return changed;
22248c2ecf20Sopenharmony_ci}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_cistatic struct net_bridge_port_group *
22278c2ecf20Sopenharmony_cibr_multicast_find_port(struct net_bridge_mdb_entry *mp,
22288c2ecf20Sopenharmony_ci		       struct net_bridge_port *p,
22298c2ecf20Sopenharmony_ci		       const unsigned char *src)
22308c2ecf20Sopenharmony_ci{
22318c2ecf20Sopenharmony_ci	struct net_bridge *br __maybe_unused = mp->br;
22328c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	for (pg = mlock_dereference(mp->ports, br);
22358c2ecf20Sopenharmony_ci	     pg;
22368c2ecf20Sopenharmony_ci	     pg = mlock_dereference(pg->next, br))
22378c2ecf20Sopenharmony_ci		if (br_port_group_equal(pg, p, src))
22388c2ecf20Sopenharmony_ci			return pg;
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	return NULL;
22418c2ecf20Sopenharmony_ci}
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_cistatic int br_ip4_multicast_igmp3_report(struct net_bridge *br,
22448c2ecf20Sopenharmony_ci					 struct net_bridge_port *port,
22458c2ecf20Sopenharmony_ci					 struct sk_buff *skb,
22468c2ecf20Sopenharmony_ci					 u16 vid)
22478c2ecf20Sopenharmony_ci{
22488c2ecf20Sopenharmony_ci	bool igmpv2 = br->multicast_igmp_version == 2;
22498c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mdst;
22508c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
22518c2ecf20Sopenharmony_ci	const unsigned char *src;
22528c2ecf20Sopenharmony_ci	struct igmpv3_report *ih;
22538c2ecf20Sopenharmony_ci	struct igmpv3_grec *grec;
22548c2ecf20Sopenharmony_ci	int i, len, num, type;
22558c2ecf20Sopenharmony_ci	bool changed = false;
22568c2ecf20Sopenharmony_ci	__be32 group;
22578c2ecf20Sopenharmony_ci	int err = 0;
22588c2ecf20Sopenharmony_ci	u16 nsrcs;
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci	ih = igmpv3_report_hdr(skb);
22618c2ecf20Sopenharmony_ci	num = ntohs(ih->ngrec);
22628c2ecf20Sopenharmony_ci	len = skb_transport_offset(skb) + sizeof(*ih);
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
22658c2ecf20Sopenharmony_ci		len += sizeof(*grec);
22668c2ecf20Sopenharmony_ci		if (!ip_mc_may_pull(skb, len))
22678c2ecf20Sopenharmony_ci			return -EINVAL;
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci		grec = (void *)(skb->data + len - sizeof(*grec));
22708c2ecf20Sopenharmony_ci		group = grec->grec_mca;
22718c2ecf20Sopenharmony_ci		type = grec->grec_type;
22728c2ecf20Sopenharmony_ci		nsrcs = ntohs(grec->grec_nsrcs);
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_ci		len += nsrcs * 4;
22758c2ecf20Sopenharmony_ci		if (!ip_mc_may_pull(skb, len))
22768c2ecf20Sopenharmony_ci			return -EINVAL;
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ci		switch (type) {
22798c2ecf20Sopenharmony_ci		case IGMPV3_MODE_IS_INCLUDE:
22808c2ecf20Sopenharmony_ci		case IGMPV3_MODE_IS_EXCLUDE:
22818c2ecf20Sopenharmony_ci		case IGMPV3_CHANGE_TO_INCLUDE:
22828c2ecf20Sopenharmony_ci		case IGMPV3_CHANGE_TO_EXCLUDE:
22838c2ecf20Sopenharmony_ci		case IGMPV3_ALLOW_NEW_SOURCES:
22848c2ecf20Sopenharmony_ci		case IGMPV3_BLOCK_OLD_SOURCES:
22858c2ecf20Sopenharmony_ci			break;
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci		default:
22888c2ecf20Sopenharmony_ci			continue;
22898c2ecf20Sopenharmony_ci		}
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci		src = eth_hdr(skb)->h_source;
22928c2ecf20Sopenharmony_ci		if (nsrcs == 0 &&
22938c2ecf20Sopenharmony_ci		    (type == IGMPV3_CHANGE_TO_INCLUDE ||
22948c2ecf20Sopenharmony_ci		     type == IGMPV3_MODE_IS_INCLUDE)) {
22958c2ecf20Sopenharmony_ci			if (!port || igmpv2) {
22968c2ecf20Sopenharmony_ci				br_ip4_multicast_leave_group(br, port, group, vid, src);
22978c2ecf20Sopenharmony_ci				continue;
22988c2ecf20Sopenharmony_ci			}
22998c2ecf20Sopenharmony_ci		} else {
23008c2ecf20Sopenharmony_ci			err = br_ip4_multicast_add_group(br, port, group, vid,
23018c2ecf20Sopenharmony_ci							 src, igmpv2);
23028c2ecf20Sopenharmony_ci			if (err)
23038c2ecf20Sopenharmony_ci				break;
23048c2ecf20Sopenharmony_ci		}
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_ci		if (!port || igmpv2)
23078c2ecf20Sopenharmony_ci			continue;
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_ci		spin_lock_bh(&br->multicast_lock);
23108c2ecf20Sopenharmony_ci		mdst = br_mdb_ip4_get(br, group, vid);
23118c2ecf20Sopenharmony_ci		if (!mdst)
23128c2ecf20Sopenharmony_ci			goto unlock_continue;
23138c2ecf20Sopenharmony_ci		pg = br_multicast_find_port(mdst, port, src);
23148c2ecf20Sopenharmony_ci		if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT))
23158c2ecf20Sopenharmony_ci			goto unlock_continue;
23168c2ecf20Sopenharmony_ci		/* reload grec */
23178c2ecf20Sopenharmony_ci		grec = (void *)(skb->data + len - sizeof(*grec) - (nsrcs * 4));
23188c2ecf20Sopenharmony_ci		switch (type) {
23198c2ecf20Sopenharmony_ci		case IGMPV3_ALLOW_NEW_SOURCES:
23208c2ecf20Sopenharmony_ci			changed = br_multicast_isinc_allow(pg, grec->grec_src,
23218c2ecf20Sopenharmony_ci							   nsrcs, sizeof(__be32));
23228c2ecf20Sopenharmony_ci			break;
23238c2ecf20Sopenharmony_ci		case IGMPV3_MODE_IS_INCLUDE:
23248c2ecf20Sopenharmony_ci			changed = br_multicast_isinc_allow(pg, grec->grec_src, nsrcs,
23258c2ecf20Sopenharmony_ci							   sizeof(__be32));
23268c2ecf20Sopenharmony_ci			break;
23278c2ecf20Sopenharmony_ci		case IGMPV3_MODE_IS_EXCLUDE:
23288c2ecf20Sopenharmony_ci			changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
23298c2ecf20Sopenharmony_ci						     sizeof(__be32));
23308c2ecf20Sopenharmony_ci			break;
23318c2ecf20Sopenharmony_ci		case IGMPV3_CHANGE_TO_INCLUDE:
23328c2ecf20Sopenharmony_ci			changed = br_multicast_toin(pg, grec->grec_src, nsrcs,
23338c2ecf20Sopenharmony_ci						    sizeof(__be32));
23348c2ecf20Sopenharmony_ci			break;
23358c2ecf20Sopenharmony_ci		case IGMPV3_CHANGE_TO_EXCLUDE:
23368c2ecf20Sopenharmony_ci			changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
23378c2ecf20Sopenharmony_ci						    sizeof(__be32));
23388c2ecf20Sopenharmony_ci			break;
23398c2ecf20Sopenharmony_ci		case IGMPV3_BLOCK_OLD_SOURCES:
23408c2ecf20Sopenharmony_ci			changed = br_multicast_block(pg, grec->grec_src, nsrcs,
23418c2ecf20Sopenharmony_ci						     sizeof(__be32));
23428c2ecf20Sopenharmony_ci			break;
23438c2ecf20Sopenharmony_ci		}
23448c2ecf20Sopenharmony_ci		if (changed)
23458c2ecf20Sopenharmony_ci			br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);
23468c2ecf20Sopenharmony_ciunlock_continue:
23478c2ecf20Sopenharmony_ci		spin_unlock_bh(&br->multicast_lock);
23488c2ecf20Sopenharmony_ci	}
23498c2ecf20Sopenharmony_ci
23508c2ecf20Sopenharmony_ci	return err;
23518c2ecf20Sopenharmony_ci}
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
23548c2ecf20Sopenharmony_cistatic int br_ip6_multicast_mld2_report(struct net_bridge *br,
23558c2ecf20Sopenharmony_ci					struct net_bridge_port *port,
23568c2ecf20Sopenharmony_ci					struct sk_buff *skb,
23578c2ecf20Sopenharmony_ci					u16 vid)
23588c2ecf20Sopenharmony_ci{
23598c2ecf20Sopenharmony_ci	bool mldv1 = br->multicast_mld_version == 1;
23608c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mdst;
23618c2ecf20Sopenharmony_ci	struct net_bridge_port_group *pg;
23628c2ecf20Sopenharmony_ci	unsigned int nsrcs_offset;
23638c2ecf20Sopenharmony_ci	const unsigned char *src;
23648c2ecf20Sopenharmony_ci	struct icmp6hdr *icmp6h;
23658c2ecf20Sopenharmony_ci	struct mld2_grec *grec;
23668c2ecf20Sopenharmony_ci	unsigned int grec_len;
23678c2ecf20Sopenharmony_ci	bool changed = false;
23688c2ecf20Sopenharmony_ci	int i, len, num;
23698c2ecf20Sopenharmony_ci	int err = 0;
23708c2ecf20Sopenharmony_ci
23718c2ecf20Sopenharmony_ci	if (!ipv6_mc_may_pull(skb, sizeof(*icmp6h)))
23728c2ecf20Sopenharmony_ci		return -EINVAL;
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	icmp6h = icmp6_hdr(skb);
23758c2ecf20Sopenharmony_ci	num = ntohs(icmp6h->icmp6_dataun.un_data16[1]);
23768c2ecf20Sopenharmony_ci	len = skb_transport_offset(skb) + sizeof(*icmp6h);
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
23798c2ecf20Sopenharmony_ci		__be16 *_nsrcs, __nsrcs;
23808c2ecf20Sopenharmony_ci		u16 nsrcs;
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci		nsrcs_offset = len + offsetof(struct mld2_grec, grec_nsrcs);
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci		if (skb_transport_offset(skb) + ipv6_transport_len(skb) <
23858c2ecf20Sopenharmony_ci		    nsrcs_offset + sizeof(__nsrcs))
23868c2ecf20Sopenharmony_ci			return -EINVAL;
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci		_nsrcs = skb_header_pointer(skb, nsrcs_offset,
23898c2ecf20Sopenharmony_ci					    sizeof(__nsrcs), &__nsrcs);
23908c2ecf20Sopenharmony_ci		if (!_nsrcs)
23918c2ecf20Sopenharmony_ci			return -EINVAL;
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci		nsrcs = ntohs(*_nsrcs);
23948c2ecf20Sopenharmony_ci		grec_len = struct_size(grec, grec_src, nsrcs);
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci		if (!ipv6_mc_may_pull(skb, len + grec_len))
23978c2ecf20Sopenharmony_ci			return -EINVAL;
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_ci		grec = (struct mld2_grec *)(skb->data + len);
24008c2ecf20Sopenharmony_ci		len += grec_len;
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci		switch (grec->grec_type) {
24038c2ecf20Sopenharmony_ci		case MLD2_MODE_IS_INCLUDE:
24048c2ecf20Sopenharmony_ci		case MLD2_MODE_IS_EXCLUDE:
24058c2ecf20Sopenharmony_ci		case MLD2_CHANGE_TO_INCLUDE:
24068c2ecf20Sopenharmony_ci		case MLD2_CHANGE_TO_EXCLUDE:
24078c2ecf20Sopenharmony_ci		case MLD2_ALLOW_NEW_SOURCES:
24088c2ecf20Sopenharmony_ci		case MLD2_BLOCK_OLD_SOURCES:
24098c2ecf20Sopenharmony_ci			break;
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci		default:
24128c2ecf20Sopenharmony_ci			continue;
24138c2ecf20Sopenharmony_ci		}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_ci		src = eth_hdr(skb)->h_source;
24168c2ecf20Sopenharmony_ci		if ((grec->grec_type == MLD2_CHANGE_TO_INCLUDE ||
24178c2ecf20Sopenharmony_ci		     grec->grec_type == MLD2_MODE_IS_INCLUDE) &&
24188c2ecf20Sopenharmony_ci		    nsrcs == 0) {
24198c2ecf20Sopenharmony_ci			if (!port || mldv1) {
24208c2ecf20Sopenharmony_ci				br_ip6_multicast_leave_group(br, port,
24218c2ecf20Sopenharmony_ci							     &grec->grec_mca,
24228c2ecf20Sopenharmony_ci							     vid, src);
24238c2ecf20Sopenharmony_ci				continue;
24248c2ecf20Sopenharmony_ci			}
24258c2ecf20Sopenharmony_ci		} else {
24268c2ecf20Sopenharmony_ci			err = br_ip6_multicast_add_group(br, port,
24278c2ecf20Sopenharmony_ci							 &grec->grec_mca, vid,
24288c2ecf20Sopenharmony_ci							 src, mldv1);
24298c2ecf20Sopenharmony_ci			if (err)
24308c2ecf20Sopenharmony_ci				break;
24318c2ecf20Sopenharmony_ci		}
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci		if (!port || mldv1)
24348c2ecf20Sopenharmony_ci			continue;
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci		spin_lock_bh(&br->multicast_lock);
24378c2ecf20Sopenharmony_ci		mdst = br_mdb_ip6_get(br, &grec->grec_mca, vid);
24388c2ecf20Sopenharmony_ci		if (!mdst)
24398c2ecf20Sopenharmony_ci			goto unlock_continue;
24408c2ecf20Sopenharmony_ci		pg = br_multicast_find_port(mdst, port, src);
24418c2ecf20Sopenharmony_ci		if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT))
24428c2ecf20Sopenharmony_ci			goto unlock_continue;
24438c2ecf20Sopenharmony_ci		switch (grec->grec_type) {
24448c2ecf20Sopenharmony_ci		case MLD2_ALLOW_NEW_SOURCES:
24458c2ecf20Sopenharmony_ci			changed = br_multicast_isinc_allow(pg, grec->grec_src,
24468c2ecf20Sopenharmony_ci							   nsrcs,
24478c2ecf20Sopenharmony_ci							   sizeof(struct in6_addr));
24488c2ecf20Sopenharmony_ci			break;
24498c2ecf20Sopenharmony_ci		case MLD2_MODE_IS_INCLUDE:
24508c2ecf20Sopenharmony_ci			changed = br_multicast_isinc_allow(pg, grec->grec_src, nsrcs,
24518c2ecf20Sopenharmony_ci							   sizeof(struct in6_addr));
24528c2ecf20Sopenharmony_ci			break;
24538c2ecf20Sopenharmony_ci		case MLD2_MODE_IS_EXCLUDE:
24548c2ecf20Sopenharmony_ci			changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
24558c2ecf20Sopenharmony_ci						     sizeof(struct in6_addr));
24568c2ecf20Sopenharmony_ci			break;
24578c2ecf20Sopenharmony_ci		case MLD2_CHANGE_TO_INCLUDE:
24588c2ecf20Sopenharmony_ci			changed = br_multicast_toin(pg, grec->grec_src, nsrcs,
24598c2ecf20Sopenharmony_ci						    sizeof(struct in6_addr));
24608c2ecf20Sopenharmony_ci			break;
24618c2ecf20Sopenharmony_ci		case MLD2_CHANGE_TO_EXCLUDE:
24628c2ecf20Sopenharmony_ci			changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
24638c2ecf20Sopenharmony_ci						    sizeof(struct in6_addr));
24648c2ecf20Sopenharmony_ci			break;
24658c2ecf20Sopenharmony_ci		case MLD2_BLOCK_OLD_SOURCES:
24668c2ecf20Sopenharmony_ci			changed = br_multicast_block(pg, grec->grec_src, nsrcs,
24678c2ecf20Sopenharmony_ci						     sizeof(struct in6_addr));
24688c2ecf20Sopenharmony_ci			break;
24698c2ecf20Sopenharmony_ci		}
24708c2ecf20Sopenharmony_ci		if (changed)
24718c2ecf20Sopenharmony_ci			br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);
24728c2ecf20Sopenharmony_ciunlock_continue:
24738c2ecf20Sopenharmony_ci		spin_unlock_bh(&br->multicast_lock);
24748c2ecf20Sopenharmony_ci	}
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci	return err;
24778c2ecf20Sopenharmony_ci}
24788c2ecf20Sopenharmony_ci#endif
24798c2ecf20Sopenharmony_ci
24808c2ecf20Sopenharmony_cistatic bool br_ip4_multicast_select_querier(struct net_bridge *br,
24818c2ecf20Sopenharmony_ci					    struct net_bridge_port *port,
24828c2ecf20Sopenharmony_ci					    __be32 saddr)
24838c2ecf20Sopenharmony_ci{
24848c2ecf20Sopenharmony_ci	if (!timer_pending(&br->ip4_own_query.timer) &&
24858c2ecf20Sopenharmony_ci	    !timer_pending(&br->ip4_other_query.timer))
24868c2ecf20Sopenharmony_ci		goto update;
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	if (!br->ip4_querier.addr.src.ip4)
24898c2ecf20Sopenharmony_ci		goto update;
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ci	if (ntohl(saddr) <= ntohl(br->ip4_querier.addr.src.ip4))
24928c2ecf20Sopenharmony_ci		goto update;
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	return false;
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ciupdate:
24978c2ecf20Sopenharmony_ci	br->ip4_querier.addr.src.ip4 = saddr;
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	/* update protected by general multicast_lock by caller */
25008c2ecf20Sopenharmony_ci	rcu_assign_pointer(br->ip4_querier.port, port);
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	return true;
25038c2ecf20Sopenharmony_ci}
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
25068c2ecf20Sopenharmony_cistatic bool br_ip6_multicast_select_querier(struct net_bridge *br,
25078c2ecf20Sopenharmony_ci					    struct net_bridge_port *port,
25088c2ecf20Sopenharmony_ci					    struct in6_addr *saddr)
25098c2ecf20Sopenharmony_ci{
25108c2ecf20Sopenharmony_ci	if (!timer_pending(&br->ip6_own_query.timer) &&
25118c2ecf20Sopenharmony_ci	    !timer_pending(&br->ip6_other_query.timer))
25128c2ecf20Sopenharmony_ci		goto update;
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_ci	if (ipv6_addr_cmp(saddr, &br->ip6_querier.addr.src.ip6) <= 0)
25158c2ecf20Sopenharmony_ci		goto update;
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	return false;
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_ciupdate:
25208c2ecf20Sopenharmony_ci	br->ip6_querier.addr.src.ip6 = *saddr;
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_ci	/* update protected by general multicast_lock by caller */
25238c2ecf20Sopenharmony_ci	rcu_assign_pointer(br->ip6_querier.port, port);
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_ci	return true;
25268c2ecf20Sopenharmony_ci}
25278c2ecf20Sopenharmony_ci#endif
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_cistatic bool br_multicast_select_querier(struct net_bridge *br,
25308c2ecf20Sopenharmony_ci					struct net_bridge_port *port,
25318c2ecf20Sopenharmony_ci					struct br_ip *saddr)
25328c2ecf20Sopenharmony_ci{
25338c2ecf20Sopenharmony_ci	switch (saddr->proto) {
25348c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
25358c2ecf20Sopenharmony_ci		return br_ip4_multicast_select_querier(br, port, saddr->src.ip4);
25368c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
25378c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
25388c2ecf20Sopenharmony_ci		return br_ip6_multicast_select_querier(br, port, &saddr->src.ip6);
25398c2ecf20Sopenharmony_ci#endif
25408c2ecf20Sopenharmony_ci	}
25418c2ecf20Sopenharmony_ci
25428c2ecf20Sopenharmony_ci	return false;
25438c2ecf20Sopenharmony_ci}
25448c2ecf20Sopenharmony_ci
25458c2ecf20Sopenharmony_cistatic void
25468c2ecf20Sopenharmony_cibr_multicast_update_query_timer(struct net_bridge *br,
25478c2ecf20Sopenharmony_ci				struct bridge_mcast_other_query *query,
25488c2ecf20Sopenharmony_ci				unsigned long max_delay)
25498c2ecf20Sopenharmony_ci{
25508c2ecf20Sopenharmony_ci	if (!timer_pending(&query->timer))
25518c2ecf20Sopenharmony_ci		query->delay_time = jiffies + max_delay;
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci	mod_timer(&query->timer, jiffies + br->multicast_querier_interval);
25548c2ecf20Sopenharmony_ci}
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_cistatic void br_port_mc_router_state_change(struct net_bridge_port *p,
25578c2ecf20Sopenharmony_ci					   bool is_mc_router)
25588c2ecf20Sopenharmony_ci{
25598c2ecf20Sopenharmony_ci	struct switchdev_attr attr = {
25608c2ecf20Sopenharmony_ci		.orig_dev = p->dev,
25618c2ecf20Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_PORT_MROUTER,
25628c2ecf20Sopenharmony_ci		.flags = SWITCHDEV_F_DEFER,
25638c2ecf20Sopenharmony_ci		.u.mrouter = is_mc_router,
25648c2ecf20Sopenharmony_ci	};
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci	switchdev_port_attr_set(p->dev, &attr);
25678c2ecf20Sopenharmony_ci}
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci/*
25708c2ecf20Sopenharmony_ci * Add port to router_list
25718c2ecf20Sopenharmony_ci *  list is maintained ordered by pointer value
25728c2ecf20Sopenharmony_ci *  and locked by br->multicast_lock and RCU
25738c2ecf20Sopenharmony_ci */
25748c2ecf20Sopenharmony_cistatic void br_multicast_add_router(struct net_bridge *br,
25758c2ecf20Sopenharmony_ci				    struct net_bridge_port *port)
25768c2ecf20Sopenharmony_ci{
25778c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
25788c2ecf20Sopenharmony_ci	struct hlist_node *slot = NULL;
25798c2ecf20Sopenharmony_ci
25808c2ecf20Sopenharmony_ci	if (!hlist_unhashed(&port->rlist))
25818c2ecf20Sopenharmony_ci		return;
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_ci	hlist_for_each_entry(p, &br->router_list, rlist) {
25848c2ecf20Sopenharmony_ci		if ((unsigned long) port >= (unsigned long) p)
25858c2ecf20Sopenharmony_ci			break;
25868c2ecf20Sopenharmony_ci		slot = &p->rlist;
25878c2ecf20Sopenharmony_ci	}
25888c2ecf20Sopenharmony_ci
25898c2ecf20Sopenharmony_ci	if (slot)
25908c2ecf20Sopenharmony_ci		hlist_add_behind_rcu(&port->rlist, slot);
25918c2ecf20Sopenharmony_ci	else
25928c2ecf20Sopenharmony_ci		hlist_add_head_rcu(&port->rlist, &br->router_list);
25938c2ecf20Sopenharmony_ci	br_rtr_notify(br->dev, port, RTM_NEWMDB);
25948c2ecf20Sopenharmony_ci	br_port_mc_router_state_change(port, true);
25958c2ecf20Sopenharmony_ci}
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_cistatic void br_multicast_mark_router(struct net_bridge *br,
25988c2ecf20Sopenharmony_ci				     struct net_bridge_port *port)
25998c2ecf20Sopenharmony_ci{
26008c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_ci	if (!port) {
26038c2ecf20Sopenharmony_ci		if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) {
26048c2ecf20Sopenharmony_ci			if (!timer_pending(&br->multicast_router_timer))
26058c2ecf20Sopenharmony_ci				br_mc_router_state_change(br, true);
26068c2ecf20Sopenharmony_ci			mod_timer(&br->multicast_router_timer,
26078c2ecf20Sopenharmony_ci				  now + br->multicast_querier_interval);
26088c2ecf20Sopenharmony_ci		}
26098c2ecf20Sopenharmony_ci		return;
26108c2ecf20Sopenharmony_ci	}
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	if (port->multicast_router == MDB_RTR_TYPE_DISABLED ||
26138c2ecf20Sopenharmony_ci	    port->multicast_router == MDB_RTR_TYPE_PERM)
26148c2ecf20Sopenharmony_ci		return;
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci	br_multicast_add_router(br, port);
26178c2ecf20Sopenharmony_ci
26188c2ecf20Sopenharmony_ci	mod_timer(&port->multicast_router_timer,
26198c2ecf20Sopenharmony_ci		  now + br->multicast_querier_interval);
26208c2ecf20Sopenharmony_ci}
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_cistatic void br_multicast_query_received(struct net_bridge *br,
26238c2ecf20Sopenharmony_ci					struct net_bridge_port *port,
26248c2ecf20Sopenharmony_ci					struct bridge_mcast_other_query *query,
26258c2ecf20Sopenharmony_ci					struct br_ip *saddr,
26268c2ecf20Sopenharmony_ci					unsigned long max_delay)
26278c2ecf20Sopenharmony_ci{
26288c2ecf20Sopenharmony_ci	if (!br_multicast_select_querier(br, port, saddr))
26298c2ecf20Sopenharmony_ci		return;
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_ci	br_multicast_update_query_timer(br, query, max_delay);
26328c2ecf20Sopenharmony_ci	br_multicast_mark_router(br, port);
26338c2ecf20Sopenharmony_ci}
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_cistatic void br_ip4_multicast_query(struct net_bridge *br,
26368c2ecf20Sopenharmony_ci				   struct net_bridge_port *port,
26378c2ecf20Sopenharmony_ci				   struct sk_buff *skb,
26388c2ecf20Sopenharmony_ci				   u16 vid)
26398c2ecf20Sopenharmony_ci{
26408c2ecf20Sopenharmony_ci	unsigned int transport_len = ip_transport_len(skb);
26418c2ecf20Sopenharmony_ci	const struct iphdr *iph = ip_hdr(skb);
26428c2ecf20Sopenharmony_ci	struct igmphdr *ih = igmp_hdr(skb);
26438c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
26448c2ecf20Sopenharmony_ci	struct igmpv3_query *ih3;
26458c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p;
26468c2ecf20Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
26478c2ecf20Sopenharmony_ci	struct br_ip saddr;
26488c2ecf20Sopenharmony_ci	unsigned long max_delay;
26498c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
26508c2ecf20Sopenharmony_ci	__be32 group;
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
26538c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) ||
26548c2ecf20Sopenharmony_ci	    (port && port->state == BR_STATE_DISABLED))
26558c2ecf20Sopenharmony_ci		goto out;
26568c2ecf20Sopenharmony_ci
26578c2ecf20Sopenharmony_ci	group = ih->group;
26588c2ecf20Sopenharmony_ci
26598c2ecf20Sopenharmony_ci	if (transport_len == sizeof(*ih)) {
26608c2ecf20Sopenharmony_ci		max_delay = ih->code * (HZ / IGMP_TIMER_SCALE);
26618c2ecf20Sopenharmony_ci
26628c2ecf20Sopenharmony_ci		if (!max_delay) {
26638c2ecf20Sopenharmony_ci			max_delay = 10 * HZ;
26648c2ecf20Sopenharmony_ci			group = 0;
26658c2ecf20Sopenharmony_ci		}
26668c2ecf20Sopenharmony_ci	} else if (transport_len >= sizeof(*ih3)) {
26678c2ecf20Sopenharmony_ci		ih3 = igmpv3_query_hdr(skb);
26688c2ecf20Sopenharmony_ci		if (ih3->nsrcs ||
26698c2ecf20Sopenharmony_ci		    (br->multicast_igmp_version == 3 && group && ih3->suppress))
26708c2ecf20Sopenharmony_ci			goto out;
26718c2ecf20Sopenharmony_ci
26728c2ecf20Sopenharmony_ci		max_delay = ih3->code ?
26738c2ecf20Sopenharmony_ci			    IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
26748c2ecf20Sopenharmony_ci	} else {
26758c2ecf20Sopenharmony_ci		goto out;
26768c2ecf20Sopenharmony_ci	}
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_ci	if (!group) {
26798c2ecf20Sopenharmony_ci		saddr.proto = htons(ETH_P_IP);
26808c2ecf20Sopenharmony_ci		saddr.src.ip4 = iph->saddr;
26818c2ecf20Sopenharmony_ci
26828c2ecf20Sopenharmony_ci		br_multicast_query_received(br, port, &br->ip4_other_query,
26838c2ecf20Sopenharmony_ci					    &saddr, max_delay);
26848c2ecf20Sopenharmony_ci		goto out;
26858c2ecf20Sopenharmony_ci	}
26868c2ecf20Sopenharmony_ci
26878c2ecf20Sopenharmony_ci	mp = br_mdb_ip4_get(br, group, vid);
26888c2ecf20Sopenharmony_ci	if (!mp)
26898c2ecf20Sopenharmony_ci		goto out;
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ci	max_delay *= br->multicast_last_member_count;
26928c2ecf20Sopenharmony_ci
26938c2ecf20Sopenharmony_ci	if (mp->host_joined &&
26948c2ecf20Sopenharmony_ci	    (timer_pending(&mp->timer) ?
26958c2ecf20Sopenharmony_ci	     time_after(mp->timer.expires, now + max_delay) :
26968c2ecf20Sopenharmony_ci	     try_to_del_timer_sync(&mp->timer) >= 0))
26978c2ecf20Sopenharmony_ci		mod_timer(&mp->timer, now + max_delay);
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_ci	for (pp = &mp->ports;
27008c2ecf20Sopenharmony_ci	     (p = mlock_dereference(*pp, br)) != NULL;
27018c2ecf20Sopenharmony_ci	     pp = &p->next) {
27028c2ecf20Sopenharmony_ci		if (timer_pending(&p->timer) ?
27038c2ecf20Sopenharmony_ci		    time_after(p->timer.expires, now + max_delay) :
27048c2ecf20Sopenharmony_ci		    try_to_del_timer_sync(&p->timer) >= 0 &&
27058c2ecf20Sopenharmony_ci		    (br->multicast_igmp_version == 2 ||
27068c2ecf20Sopenharmony_ci		     p->filter_mode == MCAST_EXCLUDE))
27078c2ecf20Sopenharmony_ci			mod_timer(&p->timer, now + max_delay);
27088c2ecf20Sopenharmony_ci	}
27098c2ecf20Sopenharmony_ci
27108c2ecf20Sopenharmony_ciout:
27118c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
27128c2ecf20Sopenharmony_ci}
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
27158c2ecf20Sopenharmony_cistatic int br_ip6_multicast_query(struct net_bridge *br,
27168c2ecf20Sopenharmony_ci				  struct net_bridge_port *port,
27178c2ecf20Sopenharmony_ci				  struct sk_buff *skb,
27188c2ecf20Sopenharmony_ci				  u16 vid)
27198c2ecf20Sopenharmony_ci{
27208c2ecf20Sopenharmony_ci	unsigned int transport_len = ipv6_transport_len(skb);
27218c2ecf20Sopenharmony_ci	struct mld_msg *mld;
27228c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
27238c2ecf20Sopenharmony_ci	struct mld2_query *mld2q;
27248c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p;
27258c2ecf20Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
27268c2ecf20Sopenharmony_ci	struct br_ip saddr;
27278c2ecf20Sopenharmony_ci	unsigned long max_delay;
27288c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
27298c2ecf20Sopenharmony_ci	unsigned int offset = skb_transport_offset(skb);
27308c2ecf20Sopenharmony_ci	const struct in6_addr *group = NULL;
27318c2ecf20Sopenharmony_ci	bool is_general_query;
27328c2ecf20Sopenharmony_ci	int err = 0;
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
27358c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) ||
27368c2ecf20Sopenharmony_ci	    (port && port->state == BR_STATE_DISABLED))
27378c2ecf20Sopenharmony_ci		goto out;
27388c2ecf20Sopenharmony_ci
27398c2ecf20Sopenharmony_ci	if (transport_len == sizeof(*mld)) {
27408c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, offset + sizeof(*mld))) {
27418c2ecf20Sopenharmony_ci			err = -EINVAL;
27428c2ecf20Sopenharmony_ci			goto out;
27438c2ecf20Sopenharmony_ci		}
27448c2ecf20Sopenharmony_ci		mld = (struct mld_msg *) icmp6_hdr(skb);
27458c2ecf20Sopenharmony_ci		max_delay = msecs_to_jiffies(ntohs(mld->mld_maxdelay));
27468c2ecf20Sopenharmony_ci		if (max_delay)
27478c2ecf20Sopenharmony_ci			group = &mld->mld_mca;
27488c2ecf20Sopenharmony_ci	} else {
27498c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, offset + sizeof(*mld2q))) {
27508c2ecf20Sopenharmony_ci			err = -EINVAL;
27518c2ecf20Sopenharmony_ci			goto out;
27528c2ecf20Sopenharmony_ci		}
27538c2ecf20Sopenharmony_ci		mld2q = (struct mld2_query *)icmp6_hdr(skb);
27548c2ecf20Sopenharmony_ci		if (!mld2q->mld2q_nsrcs)
27558c2ecf20Sopenharmony_ci			group = &mld2q->mld2q_mca;
27568c2ecf20Sopenharmony_ci		if (br->multicast_mld_version == 2 &&
27578c2ecf20Sopenharmony_ci		    !ipv6_addr_any(&mld2q->mld2q_mca) &&
27588c2ecf20Sopenharmony_ci		    mld2q->mld2q_suppress)
27598c2ecf20Sopenharmony_ci			goto out;
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci		max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL);
27628c2ecf20Sopenharmony_ci	}
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_ci	is_general_query = group && ipv6_addr_any(group);
27658c2ecf20Sopenharmony_ci
27668c2ecf20Sopenharmony_ci	if (is_general_query) {
27678c2ecf20Sopenharmony_ci		saddr.proto = htons(ETH_P_IPV6);
27688c2ecf20Sopenharmony_ci		saddr.src.ip6 = ipv6_hdr(skb)->saddr;
27698c2ecf20Sopenharmony_ci
27708c2ecf20Sopenharmony_ci		br_multicast_query_received(br, port, &br->ip6_other_query,
27718c2ecf20Sopenharmony_ci					    &saddr, max_delay);
27728c2ecf20Sopenharmony_ci		goto out;
27738c2ecf20Sopenharmony_ci	} else if (!group) {
27748c2ecf20Sopenharmony_ci		goto out;
27758c2ecf20Sopenharmony_ci	}
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_ci	mp = br_mdb_ip6_get(br, group, vid);
27788c2ecf20Sopenharmony_ci	if (!mp)
27798c2ecf20Sopenharmony_ci		goto out;
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci	max_delay *= br->multicast_last_member_count;
27828c2ecf20Sopenharmony_ci	if (mp->host_joined &&
27838c2ecf20Sopenharmony_ci	    (timer_pending(&mp->timer) ?
27848c2ecf20Sopenharmony_ci	     time_after(mp->timer.expires, now + max_delay) :
27858c2ecf20Sopenharmony_ci	     try_to_del_timer_sync(&mp->timer) >= 0))
27868c2ecf20Sopenharmony_ci		mod_timer(&mp->timer, now + max_delay);
27878c2ecf20Sopenharmony_ci
27888c2ecf20Sopenharmony_ci	for (pp = &mp->ports;
27898c2ecf20Sopenharmony_ci	     (p = mlock_dereference(*pp, br)) != NULL;
27908c2ecf20Sopenharmony_ci	     pp = &p->next) {
27918c2ecf20Sopenharmony_ci		if (timer_pending(&p->timer) ?
27928c2ecf20Sopenharmony_ci		    time_after(p->timer.expires, now + max_delay) :
27938c2ecf20Sopenharmony_ci		    try_to_del_timer_sync(&p->timer) >= 0 &&
27948c2ecf20Sopenharmony_ci		    (br->multicast_mld_version == 1 ||
27958c2ecf20Sopenharmony_ci		     p->filter_mode == MCAST_EXCLUDE))
27968c2ecf20Sopenharmony_ci			mod_timer(&p->timer, now + max_delay);
27978c2ecf20Sopenharmony_ci	}
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ciout:
28008c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
28018c2ecf20Sopenharmony_ci	return err;
28028c2ecf20Sopenharmony_ci}
28038c2ecf20Sopenharmony_ci#endif
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_cistatic void
28068c2ecf20Sopenharmony_cibr_multicast_leave_group(struct net_bridge *br,
28078c2ecf20Sopenharmony_ci			 struct net_bridge_port *port,
28088c2ecf20Sopenharmony_ci			 struct br_ip *group,
28098c2ecf20Sopenharmony_ci			 struct bridge_mcast_other_query *other_query,
28108c2ecf20Sopenharmony_ci			 struct bridge_mcast_own_query *own_query,
28118c2ecf20Sopenharmony_ci			 const unsigned char *src)
28128c2ecf20Sopenharmony_ci{
28138c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
28148c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p;
28158c2ecf20Sopenharmony_ci	unsigned long now;
28168c2ecf20Sopenharmony_ci	unsigned long time;
28178c2ecf20Sopenharmony_ci
28188c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
28198c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) ||
28208c2ecf20Sopenharmony_ci	    (port && port->state == BR_STATE_DISABLED))
28218c2ecf20Sopenharmony_ci		goto out;
28228c2ecf20Sopenharmony_ci
28238c2ecf20Sopenharmony_ci	mp = br_mdb_ip_get(br, group);
28248c2ecf20Sopenharmony_ci	if (!mp)
28258c2ecf20Sopenharmony_ci		goto out;
28268c2ecf20Sopenharmony_ci
28278c2ecf20Sopenharmony_ci	if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) {
28288c2ecf20Sopenharmony_ci		struct net_bridge_port_group __rcu **pp;
28298c2ecf20Sopenharmony_ci
28308c2ecf20Sopenharmony_ci		for (pp = &mp->ports;
28318c2ecf20Sopenharmony_ci		     (p = mlock_dereference(*pp, br)) != NULL;
28328c2ecf20Sopenharmony_ci		     pp = &p->next) {
28338c2ecf20Sopenharmony_ci			if (!br_port_group_equal(p, port, src))
28348c2ecf20Sopenharmony_ci				continue;
28358c2ecf20Sopenharmony_ci
28368c2ecf20Sopenharmony_ci			if (p->flags & MDB_PG_FLAGS_PERMANENT)
28378c2ecf20Sopenharmony_ci				break;
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_ci			p->flags |= MDB_PG_FLAGS_FAST_LEAVE;
28408c2ecf20Sopenharmony_ci			br_multicast_del_pg(mp, p, pp);
28418c2ecf20Sopenharmony_ci		}
28428c2ecf20Sopenharmony_ci		goto out;
28438c2ecf20Sopenharmony_ci	}
28448c2ecf20Sopenharmony_ci
28458c2ecf20Sopenharmony_ci	if (timer_pending(&other_query->timer))
28468c2ecf20Sopenharmony_ci		goto out;
28478c2ecf20Sopenharmony_ci
28488c2ecf20Sopenharmony_ci	if (br_opt_get(br, BROPT_MULTICAST_QUERIER)) {
28498c2ecf20Sopenharmony_ci		__br_multicast_send_query(br, port, NULL, NULL, &mp->addr,
28508c2ecf20Sopenharmony_ci					  false, 0, NULL);
28518c2ecf20Sopenharmony_ci
28528c2ecf20Sopenharmony_ci		time = jiffies + br->multicast_last_member_count *
28538c2ecf20Sopenharmony_ci				 br->multicast_last_member_interval;
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci		mod_timer(&own_query->timer, time);
28568c2ecf20Sopenharmony_ci
28578c2ecf20Sopenharmony_ci		for (p = mlock_dereference(mp->ports, br);
28588c2ecf20Sopenharmony_ci		     p != NULL;
28598c2ecf20Sopenharmony_ci		     p = mlock_dereference(p->next, br)) {
28608c2ecf20Sopenharmony_ci			if (!br_port_group_equal(p, port, src))
28618c2ecf20Sopenharmony_ci				continue;
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_ci			if (!hlist_unhashed(&p->mglist) &&
28648c2ecf20Sopenharmony_ci			    (timer_pending(&p->timer) ?
28658c2ecf20Sopenharmony_ci			     time_after(p->timer.expires, time) :
28668c2ecf20Sopenharmony_ci			     try_to_del_timer_sync(&p->timer) >= 0)) {
28678c2ecf20Sopenharmony_ci				mod_timer(&p->timer, time);
28688c2ecf20Sopenharmony_ci			}
28698c2ecf20Sopenharmony_ci
28708c2ecf20Sopenharmony_ci			break;
28718c2ecf20Sopenharmony_ci		}
28728c2ecf20Sopenharmony_ci	}
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_ci	now = jiffies;
28758c2ecf20Sopenharmony_ci	time = now + br->multicast_last_member_count *
28768c2ecf20Sopenharmony_ci		     br->multicast_last_member_interval;
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci	if (!port) {
28798c2ecf20Sopenharmony_ci		if (mp->host_joined &&
28808c2ecf20Sopenharmony_ci		    (timer_pending(&mp->timer) ?
28818c2ecf20Sopenharmony_ci		     time_after(mp->timer.expires, time) :
28828c2ecf20Sopenharmony_ci		     try_to_del_timer_sync(&mp->timer) >= 0)) {
28838c2ecf20Sopenharmony_ci			mod_timer(&mp->timer, time);
28848c2ecf20Sopenharmony_ci		}
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci		goto out;
28878c2ecf20Sopenharmony_ci	}
28888c2ecf20Sopenharmony_ci
28898c2ecf20Sopenharmony_ci	for (p = mlock_dereference(mp->ports, br);
28908c2ecf20Sopenharmony_ci	     p != NULL;
28918c2ecf20Sopenharmony_ci	     p = mlock_dereference(p->next, br)) {
28928c2ecf20Sopenharmony_ci		if (p->key.port != port)
28938c2ecf20Sopenharmony_ci			continue;
28948c2ecf20Sopenharmony_ci
28958c2ecf20Sopenharmony_ci		if (!hlist_unhashed(&p->mglist) &&
28968c2ecf20Sopenharmony_ci		    (timer_pending(&p->timer) ?
28978c2ecf20Sopenharmony_ci		     time_after(p->timer.expires, time) :
28988c2ecf20Sopenharmony_ci		     try_to_del_timer_sync(&p->timer) >= 0)) {
28998c2ecf20Sopenharmony_ci			mod_timer(&p->timer, time);
29008c2ecf20Sopenharmony_ci		}
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_ci		break;
29038c2ecf20Sopenharmony_ci	}
29048c2ecf20Sopenharmony_ciout:
29058c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
29068c2ecf20Sopenharmony_ci}
29078c2ecf20Sopenharmony_ci
29088c2ecf20Sopenharmony_cistatic void br_ip4_multicast_leave_group(struct net_bridge *br,
29098c2ecf20Sopenharmony_ci					 struct net_bridge_port *port,
29108c2ecf20Sopenharmony_ci					 __be32 group,
29118c2ecf20Sopenharmony_ci					 __u16 vid,
29128c2ecf20Sopenharmony_ci					 const unsigned char *src)
29138c2ecf20Sopenharmony_ci{
29148c2ecf20Sopenharmony_ci	struct br_ip br_group;
29158c2ecf20Sopenharmony_ci	struct bridge_mcast_own_query *own_query;
29168c2ecf20Sopenharmony_ci
29178c2ecf20Sopenharmony_ci	if (ipv4_is_local_multicast(group))
29188c2ecf20Sopenharmony_ci		return;
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ci	own_query = port ? &port->ip4_own_query : &br->ip4_own_query;
29218c2ecf20Sopenharmony_ci
29228c2ecf20Sopenharmony_ci	memset(&br_group, 0, sizeof(br_group));
29238c2ecf20Sopenharmony_ci	br_group.dst.ip4 = group;
29248c2ecf20Sopenharmony_ci	br_group.proto = htons(ETH_P_IP);
29258c2ecf20Sopenharmony_ci	br_group.vid = vid;
29268c2ecf20Sopenharmony_ci
29278c2ecf20Sopenharmony_ci	br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query,
29288c2ecf20Sopenharmony_ci				 own_query, src);
29298c2ecf20Sopenharmony_ci}
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
29328c2ecf20Sopenharmony_cistatic void br_ip6_multicast_leave_group(struct net_bridge *br,
29338c2ecf20Sopenharmony_ci					 struct net_bridge_port *port,
29348c2ecf20Sopenharmony_ci					 const struct in6_addr *group,
29358c2ecf20Sopenharmony_ci					 __u16 vid,
29368c2ecf20Sopenharmony_ci					 const unsigned char *src)
29378c2ecf20Sopenharmony_ci{
29388c2ecf20Sopenharmony_ci	struct br_ip br_group;
29398c2ecf20Sopenharmony_ci	struct bridge_mcast_own_query *own_query;
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci	if (ipv6_addr_is_ll_all_nodes(group))
29428c2ecf20Sopenharmony_ci		return;
29438c2ecf20Sopenharmony_ci
29448c2ecf20Sopenharmony_ci	own_query = port ? &port->ip6_own_query : &br->ip6_own_query;
29458c2ecf20Sopenharmony_ci
29468c2ecf20Sopenharmony_ci	memset(&br_group, 0, sizeof(br_group));
29478c2ecf20Sopenharmony_ci	br_group.dst.ip6 = *group;
29488c2ecf20Sopenharmony_ci	br_group.proto = htons(ETH_P_IPV6);
29498c2ecf20Sopenharmony_ci	br_group.vid = vid;
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci	br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query,
29528c2ecf20Sopenharmony_ci				 own_query, src);
29538c2ecf20Sopenharmony_ci}
29548c2ecf20Sopenharmony_ci#endif
29558c2ecf20Sopenharmony_ci
29568c2ecf20Sopenharmony_cistatic void br_multicast_err_count(const struct net_bridge *br,
29578c2ecf20Sopenharmony_ci				   const struct net_bridge_port *p,
29588c2ecf20Sopenharmony_ci				   __be16 proto)
29598c2ecf20Sopenharmony_ci{
29608c2ecf20Sopenharmony_ci	struct bridge_mcast_stats __percpu *stats;
29618c2ecf20Sopenharmony_ci	struct bridge_mcast_stats *pstats;
29628c2ecf20Sopenharmony_ci
29638c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED))
29648c2ecf20Sopenharmony_ci		return;
29658c2ecf20Sopenharmony_ci
29668c2ecf20Sopenharmony_ci	if (p)
29678c2ecf20Sopenharmony_ci		stats = p->mcast_stats;
29688c2ecf20Sopenharmony_ci	else
29698c2ecf20Sopenharmony_ci		stats = br->mcast_stats;
29708c2ecf20Sopenharmony_ci	if (WARN_ON(!stats))
29718c2ecf20Sopenharmony_ci		return;
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci	pstats = this_cpu_ptr(stats);
29748c2ecf20Sopenharmony_ci
29758c2ecf20Sopenharmony_ci	u64_stats_update_begin(&pstats->syncp);
29768c2ecf20Sopenharmony_ci	switch (proto) {
29778c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
29788c2ecf20Sopenharmony_ci		pstats->mstats.igmp_parse_errors++;
29798c2ecf20Sopenharmony_ci		break;
29808c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
29818c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
29828c2ecf20Sopenharmony_ci		pstats->mstats.mld_parse_errors++;
29838c2ecf20Sopenharmony_ci		break;
29848c2ecf20Sopenharmony_ci#endif
29858c2ecf20Sopenharmony_ci	}
29868c2ecf20Sopenharmony_ci	u64_stats_update_end(&pstats->syncp);
29878c2ecf20Sopenharmony_ci}
29888c2ecf20Sopenharmony_ci
29898c2ecf20Sopenharmony_cistatic void br_multicast_pim(struct net_bridge *br,
29908c2ecf20Sopenharmony_ci			     struct net_bridge_port *port,
29918c2ecf20Sopenharmony_ci			     const struct sk_buff *skb)
29928c2ecf20Sopenharmony_ci{
29938c2ecf20Sopenharmony_ci	unsigned int offset = skb_transport_offset(skb);
29948c2ecf20Sopenharmony_ci	struct pimhdr *pimhdr, _pimhdr;
29958c2ecf20Sopenharmony_ci
29968c2ecf20Sopenharmony_ci	pimhdr = skb_header_pointer(skb, offset, sizeof(_pimhdr), &_pimhdr);
29978c2ecf20Sopenharmony_ci	if (!pimhdr || pim_hdr_version(pimhdr) != PIM_VERSION ||
29988c2ecf20Sopenharmony_ci	    pim_hdr_type(pimhdr) != PIM_TYPE_HELLO)
29998c2ecf20Sopenharmony_ci		return;
30008c2ecf20Sopenharmony_ci
30018c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
30028c2ecf20Sopenharmony_ci	br_multicast_mark_router(br, port);
30038c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
30048c2ecf20Sopenharmony_ci}
30058c2ecf20Sopenharmony_ci
30068c2ecf20Sopenharmony_cistatic int br_ip4_multicast_mrd_rcv(struct net_bridge *br,
30078c2ecf20Sopenharmony_ci				    struct net_bridge_port *port,
30088c2ecf20Sopenharmony_ci				    struct sk_buff *skb)
30098c2ecf20Sopenharmony_ci{
30108c2ecf20Sopenharmony_ci	if (ip_hdr(skb)->protocol != IPPROTO_IGMP ||
30118c2ecf20Sopenharmony_ci	    igmp_hdr(skb)->type != IGMP_MRDISC_ADV)
30128c2ecf20Sopenharmony_ci		return -ENOMSG;
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
30158c2ecf20Sopenharmony_ci	br_multicast_mark_router(br, port);
30168c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
30178c2ecf20Sopenharmony_ci
30188c2ecf20Sopenharmony_ci	return 0;
30198c2ecf20Sopenharmony_ci}
30208c2ecf20Sopenharmony_ci
30218c2ecf20Sopenharmony_cistatic int br_multicast_ipv4_rcv(struct net_bridge *br,
30228c2ecf20Sopenharmony_ci				 struct net_bridge_port *port,
30238c2ecf20Sopenharmony_ci				 struct sk_buff *skb,
30248c2ecf20Sopenharmony_ci				 u16 vid)
30258c2ecf20Sopenharmony_ci{
30268c2ecf20Sopenharmony_ci	const unsigned char *src;
30278c2ecf20Sopenharmony_ci	struct igmphdr *ih;
30288c2ecf20Sopenharmony_ci	int err;
30298c2ecf20Sopenharmony_ci
30308c2ecf20Sopenharmony_ci	err = ip_mc_check_igmp(skb);
30318c2ecf20Sopenharmony_ci
30328c2ecf20Sopenharmony_ci	if (err == -ENOMSG) {
30338c2ecf20Sopenharmony_ci		if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr)) {
30348c2ecf20Sopenharmony_ci			BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
30358c2ecf20Sopenharmony_ci		} else if (pim_ipv4_all_pim_routers(ip_hdr(skb)->daddr)) {
30368c2ecf20Sopenharmony_ci			if (ip_hdr(skb)->protocol == IPPROTO_PIM)
30378c2ecf20Sopenharmony_ci				br_multicast_pim(br, port, skb);
30388c2ecf20Sopenharmony_ci		} else if (ipv4_is_all_snoopers(ip_hdr(skb)->daddr)) {
30398c2ecf20Sopenharmony_ci			br_ip4_multicast_mrd_rcv(br, port, skb);
30408c2ecf20Sopenharmony_ci		}
30418c2ecf20Sopenharmony_ci
30428c2ecf20Sopenharmony_ci		return 0;
30438c2ecf20Sopenharmony_ci	} else if (err < 0) {
30448c2ecf20Sopenharmony_ci		br_multicast_err_count(br, port, skb->protocol);
30458c2ecf20Sopenharmony_ci		return err;
30468c2ecf20Sopenharmony_ci	}
30478c2ecf20Sopenharmony_ci
30488c2ecf20Sopenharmony_ci	ih = igmp_hdr(skb);
30498c2ecf20Sopenharmony_ci	src = eth_hdr(skb)->h_source;
30508c2ecf20Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->igmp = ih->type;
30518c2ecf20Sopenharmony_ci
30528c2ecf20Sopenharmony_ci	switch (ih->type) {
30538c2ecf20Sopenharmony_ci	case IGMP_HOST_MEMBERSHIP_REPORT:
30548c2ecf20Sopenharmony_ci	case IGMPV2_HOST_MEMBERSHIP_REPORT:
30558c2ecf20Sopenharmony_ci		BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
30568c2ecf20Sopenharmony_ci		err = br_ip4_multicast_add_group(br, port, ih->group, vid, src,
30578c2ecf20Sopenharmony_ci						 true);
30588c2ecf20Sopenharmony_ci		break;
30598c2ecf20Sopenharmony_ci	case IGMPV3_HOST_MEMBERSHIP_REPORT:
30608c2ecf20Sopenharmony_ci		err = br_ip4_multicast_igmp3_report(br, port, skb, vid);
30618c2ecf20Sopenharmony_ci		break;
30628c2ecf20Sopenharmony_ci	case IGMP_HOST_MEMBERSHIP_QUERY:
30638c2ecf20Sopenharmony_ci		br_ip4_multicast_query(br, port, skb, vid);
30648c2ecf20Sopenharmony_ci		break;
30658c2ecf20Sopenharmony_ci	case IGMP_HOST_LEAVE_MESSAGE:
30668c2ecf20Sopenharmony_ci		br_ip4_multicast_leave_group(br, port, ih->group, vid, src);
30678c2ecf20Sopenharmony_ci		break;
30688c2ecf20Sopenharmony_ci	}
30698c2ecf20Sopenharmony_ci
30708c2ecf20Sopenharmony_ci	br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
30718c2ecf20Sopenharmony_ci			   BR_MCAST_DIR_RX);
30728c2ecf20Sopenharmony_ci
30738c2ecf20Sopenharmony_ci	return err;
30748c2ecf20Sopenharmony_ci}
30758c2ecf20Sopenharmony_ci
30768c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
30778c2ecf20Sopenharmony_cistatic void br_ip6_multicast_mrd_rcv(struct net_bridge *br,
30788c2ecf20Sopenharmony_ci				     struct net_bridge_port *port,
30798c2ecf20Sopenharmony_ci				     struct sk_buff *skb)
30808c2ecf20Sopenharmony_ci{
30818c2ecf20Sopenharmony_ci	if (icmp6_hdr(skb)->icmp6_type != ICMPV6_MRDISC_ADV)
30828c2ecf20Sopenharmony_ci		return;
30838c2ecf20Sopenharmony_ci
30848c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
30858c2ecf20Sopenharmony_ci	br_multicast_mark_router(br, port);
30868c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
30878c2ecf20Sopenharmony_ci}
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_cistatic int br_multicast_ipv6_rcv(struct net_bridge *br,
30908c2ecf20Sopenharmony_ci				 struct net_bridge_port *port,
30918c2ecf20Sopenharmony_ci				 struct sk_buff *skb,
30928c2ecf20Sopenharmony_ci				 u16 vid)
30938c2ecf20Sopenharmony_ci{
30948c2ecf20Sopenharmony_ci	const unsigned char *src;
30958c2ecf20Sopenharmony_ci	struct mld_msg *mld;
30968c2ecf20Sopenharmony_ci	int err;
30978c2ecf20Sopenharmony_ci
30988c2ecf20Sopenharmony_ci	err = ipv6_mc_check_mld(skb);
30998c2ecf20Sopenharmony_ci
31008c2ecf20Sopenharmony_ci	if (err == -ENOMSG || err == -ENODATA) {
31018c2ecf20Sopenharmony_ci		if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
31028c2ecf20Sopenharmony_ci			BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
31038c2ecf20Sopenharmony_ci		if (err == -ENODATA &&
31048c2ecf20Sopenharmony_ci		    ipv6_addr_is_all_snoopers(&ipv6_hdr(skb)->daddr))
31058c2ecf20Sopenharmony_ci			br_ip6_multicast_mrd_rcv(br, port, skb);
31068c2ecf20Sopenharmony_ci
31078c2ecf20Sopenharmony_ci		return 0;
31088c2ecf20Sopenharmony_ci	} else if (err < 0) {
31098c2ecf20Sopenharmony_ci		br_multicast_err_count(br, port, skb->protocol);
31108c2ecf20Sopenharmony_ci		return err;
31118c2ecf20Sopenharmony_ci	}
31128c2ecf20Sopenharmony_ci
31138c2ecf20Sopenharmony_ci	mld = (struct mld_msg *)skb_transport_header(skb);
31148c2ecf20Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->igmp = mld->mld_type;
31158c2ecf20Sopenharmony_ci
31168c2ecf20Sopenharmony_ci	switch (mld->mld_type) {
31178c2ecf20Sopenharmony_ci	case ICMPV6_MGM_REPORT:
31188c2ecf20Sopenharmony_ci		src = eth_hdr(skb)->h_source;
31198c2ecf20Sopenharmony_ci		BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
31208c2ecf20Sopenharmony_ci		err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid,
31218c2ecf20Sopenharmony_ci						 src, true);
31228c2ecf20Sopenharmony_ci		break;
31238c2ecf20Sopenharmony_ci	case ICMPV6_MLD2_REPORT:
31248c2ecf20Sopenharmony_ci		err = br_ip6_multicast_mld2_report(br, port, skb, vid);
31258c2ecf20Sopenharmony_ci		break;
31268c2ecf20Sopenharmony_ci	case ICMPV6_MGM_QUERY:
31278c2ecf20Sopenharmony_ci		err = br_ip6_multicast_query(br, port, skb, vid);
31288c2ecf20Sopenharmony_ci		break;
31298c2ecf20Sopenharmony_ci	case ICMPV6_MGM_REDUCTION:
31308c2ecf20Sopenharmony_ci		src = eth_hdr(skb)->h_source;
31318c2ecf20Sopenharmony_ci		br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid, src);
31328c2ecf20Sopenharmony_ci		break;
31338c2ecf20Sopenharmony_ci	}
31348c2ecf20Sopenharmony_ci
31358c2ecf20Sopenharmony_ci	br_multicast_count(br, port, skb, BR_INPUT_SKB_CB(skb)->igmp,
31368c2ecf20Sopenharmony_ci			   BR_MCAST_DIR_RX);
31378c2ecf20Sopenharmony_ci
31388c2ecf20Sopenharmony_ci	return err;
31398c2ecf20Sopenharmony_ci}
31408c2ecf20Sopenharmony_ci#endif
31418c2ecf20Sopenharmony_ci
31428c2ecf20Sopenharmony_ciint br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
31438c2ecf20Sopenharmony_ci		     struct sk_buff *skb, u16 vid)
31448c2ecf20Sopenharmony_ci{
31458c2ecf20Sopenharmony_ci	int ret = 0;
31468c2ecf20Sopenharmony_ci
31478c2ecf20Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->igmp = 0;
31488c2ecf20Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->mrouters_only = 0;
31498c2ecf20Sopenharmony_ci
31508c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
31518c2ecf20Sopenharmony_ci		return 0;
31528c2ecf20Sopenharmony_ci
31538c2ecf20Sopenharmony_ci	switch (skb->protocol) {
31548c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
31558c2ecf20Sopenharmony_ci		ret = br_multicast_ipv4_rcv(br, port, skb, vid);
31568c2ecf20Sopenharmony_ci		break;
31578c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
31588c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
31598c2ecf20Sopenharmony_ci		ret = br_multicast_ipv6_rcv(br, port, skb, vid);
31608c2ecf20Sopenharmony_ci		break;
31618c2ecf20Sopenharmony_ci#endif
31628c2ecf20Sopenharmony_ci	}
31638c2ecf20Sopenharmony_ci
31648c2ecf20Sopenharmony_ci	return ret;
31658c2ecf20Sopenharmony_ci}
31668c2ecf20Sopenharmony_ci
31678c2ecf20Sopenharmony_cistatic void br_multicast_query_expired(struct net_bridge *br,
31688c2ecf20Sopenharmony_ci				       struct bridge_mcast_own_query *query,
31698c2ecf20Sopenharmony_ci				       struct bridge_mcast_querier *querier)
31708c2ecf20Sopenharmony_ci{
31718c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
31728c2ecf20Sopenharmony_ci	if (query->startup_sent < br->multicast_startup_query_count)
31738c2ecf20Sopenharmony_ci		query->startup_sent++;
31748c2ecf20Sopenharmony_ci
31758c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(querier->port, NULL);
31768c2ecf20Sopenharmony_ci	br_multicast_send_query(br, NULL, query);
31778c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
31788c2ecf20Sopenharmony_ci}
31798c2ecf20Sopenharmony_ci
31808c2ecf20Sopenharmony_cistatic void br_ip4_multicast_query_expired(struct timer_list *t)
31818c2ecf20Sopenharmony_ci{
31828c2ecf20Sopenharmony_ci	struct net_bridge *br = from_timer(br, t, ip4_own_query.timer);
31838c2ecf20Sopenharmony_ci
31848c2ecf20Sopenharmony_ci	br_multicast_query_expired(br, &br->ip4_own_query, &br->ip4_querier);
31858c2ecf20Sopenharmony_ci}
31868c2ecf20Sopenharmony_ci
31878c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
31888c2ecf20Sopenharmony_cistatic void br_ip6_multicast_query_expired(struct timer_list *t)
31898c2ecf20Sopenharmony_ci{
31908c2ecf20Sopenharmony_ci	struct net_bridge *br = from_timer(br, t, ip6_own_query.timer);
31918c2ecf20Sopenharmony_ci
31928c2ecf20Sopenharmony_ci	br_multicast_query_expired(br, &br->ip6_own_query, &br->ip6_querier);
31938c2ecf20Sopenharmony_ci}
31948c2ecf20Sopenharmony_ci#endif
31958c2ecf20Sopenharmony_ci
31968c2ecf20Sopenharmony_cistatic void br_multicast_gc_work(struct work_struct *work)
31978c2ecf20Sopenharmony_ci{
31988c2ecf20Sopenharmony_ci	struct net_bridge *br = container_of(work, struct net_bridge,
31998c2ecf20Sopenharmony_ci					     mcast_gc_work);
32008c2ecf20Sopenharmony_ci	HLIST_HEAD(deleted_head);
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
32038c2ecf20Sopenharmony_ci	hlist_move_list(&br->mcast_gc_list, &deleted_head);
32048c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
32058c2ecf20Sopenharmony_ci
32068c2ecf20Sopenharmony_ci	br_multicast_gc(&deleted_head);
32078c2ecf20Sopenharmony_ci}
32088c2ecf20Sopenharmony_ci
32098c2ecf20Sopenharmony_civoid br_multicast_init(struct net_bridge *br)
32108c2ecf20Sopenharmony_ci{
32118c2ecf20Sopenharmony_ci	br->hash_max = BR_MULTICAST_DEFAULT_HASH_MAX;
32128c2ecf20Sopenharmony_ci
32138c2ecf20Sopenharmony_ci	br->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
32148c2ecf20Sopenharmony_ci	br->multicast_last_member_count = 2;
32158c2ecf20Sopenharmony_ci	br->multicast_startup_query_count = 2;
32168c2ecf20Sopenharmony_ci
32178c2ecf20Sopenharmony_ci	br->multicast_last_member_interval = HZ;
32188c2ecf20Sopenharmony_ci	br->multicast_query_response_interval = 10 * HZ;
32198c2ecf20Sopenharmony_ci	br->multicast_startup_query_interval = 125 * HZ / 4;
32208c2ecf20Sopenharmony_ci	br->multicast_query_interval = 125 * HZ;
32218c2ecf20Sopenharmony_ci	br->multicast_querier_interval = 255 * HZ;
32228c2ecf20Sopenharmony_ci	br->multicast_membership_interval = 260 * HZ;
32238c2ecf20Sopenharmony_ci
32248c2ecf20Sopenharmony_ci	br->ip4_other_query.delay_time = 0;
32258c2ecf20Sopenharmony_ci	br->ip4_querier.port = NULL;
32268c2ecf20Sopenharmony_ci	br->multicast_igmp_version = 2;
32278c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
32288c2ecf20Sopenharmony_ci	br->multicast_mld_version = 1;
32298c2ecf20Sopenharmony_ci	br->ip6_other_query.delay_time = 0;
32308c2ecf20Sopenharmony_ci	br->ip6_querier.port = NULL;
32318c2ecf20Sopenharmony_ci#endif
32328c2ecf20Sopenharmony_ci	br_opt_toggle(br, BROPT_MULTICAST_ENABLED, true);
32338c2ecf20Sopenharmony_ci	br_opt_toggle(br, BROPT_HAS_IPV6_ADDR, true);
32348c2ecf20Sopenharmony_ci
32358c2ecf20Sopenharmony_ci	spin_lock_init(&br->multicast_lock);
32368c2ecf20Sopenharmony_ci	timer_setup(&br->multicast_router_timer,
32378c2ecf20Sopenharmony_ci		    br_multicast_local_router_expired, 0);
32388c2ecf20Sopenharmony_ci	timer_setup(&br->ip4_other_query.timer,
32398c2ecf20Sopenharmony_ci		    br_ip4_multicast_querier_expired, 0);
32408c2ecf20Sopenharmony_ci	timer_setup(&br->ip4_own_query.timer,
32418c2ecf20Sopenharmony_ci		    br_ip4_multicast_query_expired, 0);
32428c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
32438c2ecf20Sopenharmony_ci	timer_setup(&br->ip6_other_query.timer,
32448c2ecf20Sopenharmony_ci		    br_ip6_multicast_querier_expired, 0);
32458c2ecf20Sopenharmony_ci	timer_setup(&br->ip6_own_query.timer,
32468c2ecf20Sopenharmony_ci		    br_ip6_multicast_query_expired, 0);
32478c2ecf20Sopenharmony_ci#endif
32488c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&br->mdb_list);
32498c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&br->mcast_gc_list);
32508c2ecf20Sopenharmony_ci	INIT_WORK(&br->mcast_gc_work, br_multicast_gc_work);
32518c2ecf20Sopenharmony_ci}
32528c2ecf20Sopenharmony_ci
32538c2ecf20Sopenharmony_cistatic void br_ip4_multicast_join_snoopers(struct net_bridge *br)
32548c2ecf20Sopenharmony_ci{
32558c2ecf20Sopenharmony_ci	struct in_device *in_dev = in_dev_get(br->dev);
32568c2ecf20Sopenharmony_ci
32578c2ecf20Sopenharmony_ci	if (!in_dev)
32588c2ecf20Sopenharmony_ci		return;
32598c2ecf20Sopenharmony_ci
32608c2ecf20Sopenharmony_ci	__ip_mc_inc_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
32618c2ecf20Sopenharmony_ci	in_dev_put(in_dev);
32628c2ecf20Sopenharmony_ci}
32638c2ecf20Sopenharmony_ci
32648c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
32658c2ecf20Sopenharmony_cistatic void br_ip6_multicast_join_snoopers(struct net_bridge *br)
32668c2ecf20Sopenharmony_ci{
32678c2ecf20Sopenharmony_ci	struct in6_addr addr;
32688c2ecf20Sopenharmony_ci
32698c2ecf20Sopenharmony_ci	ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a));
32708c2ecf20Sopenharmony_ci	ipv6_dev_mc_inc(br->dev, &addr);
32718c2ecf20Sopenharmony_ci}
32728c2ecf20Sopenharmony_ci#else
32738c2ecf20Sopenharmony_cistatic inline void br_ip6_multicast_join_snoopers(struct net_bridge *br)
32748c2ecf20Sopenharmony_ci{
32758c2ecf20Sopenharmony_ci}
32768c2ecf20Sopenharmony_ci#endif
32778c2ecf20Sopenharmony_ci
32788c2ecf20Sopenharmony_civoid br_multicast_join_snoopers(struct net_bridge *br)
32798c2ecf20Sopenharmony_ci{
32808c2ecf20Sopenharmony_ci	br_ip4_multicast_join_snoopers(br);
32818c2ecf20Sopenharmony_ci	br_ip6_multicast_join_snoopers(br);
32828c2ecf20Sopenharmony_ci}
32838c2ecf20Sopenharmony_ci
32848c2ecf20Sopenharmony_cistatic void br_ip4_multicast_leave_snoopers(struct net_bridge *br)
32858c2ecf20Sopenharmony_ci{
32868c2ecf20Sopenharmony_ci	struct in_device *in_dev = in_dev_get(br->dev);
32878c2ecf20Sopenharmony_ci
32888c2ecf20Sopenharmony_ci	if (WARN_ON(!in_dev))
32898c2ecf20Sopenharmony_ci		return;
32908c2ecf20Sopenharmony_ci
32918c2ecf20Sopenharmony_ci	__ip_mc_dec_group(in_dev, htonl(INADDR_ALLSNOOPERS_GROUP), GFP_ATOMIC);
32928c2ecf20Sopenharmony_ci	in_dev_put(in_dev);
32938c2ecf20Sopenharmony_ci}
32948c2ecf20Sopenharmony_ci
32958c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
32968c2ecf20Sopenharmony_cistatic void br_ip6_multicast_leave_snoopers(struct net_bridge *br)
32978c2ecf20Sopenharmony_ci{
32988c2ecf20Sopenharmony_ci	struct in6_addr addr;
32998c2ecf20Sopenharmony_ci
33008c2ecf20Sopenharmony_ci	ipv6_addr_set(&addr, htonl(0xff020000), 0, 0, htonl(0x6a));
33018c2ecf20Sopenharmony_ci	ipv6_dev_mc_dec(br->dev, &addr);
33028c2ecf20Sopenharmony_ci}
33038c2ecf20Sopenharmony_ci#else
33048c2ecf20Sopenharmony_cistatic inline void br_ip6_multicast_leave_snoopers(struct net_bridge *br)
33058c2ecf20Sopenharmony_ci{
33068c2ecf20Sopenharmony_ci}
33078c2ecf20Sopenharmony_ci#endif
33088c2ecf20Sopenharmony_ci
33098c2ecf20Sopenharmony_civoid br_multicast_leave_snoopers(struct net_bridge *br)
33108c2ecf20Sopenharmony_ci{
33118c2ecf20Sopenharmony_ci	br_ip4_multicast_leave_snoopers(br);
33128c2ecf20Sopenharmony_ci	br_ip6_multicast_leave_snoopers(br);
33138c2ecf20Sopenharmony_ci}
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_cistatic void __br_multicast_open(struct net_bridge *br,
33168c2ecf20Sopenharmony_ci				struct bridge_mcast_own_query *query)
33178c2ecf20Sopenharmony_ci{
33188c2ecf20Sopenharmony_ci	query->startup_sent = 0;
33198c2ecf20Sopenharmony_ci
33208c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
33218c2ecf20Sopenharmony_ci		return;
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_ci	mod_timer(&query->timer, jiffies);
33248c2ecf20Sopenharmony_ci}
33258c2ecf20Sopenharmony_ci
33268c2ecf20Sopenharmony_civoid br_multicast_open(struct net_bridge *br)
33278c2ecf20Sopenharmony_ci{
33288c2ecf20Sopenharmony_ci	__br_multicast_open(br, &br->ip4_own_query);
33298c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
33308c2ecf20Sopenharmony_ci	__br_multicast_open(br, &br->ip6_own_query);
33318c2ecf20Sopenharmony_ci#endif
33328c2ecf20Sopenharmony_ci}
33338c2ecf20Sopenharmony_ci
33348c2ecf20Sopenharmony_civoid br_multicast_stop(struct net_bridge *br)
33358c2ecf20Sopenharmony_ci{
33368c2ecf20Sopenharmony_ci	del_timer_sync(&br->multicast_router_timer);
33378c2ecf20Sopenharmony_ci	del_timer_sync(&br->ip4_other_query.timer);
33388c2ecf20Sopenharmony_ci	del_timer_sync(&br->ip4_own_query.timer);
33398c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
33408c2ecf20Sopenharmony_ci	del_timer_sync(&br->ip6_other_query.timer);
33418c2ecf20Sopenharmony_ci	del_timer_sync(&br->ip6_own_query.timer);
33428c2ecf20Sopenharmony_ci#endif
33438c2ecf20Sopenharmony_ci}
33448c2ecf20Sopenharmony_ci
33458c2ecf20Sopenharmony_civoid br_multicast_dev_del(struct net_bridge *br)
33468c2ecf20Sopenharmony_ci{
33478c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
33488c2ecf20Sopenharmony_ci	HLIST_HEAD(deleted_head);
33498c2ecf20Sopenharmony_ci	struct hlist_node *tmp;
33508c2ecf20Sopenharmony_ci
33518c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
33528c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(mp, tmp, &br->mdb_list, mdb_node)
33538c2ecf20Sopenharmony_ci		br_multicast_del_mdb_entry(mp);
33548c2ecf20Sopenharmony_ci	hlist_move_list(&br->mcast_gc_list, &deleted_head);
33558c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
33568c2ecf20Sopenharmony_ci
33578c2ecf20Sopenharmony_ci	br_multicast_gc(&deleted_head);
33588c2ecf20Sopenharmony_ci	cancel_work_sync(&br->mcast_gc_work);
33598c2ecf20Sopenharmony_ci
33608c2ecf20Sopenharmony_ci	rcu_barrier();
33618c2ecf20Sopenharmony_ci}
33628c2ecf20Sopenharmony_ci
33638c2ecf20Sopenharmony_ciint br_multicast_set_router(struct net_bridge *br, unsigned long val)
33648c2ecf20Sopenharmony_ci{
33658c2ecf20Sopenharmony_ci	int err = -EINVAL;
33668c2ecf20Sopenharmony_ci
33678c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
33688c2ecf20Sopenharmony_ci
33698c2ecf20Sopenharmony_ci	switch (val) {
33708c2ecf20Sopenharmony_ci	case MDB_RTR_TYPE_DISABLED:
33718c2ecf20Sopenharmony_ci	case MDB_RTR_TYPE_PERM:
33728c2ecf20Sopenharmony_ci		br_mc_router_state_change(br, val == MDB_RTR_TYPE_PERM);
33738c2ecf20Sopenharmony_ci		del_timer(&br->multicast_router_timer);
33748c2ecf20Sopenharmony_ci		br->multicast_router = val;
33758c2ecf20Sopenharmony_ci		err = 0;
33768c2ecf20Sopenharmony_ci		break;
33778c2ecf20Sopenharmony_ci	case MDB_RTR_TYPE_TEMP_QUERY:
33788c2ecf20Sopenharmony_ci		if (br->multicast_router != MDB_RTR_TYPE_TEMP_QUERY)
33798c2ecf20Sopenharmony_ci			br_mc_router_state_change(br, false);
33808c2ecf20Sopenharmony_ci		br->multicast_router = val;
33818c2ecf20Sopenharmony_ci		err = 0;
33828c2ecf20Sopenharmony_ci		break;
33838c2ecf20Sopenharmony_ci	}
33848c2ecf20Sopenharmony_ci
33858c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
33868c2ecf20Sopenharmony_ci
33878c2ecf20Sopenharmony_ci	return err;
33888c2ecf20Sopenharmony_ci}
33898c2ecf20Sopenharmony_ci
33908c2ecf20Sopenharmony_cistatic void __del_port_router(struct net_bridge_port *p)
33918c2ecf20Sopenharmony_ci{
33928c2ecf20Sopenharmony_ci	if (hlist_unhashed(&p->rlist))
33938c2ecf20Sopenharmony_ci		return;
33948c2ecf20Sopenharmony_ci	hlist_del_init_rcu(&p->rlist);
33958c2ecf20Sopenharmony_ci	br_rtr_notify(p->br->dev, p, RTM_DELMDB);
33968c2ecf20Sopenharmony_ci	br_port_mc_router_state_change(p, false);
33978c2ecf20Sopenharmony_ci
33988c2ecf20Sopenharmony_ci	/* don't allow timer refresh */
33998c2ecf20Sopenharmony_ci	if (p->multicast_router == MDB_RTR_TYPE_TEMP)
34008c2ecf20Sopenharmony_ci		p->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
34018c2ecf20Sopenharmony_ci}
34028c2ecf20Sopenharmony_ci
34038c2ecf20Sopenharmony_ciint br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
34048c2ecf20Sopenharmony_ci{
34058c2ecf20Sopenharmony_ci	struct net_bridge *br = p->br;
34068c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
34078c2ecf20Sopenharmony_ci	int err = -EINVAL;
34088c2ecf20Sopenharmony_ci
34098c2ecf20Sopenharmony_ci	spin_lock(&br->multicast_lock);
34108c2ecf20Sopenharmony_ci	if (p->multicast_router == val) {
34118c2ecf20Sopenharmony_ci		/* Refresh the temp router port timer */
34128c2ecf20Sopenharmony_ci		if (p->multicast_router == MDB_RTR_TYPE_TEMP)
34138c2ecf20Sopenharmony_ci			mod_timer(&p->multicast_router_timer,
34148c2ecf20Sopenharmony_ci				  now + br->multicast_querier_interval);
34158c2ecf20Sopenharmony_ci		err = 0;
34168c2ecf20Sopenharmony_ci		goto unlock;
34178c2ecf20Sopenharmony_ci	}
34188c2ecf20Sopenharmony_ci	switch (val) {
34198c2ecf20Sopenharmony_ci	case MDB_RTR_TYPE_DISABLED:
34208c2ecf20Sopenharmony_ci		p->multicast_router = MDB_RTR_TYPE_DISABLED;
34218c2ecf20Sopenharmony_ci		__del_port_router(p);
34228c2ecf20Sopenharmony_ci		del_timer(&p->multicast_router_timer);
34238c2ecf20Sopenharmony_ci		break;
34248c2ecf20Sopenharmony_ci	case MDB_RTR_TYPE_TEMP_QUERY:
34258c2ecf20Sopenharmony_ci		p->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
34268c2ecf20Sopenharmony_ci		__del_port_router(p);
34278c2ecf20Sopenharmony_ci		break;
34288c2ecf20Sopenharmony_ci	case MDB_RTR_TYPE_PERM:
34298c2ecf20Sopenharmony_ci		p->multicast_router = MDB_RTR_TYPE_PERM;
34308c2ecf20Sopenharmony_ci		del_timer(&p->multicast_router_timer);
34318c2ecf20Sopenharmony_ci		br_multicast_add_router(br, p);
34328c2ecf20Sopenharmony_ci		break;
34338c2ecf20Sopenharmony_ci	case MDB_RTR_TYPE_TEMP:
34348c2ecf20Sopenharmony_ci		p->multicast_router = MDB_RTR_TYPE_TEMP;
34358c2ecf20Sopenharmony_ci		br_multicast_mark_router(br, p);
34368c2ecf20Sopenharmony_ci		break;
34378c2ecf20Sopenharmony_ci	default:
34388c2ecf20Sopenharmony_ci		goto unlock;
34398c2ecf20Sopenharmony_ci	}
34408c2ecf20Sopenharmony_ci	err = 0;
34418c2ecf20Sopenharmony_ciunlock:
34428c2ecf20Sopenharmony_ci	spin_unlock(&br->multicast_lock);
34438c2ecf20Sopenharmony_ci
34448c2ecf20Sopenharmony_ci	return err;
34458c2ecf20Sopenharmony_ci}
34468c2ecf20Sopenharmony_ci
34478c2ecf20Sopenharmony_cistatic void br_multicast_start_querier(struct net_bridge *br,
34488c2ecf20Sopenharmony_ci				       struct bridge_mcast_own_query *query)
34498c2ecf20Sopenharmony_ci{
34508c2ecf20Sopenharmony_ci	struct net_bridge_port *port;
34518c2ecf20Sopenharmony_ci
34528c2ecf20Sopenharmony_ci	__br_multicast_open(br, query);
34538c2ecf20Sopenharmony_ci
34548c2ecf20Sopenharmony_ci	rcu_read_lock();
34558c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(port, &br->port_list, list) {
34568c2ecf20Sopenharmony_ci		if (port->state == BR_STATE_DISABLED ||
34578c2ecf20Sopenharmony_ci		    port->state == BR_STATE_BLOCKING)
34588c2ecf20Sopenharmony_ci			continue;
34598c2ecf20Sopenharmony_ci
34608c2ecf20Sopenharmony_ci		if (query == &br->ip4_own_query)
34618c2ecf20Sopenharmony_ci			br_multicast_enable(&port->ip4_own_query);
34628c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
34638c2ecf20Sopenharmony_ci		else
34648c2ecf20Sopenharmony_ci			br_multicast_enable(&port->ip6_own_query);
34658c2ecf20Sopenharmony_ci#endif
34668c2ecf20Sopenharmony_ci	}
34678c2ecf20Sopenharmony_ci	rcu_read_unlock();
34688c2ecf20Sopenharmony_ci}
34698c2ecf20Sopenharmony_ci
34708c2ecf20Sopenharmony_ciint br_multicast_toggle(struct net_bridge *br, unsigned long val)
34718c2ecf20Sopenharmony_ci{
34728c2ecf20Sopenharmony_ci	struct net_bridge_port *port;
34738c2ecf20Sopenharmony_ci	bool change_snoopers = false;
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
34768c2ecf20Sopenharmony_ci	if (!!br_opt_get(br, BROPT_MULTICAST_ENABLED) == !!val)
34778c2ecf20Sopenharmony_ci		goto unlock;
34788c2ecf20Sopenharmony_ci
34798c2ecf20Sopenharmony_ci	br_mc_disabled_update(br->dev, val);
34808c2ecf20Sopenharmony_ci	br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val);
34818c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) {
34828c2ecf20Sopenharmony_ci		change_snoopers = true;
34838c2ecf20Sopenharmony_ci		goto unlock;
34848c2ecf20Sopenharmony_ci	}
34858c2ecf20Sopenharmony_ci
34868c2ecf20Sopenharmony_ci	if (!netif_running(br->dev))
34878c2ecf20Sopenharmony_ci		goto unlock;
34888c2ecf20Sopenharmony_ci
34898c2ecf20Sopenharmony_ci	br_multicast_open(br);
34908c2ecf20Sopenharmony_ci	list_for_each_entry(port, &br->port_list, list)
34918c2ecf20Sopenharmony_ci		__br_multicast_enable_port(port);
34928c2ecf20Sopenharmony_ci
34938c2ecf20Sopenharmony_ci	change_snoopers = true;
34948c2ecf20Sopenharmony_ci
34958c2ecf20Sopenharmony_ciunlock:
34968c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
34978c2ecf20Sopenharmony_ci
34988c2ecf20Sopenharmony_ci	/* br_multicast_join_snoopers has the potential to cause
34998c2ecf20Sopenharmony_ci	 * an MLD Report/Leave to be delivered to br_multicast_rcv,
35008c2ecf20Sopenharmony_ci	 * which would in turn call br_multicast_add_group, which would
35018c2ecf20Sopenharmony_ci	 * attempt to acquire multicast_lock. This function should be
35028c2ecf20Sopenharmony_ci	 * called after the lock has been released to avoid deadlocks on
35038c2ecf20Sopenharmony_ci	 * multicast_lock.
35048c2ecf20Sopenharmony_ci	 *
35058c2ecf20Sopenharmony_ci	 * br_multicast_leave_snoopers does not have the problem since
35068c2ecf20Sopenharmony_ci	 * br_multicast_rcv first checks BROPT_MULTICAST_ENABLED, and
35078c2ecf20Sopenharmony_ci	 * returns without calling br_multicast_ipv4/6_rcv if it's not
35088c2ecf20Sopenharmony_ci	 * enabled. Moved both functions out just for symmetry.
35098c2ecf20Sopenharmony_ci	 */
35108c2ecf20Sopenharmony_ci	if (change_snoopers) {
35118c2ecf20Sopenharmony_ci		if (br_opt_get(br, BROPT_MULTICAST_ENABLED))
35128c2ecf20Sopenharmony_ci			br_multicast_join_snoopers(br);
35138c2ecf20Sopenharmony_ci		else
35148c2ecf20Sopenharmony_ci			br_multicast_leave_snoopers(br);
35158c2ecf20Sopenharmony_ci	}
35168c2ecf20Sopenharmony_ci
35178c2ecf20Sopenharmony_ci	return 0;
35188c2ecf20Sopenharmony_ci}
35198c2ecf20Sopenharmony_ci
35208c2ecf20Sopenharmony_cibool br_multicast_enabled(const struct net_device *dev)
35218c2ecf20Sopenharmony_ci{
35228c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
35238c2ecf20Sopenharmony_ci
35248c2ecf20Sopenharmony_ci	return !!br_opt_get(br, BROPT_MULTICAST_ENABLED);
35258c2ecf20Sopenharmony_ci}
35268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_enabled);
35278c2ecf20Sopenharmony_ci
35288c2ecf20Sopenharmony_cibool br_multicast_router(const struct net_device *dev)
35298c2ecf20Sopenharmony_ci{
35308c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
35318c2ecf20Sopenharmony_ci	bool is_router;
35328c2ecf20Sopenharmony_ci
35338c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
35348c2ecf20Sopenharmony_ci	is_router = br_multicast_is_router(br);
35358c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
35368c2ecf20Sopenharmony_ci	return is_router;
35378c2ecf20Sopenharmony_ci}
35388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_router);
35398c2ecf20Sopenharmony_ci
35408c2ecf20Sopenharmony_ciint br_multicast_set_querier(struct net_bridge *br, unsigned long val)
35418c2ecf20Sopenharmony_ci{
35428c2ecf20Sopenharmony_ci	unsigned long max_delay;
35438c2ecf20Sopenharmony_ci
35448c2ecf20Sopenharmony_ci	val = !!val;
35458c2ecf20Sopenharmony_ci
35468c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
35478c2ecf20Sopenharmony_ci	if (br_opt_get(br, BROPT_MULTICAST_QUERIER) == val)
35488c2ecf20Sopenharmony_ci		goto unlock;
35498c2ecf20Sopenharmony_ci
35508c2ecf20Sopenharmony_ci	br_opt_toggle(br, BROPT_MULTICAST_QUERIER, !!val);
35518c2ecf20Sopenharmony_ci	if (!val)
35528c2ecf20Sopenharmony_ci		goto unlock;
35538c2ecf20Sopenharmony_ci
35548c2ecf20Sopenharmony_ci	max_delay = br->multicast_query_response_interval;
35558c2ecf20Sopenharmony_ci
35568c2ecf20Sopenharmony_ci	if (!timer_pending(&br->ip4_other_query.timer))
35578c2ecf20Sopenharmony_ci		br->ip4_other_query.delay_time = jiffies + max_delay;
35588c2ecf20Sopenharmony_ci
35598c2ecf20Sopenharmony_ci	br_multicast_start_querier(br, &br->ip4_own_query);
35608c2ecf20Sopenharmony_ci
35618c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
35628c2ecf20Sopenharmony_ci	if (!timer_pending(&br->ip6_other_query.timer))
35638c2ecf20Sopenharmony_ci		br->ip6_other_query.delay_time = jiffies + max_delay;
35648c2ecf20Sopenharmony_ci
35658c2ecf20Sopenharmony_ci	br_multicast_start_querier(br, &br->ip6_own_query);
35668c2ecf20Sopenharmony_ci#endif
35678c2ecf20Sopenharmony_ci
35688c2ecf20Sopenharmony_ciunlock:
35698c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
35708c2ecf20Sopenharmony_ci
35718c2ecf20Sopenharmony_ci	return 0;
35728c2ecf20Sopenharmony_ci}
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_ciint br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val)
35758c2ecf20Sopenharmony_ci{
35768c2ecf20Sopenharmony_ci	/* Currently we support only version 2 and 3 */
35778c2ecf20Sopenharmony_ci	switch (val) {
35788c2ecf20Sopenharmony_ci	case 2:
35798c2ecf20Sopenharmony_ci	case 3:
35808c2ecf20Sopenharmony_ci		break;
35818c2ecf20Sopenharmony_ci	default:
35828c2ecf20Sopenharmony_ci		return -EINVAL;
35838c2ecf20Sopenharmony_ci	}
35848c2ecf20Sopenharmony_ci
35858c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
35868c2ecf20Sopenharmony_ci	br->multicast_igmp_version = val;
35878c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
35888c2ecf20Sopenharmony_ci
35898c2ecf20Sopenharmony_ci	return 0;
35908c2ecf20Sopenharmony_ci}
35918c2ecf20Sopenharmony_ci
35928c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
35938c2ecf20Sopenharmony_ciint br_multicast_set_mld_version(struct net_bridge *br, unsigned long val)
35948c2ecf20Sopenharmony_ci{
35958c2ecf20Sopenharmony_ci	/* Currently we support version 1 and 2 */
35968c2ecf20Sopenharmony_ci	switch (val) {
35978c2ecf20Sopenharmony_ci	case 1:
35988c2ecf20Sopenharmony_ci	case 2:
35998c2ecf20Sopenharmony_ci		break;
36008c2ecf20Sopenharmony_ci	default:
36018c2ecf20Sopenharmony_ci		return -EINVAL;
36028c2ecf20Sopenharmony_ci	}
36038c2ecf20Sopenharmony_ci
36048c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
36058c2ecf20Sopenharmony_ci	br->multicast_mld_version = val;
36068c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
36078c2ecf20Sopenharmony_ci
36088c2ecf20Sopenharmony_ci	return 0;
36098c2ecf20Sopenharmony_ci}
36108c2ecf20Sopenharmony_ci#endif
36118c2ecf20Sopenharmony_ci
36128c2ecf20Sopenharmony_ci/**
36138c2ecf20Sopenharmony_ci * br_multicast_list_adjacent - Returns snooped multicast addresses
36148c2ecf20Sopenharmony_ci * @dev:	The bridge port adjacent to which to retrieve addresses
36158c2ecf20Sopenharmony_ci * @br_ip_list:	The list to store found, snooped multicast IP addresses in
36168c2ecf20Sopenharmony_ci *
36178c2ecf20Sopenharmony_ci * Creates a list of IP addresses (struct br_ip_list) sensed by the multicast
36188c2ecf20Sopenharmony_ci * snooping feature on all bridge ports of dev's bridge device, excluding
36198c2ecf20Sopenharmony_ci * the addresses from dev itself.
36208c2ecf20Sopenharmony_ci *
36218c2ecf20Sopenharmony_ci * Returns the number of items added to br_ip_list.
36228c2ecf20Sopenharmony_ci *
36238c2ecf20Sopenharmony_ci * Notes:
36248c2ecf20Sopenharmony_ci * - br_ip_list needs to be initialized by caller
36258c2ecf20Sopenharmony_ci * - br_ip_list might contain duplicates in the end
36268c2ecf20Sopenharmony_ci *   (needs to be taken care of by caller)
36278c2ecf20Sopenharmony_ci * - br_ip_list needs to be freed by caller
36288c2ecf20Sopenharmony_ci */
36298c2ecf20Sopenharmony_ciint br_multicast_list_adjacent(struct net_device *dev,
36308c2ecf20Sopenharmony_ci			       struct list_head *br_ip_list)
36318c2ecf20Sopenharmony_ci{
36328c2ecf20Sopenharmony_ci	struct net_bridge *br;
36338c2ecf20Sopenharmony_ci	struct net_bridge_port *port;
36348c2ecf20Sopenharmony_ci	struct net_bridge_port_group *group;
36358c2ecf20Sopenharmony_ci	struct br_ip_list *entry;
36368c2ecf20Sopenharmony_ci	int count = 0;
36378c2ecf20Sopenharmony_ci
36388c2ecf20Sopenharmony_ci	rcu_read_lock();
36398c2ecf20Sopenharmony_ci	if (!br_ip_list || !netif_is_bridge_port(dev))
36408c2ecf20Sopenharmony_ci		goto unlock;
36418c2ecf20Sopenharmony_ci
36428c2ecf20Sopenharmony_ci	port = br_port_get_rcu(dev);
36438c2ecf20Sopenharmony_ci	if (!port || !port->br)
36448c2ecf20Sopenharmony_ci		goto unlock;
36458c2ecf20Sopenharmony_ci
36468c2ecf20Sopenharmony_ci	br = port->br;
36478c2ecf20Sopenharmony_ci
36488c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(port, &br->port_list, list) {
36498c2ecf20Sopenharmony_ci		if (!port->dev || port->dev == dev)
36508c2ecf20Sopenharmony_ci			continue;
36518c2ecf20Sopenharmony_ci
36528c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(group, &port->mglist, mglist) {
36538c2ecf20Sopenharmony_ci			entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
36548c2ecf20Sopenharmony_ci			if (!entry)
36558c2ecf20Sopenharmony_ci				goto unlock;
36568c2ecf20Sopenharmony_ci
36578c2ecf20Sopenharmony_ci			entry->addr = group->key.addr;
36588c2ecf20Sopenharmony_ci			list_add(&entry->list, br_ip_list);
36598c2ecf20Sopenharmony_ci			count++;
36608c2ecf20Sopenharmony_ci		}
36618c2ecf20Sopenharmony_ci	}
36628c2ecf20Sopenharmony_ci
36638c2ecf20Sopenharmony_ciunlock:
36648c2ecf20Sopenharmony_ci	rcu_read_unlock();
36658c2ecf20Sopenharmony_ci	return count;
36668c2ecf20Sopenharmony_ci}
36678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_list_adjacent);
36688c2ecf20Sopenharmony_ci
36698c2ecf20Sopenharmony_ci/**
36708c2ecf20Sopenharmony_ci * br_multicast_has_querier_anywhere - Checks for a querier on a bridge
36718c2ecf20Sopenharmony_ci * @dev: The bridge port providing the bridge on which to check for a querier
36728c2ecf20Sopenharmony_ci * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
36738c2ecf20Sopenharmony_ci *
36748c2ecf20Sopenharmony_ci * Checks whether the given interface has a bridge on top and if so returns
36758c2ecf20Sopenharmony_ci * true if a valid querier exists anywhere on the bridged link layer.
36768c2ecf20Sopenharmony_ci * Otherwise returns false.
36778c2ecf20Sopenharmony_ci */
36788c2ecf20Sopenharmony_cibool br_multicast_has_querier_anywhere(struct net_device *dev, int proto)
36798c2ecf20Sopenharmony_ci{
36808c2ecf20Sopenharmony_ci	struct net_bridge *br;
36818c2ecf20Sopenharmony_ci	struct net_bridge_port *port;
36828c2ecf20Sopenharmony_ci	struct ethhdr eth;
36838c2ecf20Sopenharmony_ci	bool ret = false;
36848c2ecf20Sopenharmony_ci
36858c2ecf20Sopenharmony_ci	rcu_read_lock();
36868c2ecf20Sopenharmony_ci	if (!netif_is_bridge_port(dev))
36878c2ecf20Sopenharmony_ci		goto unlock;
36888c2ecf20Sopenharmony_ci
36898c2ecf20Sopenharmony_ci	port = br_port_get_rcu(dev);
36908c2ecf20Sopenharmony_ci	if (!port || !port->br)
36918c2ecf20Sopenharmony_ci		goto unlock;
36928c2ecf20Sopenharmony_ci
36938c2ecf20Sopenharmony_ci	br = port->br;
36948c2ecf20Sopenharmony_ci
36958c2ecf20Sopenharmony_ci	memset(&eth, 0, sizeof(eth));
36968c2ecf20Sopenharmony_ci	eth.h_proto = htons(proto);
36978c2ecf20Sopenharmony_ci
36988c2ecf20Sopenharmony_ci	ret = br_multicast_querier_exists(br, &eth);
36998c2ecf20Sopenharmony_ci
37008c2ecf20Sopenharmony_ciunlock:
37018c2ecf20Sopenharmony_ci	rcu_read_unlock();
37028c2ecf20Sopenharmony_ci	return ret;
37038c2ecf20Sopenharmony_ci}
37048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_has_querier_anywhere);
37058c2ecf20Sopenharmony_ci
37068c2ecf20Sopenharmony_ci/**
37078c2ecf20Sopenharmony_ci * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port
37088c2ecf20Sopenharmony_ci * @dev: The bridge port adjacent to which to check for a querier
37098c2ecf20Sopenharmony_ci * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
37108c2ecf20Sopenharmony_ci *
37118c2ecf20Sopenharmony_ci * Checks whether the given interface has a bridge on top and if so returns
37128c2ecf20Sopenharmony_ci * true if a selected querier is behind one of the other ports of this
37138c2ecf20Sopenharmony_ci * bridge. Otherwise returns false.
37148c2ecf20Sopenharmony_ci */
37158c2ecf20Sopenharmony_cibool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
37168c2ecf20Sopenharmony_ci{
37178c2ecf20Sopenharmony_ci	struct net_bridge *br;
37188c2ecf20Sopenharmony_ci	struct net_bridge_port *port;
37198c2ecf20Sopenharmony_ci	bool ret = false;
37208c2ecf20Sopenharmony_ci
37218c2ecf20Sopenharmony_ci	rcu_read_lock();
37228c2ecf20Sopenharmony_ci	if (!netif_is_bridge_port(dev))
37238c2ecf20Sopenharmony_ci		goto unlock;
37248c2ecf20Sopenharmony_ci
37258c2ecf20Sopenharmony_ci	port = br_port_get_rcu(dev);
37268c2ecf20Sopenharmony_ci	if (!port || !port->br)
37278c2ecf20Sopenharmony_ci		goto unlock;
37288c2ecf20Sopenharmony_ci
37298c2ecf20Sopenharmony_ci	br = port->br;
37308c2ecf20Sopenharmony_ci
37318c2ecf20Sopenharmony_ci	switch (proto) {
37328c2ecf20Sopenharmony_ci	case ETH_P_IP:
37338c2ecf20Sopenharmony_ci		if (!timer_pending(&br->ip4_other_query.timer) ||
37348c2ecf20Sopenharmony_ci		    rcu_dereference(br->ip4_querier.port) == port)
37358c2ecf20Sopenharmony_ci			goto unlock;
37368c2ecf20Sopenharmony_ci		break;
37378c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
37388c2ecf20Sopenharmony_ci	case ETH_P_IPV6:
37398c2ecf20Sopenharmony_ci		if (!timer_pending(&br->ip6_other_query.timer) ||
37408c2ecf20Sopenharmony_ci		    rcu_dereference(br->ip6_querier.port) == port)
37418c2ecf20Sopenharmony_ci			goto unlock;
37428c2ecf20Sopenharmony_ci		break;
37438c2ecf20Sopenharmony_ci#endif
37448c2ecf20Sopenharmony_ci	default:
37458c2ecf20Sopenharmony_ci		goto unlock;
37468c2ecf20Sopenharmony_ci	}
37478c2ecf20Sopenharmony_ci
37488c2ecf20Sopenharmony_ci	ret = true;
37498c2ecf20Sopenharmony_ciunlock:
37508c2ecf20Sopenharmony_ci	rcu_read_unlock();
37518c2ecf20Sopenharmony_ci	return ret;
37528c2ecf20Sopenharmony_ci}
37538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent);
37548c2ecf20Sopenharmony_ci
37558c2ecf20Sopenharmony_cistatic void br_mcast_stats_add(struct bridge_mcast_stats __percpu *stats,
37568c2ecf20Sopenharmony_ci			       const struct sk_buff *skb, u8 type, u8 dir)
37578c2ecf20Sopenharmony_ci{
37588c2ecf20Sopenharmony_ci	struct bridge_mcast_stats *pstats = this_cpu_ptr(stats);
37598c2ecf20Sopenharmony_ci	__be16 proto = skb->protocol;
37608c2ecf20Sopenharmony_ci	unsigned int t_len;
37618c2ecf20Sopenharmony_ci
37628c2ecf20Sopenharmony_ci	u64_stats_update_begin(&pstats->syncp);
37638c2ecf20Sopenharmony_ci	switch (proto) {
37648c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
37658c2ecf20Sopenharmony_ci		t_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb);
37668c2ecf20Sopenharmony_ci		switch (type) {
37678c2ecf20Sopenharmony_ci		case IGMP_HOST_MEMBERSHIP_REPORT:
37688c2ecf20Sopenharmony_ci			pstats->mstats.igmp_v1reports[dir]++;
37698c2ecf20Sopenharmony_ci			break;
37708c2ecf20Sopenharmony_ci		case IGMPV2_HOST_MEMBERSHIP_REPORT:
37718c2ecf20Sopenharmony_ci			pstats->mstats.igmp_v2reports[dir]++;
37728c2ecf20Sopenharmony_ci			break;
37738c2ecf20Sopenharmony_ci		case IGMPV3_HOST_MEMBERSHIP_REPORT:
37748c2ecf20Sopenharmony_ci			pstats->mstats.igmp_v3reports[dir]++;
37758c2ecf20Sopenharmony_ci			break;
37768c2ecf20Sopenharmony_ci		case IGMP_HOST_MEMBERSHIP_QUERY:
37778c2ecf20Sopenharmony_ci			if (t_len != sizeof(struct igmphdr)) {
37788c2ecf20Sopenharmony_ci				pstats->mstats.igmp_v3queries[dir]++;
37798c2ecf20Sopenharmony_ci			} else {
37808c2ecf20Sopenharmony_ci				unsigned int offset = skb_transport_offset(skb);
37818c2ecf20Sopenharmony_ci				struct igmphdr *ih, _ihdr;
37828c2ecf20Sopenharmony_ci
37838c2ecf20Sopenharmony_ci				ih = skb_header_pointer(skb, offset,
37848c2ecf20Sopenharmony_ci							sizeof(_ihdr), &_ihdr);
37858c2ecf20Sopenharmony_ci				if (!ih)
37868c2ecf20Sopenharmony_ci					break;
37878c2ecf20Sopenharmony_ci				if (!ih->code)
37888c2ecf20Sopenharmony_ci					pstats->mstats.igmp_v1queries[dir]++;
37898c2ecf20Sopenharmony_ci				else
37908c2ecf20Sopenharmony_ci					pstats->mstats.igmp_v2queries[dir]++;
37918c2ecf20Sopenharmony_ci			}
37928c2ecf20Sopenharmony_ci			break;
37938c2ecf20Sopenharmony_ci		case IGMP_HOST_LEAVE_MESSAGE:
37948c2ecf20Sopenharmony_ci			pstats->mstats.igmp_leaves[dir]++;
37958c2ecf20Sopenharmony_ci			break;
37968c2ecf20Sopenharmony_ci		}
37978c2ecf20Sopenharmony_ci		break;
37988c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
37998c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
38008c2ecf20Sopenharmony_ci		t_len = ntohs(ipv6_hdr(skb)->payload_len) +
38018c2ecf20Sopenharmony_ci			sizeof(struct ipv6hdr);
38028c2ecf20Sopenharmony_ci		t_len -= skb_network_header_len(skb);
38038c2ecf20Sopenharmony_ci		switch (type) {
38048c2ecf20Sopenharmony_ci		case ICMPV6_MGM_REPORT:
38058c2ecf20Sopenharmony_ci			pstats->mstats.mld_v1reports[dir]++;
38068c2ecf20Sopenharmony_ci			break;
38078c2ecf20Sopenharmony_ci		case ICMPV6_MLD2_REPORT:
38088c2ecf20Sopenharmony_ci			pstats->mstats.mld_v2reports[dir]++;
38098c2ecf20Sopenharmony_ci			break;
38108c2ecf20Sopenharmony_ci		case ICMPV6_MGM_QUERY:
38118c2ecf20Sopenharmony_ci			if (t_len != sizeof(struct mld_msg))
38128c2ecf20Sopenharmony_ci				pstats->mstats.mld_v2queries[dir]++;
38138c2ecf20Sopenharmony_ci			else
38148c2ecf20Sopenharmony_ci				pstats->mstats.mld_v1queries[dir]++;
38158c2ecf20Sopenharmony_ci			break;
38168c2ecf20Sopenharmony_ci		case ICMPV6_MGM_REDUCTION:
38178c2ecf20Sopenharmony_ci			pstats->mstats.mld_leaves[dir]++;
38188c2ecf20Sopenharmony_ci			break;
38198c2ecf20Sopenharmony_ci		}
38208c2ecf20Sopenharmony_ci		break;
38218c2ecf20Sopenharmony_ci#endif /* CONFIG_IPV6 */
38228c2ecf20Sopenharmony_ci	}
38238c2ecf20Sopenharmony_ci	u64_stats_update_end(&pstats->syncp);
38248c2ecf20Sopenharmony_ci}
38258c2ecf20Sopenharmony_ci
38268c2ecf20Sopenharmony_civoid br_multicast_count(struct net_bridge *br, const struct net_bridge_port *p,
38278c2ecf20Sopenharmony_ci			const struct sk_buff *skb, u8 type, u8 dir)
38288c2ecf20Sopenharmony_ci{
38298c2ecf20Sopenharmony_ci	struct bridge_mcast_stats __percpu *stats;
38308c2ecf20Sopenharmony_ci
38318c2ecf20Sopenharmony_ci	/* if multicast_disabled is true then igmp type can't be set */
38328c2ecf20Sopenharmony_ci	if (!type || !br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED))
38338c2ecf20Sopenharmony_ci		return;
38348c2ecf20Sopenharmony_ci
38358c2ecf20Sopenharmony_ci	if (p)
38368c2ecf20Sopenharmony_ci		stats = p->mcast_stats;
38378c2ecf20Sopenharmony_ci	else
38388c2ecf20Sopenharmony_ci		stats = br->mcast_stats;
38398c2ecf20Sopenharmony_ci	if (WARN_ON(!stats))
38408c2ecf20Sopenharmony_ci		return;
38418c2ecf20Sopenharmony_ci
38428c2ecf20Sopenharmony_ci	br_mcast_stats_add(stats, skb, type, dir);
38438c2ecf20Sopenharmony_ci}
38448c2ecf20Sopenharmony_ci
38458c2ecf20Sopenharmony_ciint br_multicast_init_stats(struct net_bridge *br)
38468c2ecf20Sopenharmony_ci{
38478c2ecf20Sopenharmony_ci	br->mcast_stats = netdev_alloc_pcpu_stats(struct bridge_mcast_stats);
38488c2ecf20Sopenharmony_ci	if (!br->mcast_stats)
38498c2ecf20Sopenharmony_ci		return -ENOMEM;
38508c2ecf20Sopenharmony_ci
38518c2ecf20Sopenharmony_ci	return 0;
38528c2ecf20Sopenharmony_ci}
38538c2ecf20Sopenharmony_ci
38548c2ecf20Sopenharmony_civoid br_multicast_uninit_stats(struct net_bridge *br)
38558c2ecf20Sopenharmony_ci{
38568c2ecf20Sopenharmony_ci	free_percpu(br->mcast_stats);
38578c2ecf20Sopenharmony_ci}
38588c2ecf20Sopenharmony_ci
38598c2ecf20Sopenharmony_ci/* noinline for https://bugs.llvm.org/show_bug.cgi?id=45802#c9 */
38608c2ecf20Sopenharmony_cistatic noinline_for_stack void mcast_stats_add_dir(u64 *dst, u64 *src)
38618c2ecf20Sopenharmony_ci{
38628c2ecf20Sopenharmony_ci	dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX];
38638c2ecf20Sopenharmony_ci	dst[BR_MCAST_DIR_TX] += src[BR_MCAST_DIR_TX];
38648c2ecf20Sopenharmony_ci}
38658c2ecf20Sopenharmony_ci
38668c2ecf20Sopenharmony_civoid br_multicast_get_stats(const struct net_bridge *br,
38678c2ecf20Sopenharmony_ci			    const struct net_bridge_port *p,
38688c2ecf20Sopenharmony_ci			    struct br_mcast_stats *dest)
38698c2ecf20Sopenharmony_ci{
38708c2ecf20Sopenharmony_ci	struct bridge_mcast_stats __percpu *stats;
38718c2ecf20Sopenharmony_ci	struct br_mcast_stats tdst;
38728c2ecf20Sopenharmony_ci	int i;
38738c2ecf20Sopenharmony_ci
38748c2ecf20Sopenharmony_ci	memset(dest, 0, sizeof(*dest));
38758c2ecf20Sopenharmony_ci	if (p)
38768c2ecf20Sopenharmony_ci		stats = p->mcast_stats;
38778c2ecf20Sopenharmony_ci	else
38788c2ecf20Sopenharmony_ci		stats = br->mcast_stats;
38798c2ecf20Sopenharmony_ci	if (WARN_ON(!stats))
38808c2ecf20Sopenharmony_ci		return;
38818c2ecf20Sopenharmony_ci
38828c2ecf20Sopenharmony_ci	memset(&tdst, 0, sizeof(tdst));
38838c2ecf20Sopenharmony_ci	for_each_possible_cpu(i) {
38848c2ecf20Sopenharmony_ci		struct bridge_mcast_stats *cpu_stats = per_cpu_ptr(stats, i);
38858c2ecf20Sopenharmony_ci		struct br_mcast_stats temp;
38868c2ecf20Sopenharmony_ci		unsigned int start;
38878c2ecf20Sopenharmony_ci
38888c2ecf20Sopenharmony_ci		do {
38898c2ecf20Sopenharmony_ci			start = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
38908c2ecf20Sopenharmony_ci			memcpy(&temp, &cpu_stats->mstats, sizeof(temp));
38918c2ecf20Sopenharmony_ci		} while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start));
38928c2ecf20Sopenharmony_ci
38938c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v1queries, temp.igmp_v1queries);
38948c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v2queries, temp.igmp_v2queries);
38958c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v3queries, temp.igmp_v3queries);
38968c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_leaves, temp.igmp_leaves);
38978c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v1reports, temp.igmp_v1reports);
38988c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v2reports, temp.igmp_v2reports);
38998c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.igmp_v3reports, temp.igmp_v3reports);
39008c2ecf20Sopenharmony_ci		tdst.igmp_parse_errors += temp.igmp_parse_errors;
39018c2ecf20Sopenharmony_ci
39028c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_v1queries, temp.mld_v1queries);
39038c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_v2queries, temp.mld_v2queries);
39048c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_leaves, temp.mld_leaves);
39058c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_v1reports, temp.mld_v1reports);
39068c2ecf20Sopenharmony_ci		mcast_stats_add_dir(tdst.mld_v2reports, temp.mld_v2reports);
39078c2ecf20Sopenharmony_ci		tdst.mld_parse_errors += temp.mld_parse_errors;
39088c2ecf20Sopenharmony_ci	}
39098c2ecf20Sopenharmony_ci	memcpy(dest, &tdst, sizeof(*dest));
39108c2ecf20Sopenharmony_ci}
39118c2ecf20Sopenharmony_ci
39128c2ecf20Sopenharmony_ciint br_mdb_hash_init(struct net_bridge *br)
39138c2ecf20Sopenharmony_ci{
39148c2ecf20Sopenharmony_ci	int err;
39158c2ecf20Sopenharmony_ci
39168c2ecf20Sopenharmony_ci	err = rhashtable_init(&br->sg_port_tbl, &br_sg_port_rht_params);
39178c2ecf20Sopenharmony_ci	if (err)
39188c2ecf20Sopenharmony_ci		return err;
39198c2ecf20Sopenharmony_ci
39208c2ecf20Sopenharmony_ci	err = rhashtable_init(&br->mdb_hash_tbl, &br_mdb_rht_params);
39218c2ecf20Sopenharmony_ci	if (err) {
39228c2ecf20Sopenharmony_ci		rhashtable_destroy(&br->sg_port_tbl);
39238c2ecf20Sopenharmony_ci		return err;
39248c2ecf20Sopenharmony_ci	}
39258c2ecf20Sopenharmony_ci
39268c2ecf20Sopenharmony_ci	return 0;
39278c2ecf20Sopenharmony_ci}
39288c2ecf20Sopenharmony_ci
39298c2ecf20Sopenharmony_civoid br_mdb_hash_fini(struct net_bridge *br)
39308c2ecf20Sopenharmony_ci{
39318c2ecf20Sopenharmony_ci	rhashtable_destroy(&br->sg_port_tbl);
39328c2ecf20Sopenharmony_ci	rhashtable_destroy(&br->mdb_hash_tbl);
39338c2ecf20Sopenharmony_ci}
3934