162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	Userspace interface
462306a36Sopenharmony_ci *	Linux ethernet bridge
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *	Authors:
762306a36Sopenharmony_ci *	Lennert Buytenhek		<buytenh@gnu.org>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/netdevice.h>
1262306a36Sopenharmony_ci#include <linux/etherdevice.h>
1362306a36Sopenharmony_ci#include <linux/netpoll.h>
1462306a36Sopenharmony_ci#include <linux/ethtool.h>
1562306a36Sopenharmony_ci#include <linux/if_arp.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1962306a36Sopenharmony_ci#include <linux/if_ether.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <net/dsa.h>
2262306a36Sopenharmony_ci#include <net/sock.h>
2362306a36Sopenharmony_ci#include <linux/if_vlan.h>
2462306a36Sopenharmony_ci#include <net/switchdev.h>
2562306a36Sopenharmony_ci#include <net/net_namespace.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "br_private.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Determine initial path cost based on speed.
3162306a36Sopenharmony_ci * using recommendations from 802.1d standard
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Since driver might sleep need to not be holding any locks.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_cistatic int port_cost(struct net_device *dev)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct ethtool_link_ksettings ecmd;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (!__ethtool_get_link_ksettings(dev, &ecmd)) {
4062306a36Sopenharmony_ci		switch (ecmd.base.speed) {
4162306a36Sopenharmony_ci		case SPEED_10000:
4262306a36Sopenharmony_ci			return 2;
4362306a36Sopenharmony_ci		case SPEED_5000:
4462306a36Sopenharmony_ci			return 3;
4562306a36Sopenharmony_ci		case SPEED_2500:
4662306a36Sopenharmony_ci			return 4;
4762306a36Sopenharmony_ci		case SPEED_1000:
4862306a36Sopenharmony_ci			return 5;
4962306a36Sopenharmony_ci		case SPEED_100:
5062306a36Sopenharmony_ci			return 19;
5162306a36Sopenharmony_ci		case SPEED_10:
5262306a36Sopenharmony_ci			return 100;
5362306a36Sopenharmony_ci		case SPEED_UNKNOWN:
5462306a36Sopenharmony_ci			return 100;
5562306a36Sopenharmony_ci		default:
5662306a36Sopenharmony_ci			if (ecmd.base.speed > SPEED_10000)
5762306a36Sopenharmony_ci				return 1;
5862306a36Sopenharmony_ci		}
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* Old silly heuristics based on name */
6262306a36Sopenharmony_ci	if (!strncmp(dev->name, "lec", 3))
6362306a36Sopenharmony_ci		return 7;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (!strncmp(dev->name, "plip", 4))
6662306a36Sopenharmony_ci		return 2500;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return 100;	/* assume old 10Mbps */
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Check for port carrier transitions. */
7362306a36Sopenharmony_civoid br_port_carrier_check(struct net_bridge_port *p, bool *notified)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct net_device *dev = p->dev;
7662306a36Sopenharmony_ci	struct net_bridge *br = p->br;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (!(p->flags & BR_ADMIN_COST) &&
7962306a36Sopenharmony_ci	    netif_running(dev) && netif_oper_up(dev))
8062306a36Sopenharmony_ci		p->path_cost = port_cost(dev);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	*notified = false;
8362306a36Sopenharmony_ci	if (!netif_running(br->dev))
8462306a36Sopenharmony_ci		return;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	spin_lock_bh(&br->lock);
8762306a36Sopenharmony_ci	if (netif_running(dev) && netif_oper_up(dev)) {
8862306a36Sopenharmony_ci		if (p->state == BR_STATE_DISABLED) {
8962306a36Sopenharmony_ci			br_stp_enable_port(p);
9062306a36Sopenharmony_ci			*notified = true;
9162306a36Sopenharmony_ci		}
9262306a36Sopenharmony_ci	} else {
9362306a36Sopenharmony_ci		if (p->state != BR_STATE_DISABLED) {
9462306a36Sopenharmony_ci			br_stp_disable_port(p);
9562306a36Sopenharmony_ci			*notified = true;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci	spin_unlock_bh(&br->lock);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void br_port_set_promisc(struct net_bridge_port *p)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	int err = 0;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (br_promisc_port(p))
10662306a36Sopenharmony_ci		return;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	err = dev_set_promiscuity(p->dev, 1);
10962306a36Sopenharmony_ci	if (err)
11062306a36Sopenharmony_ci		return;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	br_fdb_unsync_static(p->br, p);
11362306a36Sopenharmony_ci	p->flags |= BR_PROMISC;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void br_port_clear_promisc(struct net_bridge_port *p)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	int err;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Check if the port is already non-promisc or if it doesn't
12162306a36Sopenharmony_ci	 * support UNICAST filtering.  Without unicast filtering support
12262306a36Sopenharmony_ci	 * we'll end up re-enabling promisc mode anyway, so just check for
12362306a36Sopenharmony_ci	 * it here.
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci	if (!br_promisc_port(p) || !(p->dev->priv_flags & IFF_UNICAST_FLT))
12662306a36Sopenharmony_ci		return;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Since we'll be clearing the promisc mode, program the port
12962306a36Sopenharmony_ci	 * first so that we don't have interruption in traffic.
13062306a36Sopenharmony_ci	 */
13162306a36Sopenharmony_ci	err = br_fdb_sync_static(p->br, p);
13262306a36Sopenharmony_ci	if (err)
13362306a36Sopenharmony_ci		return;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	dev_set_promiscuity(p->dev, -1);
13662306a36Sopenharmony_ci	p->flags &= ~BR_PROMISC;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* When a port is added or removed or when certain port flags
14062306a36Sopenharmony_ci * change, this function is called to automatically manage
14162306a36Sopenharmony_ci * promiscuity setting of all the bridge ports.  We are always called
14262306a36Sopenharmony_ci * under RTNL so can skip using rcu primitives.
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_civoid br_manage_promisc(struct net_bridge *br)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct net_bridge_port *p;
14762306a36Sopenharmony_ci	bool set_all = false;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* If vlan filtering is disabled or bridge interface is placed
15062306a36Sopenharmony_ci	 * into promiscuous mode, place all ports in promiscuous mode.
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	if ((br->dev->flags & IFF_PROMISC) || !br_vlan_enabled(br->dev))
15362306a36Sopenharmony_ci		set_all = true;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
15662306a36Sopenharmony_ci		if (set_all) {
15762306a36Sopenharmony_ci			br_port_set_promisc(p);
15862306a36Sopenharmony_ci		} else {
15962306a36Sopenharmony_ci			/* If the number of auto-ports is <= 1, then all other
16062306a36Sopenharmony_ci			 * ports will have their output configuration
16162306a36Sopenharmony_ci			 * statically specified through fdbs.  Since ingress
16262306a36Sopenharmony_ci			 * on the auto-port becomes forwarding/egress to other
16362306a36Sopenharmony_ci			 * ports and egress configuration is statically known,
16462306a36Sopenharmony_ci			 * we can say that ingress configuration of the
16562306a36Sopenharmony_ci			 * auto-port is also statically known.
16662306a36Sopenharmony_ci			 * This lets us disable promiscuous mode and write
16762306a36Sopenharmony_ci			 * this config to hw.
16862306a36Sopenharmony_ci			 */
16962306a36Sopenharmony_ci			if ((p->dev->priv_flags & IFF_UNICAST_FLT) &&
17062306a36Sopenharmony_ci			    (br->auto_cnt == 0 ||
17162306a36Sopenharmony_ci			     (br->auto_cnt == 1 && br_auto_port(p))))
17262306a36Sopenharmony_ci				br_port_clear_promisc(p);
17362306a36Sopenharmony_ci			else
17462306a36Sopenharmony_ci				br_port_set_promisc(p);
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciint nbp_backup_change(struct net_bridge_port *p,
18062306a36Sopenharmony_ci		      struct net_device *backup_dev)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct net_bridge_port *old_backup = rtnl_dereference(p->backup_port);
18362306a36Sopenharmony_ci	struct net_bridge_port *backup_p = NULL;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	ASSERT_RTNL();
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (backup_dev) {
18862306a36Sopenharmony_ci		if (!netif_is_bridge_port(backup_dev))
18962306a36Sopenharmony_ci			return -ENOENT;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		backup_p = br_port_get_rtnl(backup_dev);
19262306a36Sopenharmony_ci		if (backup_p->br != p->br)
19362306a36Sopenharmony_ci			return -EINVAL;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (p == backup_p)
19762306a36Sopenharmony_ci		return -EINVAL;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (old_backup == backup_p)
20062306a36Sopenharmony_ci		return 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* if the backup link is already set, clear it */
20362306a36Sopenharmony_ci	if (old_backup)
20462306a36Sopenharmony_ci		old_backup->backup_redirected_cnt--;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (backup_p)
20762306a36Sopenharmony_ci		backup_p->backup_redirected_cnt++;
20862306a36Sopenharmony_ci	rcu_assign_pointer(p->backup_port, backup_p);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void nbp_backup_clear(struct net_bridge_port *p)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	nbp_backup_change(p, NULL);
21662306a36Sopenharmony_ci	if (p->backup_redirected_cnt) {
21762306a36Sopenharmony_ci		struct net_bridge_port *cur_p;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		list_for_each_entry(cur_p, &p->br->port_list, list) {
22062306a36Sopenharmony_ci			struct net_bridge_port *backup_p;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci			backup_p = rtnl_dereference(cur_p->backup_port);
22362306a36Sopenharmony_ci			if (backup_p == p)
22462306a36Sopenharmony_ci				nbp_backup_change(cur_p, NULL);
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	WARN_ON(rcu_access_pointer(p->backup_port) || p->backup_redirected_cnt);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void nbp_update_port_count(struct net_bridge *br)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct net_bridge_port *p;
23462306a36Sopenharmony_ci	u32 cnt = 0;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
23762306a36Sopenharmony_ci		if (br_auto_port(p))
23862306a36Sopenharmony_ci			cnt++;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci	if (br->auto_cnt != cnt) {
24162306a36Sopenharmony_ci		br->auto_cnt = cnt;
24262306a36Sopenharmony_ci		br_manage_promisc(br);
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic void nbp_delete_promisc(struct net_bridge_port *p)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	/* If port is currently promiscuous, unset promiscuity.
24962306a36Sopenharmony_ci	 * Otherwise, it is a static port so remove all addresses
25062306a36Sopenharmony_ci	 * from it.
25162306a36Sopenharmony_ci	 */
25262306a36Sopenharmony_ci	dev_set_allmulti(p->dev, -1);
25362306a36Sopenharmony_ci	if (br_promisc_port(p))
25462306a36Sopenharmony_ci		dev_set_promiscuity(p->dev, -1);
25562306a36Sopenharmony_ci	else
25662306a36Sopenharmony_ci		br_fdb_unsync_static(p->br, p);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void release_nbp(struct kobject *kobj)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct net_bridge_port *p
26262306a36Sopenharmony_ci		= container_of(kobj, struct net_bridge_port, kobj);
26362306a36Sopenharmony_ci	kfree(p);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void brport_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct net_bridge_port *p = kobj_to_brport(kobj);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	net_ns_get_ownership(dev_net(p->dev), uid, gid);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic const struct kobj_type brport_ktype = {
27462306a36Sopenharmony_ci#ifdef CONFIG_SYSFS
27562306a36Sopenharmony_ci	.sysfs_ops = &brport_sysfs_ops,
27662306a36Sopenharmony_ci#endif
27762306a36Sopenharmony_ci	.release = release_nbp,
27862306a36Sopenharmony_ci	.get_ownership = brport_get_ownership,
27962306a36Sopenharmony_ci};
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic void destroy_nbp(struct net_bridge_port *p)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct net_device *dev = p->dev;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	p->br = NULL;
28662306a36Sopenharmony_ci	p->dev = NULL;
28762306a36Sopenharmony_ci	netdev_put(dev, &p->dev_tracker);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	kobject_put(&p->kobj);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void destroy_nbp_rcu(struct rcu_head *head)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct net_bridge_port *p =
29562306a36Sopenharmony_ci			container_of(head, struct net_bridge_port, rcu);
29662306a36Sopenharmony_ci	destroy_nbp(p);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic unsigned get_max_headroom(struct net_bridge *br)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	unsigned max_headroom = 0;
30262306a36Sopenharmony_ci	struct net_bridge_port *p;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
30562306a36Sopenharmony_ci		unsigned dev_headroom = netdev_get_fwd_headroom(p->dev);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		if (dev_headroom > max_headroom)
30862306a36Sopenharmony_ci			max_headroom = dev_headroom;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return max_headroom;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void update_headroom(struct net_bridge *br, int new_hr)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct net_bridge_port *p;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list)
31962306a36Sopenharmony_ci		netdev_set_rx_headroom(p->dev, new_hr);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	br->dev->needed_headroom = new_hr;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci/* Delete port(interface) from bridge is done in two steps.
32562306a36Sopenharmony_ci * via RCU. First step, marks device as down. That deletes
32662306a36Sopenharmony_ci * all the timers and stops new packets from flowing through.
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * Final cleanup doesn't occur until after all CPU's finished
32962306a36Sopenharmony_ci * processing packets.
33062306a36Sopenharmony_ci *
33162306a36Sopenharmony_ci * Protected from multiple admin operations by RTNL mutex
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_cistatic void del_nbp(struct net_bridge_port *p)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct net_bridge *br = p->br;
33662306a36Sopenharmony_ci	struct net_device *dev = p->dev;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	sysfs_remove_link(br->ifobj, p->dev->name);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	nbp_delete_promisc(p);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	spin_lock_bh(&br->lock);
34362306a36Sopenharmony_ci	br_stp_disable_port(p);
34462306a36Sopenharmony_ci	spin_unlock_bh(&br->lock);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	br_mrp_port_del(br, p);
34762306a36Sopenharmony_ci	br_cfm_port_del(br, p);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	br_ifinfo_notify(RTM_DELLINK, NULL, p);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	list_del_rcu(&p->list);
35262306a36Sopenharmony_ci	if (netdev_get_fwd_headroom(dev) == br->dev->needed_headroom)
35362306a36Sopenharmony_ci		update_headroom(br, get_max_headroom(br));
35462306a36Sopenharmony_ci	netdev_reset_rx_headroom(dev);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	nbp_vlan_flush(p);
35762306a36Sopenharmony_ci	br_fdb_delete_by_port(br, p, 0, 1);
35862306a36Sopenharmony_ci	switchdev_deferred_process();
35962306a36Sopenharmony_ci	nbp_backup_clear(p);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	nbp_update_port_count(br);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	netdev_upper_dev_unlink(dev, br->dev);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	dev->priv_flags &= ~IFF_BRIDGE_PORT;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	netdev_rx_handler_unregister(dev);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	br_multicast_del_port(p);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	kobject_uevent(&p->kobj, KOBJ_REMOVE);
37262306a36Sopenharmony_ci	kobject_del(&p->kobj);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	br_netpoll_disable(p);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	call_rcu(&p->rcu, destroy_nbp_rcu);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci/* Delete bridge device */
38062306a36Sopenharmony_civoid br_dev_delete(struct net_device *dev, struct list_head *head)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
38362306a36Sopenharmony_ci	struct net_bridge_port *p, *n;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	list_for_each_entry_safe(p, n, &br->port_list, list) {
38662306a36Sopenharmony_ci		del_nbp(p);
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	br_recalculate_neigh_suppress_enabled(br);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	br_fdb_delete_by_port(br, NULL, 0, 1);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	cancel_delayed_work_sync(&br->gc_work);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	br_sysfs_delbr(br->dev);
39662306a36Sopenharmony_ci	unregister_netdevice_queue(br->dev, head);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/* find an available port number */
40062306a36Sopenharmony_cistatic int find_portno(struct net_bridge *br)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	int index;
40362306a36Sopenharmony_ci	struct net_bridge_port *p;
40462306a36Sopenharmony_ci	unsigned long *inuse;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	inuse = bitmap_zalloc(BR_MAX_PORTS, GFP_KERNEL);
40762306a36Sopenharmony_ci	if (!inuse)
40862306a36Sopenharmony_ci		return -ENOMEM;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	__set_bit(0, inuse);	/* zero is reserved */
41162306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list)
41262306a36Sopenharmony_ci		__set_bit(p->port_no, inuse);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	index = find_first_zero_bit(inuse, BR_MAX_PORTS);
41562306a36Sopenharmony_ci	bitmap_free(inuse);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return (index >= BR_MAX_PORTS) ? -EXFULL : index;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci/* called with RTNL but without bridge lock */
42162306a36Sopenharmony_cistatic struct net_bridge_port *new_nbp(struct net_bridge *br,
42262306a36Sopenharmony_ci				       struct net_device *dev)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct net_bridge_port *p;
42562306a36Sopenharmony_ci	int index, err;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	index = find_portno(br);
42862306a36Sopenharmony_ci	if (index < 0)
42962306a36Sopenharmony_ci		return ERR_PTR(index);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	p = kzalloc(sizeof(*p), GFP_KERNEL);
43262306a36Sopenharmony_ci	if (p == NULL)
43362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	p->br = br;
43662306a36Sopenharmony_ci	netdev_hold(dev, &p->dev_tracker, GFP_KERNEL);
43762306a36Sopenharmony_ci	p->dev = dev;
43862306a36Sopenharmony_ci	p->path_cost = port_cost(dev);
43962306a36Sopenharmony_ci	p->priority = 0x8000 >> BR_PORT_BITS;
44062306a36Sopenharmony_ci	p->port_no = index;
44162306a36Sopenharmony_ci	p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
44262306a36Sopenharmony_ci	br_init_port(p);
44362306a36Sopenharmony_ci	br_set_state(p, BR_STATE_DISABLED);
44462306a36Sopenharmony_ci	br_stp_port_timer_init(p);
44562306a36Sopenharmony_ci	err = br_multicast_add_port(p);
44662306a36Sopenharmony_ci	if (err) {
44762306a36Sopenharmony_ci		netdev_put(dev, &p->dev_tracker);
44862306a36Sopenharmony_ci		kfree(p);
44962306a36Sopenharmony_ci		p = ERR_PTR(err);
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return p;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ciint br_add_bridge(struct net *net, const char *name)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct net_device *dev;
45862306a36Sopenharmony_ci	int res;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	dev = alloc_netdev(sizeof(struct net_bridge), name, NET_NAME_UNKNOWN,
46162306a36Sopenharmony_ci			   br_dev_setup);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (!dev)
46462306a36Sopenharmony_ci		return -ENOMEM;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	dev_net_set(dev, net);
46762306a36Sopenharmony_ci	dev->rtnl_link_ops = &br_link_ops;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	res = register_netdevice(dev);
47062306a36Sopenharmony_ci	if (res)
47162306a36Sopenharmony_ci		free_netdev(dev);
47262306a36Sopenharmony_ci	return res;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciint br_del_bridge(struct net *net, const char *name)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct net_device *dev;
47862306a36Sopenharmony_ci	int ret = 0;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	dev = __dev_get_by_name(net, name);
48162306a36Sopenharmony_ci	if (dev == NULL)
48262306a36Sopenharmony_ci		ret =  -ENXIO; 	/* Could not find device */
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	else if (!netif_is_bridge_master(dev)) {
48562306a36Sopenharmony_ci		/* Attempt to delete non bridge device! */
48662306a36Sopenharmony_ci		ret = -EPERM;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	else if (dev->flags & IFF_UP) {
49062306a36Sopenharmony_ci		/* Not shutdown yet. */
49162306a36Sopenharmony_ci		ret = -EBUSY;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	else
49562306a36Sopenharmony_ci		br_dev_delete(dev, NULL);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return ret;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/* MTU of the bridge pseudo-device: ETH_DATA_LEN or the minimum of the ports */
50162306a36Sopenharmony_cistatic int br_mtu_min(const struct net_bridge *br)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	const struct net_bridge_port *p;
50462306a36Sopenharmony_ci	int ret_mtu = 0;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list)
50762306a36Sopenharmony_ci		if (!ret_mtu || ret_mtu > p->dev->mtu)
50862306a36Sopenharmony_ci			ret_mtu = p->dev->mtu;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return ret_mtu ? ret_mtu : ETH_DATA_LEN;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_civoid br_mtu_auto_adjust(struct net_bridge *br)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	ASSERT_RTNL();
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* if the bridge MTU was manually configured don't mess with it */
51862306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_MTU_SET_BY_USER))
51962306a36Sopenharmony_ci		return;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* change to the minimum MTU and clear the flag which was set by
52262306a36Sopenharmony_ci	 * the bridge ndo_change_mtu callback
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci	dev_set_mtu(br->dev, br_mtu_min(br));
52562306a36Sopenharmony_ci	br_opt_toggle(br, BROPT_MTU_SET_BY_USER, false);
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic void br_set_gso_limits(struct net_bridge *br)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	unsigned int tso_max_size = TSO_MAX_SIZE;
53162306a36Sopenharmony_ci	const struct net_bridge_port *p;
53262306a36Sopenharmony_ci	u16 tso_max_segs = TSO_MAX_SEGS;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
53562306a36Sopenharmony_ci		tso_max_size = min(tso_max_size, p->dev->tso_max_size);
53662306a36Sopenharmony_ci		tso_max_segs = min(tso_max_segs, p->dev->tso_max_segs);
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci	netif_set_tso_max_size(br->dev, tso_max_size);
53962306a36Sopenharmony_ci	netif_set_tso_max_segs(br->dev, tso_max_segs);
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci/*
54362306a36Sopenharmony_ci * Recomputes features using slave's features
54462306a36Sopenharmony_ci */
54562306a36Sopenharmony_cinetdev_features_t br_features_recompute(struct net_bridge *br,
54662306a36Sopenharmony_ci	netdev_features_t features)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct net_bridge_port *p;
54962306a36Sopenharmony_ci	netdev_features_t mask;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (list_empty(&br->port_list))
55262306a36Sopenharmony_ci		return features;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	mask = features;
55562306a36Sopenharmony_ci	features &= ~NETIF_F_ONE_FOR_ALL;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
55862306a36Sopenharmony_ci		features = netdev_increment_features(features,
55962306a36Sopenharmony_ci						     p->dev->features, mask);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci	features = netdev_add_tso_features(features, mask);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return features;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci/* called with RTNL */
56762306a36Sopenharmony_ciint br_add_if(struct net_bridge *br, struct net_device *dev,
56862306a36Sopenharmony_ci	      struct netlink_ext_ack *extack)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct net_bridge_port *p;
57162306a36Sopenharmony_ci	int err = 0;
57262306a36Sopenharmony_ci	unsigned br_hr, dev_hr;
57362306a36Sopenharmony_ci	bool changed_addr, fdb_synced = false;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* Don't allow bridging non-ethernet like devices. */
57662306a36Sopenharmony_ci	if ((dev->flags & IFF_LOOPBACK) ||
57762306a36Sopenharmony_ci	    dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN ||
57862306a36Sopenharmony_ci	    !is_valid_ether_addr(dev->dev_addr))
57962306a36Sopenharmony_ci		return -EINVAL;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/* No bridging of bridges */
58262306a36Sopenharmony_ci	if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit) {
58362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
58462306a36Sopenharmony_ci			       "Can not enslave a bridge to a bridge");
58562306a36Sopenharmony_ci		return -ELOOP;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	/* Device has master upper dev */
58962306a36Sopenharmony_ci	if (netdev_master_upper_dev_get(dev))
59062306a36Sopenharmony_ci		return -EBUSY;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* No bridging devices that dislike that (e.g. wireless) */
59362306a36Sopenharmony_ci	if (dev->priv_flags & IFF_DONT_BRIDGE) {
59462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
59562306a36Sopenharmony_ci			       "Device does not allow enslaving to a bridge");
59662306a36Sopenharmony_ci		return -EOPNOTSUPP;
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	p = new_nbp(br, dev);
60062306a36Sopenharmony_ci	if (IS_ERR(p))
60162306a36Sopenharmony_ci		return PTR_ERR(p);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	call_netdevice_notifiers(NETDEV_JOIN, dev);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	err = dev_set_allmulti(dev, 1);
60662306a36Sopenharmony_ci	if (err) {
60762306a36Sopenharmony_ci		br_multicast_del_port(p);
60862306a36Sopenharmony_ci		netdev_put(dev, &p->dev_tracker);
60962306a36Sopenharmony_ci		kfree(p);	/* kobject not yet init'd, manually free */
61062306a36Sopenharmony_ci		goto err1;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj),
61462306a36Sopenharmony_ci				   SYSFS_BRIDGE_PORT_ATTR);
61562306a36Sopenharmony_ci	if (err)
61662306a36Sopenharmony_ci		goto err2;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	err = br_sysfs_addif(p);
61962306a36Sopenharmony_ci	if (err)
62062306a36Sopenharmony_ci		goto err2;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	err = br_netpoll_enable(p);
62362306a36Sopenharmony_ci	if (err)
62462306a36Sopenharmony_ci		goto err3;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	err = netdev_rx_handler_register(dev, br_get_rx_handler(dev), p);
62762306a36Sopenharmony_ci	if (err)
62862306a36Sopenharmony_ci		goto err4;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	dev->priv_flags |= IFF_BRIDGE_PORT;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL, extack);
63362306a36Sopenharmony_ci	if (err)
63462306a36Sopenharmony_ci		goto err5;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	dev_disable_lro(dev);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	list_add_rcu(&p->list, &br->port_list);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	nbp_update_port_count(br);
64162306a36Sopenharmony_ci	if (!br_promisc_port(p) && (p->dev->priv_flags & IFF_UNICAST_FLT)) {
64262306a36Sopenharmony_ci		/* When updating the port count we also update all ports'
64362306a36Sopenharmony_ci		 * promiscuous mode.
64462306a36Sopenharmony_ci		 * A port leaving promiscuous mode normally gets the bridge's
64562306a36Sopenharmony_ci		 * fdb synced to the unicast filter (if supported), however,
64662306a36Sopenharmony_ci		 * `br_port_clear_promisc` does not distinguish between
64762306a36Sopenharmony_ci		 * non-promiscuous ports and *new* ports, so we need to
64862306a36Sopenharmony_ci		 * sync explicitly here.
64962306a36Sopenharmony_ci		 */
65062306a36Sopenharmony_ci		fdb_synced = br_fdb_sync_static(br, p) == 0;
65162306a36Sopenharmony_ci		if (!fdb_synced)
65262306a36Sopenharmony_ci			netdev_err(dev, "failed to sync bridge static fdb addresses to this port\n");
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	netdev_update_features(br->dev);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	br_hr = br->dev->needed_headroom;
65862306a36Sopenharmony_ci	dev_hr = netdev_get_fwd_headroom(dev);
65962306a36Sopenharmony_ci	if (br_hr < dev_hr)
66062306a36Sopenharmony_ci		update_headroom(br, dev_hr);
66162306a36Sopenharmony_ci	else
66262306a36Sopenharmony_ci		netdev_set_rx_headroom(dev, br_hr);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (br_fdb_add_local(br, p, dev->dev_addr, 0))
66562306a36Sopenharmony_ci		netdev_err(dev, "failed insert local address bridge forwarding table\n");
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (br->dev->addr_assign_type != NET_ADDR_SET) {
66862306a36Sopenharmony_ci		/* Ask for permission to use this MAC address now, even if we
66962306a36Sopenharmony_ci		 * don't end up choosing it below.
67062306a36Sopenharmony_ci		 */
67162306a36Sopenharmony_ci		err = dev_pre_changeaddr_notify(br->dev, dev->dev_addr, extack);
67262306a36Sopenharmony_ci		if (err)
67362306a36Sopenharmony_ci			goto err6;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	err = nbp_vlan_init(p, extack);
67762306a36Sopenharmony_ci	if (err) {
67862306a36Sopenharmony_ci		netdev_err(dev, "failed to initialize vlan filtering on this port\n");
67962306a36Sopenharmony_ci		goto err6;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	spin_lock_bh(&br->lock);
68362306a36Sopenharmony_ci	changed_addr = br_stp_recalculate_bridge_id(br);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (netif_running(dev) && netif_oper_up(dev) &&
68662306a36Sopenharmony_ci	    (br->dev->flags & IFF_UP))
68762306a36Sopenharmony_ci		br_stp_enable_port(p);
68862306a36Sopenharmony_ci	spin_unlock_bh(&br->lock);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	br_ifinfo_notify(RTM_NEWLINK, NULL, p);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (changed_addr)
69362306a36Sopenharmony_ci		call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	br_mtu_auto_adjust(br);
69662306a36Sopenharmony_ci	br_set_gso_limits(br);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	kobject_uevent(&p->kobj, KOBJ_ADD);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	return 0;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cierr6:
70362306a36Sopenharmony_ci	if (fdb_synced)
70462306a36Sopenharmony_ci		br_fdb_unsync_static(br, p);
70562306a36Sopenharmony_ci	list_del_rcu(&p->list);
70662306a36Sopenharmony_ci	br_fdb_delete_by_port(br, p, 0, 1);
70762306a36Sopenharmony_ci	nbp_update_port_count(br);
70862306a36Sopenharmony_ci	netdev_upper_dev_unlink(dev, br->dev);
70962306a36Sopenharmony_cierr5:
71062306a36Sopenharmony_ci	dev->priv_flags &= ~IFF_BRIDGE_PORT;
71162306a36Sopenharmony_ci	netdev_rx_handler_unregister(dev);
71262306a36Sopenharmony_cierr4:
71362306a36Sopenharmony_ci	br_netpoll_disable(p);
71462306a36Sopenharmony_cierr3:
71562306a36Sopenharmony_ci	sysfs_remove_link(br->ifobj, p->dev->name);
71662306a36Sopenharmony_cierr2:
71762306a36Sopenharmony_ci	br_multicast_del_port(p);
71862306a36Sopenharmony_ci	netdev_put(dev, &p->dev_tracker);
71962306a36Sopenharmony_ci	kobject_put(&p->kobj);
72062306a36Sopenharmony_ci	dev_set_allmulti(dev, -1);
72162306a36Sopenharmony_cierr1:
72262306a36Sopenharmony_ci	return err;
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci/* called with RTNL */
72662306a36Sopenharmony_ciint br_del_if(struct net_bridge *br, struct net_device *dev)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	struct net_bridge_port *p;
72962306a36Sopenharmony_ci	bool changed_addr;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	p = br_port_get_rtnl(dev);
73262306a36Sopenharmony_ci	if (!p || p->br != br)
73362306a36Sopenharmony_ci		return -EINVAL;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	/* Since more than one interface can be attached to a bridge,
73662306a36Sopenharmony_ci	 * there still maybe an alternate path for netconsole to use;
73762306a36Sopenharmony_ci	 * therefore there is no reason for a NETDEV_RELEASE event.
73862306a36Sopenharmony_ci	 */
73962306a36Sopenharmony_ci	del_nbp(p);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	br_mtu_auto_adjust(br);
74262306a36Sopenharmony_ci	br_set_gso_limits(br);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	spin_lock_bh(&br->lock);
74562306a36Sopenharmony_ci	changed_addr = br_stp_recalculate_bridge_id(br);
74662306a36Sopenharmony_ci	spin_unlock_bh(&br->lock);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (changed_addr)
74962306a36Sopenharmony_ci		call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	netdev_update_features(br->dev);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	return 0;
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_civoid br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	struct net_bridge *br = p->br;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	if (mask & BR_AUTO_MASK)
76162306a36Sopenharmony_ci		nbp_update_port_count(br);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	if (mask & (BR_NEIGH_SUPPRESS | BR_NEIGH_VLAN_SUPPRESS))
76462306a36Sopenharmony_ci		br_recalculate_neigh_suppress_enabled(br);
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cibool br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	struct net_bridge_port *p;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	p = br_port_get_rtnl_rcu(dev);
77262306a36Sopenharmony_ci	if (!p)
77362306a36Sopenharmony_ci		return false;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return p->flags & flag;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_port_flag_is_set);
778