2005-04-17 02:20:36 +04:00
/*
* Generic parts
* Linux ethernet bridge
*
* Authors :
* Lennert Buytenhek < buytenh @ gnu . org >
*
* 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
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/init.h>
2006-03-21 09:59:06 +03:00
# include <linux/llc.h>
# include <net/llc.h>
2008-07-06 08:25:56 +04:00
# include <net/stp.h>
2015-01-16 01:49:37 +03:00
# include <net/switchdev.h>
2005-04-17 02:20:36 +04:00
# include "br_private.h"
2014-05-21 04:30:00 +04:00
/*
* 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 )
{
2018-12-13 14:54:39 +03:00
struct netlink_ext_ack * extack = netdev_notifier_info_to_extack ( ptr ) ;
struct netdev_notifier_pre_changeaddr_info * prechaddr_info ;
2014-05-21 04:30:00 +04:00
struct net_device * dev = netdev_notifier_info_to_dev ( ptr ) ;
struct net_bridge_port * p ;
struct net_bridge * br ;
2018-05-03 13:47:24 +03:00
bool notified = false ;
2014-05-21 04:30:00 +04:00
bool changed_addr ;
int err ;
2019-04-18 20:35:33 +03:00
if ( dev - > priv_flags & IFF_EBRIDGE ) {
if ( event = = NETDEV_REGISTER ) {
/* register of bridge completed, add sysfs entries */
br_sysfs_addbr ( dev ) ;
return NOTIFY_DONE ;
}
br_vlan_bridge_event ( dev , event , ptr ) ;
2014-05-21 04:30:00 +04:00
}
/* 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 :
2018-03-30 13:46:19 +03:00
br_mtu_auto_adjust ( br ) ;
2014-05-21 04:30:00 +04:00
break ;
2018-12-13 14:54:39 +03:00
case NETDEV_PRE_CHANGEADDR :
if ( br - > dev - > addr_assign_type = = NET_ADDR_SET )
break ;
prechaddr_info = ptr ;
err = dev_pre_changeaddr_notify ( br - > dev ,
prechaddr_info - > dev_addr ,
extack ) ;
if ( err )
return notifier_from_errno ( err ) ;
break ;
2014-05-21 04:30:00 +04:00
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 :
2018-05-03 13:47:24 +03:00
br_port_carrier_check ( p , & notified ) ;
2014-05-21 04:30:00 +04:00
break ;
case NETDEV_FEAT_CHANGE :
netdev_update_features ( br - > dev ) ;
break ;
case NETDEV_DOWN :
spin_lock_bh ( & br - > lock ) ;
2018-05-03 13:47:24 +03:00
if ( br - > dev - > flags & IFF_UP ) {
2014-05-21 04:30:00 +04:00
br_stp_disable_port ( p ) ;
2018-05-03 13:47:24 +03:00
notified = true ;
}
2014-05-21 04:30:00 +04:00
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 ) ;
2018-05-03 13:47:24 +03:00
notified = true ;
2014-05-21 04:30:00 +04:00
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 ;
}
2019-04-22 12:33:19 +03:00
if ( event ! = NETDEV_UNREGISTER )
br_vlan_port_event ( p , event ) ;
2019-04-18 20:35:33 +03:00
2014-05-21 04:30:00 +04:00
/* Events that may cause spanning tree to refresh */
2018-05-03 13:47:24 +03:00
if ( ! notified & & ( event = = NETDEV_CHANGEADDR | | event = = NETDEV_UP | |
event = = NETDEV_CHANGE | | event = = NETDEV_DOWN ) )
2017-11-01 13:18:13 +03:00
br_ifinfo_notify ( RTM_NEWLINK , NULL , p ) ;
2014-05-21 04:30:00 +04:00
return NOTIFY_DONE ;
}
static struct notifier_block br_device_notifier = {
. notifier_call = br_device_event
} ;
2017-06-08 09:44:12 +03:00
/* called with RTNL or RCU */
2015-05-10 19:47:46 +03:00
static int br_switchdev_event ( struct notifier_block * unused ,
unsigned long event , void * ptr )
2015-01-16 01:49:37 +03:00
{
2015-05-10 19:47:46 +03:00
struct net_device * dev = switchdev_notifier_info_to_dev ( ptr ) ;
2015-01-16 01:49:37 +03:00
struct net_bridge_port * p ;
struct net_bridge * br ;
2015-05-10 19:47:46 +03:00
struct switchdev_notifier_fdb_info * fdb_info ;
2015-01-16 01:49:37 +03:00
int err = NOTIFY_DONE ;
2017-06-08 09:44:12 +03:00
p = br_port_get_rtnl_rcu ( dev ) ;
2015-01-16 01:49:37 +03:00
if ( ! p )
goto out ;
br = p - > br ;
switch ( event ) {
2017-06-08 09:44:14 +03:00
case SWITCHDEV_FDB_ADD_TO_BRIDGE :
2015-01-16 01:49:37 +03:00
fdb_info = ptr ;
err = br_fdb_external_learn_add ( br , p , fdb_info - > addr ,
2018-05-03 15:43:53 +03:00
fdb_info - > vid , false ) ;
2017-06-08 09:44:15 +03:00
if ( err ) {
2015-01-16 01:49:37 +03:00
err = notifier_from_errno ( err ) ;
2017-06-08 09:44:15 +03:00
break ;
}
br_fdb_offloaded_set ( br , p , fdb_info - > addr ,
2018-10-17 11:53:29 +03:00
fdb_info - > vid , true ) ;
2015-01-16 01:49:37 +03:00
break ;
2017-06-08 09:44:14 +03:00
case SWITCHDEV_FDB_DEL_TO_BRIDGE :
2015-01-16 01:49:37 +03:00
fdb_info = ptr ;
err = br_fdb_external_learn_del ( br , p , fdb_info - > addr ,
2018-05-03 15:43:53 +03:00
fdb_info - > vid , false ) ;
2015-01-16 01:49:37 +03:00
if ( err )
err = notifier_from_errno ( err ) ;
break ;
2017-06-08 09:44:15 +03:00
case SWITCHDEV_FDB_OFFLOADED :
fdb_info = ptr ;
br_fdb_offloaded_set ( br , p , fdb_info - > addr ,
2018-10-17 11:53:29 +03:00
fdb_info - > vid , fdb_info - > offloaded ) ;
2017-06-08 09:44:15 +03:00
break ;
2015-01-16 01:49:37 +03:00
}
out :
return err ;
}
2015-05-10 19:47:46 +03:00
static struct notifier_block br_switchdev_notifier = {
. notifier_call = br_switchdev_event ,
2015-01-16 01:49:37 +03:00
} ;
2018-11-24 05:34:20 +03:00
/* br_boolopt_toggle - change user-controlled boolean option
*
* @ br : bridge device
* @ opt : id of the option to change
* @ on : new option value
* @ extack : extack for error messages
*
* Changes the value of the respective boolean option to @ on taking care of
* any internal option value mapping and configuration .
*/
int br_boolopt_toggle ( struct net_bridge * br , enum br_boolopt_id opt , bool on ,
struct netlink_ext_ack * extack )
{
switch ( opt ) {
2018-11-24 05:34:21 +03:00
case BR_BOOLOPT_NO_LL_LEARN :
br_opt_toggle ( br , BROPT_NO_LL_LEARN , on ) ;
break ;
2018-11-24 05:34:20 +03:00
default :
/* shouldn't be called with unsupported options */
WARN_ON ( 1 ) ;
break ;
}
return 0 ;
}
int br_boolopt_get ( const struct net_bridge * br , enum br_boolopt_id opt )
{
switch ( opt ) {
2018-11-24 05:34:21 +03:00
case BR_BOOLOPT_NO_LL_LEARN :
return br_opt_get ( br , BROPT_NO_LL_LEARN ) ;
2018-11-24 05:34:20 +03:00
default :
/* shouldn't be called with unsupported options */
WARN_ON ( 1 ) ;
break ;
}
return 0 ;
}
int br_boolopt_multi_toggle ( struct net_bridge * br ,
struct br_boolopt_multi * bm ,
struct netlink_ext_ack * extack )
{
unsigned long bitmap = bm - > optmask ;
int err = 0 ;
int opt_id ;
for_each_set_bit ( opt_id , & bitmap , BR_BOOLOPT_MAX ) {
bool on = ! ! ( bm - > optval & BIT ( opt_id ) ) ;
err = br_boolopt_toggle ( br , opt_id , on , extack ) ;
if ( err ) {
br_debug ( br , " boolopt multi-toggle error: option: %d current: %d new: %d error: %d \n " ,
opt_id , br_boolopt_get ( br , opt_id ) , on , err ) ;
break ;
}
}
return err ;
}
void br_boolopt_multi_get ( const struct net_bridge * br ,
struct br_boolopt_multi * bm )
{
u32 optval = 0 ;
int opt_id ;
for ( opt_id = 0 ; opt_id < BR_BOOLOPT_MAX ; opt_id + + )
optval | = ( br_boolopt_get ( br , opt_id ) < < opt_id ) ;
bm - > optval = optval ;
2018-11-24 05:34:22 +03:00
bm - > optmask = GENMASK ( ( BR_BOOLOPT_MAX - 1 ) , 0 ) ;
2018-11-24 05:34:20 +03:00
}
/* private bridge options, controlled by the kernel */
2018-09-26 17:01:00 +03:00
void br_opt_toggle ( struct net_bridge * br , enum net_bridge_opts opt , bool on )
{
bool cur = ! ! br_opt_get ( br , opt ) ;
br_debug ( br , " toggle option: %d state: %d -> %d \n " ,
opt , cur , on ) ;
if ( cur = = on )
return ;
if ( on )
set_bit ( opt , & br - > options ) ;
else
clear_bit ( opt , & br - > options ) ;
}
2014-01-11 01:58:47 +04:00
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 ( ) ;
}
2006-03-21 09:59:06 +03:00
2008-09-09 03:20:18 +04:00
static struct pernet_operations br_net_ops = {
. exit = br_net_exit ,
} ;
2014-01-11 01:58:47 +04:00
static const struct stp_proto br_stp_proto = {
. rcv = br_stp_rcv ,
} ;
2005-04-17 02:20:36 +04:00
static int __init br_init ( void )
{
2006-05-26 02:59:33 +04:00
int err ;
2015-03-03 15:53:31 +03:00
BUILD_BUG_ON ( sizeof ( struct br_input_skb_cb ) > FIELD_SIZEOF ( struct sk_buff , cb ) ) ;
2008-07-06 08:25:56 +04:00
err = stp_proto_register ( & br_stp_proto ) ;
if ( err < 0 ) {
2010-05-10 13:31:09 +04:00
pr_err ( " bridge: can't register sap for STP \n " ) ;
2008-07-06 08:25:56 +04:00
return err ;
2006-03-21 09:59:06 +03:00
}
2007-04-07 13:57:07 +04:00
err = br_fdb_init ( ) ;
if ( err )
2007-11-29 15:41:43 +03:00
goto err_out ;
2005-04-17 02:20:36 +04:00
2008-09-09 03:20:18 +04:00
err = register_pernet_subsys ( & br_net_ops ) ;
2006-05-26 02:59:33 +04:00
if ( err )
goto err_out1 ;
2014-09-18 13:29:03 +04:00
err = br_nf_core_init ( ) ;
2006-05-26 02:59:33 +04:00
if ( err )
goto err_out2 ;
2008-09-09 03:20:18 +04:00
err = register_netdevice_notifier ( & br_device_notifier ) ;
2007-03-22 21:59:03 +03:00
if ( err )
goto err_out3 ;
2015-05-10 19:47:46 +03:00
err = register_switchdev_notifier ( & br_switchdev_notifier ) ;
2008-09-09 03:20:18 +04:00
if ( err )
goto err_out4 ;
2015-01-16 01:49:37 +03:00
err = br_netlink_init ( ) ;
if ( err )
goto err_out5 ;
2005-04-17 02:20:36 +04:00
brioctl_set ( br_ioctl_deviceless_stub ) ;
2011-12-12 06:58:25 +04:00
# if IS_ENABLED(CONFIG_ATM_LANE)
2009-06-05 09:35:28 +04:00
br_fdb_test_addr_hook = br_fdb_test_addr ;
# endif
2005-04-17 02:20:36 +04:00
2016-09-29 01:05:28 +03:00
# if IS_MODULE(CONFIG_BRIDGE_NETFILTER)
pr_info ( " bridge: filtering via arp/ip/ip6tables is no longer available "
" by default. Update your scripts to load br_netfilter if you "
2014-09-18 13:29:03 +04:00
" need this. \n " ) ;
2016-09-29 01:05:28 +03:00
# endif
2014-09-18 13:29:03 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
2014-09-18 13:29:03 +04:00
2015-01-16 01:49:37 +03:00
err_out5 :
2015-05-10 19:47:46 +03:00
unregister_switchdev_notifier ( & br_switchdev_notifier ) ;
2008-09-09 03:20:18 +04:00
err_out4 :
2007-03-22 21:59:03 +03:00
unregister_netdevice_notifier ( & br_device_notifier ) ;
2008-09-09 03:20:18 +04:00
err_out3 :
2014-09-18 13:29:03 +04:00
br_nf_core_fini ( ) ;
2008-09-09 03:20:18 +04:00
err_out2 :
unregister_pernet_subsys ( & br_net_ops ) ;
2006-05-26 02:59:33 +04:00
err_out1 :
2007-11-29 15:41:43 +03:00
br_fdb_fini ( ) ;
err_out :
2008-07-06 08:25:56 +04:00
stp_proto_unregister ( & br_stp_proto ) ;
2006-05-26 02:59:33 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static void __exit br_deinit ( void )
{
2008-07-06 08:25:56 +04:00
stp_proto_unregister ( & br_stp_proto ) ;
2006-05-26 03:00:12 +04:00
br_netlink_fini ( ) ;
2015-05-10 19:47:46 +03:00
unregister_switchdev_notifier ( & br_switchdev_notifier ) ;
2005-04-17 02:20:36 +04:00
unregister_netdevice_notifier ( & br_device_notifier ) ;
brioctl_set ( NULL ) ;
2008-09-09 03:20:18 +04:00
unregister_pernet_subsys ( & br_net_ops ) ;
2005-04-17 02:20:36 +04:00
2009-06-26 14:45:48 +04:00
rcu_barrier ( ) ; /* Wait for completion of call_rcu()'s */
2005-04-17 02:20:36 +04:00
2014-09-18 13:29:03 +04:00
br_nf_core_fini ( ) ;
2011-12-12 06:58:25 +04:00
# if IS_ENABLED(CONFIG_ATM_LANE)
2009-06-05 09:35:28 +04:00
br_fdb_test_addr_hook = NULL ;
# endif
2005-04-17 02:20:36 +04:00
br_fdb_fini ( ) ;
}
module_init ( br_init )
module_exit ( br_deinit )
MODULE_LICENSE ( " GPL " ) ;
2005-12-22 06:01:30 +03:00
MODULE_VERSION ( BR_VERSION ) ;
2011-04-04 18:03:32 +04:00
MODULE_ALIAS_RTNL_LINK ( " bridge " ) ;