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