18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/err.h>
38c2ecf20Sopenharmony_ci#include <linux/igmp.h>
48c2ecf20Sopenharmony_ci#include <linux/kernel.h>
58c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
68c2ecf20Sopenharmony_ci#include <linux/rculist.h>
78c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
88c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
98c2ecf20Sopenharmony_ci#include <net/ip.h>
108c2ecf20Sopenharmony_ci#include <net/netlink.h>
118c2ecf20Sopenharmony_ci#include <net/switchdev.h>
128c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
138c2ecf20Sopenharmony_ci#include <net/ipv6.h>
148c2ecf20Sopenharmony_ci#include <net/addrconf.h>
158c2ecf20Sopenharmony_ci#endif
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "br_private.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
208c2ecf20Sopenharmony_ci			       struct net_device *dev)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
238c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
248c2ecf20Sopenharmony_ci	struct nlattr *nest, *port_nest;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	if (!br->multicast_router || hlist_empty(&br->router_list))
278c2ecf20Sopenharmony_ci		return 0;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	nest = nla_nest_start_noflag(skb, MDBA_ROUTER);
308c2ecf20Sopenharmony_ci	if (nest == NULL)
318c2ecf20Sopenharmony_ci		return -EMSGSIZE;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(p, &br->router_list, rlist) {
348c2ecf20Sopenharmony_ci		if (!p)
358c2ecf20Sopenharmony_ci			continue;
368c2ecf20Sopenharmony_ci		port_nest = nla_nest_start_noflag(skb, MDBA_ROUTER_PORT);
378c2ecf20Sopenharmony_ci		if (!port_nest)
388c2ecf20Sopenharmony_ci			goto fail;
398c2ecf20Sopenharmony_ci		if (nla_put_nohdr(skb, sizeof(u32), &p->dev->ifindex) ||
408c2ecf20Sopenharmony_ci		    nla_put_u32(skb, MDBA_ROUTER_PATTR_TIMER,
418c2ecf20Sopenharmony_ci				br_timer_value(&p->multicast_router_timer)) ||
428c2ecf20Sopenharmony_ci		    nla_put_u8(skb, MDBA_ROUTER_PATTR_TYPE,
438c2ecf20Sopenharmony_ci			       p->multicast_router)) {
448c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, port_nest);
458c2ecf20Sopenharmony_ci			goto fail;
468c2ecf20Sopenharmony_ci		}
478c2ecf20Sopenharmony_ci		nla_nest_end(skb, port_nest);
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
518c2ecf20Sopenharmony_ci	return 0;
528c2ecf20Sopenharmony_cifail:
538c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
548c2ecf20Sopenharmony_ci	return -EMSGSIZE;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void __mdb_entry_fill_flags(struct br_mdb_entry *e, unsigned char flags)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	e->state = flags & MDB_PG_FLAGS_PERMANENT;
608c2ecf20Sopenharmony_ci	e->flags = 0;
618c2ecf20Sopenharmony_ci	if (flags & MDB_PG_FLAGS_OFFLOAD)
628c2ecf20Sopenharmony_ci		e->flags |= MDB_FLAGS_OFFLOAD;
638c2ecf20Sopenharmony_ci	if (flags & MDB_PG_FLAGS_FAST_LEAVE)
648c2ecf20Sopenharmony_ci		e->flags |= MDB_FLAGS_FAST_LEAVE;
658c2ecf20Sopenharmony_ci	if (flags & MDB_PG_FLAGS_STAR_EXCL)
668c2ecf20Sopenharmony_ci		e->flags |= MDB_FLAGS_STAR_EXCL;
678c2ecf20Sopenharmony_ci	if (flags & MDB_PG_FLAGS_BLOCKED)
688c2ecf20Sopenharmony_ci		e->flags |= MDB_FLAGS_BLOCKED;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip,
728c2ecf20Sopenharmony_ci				 struct nlattr **mdb_attrs)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	memset(ip, 0, sizeof(struct br_ip));
758c2ecf20Sopenharmony_ci	ip->vid = entry->vid;
768c2ecf20Sopenharmony_ci	ip->proto = entry->addr.proto;
778c2ecf20Sopenharmony_ci	switch (ip->proto) {
788c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
798c2ecf20Sopenharmony_ci		ip->dst.ip4 = entry->addr.u.ip4;
808c2ecf20Sopenharmony_ci		if (mdb_attrs && mdb_attrs[MDBE_ATTR_SOURCE])
818c2ecf20Sopenharmony_ci			ip->src.ip4 = nla_get_in_addr(mdb_attrs[MDBE_ATTR_SOURCE]);
828c2ecf20Sopenharmony_ci		break;
838c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
848c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
858c2ecf20Sopenharmony_ci		ip->dst.ip6 = entry->addr.u.ip6;
868c2ecf20Sopenharmony_ci		if (mdb_attrs && mdb_attrs[MDBE_ATTR_SOURCE])
878c2ecf20Sopenharmony_ci			ip->src.ip6 = nla_get_in6_addr(mdb_attrs[MDBE_ATTR_SOURCE]);
888c2ecf20Sopenharmony_ci		break;
898c2ecf20Sopenharmony_ci#endif
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int __mdb_fill_srcs(struct sk_buff *skb,
958c2ecf20Sopenharmony_ci			   struct net_bridge_port_group *p)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
988c2ecf20Sopenharmony_ci	struct nlattr *nest, *nest_ent;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (hlist_empty(&p->src_list))
1018c2ecf20Sopenharmony_ci		return 0;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	nest = nla_nest_start(skb, MDBA_MDB_EATTR_SRC_LIST);
1048c2ecf20Sopenharmony_ci	if (!nest)
1058c2ecf20Sopenharmony_ci		return -EMSGSIZE;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(ent, &p->src_list, node,
1088c2ecf20Sopenharmony_ci				 lockdep_is_held(&p->key.port->br->multicast_lock)) {
1098c2ecf20Sopenharmony_ci		nest_ent = nla_nest_start(skb, MDBA_MDB_SRCLIST_ENTRY);
1108c2ecf20Sopenharmony_ci		if (!nest_ent)
1118c2ecf20Sopenharmony_ci			goto out_cancel_err;
1128c2ecf20Sopenharmony_ci		switch (ent->addr.proto) {
1138c2ecf20Sopenharmony_ci		case htons(ETH_P_IP):
1148c2ecf20Sopenharmony_ci			if (nla_put_in_addr(skb, MDBA_MDB_SRCATTR_ADDRESS,
1158c2ecf20Sopenharmony_ci					    ent->addr.src.ip4)) {
1168c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, nest_ent);
1178c2ecf20Sopenharmony_ci				goto out_cancel_err;
1188c2ecf20Sopenharmony_ci			}
1198c2ecf20Sopenharmony_ci			break;
1208c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1218c2ecf20Sopenharmony_ci		case htons(ETH_P_IPV6):
1228c2ecf20Sopenharmony_ci			if (nla_put_in6_addr(skb, MDBA_MDB_SRCATTR_ADDRESS,
1238c2ecf20Sopenharmony_ci					     &ent->addr.src.ip6)) {
1248c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, nest_ent);
1258c2ecf20Sopenharmony_ci				goto out_cancel_err;
1268c2ecf20Sopenharmony_ci			}
1278c2ecf20Sopenharmony_ci			break;
1288c2ecf20Sopenharmony_ci#endif
1298c2ecf20Sopenharmony_ci		default:
1308c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, nest_ent);
1318c2ecf20Sopenharmony_ci			continue;
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, MDBA_MDB_SRCATTR_TIMER,
1348c2ecf20Sopenharmony_ci				br_timer_value(&ent->timer))) {
1358c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, nest_ent);
1368c2ecf20Sopenharmony_ci			goto out_cancel_err;
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci		nla_nest_end(skb, nest_ent);
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ciout_cancel_err:
1468c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
1478c2ecf20Sopenharmony_ci	return -EMSGSIZE;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int __mdb_fill_info(struct sk_buff *skb,
1518c2ecf20Sopenharmony_ci			   struct net_bridge_mdb_entry *mp,
1528c2ecf20Sopenharmony_ci			   struct net_bridge_port_group *p)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	bool dump_srcs_mode = false;
1558c2ecf20Sopenharmony_ci	struct timer_list *mtimer;
1568c2ecf20Sopenharmony_ci	struct nlattr *nest_ent;
1578c2ecf20Sopenharmony_ci	struct br_mdb_entry e;
1588c2ecf20Sopenharmony_ci	u8 flags = 0;
1598c2ecf20Sopenharmony_ci	int ifindex;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	memset(&e, 0, sizeof(e));
1628c2ecf20Sopenharmony_ci	if (p) {
1638c2ecf20Sopenharmony_ci		ifindex = p->key.port->dev->ifindex;
1648c2ecf20Sopenharmony_ci		mtimer = &p->timer;
1658c2ecf20Sopenharmony_ci		flags = p->flags;
1668c2ecf20Sopenharmony_ci	} else {
1678c2ecf20Sopenharmony_ci		ifindex = mp->br->dev->ifindex;
1688c2ecf20Sopenharmony_ci		mtimer = &mp->timer;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	__mdb_entry_fill_flags(&e, flags);
1728c2ecf20Sopenharmony_ci	e.ifindex = ifindex;
1738c2ecf20Sopenharmony_ci	e.vid = mp->addr.vid;
1748c2ecf20Sopenharmony_ci	if (mp->addr.proto == htons(ETH_P_IP))
1758c2ecf20Sopenharmony_ci		e.addr.u.ip4 = mp->addr.dst.ip4;
1768c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1778c2ecf20Sopenharmony_ci	if (mp->addr.proto == htons(ETH_P_IPV6))
1788c2ecf20Sopenharmony_ci		e.addr.u.ip6 = mp->addr.dst.ip6;
1798c2ecf20Sopenharmony_ci#endif
1808c2ecf20Sopenharmony_ci	e.addr.proto = mp->addr.proto;
1818c2ecf20Sopenharmony_ci	nest_ent = nla_nest_start_noflag(skb,
1828c2ecf20Sopenharmony_ci					 MDBA_MDB_ENTRY_INFO);
1838c2ecf20Sopenharmony_ci	if (!nest_ent)
1848c2ecf20Sopenharmony_ci		return -EMSGSIZE;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (nla_put_nohdr(skb, sizeof(e), &e) ||
1878c2ecf20Sopenharmony_ci	    nla_put_u32(skb,
1888c2ecf20Sopenharmony_ci			MDBA_MDB_EATTR_TIMER,
1898c2ecf20Sopenharmony_ci			br_timer_value(mtimer)))
1908c2ecf20Sopenharmony_ci		goto nest_err;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	switch (mp->addr.proto) {
1938c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
1948c2ecf20Sopenharmony_ci		dump_srcs_mode = !!(mp->br->multicast_igmp_version == 3);
1958c2ecf20Sopenharmony_ci		if (mp->addr.src.ip4) {
1968c2ecf20Sopenharmony_ci			if (nla_put_in_addr(skb, MDBA_MDB_EATTR_SOURCE,
1978c2ecf20Sopenharmony_ci					    mp->addr.src.ip4))
1988c2ecf20Sopenharmony_ci				goto nest_err;
1998c2ecf20Sopenharmony_ci			break;
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
2038c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
2048c2ecf20Sopenharmony_ci		dump_srcs_mode = !!(mp->br->multicast_mld_version == 2);
2058c2ecf20Sopenharmony_ci		if (!ipv6_addr_any(&mp->addr.src.ip6)) {
2068c2ecf20Sopenharmony_ci			if (nla_put_in6_addr(skb, MDBA_MDB_EATTR_SOURCE,
2078c2ecf20Sopenharmony_ci					     &mp->addr.src.ip6))
2088c2ecf20Sopenharmony_ci				goto nest_err;
2098c2ecf20Sopenharmony_ci			break;
2108c2ecf20Sopenharmony_ci		}
2118c2ecf20Sopenharmony_ci		break;
2128c2ecf20Sopenharmony_ci#endif
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci	if (p) {
2158c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, MDBA_MDB_EATTR_RTPROT, p->rt_protocol))
2168c2ecf20Sopenharmony_ci			goto nest_err;
2178c2ecf20Sopenharmony_ci		if (dump_srcs_mode &&
2188c2ecf20Sopenharmony_ci		    (__mdb_fill_srcs(skb, p) ||
2198c2ecf20Sopenharmony_ci		     nla_put_u8(skb, MDBA_MDB_EATTR_GROUP_MODE,
2208c2ecf20Sopenharmony_ci				p->filter_mode)))
2218c2ecf20Sopenharmony_ci			goto nest_err;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest_ent);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return 0;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cinest_err:
2288c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest_ent);
2298c2ecf20Sopenharmony_ci	return -EMSGSIZE;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
2338c2ecf20Sopenharmony_ci			    struct net_device *dev)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	int idx = 0, s_idx = cb->args[1], err = 0, pidx = 0, s_pidx = cb->args[2];
2368c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
2378c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
2388c2ecf20Sopenharmony_ci	struct nlattr *nest, *nest2;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED))
2418c2ecf20Sopenharmony_ci		return 0;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	nest = nla_nest_start_noflag(skb, MDBA_MDB);
2448c2ecf20Sopenharmony_ci	if (nest == NULL)
2458c2ecf20Sopenharmony_ci		return -EMSGSIZE;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) {
2488c2ecf20Sopenharmony_ci		struct net_bridge_port_group *p;
2498c2ecf20Sopenharmony_ci		struct net_bridge_port_group __rcu **pp;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		if (idx < s_idx)
2528c2ecf20Sopenharmony_ci			goto skip;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		nest2 = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY);
2558c2ecf20Sopenharmony_ci		if (!nest2) {
2568c2ecf20Sopenharmony_ci			err = -EMSGSIZE;
2578c2ecf20Sopenharmony_ci			break;
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		if (!s_pidx && mp->host_joined) {
2618c2ecf20Sopenharmony_ci			err = __mdb_fill_info(skb, mp, NULL);
2628c2ecf20Sopenharmony_ci			if (err) {
2638c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, nest2);
2648c2ecf20Sopenharmony_ci				break;
2658c2ecf20Sopenharmony_ci			}
2668c2ecf20Sopenharmony_ci		}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL;
2698c2ecf20Sopenharmony_ci		      pp = &p->next) {
2708c2ecf20Sopenharmony_ci			if (!p->key.port)
2718c2ecf20Sopenharmony_ci				continue;
2728c2ecf20Sopenharmony_ci			if (pidx < s_pidx)
2738c2ecf20Sopenharmony_ci				goto skip_pg;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci			err = __mdb_fill_info(skb, mp, p);
2768c2ecf20Sopenharmony_ci			if (err) {
2778c2ecf20Sopenharmony_ci				nla_nest_end(skb, nest2);
2788c2ecf20Sopenharmony_ci				goto out;
2798c2ecf20Sopenharmony_ci			}
2808c2ecf20Sopenharmony_ciskip_pg:
2818c2ecf20Sopenharmony_ci			pidx++;
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci		pidx = 0;
2848c2ecf20Sopenharmony_ci		s_pidx = 0;
2858c2ecf20Sopenharmony_ci		nla_nest_end(skb, nest2);
2868c2ecf20Sopenharmony_ciskip:
2878c2ecf20Sopenharmony_ci		idx++;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ciout:
2918c2ecf20Sopenharmony_ci	cb->args[1] = idx;
2928c2ecf20Sopenharmony_ci	cb->args[2] = pidx;
2938c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
2948c2ecf20Sopenharmony_ci	return err;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int br_mdb_valid_dump_req(const struct nlmsghdr *nlh,
2988c2ecf20Sopenharmony_ci				 struct netlink_ext_ack *extack)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct br_port_msg *bpm;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*bpm))) {
3038c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for mdb dump request");
3048c2ecf20Sopenharmony_ci		return -EINVAL;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	bpm = nlmsg_data(nlh);
3088c2ecf20Sopenharmony_ci	if (bpm->ifindex) {
3098c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Filtering by device index is not supported for mdb dump request");
3108c2ecf20Sopenharmony_ci		return -EINVAL;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci	if (nlmsg_attrlen(nlh, sizeof(*bpm))) {
3138c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid data after header in mdb dump request");
3148c2ecf20Sopenharmony_ci		return -EINVAL;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return 0;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct net_device *dev;
3238c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
3248c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh = NULL;
3258c2ecf20Sopenharmony_ci	int idx = 0, s_idx;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (cb->strict_check) {
3288c2ecf20Sopenharmony_ci		int err = br_mdb_valid_dump_req(cb->nlh, cb->extack);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		if (err < 0)
3318c2ecf20Sopenharmony_ci			return err;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	s_idx = cb->args[0];
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	rcu_read_lock();
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	cb->seq = net->dev_base_seq;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	for_each_netdev_rcu(net, dev) {
3418c2ecf20Sopenharmony_ci		if (dev->priv_flags & IFF_EBRIDGE) {
3428c2ecf20Sopenharmony_ci			struct br_port_msg *bpm;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci			if (idx < s_idx)
3458c2ecf20Sopenharmony_ci				goto skip;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci			nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
3488c2ecf20Sopenharmony_ci					cb->nlh->nlmsg_seq, RTM_GETMDB,
3498c2ecf20Sopenharmony_ci					sizeof(*bpm), NLM_F_MULTI);
3508c2ecf20Sopenharmony_ci			if (nlh == NULL)
3518c2ecf20Sopenharmony_ci				break;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci			bpm = nlmsg_data(nlh);
3548c2ecf20Sopenharmony_ci			memset(bpm, 0, sizeof(*bpm));
3558c2ecf20Sopenharmony_ci			bpm->ifindex = dev->ifindex;
3568c2ecf20Sopenharmony_ci			if (br_mdb_fill_info(skb, cb, dev) < 0)
3578c2ecf20Sopenharmony_ci				goto out;
3588c2ecf20Sopenharmony_ci			if (br_rports_fill_info(skb, cb, dev) < 0)
3598c2ecf20Sopenharmony_ci				goto out;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci			cb->args[1] = 0;
3628c2ecf20Sopenharmony_ci			nlmsg_end(skb, nlh);
3638c2ecf20Sopenharmony_ci		skip:
3648c2ecf20Sopenharmony_ci			idx++;
3658c2ecf20Sopenharmony_ci		}
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ciout:
3698c2ecf20Sopenharmony_ci	if (nlh)
3708c2ecf20Sopenharmony_ci		nlmsg_end(skb, nlh);
3718c2ecf20Sopenharmony_ci	rcu_read_unlock();
3728c2ecf20Sopenharmony_ci	cb->args[0] = idx;
3738c2ecf20Sopenharmony_ci	return skb->len;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic int nlmsg_populate_mdb_fill(struct sk_buff *skb,
3778c2ecf20Sopenharmony_ci				   struct net_device *dev,
3788c2ecf20Sopenharmony_ci				   struct net_bridge_mdb_entry *mp,
3798c2ecf20Sopenharmony_ci				   struct net_bridge_port_group *pg,
3808c2ecf20Sopenharmony_ci				   int type)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
3838c2ecf20Sopenharmony_ci	struct br_port_msg *bpm;
3848c2ecf20Sopenharmony_ci	struct nlattr *nest, *nest2;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, 0, 0, type, sizeof(*bpm), 0);
3878c2ecf20Sopenharmony_ci	if (!nlh)
3888c2ecf20Sopenharmony_ci		return -EMSGSIZE;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	bpm = nlmsg_data(nlh);
3918c2ecf20Sopenharmony_ci	memset(bpm, 0, sizeof(*bpm));
3928c2ecf20Sopenharmony_ci	bpm->family  = AF_BRIDGE;
3938c2ecf20Sopenharmony_ci	bpm->ifindex = dev->ifindex;
3948c2ecf20Sopenharmony_ci	nest = nla_nest_start_noflag(skb, MDBA_MDB);
3958c2ecf20Sopenharmony_ci	if (nest == NULL)
3968c2ecf20Sopenharmony_ci		goto cancel;
3978c2ecf20Sopenharmony_ci	nest2 = nla_nest_start_noflag(skb, MDBA_MDB_ENTRY);
3988c2ecf20Sopenharmony_ci	if (nest2 == NULL)
3998c2ecf20Sopenharmony_ci		goto end;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (__mdb_fill_info(skb, mp, pg))
4028c2ecf20Sopenharmony_ci		goto end;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest2);
4058c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
4068c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
4078c2ecf20Sopenharmony_ci	return 0;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ciend:
4108c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
4118c2ecf20Sopenharmony_cicancel:
4128c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
4138c2ecf20Sopenharmony_ci	return -EMSGSIZE;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic size_t rtnl_mdb_nlmsg_size(struct net_bridge_port_group *pg)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	size_t nlmsg_size = NLMSG_ALIGN(sizeof(struct br_port_msg)) +
4198c2ecf20Sopenharmony_ci			    nla_total_size(sizeof(struct br_mdb_entry)) +
4208c2ecf20Sopenharmony_ci			    nla_total_size(sizeof(u32));
4218c2ecf20Sopenharmony_ci	struct net_bridge_group_src *ent;
4228c2ecf20Sopenharmony_ci	size_t addr_size = 0;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (!pg)
4258c2ecf20Sopenharmony_ci		goto out;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* MDBA_MDB_EATTR_RTPROT */
4288c2ecf20Sopenharmony_ci	nlmsg_size += nla_total_size(sizeof(u8));
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	switch (pg->key.addr.proto) {
4318c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
4328c2ecf20Sopenharmony_ci		/* MDBA_MDB_EATTR_SOURCE */
4338c2ecf20Sopenharmony_ci		if (pg->key.addr.src.ip4)
4348c2ecf20Sopenharmony_ci			nlmsg_size += nla_total_size(sizeof(__be32));
4358c2ecf20Sopenharmony_ci		if (pg->key.port->br->multicast_igmp_version == 2)
4368c2ecf20Sopenharmony_ci			goto out;
4378c2ecf20Sopenharmony_ci		addr_size = sizeof(__be32);
4388c2ecf20Sopenharmony_ci		break;
4398c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
4408c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
4418c2ecf20Sopenharmony_ci		/* MDBA_MDB_EATTR_SOURCE */
4428c2ecf20Sopenharmony_ci		if (!ipv6_addr_any(&pg->key.addr.src.ip6))
4438c2ecf20Sopenharmony_ci			nlmsg_size += nla_total_size(sizeof(struct in6_addr));
4448c2ecf20Sopenharmony_ci		if (pg->key.port->br->multicast_mld_version == 1)
4458c2ecf20Sopenharmony_ci			goto out;
4468c2ecf20Sopenharmony_ci		addr_size = sizeof(struct in6_addr);
4478c2ecf20Sopenharmony_ci		break;
4488c2ecf20Sopenharmony_ci#endif
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	/* MDBA_MDB_EATTR_GROUP_MODE */
4528c2ecf20Sopenharmony_ci	nlmsg_size += nla_total_size(sizeof(u8));
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* MDBA_MDB_EATTR_SRC_LIST nested attr */
4558c2ecf20Sopenharmony_ci	if (!hlist_empty(&pg->src_list))
4568c2ecf20Sopenharmony_ci		nlmsg_size += nla_total_size(0);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	hlist_for_each_entry(ent, &pg->src_list, node) {
4598c2ecf20Sopenharmony_ci		/* MDBA_MDB_SRCLIST_ENTRY nested attr +
4608c2ecf20Sopenharmony_ci		 * MDBA_MDB_SRCATTR_ADDRESS + MDBA_MDB_SRCATTR_TIMER
4618c2ecf20Sopenharmony_ci		 */
4628c2ecf20Sopenharmony_ci		nlmsg_size += nla_total_size(0) +
4638c2ecf20Sopenharmony_ci			      nla_total_size(addr_size) +
4648c2ecf20Sopenharmony_ci			      nla_total_size(sizeof(u32));
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ciout:
4678c2ecf20Sopenharmony_ci	return nlmsg_size;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistruct br_mdb_complete_info {
4718c2ecf20Sopenharmony_ci	struct net_bridge_port *port;
4728c2ecf20Sopenharmony_ci	struct br_ip ip;
4738c2ecf20Sopenharmony_ci};
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic void br_mdb_complete(struct net_device *dev, int err, void *priv)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct br_mdb_complete_info *data = priv;
4788c2ecf20Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
4798c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p;
4808c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
4818c2ecf20Sopenharmony_ci	struct net_bridge_port *port = data->port;
4828c2ecf20Sopenharmony_ci	struct net_bridge *br = port->br;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (err)
4858c2ecf20Sopenharmony_ci		goto err;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
4888c2ecf20Sopenharmony_ci	mp = br_mdb_ip_get(br, &data->ip);
4898c2ecf20Sopenharmony_ci	if (!mp)
4908c2ecf20Sopenharmony_ci		goto out;
4918c2ecf20Sopenharmony_ci	for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
4928c2ecf20Sopenharmony_ci	     pp = &p->next) {
4938c2ecf20Sopenharmony_ci		if (p->key.port != port)
4948c2ecf20Sopenharmony_ci			continue;
4958c2ecf20Sopenharmony_ci		p->flags |= MDB_PG_FLAGS_OFFLOAD;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ciout:
4988c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
4998c2ecf20Sopenharmony_cierr:
5008c2ecf20Sopenharmony_ci	kfree(priv);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic void br_mdb_switchdev_host_port(struct net_device *dev,
5048c2ecf20Sopenharmony_ci				       struct net_device *lower_dev,
5058c2ecf20Sopenharmony_ci				       struct net_bridge_mdb_entry *mp,
5068c2ecf20Sopenharmony_ci				       int type)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	struct switchdev_obj_port_mdb mdb = {
5098c2ecf20Sopenharmony_ci		.obj = {
5108c2ecf20Sopenharmony_ci			.id = SWITCHDEV_OBJ_ID_HOST_MDB,
5118c2ecf20Sopenharmony_ci			.flags = SWITCHDEV_F_DEFER,
5128c2ecf20Sopenharmony_ci		},
5138c2ecf20Sopenharmony_ci		.vid = mp->addr.vid,
5148c2ecf20Sopenharmony_ci	};
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	if (mp->addr.proto == htons(ETH_P_IP))
5178c2ecf20Sopenharmony_ci		ip_eth_mc_map(mp->addr.dst.ip4, mdb.addr);
5188c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
5198c2ecf20Sopenharmony_ci	else
5208c2ecf20Sopenharmony_ci		ipv6_eth_mc_map(&mp->addr.dst.ip6, mdb.addr);
5218c2ecf20Sopenharmony_ci#endif
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	mdb.obj.orig_dev = dev;
5248c2ecf20Sopenharmony_ci	switch (type) {
5258c2ecf20Sopenharmony_ci	case RTM_NEWMDB:
5268c2ecf20Sopenharmony_ci		switchdev_port_obj_add(lower_dev, &mdb.obj, NULL);
5278c2ecf20Sopenharmony_ci		break;
5288c2ecf20Sopenharmony_ci	case RTM_DELMDB:
5298c2ecf20Sopenharmony_ci		switchdev_port_obj_del(lower_dev, &mdb.obj);
5308c2ecf20Sopenharmony_ci		break;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic void br_mdb_switchdev_host(struct net_device *dev,
5358c2ecf20Sopenharmony_ci				  struct net_bridge_mdb_entry *mp, int type)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct net_device *lower_dev;
5388c2ecf20Sopenharmony_ci	struct list_head *iter;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	netdev_for_each_lower_dev(dev, lower_dev, iter)
5418c2ecf20Sopenharmony_ci		br_mdb_switchdev_host_port(dev, lower_dev, mp, type);
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_civoid br_mdb_notify(struct net_device *dev,
5458c2ecf20Sopenharmony_ci		   struct net_bridge_mdb_entry *mp,
5468c2ecf20Sopenharmony_ci		   struct net_bridge_port_group *pg,
5478c2ecf20Sopenharmony_ci		   int type)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct br_mdb_complete_info *complete_info;
5508c2ecf20Sopenharmony_ci	struct switchdev_obj_port_mdb mdb = {
5518c2ecf20Sopenharmony_ci		.obj = {
5528c2ecf20Sopenharmony_ci			.id = SWITCHDEV_OBJ_ID_PORT_MDB,
5538c2ecf20Sopenharmony_ci			.flags = SWITCHDEV_F_DEFER,
5548c2ecf20Sopenharmony_ci		},
5558c2ecf20Sopenharmony_ci		.vid = mp->addr.vid,
5568c2ecf20Sopenharmony_ci	};
5578c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
5588c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5598c2ecf20Sopenharmony_ci	int err = -ENOBUFS;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	if (pg) {
5628c2ecf20Sopenharmony_ci		if (mp->addr.proto == htons(ETH_P_IP))
5638c2ecf20Sopenharmony_ci			ip_eth_mc_map(mp->addr.dst.ip4, mdb.addr);
5648c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
5658c2ecf20Sopenharmony_ci		else
5668c2ecf20Sopenharmony_ci			ipv6_eth_mc_map(&mp->addr.dst.ip6, mdb.addr);
5678c2ecf20Sopenharmony_ci#endif
5688c2ecf20Sopenharmony_ci		mdb.obj.orig_dev = pg->key.port->dev;
5698c2ecf20Sopenharmony_ci		switch (type) {
5708c2ecf20Sopenharmony_ci		case RTM_NEWMDB:
5718c2ecf20Sopenharmony_ci			complete_info = kmalloc(sizeof(*complete_info), GFP_ATOMIC);
5728c2ecf20Sopenharmony_ci			if (!complete_info)
5738c2ecf20Sopenharmony_ci				break;
5748c2ecf20Sopenharmony_ci			complete_info->port = pg->key.port;
5758c2ecf20Sopenharmony_ci			complete_info->ip = mp->addr;
5768c2ecf20Sopenharmony_ci			mdb.obj.complete_priv = complete_info;
5778c2ecf20Sopenharmony_ci			mdb.obj.complete = br_mdb_complete;
5788c2ecf20Sopenharmony_ci			if (switchdev_port_obj_add(pg->key.port->dev, &mdb.obj, NULL))
5798c2ecf20Sopenharmony_ci				kfree(complete_info);
5808c2ecf20Sopenharmony_ci			break;
5818c2ecf20Sopenharmony_ci		case RTM_DELMDB:
5828c2ecf20Sopenharmony_ci			switchdev_port_obj_del(pg->key.port->dev, &mdb.obj);
5838c2ecf20Sopenharmony_ci			break;
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci	} else {
5868c2ecf20Sopenharmony_ci		br_mdb_switchdev_host(dev, mp, type);
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	skb = nlmsg_new(rtnl_mdb_nlmsg_size(pg), GFP_ATOMIC);
5908c2ecf20Sopenharmony_ci	if (!skb)
5918c2ecf20Sopenharmony_ci		goto errout;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	err = nlmsg_populate_mdb_fill(skb, dev, mp, pg, type);
5948c2ecf20Sopenharmony_ci	if (err < 0) {
5958c2ecf20Sopenharmony_ci		kfree_skb(skb);
5968c2ecf20Sopenharmony_ci		goto errout;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC);
6008c2ecf20Sopenharmony_ci	return;
6018c2ecf20Sopenharmony_cierrout:
6028c2ecf20Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_MDB, err);
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int nlmsg_populate_rtr_fill(struct sk_buff *skb,
6068c2ecf20Sopenharmony_ci				   struct net_device *dev,
6078c2ecf20Sopenharmony_ci				   int ifindex, u32 pid,
6088c2ecf20Sopenharmony_ci				   u32 seq, int type, unsigned int flags)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct br_port_msg *bpm;
6118c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
6128c2ecf20Sopenharmony_ci	struct nlattr *nest;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*bpm), 0);
6158c2ecf20Sopenharmony_ci	if (!nlh)
6168c2ecf20Sopenharmony_ci		return -EMSGSIZE;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	bpm = nlmsg_data(nlh);
6198c2ecf20Sopenharmony_ci	memset(bpm, 0, sizeof(*bpm));
6208c2ecf20Sopenharmony_ci	bpm->family = AF_BRIDGE;
6218c2ecf20Sopenharmony_ci	bpm->ifindex = dev->ifindex;
6228c2ecf20Sopenharmony_ci	nest = nla_nest_start_noflag(skb, MDBA_ROUTER);
6238c2ecf20Sopenharmony_ci	if (!nest)
6248c2ecf20Sopenharmony_ci		goto cancel;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, MDBA_ROUTER_PORT, ifindex))
6278c2ecf20Sopenharmony_ci		goto end;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
6308c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
6318c2ecf20Sopenharmony_ci	return 0;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ciend:
6348c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
6358c2ecf20Sopenharmony_cicancel:
6368c2ecf20Sopenharmony_ci	nlmsg_cancel(skb, nlh);
6378c2ecf20Sopenharmony_ci	return -EMSGSIZE;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cistatic inline size_t rtnl_rtr_nlmsg_size(void)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct br_port_msg))
6438c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(__u32));
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_civoid br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
6478c2ecf20Sopenharmony_ci		   int type)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
6508c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6518c2ecf20Sopenharmony_ci	int err = -ENOBUFS;
6528c2ecf20Sopenharmony_ci	int ifindex;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	ifindex = port ? port->dev->ifindex : 0;
6558c2ecf20Sopenharmony_ci	skb = nlmsg_new(rtnl_rtr_nlmsg_size(), GFP_ATOMIC);
6568c2ecf20Sopenharmony_ci	if (!skb)
6578c2ecf20Sopenharmony_ci		goto errout;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	err = nlmsg_populate_rtr_fill(skb, dev, ifindex, 0, 0, type, NTF_SELF);
6608c2ecf20Sopenharmony_ci	if (err < 0) {
6618c2ecf20Sopenharmony_ci		kfree_skb(skb);
6628c2ecf20Sopenharmony_ci		goto errout;
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC);
6668c2ecf20Sopenharmony_ci	return;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cierrout:
6698c2ecf20Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_MDB, err);
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic bool is_valid_mdb_entry(struct br_mdb_entry *entry,
6738c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	if (entry->ifindex == 0) {
6768c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Zero entry ifindex is not allowed");
6778c2ecf20Sopenharmony_ci		return false;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (entry->addr.proto == htons(ETH_P_IP)) {
6818c2ecf20Sopenharmony_ci		if (!ipv4_is_multicast(entry->addr.u.ip4)) {
6828c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "IPv4 entry group address is not multicast");
6838c2ecf20Sopenharmony_ci			return false;
6848c2ecf20Sopenharmony_ci		}
6858c2ecf20Sopenharmony_ci		if (ipv4_is_local_multicast(entry->addr.u.ip4)) {
6868c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "IPv4 entry group address is local multicast");
6878c2ecf20Sopenharmony_ci			return false;
6888c2ecf20Sopenharmony_ci		}
6898c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
6908c2ecf20Sopenharmony_ci	} else if (entry->addr.proto == htons(ETH_P_IPV6)) {
6918c2ecf20Sopenharmony_ci		if (ipv6_addr_is_ll_all_nodes(&entry->addr.u.ip6)) {
6928c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "IPv6 entry group address is link-local all nodes");
6938c2ecf20Sopenharmony_ci			return false;
6948c2ecf20Sopenharmony_ci		}
6958c2ecf20Sopenharmony_ci#endif
6968c2ecf20Sopenharmony_ci	} else {
6978c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unknown entry protocol");
6988c2ecf20Sopenharmony_ci		return false;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	if (entry->state != MDB_PERMANENT && entry->state != MDB_TEMPORARY) {
7028c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unknown entry state");
7038c2ecf20Sopenharmony_ci		return false;
7048c2ecf20Sopenharmony_ci	}
7058c2ecf20Sopenharmony_ci	if (entry->vid >= VLAN_VID_MASK) {
7068c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid entry VLAN id");
7078c2ecf20Sopenharmony_ci		return false;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return true;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic bool is_valid_mdb_source(struct nlattr *attr, __be16 proto,
7148c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	switch (proto) {
7178c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
7188c2ecf20Sopenharmony_ci		if (nla_len(attr) != sizeof(struct in_addr)) {
7198c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "IPv4 invalid source address length");
7208c2ecf20Sopenharmony_ci			return false;
7218c2ecf20Sopenharmony_ci		}
7228c2ecf20Sopenharmony_ci		if (ipv4_is_multicast(nla_get_in_addr(attr))) {
7238c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed");
7248c2ecf20Sopenharmony_ci			return false;
7258c2ecf20Sopenharmony_ci		}
7268c2ecf20Sopenharmony_ci		break;
7278c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
7288c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6): {
7298c2ecf20Sopenharmony_ci		struct in6_addr src;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci		if (nla_len(attr) != sizeof(struct in6_addr)) {
7328c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "IPv6 invalid source address length");
7338c2ecf20Sopenharmony_ci			return false;
7348c2ecf20Sopenharmony_ci		}
7358c2ecf20Sopenharmony_ci		src = nla_get_in6_addr(attr);
7368c2ecf20Sopenharmony_ci		if (ipv6_addr_is_multicast(&src)) {
7378c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed");
7388c2ecf20Sopenharmony_ci			return false;
7398c2ecf20Sopenharmony_ci		}
7408c2ecf20Sopenharmony_ci		break;
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci#endif
7438c2ecf20Sopenharmony_ci	default:
7448c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid protocol used with source address");
7458c2ecf20Sopenharmony_ci		return false;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	return true;
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = {
7528c2ecf20Sopenharmony_ci	[MDBE_ATTR_SOURCE] = NLA_POLICY_RANGE(NLA_BINARY,
7538c2ecf20Sopenharmony_ci					      sizeof(struct in_addr),
7548c2ecf20Sopenharmony_ci					      sizeof(struct in6_addr)),
7558c2ecf20Sopenharmony_ci};
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh,
7588c2ecf20Sopenharmony_ci			struct net_device **pdev, struct br_mdb_entry **pentry,
7598c2ecf20Sopenharmony_ci			struct nlattr **mdb_attrs, struct netlink_ext_ack *extack)
7608c2ecf20Sopenharmony_ci{
7618c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
7628c2ecf20Sopenharmony_ci	struct br_mdb_entry *entry;
7638c2ecf20Sopenharmony_ci	struct br_port_msg *bpm;
7648c2ecf20Sopenharmony_ci	struct nlattr *tb[MDBA_SET_ENTRY_MAX+1];
7658c2ecf20Sopenharmony_ci	struct net_device *dev;
7668c2ecf20Sopenharmony_ci	int err;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb,
7698c2ecf20Sopenharmony_ci				     MDBA_SET_ENTRY_MAX, NULL, NULL);
7708c2ecf20Sopenharmony_ci	if (err < 0)
7718c2ecf20Sopenharmony_ci		return err;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	bpm = nlmsg_data(nlh);
7748c2ecf20Sopenharmony_ci	if (bpm->ifindex == 0) {
7758c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid bridge ifindex");
7768c2ecf20Sopenharmony_ci		return -EINVAL;
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	dev = __dev_get_by_index(net, bpm->ifindex);
7808c2ecf20Sopenharmony_ci	if (dev == NULL) {
7818c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Bridge device doesn't exist");
7828c2ecf20Sopenharmony_ci		return -ENODEV;
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	if (!(dev->priv_flags & IFF_EBRIDGE)) {
7868c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Device is not a bridge");
7878c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	*pdev = dev;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (!tb[MDBA_SET_ENTRY]) {
7938c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Missing MDBA_SET_ENTRY attribute");
7948c2ecf20Sopenharmony_ci		return -EINVAL;
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci	if (nla_len(tb[MDBA_SET_ENTRY]) != sizeof(struct br_mdb_entry)) {
7978c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid MDBA_SET_ENTRY attribute length");
7988c2ecf20Sopenharmony_ci		return -EINVAL;
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	entry = nla_data(tb[MDBA_SET_ENTRY]);
8028c2ecf20Sopenharmony_ci	if (!is_valid_mdb_entry(entry, extack))
8038c2ecf20Sopenharmony_ci		return -EINVAL;
8048c2ecf20Sopenharmony_ci	*pentry = entry;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (tb[MDBA_SET_ENTRY_ATTRS]) {
8078c2ecf20Sopenharmony_ci		err = nla_parse_nested(mdb_attrs, MDBE_ATTR_MAX,
8088c2ecf20Sopenharmony_ci				       tb[MDBA_SET_ENTRY_ATTRS],
8098c2ecf20Sopenharmony_ci				       br_mdbe_attrs_pol, extack);
8108c2ecf20Sopenharmony_ci		if (err)
8118c2ecf20Sopenharmony_ci			return err;
8128c2ecf20Sopenharmony_ci		if (mdb_attrs[MDBE_ATTR_SOURCE] &&
8138c2ecf20Sopenharmony_ci		    !is_valid_mdb_source(mdb_attrs[MDBE_ATTR_SOURCE],
8148c2ecf20Sopenharmony_ci					 entry->addr.proto, extack))
8158c2ecf20Sopenharmony_ci			return -EINVAL;
8168c2ecf20Sopenharmony_ci	} else {
8178c2ecf20Sopenharmony_ci		memset(mdb_attrs, 0,
8188c2ecf20Sopenharmony_ci		       sizeof(struct nlattr *) * (MDBE_ATTR_MAX + 1));
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	return 0;
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
8258c2ecf20Sopenharmony_ci			    struct br_mdb_entry *entry,
8268c2ecf20Sopenharmony_ci			    struct nlattr **mdb_attrs,
8278c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
8288c2ecf20Sopenharmony_ci{
8298c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp, *star_mp;
8308c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p;
8318c2ecf20Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
8328c2ecf20Sopenharmony_ci	struct br_ip group, star_group;
8338c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
8348c2ecf20Sopenharmony_ci	u8 filter_mode;
8358c2ecf20Sopenharmony_ci	int err;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	__mdb_entry_to_br_ip(entry, &group, mdb_attrs);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/* host join errors which can happen before creating the group */
8408c2ecf20Sopenharmony_ci	if (!port) {
8418c2ecf20Sopenharmony_ci		/* don't allow any flags for host-joined groups */
8428c2ecf20Sopenharmony_ci		if (entry->state) {
8438c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Flags are not allowed for host groups");
8448c2ecf20Sopenharmony_ci			return -EINVAL;
8458c2ecf20Sopenharmony_ci		}
8468c2ecf20Sopenharmony_ci		if (!br_multicast_is_star_g(&group)) {
8478c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Groups with sources cannot be manually host joined");
8488c2ecf20Sopenharmony_ci			return -EINVAL;
8498c2ecf20Sopenharmony_ci		}
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	mp = br_mdb_ip_get(br, &group);
8538c2ecf20Sopenharmony_ci	if (!mp) {
8548c2ecf20Sopenharmony_ci		mp = br_multicast_new_group(br, &group);
8558c2ecf20Sopenharmony_ci		err = PTR_ERR_OR_ZERO(mp);
8568c2ecf20Sopenharmony_ci		if (err)
8578c2ecf20Sopenharmony_ci			return err;
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	/* host join */
8618c2ecf20Sopenharmony_ci	if (!port) {
8628c2ecf20Sopenharmony_ci		if (mp->host_joined) {
8638c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Group is already joined by host");
8648c2ecf20Sopenharmony_ci			return -EEXIST;
8658c2ecf20Sopenharmony_ci		}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci		br_multicast_host_join(mp, false);
8688c2ecf20Sopenharmony_ci		br_mdb_notify(br->dev, mp, NULL, RTM_NEWMDB);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci		return 0;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	for (pp = &mp->ports;
8748c2ecf20Sopenharmony_ci	     (p = mlock_dereference(*pp, br)) != NULL;
8758c2ecf20Sopenharmony_ci	     pp = &p->next) {
8768c2ecf20Sopenharmony_ci		if (p->key.port == port) {
8778c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Group is already joined by port");
8788c2ecf20Sopenharmony_ci			return -EEXIST;
8798c2ecf20Sopenharmony_ci		}
8808c2ecf20Sopenharmony_ci		if ((unsigned long)p->key.port < (unsigned long)port)
8818c2ecf20Sopenharmony_ci			break;
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	filter_mode = br_multicast_is_star_g(&group) ? MCAST_EXCLUDE :
8858c2ecf20Sopenharmony_ci						       MCAST_INCLUDE;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	p = br_multicast_new_port_group(port, &group, *pp, entry->state, NULL,
8888c2ecf20Sopenharmony_ci					filter_mode, RTPROT_STATIC);
8898c2ecf20Sopenharmony_ci	if (unlikely(!p)) {
8908c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate new port group");
8918c2ecf20Sopenharmony_ci		return -ENOMEM;
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci	rcu_assign_pointer(*pp, p);
8948c2ecf20Sopenharmony_ci	if (entry->state == MDB_TEMPORARY)
8958c2ecf20Sopenharmony_ci		mod_timer(&p->timer, now + br->multicast_membership_interval);
8968c2ecf20Sopenharmony_ci	br_mdb_notify(br->dev, mp, p, RTM_NEWMDB);
8978c2ecf20Sopenharmony_ci	/* if we are adding a new EXCLUDE port group (*,G) it needs to be also
8988c2ecf20Sopenharmony_ci	 * added to all S,G entries for proper replication, if we are adding
8998c2ecf20Sopenharmony_ci	 * a new INCLUDE port (S,G) then all of *,G EXCLUDE ports need to be
9008c2ecf20Sopenharmony_ci	 * added to it for proper replication
9018c2ecf20Sopenharmony_ci	 */
9028c2ecf20Sopenharmony_ci	if (br_multicast_should_handle_mode(br, group.proto)) {
9038c2ecf20Sopenharmony_ci		switch (filter_mode) {
9048c2ecf20Sopenharmony_ci		case MCAST_EXCLUDE:
9058c2ecf20Sopenharmony_ci			br_multicast_star_g_handle_mode(p, MCAST_EXCLUDE);
9068c2ecf20Sopenharmony_ci			break;
9078c2ecf20Sopenharmony_ci		case MCAST_INCLUDE:
9088c2ecf20Sopenharmony_ci			star_group = p->key.addr;
9098c2ecf20Sopenharmony_ci			memset(&star_group.src, 0, sizeof(star_group.src));
9108c2ecf20Sopenharmony_ci			star_mp = br_mdb_ip_get(br, &star_group);
9118c2ecf20Sopenharmony_ci			if (star_mp)
9128c2ecf20Sopenharmony_ci				br_multicast_sg_add_exclude_ports(star_mp, p);
9138c2ecf20Sopenharmony_ci			break;
9148c2ecf20Sopenharmony_ci		}
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	return 0;
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_cistatic int __br_mdb_add(struct net *net, struct net_bridge *br,
9218c2ecf20Sopenharmony_ci			struct net_bridge_port *p,
9228c2ecf20Sopenharmony_ci			struct br_mdb_entry *entry,
9238c2ecf20Sopenharmony_ci			struct nlattr **mdb_attrs,
9248c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	int ret;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
9298c2ecf20Sopenharmony_ci	ret = br_mdb_add_group(br, p, entry, mdb_attrs, extack);
9308c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	return ret;
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
9368c2ecf20Sopenharmony_ci		      struct netlink_ext_ack *extack)
9378c2ecf20Sopenharmony_ci{
9388c2ecf20Sopenharmony_ci	struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1];
9398c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
9408c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
9418c2ecf20Sopenharmony_ci	struct net_bridge_port *p = NULL;
9428c2ecf20Sopenharmony_ci	struct net_device *dev, *pdev;
9438c2ecf20Sopenharmony_ci	struct br_mdb_entry *entry;
9448c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
9458c2ecf20Sopenharmony_ci	struct net_bridge *br;
9468c2ecf20Sopenharmony_ci	int err;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack);
9498c2ecf20Sopenharmony_ci	if (err < 0)
9508c2ecf20Sopenharmony_ci		return err;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	br = netdev_priv(dev);
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if (!netif_running(br->dev)) {
9558c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Bridge device is not running");
9568c2ecf20Sopenharmony_ci		return -EINVAL;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) {
9608c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Bridge's multicast processing is disabled");
9618c2ecf20Sopenharmony_ci		return -EINVAL;
9628c2ecf20Sopenharmony_ci	}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	if (entry->ifindex != br->dev->ifindex) {
9658c2ecf20Sopenharmony_ci		pdev = __dev_get_by_index(net, entry->ifindex);
9668c2ecf20Sopenharmony_ci		if (!pdev) {
9678c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Port net device doesn't exist");
9688c2ecf20Sopenharmony_ci			return -ENODEV;
9698c2ecf20Sopenharmony_ci		}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci		p = br_port_get_rtnl(pdev);
9728c2ecf20Sopenharmony_ci		if (!p) {
9738c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Net device is not a bridge port");
9748c2ecf20Sopenharmony_ci			return -EINVAL;
9758c2ecf20Sopenharmony_ci		}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci		if (p->br != br) {
9788c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Port belongs to a different bridge device");
9798c2ecf20Sopenharmony_ci			return -EINVAL;
9808c2ecf20Sopenharmony_ci		}
9818c2ecf20Sopenharmony_ci		if (p->state == BR_STATE_DISABLED) {
9828c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Port is in disabled state");
9838c2ecf20Sopenharmony_ci			return -EINVAL;
9848c2ecf20Sopenharmony_ci		}
9858c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
9868c2ecf20Sopenharmony_ci	} else {
9878c2ecf20Sopenharmony_ci		vg = br_vlan_group(br);
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	/* If vlan filtering is enabled and VLAN is not specified
9918c2ecf20Sopenharmony_ci	 * install mdb entry on all vlans configured on the port.
9928c2ecf20Sopenharmony_ci	 */
9938c2ecf20Sopenharmony_ci	if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) {
9948c2ecf20Sopenharmony_ci		list_for_each_entry(v, &vg->vlan_list, vlist) {
9958c2ecf20Sopenharmony_ci			entry->vid = v->vid;
9968c2ecf20Sopenharmony_ci			err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack);
9978c2ecf20Sopenharmony_ci			if (err)
9988c2ecf20Sopenharmony_ci				break;
9998c2ecf20Sopenharmony_ci		}
10008c2ecf20Sopenharmony_ci	} else {
10018c2ecf20Sopenharmony_ci		err = __br_mdb_add(net, br, p, entry, mdb_attrs, extack);
10028c2ecf20Sopenharmony_ci	}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	return err;
10058c2ecf20Sopenharmony_ci}
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_cistatic int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry,
10088c2ecf20Sopenharmony_ci			struct nlattr **mdb_attrs)
10098c2ecf20Sopenharmony_ci{
10108c2ecf20Sopenharmony_ci	struct net_bridge_mdb_entry *mp;
10118c2ecf20Sopenharmony_ci	struct net_bridge_port_group *p;
10128c2ecf20Sopenharmony_ci	struct net_bridge_port_group __rcu **pp;
10138c2ecf20Sopenharmony_ci	struct br_ip ip;
10148c2ecf20Sopenharmony_ci	int err = -EINVAL;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED))
10178c2ecf20Sopenharmony_ci		return -EINVAL;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	__mdb_entry_to_br_ip(entry, &ip, mdb_attrs);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	spin_lock_bh(&br->multicast_lock);
10228c2ecf20Sopenharmony_ci	mp = br_mdb_ip_get(br, &ip);
10238c2ecf20Sopenharmony_ci	if (!mp)
10248c2ecf20Sopenharmony_ci		goto unlock;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	/* host leave */
10278c2ecf20Sopenharmony_ci	if (entry->ifindex == mp->br->dev->ifindex && mp->host_joined) {
10288c2ecf20Sopenharmony_ci		br_multicast_host_leave(mp, false);
10298c2ecf20Sopenharmony_ci		err = 0;
10308c2ecf20Sopenharmony_ci		br_mdb_notify(br->dev, mp, NULL, RTM_DELMDB);
10318c2ecf20Sopenharmony_ci		if (!mp->ports && netif_running(br->dev))
10328c2ecf20Sopenharmony_ci			mod_timer(&mp->timer, jiffies);
10338c2ecf20Sopenharmony_ci		goto unlock;
10348c2ecf20Sopenharmony_ci	}
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	for (pp = &mp->ports;
10378c2ecf20Sopenharmony_ci	     (p = mlock_dereference(*pp, br)) != NULL;
10388c2ecf20Sopenharmony_ci	     pp = &p->next) {
10398c2ecf20Sopenharmony_ci		if (!p->key.port || p->key.port->dev->ifindex != entry->ifindex)
10408c2ecf20Sopenharmony_ci			continue;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci		if (p->key.port->state == BR_STATE_DISABLED)
10438c2ecf20Sopenharmony_ci			goto unlock;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci		br_multicast_del_pg(mp, p, pp);
10468c2ecf20Sopenharmony_ci		err = 0;
10478c2ecf20Sopenharmony_ci		break;
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ciunlock:
10518c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->multicast_lock);
10528c2ecf20Sopenharmony_ci	return err;
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_cistatic int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
10568c2ecf20Sopenharmony_ci		      struct netlink_ext_ack *extack)
10578c2ecf20Sopenharmony_ci{
10588c2ecf20Sopenharmony_ci	struct nlattr *mdb_attrs[MDBE_ATTR_MAX + 1];
10598c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
10608c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
10618c2ecf20Sopenharmony_ci	struct net_bridge_port *p = NULL;
10628c2ecf20Sopenharmony_ci	struct net_device *dev, *pdev;
10638c2ecf20Sopenharmony_ci	struct br_mdb_entry *entry;
10648c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
10658c2ecf20Sopenharmony_ci	struct net_bridge *br;
10668c2ecf20Sopenharmony_ci	int err;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	err = br_mdb_parse(skb, nlh, &dev, &entry, mdb_attrs, extack);
10698c2ecf20Sopenharmony_ci	if (err < 0)
10708c2ecf20Sopenharmony_ci		return err;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	br = netdev_priv(dev);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	if (entry->ifindex != br->dev->ifindex) {
10758c2ecf20Sopenharmony_ci		pdev = __dev_get_by_index(net, entry->ifindex);
10768c2ecf20Sopenharmony_ci		if (!pdev)
10778c2ecf20Sopenharmony_ci			return -ENODEV;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci		p = br_port_get_rtnl(pdev);
10808c2ecf20Sopenharmony_ci		if (!p || p->br != br || p->state == BR_STATE_DISABLED)
10818c2ecf20Sopenharmony_ci			return -EINVAL;
10828c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
10838c2ecf20Sopenharmony_ci	} else {
10848c2ecf20Sopenharmony_ci		vg = br_vlan_group(br);
10858c2ecf20Sopenharmony_ci	}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	/* If vlan filtering is enabled and VLAN is not specified
10888c2ecf20Sopenharmony_ci	 * delete mdb entry on all vlans configured on the port.
10898c2ecf20Sopenharmony_ci	 */
10908c2ecf20Sopenharmony_ci	if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) {
10918c2ecf20Sopenharmony_ci		list_for_each_entry(v, &vg->vlan_list, vlist) {
10928c2ecf20Sopenharmony_ci			entry->vid = v->vid;
10938c2ecf20Sopenharmony_ci			err = __br_mdb_del(br, entry, mdb_attrs);
10948c2ecf20Sopenharmony_ci		}
10958c2ecf20Sopenharmony_ci	} else {
10968c2ecf20Sopenharmony_ci		err = __br_mdb_del(br, entry, mdb_attrs);
10978c2ecf20Sopenharmony_ci	}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	return err;
11008c2ecf20Sopenharmony_ci}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_civoid br_mdb_init(void)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, 0);
11058c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_NEWMDB, br_mdb_add, NULL, 0);
11068c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_DELMDB, br_mdb_del, NULL, 0);
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_civoid br_mdb_uninit(void)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	rtnl_unregister(PF_BRIDGE, RTM_GETMDB);
11128c2ecf20Sopenharmony_ci	rtnl_unregister(PF_BRIDGE, RTM_NEWMDB);
11138c2ecf20Sopenharmony_ci	rtnl_unregister(PF_BRIDGE, RTM_DELMDB);
11148c2ecf20Sopenharmony_ci}
1115