2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* Spanning tree protocol ; interface code
* Linux ethernet bridge
*
* Authors :
* Lennert Buytenhek < buytenh @ gnu . org >
*/
# include <linux/kernel.h>
2011-08-30 01:55:05 +04:00
# include <linux/kmod.h>
2005-10-26 02:04:59 +04:00
# include <linux/etherdevice.h>
2006-05-26 03:00:12 +04:00
# include <linux/rtnetlink.h>
2015-10-19 15:37:25 +03:00
# include <net/switchdev.h>
2005-04-17 02:20:36 +04:00
# include "br_private.h"
# include "br_private_stp.h"
/* Port id is composed of priority and port number.
2011-04-04 18:03:33 +04:00
* NB : some bits of priority are dropped to
2005-04-17 02:20:36 +04:00
* make room for more ports .
*/
static inline port_id br_make_port_id ( __u8 priority , __u16 port_no )
{
2007-02-09 17:24:35 +03:00
return ( ( u16 ) priority < < BR_PORT_BITS )
2005-04-17 02:20:36 +04:00
| ( port_no & ( ( 1 < < BR_PORT_BITS ) - 1 ) ) ;
}
2011-04-04 18:03:33 +04:00
# define BR_MAX_PORT_PRIORITY ((u16)~0 >> BR_PORT_BITS)
2005-04-17 02:20:36 +04:00
/* called under bridge lock */
void br_init_port ( struct net_bridge_port * p )
{
2015-10-19 15:37:25 +03:00
int err ;
2005-04-17 02:20:36 +04:00
p - > port_id = br_make_port_id ( p - > priority , p - > port_no ) ;
br_become_designated_port ( p ) ;
2014-10-01 03:13:19 +04:00
br_set_state ( p , BR_STATE_BLOCKING ) ;
2005-04-17 02:20:36 +04:00
p - > topology_change_ack = 0 ;
p - > config_pending = 0 ;
2015-10-19 15:37:25 +03:00
2016-12-10 21:44:27 +03:00
err = __set_ageing_time ( p - > dev , p - > br - > ageing_time ) ;
if ( err )
netdev_err ( p - > dev , " failed to offload ageing time \n " ) ;
2005-04-17 02:20:36 +04:00
}
2016-07-21 19:42:10 +03:00
/* NO locks held */
2005-04-17 02:20:36 +04:00
void br_stp_enable_bridge ( struct net_bridge * br )
{
struct net_bridge_port * p ;
spin_lock_bh ( & br - > lock ) ;
2015-07-23 21:01:05 +03:00
if ( br - > stp_enabled = = BR_KERNEL_STP )
mod_timer ( & br - > hello_timer , jiffies + br - > hello_time ) ;
2017-02-04 20:05:07 +03:00
mod_delayed_work ( system_long_wq , & br - > gc_work , HZ / 10 ) ;
2007-02-09 17:24:35 +03:00
2005-04-17 02:20:36 +04:00
br_config_bpdu_generation ( br ) ;
list_for_each_entry ( p , & br - > port_list , list ) {
2012-12-28 22:15:22 +04:00
if ( netif_running ( p - > dev ) & & netif_oper_up ( p - > dev ) )
2005-04-17 02:20:36 +04:00
br_stp_enable_port ( p ) ;
}
spin_unlock_bh ( & br - > lock ) ;
}
/* NO locks held */
void br_stp_disable_bridge ( struct net_bridge * br )
{
struct net_bridge_port * p ;
2006-02-15 12:47:48 +03:00
spin_lock_bh ( & br - > lock ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( p , & br - > port_list , list ) {
if ( p - > state ! = BR_STATE_DISABLED )
br_stp_disable_port ( p ) ;
}
2016-12-10 21:44:28 +03:00
__br_set_topology_change ( br , 0 ) ;
2005-04-17 02:20:36 +04:00
br - > topology_change_detected = 0 ;
2006-02-15 12:47:48 +03:00
spin_unlock_bh ( & br - > lock ) ;
2005-04-17 02:20:36 +04:00
del_timer_sync ( & br - > hello_timer ) ;
del_timer_sync ( & br - > topology_change_timer ) ;
del_timer_sync ( & br - > tcn_timer ) ;
2017-02-04 20:05:07 +03:00
cancel_delayed_work_sync ( & br - > gc_work ) ;
2005-04-17 02:20:36 +04:00
}
/* called under bridge lock */
void br_stp_enable_port ( struct net_bridge_port * p )
{
br_init_port ( p ) ;
br_port_state_selection ( p - > br ) ;
2017-11-01 13:18:13 +03:00
br_ifinfo_notify ( RTM_NEWLINK , NULL , p ) ;
2005-04-17 02:20:36 +04:00
}
/* called under bridge lock */
void br_stp_disable_port ( struct net_bridge_port * p )
{
2010-05-10 13:31:09 +04:00
struct net_bridge * br = p - > br ;
2005-04-17 02:20:36 +04:00
int wasroot ;
wasroot = br_is_root_bridge ( br ) ;
br_become_designated_port ( p ) ;
2014-10-01 03:13:19 +04:00
br_set_state ( p , BR_STATE_DISABLED ) ;
2005-04-17 02:20:36 +04:00
p - > topology_change_ack = 0 ;
p - > config_pending = 0 ;
2017-11-01 13:18:13 +03:00
br_ifinfo_notify ( RTM_NEWLINK , NULL , p ) ;
2011-07-22 11:47:09 +04:00
2005-04-17 02:20:36 +04:00
del_timer ( & p - > message_age_timer ) ;
del_timer ( & p - > forward_delay_timer ) ;
del_timer ( & p - > hold_timer ) ;
2019-04-03 13:49:24 +03:00
if ( ! rcu_access_pointer ( p - > backup_port ) )
br_fdb_delete_by_port ( br , p , 0 , 0 ) ;
2010-02-28 11:49:38 +03:00
br_multicast_disable_port ( p ) ;
2006-10-13 01:45:38 +04:00
2005-04-17 02:20:36 +04:00
br_configuration_update ( br ) ;
br_port_state_selection ( br ) ;
if ( br_is_root_bridge ( br ) & & ! wasroot )
br_become_root_bridge ( br ) ;
}
2016-09-08 19:50:43 +03:00
static int br_stp_call_user ( struct net_bridge * br , char * arg )
2007-03-22 00:22:44 +03:00
{
2016-09-08 19:50:43 +03:00
char * argv [ ] = { BR_STP_PROG , br - > dev - > name , arg , NULL } ;
2007-03-22 00:22:44 +03:00
char * envp [ ] = { NULL } ;
2016-09-08 19:50:43 +03:00
int rc ;
/* call userspace STP and report program errors */
rc = call_usermodehelper ( BR_STP_PROG , argv , envp , UMH_WAIT_PROC ) ;
if ( rc > 0 ) {
if ( rc & 0xff )
br_debug ( br , BR_STP_PROG " received signal %d \n " ,
rc & 0x7f ) ;
else
br_debug ( br , BR_STP_PROG " exited with code %d \n " ,
( rc > > 8 ) & 0xff ) ;
}
return rc ;
}
static void br_stp_start ( struct net_bridge * br )
{
int err = - ENOENT ;
2007-03-22 00:22:44 +03:00
2016-01-05 12:46:00 +03:00
if ( net_eq ( dev_net ( br - > dev ) , & init_net ) )
2016-09-08 19:50:43 +03:00
err = br_stp_call_user ( br , " start " ) ;
if ( err & & err ! = - ENOENT )
br_err ( br , " failed to start userspace STP (%d) \n " , err ) ;
2013-09-12 11:12:05 +04:00
spin_lock_bh ( & br - > lock ) ;
if ( br - > bridge_forward_delay < BR_MIN_FORWARD_DELAY )
__br_set_forward_delay ( br , BR_MIN_FORWARD_DELAY ) ;
2013-10-15 22:57:45 +04:00
else if ( br - > bridge_forward_delay > BR_MAX_FORWARD_DELAY )
2013-09-12 11:12:05 +04:00
__br_set_forward_delay ( br , BR_MAX_FORWARD_DELAY ) ;
2016-09-08 19:50:43 +03:00
if ( ! err ) {
2007-03-22 00:22:44 +03:00
br - > stp_enabled = BR_USER_STP ;
2010-05-10 13:31:09 +04:00
br_debug ( br , " userspace STP started \n " ) ;
2007-03-22 00:22:44 +03:00
} else {
br - > stp_enabled = BR_KERNEL_STP ;
2010-05-10 13:31:09 +04:00
br_debug ( br , " using kernel STP \n " ) ;
2007-03-22 00:22:44 +03:00
/* To start timers on any ports left in blocking */
2017-06-01 18:07:55 +03:00
if ( br - > dev - > flags & IFF_UP )
mod_timer ( & br - > hello_timer , jiffies + br - > hello_time ) ;
2007-03-22 00:22:44 +03:00
br_port_state_selection ( br ) ;
}
2013-09-12 11:12:05 +04:00
spin_unlock_bh ( & br - > lock ) ;
2007-03-22 00:22:44 +03:00
}
static void br_stp_stop ( struct net_bridge * br )
{
2016-09-08 19:50:43 +03:00
int err ;
2007-03-22 00:22:44 +03:00
if ( br - > stp_enabled = = BR_USER_STP ) {
2016-09-08 19:50:43 +03:00
err = br_stp_call_user ( br , " stop " ) ;
if ( err )
br_err ( br , " failed to stop userspace STP (%d) \n " , err ) ;
2007-03-22 00:22:44 +03:00
/* To start timers on any ports left in blocking */
spin_lock_bh ( & br - > lock ) ;
br_port_state_selection ( br ) ;
spin_unlock_bh ( & br - > lock ) ;
}
br - > stp_enabled = BR_NO_STP ;
}
2020-04-26 16:22:08 +03:00
int br_stp_set_enabled ( struct net_bridge * br , unsigned long val ,
struct netlink_ext_ack * extack )
2007-03-22 00:22:44 +03:00
{
ASSERT_RTNL ( ) ;
2020-04-26 16:22:08 +03:00
if ( br_mrp_enabled ( br ) ) {
NL_SET_ERR_MSG_MOD ( extack ,
2020-05-07 03:58:27 +03:00
" STP can't be enabled if MRP is already enabled " ) ;
2020-04-26 16:22:08 +03:00
return - EINVAL ;
}
2007-03-22 00:22:44 +03:00
if ( val ) {
if ( br - > stp_enabled = = BR_NO_STP )
br_stp_start ( br ) ;
} else {
if ( br - > stp_enabled ! = BR_NO_STP )
br_stp_stop ( br ) ;
}
2020-04-26 16:22:08 +03:00
return 0 ;
2007-03-22 00:22:44 +03:00
}
2005-04-17 02:20:36 +04:00
/* called under bridge lock */
2005-12-22 05:51:49 +03:00
void br_stp_change_bridge_id ( struct net_bridge * br , const unsigned char * addr )
2005-04-17 02:20:36 +04:00
{
bridge: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 22:56:49 +04:00
/* should be aligned on 2 bytes for ether_addr_equal() */
2007-04-17 23:31:24 +04:00
unsigned short oldaddr_aligned [ ETH_ALEN > > 1 ] ;
unsigned char * oldaddr = ( unsigned char * ) oldaddr_aligned ;
2005-04-17 02:20:36 +04:00
struct net_bridge_port * p ;
int wasroot ;
wasroot = br_is_root_bridge ( br ) ;
2014-02-07 11:48:21 +04:00
br_fdb_change_mac_address ( br , addr ) ;
2005-04-17 02:20:36 +04:00
memcpy ( oldaddr , br - > bridge_id . addr , ETH_ALEN ) ;
memcpy ( br - > bridge_id . addr , addr , ETH_ALEN ) ;
2021-10-02 00:32:19 +03:00
eth_hw_addr_set ( br - > dev , addr ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( p , & br - > port_list , list ) {
bridge: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 22:56:49 +04:00
if ( ether_addr_equal ( p - > designated_bridge . addr , oldaddr ) )
2005-04-17 02:20:36 +04:00
memcpy ( p - > designated_bridge . addr , addr , ETH_ALEN ) ;
bridge: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 22:56:49 +04:00
if ( ether_addr_equal ( p - > designated_root . addr , oldaddr ) )
2005-04-17 02:20:36 +04:00
memcpy ( p - > designated_root . addr , addr , ETH_ALEN ) ;
}
br_configuration_update ( br ) ;
br_port_state_selection ( br ) ;
if ( br_is_root_bridge ( br ) & & ! wasroot )
br_become_root_bridge ( br ) ;
}
bridge: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 22:56:49 +04:00
/* should be aligned on 2 bytes for ether_addr_equal() */
2007-04-17 23:31:24 +04:00
static const unsigned short br_mac_zero_aligned [ ETH_ALEN > > 1 ] ;
2005-04-17 02:20:36 +04:00
/* called under bridge lock */
2011-03-24 16:24:01 +03:00
bool br_stp_recalculate_bridge_id ( struct net_bridge * br )
2005-04-17 02:20:36 +04:00
{
2007-04-17 23:31:24 +04:00
const unsigned char * br_mac_zero =
( const unsigned char * ) br_mac_zero_aligned ;
2005-04-17 02:20:36 +04:00
const unsigned char * addr = br_mac_zero ;
struct net_bridge_port * p ;
2008-06-18 03:10:06 +04:00
/* user has chosen a value so keep it */
2013-02-10 15:41:01 +04:00
if ( br - > dev - > addr_assign_type = = NET_ADDR_SET )
2011-03-29 10:20:04 +04:00
return false ;
2008-06-18 03:10:06 +04:00
2005-04-17 02:20:36 +04:00
list_for_each_entry ( p , & br - > port_list , list ) {
if ( addr = = br_mac_zero | |
2006-01-04 01:35:54 +03:00
memcmp ( p - > dev - > dev_addr , addr , ETH_ALEN ) < 0 )
2005-04-17 02:20:36 +04:00
addr = p - > dev - > dev_addr ;
}
bridge: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 22:56:49 +04:00
if ( ether_addr_equal ( br - > bridge_id . addr , addr ) )
2011-03-24 16:24:01 +03:00
return false ; /* no change */
br_stp_change_bridge_id ( br , addr ) ;
return true ;
2005-04-17 02:20:36 +04:00
}
2015-06-15 20:28:51 +03:00
/* Acquires and releases bridge lock */
2005-04-17 02:20:36 +04:00
void br_stp_set_bridge_priority ( struct net_bridge * br , u16 newprio )
{
struct net_bridge_port * p ;
int wasroot ;
2015-06-15 20:28:51 +03:00
spin_lock_bh ( & br - > lock ) ;
2005-04-17 02:20:36 +04:00
wasroot = br_is_root_bridge ( br ) ;
list_for_each_entry ( p , & br - > port_list , list ) {
if ( p - > state ! = BR_STATE_DISABLED & &
br_is_designated_port ( p ) ) {
p - > designated_bridge . prio [ 0 ] = ( newprio > > 8 ) & 0xFF ;
p - > designated_bridge . prio [ 1 ] = newprio & 0xFF ;
}
}
br - > bridge_id . prio [ 0 ] = ( newprio > > 8 ) & 0xFF ;
br - > bridge_id . prio [ 1 ] = newprio & 0xFF ;
br_configuration_update ( br ) ;
br_port_state_selection ( br ) ;
if ( br_is_root_bridge ( br ) & & ! wasroot )
br_become_root_bridge ( br ) ;
2015-06-15 20:28:51 +03:00
spin_unlock_bh ( & br - > lock ) ;
2005-04-17 02:20:36 +04:00
}
/* called under bridge lock */
2011-04-04 18:03:33 +04:00
int br_stp_set_port_priority ( struct net_bridge_port * p , unsigned long newprio )
2005-04-17 02:20:36 +04:00
{
2011-04-04 18:03:33 +04:00
port_id new_port_id ;
if ( newprio > BR_MAX_PORT_PRIORITY )
return - ERANGE ;
2005-04-17 02:20:36 +04:00
2011-04-04 18:03:33 +04:00
new_port_id = br_make_port_id ( newprio , p - > port_no ) ;
2005-04-17 02:20:36 +04:00
if ( br_is_designated_port ( p ) )
p - > designated_port = new_port_id ;
p - > port_id = new_port_id ;
p - > priority = newprio ;
if ( ! memcmp ( & p - > br - > bridge_id , & p - > designated_bridge , 8 ) & &
p - > port_id < p - > designated_port ) {
br_become_designated_port ( p ) ;
br_port_state_selection ( p - > br ) ;
}
2011-04-04 18:03:33 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/* called under bridge lock */
2011-04-04 18:03:33 +04:00
int br_stp_set_path_cost ( struct net_bridge_port * p , unsigned long path_cost )
2005-04-17 02:20:36 +04:00
{
2011-04-04 18:03:33 +04:00
if ( path_cost < BR_MIN_PATH_COST | |
path_cost > BR_MAX_PATH_COST )
return - ERANGE ;
2013-04-13 18:06:07 +04:00
p - > flags | = BR_ADMIN_COST ;
2005-04-17 02:20:36 +04:00
p - > path_cost = path_cost ;
br_configuration_update ( p - > br ) ;
br_port_state_selection ( p - > br ) ;
2011-04-04 18:03:33 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
ssize_t br_show_bridge_id ( char * buf , const struct bridge_id * id )
{
return sprintf ( buf , " %.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x \n " ,
id - > prio [ 0 ] , id - > prio [ 1 ] ,
id - > addr [ 0 ] , id - > addr [ 1 ] , id - > addr [ 2 ] ,
id - > addr [ 3 ] , id - > addr [ 4 ] , id - > addr [ 5 ] ) ;
}