diff options
Diffstat (limited to 'net/bridge/br.c')
| -rw-r--r-- | net/bridge/br.c | 174 |
1 files changed, 147 insertions, 27 deletions
diff --git a/net/bridge/br.c b/net/bridge/br.c index a9018287312..1a755a1e541 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek <buytenh@gnu.org> * - * $Id: br.c,v 1.47 2001/12/24 00:56:41 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version @@ -20,81 +18,203 @@ #include <linux/init.h> #include <linux/llc.h> #include <net/llc.h> +#include <net/stp.h> #include "br_private.h" -int (*br_should_route_hook)(struct sk_buff *skb); +/* + * Handle changes in state of network devices enslaved to a bridge. + * + * Note: don't care about up/down if bridge itself is down, because + * port state is checked when bridge is brought up. + */ +static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct net_bridge_port *p; + struct net_bridge *br; + bool changed_addr; + int err; + + /* register of bridge completed, add sysfs entries */ + if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) { + br_sysfs_addbr(dev); + return NOTIFY_DONE; + } + + /* not a port of a bridge */ + p = br_port_get_rtnl(dev); + if (!p) + return NOTIFY_DONE; + + br = p->br; + + switch (event) { + case NETDEV_CHANGEMTU: + dev_set_mtu(br->dev, br_min_mtu(br)); + break; + + case NETDEV_CHANGEADDR: + spin_lock_bh(&br->lock); + br_fdb_changeaddr(p, dev->dev_addr); + changed_addr = br_stp_recalculate_bridge_id(br); + spin_unlock_bh(&br->lock); + + if (changed_addr) + call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); + + break; + + case NETDEV_CHANGE: + br_port_carrier_check(p); + break; + + case NETDEV_FEAT_CHANGE: + netdev_update_features(br->dev); + break; + + case NETDEV_DOWN: + spin_lock_bh(&br->lock); + if (br->dev->flags & IFF_UP) + br_stp_disable_port(p); + spin_unlock_bh(&br->lock); + break; + + case NETDEV_UP: + if (netif_running(br->dev) && netif_oper_up(dev)) { + spin_lock_bh(&br->lock); + br_stp_enable_port(p); + spin_unlock_bh(&br->lock); + } + break; + + case NETDEV_UNREGISTER: + br_del_if(br, dev); + break; + + case NETDEV_CHANGENAME: + err = br_sysfs_renameif(p); + if (err) + return notifier_from_errno(err); + break; + + case NETDEV_PRE_TYPE_CHANGE: + /* Forbid underlaying device to change its type. */ + return NOTIFY_BAD; + + case NETDEV_RESEND_IGMP: + /* Propagate to master device */ + call_netdevice_notifiers(event, br->dev); + break; + } + + /* Events that may cause spanning tree to refresh */ + if (event == NETDEV_CHANGEADDR || event == NETDEV_UP || + event == NETDEV_CHANGE || event == NETDEV_DOWN) + br_ifinfo_notify(RTM_NEWLINK, p); + + return NOTIFY_DONE; +} + +static struct notifier_block br_device_notifier = { + .notifier_call = br_device_event +}; + +static void __net_exit br_net_exit(struct net *net) +{ + struct net_device *dev; + LIST_HEAD(list); + + rtnl_lock(); + for_each_netdev(net, dev) + if (dev->priv_flags & IFF_EBRIDGE) + br_dev_delete(dev, &list); + + unregister_netdevice_many(&list); + rtnl_unlock(); -static struct llc_sap *br_stp_sap; +} + +static struct pernet_operations br_net_ops = { + .exit = br_net_exit, +}; + +static const struct stp_proto br_stp_proto = { + .rcv = br_stp_rcv, +}; static int __init br_init(void) { int err; - br_stp_sap = llc_sap_open(LLC_SAP_BSPAN, br_stp_rcv); - if (!br_stp_sap) { - printk(KERN_ERR "bridge: can't register sap for STP\n"); - return -EADDRINUSE; + err = stp_proto_register(&br_stp_proto); + if (err < 0) { + pr_err("bridge: can't register sap for STP\n"); + return err; } err = br_fdb_init(); if (err) goto err_out; - err = br_netfilter_init(); + err = register_pernet_subsys(&br_net_ops); if (err) goto err_out1; - err = register_netdevice_notifier(&br_device_notifier); + err = br_netfilter_init(); if (err) goto err_out2; - err = br_netlink_init(); + err = register_netdevice_notifier(&br_device_notifier); if (err) goto err_out3; + err = br_netlink_init(); + if (err) + goto err_out4; + brioctl_set(br_ioctl_deviceless_stub); - br_handle_frame_hook = br_handle_frame; - br_fdb_get_hook = br_fdb_get; - br_fdb_put_hook = br_fdb_put; +#if IS_ENABLED(CONFIG_ATM_LANE) + br_fdb_test_addr_hook = br_fdb_test_addr; +#endif return 0; -err_out3: +err_out4: unregister_netdevice_notifier(&br_device_notifier); -err_out2: +err_out3: br_netfilter_fini(); +err_out2: + unregister_pernet_subsys(&br_net_ops); err_out1: br_fdb_fini(); err_out: - llc_sap_put(br_stp_sap); + stp_proto_unregister(&br_stp_proto); return err; } static void __exit br_deinit(void) { - rcu_assign_pointer(br_stp_sap->rcv_func, NULL); + stp_proto_unregister(&br_stp_proto); br_netlink_fini(); - br_netfilter_fini(); unregister_netdevice_notifier(&br_device_notifier); brioctl_set(NULL); - br_cleanup_bridges(); + unregister_pernet_subsys(&br_net_ops); - synchronize_net(); + rcu_barrier(); /* Wait for completion of call_rcu()'s */ - llc_sap_put(br_stp_sap); - br_fdb_get_hook = NULL; - br_fdb_put_hook = NULL; + br_netfilter_fini(); +#if IS_ENABLED(CONFIG_ATM_LANE) + br_fdb_test_addr_hook = NULL; +#endif - br_handle_frame_hook = NULL; br_fdb_fini(); } -EXPORT_SYMBOL(br_should_route_hook); - module_init(br_init) module_exit(br_deinit) MODULE_LICENSE("GPL"); MODULE_VERSION(BR_VERSION); +MODULE_ALIAS_RTNL_LINK("bridge"); |
