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, ¬ified); 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