162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	Bridge netlink control interface
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Authors:
662306a36Sopenharmony_ci *	Stephen Hemminger		<shemminger@osdl.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/etherdevice.h>
1262306a36Sopenharmony_ci#include <net/rtnetlink.h>
1362306a36Sopenharmony_ci#include <net/net_namespace.h>
1462306a36Sopenharmony_ci#include <net/sock.h>
1562306a36Sopenharmony_ci#include <uapi/linux/if_bridge.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "br_private.h"
1862306a36Sopenharmony_ci#include "br_private_stp.h"
1962306a36Sopenharmony_ci#include "br_private_cfm.h"
2062306a36Sopenharmony_ci#include "br_private_tunnel.h"
2162306a36Sopenharmony_ci#include "br_private_mcast_eht.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
2462306a36Sopenharmony_ci				u32 filter_mask)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct net_bridge_vlan *v;
2762306a36Sopenharmony_ci	u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
2862306a36Sopenharmony_ci	u16 flags, pvid;
2962306a36Sopenharmony_ci	int num_vlans = 0;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (!(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED))
3262306a36Sopenharmony_ci		return 0;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	pvid = br_get_pvid(vg);
3562306a36Sopenharmony_ci	/* Count number of vlan infos */
3662306a36Sopenharmony_ci	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
3762306a36Sopenharmony_ci		flags = 0;
3862306a36Sopenharmony_ci		/* only a context, bridge vlan not activated */
3962306a36Sopenharmony_ci		if (!br_vlan_should_use(v))
4062306a36Sopenharmony_ci			continue;
4162306a36Sopenharmony_ci		if (v->vid == pvid)
4262306a36Sopenharmony_ci			flags |= BRIDGE_VLAN_INFO_PVID;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci		if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
4562306a36Sopenharmony_ci			flags |= BRIDGE_VLAN_INFO_UNTAGGED;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		if (vid_range_start == 0) {
4862306a36Sopenharmony_ci			goto initvars;
4962306a36Sopenharmony_ci		} else if ((v->vid - vid_range_end) == 1 &&
5062306a36Sopenharmony_ci			flags == vid_range_flags) {
5162306a36Sopenharmony_ci			vid_range_end = v->vid;
5262306a36Sopenharmony_ci			continue;
5362306a36Sopenharmony_ci		} else {
5462306a36Sopenharmony_ci			if ((vid_range_end - vid_range_start) > 0)
5562306a36Sopenharmony_ci				num_vlans += 2;
5662306a36Sopenharmony_ci			else
5762306a36Sopenharmony_ci				num_vlans += 1;
5862306a36Sopenharmony_ci		}
5962306a36Sopenharmony_ciinitvars:
6062306a36Sopenharmony_ci		vid_range_start = v->vid;
6162306a36Sopenharmony_ci		vid_range_end = v->vid;
6262306a36Sopenharmony_ci		vid_range_flags = flags;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (vid_range_start != 0) {
6662306a36Sopenharmony_ci		if ((vid_range_end - vid_range_start) > 0)
6762306a36Sopenharmony_ci			num_vlans += 2;
6862306a36Sopenharmony_ci		else
6962306a36Sopenharmony_ci			num_vlans += 1;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return num_vlans;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int br_get_num_vlan_infos(struct net_bridge_vlan_group *vg,
7662306a36Sopenharmony_ci				 u32 filter_mask)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	int num_vlans;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!vg)
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (filter_mask & RTEXT_FILTER_BRVLAN)
8462306a36Sopenharmony_ci		return vg->num_vlans;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	rcu_read_lock();
8762306a36Sopenharmony_ci	num_vlans = __get_num_vlan_infos(vg, filter_mask);
8862306a36Sopenharmony_ci	rcu_read_unlock();
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return num_vlans;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic size_t br_get_link_af_size_filtered(const struct net_device *dev,
9462306a36Sopenharmony_ci					   u32 filter_mask)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg = NULL;
9762306a36Sopenharmony_ci	struct net_bridge_port *p = NULL;
9862306a36Sopenharmony_ci	struct net_bridge *br = NULL;
9962306a36Sopenharmony_ci	u32 num_cfm_peer_mep_infos;
10062306a36Sopenharmony_ci	u32 num_cfm_mep_infos;
10162306a36Sopenharmony_ci	size_t vinfo_sz = 0;
10262306a36Sopenharmony_ci	int num_vlan_infos;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	rcu_read_lock();
10562306a36Sopenharmony_ci	if (netif_is_bridge_port(dev)) {
10662306a36Sopenharmony_ci		p = br_port_get_check_rcu(dev);
10762306a36Sopenharmony_ci		if (p)
10862306a36Sopenharmony_ci			vg = nbp_vlan_group_rcu(p);
10962306a36Sopenharmony_ci	} else if (netif_is_bridge_master(dev)) {
11062306a36Sopenharmony_ci		br = netdev_priv(dev);
11162306a36Sopenharmony_ci		vg = br_vlan_group_rcu(br);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci	num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask);
11462306a36Sopenharmony_ci	rcu_read_unlock();
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (p && (p->flags & BR_VLAN_TUNNEL))
11762306a36Sopenharmony_ci		vinfo_sz += br_get_vlan_tunnel_info_size(vg);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* Each VLAN is returned in bridge_vlan_info along with flags */
12062306a36Sopenharmony_ci	vinfo_sz += num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info));
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (p && vg && (filter_mask & RTEXT_FILTER_MST))
12362306a36Sopenharmony_ci		vinfo_sz += br_mst_info_size(vg);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (!(filter_mask & RTEXT_FILTER_CFM_STATUS))
12662306a36Sopenharmony_ci		return vinfo_sz;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (!br)
12962306a36Sopenharmony_ci		return vinfo_sz;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* CFM status info must be added */
13262306a36Sopenharmony_ci	br_cfm_mep_count(br, &num_cfm_mep_infos);
13362306a36Sopenharmony_ci	br_cfm_peer_mep_count(br, &num_cfm_peer_mep_infos);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	vinfo_sz += nla_total_size(0);	/* IFLA_BRIDGE_CFM */
13662306a36Sopenharmony_ci	/* For each status struct the MEP instance (u32) is added */
13762306a36Sopenharmony_ci	/* MEP instance (u32) + br_cfm_mep_status */
13862306a36Sopenharmony_ci	vinfo_sz += num_cfm_mep_infos *
13962306a36Sopenharmony_ci		     /*IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE */
14062306a36Sopenharmony_ci		    (nla_total_size(sizeof(u32))
14162306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN */
14262306a36Sopenharmony_ci		     + nla_total_size(sizeof(u32))
14362306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN */
14462306a36Sopenharmony_ci		     + nla_total_size(sizeof(u32))
14562306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN */
14662306a36Sopenharmony_ci		     + nla_total_size(sizeof(u32)));
14762306a36Sopenharmony_ci	/* MEP instance (u32) + br_cfm_cc_peer_status */
14862306a36Sopenharmony_ci	vinfo_sz += num_cfm_peer_mep_infos *
14962306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE */
15062306a36Sopenharmony_ci		    (nla_total_size(sizeof(u32))
15162306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID */
15262306a36Sopenharmony_ci		     + nla_total_size(sizeof(u32))
15362306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT */
15462306a36Sopenharmony_ci		     + nla_total_size(sizeof(u32))
15562306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI */
15662306a36Sopenharmony_ci		     + nla_total_size(sizeof(u32))
15762306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE */
15862306a36Sopenharmony_ci		     + nla_total_size(sizeof(u8))
15962306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE */
16062306a36Sopenharmony_ci		     + nla_total_size(sizeof(u8))
16162306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN */
16262306a36Sopenharmony_ci		     + nla_total_size(sizeof(u32))
16362306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN */
16462306a36Sopenharmony_ci		     + nla_total_size(sizeof(u32))
16562306a36Sopenharmony_ci		     /* IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN */
16662306a36Sopenharmony_ci		     + nla_total_size(sizeof(u32)));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return vinfo_sz;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic inline size_t br_port_info_size(void)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	return nla_total_size(1)	/* IFLA_BRPORT_STATE  */
17462306a36Sopenharmony_ci		+ nla_total_size(2)	/* IFLA_BRPORT_PRIORITY */
17562306a36Sopenharmony_ci		+ nla_total_size(4)	/* IFLA_BRPORT_COST */
17662306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_MODE */
17762306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_GUARD */
17862306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_PROTECT */
17962306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_FAST_LEAVE */
18062306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_MCAST_TO_UCAST */
18162306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_LEARNING */
18262306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_UNICAST_FLOOD */
18362306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_MCAST_FLOOD */
18462306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_BCAST_FLOOD */
18562306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_PROXYARP */
18662306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_PROXYARP_WIFI */
18762306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_VLAN_TUNNEL */
18862306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_NEIGH_SUPPRESS */
18962306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_ISOLATED */
19062306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_LOCKED */
19162306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_MAB */
19262306a36Sopenharmony_ci		+ nla_total_size(1)	/* IFLA_BRPORT_NEIGH_VLAN_SUPPRESS */
19362306a36Sopenharmony_ci		+ nla_total_size(sizeof(struct ifla_bridge_id))	/* IFLA_BRPORT_ROOT_ID */
19462306a36Sopenharmony_ci		+ nla_total_size(sizeof(struct ifla_bridge_id))	/* IFLA_BRPORT_BRIDGE_ID */
19562306a36Sopenharmony_ci		+ nla_total_size(sizeof(u16))	/* IFLA_BRPORT_DESIGNATED_PORT */
19662306a36Sopenharmony_ci		+ nla_total_size(sizeof(u16))	/* IFLA_BRPORT_DESIGNATED_COST */
19762306a36Sopenharmony_ci		+ nla_total_size(sizeof(u16))	/* IFLA_BRPORT_ID */
19862306a36Sopenharmony_ci		+ nla_total_size(sizeof(u16))	/* IFLA_BRPORT_NO */
19962306a36Sopenharmony_ci		+ nla_total_size(sizeof(u8))	/* IFLA_BRPORT_TOPOLOGY_CHANGE_ACK */
20062306a36Sopenharmony_ci		+ nla_total_size(sizeof(u8))	/* IFLA_BRPORT_CONFIG_PENDING */
20162306a36Sopenharmony_ci		+ nla_total_size_64bit(sizeof(u64)) /* IFLA_BRPORT_MESSAGE_AGE_TIMER */
20262306a36Sopenharmony_ci		+ nla_total_size_64bit(sizeof(u64)) /* IFLA_BRPORT_FORWARD_DELAY_TIMER */
20362306a36Sopenharmony_ci		+ nla_total_size_64bit(sizeof(u64)) /* IFLA_BRPORT_HOLD_TIMER */
20462306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
20562306a36Sopenharmony_ci		+ nla_total_size(sizeof(u8))	/* IFLA_BRPORT_MULTICAST_ROUTER */
20662306a36Sopenharmony_ci		+ nla_total_size(sizeof(u32))	/* IFLA_BRPORT_MCAST_N_GROUPS */
20762306a36Sopenharmony_ci		+ nla_total_size(sizeof(u32))	/* IFLA_BRPORT_MCAST_MAX_GROUPS */
20862306a36Sopenharmony_ci#endif
20962306a36Sopenharmony_ci		+ nla_total_size(sizeof(u16))	/* IFLA_BRPORT_GROUP_FWD_MASK */
21062306a36Sopenharmony_ci		+ nla_total_size(sizeof(u8))	/* IFLA_BRPORT_MRP_RING_OPEN */
21162306a36Sopenharmony_ci		+ nla_total_size(sizeof(u8))	/* IFLA_BRPORT_MRP_IN_OPEN */
21262306a36Sopenharmony_ci		+ nla_total_size(sizeof(u32))	/* IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT */
21362306a36Sopenharmony_ci		+ nla_total_size(sizeof(u32))	/* IFLA_BRPORT_MCAST_EHT_HOSTS_CNT */
21462306a36Sopenharmony_ci		+ nla_total_size(sizeof(u32))	/* IFLA_BRPORT_BACKUP_NHID */
21562306a36Sopenharmony_ci		+ 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic inline size_t br_nlmsg_size(struct net_device *dev, u32 filter_mask)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ifinfomsg))
22162306a36Sopenharmony_ci		+ nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
22262306a36Sopenharmony_ci		+ nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
22362306a36Sopenharmony_ci		+ nla_total_size(4) /* IFLA_MASTER */
22462306a36Sopenharmony_ci		+ nla_total_size(4) /* IFLA_MTU */
22562306a36Sopenharmony_ci		+ nla_total_size(4) /* IFLA_LINK */
22662306a36Sopenharmony_ci		+ nla_total_size(1) /* IFLA_OPERSTATE */
22762306a36Sopenharmony_ci		+ nla_total_size(br_port_info_size()) /* IFLA_PROTINFO */
22862306a36Sopenharmony_ci		+ nla_total_size(br_get_link_af_size_filtered(dev,
22962306a36Sopenharmony_ci				 filter_mask)) /* IFLA_AF_SPEC */
23062306a36Sopenharmony_ci		+ nla_total_size(4); /* IFLA_BRPORT_BACKUP_PORT */
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int br_port_fill_attrs(struct sk_buff *skb,
23462306a36Sopenharmony_ci			      const struct net_bridge_port *p)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	u8 mode = !!(p->flags & BR_HAIRPIN_MODE);
23762306a36Sopenharmony_ci	struct net_bridge_port *backup_p;
23862306a36Sopenharmony_ci	u64 timerval;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) ||
24162306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) ||
24262306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BRPORT_COST, p->path_cost) ||
24362306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_MODE, mode) ||
24462306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD)) ||
24562306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_PROTECT,
24662306a36Sopenharmony_ci		       !!(p->flags & BR_ROOT_BLOCK)) ||
24762306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE,
24862306a36Sopenharmony_ci		       !!(p->flags & BR_MULTICAST_FAST_LEAVE)) ||
24962306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_MCAST_TO_UCAST,
25062306a36Sopenharmony_ci		       !!(p->flags & BR_MULTICAST_TO_UNICAST)) ||
25162306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) ||
25262306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD,
25362306a36Sopenharmony_ci		       !!(p->flags & BR_FLOOD)) ||
25462306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_MCAST_FLOOD,
25562306a36Sopenharmony_ci		       !!(p->flags & BR_MCAST_FLOOD)) ||
25662306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_BCAST_FLOOD,
25762306a36Sopenharmony_ci		       !!(p->flags & BR_BCAST_FLOOD)) ||
25862306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP)) ||
25962306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_PROXYARP_WIFI,
26062306a36Sopenharmony_ci		       !!(p->flags & BR_PROXYARP_WIFI)) ||
26162306a36Sopenharmony_ci	    nla_put(skb, IFLA_BRPORT_ROOT_ID, sizeof(struct ifla_bridge_id),
26262306a36Sopenharmony_ci		    &p->designated_root) ||
26362306a36Sopenharmony_ci	    nla_put(skb, IFLA_BRPORT_BRIDGE_ID, sizeof(struct ifla_bridge_id),
26462306a36Sopenharmony_ci		    &p->designated_bridge) ||
26562306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_PORT, p->designated_port) ||
26662306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BRPORT_DESIGNATED_COST, p->designated_cost) ||
26762306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BRPORT_ID, p->port_id) ||
26862306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) ||
26962306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
27062306a36Sopenharmony_ci		       p->topology_change_ack) ||
27162306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) ||
27262306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(p->flags &
27362306a36Sopenharmony_ci							BR_VLAN_TUNNEL)) ||
27462306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BRPORT_GROUP_FWD_MASK, p->group_fwd_mask) ||
27562306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_NEIGH_SUPPRESS,
27662306a36Sopenharmony_ci		       !!(p->flags & BR_NEIGH_SUPPRESS)) ||
27762306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_MRP_RING_OPEN, !!(p->flags &
27862306a36Sopenharmony_ci							  BR_MRP_LOST_CONT)) ||
27962306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_MRP_IN_OPEN,
28062306a36Sopenharmony_ci		       !!(p->flags & BR_MRP_LOST_IN_CONT)) ||
28162306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)) ||
28262306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED)) ||
28362306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_MAB, !!(p->flags & BR_PORT_MAB)) ||
28462306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
28562306a36Sopenharmony_ci		       !!(p->flags & BR_NEIGH_VLAN_SUPPRESS)))
28662306a36Sopenharmony_ci		return -EMSGSIZE;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	timerval = br_timer_value(&p->message_age_timer);
28962306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BRPORT_MESSAGE_AGE_TIMER, timerval,
29062306a36Sopenharmony_ci			      IFLA_BRPORT_PAD))
29162306a36Sopenharmony_ci		return -EMSGSIZE;
29262306a36Sopenharmony_ci	timerval = br_timer_value(&p->forward_delay_timer);
29362306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BRPORT_FORWARD_DELAY_TIMER, timerval,
29462306a36Sopenharmony_ci			      IFLA_BRPORT_PAD))
29562306a36Sopenharmony_ci		return -EMSGSIZE;
29662306a36Sopenharmony_ci	timerval = br_timer_value(&p->hold_timer);
29762306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BRPORT_HOLD_TIMER, timerval,
29862306a36Sopenharmony_ci			      IFLA_BRPORT_PAD))
29962306a36Sopenharmony_ci		return -EMSGSIZE;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
30262306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_BRPORT_MULTICAST_ROUTER,
30362306a36Sopenharmony_ci		       p->multicast_ctx.multicast_router) ||
30462306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
30562306a36Sopenharmony_ci			p->multicast_eht_hosts_limit) ||
30662306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
30762306a36Sopenharmony_ci			p->multicast_eht_hosts_cnt) ||
30862306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BRPORT_MCAST_N_GROUPS,
30962306a36Sopenharmony_ci			br_multicast_ngroups_get(&p->multicast_ctx)) ||
31062306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BRPORT_MCAST_MAX_GROUPS,
31162306a36Sopenharmony_ci			br_multicast_ngroups_get_max(&p->multicast_ctx)))
31262306a36Sopenharmony_ci		return -EMSGSIZE;
31362306a36Sopenharmony_ci#endif
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* we might be called only with br->lock */
31662306a36Sopenharmony_ci	rcu_read_lock();
31762306a36Sopenharmony_ci	backup_p = rcu_dereference(p->backup_port);
31862306a36Sopenharmony_ci	if (backup_p)
31962306a36Sopenharmony_ci		nla_put_u32(skb, IFLA_BRPORT_BACKUP_PORT,
32062306a36Sopenharmony_ci			    backup_p->dev->ifindex);
32162306a36Sopenharmony_ci	rcu_read_unlock();
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (p->backup_nhid &&
32462306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BRPORT_BACKUP_NHID, p->backup_nhid))
32562306a36Sopenharmony_ci		return -EMSGSIZE;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return 0;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int br_fill_ifvlaninfo_range(struct sk_buff *skb, u16 vid_start,
33162306a36Sopenharmony_ci				    u16 vid_end, u16 flags)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct  bridge_vlan_info vinfo;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if ((vid_end - vid_start) > 0) {
33662306a36Sopenharmony_ci		/* add range to skb */
33762306a36Sopenharmony_ci		vinfo.vid = vid_start;
33862306a36Sopenharmony_ci		vinfo.flags = flags | BRIDGE_VLAN_INFO_RANGE_BEGIN;
33962306a36Sopenharmony_ci		if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
34062306a36Sopenharmony_ci			    sizeof(vinfo), &vinfo))
34162306a36Sopenharmony_ci			goto nla_put_failure;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		vinfo.vid = vid_end;
34462306a36Sopenharmony_ci		vinfo.flags = flags | BRIDGE_VLAN_INFO_RANGE_END;
34562306a36Sopenharmony_ci		if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
34662306a36Sopenharmony_ci			    sizeof(vinfo), &vinfo))
34762306a36Sopenharmony_ci			goto nla_put_failure;
34862306a36Sopenharmony_ci	} else {
34962306a36Sopenharmony_ci		vinfo.vid = vid_start;
35062306a36Sopenharmony_ci		vinfo.flags = flags;
35162306a36Sopenharmony_ci		if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
35262306a36Sopenharmony_ci			    sizeof(vinfo), &vinfo))
35362306a36Sopenharmony_ci			goto nla_put_failure;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return 0;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cinla_put_failure:
35962306a36Sopenharmony_ci	return -EMSGSIZE;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
36362306a36Sopenharmony_ci					 struct net_bridge_vlan_group *vg)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct net_bridge_vlan *v;
36662306a36Sopenharmony_ci	u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
36762306a36Sopenharmony_ci	u16 flags, pvid;
36862306a36Sopenharmony_ci	int err = 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* Pack IFLA_BRIDGE_VLAN_INFO's for every vlan
37162306a36Sopenharmony_ci	 * and mark vlan info with begin and end flags
37262306a36Sopenharmony_ci	 * if vlaninfo represents a range
37362306a36Sopenharmony_ci	 */
37462306a36Sopenharmony_ci	pvid = br_get_pvid(vg);
37562306a36Sopenharmony_ci	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
37662306a36Sopenharmony_ci		flags = 0;
37762306a36Sopenharmony_ci		if (!br_vlan_should_use(v))
37862306a36Sopenharmony_ci			continue;
37962306a36Sopenharmony_ci		if (v->vid == pvid)
38062306a36Sopenharmony_ci			flags |= BRIDGE_VLAN_INFO_PVID;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
38362306a36Sopenharmony_ci			flags |= BRIDGE_VLAN_INFO_UNTAGGED;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		if (vid_range_start == 0) {
38662306a36Sopenharmony_ci			goto initvars;
38762306a36Sopenharmony_ci		} else if ((v->vid - vid_range_end) == 1 &&
38862306a36Sopenharmony_ci			flags == vid_range_flags) {
38962306a36Sopenharmony_ci			vid_range_end = v->vid;
39062306a36Sopenharmony_ci			continue;
39162306a36Sopenharmony_ci		} else {
39262306a36Sopenharmony_ci			err = br_fill_ifvlaninfo_range(skb, vid_range_start,
39362306a36Sopenharmony_ci						       vid_range_end,
39462306a36Sopenharmony_ci						       vid_range_flags);
39562306a36Sopenharmony_ci			if (err)
39662306a36Sopenharmony_ci				return err;
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ciinitvars:
40062306a36Sopenharmony_ci		vid_range_start = v->vid;
40162306a36Sopenharmony_ci		vid_range_end = v->vid;
40262306a36Sopenharmony_ci		vid_range_flags = flags;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (vid_range_start != 0) {
40662306a36Sopenharmony_ci		/* Call it once more to send any left over vlans */
40762306a36Sopenharmony_ci		err = br_fill_ifvlaninfo_range(skb, vid_range_start,
40862306a36Sopenharmony_ci					       vid_range_end,
40962306a36Sopenharmony_ci					       vid_range_flags);
41062306a36Sopenharmony_ci		if (err)
41162306a36Sopenharmony_ci			return err;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return 0;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic int br_fill_ifvlaninfo(struct sk_buff *skb,
41862306a36Sopenharmony_ci			      struct net_bridge_vlan_group *vg)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct bridge_vlan_info vinfo;
42162306a36Sopenharmony_ci	struct net_bridge_vlan *v;
42262306a36Sopenharmony_ci	u16 pvid;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	pvid = br_get_pvid(vg);
42562306a36Sopenharmony_ci	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
42662306a36Sopenharmony_ci		if (!br_vlan_should_use(v))
42762306a36Sopenharmony_ci			continue;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		vinfo.vid = v->vid;
43062306a36Sopenharmony_ci		vinfo.flags = 0;
43162306a36Sopenharmony_ci		if (v->vid == pvid)
43262306a36Sopenharmony_ci			vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
43562306a36Sopenharmony_ci			vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
43862306a36Sopenharmony_ci			    sizeof(vinfo), &vinfo))
43962306a36Sopenharmony_ci			goto nla_put_failure;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	return 0;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cinla_put_failure:
44562306a36Sopenharmony_ci	return -EMSGSIZE;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/*
44962306a36Sopenharmony_ci * Create one netlink message for one interface
45062306a36Sopenharmony_ci * Contains port and master info as well as carrier and bridge state.
45162306a36Sopenharmony_ci */
45262306a36Sopenharmony_cistatic int br_fill_ifinfo(struct sk_buff *skb,
45362306a36Sopenharmony_ci			  const struct net_bridge_port *port,
45462306a36Sopenharmony_ci			  u32 pid, u32 seq, int event, unsigned int flags,
45562306a36Sopenharmony_ci			  u32 filter_mask, const struct net_device *dev,
45662306a36Sopenharmony_ci			  bool getlink)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
45962306a36Sopenharmony_ci	struct nlattr *af = NULL;
46062306a36Sopenharmony_ci	struct net_bridge *br;
46162306a36Sopenharmony_ci	struct ifinfomsg *hdr;
46262306a36Sopenharmony_ci	struct nlmsghdr *nlh;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (port)
46562306a36Sopenharmony_ci		br = port->br;
46662306a36Sopenharmony_ci	else
46762306a36Sopenharmony_ci		br = netdev_priv(dev);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	br_debug(br, "br_fill_info event %d port %s master %s\n",
47062306a36Sopenharmony_ci		     event, dev->name, br->dev->name);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags);
47362306a36Sopenharmony_ci	if (nlh == NULL)
47462306a36Sopenharmony_ci		return -EMSGSIZE;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	hdr = nlmsg_data(nlh);
47762306a36Sopenharmony_ci	hdr->ifi_family = AF_BRIDGE;
47862306a36Sopenharmony_ci	hdr->__ifi_pad = 0;
47962306a36Sopenharmony_ci	hdr->ifi_type = dev->type;
48062306a36Sopenharmony_ci	hdr->ifi_index = dev->ifindex;
48162306a36Sopenharmony_ci	hdr->ifi_flags = dev_get_flags(dev);
48262306a36Sopenharmony_ci	hdr->ifi_change = 0;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
48562306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_MASTER, br->dev->ifindex) ||
48662306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_MTU, dev->mtu) ||
48762306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_OPERSTATE, operstate) ||
48862306a36Sopenharmony_ci	    (dev->addr_len &&
48962306a36Sopenharmony_ci	     nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
49062306a36Sopenharmony_ci	    (dev->ifindex != dev_get_iflink(dev) &&
49162306a36Sopenharmony_ci	     nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))))
49262306a36Sopenharmony_ci		goto nla_put_failure;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (event == RTM_NEWLINK && port) {
49562306a36Sopenharmony_ci		struct nlattr *nest;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		nest = nla_nest_start(skb, IFLA_PROTINFO);
49862306a36Sopenharmony_ci		if (nest == NULL || br_port_fill_attrs(skb, port) < 0)
49962306a36Sopenharmony_ci			goto nla_put_failure;
50062306a36Sopenharmony_ci		nla_nest_end(skb, nest);
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (filter_mask & (RTEXT_FILTER_BRVLAN |
50462306a36Sopenharmony_ci			   RTEXT_FILTER_BRVLAN_COMPRESSED |
50562306a36Sopenharmony_ci			   RTEXT_FILTER_MRP |
50662306a36Sopenharmony_ci			   RTEXT_FILTER_CFM_CONFIG |
50762306a36Sopenharmony_ci			   RTEXT_FILTER_CFM_STATUS |
50862306a36Sopenharmony_ci			   RTEXT_FILTER_MST)) {
50962306a36Sopenharmony_ci		af = nla_nest_start_noflag(skb, IFLA_AF_SPEC);
51062306a36Sopenharmony_ci		if (!af)
51162306a36Sopenharmony_ci			goto nla_put_failure;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* Check if  the VID information is requested */
51562306a36Sopenharmony_ci	if ((filter_mask & RTEXT_FILTER_BRVLAN) ||
51662306a36Sopenharmony_ci	    (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) {
51762306a36Sopenharmony_ci		struct net_bridge_vlan_group *vg;
51862306a36Sopenharmony_ci		int err;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		/* RCU needed because of the VLAN locking rules (rcu || rtnl) */
52162306a36Sopenharmony_ci		rcu_read_lock();
52262306a36Sopenharmony_ci		if (port)
52362306a36Sopenharmony_ci			vg = nbp_vlan_group_rcu(port);
52462306a36Sopenharmony_ci		else
52562306a36Sopenharmony_ci			vg = br_vlan_group_rcu(br);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		if (!vg || !vg->num_vlans) {
52862306a36Sopenharmony_ci			rcu_read_unlock();
52962306a36Sopenharmony_ci			goto done;
53062306a36Sopenharmony_ci		}
53162306a36Sopenharmony_ci		if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
53262306a36Sopenharmony_ci			err = br_fill_ifvlaninfo_compressed(skb, vg);
53362306a36Sopenharmony_ci		else
53462306a36Sopenharmony_ci			err = br_fill_ifvlaninfo(skb, vg);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		if (port && (port->flags & BR_VLAN_TUNNEL))
53762306a36Sopenharmony_ci			err = br_fill_vlan_tunnel_info(skb, vg);
53862306a36Sopenharmony_ci		rcu_read_unlock();
53962306a36Sopenharmony_ci		if (err)
54062306a36Sopenharmony_ci			goto nla_put_failure;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (filter_mask & RTEXT_FILTER_MRP) {
54462306a36Sopenharmony_ci		int err;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		if (!br_mrp_enabled(br) || port)
54762306a36Sopenharmony_ci			goto done;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		rcu_read_lock();
55062306a36Sopenharmony_ci		err = br_mrp_fill_info(skb, br);
55162306a36Sopenharmony_ci		rcu_read_unlock();
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		if (err)
55462306a36Sopenharmony_ci			goto nla_put_failure;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (filter_mask & (RTEXT_FILTER_CFM_CONFIG | RTEXT_FILTER_CFM_STATUS)) {
55862306a36Sopenharmony_ci		struct nlattr *cfm_nest = NULL;
55962306a36Sopenharmony_ci		int err;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		if (!br_cfm_created(br) || port)
56262306a36Sopenharmony_ci			goto done;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		cfm_nest = nla_nest_start(skb, IFLA_BRIDGE_CFM);
56562306a36Sopenharmony_ci		if (!cfm_nest)
56662306a36Sopenharmony_ci			goto nla_put_failure;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		if (filter_mask & RTEXT_FILTER_CFM_CONFIG) {
56962306a36Sopenharmony_ci			rcu_read_lock();
57062306a36Sopenharmony_ci			err = br_cfm_config_fill_info(skb, br);
57162306a36Sopenharmony_ci			rcu_read_unlock();
57262306a36Sopenharmony_ci			if (err)
57362306a36Sopenharmony_ci				goto nla_put_failure;
57462306a36Sopenharmony_ci		}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		if (filter_mask & RTEXT_FILTER_CFM_STATUS) {
57762306a36Sopenharmony_ci			rcu_read_lock();
57862306a36Sopenharmony_ci			err = br_cfm_status_fill_info(skb, br, getlink);
57962306a36Sopenharmony_ci			rcu_read_unlock();
58062306a36Sopenharmony_ci			if (err)
58162306a36Sopenharmony_ci				goto nla_put_failure;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		nla_nest_end(skb, cfm_nest);
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if ((filter_mask & RTEXT_FILTER_MST) &&
58862306a36Sopenharmony_ci	    br_opt_get(br, BROPT_MST_ENABLED) && port) {
58962306a36Sopenharmony_ci		const struct net_bridge_vlan_group *vg = nbp_vlan_group(port);
59062306a36Sopenharmony_ci		struct nlattr *mst_nest;
59162306a36Sopenharmony_ci		int err;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		if (!vg || !vg->num_vlans)
59462306a36Sopenharmony_ci			goto done;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		mst_nest = nla_nest_start(skb, IFLA_BRIDGE_MST);
59762306a36Sopenharmony_ci		if (!mst_nest)
59862306a36Sopenharmony_ci			goto nla_put_failure;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		err = br_mst_fill_info(skb, vg);
60162306a36Sopenharmony_ci		if (err)
60262306a36Sopenharmony_ci			goto nla_put_failure;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		nla_nest_end(skb, mst_nest);
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cidone:
60862306a36Sopenharmony_ci	if (af) {
60962306a36Sopenharmony_ci		if (nlmsg_get_pos(skb) - (void *)af > nla_attr_size(0))
61062306a36Sopenharmony_ci			nla_nest_end(skb, af);
61162306a36Sopenharmony_ci		else
61262306a36Sopenharmony_ci			nla_nest_cancel(skb, af);
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
61662306a36Sopenharmony_ci	return 0;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cinla_put_failure:
61962306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
62062306a36Sopenharmony_ci	return -EMSGSIZE;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_civoid br_info_notify(int event, const struct net_bridge *br,
62462306a36Sopenharmony_ci		    const struct net_bridge_port *port, u32 filter)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct net_device *dev;
62762306a36Sopenharmony_ci	struct sk_buff *skb;
62862306a36Sopenharmony_ci	int err = -ENOBUFS;
62962306a36Sopenharmony_ci	struct net *net;
63062306a36Sopenharmony_ci	u16 port_no = 0;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if (WARN_ON(!port && !br))
63362306a36Sopenharmony_ci		return;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (port) {
63662306a36Sopenharmony_ci		dev = port->dev;
63762306a36Sopenharmony_ci		br = port->br;
63862306a36Sopenharmony_ci		port_no = port->port_no;
63962306a36Sopenharmony_ci	} else {
64062306a36Sopenharmony_ci		dev = br->dev;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	net = dev_net(dev);
64462306a36Sopenharmony_ci	br_debug(br, "port %u(%s) event %d\n", port_no, dev->name, event);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	skb = nlmsg_new(br_nlmsg_size(dev, filter), GFP_ATOMIC);
64762306a36Sopenharmony_ci	if (skb == NULL)
64862306a36Sopenharmony_ci		goto errout;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	err = br_fill_ifinfo(skb, port, 0, 0, event, 0, filter, dev, false);
65162306a36Sopenharmony_ci	if (err < 0) {
65262306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in br_nlmsg_size() */
65362306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
65462306a36Sopenharmony_ci		kfree_skb(skb);
65562306a36Sopenharmony_ci		goto errout;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
65862306a36Sopenharmony_ci	return;
65962306a36Sopenharmony_cierrout:
66062306a36Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_LINK, err);
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci/* Notify listeners of a change in bridge or port information */
66462306a36Sopenharmony_civoid br_ifinfo_notify(int event, const struct net_bridge *br,
66562306a36Sopenharmony_ci		      const struct net_bridge_port *port)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	u32 filter = RTEXT_FILTER_BRVLAN_COMPRESSED;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	return br_info_notify(event, br, port, filter);
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci/*
67362306a36Sopenharmony_ci * Dump information about all ports, in response to GETLINK
67462306a36Sopenharmony_ci */
67562306a36Sopenharmony_ciint br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
67662306a36Sopenharmony_ci	       struct net_device *dev, u32 filter_mask, int nlflags)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct net_bridge_port *port = br_port_get_rtnl(dev);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN) &&
68162306a36Sopenharmony_ci	    !(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) &&
68262306a36Sopenharmony_ci	    !(filter_mask & RTEXT_FILTER_MRP) &&
68362306a36Sopenharmony_ci	    !(filter_mask & RTEXT_FILTER_CFM_CONFIG) &&
68462306a36Sopenharmony_ci	    !(filter_mask & RTEXT_FILTER_CFM_STATUS))
68562306a36Sopenharmony_ci		return 0;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	return br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, nlflags,
68862306a36Sopenharmony_ci			      filter_mask, dev, true);
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
69262306a36Sopenharmony_ci			int cmd, struct bridge_vlan_info *vinfo, bool *changed,
69362306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	bool curr_change;
69662306a36Sopenharmony_ci	int err = 0;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	switch (cmd) {
69962306a36Sopenharmony_ci	case RTM_SETLINK:
70062306a36Sopenharmony_ci		if (p) {
70162306a36Sopenharmony_ci			/* if the MASTER flag is set this will act on the global
70262306a36Sopenharmony_ci			 * per-VLAN entry as well
70362306a36Sopenharmony_ci			 */
70462306a36Sopenharmony_ci			err = nbp_vlan_add(p, vinfo->vid, vinfo->flags,
70562306a36Sopenharmony_ci					   &curr_change, extack);
70662306a36Sopenharmony_ci		} else {
70762306a36Sopenharmony_ci			vinfo->flags |= BRIDGE_VLAN_INFO_BRENTRY;
70862306a36Sopenharmony_ci			err = br_vlan_add(br, vinfo->vid, vinfo->flags,
70962306a36Sopenharmony_ci					  &curr_change, extack);
71062306a36Sopenharmony_ci		}
71162306a36Sopenharmony_ci		if (curr_change)
71262306a36Sopenharmony_ci			*changed = true;
71362306a36Sopenharmony_ci		break;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	case RTM_DELLINK:
71662306a36Sopenharmony_ci		if (p) {
71762306a36Sopenharmony_ci			if (!nbp_vlan_delete(p, vinfo->vid))
71862306a36Sopenharmony_ci				*changed = true;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci			if ((vinfo->flags & BRIDGE_VLAN_INFO_MASTER) &&
72162306a36Sopenharmony_ci			    !br_vlan_delete(p->br, vinfo->vid))
72262306a36Sopenharmony_ci				*changed = true;
72362306a36Sopenharmony_ci		} else if (!br_vlan_delete(br, vinfo->vid)) {
72462306a36Sopenharmony_ci			*changed = true;
72562306a36Sopenharmony_ci		}
72662306a36Sopenharmony_ci		break;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return err;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ciint br_process_vlan_info(struct net_bridge *br,
73362306a36Sopenharmony_ci			 struct net_bridge_port *p, int cmd,
73462306a36Sopenharmony_ci			 struct bridge_vlan_info *vinfo_curr,
73562306a36Sopenharmony_ci			 struct bridge_vlan_info **vinfo_last,
73662306a36Sopenharmony_ci			 bool *changed,
73762306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	int err, rtm_cmd;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (!br_vlan_valid_id(vinfo_curr->vid, extack))
74262306a36Sopenharmony_ci		return -EINVAL;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* needed for vlan-only NEWVLAN/DELVLAN notifications */
74562306a36Sopenharmony_ci	rtm_cmd = br_afspec_cmd_to_rtm(cmd);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	if (vinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
74862306a36Sopenharmony_ci		if (!br_vlan_valid_range(vinfo_curr, *vinfo_last, extack))
74962306a36Sopenharmony_ci			return -EINVAL;
75062306a36Sopenharmony_ci		*vinfo_last = vinfo_curr;
75162306a36Sopenharmony_ci		return 0;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (*vinfo_last) {
75562306a36Sopenharmony_ci		struct bridge_vlan_info tmp_vinfo;
75662306a36Sopenharmony_ci		int v, v_change_start = 0;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		if (!br_vlan_valid_range(vinfo_curr, *vinfo_last, extack))
75962306a36Sopenharmony_ci			return -EINVAL;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		memcpy(&tmp_vinfo, *vinfo_last,
76262306a36Sopenharmony_ci		       sizeof(struct bridge_vlan_info));
76362306a36Sopenharmony_ci		for (v = (*vinfo_last)->vid; v <= vinfo_curr->vid; v++) {
76462306a36Sopenharmony_ci			bool curr_change = false;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci			tmp_vinfo.vid = v;
76762306a36Sopenharmony_ci			err = br_vlan_info(br, p, cmd, &tmp_vinfo, &curr_change,
76862306a36Sopenharmony_ci					   extack);
76962306a36Sopenharmony_ci			if (err)
77062306a36Sopenharmony_ci				break;
77162306a36Sopenharmony_ci			if (curr_change) {
77262306a36Sopenharmony_ci				*changed = curr_change;
77362306a36Sopenharmony_ci				if (!v_change_start)
77462306a36Sopenharmony_ci					v_change_start = v;
77562306a36Sopenharmony_ci			} else {
77662306a36Sopenharmony_ci				/* nothing to notify yet */
77762306a36Sopenharmony_ci				if (!v_change_start)
77862306a36Sopenharmony_ci					continue;
77962306a36Sopenharmony_ci				br_vlan_notify(br, p, v_change_start,
78062306a36Sopenharmony_ci					       v - 1, rtm_cmd);
78162306a36Sopenharmony_ci				v_change_start = 0;
78262306a36Sopenharmony_ci			}
78362306a36Sopenharmony_ci			cond_resched();
78462306a36Sopenharmony_ci		}
78562306a36Sopenharmony_ci		/* v_change_start is set only if the last/whole range changed */
78662306a36Sopenharmony_ci		if (v_change_start)
78762306a36Sopenharmony_ci			br_vlan_notify(br, p, v_change_start,
78862306a36Sopenharmony_ci				       v - 1, rtm_cmd);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		*vinfo_last = NULL;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		return err;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	err = br_vlan_info(br, p, cmd, vinfo_curr, changed, extack);
79662306a36Sopenharmony_ci	if (*changed)
79762306a36Sopenharmony_ci		br_vlan_notify(br, p, vinfo_curr->vid, 0, rtm_cmd);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return err;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int br_afspec(struct net_bridge *br,
80362306a36Sopenharmony_ci		     struct net_bridge_port *p,
80462306a36Sopenharmony_ci		     struct nlattr *af_spec,
80562306a36Sopenharmony_ci		     int cmd, bool *changed,
80662306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct bridge_vlan_info *vinfo_curr = NULL;
80962306a36Sopenharmony_ci	struct bridge_vlan_info *vinfo_last = NULL;
81062306a36Sopenharmony_ci	struct nlattr *attr;
81162306a36Sopenharmony_ci	struct vtunnel_info tinfo_last = {};
81262306a36Sopenharmony_ci	struct vtunnel_info tinfo_curr = {};
81362306a36Sopenharmony_ci	int err = 0, rem;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	nla_for_each_nested(attr, af_spec, rem) {
81662306a36Sopenharmony_ci		err = 0;
81762306a36Sopenharmony_ci		switch (nla_type(attr)) {
81862306a36Sopenharmony_ci		case IFLA_BRIDGE_VLAN_TUNNEL_INFO:
81962306a36Sopenharmony_ci			if (!p || !(p->flags & BR_VLAN_TUNNEL))
82062306a36Sopenharmony_ci				return -EINVAL;
82162306a36Sopenharmony_ci			err = br_parse_vlan_tunnel_info(attr, &tinfo_curr);
82262306a36Sopenharmony_ci			if (err)
82362306a36Sopenharmony_ci				return err;
82462306a36Sopenharmony_ci			err = br_process_vlan_tunnel_info(br, p, cmd,
82562306a36Sopenharmony_ci							  &tinfo_curr,
82662306a36Sopenharmony_ci							  &tinfo_last,
82762306a36Sopenharmony_ci							  changed);
82862306a36Sopenharmony_ci			if (err)
82962306a36Sopenharmony_ci				return err;
83062306a36Sopenharmony_ci			break;
83162306a36Sopenharmony_ci		case IFLA_BRIDGE_VLAN_INFO:
83262306a36Sopenharmony_ci			if (nla_len(attr) != sizeof(struct bridge_vlan_info))
83362306a36Sopenharmony_ci				return -EINVAL;
83462306a36Sopenharmony_ci			vinfo_curr = nla_data(attr);
83562306a36Sopenharmony_ci			err = br_process_vlan_info(br, p, cmd, vinfo_curr,
83662306a36Sopenharmony_ci						   &vinfo_last, changed,
83762306a36Sopenharmony_ci						   extack);
83862306a36Sopenharmony_ci			if (err)
83962306a36Sopenharmony_ci				return err;
84062306a36Sopenharmony_ci			break;
84162306a36Sopenharmony_ci		case IFLA_BRIDGE_MRP:
84262306a36Sopenharmony_ci			err = br_mrp_parse(br, p, attr, cmd, extack);
84362306a36Sopenharmony_ci			if (err)
84462306a36Sopenharmony_ci				return err;
84562306a36Sopenharmony_ci			break;
84662306a36Sopenharmony_ci		case IFLA_BRIDGE_CFM:
84762306a36Sopenharmony_ci			err = br_cfm_parse(br, p, attr, cmd, extack);
84862306a36Sopenharmony_ci			if (err)
84962306a36Sopenharmony_ci				return err;
85062306a36Sopenharmony_ci			break;
85162306a36Sopenharmony_ci		case IFLA_BRIDGE_MST:
85262306a36Sopenharmony_ci			if (!p) {
85362306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack,
85462306a36Sopenharmony_ci					       "MST states can only be set on bridge ports");
85562306a36Sopenharmony_ci				return -EINVAL;
85662306a36Sopenharmony_ci			}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci			if (cmd != RTM_SETLINK) {
85962306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack,
86062306a36Sopenharmony_ci					       "MST states can only be set through RTM_SETLINK");
86162306a36Sopenharmony_ci				return -EINVAL;
86262306a36Sopenharmony_ci			}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci			err = br_mst_process(p, attr, extack);
86562306a36Sopenharmony_ci			if (err)
86662306a36Sopenharmony_ci				return err;
86762306a36Sopenharmony_ci			break;
86862306a36Sopenharmony_ci		}
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	return err;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = {
87562306a36Sopenharmony_ci	[IFLA_BRPORT_UNSPEC]	= { .strict_start_type =
87662306a36Sopenharmony_ci					IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT + 1 },
87762306a36Sopenharmony_ci	[IFLA_BRPORT_STATE]	= { .type = NLA_U8 },
87862306a36Sopenharmony_ci	[IFLA_BRPORT_COST]	= { .type = NLA_U32 },
87962306a36Sopenharmony_ci	[IFLA_BRPORT_PRIORITY]	= { .type = NLA_U16 },
88062306a36Sopenharmony_ci	[IFLA_BRPORT_MODE]	= { .type = NLA_U8 },
88162306a36Sopenharmony_ci	[IFLA_BRPORT_GUARD]	= { .type = NLA_U8 },
88262306a36Sopenharmony_ci	[IFLA_BRPORT_PROTECT]	= { .type = NLA_U8 },
88362306a36Sopenharmony_ci	[IFLA_BRPORT_FAST_LEAVE]= { .type = NLA_U8 },
88462306a36Sopenharmony_ci	[IFLA_BRPORT_LEARNING]	= { .type = NLA_U8 },
88562306a36Sopenharmony_ci	[IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
88662306a36Sopenharmony_ci	[IFLA_BRPORT_PROXYARP]	= { .type = NLA_U8 },
88762306a36Sopenharmony_ci	[IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 },
88862306a36Sopenharmony_ci	[IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NLA_U8 },
88962306a36Sopenharmony_ci	[IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NLA_U8 },
89062306a36Sopenharmony_ci	[IFLA_BRPORT_MCAST_FLOOD] = { .type = NLA_U8 },
89162306a36Sopenharmony_ci	[IFLA_BRPORT_BCAST_FLOOD] = { .type = NLA_U8 },
89262306a36Sopenharmony_ci	[IFLA_BRPORT_VLAN_TUNNEL] = { .type = NLA_U8 },
89362306a36Sopenharmony_ci	[IFLA_BRPORT_GROUP_FWD_MASK] = { .type = NLA_U16 },
89462306a36Sopenharmony_ci	[IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NLA_U8 },
89562306a36Sopenharmony_ci	[IFLA_BRPORT_ISOLATED]	= { .type = NLA_U8 },
89662306a36Sopenharmony_ci	[IFLA_BRPORT_LOCKED] = { .type = NLA_U8 },
89762306a36Sopenharmony_ci	[IFLA_BRPORT_MAB] = { .type = NLA_U8 },
89862306a36Sopenharmony_ci	[IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 },
89962306a36Sopenharmony_ci	[IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 },
90062306a36Sopenharmony_ci	[IFLA_BRPORT_MCAST_N_GROUPS] = { .type = NLA_REJECT },
90162306a36Sopenharmony_ci	[IFLA_BRPORT_MCAST_MAX_GROUPS] = { .type = NLA_U32 },
90262306a36Sopenharmony_ci	[IFLA_BRPORT_NEIGH_VLAN_SUPPRESS] = NLA_POLICY_MAX(NLA_U8, 1),
90362306a36Sopenharmony_ci	[IFLA_BRPORT_BACKUP_NHID] = { .type = NLA_U32 },
90462306a36Sopenharmony_ci};
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci/* Change the state of the port and notify spanning tree */
90762306a36Sopenharmony_cistatic int br_set_port_state(struct net_bridge_port *p, u8 state)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	if (state > BR_STATE_BLOCKING)
91062306a36Sopenharmony_ci		return -EINVAL;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	/* if kernel STP is running, don't allow changes */
91362306a36Sopenharmony_ci	if (p->br->stp_enabled == BR_KERNEL_STP)
91462306a36Sopenharmony_ci		return -EBUSY;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* if device is not up, change is not allowed
91762306a36Sopenharmony_ci	 * if link is not present, only allowable state is disabled
91862306a36Sopenharmony_ci	 */
91962306a36Sopenharmony_ci	if (!netif_running(p->dev) ||
92062306a36Sopenharmony_ci	    (!netif_oper_up(p->dev) && state != BR_STATE_DISABLED))
92162306a36Sopenharmony_ci		return -ENETDOWN;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	br_set_state(p, state);
92462306a36Sopenharmony_ci	br_port_state_selection(p->br);
92562306a36Sopenharmony_ci	return 0;
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci/* Set/clear or port flags based on attribute */
92962306a36Sopenharmony_cistatic void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
93062306a36Sopenharmony_ci			     int attrtype, unsigned long mask)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	if (!tb[attrtype])
93362306a36Sopenharmony_ci		return;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (nla_get_u8(tb[attrtype]))
93662306a36Sopenharmony_ci		p->flags |= mask;
93762306a36Sopenharmony_ci	else
93862306a36Sopenharmony_ci		p->flags &= ~mask;
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci/* Process bridge protocol info on port */
94262306a36Sopenharmony_cistatic int br_setport(struct net_bridge_port *p, struct nlattr *tb[],
94362306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	unsigned long old_flags, changed_mask;
94662306a36Sopenharmony_ci	bool br_vlan_tunnel_old;
94762306a36Sopenharmony_ci	int err;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	old_flags = p->flags;
95062306a36Sopenharmony_ci	br_vlan_tunnel_old = (old_flags & BR_VLAN_TUNNEL) ? true : false;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
95362306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
95462306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE,
95562306a36Sopenharmony_ci			 BR_MULTICAST_FAST_LEAVE);
95662306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK);
95762306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
95862306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
95962306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD);
96062306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST,
96162306a36Sopenharmony_ci			 BR_MULTICAST_TO_UNICAST);
96262306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD);
96362306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
96462306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI);
96562306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL);
96662306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, BR_NEIGH_SUPPRESS);
96762306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED);
96862306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED);
96962306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_MAB, BR_PORT_MAB);
97062306a36Sopenharmony_ci	br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
97162306a36Sopenharmony_ci			 BR_NEIGH_VLAN_SUPPRESS);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if ((p->flags & BR_PORT_MAB) &&
97462306a36Sopenharmony_ci	    (!(p->flags & BR_PORT_LOCKED) || !(p->flags & BR_LEARNING))) {
97562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Bridge port must be locked and have learning enabled when MAB is enabled");
97662306a36Sopenharmony_ci		p->flags = old_flags;
97762306a36Sopenharmony_ci		return -EINVAL;
97862306a36Sopenharmony_ci	} else if (!(p->flags & BR_PORT_MAB) && (old_flags & BR_PORT_MAB)) {
97962306a36Sopenharmony_ci		struct net_bridge_fdb_flush_desc desc = {
98062306a36Sopenharmony_ci			.flags = BIT(BR_FDB_LOCKED),
98162306a36Sopenharmony_ci			.flags_mask = BIT(BR_FDB_LOCKED),
98262306a36Sopenharmony_ci			.port_ifindex = p->dev->ifindex,
98362306a36Sopenharmony_ci		};
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		br_fdb_flush(p->br, &desc);
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	changed_mask = old_flags ^ p->flags;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	err = br_switchdev_set_port_flag(p, p->flags, changed_mask, extack);
99162306a36Sopenharmony_ci	if (err) {
99262306a36Sopenharmony_ci		p->flags = old_flags;
99362306a36Sopenharmony_ci		return err;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (br_vlan_tunnel_old && !(p->flags & BR_VLAN_TUNNEL))
99762306a36Sopenharmony_ci		nbp_vlan_tunnel_info_flush(p);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	br_port_flags_change(p, changed_mask);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_COST]) {
100262306a36Sopenharmony_ci		err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
100362306a36Sopenharmony_ci		if (err)
100462306a36Sopenharmony_ci			return err;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_PRIORITY]) {
100862306a36Sopenharmony_ci		err = br_stp_set_port_priority(p, nla_get_u16(tb[IFLA_BRPORT_PRIORITY]));
100962306a36Sopenharmony_ci		if (err)
101062306a36Sopenharmony_ci			return err;
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_STATE]) {
101462306a36Sopenharmony_ci		err = br_set_port_state(p, nla_get_u8(tb[IFLA_BRPORT_STATE]));
101562306a36Sopenharmony_ci		if (err)
101662306a36Sopenharmony_ci			return err;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_FLUSH])
102062306a36Sopenharmony_ci		br_fdb_delete_by_port(p->br, p, 0, 0);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
102362306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_MULTICAST_ROUTER]) {
102462306a36Sopenharmony_ci		u8 mcast_router = nla_get_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER]);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		err = br_multicast_set_port_router(&p->multicast_ctx,
102762306a36Sopenharmony_ci						   mcast_router);
102862306a36Sopenharmony_ci		if (err)
102962306a36Sopenharmony_ci			return err;
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT]) {
103362306a36Sopenharmony_ci		u32 hlimit;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		hlimit = nla_get_u32(tb[IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT]);
103662306a36Sopenharmony_ci		err = br_multicast_eht_set_hosts_limit(p, hlimit);
103762306a36Sopenharmony_ci		if (err)
103862306a36Sopenharmony_ci			return err;
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_MCAST_MAX_GROUPS]) {
104262306a36Sopenharmony_ci		u32 max_groups;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci		max_groups = nla_get_u32(tb[IFLA_BRPORT_MCAST_MAX_GROUPS]);
104562306a36Sopenharmony_ci		br_multicast_ngroups_set_max(&p->multicast_ctx, max_groups);
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci#endif
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) {
105062306a36Sopenharmony_ci		u16 fwd_mask = nla_get_u16(tb[IFLA_BRPORT_GROUP_FWD_MASK]);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		if (fwd_mask & BR_GROUPFWD_MACPAUSE)
105362306a36Sopenharmony_ci			return -EINVAL;
105462306a36Sopenharmony_ci		p->group_fwd_mask = fwd_mask;
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_BACKUP_PORT]) {
105862306a36Sopenharmony_ci		struct net_device *backup_dev = NULL;
105962306a36Sopenharmony_ci		u32 backup_ifindex;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci		backup_ifindex = nla_get_u32(tb[IFLA_BRPORT_BACKUP_PORT]);
106262306a36Sopenharmony_ci		if (backup_ifindex) {
106362306a36Sopenharmony_ci			backup_dev = __dev_get_by_index(dev_net(p->dev),
106462306a36Sopenharmony_ci							backup_ifindex);
106562306a36Sopenharmony_ci			if (!backup_dev)
106662306a36Sopenharmony_ci				return -ENOENT;
106762306a36Sopenharmony_ci		}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci		err = nbp_backup_change(p, backup_dev);
107062306a36Sopenharmony_ci		if (err)
107162306a36Sopenharmony_ci			return err;
107262306a36Sopenharmony_ci	}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	if (tb[IFLA_BRPORT_BACKUP_NHID]) {
107562306a36Sopenharmony_ci		u32 backup_nhid = nla_get_u32(tb[IFLA_BRPORT_BACKUP_NHID]);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci		WRITE_ONCE(p->backup_nhid, backup_nhid);
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	return 0;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci/* Change state and parameters on port. */
108462306a36Sopenharmony_ciint br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags,
108562306a36Sopenharmony_ci	       struct netlink_ext_ack *extack)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	struct net_bridge *br = (struct net_bridge *)netdev_priv(dev);
108862306a36Sopenharmony_ci	struct nlattr *tb[IFLA_BRPORT_MAX + 1];
108962306a36Sopenharmony_ci	struct net_bridge_port *p;
109062306a36Sopenharmony_ci	struct nlattr *protinfo;
109162306a36Sopenharmony_ci	struct nlattr *afspec;
109262306a36Sopenharmony_ci	bool changed = false;
109362306a36Sopenharmony_ci	int err = 0;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_PROTINFO);
109662306a36Sopenharmony_ci	afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
109762306a36Sopenharmony_ci	if (!protinfo && !afspec)
109862306a36Sopenharmony_ci		return 0;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	p = br_port_get_rtnl(dev);
110162306a36Sopenharmony_ci	/* We want to accept dev as bridge itself if the AF_SPEC
110262306a36Sopenharmony_ci	 * is set to see if someone is setting vlan info on the bridge
110362306a36Sopenharmony_ci	 */
110462306a36Sopenharmony_ci	if (!p && !afspec)
110562306a36Sopenharmony_ci		return -EINVAL;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	if (p && protinfo) {
110862306a36Sopenharmony_ci		if (protinfo->nla_type & NLA_F_NESTED) {
110962306a36Sopenharmony_ci			err = nla_parse_nested_deprecated(tb, IFLA_BRPORT_MAX,
111062306a36Sopenharmony_ci							  protinfo,
111162306a36Sopenharmony_ci							  br_port_policy,
111262306a36Sopenharmony_ci							  NULL);
111362306a36Sopenharmony_ci			if (err)
111462306a36Sopenharmony_ci				return err;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci			spin_lock_bh(&p->br->lock);
111762306a36Sopenharmony_ci			err = br_setport(p, tb, extack);
111862306a36Sopenharmony_ci			spin_unlock_bh(&p->br->lock);
111962306a36Sopenharmony_ci		} else {
112062306a36Sopenharmony_ci			/* Binary compatibility with old RSTP */
112162306a36Sopenharmony_ci			if (nla_len(protinfo) < sizeof(u8))
112262306a36Sopenharmony_ci				return -EINVAL;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci			spin_lock_bh(&p->br->lock);
112562306a36Sopenharmony_ci			err = br_set_port_state(p, nla_get_u8(protinfo));
112662306a36Sopenharmony_ci			spin_unlock_bh(&p->br->lock);
112762306a36Sopenharmony_ci		}
112862306a36Sopenharmony_ci		if (err)
112962306a36Sopenharmony_ci			goto out;
113062306a36Sopenharmony_ci		changed = true;
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	if (afspec)
113462306a36Sopenharmony_ci		err = br_afspec(br, p, afspec, RTM_SETLINK, &changed, extack);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	if (changed)
113762306a36Sopenharmony_ci		br_ifinfo_notify(RTM_NEWLINK, br, p);
113862306a36Sopenharmony_ciout:
113962306a36Sopenharmony_ci	return err;
114062306a36Sopenharmony_ci}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci/* Delete port information */
114362306a36Sopenharmony_ciint br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct net_bridge *br = (struct net_bridge *)netdev_priv(dev);
114662306a36Sopenharmony_ci	struct net_bridge_port *p;
114762306a36Sopenharmony_ci	struct nlattr *afspec;
114862306a36Sopenharmony_ci	bool changed = false;
114962306a36Sopenharmony_ci	int err = 0;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
115262306a36Sopenharmony_ci	if (!afspec)
115362306a36Sopenharmony_ci		return 0;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	p = br_port_get_rtnl(dev);
115662306a36Sopenharmony_ci	/* We want to accept dev as bridge itself as well */
115762306a36Sopenharmony_ci	if (!p && !netif_is_bridge_master(dev))
115862306a36Sopenharmony_ci		return -EINVAL;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	err = br_afspec(br, p, afspec, RTM_DELLINK, &changed, NULL);
116162306a36Sopenharmony_ci	if (changed)
116262306a36Sopenharmony_ci		/* Send RTM_NEWLINK because userspace
116362306a36Sopenharmony_ci		 * expects RTM_NEWLINK for vlan dels
116462306a36Sopenharmony_ci		 */
116562306a36Sopenharmony_ci		br_ifinfo_notify(RTM_NEWLINK, br, p);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	return err;
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic int br_validate(struct nlattr *tb[], struct nlattr *data[],
117162306a36Sopenharmony_ci		       struct netlink_ext_ack *extack)
117262306a36Sopenharmony_ci{
117362306a36Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
117462306a36Sopenharmony_ci		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
117562306a36Sopenharmony_ci			return -EINVAL;
117662306a36Sopenharmony_ci		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
117762306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	if (!data)
118162306a36Sopenharmony_ci		return 0;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_VLAN_FILTERING
118462306a36Sopenharmony_ci	if (data[IFLA_BR_VLAN_PROTOCOL] &&
118562306a36Sopenharmony_ci	    !eth_type_vlan(nla_get_be16(data[IFLA_BR_VLAN_PROTOCOL])))
118662306a36Sopenharmony_ci		return -EPROTONOSUPPORT;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	if (data[IFLA_BR_VLAN_DEFAULT_PVID]) {
118962306a36Sopenharmony_ci		__u16 defpvid = nla_get_u16(data[IFLA_BR_VLAN_DEFAULT_PVID]);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci		if (defpvid >= VLAN_VID_MASK)
119262306a36Sopenharmony_ci			return -EINVAL;
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci#endif
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	return 0;
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic int br_port_slave_changelink(struct net_device *brdev,
120062306a36Sopenharmony_ci				    struct net_device *dev,
120162306a36Sopenharmony_ci				    struct nlattr *tb[],
120262306a36Sopenharmony_ci				    struct nlattr *data[],
120362306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(brdev);
120662306a36Sopenharmony_ci	int ret;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (!data)
120962306a36Sopenharmony_ci		return 0;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	spin_lock_bh(&br->lock);
121262306a36Sopenharmony_ci	ret = br_setport(br_port_get_rtnl(dev), data, extack);
121362306a36Sopenharmony_ci	spin_unlock_bh(&br->lock);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	return ret;
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_cistatic int br_port_fill_slave_info(struct sk_buff *skb,
121962306a36Sopenharmony_ci				   const struct net_device *brdev,
122062306a36Sopenharmony_ci				   const struct net_device *dev)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	return br_port_fill_attrs(skb, br_port_get_rtnl(dev));
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cistatic size_t br_port_get_slave_size(const struct net_device *brdev,
122662306a36Sopenharmony_ci				     const struct net_device *dev)
122762306a36Sopenharmony_ci{
122862306a36Sopenharmony_ci	return br_port_info_size();
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_cistatic const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
123262306a36Sopenharmony_ci	[IFLA_BR_FORWARD_DELAY]	= { .type = NLA_U32 },
123362306a36Sopenharmony_ci	[IFLA_BR_HELLO_TIME]	= { .type = NLA_U32 },
123462306a36Sopenharmony_ci	[IFLA_BR_MAX_AGE]	= { .type = NLA_U32 },
123562306a36Sopenharmony_ci	[IFLA_BR_AGEING_TIME] = { .type = NLA_U32 },
123662306a36Sopenharmony_ci	[IFLA_BR_STP_STATE] = { .type = NLA_U32 },
123762306a36Sopenharmony_ci	[IFLA_BR_PRIORITY] = { .type = NLA_U16 },
123862306a36Sopenharmony_ci	[IFLA_BR_VLAN_FILTERING] = { .type = NLA_U8 },
123962306a36Sopenharmony_ci	[IFLA_BR_VLAN_PROTOCOL] = { .type = NLA_U16 },
124062306a36Sopenharmony_ci	[IFLA_BR_GROUP_FWD_MASK] = { .type = NLA_U16 },
124162306a36Sopenharmony_ci	[IFLA_BR_GROUP_ADDR] = { .type = NLA_BINARY,
124262306a36Sopenharmony_ci				 .len  = ETH_ALEN },
124362306a36Sopenharmony_ci	[IFLA_BR_MCAST_ROUTER] = { .type = NLA_U8 },
124462306a36Sopenharmony_ci	[IFLA_BR_MCAST_SNOOPING] = { .type = NLA_U8 },
124562306a36Sopenharmony_ci	[IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NLA_U8 },
124662306a36Sopenharmony_ci	[IFLA_BR_MCAST_QUERIER] = { .type = NLA_U8 },
124762306a36Sopenharmony_ci	[IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NLA_U32 },
124862306a36Sopenharmony_ci	[IFLA_BR_MCAST_HASH_MAX] = { .type = NLA_U32 },
124962306a36Sopenharmony_ci	[IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NLA_U32 },
125062306a36Sopenharmony_ci	[IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NLA_U32 },
125162306a36Sopenharmony_ci	[IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NLA_U64 },
125262306a36Sopenharmony_ci	[IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NLA_U64 },
125362306a36Sopenharmony_ci	[IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NLA_U64 },
125462306a36Sopenharmony_ci	[IFLA_BR_MCAST_QUERY_INTVL] = { .type = NLA_U64 },
125562306a36Sopenharmony_ci	[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NLA_U64 },
125662306a36Sopenharmony_ci	[IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NLA_U64 },
125762306a36Sopenharmony_ci	[IFLA_BR_NF_CALL_IPTABLES] = { .type = NLA_U8 },
125862306a36Sopenharmony_ci	[IFLA_BR_NF_CALL_IP6TABLES] = { .type = NLA_U8 },
125962306a36Sopenharmony_ci	[IFLA_BR_NF_CALL_ARPTABLES] = { .type = NLA_U8 },
126062306a36Sopenharmony_ci	[IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NLA_U16 },
126162306a36Sopenharmony_ci	[IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 },
126262306a36Sopenharmony_ci	[IFLA_BR_MCAST_STATS_ENABLED] = { .type = NLA_U8 },
126362306a36Sopenharmony_ci	[IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
126462306a36Sopenharmony_ci	[IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 },
126562306a36Sopenharmony_ci	[IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NLA_U8 },
126662306a36Sopenharmony_ci	[IFLA_BR_MULTI_BOOLOPT] =
126762306a36Sopenharmony_ci		NLA_POLICY_EXACT_LEN(sizeof(struct br_boolopt_multi)),
126862306a36Sopenharmony_ci};
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_cistatic int br_changelink(struct net_device *brdev, struct nlattr *tb[],
127162306a36Sopenharmony_ci			 struct nlattr *data[],
127262306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(brdev);
127562306a36Sopenharmony_ci	int err;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	if (!data)
127862306a36Sopenharmony_ci		return 0;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	if (data[IFLA_BR_FORWARD_DELAY]) {
128162306a36Sopenharmony_ci		err = br_set_forward_delay(br, nla_get_u32(data[IFLA_BR_FORWARD_DELAY]));
128262306a36Sopenharmony_ci		if (err)
128362306a36Sopenharmony_ci			return err;
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	if (data[IFLA_BR_HELLO_TIME]) {
128762306a36Sopenharmony_ci		err = br_set_hello_time(br, nla_get_u32(data[IFLA_BR_HELLO_TIME]));
128862306a36Sopenharmony_ci		if (err)
128962306a36Sopenharmony_ci			return err;
129062306a36Sopenharmony_ci	}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	if (data[IFLA_BR_MAX_AGE]) {
129362306a36Sopenharmony_ci		err = br_set_max_age(br, nla_get_u32(data[IFLA_BR_MAX_AGE]));
129462306a36Sopenharmony_ci		if (err)
129562306a36Sopenharmony_ci			return err;
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	if (data[IFLA_BR_AGEING_TIME]) {
129962306a36Sopenharmony_ci		err = br_set_ageing_time(br, nla_get_u32(data[IFLA_BR_AGEING_TIME]));
130062306a36Sopenharmony_ci		if (err)
130162306a36Sopenharmony_ci			return err;
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	if (data[IFLA_BR_STP_STATE]) {
130562306a36Sopenharmony_ci		u32 stp_enabled = nla_get_u32(data[IFLA_BR_STP_STATE]);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci		err = br_stp_set_enabled(br, stp_enabled, extack);
130862306a36Sopenharmony_ci		if (err)
130962306a36Sopenharmony_ci			return err;
131062306a36Sopenharmony_ci	}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	if (data[IFLA_BR_PRIORITY]) {
131362306a36Sopenharmony_ci		u32 priority = nla_get_u16(data[IFLA_BR_PRIORITY]);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci		br_stp_set_bridge_priority(br, priority);
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	if (data[IFLA_BR_VLAN_FILTERING]) {
131962306a36Sopenharmony_ci		u8 vlan_filter = nla_get_u8(data[IFLA_BR_VLAN_FILTERING]);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci		err = br_vlan_filter_toggle(br, vlan_filter, extack);
132262306a36Sopenharmony_ci		if (err)
132362306a36Sopenharmony_ci			return err;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_VLAN_FILTERING
132762306a36Sopenharmony_ci	if (data[IFLA_BR_VLAN_PROTOCOL]) {
132862306a36Sopenharmony_ci		__be16 vlan_proto = nla_get_be16(data[IFLA_BR_VLAN_PROTOCOL]);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci		err = __br_vlan_set_proto(br, vlan_proto, extack);
133162306a36Sopenharmony_ci		if (err)
133262306a36Sopenharmony_ci			return err;
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	if (data[IFLA_BR_VLAN_DEFAULT_PVID]) {
133662306a36Sopenharmony_ci		__u16 defpvid = nla_get_u16(data[IFLA_BR_VLAN_DEFAULT_PVID]);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci		err = __br_vlan_set_default_pvid(br, defpvid, extack);
133962306a36Sopenharmony_ci		if (err)
134062306a36Sopenharmony_ci			return err;
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (data[IFLA_BR_VLAN_STATS_ENABLED]) {
134462306a36Sopenharmony_ci		__u8 vlan_stats = nla_get_u8(data[IFLA_BR_VLAN_STATS_ENABLED]);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci		err = br_vlan_set_stats(br, vlan_stats);
134762306a36Sopenharmony_ci		if (err)
134862306a36Sopenharmony_ci			return err;
134962306a36Sopenharmony_ci	}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	if (data[IFLA_BR_VLAN_STATS_PER_PORT]) {
135262306a36Sopenharmony_ci		__u8 per_port = nla_get_u8(data[IFLA_BR_VLAN_STATS_PER_PORT]);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci		err = br_vlan_set_stats_per_port(br, per_port);
135562306a36Sopenharmony_ci		if (err)
135662306a36Sopenharmony_ci			return err;
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci#endif
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	if (data[IFLA_BR_GROUP_FWD_MASK]) {
136162306a36Sopenharmony_ci		u16 fwd_mask = nla_get_u16(data[IFLA_BR_GROUP_FWD_MASK]);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci		if (fwd_mask & BR_GROUPFWD_RESTRICTED)
136462306a36Sopenharmony_ci			return -EINVAL;
136562306a36Sopenharmony_ci		br->group_fwd_mask = fwd_mask;
136662306a36Sopenharmony_ci	}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	if (data[IFLA_BR_GROUP_ADDR]) {
136962306a36Sopenharmony_ci		u8 new_addr[ETH_ALEN];
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci		if (nla_len(data[IFLA_BR_GROUP_ADDR]) != ETH_ALEN)
137262306a36Sopenharmony_ci			return -EINVAL;
137362306a36Sopenharmony_ci		memcpy(new_addr, nla_data(data[IFLA_BR_GROUP_ADDR]), ETH_ALEN);
137462306a36Sopenharmony_ci		if (!is_link_local_ether_addr(new_addr))
137562306a36Sopenharmony_ci			return -EINVAL;
137662306a36Sopenharmony_ci		if (new_addr[5] == 1 ||		/* 802.3x Pause address */
137762306a36Sopenharmony_ci		    new_addr[5] == 2 ||		/* 802.3ad Slow protocols */
137862306a36Sopenharmony_ci		    new_addr[5] == 3)		/* 802.1X PAE address */
137962306a36Sopenharmony_ci			return -EINVAL;
138062306a36Sopenharmony_ci		spin_lock_bh(&br->lock);
138162306a36Sopenharmony_ci		memcpy(br->group_addr, new_addr, sizeof(br->group_addr));
138262306a36Sopenharmony_ci		spin_unlock_bh(&br->lock);
138362306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_GROUP_ADDR_SET, true);
138462306a36Sopenharmony_ci		br_recalculate_fwd_mask(br);
138562306a36Sopenharmony_ci	}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	if (data[IFLA_BR_FDB_FLUSH]) {
138862306a36Sopenharmony_ci		struct net_bridge_fdb_flush_desc desc = {
138962306a36Sopenharmony_ci			.flags_mask = BIT(BR_FDB_STATIC)
139062306a36Sopenharmony_ci		};
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci		br_fdb_flush(br, &desc);
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
139662306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_ROUTER]) {
139762306a36Sopenharmony_ci		u8 multicast_router = nla_get_u8(data[IFLA_BR_MCAST_ROUTER]);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci		err = br_multicast_set_router(&br->multicast_ctx,
140062306a36Sopenharmony_ci					      multicast_router);
140162306a36Sopenharmony_ci		if (err)
140262306a36Sopenharmony_ci			return err;
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_SNOOPING]) {
140662306a36Sopenharmony_ci		u8 mcast_snooping = nla_get_u8(data[IFLA_BR_MCAST_SNOOPING]);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		err = br_multicast_toggle(br, mcast_snooping, extack);
140962306a36Sopenharmony_ci		if (err)
141062306a36Sopenharmony_ci			return err;
141162306a36Sopenharmony_ci	}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_QUERY_USE_IFADDR]) {
141462306a36Sopenharmony_ci		u8 val;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci		val = nla_get_u8(data[IFLA_BR_MCAST_QUERY_USE_IFADDR]);
141762306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_MULTICAST_QUERY_USE_IFADDR, !!val);
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_QUERIER]) {
142162306a36Sopenharmony_ci		u8 mcast_querier = nla_get_u8(data[IFLA_BR_MCAST_QUERIER]);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci		err = br_multicast_set_querier(&br->multicast_ctx,
142462306a36Sopenharmony_ci					       mcast_querier);
142562306a36Sopenharmony_ci		if (err)
142662306a36Sopenharmony_ci			return err;
142762306a36Sopenharmony_ci	}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_HASH_ELASTICITY])
143062306a36Sopenharmony_ci		br_warn(br, "the hash_elasticity option has been deprecated and is always %u\n",
143162306a36Sopenharmony_ci			RHT_ELASTICITY);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_HASH_MAX])
143462306a36Sopenharmony_ci		br->hash_max = nla_get_u32(data[IFLA_BR_MCAST_HASH_MAX]);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_LAST_MEMBER_CNT]) {
143762306a36Sopenharmony_ci		u32 val = nla_get_u32(data[IFLA_BR_MCAST_LAST_MEMBER_CNT]);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci		br->multicast_ctx.multicast_last_member_count = val;
144062306a36Sopenharmony_ci	}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) {
144362306a36Sopenharmony_ci		u32 val = nla_get_u32(data[IFLA_BR_MCAST_STARTUP_QUERY_CNT]);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci		br->multicast_ctx.multicast_startup_query_count = val;
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) {
144962306a36Sopenharmony_ci		u64 val = nla_get_u64(data[IFLA_BR_MCAST_LAST_MEMBER_INTVL]);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci		br->multicast_ctx.multicast_last_member_interval = clock_t_to_jiffies(val);
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) {
145562306a36Sopenharmony_ci		u64 val = nla_get_u64(data[IFLA_BR_MCAST_MEMBERSHIP_INTVL]);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci		br->multicast_ctx.multicast_membership_interval = clock_t_to_jiffies(val);
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_QUERIER_INTVL]) {
146162306a36Sopenharmony_ci		u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERIER_INTVL]);
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci		br->multicast_ctx.multicast_querier_interval = clock_t_to_jiffies(val);
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_QUERY_INTVL]) {
146762306a36Sopenharmony_ci		u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERY_INTVL]);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci		br_multicast_set_query_intvl(&br->multicast_ctx, val);
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) {
147362306a36Sopenharmony_ci		u64 val = nla_get_u64(data[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci		br->multicast_ctx.multicast_query_response_interval = clock_t_to_jiffies(val);
147662306a36Sopenharmony_ci	}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) {
147962306a36Sopenharmony_ci		u64 val = nla_get_u64(data[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci		br_multicast_set_startup_query_intvl(&br->multicast_ctx, val);
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_STATS_ENABLED]) {
148562306a36Sopenharmony_ci		__u8 mcast_stats;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		mcast_stats = nla_get_u8(data[IFLA_BR_MCAST_STATS_ENABLED]);
148862306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_MULTICAST_STATS_ENABLED, !!mcast_stats);
148962306a36Sopenharmony_ci	}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_IGMP_VERSION]) {
149262306a36Sopenharmony_ci		__u8 igmp_version;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci		igmp_version = nla_get_u8(data[IFLA_BR_MCAST_IGMP_VERSION]);
149562306a36Sopenharmony_ci		err = br_multicast_set_igmp_version(&br->multicast_ctx,
149662306a36Sopenharmony_ci						    igmp_version);
149762306a36Sopenharmony_ci		if (err)
149862306a36Sopenharmony_ci			return err;
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
150262306a36Sopenharmony_ci	if (data[IFLA_BR_MCAST_MLD_VERSION]) {
150362306a36Sopenharmony_ci		__u8 mld_version;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci		mld_version = nla_get_u8(data[IFLA_BR_MCAST_MLD_VERSION]);
150662306a36Sopenharmony_ci		err = br_multicast_set_mld_version(&br->multicast_ctx,
150762306a36Sopenharmony_ci						   mld_version);
150862306a36Sopenharmony_ci		if (err)
150962306a36Sopenharmony_ci			return err;
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci#endif
151262306a36Sopenharmony_ci#endif
151362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
151462306a36Sopenharmony_ci	if (data[IFLA_BR_NF_CALL_IPTABLES]) {
151562306a36Sopenharmony_ci		u8 val = nla_get_u8(data[IFLA_BR_NF_CALL_IPTABLES]);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_NF_CALL_IPTABLES, !!val);
151862306a36Sopenharmony_ci	}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	if (data[IFLA_BR_NF_CALL_IP6TABLES]) {
152162306a36Sopenharmony_ci		u8 val = nla_get_u8(data[IFLA_BR_NF_CALL_IP6TABLES]);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_NF_CALL_IP6TABLES, !!val);
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	if (data[IFLA_BR_NF_CALL_ARPTABLES]) {
152762306a36Sopenharmony_ci		u8 val = nla_get_u8(data[IFLA_BR_NF_CALL_ARPTABLES]);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_NF_CALL_ARPTABLES, !!val);
153062306a36Sopenharmony_ci	}
153162306a36Sopenharmony_ci#endif
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	if (data[IFLA_BR_MULTI_BOOLOPT]) {
153462306a36Sopenharmony_ci		struct br_boolopt_multi *bm;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci		bm = nla_data(data[IFLA_BR_MULTI_BOOLOPT]);
153762306a36Sopenharmony_ci		err = br_boolopt_multi_toggle(br, bm, extack);
153862306a36Sopenharmony_ci		if (err)
153962306a36Sopenharmony_ci			return err;
154062306a36Sopenharmony_ci	}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	return 0;
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic int br_dev_newlink(struct net *src_net, struct net_device *dev,
154662306a36Sopenharmony_ci			  struct nlattr *tb[], struct nlattr *data[],
154762306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
154862306a36Sopenharmony_ci{
154962306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
155062306a36Sopenharmony_ci	int err;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	err = register_netdevice(dev);
155362306a36Sopenharmony_ci	if (err)
155462306a36Sopenharmony_ci		return err;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
155762306a36Sopenharmony_ci		spin_lock_bh(&br->lock);
155862306a36Sopenharmony_ci		br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS]));
155962306a36Sopenharmony_ci		spin_unlock_bh(&br->lock);
156062306a36Sopenharmony_ci	}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	err = br_changelink(dev, tb, data, extack);
156362306a36Sopenharmony_ci	if (err)
156462306a36Sopenharmony_ci		br_dev_delete(dev, NULL);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	return err;
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_cistatic size_t br_get_size(const struct net_device *brdev)
157062306a36Sopenharmony_ci{
157162306a36Sopenharmony_ci	return nla_total_size(sizeof(u32)) +	/* IFLA_BR_FORWARD_DELAY  */
157262306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* IFLA_BR_HELLO_TIME */
157362306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* IFLA_BR_MAX_AGE */
157462306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +    /* IFLA_BR_AGEING_TIME */
157562306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +    /* IFLA_BR_STP_STATE */
157662306a36Sopenharmony_ci	       nla_total_size(sizeof(u16)) +    /* IFLA_BR_PRIORITY */
157762306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_VLAN_FILTERING */
157862306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_VLAN_FILTERING
157962306a36Sopenharmony_ci	       nla_total_size(sizeof(__be16)) +	/* IFLA_BR_VLAN_PROTOCOL */
158062306a36Sopenharmony_ci	       nla_total_size(sizeof(u16)) +    /* IFLA_BR_VLAN_DEFAULT_PVID */
158162306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_VLAN_STATS_ENABLED */
158262306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +	/* IFLA_BR_VLAN_STATS_PER_PORT */
158362306a36Sopenharmony_ci#endif
158462306a36Sopenharmony_ci	       nla_total_size(sizeof(u16)) +    /* IFLA_BR_GROUP_FWD_MASK */
158562306a36Sopenharmony_ci	       nla_total_size(sizeof(struct ifla_bridge_id)) +   /* IFLA_BR_ROOT_ID */
158662306a36Sopenharmony_ci	       nla_total_size(sizeof(struct ifla_bridge_id)) +   /* IFLA_BR_BRIDGE_ID */
158762306a36Sopenharmony_ci	       nla_total_size(sizeof(u16)) +    /* IFLA_BR_ROOT_PORT */
158862306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +    /* IFLA_BR_ROOT_PATH_COST */
158962306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_TOPOLOGY_CHANGE */
159062306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_TOPOLOGY_CHANGE_DETECTED */
159162306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_HELLO_TIMER */
159262306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_TCN_TIMER */
159362306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_TOPOLOGY_CHANGE_TIMER */
159462306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_GC_TIMER */
159562306a36Sopenharmony_ci	       nla_total_size(ETH_ALEN) +       /* IFLA_BR_GROUP_ADDR */
159662306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
159762306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_ROUTER */
159862306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_SNOOPING */
159962306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_QUERY_USE_IFADDR */
160062306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_QUERIER */
160162306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_MCAST_STATS_ENABLED */
160262306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_HASH_ELASTICITY */
160362306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_HASH_MAX */
160462306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_LAST_MEMBER_CNT */
160562306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +    /* IFLA_BR_MCAST_STARTUP_QUERY_CNT */
160662306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_LAST_MEMBER_INTVL */
160762306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_MEMBERSHIP_INTVL */
160862306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERIER_INTVL */
160962306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_INTVL */
161062306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_RESPONSE_INTVL */
161162306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */
161262306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +	/* IFLA_BR_MCAST_IGMP_VERSION */
161362306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +	/* IFLA_BR_MCAST_MLD_VERSION */
161462306a36Sopenharmony_ci	       br_multicast_querier_state_size() + /* IFLA_BR_MCAST_QUERIER_STATE */
161562306a36Sopenharmony_ci#endif
161662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
161762306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IPTABLES */
161862306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IP6TABLES */
161962306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_ARPTABLES */
162062306a36Sopenharmony_ci#endif
162162306a36Sopenharmony_ci	       nla_total_size(sizeof(struct br_boolopt_multi)) + /* IFLA_BR_MULTI_BOOLOPT */
162262306a36Sopenharmony_ci	       0;
162362306a36Sopenharmony_ci}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_cistatic int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
162662306a36Sopenharmony_ci{
162762306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(brdev);
162862306a36Sopenharmony_ci	u32 forward_delay = jiffies_to_clock_t(br->forward_delay);
162962306a36Sopenharmony_ci	u32 hello_time = jiffies_to_clock_t(br->hello_time);
163062306a36Sopenharmony_ci	u32 age_time = jiffies_to_clock_t(br->max_age);
163162306a36Sopenharmony_ci	u32 ageing_time = jiffies_to_clock_t(br->ageing_time);
163262306a36Sopenharmony_ci	u32 stp_enabled = br->stp_enabled;
163362306a36Sopenharmony_ci	u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1];
163462306a36Sopenharmony_ci	u8 vlan_enabled = br_vlan_enabled(br->dev);
163562306a36Sopenharmony_ci	struct br_boolopt_multi bm;
163662306a36Sopenharmony_ci	u64 clockval;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	clockval = br_timer_value(&br->hello_timer);
163962306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_HELLO_TIMER, clockval, IFLA_BR_PAD))
164062306a36Sopenharmony_ci		return -EMSGSIZE;
164162306a36Sopenharmony_ci	clockval = br_timer_value(&br->tcn_timer);
164262306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_TCN_TIMER, clockval, IFLA_BR_PAD))
164362306a36Sopenharmony_ci		return -EMSGSIZE;
164462306a36Sopenharmony_ci	clockval = br_timer_value(&br->topology_change_timer);
164562306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER, clockval,
164662306a36Sopenharmony_ci			      IFLA_BR_PAD))
164762306a36Sopenharmony_ci		return -EMSGSIZE;
164862306a36Sopenharmony_ci	clockval = br_timer_value(&br->gc_work.timer);
164962306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_GC_TIMER, clockval, IFLA_BR_PAD))
165062306a36Sopenharmony_ci		return -EMSGSIZE;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	br_boolopt_multi_get(br, &bm);
165362306a36Sopenharmony_ci	if (nla_put_u32(skb, IFLA_BR_FORWARD_DELAY, forward_delay) ||
165462306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BR_HELLO_TIME, hello_time) ||
165562306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BR_MAX_AGE, age_time) ||
165662306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BR_AGEING_TIME, ageing_time) ||
165762306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BR_STP_STATE, stp_enabled) ||
165862306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BR_PRIORITY, priority) ||
165962306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_VLAN_FILTERING, vlan_enabled) ||
166062306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BR_GROUP_FWD_MASK, br->group_fwd_mask) ||
166162306a36Sopenharmony_ci	    nla_put(skb, IFLA_BR_BRIDGE_ID, sizeof(struct ifla_bridge_id),
166262306a36Sopenharmony_ci		    &br->bridge_id) ||
166362306a36Sopenharmony_ci	    nla_put(skb, IFLA_BR_ROOT_ID, sizeof(struct ifla_bridge_id),
166462306a36Sopenharmony_ci		    &br->designated_root) ||
166562306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BR_ROOT_PORT, br->root_port) ||
166662306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BR_ROOT_PATH_COST, br->root_path_cost) ||
166762306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE, br->topology_change) ||
166862306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
166962306a36Sopenharmony_ci		       br->topology_change_detected) ||
167062306a36Sopenharmony_ci	    nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr) ||
167162306a36Sopenharmony_ci	    nla_put(skb, IFLA_BR_MULTI_BOOLOPT, sizeof(bm), &bm))
167262306a36Sopenharmony_ci		return -EMSGSIZE;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_VLAN_FILTERING
167562306a36Sopenharmony_ci	if (nla_put_be16(skb, IFLA_BR_VLAN_PROTOCOL, br->vlan_proto) ||
167662306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_BR_VLAN_DEFAULT_PVID, br->default_pvid) ||
167762306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_VLAN_STATS_ENABLED,
167862306a36Sopenharmony_ci		       br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) ||
167962306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_VLAN_STATS_PER_PORT,
168062306a36Sopenharmony_ci		       br_opt_get(br, BROPT_VLAN_STATS_PER_PORT)))
168162306a36Sopenharmony_ci		return -EMSGSIZE;
168262306a36Sopenharmony_ci#endif
168362306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
168462306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_BR_MCAST_ROUTER,
168562306a36Sopenharmony_ci		       br->multicast_ctx.multicast_router) ||
168662306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_MCAST_SNOOPING,
168762306a36Sopenharmony_ci		       br_opt_get(br, BROPT_MULTICAST_ENABLED)) ||
168862306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR,
168962306a36Sopenharmony_ci		       br_opt_get(br, BROPT_MULTICAST_QUERY_USE_IFADDR)) ||
169062306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_MCAST_QUERIER,
169162306a36Sopenharmony_ci		       br->multicast_ctx.multicast_querier) ||
169262306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_MCAST_STATS_ENABLED,
169362306a36Sopenharmony_ci		       br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) ||
169462306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, RHT_ELASTICITY) ||
169562306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BR_MCAST_HASH_MAX, br->hash_max) ||
169662306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BR_MCAST_LAST_MEMBER_CNT,
169762306a36Sopenharmony_ci			br->multicast_ctx.multicast_last_member_count) ||
169862306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_BR_MCAST_STARTUP_QUERY_CNT,
169962306a36Sopenharmony_ci			br->multicast_ctx.multicast_startup_query_count) ||
170062306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION,
170162306a36Sopenharmony_ci		       br->multicast_ctx.multicast_igmp_version) ||
170262306a36Sopenharmony_ci	    br_multicast_dump_querier_state(skb, &br->multicast_ctx,
170362306a36Sopenharmony_ci					    IFLA_BR_MCAST_QUERIER_STATE))
170462306a36Sopenharmony_ci		return -EMSGSIZE;
170562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
170662306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION,
170762306a36Sopenharmony_ci		       br->multicast_ctx.multicast_mld_version))
170862306a36Sopenharmony_ci		return -EMSGSIZE;
170962306a36Sopenharmony_ci#endif
171062306a36Sopenharmony_ci	clockval = jiffies_to_clock_t(br->multicast_ctx.multicast_last_member_interval);
171162306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_MCAST_LAST_MEMBER_INTVL, clockval,
171262306a36Sopenharmony_ci			      IFLA_BR_PAD))
171362306a36Sopenharmony_ci		return -EMSGSIZE;
171462306a36Sopenharmony_ci	clockval = jiffies_to_clock_t(br->multicast_ctx.multicast_membership_interval);
171562306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_MCAST_MEMBERSHIP_INTVL, clockval,
171662306a36Sopenharmony_ci			      IFLA_BR_PAD))
171762306a36Sopenharmony_ci		return -EMSGSIZE;
171862306a36Sopenharmony_ci	clockval = jiffies_to_clock_t(br->multicast_ctx.multicast_querier_interval);
171962306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_MCAST_QUERIER_INTVL, clockval,
172062306a36Sopenharmony_ci			      IFLA_BR_PAD))
172162306a36Sopenharmony_ci		return -EMSGSIZE;
172262306a36Sopenharmony_ci	clockval = jiffies_to_clock_t(br->multicast_ctx.multicast_query_interval);
172362306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_MCAST_QUERY_INTVL, clockval,
172462306a36Sopenharmony_ci			      IFLA_BR_PAD))
172562306a36Sopenharmony_ci		return -EMSGSIZE;
172662306a36Sopenharmony_ci	clockval = jiffies_to_clock_t(br->multicast_ctx.multicast_query_response_interval);
172762306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, clockval,
172862306a36Sopenharmony_ci			      IFLA_BR_PAD))
172962306a36Sopenharmony_ci		return -EMSGSIZE;
173062306a36Sopenharmony_ci	clockval = jiffies_to_clock_t(br->multicast_ctx.multicast_startup_query_interval);
173162306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, clockval,
173262306a36Sopenharmony_ci			      IFLA_BR_PAD))
173362306a36Sopenharmony_ci		return -EMSGSIZE;
173462306a36Sopenharmony_ci#endif
173562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
173662306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_BR_NF_CALL_IPTABLES,
173762306a36Sopenharmony_ci		       br_opt_get(br, BROPT_NF_CALL_IPTABLES) ? 1 : 0) ||
173862306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_NF_CALL_IP6TABLES,
173962306a36Sopenharmony_ci		       br_opt_get(br, BROPT_NF_CALL_IP6TABLES) ? 1 : 0) ||
174062306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_BR_NF_CALL_ARPTABLES,
174162306a36Sopenharmony_ci		       br_opt_get(br, BROPT_NF_CALL_ARPTABLES) ? 1 : 0))
174262306a36Sopenharmony_ci		return -EMSGSIZE;
174362306a36Sopenharmony_ci#endif
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	return 0;
174662306a36Sopenharmony_ci}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_cistatic size_t br_get_linkxstats_size(const struct net_device *dev, int attr)
174962306a36Sopenharmony_ci{
175062306a36Sopenharmony_ci	struct net_bridge_port *p = NULL;
175162306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
175262306a36Sopenharmony_ci	struct net_bridge_vlan *v;
175362306a36Sopenharmony_ci	struct net_bridge *br;
175462306a36Sopenharmony_ci	int numvls = 0;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	switch (attr) {
175762306a36Sopenharmony_ci	case IFLA_STATS_LINK_XSTATS:
175862306a36Sopenharmony_ci		br = netdev_priv(dev);
175962306a36Sopenharmony_ci		vg = br_vlan_group(br);
176062306a36Sopenharmony_ci		break;
176162306a36Sopenharmony_ci	case IFLA_STATS_LINK_XSTATS_SLAVE:
176262306a36Sopenharmony_ci		p = br_port_get_rtnl(dev);
176362306a36Sopenharmony_ci		if (!p)
176462306a36Sopenharmony_ci			return 0;
176562306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
176662306a36Sopenharmony_ci		break;
176762306a36Sopenharmony_ci	default:
176862306a36Sopenharmony_ci		return 0;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	if (vg) {
177262306a36Sopenharmony_ci		/* we need to count all, even placeholder entries */
177362306a36Sopenharmony_ci		list_for_each_entry(v, &vg->vlan_list, vlist)
177462306a36Sopenharmony_ci			numvls++;
177562306a36Sopenharmony_ci	}
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	return numvls * nla_total_size(sizeof(struct bridge_vlan_xstats)) +
177862306a36Sopenharmony_ci	       nla_total_size_64bit(sizeof(struct br_mcast_stats)) +
177962306a36Sopenharmony_ci	       (p ? nla_total_size_64bit(sizeof(p->stp_xstats)) : 0) +
178062306a36Sopenharmony_ci	       nla_total_size(0);
178162306a36Sopenharmony_ci}
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_cistatic int br_fill_linkxstats(struct sk_buff *skb,
178462306a36Sopenharmony_ci			      const struct net_device *dev,
178562306a36Sopenharmony_ci			      int *prividx, int attr)
178662306a36Sopenharmony_ci{
178762306a36Sopenharmony_ci	struct nlattr *nla __maybe_unused;
178862306a36Sopenharmony_ci	struct net_bridge_port *p = NULL;
178962306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
179062306a36Sopenharmony_ci	struct net_bridge_vlan *v;
179162306a36Sopenharmony_ci	struct net_bridge *br;
179262306a36Sopenharmony_ci	struct nlattr *nest;
179362306a36Sopenharmony_ci	int vl_idx = 0;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	switch (attr) {
179662306a36Sopenharmony_ci	case IFLA_STATS_LINK_XSTATS:
179762306a36Sopenharmony_ci		br = netdev_priv(dev);
179862306a36Sopenharmony_ci		vg = br_vlan_group(br);
179962306a36Sopenharmony_ci		break;
180062306a36Sopenharmony_ci	case IFLA_STATS_LINK_XSTATS_SLAVE:
180162306a36Sopenharmony_ci		p = br_port_get_rtnl(dev);
180262306a36Sopenharmony_ci		if (!p)
180362306a36Sopenharmony_ci			return 0;
180462306a36Sopenharmony_ci		br = p->br;
180562306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
180662306a36Sopenharmony_ci		break;
180762306a36Sopenharmony_ci	default:
180862306a36Sopenharmony_ci		return -EINVAL;
180962306a36Sopenharmony_ci	}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	nest = nla_nest_start_noflag(skb, LINK_XSTATS_TYPE_BRIDGE);
181262306a36Sopenharmony_ci	if (!nest)
181362306a36Sopenharmony_ci		return -EMSGSIZE;
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	if (vg) {
181662306a36Sopenharmony_ci		u16 pvid;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci		pvid = br_get_pvid(vg);
181962306a36Sopenharmony_ci		list_for_each_entry(v, &vg->vlan_list, vlist) {
182062306a36Sopenharmony_ci			struct bridge_vlan_xstats vxi;
182162306a36Sopenharmony_ci			struct pcpu_sw_netstats stats;
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci			if (++vl_idx < *prividx)
182462306a36Sopenharmony_ci				continue;
182562306a36Sopenharmony_ci			memset(&vxi, 0, sizeof(vxi));
182662306a36Sopenharmony_ci			vxi.vid = v->vid;
182762306a36Sopenharmony_ci			vxi.flags = v->flags;
182862306a36Sopenharmony_ci			if (v->vid == pvid)
182962306a36Sopenharmony_ci				vxi.flags |= BRIDGE_VLAN_INFO_PVID;
183062306a36Sopenharmony_ci			br_vlan_get_stats(v, &stats);
183162306a36Sopenharmony_ci			vxi.rx_bytes = u64_stats_read(&stats.rx_bytes);
183262306a36Sopenharmony_ci			vxi.rx_packets = u64_stats_read(&stats.rx_packets);
183362306a36Sopenharmony_ci			vxi.tx_bytes = u64_stats_read(&stats.tx_bytes);
183462306a36Sopenharmony_ci			vxi.tx_packets = u64_stats_read(&stats.tx_packets);
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci			if (nla_put(skb, BRIDGE_XSTATS_VLAN, sizeof(vxi), &vxi))
183762306a36Sopenharmony_ci				goto nla_put_failure;
183862306a36Sopenharmony_ci		}
183962306a36Sopenharmony_ci	}
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
184262306a36Sopenharmony_ci	if (++vl_idx >= *prividx) {
184362306a36Sopenharmony_ci		nla = nla_reserve_64bit(skb, BRIDGE_XSTATS_MCAST,
184462306a36Sopenharmony_ci					sizeof(struct br_mcast_stats),
184562306a36Sopenharmony_ci					BRIDGE_XSTATS_PAD);
184662306a36Sopenharmony_ci		if (!nla)
184762306a36Sopenharmony_ci			goto nla_put_failure;
184862306a36Sopenharmony_ci		br_multicast_get_stats(br, p, nla_data(nla));
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci#endif
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	if (p) {
185362306a36Sopenharmony_ci		nla = nla_reserve_64bit(skb, BRIDGE_XSTATS_STP,
185462306a36Sopenharmony_ci					sizeof(p->stp_xstats),
185562306a36Sopenharmony_ci					BRIDGE_XSTATS_PAD);
185662306a36Sopenharmony_ci		if (!nla)
185762306a36Sopenharmony_ci			goto nla_put_failure;
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci		spin_lock_bh(&br->lock);
186062306a36Sopenharmony_ci		memcpy(nla_data(nla), &p->stp_xstats, sizeof(p->stp_xstats));
186162306a36Sopenharmony_ci		spin_unlock_bh(&br->lock);
186262306a36Sopenharmony_ci	}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	nla_nest_end(skb, nest);
186562306a36Sopenharmony_ci	*prividx = 0;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	return 0;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_cinla_put_failure:
187062306a36Sopenharmony_ci	nla_nest_end(skb, nest);
187162306a36Sopenharmony_ci	*prividx = vl_idx;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	return -EMSGSIZE;
187462306a36Sopenharmony_ci}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_cistatic struct rtnl_af_ops br_af_ops __read_mostly = {
187762306a36Sopenharmony_ci	.family			= AF_BRIDGE,
187862306a36Sopenharmony_ci	.get_link_af_size	= br_get_link_af_size_filtered,
187962306a36Sopenharmony_ci};
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_cistruct rtnl_link_ops br_link_ops __read_mostly = {
188262306a36Sopenharmony_ci	.kind			= "bridge",
188362306a36Sopenharmony_ci	.priv_size		= sizeof(struct net_bridge),
188462306a36Sopenharmony_ci	.setup			= br_dev_setup,
188562306a36Sopenharmony_ci	.maxtype		= IFLA_BR_MAX,
188662306a36Sopenharmony_ci	.policy			= br_policy,
188762306a36Sopenharmony_ci	.validate		= br_validate,
188862306a36Sopenharmony_ci	.newlink		= br_dev_newlink,
188962306a36Sopenharmony_ci	.changelink		= br_changelink,
189062306a36Sopenharmony_ci	.dellink		= br_dev_delete,
189162306a36Sopenharmony_ci	.get_size		= br_get_size,
189262306a36Sopenharmony_ci	.fill_info		= br_fill_info,
189362306a36Sopenharmony_ci	.fill_linkxstats	= br_fill_linkxstats,
189462306a36Sopenharmony_ci	.get_linkxstats_size	= br_get_linkxstats_size,
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci	.slave_maxtype		= IFLA_BRPORT_MAX,
189762306a36Sopenharmony_ci	.slave_policy		= br_port_policy,
189862306a36Sopenharmony_ci	.slave_changelink	= br_port_slave_changelink,
189962306a36Sopenharmony_ci	.get_slave_size		= br_port_get_slave_size,
190062306a36Sopenharmony_ci	.fill_slave_info	= br_port_fill_slave_info,
190162306a36Sopenharmony_ci};
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ciint __init br_netlink_init(void)
190462306a36Sopenharmony_ci{
190562306a36Sopenharmony_ci	int err;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	br_vlan_rtnl_init();
190862306a36Sopenharmony_ci	rtnl_af_register(&br_af_ops);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	err = rtnl_link_register(&br_link_ops);
191162306a36Sopenharmony_ci	if (err)
191262306a36Sopenharmony_ci		goto out_af;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	return 0;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ciout_af:
191762306a36Sopenharmony_ci	rtnl_af_unregister(&br_af_ops);
191862306a36Sopenharmony_ci	return err;
191962306a36Sopenharmony_ci}
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_civoid br_netlink_fini(void)
192262306a36Sopenharmony_ci{
192362306a36Sopenharmony_ci	br_vlan_rtnl_uninit();
192462306a36Sopenharmony_ci	rtnl_af_unregister(&br_af_ops);
192562306a36Sopenharmony_ci	rtnl_link_unregister(&br_link_ops);
192662306a36Sopenharmony_ci}
1927