net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Bridge Multiple Spanning Tree Support
*
* Authors :
* Tobias Waldekranz < tobias @ waldekranz . com >
*/
# include <linux/kernel.h>
2022-03-16 16:08:46 +01:00
# include <net/switchdev.h>
net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
# include "br_private.h"
DEFINE_STATIC_KEY_FALSE ( br_mst_used ) ;
2022-03-16 16:08:50 +01:00
bool br_mst_enabled ( const struct net_device * dev )
{
if ( ! netif_is_bridge_master ( dev ) )
return false ;
return br_opt_get ( netdev_priv ( dev ) , BROPT_MST_ENABLED ) ;
}
EXPORT_SYMBOL_GPL ( br_mst_enabled ) ;
2022-03-16 16:08:49 +01:00
int br_mst_get_info ( const struct net_device * dev , u16 msti , unsigned long * vids )
{
const struct net_bridge_vlan_group * vg ;
const struct net_bridge_vlan * v ;
const struct net_bridge * br ;
ASSERT_RTNL ( ) ;
if ( ! netif_is_bridge_master ( dev ) )
return - EINVAL ;
br = netdev_priv ( dev ) ;
if ( ! br_opt_get ( br , BROPT_MST_ENABLED ) )
return - EINVAL ;
vg = br_vlan_group ( br ) ;
list_for_each_entry ( v , & vg - > vlan_list , vlist ) {
if ( v - > msti = = msti )
__set_bit ( v - > vid , vids ) ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( br_mst_get_info ) ;
2022-03-16 16:08:51 +01:00
int br_mst_get_state ( const struct net_device * dev , u16 msti , u8 * state )
{
const struct net_bridge_port * p = NULL ;
const struct net_bridge_vlan_group * vg ;
const struct net_bridge_vlan * v ;
ASSERT_RTNL ( ) ;
p = br_port_get_check_rtnl ( dev ) ;
if ( ! p | | ! br_opt_get ( p - > br , BROPT_MST_ENABLED ) )
return - EINVAL ;
vg = nbp_vlan_group ( p ) ;
list_for_each_entry ( v , & vg - > vlan_list , vlist ) {
if ( v - > brvlan - > msti = = msti ) {
* state = v - > state ;
return 0 ;
}
}
return - ENOENT ;
}
EXPORT_SYMBOL_GPL ( br_mst_get_state ) ;
net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
static void br_mst_vlan_set_state ( struct net_bridge_port * p , struct net_bridge_vlan * v ,
u8 state )
{
struct net_bridge_vlan_group * vg = nbp_vlan_group ( p ) ;
if ( v - > state = = state )
return ;
br_vlan_set_state ( v , state ) ;
if ( v - > vid = = vg - > pvid )
br_vlan_set_pvid_state ( vg , state ) ;
}
int br_mst_set_state ( struct net_bridge_port * p , u16 msti , u8 state ,
struct netlink_ext_ack * extack )
{
2022-03-16 16:08:48 +01:00
struct switchdev_attr attr = {
. id = SWITCHDEV_ATTR_ID_PORT_MST_STATE ,
. orig_dev = p - > dev ,
. u . mst_state = {
. msti = msti ,
. state = state ,
} ,
} ;
net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
struct net_bridge_vlan_group * vg ;
struct net_bridge_vlan * v ;
2022-03-16 16:08:48 +01:00
int err ;
net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
vg = nbp_vlan_group ( p ) ;
if ( ! vg )
return 0 ;
2022-03-16 16:08:48 +01:00
/* MSTI 0 (CST) state changes are notified via the regular
* SWITCHDEV_ATTR_ID_PORT_STP_STATE .
*/
if ( msti ) {
err = switchdev_port_attr_set ( p - > dev , & attr , extack ) ;
if ( err & & err ! = - EOPNOTSUPP )
return err ;
}
net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
list_for_each_entry ( v , & vg - > vlan_list , vlist ) {
if ( v - > brvlan - > msti ! = msti )
continue ;
br_mst_vlan_set_state ( p , v , state ) ;
}
return 0 ;
}
2022-03-16 16:08:44 +01:00
static void br_mst_vlan_sync_state ( struct net_bridge_vlan * pv , u16 msti )
{
struct net_bridge_vlan_group * vg = nbp_vlan_group ( pv - > port ) ;
struct net_bridge_vlan * v ;
list_for_each_entry ( v , & vg - > vlan_list , vlist ) {
/* If this port already has a defined state in this
* MSTI ( through some other VLAN membership ) , inherit
* it .
*/
if ( v ! = pv & & v - > brvlan - > msti = = msti ) {
br_mst_vlan_set_state ( pv - > port , pv , v - > state ) ;
return ;
}
}
/* Otherwise, start out in a new MSTI with all ports disabled. */
return br_mst_vlan_set_state ( pv - > port , pv , BR_STATE_DISABLED ) ;
}
int br_mst_vlan_set_msti ( struct net_bridge_vlan * mv , u16 msti )
{
2022-03-16 16:08:47 +01:00
struct switchdev_attr attr = {
. id = SWITCHDEV_ATTR_ID_VLAN_MSTI ,
. orig_dev = mv - > br - > dev ,
. u . vlan_msti = {
. vid = mv - > vid ,
. msti = msti ,
} ,
} ;
2022-03-16 16:08:44 +01:00
struct net_bridge_vlan_group * vg ;
struct net_bridge_vlan * pv ;
struct net_bridge_port * p ;
2022-03-16 16:08:47 +01:00
int err ;
2022-03-16 16:08:44 +01:00
if ( mv - > msti = = msti )
return 0 ;
2022-03-16 16:08:47 +01:00
err = switchdev_port_attr_set ( mv - > br - > dev , & attr , NULL ) ;
if ( err & & err ! = - EOPNOTSUPP )
return err ;
2022-03-16 16:08:44 +01:00
mv - > msti = msti ;
list_for_each_entry ( p , & mv - > br - > port_list , list ) {
vg = nbp_vlan_group ( p ) ;
pv = br_vlan_find ( vg , mv - > vid ) ;
if ( pv )
br_mst_vlan_sync_state ( pv , msti ) ;
}
return 0 ;
}
net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
void br_mst_vlan_init_state ( struct net_bridge_vlan * v )
{
/* VLANs always start out in MSTI 0 (CST) */
v - > msti = 0 ;
if ( br_vlan_is_master ( v ) )
v - > state = BR_STATE_FORWARDING ;
else
v - > state = v - > port - > state ;
}
int br_mst_set_enabled ( struct net_bridge * br , bool on ,
struct netlink_ext_ack * extack )
{
2022-03-16 16:08:46 +01:00
struct switchdev_attr attr = {
. id = SWITCHDEV_ATTR_ID_BRIDGE_MST ,
. orig_dev = br - > dev ,
. u . mst = on ,
} ;
net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
struct net_bridge_vlan_group * vg ;
struct net_bridge_port * p ;
2022-03-16 16:08:46 +01:00
int err ;
net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
list_for_each_entry ( p , & br - > port_list , list ) {
vg = nbp_vlan_group ( p ) ;
if ( ! vg - > num_vlans )
continue ;
NL_SET_ERR_MSG ( extack ,
" MST mode can't be changed while VLANs exist " ) ;
return - EBUSY ;
}
if ( br_opt_get ( br , BROPT_MST_ENABLED ) = = on )
return 0 ;
2022-03-16 16:08:46 +01:00
err = switchdev_port_attr_set ( br - > dev , & attr , extack ) ;
if ( err & & err ! = - EOPNOTSUPP )
return err ;
net: bridge: mst: Multiple Spanning Tree (MST) mode
Allow the user to switch from the current per-VLAN STP mode to an MST
mode.
Up to this point, per-VLAN STP states where always isolated from each
other. This is in contrast to the MSTP standard (802.1Q-2018, Clause
13.5), where VLANs are grouped into MST instances (MSTIs), and the
state is managed on a per-MSTI level, rather that at the per-VLAN
level.
Perhaps due to the prevalence of the standard, many switching ASICs
are built after the same model. Therefore, add a corresponding MST
mode to the bridge, which we can later add offloading support for in a
straight-forward way.
For now, all VLANs are fixed to MSTI 0, also called the Common
Spanning Tree (CST). That is, all VLANs will follow the port-global
state.
Upcoming changes will make this actually useful by allowing VLANs to
be mapped to arbitrary MSTIs and allow individual MSTI states to be
changed.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2022-03-16 16:08:43 +01:00
if ( on )
static_branch_enable ( & br_mst_used ) ;
else
static_branch_disable ( & br_mst_used ) ;
br_opt_toggle ( br , BROPT_MST_ENABLED , on ) ;
return 0 ;
}
2022-03-16 16:08:45 +01:00
size_t br_mst_info_size ( const struct net_bridge_vlan_group * vg )
{
DECLARE_BITMAP ( seen , VLAN_N_VID ) = { 0 } ;
const struct net_bridge_vlan * v ;
size_t sz ;
/* IFLA_BRIDGE_MST */
sz = nla_total_size ( 0 ) ;
list_for_each_entry_rcu ( v , & vg - > vlan_list , vlist ) {
if ( test_bit ( v - > brvlan - > msti , seen ) )
continue ;
/* IFLA_BRIDGE_MST_ENTRY */
sz + = nla_total_size ( 0 ) +
/* IFLA_BRIDGE_MST_ENTRY_MSTI */
nla_total_size ( sizeof ( u16 ) ) +
/* IFLA_BRIDGE_MST_ENTRY_STATE */
nla_total_size ( sizeof ( u8 ) ) ;
__set_bit ( v - > brvlan - > msti , seen ) ;
}
return sz ;
}
int br_mst_fill_info ( struct sk_buff * skb ,
const struct net_bridge_vlan_group * vg )
{
DECLARE_BITMAP ( seen , VLAN_N_VID ) = { 0 } ;
const struct net_bridge_vlan * v ;
struct nlattr * nest ;
int err = 0 ;
list_for_each_entry ( v , & vg - > vlan_list , vlist ) {
if ( test_bit ( v - > brvlan - > msti , seen ) )
continue ;
nest = nla_nest_start_noflag ( skb , IFLA_BRIDGE_MST_ENTRY ) ;
if ( ! nest | |
nla_put_u16 ( skb , IFLA_BRIDGE_MST_ENTRY_MSTI , v - > brvlan - > msti ) | |
nla_put_u8 ( skb , IFLA_BRIDGE_MST_ENTRY_STATE , v - > state ) ) {
err = - EMSGSIZE ;
break ;
}
nla_nest_end ( skb , nest ) ;
__set_bit ( v - > brvlan - > msti , seen ) ;
}
return err ;
}
static const struct nla_policy br_mst_nl_policy [ IFLA_BRIDGE_MST_ENTRY_MAX + 1 ] = {
[ IFLA_BRIDGE_MST_ENTRY_MSTI ] = NLA_POLICY_RANGE ( NLA_U16 ,
1 , /* 0 reserved for CST */
VLAN_N_VID - 1 ) ,
[ IFLA_BRIDGE_MST_ENTRY_STATE ] = NLA_POLICY_RANGE ( NLA_U8 ,
BR_STATE_DISABLED ,
BR_STATE_BLOCKING ) ,
} ;
static int br_mst_process_one ( struct net_bridge_port * p ,
const struct nlattr * attr ,
struct netlink_ext_ack * extack )
{
struct nlattr * tb [ IFLA_BRIDGE_MST_ENTRY_MAX + 1 ] ;
u16 msti ;
u8 state ;
int err ;
err = nla_parse_nested ( tb , IFLA_BRIDGE_MST_ENTRY_MAX , attr ,
br_mst_nl_policy , extack ) ;
if ( err )
return err ;
if ( ! tb [ IFLA_BRIDGE_MST_ENTRY_MSTI ] ) {
NL_SET_ERR_MSG_MOD ( extack , " MSTI not specified " ) ;
return - EINVAL ;
}
if ( ! tb [ IFLA_BRIDGE_MST_ENTRY_STATE ] ) {
NL_SET_ERR_MSG_MOD ( extack , " State not specified " ) ;
return - EINVAL ;
}
msti = nla_get_u16 ( tb [ IFLA_BRIDGE_MST_ENTRY_MSTI ] ) ;
state = nla_get_u8 ( tb [ IFLA_BRIDGE_MST_ENTRY_STATE ] ) ;
return br_mst_set_state ( p , msti , state , extack ) ;
}
int br_mst_process ( struct net_bridge_port * p , const struct nlattr * mst_attr ,
struct netlink_ext_ack * extack )
{
struct nlattr * attr ;
int err , msts = 0 ;
int rem ;
if ( ! br_opt_get ( p - > br , BROPT_MST_ENABLED ) ) {
NL_SET_ERR_MSG_MOD ( extack , " Can't modify MST state when MST is disabled " ) ;
return - EBUSY ;
}
nla_for_each_nested ( attr , mst_attr , rem ) {
switch ( nla_type ( attr ) ) {
case IFLA_BRIDGE_MST_ENTRY :
err = br_mst_process_one ( p , attr , extack ) ;
break ;
default :
continue ;
}
msts + + ;
if ( err )
break ;
}
if ( ! msts ) {
NL_SET_ERR_MSG_MOD ( extack , " Found no MST entries to process " ) ;
err = - EINVAL ;
}
return err ;
}