162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	Generic parts
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/module.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/netdevice.h>
1362306a36Sopenharmony_ci#include <linux/etherdevice.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/llc.h>
1662306a36Sopenharmony_ci#include <net/llc.h>
1762306a36Sopenharmony_ci#include <net/stp.h>
1862306a36Sopenharmony_ci#include <net/switchdev.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "br_private.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * Handle changes in state of network devices enslaved to a bridge.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * Note: don't care about up/down if bridge itself is down, because
2662306a36Sopenharmony_ci *     port state is checked when bridge is brought up.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistatic int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
3162306a36Sopenharmony_ci	struct netdev_notifier_pre_changeaddr_info *prechaddr_info;
3262306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3362306a36Sopenharmony_ci	struct net_bridge_port *p;
3462306a36Sopenharmony_ci	struct net_bridge *br;
3562306a36Sopenharmony_ci	bool notified = false;
3662306a36Sopenharmony_ci	bool changed_addr;
3762306a36Sopenharmony_ci	int err;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (netif_is_bridge_master(dev)) {
4062306a36Sopenharmony_ci		err = br_vlan_bridge_event(dev, event, ptr);
4162306a36Sopenharmony_ci		if (err)
4262306a36Sopenharmony_ci			return notifier_from_errno(err);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci		if (event == NETDEV_REGISTER) {
4562306a36Sopenharmony_ci			/* register of bridge completed, add sysfs entries */
4662306a36Sopenharmony_ci			err = br_sysfs_addbr(dev);
4762306a36Sopenharmony_ci			if (err)
4862306a36Sopenharmony_ci				return notifier_from_errno(err);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci			return NOTIFY_DONE;
5162306a36Sopenharmony_ci		}
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* not a port of a bridge */
5562306a36Sopenharmony_ci	p = br_port_get_rtnl(dev);
5662306a36Sopenharmony_ci	if (!p)
5762306a36Sopenharmony_ci		return NOTIFY_DONE;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	br = p->br;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	switch (event) {
6262306a36Sopenharmony_ci	case NETDEV_CHANGEMTU:
6362306a36Sopenharmony_ci		br_mtu_auto_adjust(br);
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	case NETDEV_PRE_CHANGEADDR:
6762306a36Sopenharmony_ci		if (br->dev->addr_assign_type == NET_ADDR_SET)
6862306a36Sopenharmony_ci			break;
6962306a36Sopenharmony_ci		prechaddr_info = ptr;
7062306a36Sopenharmony_ci		err = dev_pre_changeaddr_notify(br->dev,
7162306a36Sopenharmony_ci						prechaddr_info->dev_addr,
7262306a36Sopenharmony_ci						extack);
7362306a36Sopenharmony_ci		if (err)
7462306a36Sopenharmony_ci			return notifier_from_errno(err);
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	case NETDEV_CHANGEADDR:
7862306a36Sopenharmony_ci		spin_lock_bh(&br->lock);
7962306a36Sopenharmony_ci		br_fdb_changeaddr(p, dev->dev_addr);
8062306a36Sopenharmony_ci		changed_addr = br_stp_recalculate_bridge_id(br);
8162306a36Sopenharmony_ci		spin_unlock_bh(&br->lock);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		if (changed_addr)
8462306a36Sopenharmony_ci			call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		break;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	case NETDEV_CHANGE:
8962306a36Sopenharmony_ci		br_port_carrier_check(p, &notified);
9062306a36Sopenharmony_ci		break;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	case NETDEV_FEAT_CHANGE:
9362306a36Sopenharmony_ci		netdev_update_features(br->dev);
9462306a36Sopenharmony_ci		break;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	case NETDEV_DOWN:
9762306a36Sopenharmony_ci		spin_lock_bh(&br->lock);
9862306a36Sopenharmony_ci		if (br->dev->flags & IFF_UP) {
9962306a36Sopenharmony_ci			br_stp_disable_port(p);
10062306a36Sopenharmony_ci			notified = true;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci		spin_unlock_bh(&br->lock);
10362306a36Sopenharmony_ci		break;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	case NETDEV_UP:
10662306a36Sopenharmony_ci		if (netif_running(br->dev) && netif_oper_up(dev)) {
10762306a36Sopenharmony_ci			spin_lock_bh(&br->lock);
10862306a36Sopenharmony_ci			br_stp_enable_port(p);
10962306a36Sopenharmony_ci			notified = true;
11062306a36Sopenharmony_ci			spin_unlock_bh(&br->lock);
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
11562306a36Sopenharmony_ci		br_del_if(br, dev);
11662306a36Sopenharmony_ci		break;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	case NETDEV_CHANGENAME:
11962306a36Sopenharmony_ci		err = br_sysfs_renameif(p);
12062306a36Sopenharmony_ci		if (err)
12162306a36Sopenharmony_ci			return notifier_from_errno(err);
12262306a36Sopenharmony_ci		break;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	case NETDEV_PRE_TYPE_CHANGE:
12562306a36Sopenharmony_ci		/* Forbid underlying device to change its type. */
12662306a36Sopenharmony_ci		return NOTIFY_BAD;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	case NETDEV_RESEND_IGMP:
12962306a36Sopenharmony_ci		/* Propagate to master device */
13062306a36Sopenharmony_ci		call_netdevice_notifiers(event, br->dev);
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (event != NETDEV_UNREGISTER)
13562306a36Sopenharmony_ci		br_vlan_port_event(p, event);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Events that may cause spanning tree to refresh */
13862306a36Sopenharmony_ci	if (!notified && (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
13962306a36Sopenharmony_ci			  event == NETDEV_CHANGE || event == NETDEV_DOWN))
14062306a36Sopenharmony_ci		br_ifinfo_notify(RTM_NEWLINK, NULL, p);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return NOTIFY_DONE;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic struct notifier_block br_device_notifier = {
14662306a36Sopenharmony_ci	.notifier_call = br_device_event
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/* called with RTNL or RCU */
15062306a36Sopenharmony_cistatic int br_switchdev_event(struct notifier_block *unused,
15162306a36Sopenharmony_ci			      unsigned long event, void *ptr)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
15462306a36Sopenharmony_ci	struct net_bridge_port *p;
15562306a36Sopenharmony_ci	struct net_bridge *br;
15662306a36Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
15762306a36Sopenharmony_ci	int err = NOTIFY_DONE;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	p = br_port_get_rtnl_rcu(dev);
16062306a36Sopenharmony_ci	if (!p)
16162306a36Sopenharmony_ci		goto out;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	br = p->br;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	switch (event) {
16662306a36Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_BRIDGE:
16762306a36Sopenharmony_ci		fdb_info = ptr;
16862306a36Sopenharmony_ci		err = br_fdb_external_learn_add(br, p, fdb_info->addr,
16962306a36Sopenharmony_ci						fdb_info->vid,
17062306a36Sopenharmony_ci						fdb_info->locked, false);
17162306a36Sopenharmony_ci		if (err) {
17262306a36Sopenharmony_ci			err = notifier_from_errno(err);
17362306a36Sopenharmony_ci			break;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci		br_fdb_offloaded_set(br, p, fdb_info->addr,
17662306a36Sopenharmony_ci				     fdb_info->vid, fdb_info->offloaded);
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_BRIDGE:
17962306a36Sopenharmony_ci		fdb_info = ptr;
18062306a36Sopenharmony_ci		err = br_fdb_external_learn_del(br, p, fdb_info->addr,
18162306a36Sopenharmony_ci						fdb_info->vid, false);
18262306a36Sopenharmony_ci		if (err)
18362306a36Sopenharmony_ci			err = notifier_from_errno(err);
18462306a36Sopenharmony_ci		break;
18562306a36Sopenharmony_ci	case SWITCHDEV_FDB_OFFLOADED:
18662306a36Sopenharmony_ci		fdb_info = ptr;
18762306a36Sopenharmony_ci		br_fdb_offloaded_set(br, p, fdb_info->addr,
18862306a36Sopenharmony_ci				     fdb_info->vid, fdb_info->offloaded);
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	case SWITCHDEV_FDB_FLUSH_TO_BRIDGE:
19162306a36Sopenharmony_ci		fdb_info = ptr;
19262306a36Sopenharmony_ci		/* Don't delete static entries */
19362306a36Sopenharmony_ci		br_fdb_delete_by_port(br, p, fdb_info->vid, 0);
19462306a36Sopenharmony_ci		break;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ciout:
19862306a36Sopenharmony_ci	return err;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic struct notifier_block br_switchdev_notifier = {
20262306a36Sopenharmony_ci	.notifier_call = br_switchdev_event,
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/* called under rtnl_mutex */
20662306a36Sopenharmony_cistatic int br_switchdev_blocking_event(struct notifier_block *nb,
20762306a36Sopenharmony_ci				       unsigned long event, void *ptr)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
21062306a36Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
21162306a36Sopenharmony_ci	struct switchdev_notifier_brport_info *brport_info;
21262306a36Sopenharmony_ci	const struct switchdev_brport *b;
21362306a36Sopenharmony_ci	struct net_bridge_port *p;
21462306a36Sopenharmony_ci	int err = NOTIFY_DONE;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	p = br_port_get_rtnl(dev);
21762306a36Sopenharmony_ci	if (!p)
21862306a36Sopenharmony_ci		goto out;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	switch (event) {
22162306a36Sopenharmony_ci	case SWITCHDEV_BRPORT_OFFLOADED:
22262306a36Sopenharmony_ci		brport_info = ptr;
22362306a36Sopenharmony_ci		b = &brport_info->brport;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		err = br_switchdev_port_offload(p, b->dev, b->ctx,
22662306a36Sopenharmony_ci						b->atomic_nb, b->blocking_nb,
22762306a36Sopenharmony_ci						b->tx_fwd_offload, extack);
22862306a36Sopenharmony_ci		err = notifier_from_errno(err);
22962306a36Sopenharmony_ci		break;
23062306a36Sopenharmony_ci	case SWITCHDEV_BRPORT_UNOFFLOADED:
23162306a36Sopenharmony_ci		brport_info = ptr;
23262306a36Sopenharmony_ci		b = &brport_info->brport;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		br_switchdev_port_unoffload(p, b->ctx, b->atomic_nb,
23562306a36Sopenharmony_ci					    b->blocking_nb);
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci	case SWITCHDEV_BRPORT_REPLAY:
23862306a36Sopenharmony_ci		brport_info = ptr;
23962306a36Sopenharmony_ci		b = &brport_info->brport;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		err = br_switchdev_port_replay(p, b->dev, b->ctx, b->atomic_nb,
24262306a36Sopenharmony_ci					       b->blocking_nb, extack);
24362306a36Sopenharmony_ci		err = notifier_from_errno(err);
24462306a36Sopenharmony_ci		break;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ciout:
24862306a36Sopenharmony_ci	return err;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic struct notifier_block br_switchdev_blocking_notifier = {
25262306a36Sopenharmony_ci	.notifier_call = br_switchdev_blocking_event,
25362306a36Sopenharmony_ci};
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci/* br_boolopt_toggle - change user-controlled boolean option
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci * @br: bridge device
25862306a36Sopenharmony_ci * @opt: id of the option to change
25962306a36Sopenharmony_ci * @on: new option value
26062306a36Sopenharmony_ci * @extack: extack for error messages
26162306a36Sopenharmony_ci *
26262306a36Sopenharmony_ci * Changes the value of the respective boolean option to @on taking care of
26362306a36Sopenharmony_ci * any internal option value mapping and configuration.
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_ciint br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
26662306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int err = 0;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	switch (opt) {
27162306a36Sopenharmony_ci	case BR_BOOLOPT_NO_LL_LEARN:
27262306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_NO_LL_LEARN, on);
27362306a36Sopenharmony_ci		break;
27462306a36Sopenharmony_ci	case BR_BOOLOPT_MCAST_VLAN_SNOOPING:
27562306a36Sopenharmony_ci		err = br_multicast_toggle_vlan_snooping(br, on, extack);
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	case BR_BOOLOPT_MST_ENABLE:
27862306a36Sopenharmony_ci		err = br_mst_set_enabled(br, on, extack);
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	default:
28162306a36Sopenharmony_ci		/* shouldn't be called with unsupported options */
28262306a36Sopenharmony_ci		WARN_ON(1);
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return err;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ciint br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	switch (opt) {
29262306a36Sopenharmony_ci	case BR_BOOLOPT_NO_LL_LEARN:
29362306a36Sopenharmony_ci		return br_opt_get(br, BROPT_NO_LL_LEARN);
29462306a36Sopenharmony_ci	case BR_BOOLOPT_MCAST_VLAN_SNOOPING:
29562306a36Sopenharmony_ci		return br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED);
29662306a36Sopenharmony_ci	case BR_BOOLOPT_MST_ENABLE:
29762306a36Sopenharmony_ci		return br_opt_get(br, BROPT_MST_ENABLED);
29862306a36Sopenharmony_ci	default:
29962306a36Sopenharmony_ci		/* shouldn't be called with unsupported options */
30062306a36Sopenharmony_ci		WARN_ON(1);
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return 0;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ciint br_boolopt_multi_toggle(struct net_bridge *br,
30862306a36Sopenharmony_ci			    struct br_boolopt_multi *bm,
30962306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	unsigned long bitmap = bm->optmask;
31262306a36Sopenharmony_ci	int err = 0;
31362306a36Sopenharmony_ci	int opt_id;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	for_each_set_bit(opt_id, &bitmap, BR_BOOLOPT_MAX) {
31662306a36Sopenharmony_ci		bool on = !!(bm->optval & BIT(opt_id));
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		err = br_boolopt_toggle(br, opt_id, on, extack);
31962306a36Sopenharmony_ci		if (err) {
32062306a36Sopenharmony_ci			br_debug(br, "boolopt multi-toggle error: option: %d current: %d new: %d error: %d\n",
32162306a36Sopenharmony_ci				 opt_id, br_boolopt_get(br, opt_id), on, err);
32262306a36Sopenharmony_ci			break;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return err;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_civoid br_boolopt_multi_get(const struct net_bridge *br,
33062306a36Sopenharmony_ci			  struct br_boolopt_multi *bm)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	u32 optval = 0;
33362306a36Sopenharmony_ci	int opt_id;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	for (opt_id = 0; opt_id < BR_BOOLOPT_MAX; opt_id++)
33662306a36Sopenharmony_ci		optval |= (br_boolopt_get(br, opt_id) << opt_id);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	bm->optval = optval;
33962306a36Sopenharmony_ci	bm->optmask = GENMASK((BR_BOOLOPT_MAX - 1), 0);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/* private bridge options, controlled by the kernel */
34362306a36Sopenharmony_civoid br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	bool cur = !!br_opt_get(br, opt);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	br_debug(br, "toggle option: %d state: %d -> %d\n",
34862306a36Sopenharmony_ci		 opt, cur, on);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (cur == on)
35162306a36Sopenharmony_ci		return;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (on)
35462306a36Sopenharmony_ci		set_bit(opt, &br->options);
35562306a36Sopenharmony_ci	else
35662306a36Sopenharmony_ci		clear_bit(opt, &br->options);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic void __net_exit br_net_exit_batch(struct list_head *net_list)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct net_device *dev;
36262306a36Sopenharmony_ci	struct net *net;
36362306a36Sopenharmony_ci	LIST_HEAD(list);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	rtnl_lock();
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	list_for_each_entry(net, net_list, exit_list)
36862306a36Sopenharmony_ci		for_each_netdev(net, dev)
36962306a36Sopenharmony_ci			if (netif_is_bridge_master(dev))
37062306a36Sopenharmony_ci				br_dev_delete(dev, &list);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	unregister_netdevice_many(&list);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	rtnl_unlock();
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic struct pernet_operations br_net_ops = {
37862306a36Sopenharmony_ci	.exit_batch	= br_net_exit_batch,
37962306a36Sopenharmony_ci};
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic const struct stp_proto br_stp_proto = {
38262306a36Sopenharmony_ci	.rcv	= br_stp_rcv,
38362306a36Sopenharmony_ci};
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int __init br_init(void)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	int err;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct br_input_skb_cb) > sizeof_field(struct sk_buff, cb));
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	err = stp_proto_register(&br_stp_proto);
39262306a36Sopenharmony_ci	if (err < 0) {
39362306a36Sopenharmony_ci		pr_err("bridge: can't register sap for STP\n");
39462306a36Sopenharmony_ci		return err;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	err = br_fdb_init();
39862306a36Sopenharmony_ci	if (err)
39962306a36Sopenharmony_ci		goto err_out;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	err = register_pernet_subsys(&br_net_ops);
40262306a36Sopenharmony_ci	if (err)
40362306a36Sopenharmony_ci		goto err_out1;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	err = br_nf_core_init();
40662306a36Sopenharmony_ci	if (err)
40762306a36Sopenharmony_ci		goto err_out2;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	err = register_netdevice_notifier(&br_device_notifier);
41062306a36Sopenharmony_ci	if (err)
41162306a36Sopenharmony_ci		goto err_out3;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	err = register_switchdev_notifier(&br_switchdev_notifier);
41462306a36Sopenharmony_ci	if (err)
41562306a36Sopenharmony_ci		goto err_out4;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	err = register_switchdev_blocking_notifier(&br_switchdev_blocking_notifier);
41862306a36Sopenharmony_ci	if (err)
41962306a36Sopenharmony_ci		goto err_out5;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	err = br_netlink_init();
42262306a36Sopenharmony_ci	if (err)
42362306a36Sopenharmony_ci		goto err_out6;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	brioctl_set(br_ioctl_stub);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ATM_LANE)
42862306a36Sopenharmony_ci	br_fdb_test_addr_hook = br_fdb_test_addr;
42962306a36Sopenharmony_ci#endif
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci#if IS_MODULE(CONFIG_BRIDGE_NETFILTER)
43262306a36Sopenharmony_ci	pr_info("bridge: filtering via arp/ip/ip6tables is no longer available "
43362306a36Sopenharmony_ci		"by default. Update your scripts to load br_netfilter if you "
43462306a36Sopenharmony_ci		"need this.\n");
43562306a36Sopenharmony_ci#endif
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	return 0;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cierr_out6:
44062306a36Sopenharmony_ci	unregister_switchdev_blocking_notifier(&br_switchdev_blocking_notifier);
44162306a36Sopenharmony_cierr_out5:
44262306a36Sopenharmony_ci	unregister_switchdev_notifier(&br_switchdev_notifier);
44362306a36Sopenharmony_cierr_out4:
44462306a36Sopenharmony_ci	unregister_netdevice_notifier(&br_device_notifier);
44562306a36Sopenharmony_cierr_out3:
44662306a36Sopenharmony_ci	br_nf_core_fini();
44762306a36Sopenharmony_cierr_out2:
44862306a36Sopenharmony_ci	unregister_pernet_subsys(&br_net_ops);
44962306a36Sopenharmony_cierr_out1:
45062306a36Sopenharmony_ci	br_fdb_fini();
45162306a36Sopenharmony_cierr_out:
45262306a36Sopenharmony_ci	stp_proto_unregister(&br_stp_proto);
45362306a36Sopenharmony_ci	return err;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void __exit br_deinit(void)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	stp_proto_unregister(&br_stp_proto);
45962306a36Sopenharmony_ci	br_netlink_fini();
46062306a36Sopenharmony_ci	unregister_switchdev_blocking_notifier(&br_switchdev_blocking_notifier);
46162306a36Sopenharmony_ci	unregister_switchdev_notifier(&br_switchdev_notifier);
46262306a36Sopenharmony_ci	unregister_netdevice_notifier(&br_device_notifier);
46362306a36Sopenharmony_ci	brioctl_set(NULL);
46462306a36Sopenharmony_ci	unregister_pernet_subsys(&br_net_ops);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	rcu_barrier(); /* Wait for completion of call_rcu()'s */
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	br_nf_core_fini();
46962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ATM_LANE)
47062306a36Sopenharmony_ci	br_fdb_test_addr_hook = NULL;
47162306a36Sopenharmony_ci#endif
47262306a36Sopenharmony_ci	br_fdb_fini();
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cimodule_init(br_init)
47662306a36Sopenharmony_cimodule_exit(br_deinit)
47762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
47862306a36Sopenharmony_ciMODULE_VERSION(BR_VERSION);
47962306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("bridge");
480