162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Bridge Multiple Spanning Tree Support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Tobias Waldekranz <tobias@waldekranz.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <net/switchdev.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "br_private.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(br_mst_used); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cibool br_mst_enabled(const struct net_device *dev) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci if (!netif_is_bridge_master(dev)) 1962306a36Sopenharmony_ci return false; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci return br_opt_get(netdev_priv(dev), BROPT_MST_ENABLED); 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_mst_enabled); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciint br_mst_get_info(const struct net_device *dev, u16 msti, unsigned long *vids) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci const struct net_bridge_vlan_group *vg; 2862306a36Sopenharmony_ci const struct net_bridge_vlan *v; 2962306a36Sopenharmony_ci const struct net_bridge *br; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci ASSERT_RTNL(); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (!netif_is_bridge_master(dev)) 3462306a36Sopenharmony_ci return -EINVAL; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci br = netdev_priv(dev); 3762306a36Sopenharmony_ci if (!br_opt_get(br, BROPT_MST_ENABLED)) 3862306a36Sopenharmony_ci return -EINVAL; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci vg = br_vlan_group(br); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci list_for_each_entry(v, &vg->vlan_list, vlist) { 4362306a36Sopenharmony_ci if (v->msti == msti) 4462306a36Sopenharmony_ci __set_bit(v->vid, vids); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_mst_get_info); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciint br_mst_get_state(const struct net_device *dev, u16 msti, u8 *state) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci const struct net_bridge_port *p = NULL; 5462306a36Sopenharmony_ci const struct net_bridge_vlan_group *vg; 5562306a36Sopenharmony_ci const struct net_bridge_vlan *v; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci ASSERT_RTNL(); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci p = br_port_get_check_rtnl(dev); 6062306a36Sopenharmony_ci if (!p || !br_opt_get(p->br, BROPT_MST_ENABLED)) 6162306a36Sopenharmony_ci return -EINVAL; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci vg = nbp_vlan_group(p); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci list_for_each_entry(v, &vg->vlan_list, vlist) { 6662306a36Sopenharmony_ci if (v->brvlan->msti == msti) { 6762306a36Sopenharmony_ci *state = v->state; 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return -ENOENT; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_mst_get_state); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void br_mst_vlan_set_state(struct net_bridge_port *p, struct net_bridge_vlan *v, 7762306a36Sopenharmony_ci u8 state) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct net_bridge_vlan_group *vg = nbp_vlan_group(p); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (v->state == state) 8262306a36Sopenharmony_ci return; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci br_vlan_set_state(v, state); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (v->vid == vg->pvid) 8762306a36Sopenharmony_ci br_vlan_set_pvid_state(vg, state); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ciint br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state, 9162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct switchdev_attr attr = { 9462306a36Sopenharmony_ci .id = SWITCHDEV_ATTR_ID_PORT_MST_STATE, 9562306a36Sopenharmony_ci .orig_dev = p->dev, 9662306a36Sopenharmony_ci .u.mst_state = { 9762306a36Sopenharmony_ci .msti = msti, 9862306a36Sopenharmony_ci .state = state, 9962306a36Sopenharmony_ci }, 10062306a36Sopenharmony_ci }; 10162306a36Sopenharmony_ci struct net_bridge_vlan_group *vg; 10262306a36Sopenharmony_ci struct net_bridge_vlan *v; 10362306a36Sopenharmony_ci int err; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci vg = nbp_vlan_group(p); 10662306a36Sopenharmony_ci if (!vg) 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* MSTI 0 (CST) state changes are notified via the regular 11062306a36Sopenharmony_ci * SWITCHDEV_ATTR_ID_PORT_STP_STATE. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci if (msti) { 11362306a36Sopenharmony_ci err = switchdev_port_attr_set(p->dev, &attr, extack); 11462306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 11562306a36Sopenharmony_ci return err; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci list_for_each_entry(v, &vg->vlan_list, vlist) { 11962306a36Sopenharmony_ci if (v->brvlan->msti != msti) 12062306a36Sopenharmony_ci continue; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci br_mst_vlan_set_state(p, v, state); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void br_mst_vlan_sync_state(struct net_bridge_vlan *pv, u16 msti) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct net_bridge_vlan_group *vg = nbp_vlan_group(pv->port); 13162306a36Sopenharmony_ci struct net_bridge_vlan *v; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci list_for_each_entry(v, &vg->vlan_list, vlist) { 13462306a36Sopenharmony_ci /* If this port already has a defined state in this 13562306a36Sopenharmony_ci * MSTI (through some other VLAN membership), inherit 13662306a36Sopenharmony_ci * it. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci if (v != pv && v->brvlan->msti == msti) { 13962306a36Sopenharmony_ci br_mst_vlan_set_state(pv->port, pv, v->state); 14062306a36Sopenharmony_ci return; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Otherwise, start out in a new MSTI with all ports disabled. */ 14562306a36Sopenharmony_ci return br_mst_vlan_set_state(pv->port, pv, BR_STATE_DISABLED); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint br_mst_vlan_set_msti(struct net_bridge_vlan *mv, u16 msti) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct switchdev_attr attr = { 15162306a36Sopenharmony_ci .id = SWITCHDEV_ATTR_ID_VLAN_MSTI, 15262306a36Sopenharmony_ci .orig_dev = mv->br->dev, 15362306a36Sopenharmony_ci .u.vlan_msti = { 15462306a36Sopenharmony_ci .vid = mv->vid, 15562306a36Sopenharmony_ci .msti = msti, 15662306a36Sopenharmony_ci }, 15762306a36Sopenharmony_ci }; 15862306a36Sopenharmony_ci struct net_bridge_vlan_group *vg; 15962306a36Sopenharmony_ci struct net_bridge_vlan *pv; 16062306a36Sopenharmony_ci struct net_bridge_port *p; 16162306a36Sopenharmony_ci int err; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (mv->msti == msti) 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci err = switchdev_port_attr_set(mv->br->dev, &attr, NULL); 16762306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 16862306a36Sopenharmony_ci return err; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci mv->msti = msti; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci list_for_each_entry(p, &mv->br->port_list, list) { 17362306a36Sopenharmony_ci vg = nbp_vlan_group(p); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci pv = br_vlan_find(vg, mv->vid); 17662306a36Sopenharmony_ci if (pv) 17762306a36Sopenharmony_ci br_mst_vlan_sync_state(pv, msti); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_civoid br_mst_vlan_init_state(struct net_bridge_vlan *v) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci /* VLANs always start out in MSTI 0 (CST) */ 18662306a36Sopenharmony_ci v->msti = 0; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (br_vlan_is_master(v)) 18962306a36Sopenharmony_ci v->state = BR_STATE_FORWARDING; 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci v->state = v->port->state; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciint br_mst_set_enabled(struct net_bridge *br, bool on, 19562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct switchdev_attr attr = { 19862306a36Sopenharmony_ci .id = SWITCHDEV_ATTR_ID_BRIDGE_MST, 19962306a36Sopenharmony_ci .orig_dev = br->dev, 20062306a36Sopenharmony_ci .u.mst = on, 20162306a36Sopenharmony_ci }; 20262306a36Sopenharmony_ci struct net_bridge_vlan_group *vg; 20362306a36Sopenharmony_ci struct net_bridge_port *p; 20462306a36Sopenharmony_ci int err; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci list_for_each_entry(p, &br->port_list, list) { 20762306a36Sopenharmony_ci vg = nbp_vlan_group(p); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (!vg->num_vlans) 21062306a36Sopenharmony_ci continue; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 21362306a36Sopenharmony_ci "MST mode can't be changed while VLANs exist"); 21462306a36Sopenharmony_ci return -EBUSY; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (br_opt_get(br, BROPT_MST_ENABLED) == on) 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci err = switchdev_port_attr_set(br->dev, &attr, extack); 22162306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 22262306a36Sopenharmony_ci return err; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (on) 22562306a36Sopenharmony_ci static_branch_enable(&br_mst_used); 22662306a36Sopenharmony_ci else 22762306a36Sopenharmony_ci static_branch_disable(&br_mst_used); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci br_opt_toggle(br, BROPT_MST_ENABLED, on); 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cisize_t br_mst_info_size(const struct net_bridge_vlan_group *vg) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci DECLARE_BITMAP(seen, VLAN_N_VID) = { 0 }; 23662306a36Sopenharmony_ci const struct net_bridge_vlan *v; 23762306a36Sopenharmony_ci size_t sz; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* IFLA_BRIDGE_MST */ 24062306a36Sopenharmony_ci sz = nla_total_size(0); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci list_for_each_entry_rcu(v, &vg->vlan_list, vlist) { 24362306a36Sopenharmony_ci if (test_bit(v->brvlan->msti, seen)) 24462306a36Sopenharmony_ci continue; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* IFLA_BRIDGE_MST_ENTRY */ 24762306a36Sopenharmony_ci sz += nla_total_size(0) + 24862306a36Sopenharmony_ci /* IFLA_BRIDGE_MST_ENTRY_MSTI */ 24962306a36Sopenharmony_ci nla_total_size(sizeof(u16)) + 25062306a36Sopenharmony_ci /* IFLA_BRIDGE_MST_ENTRY_STATE */ 25162306a36Sopenharmony_ci nla_total_size(sizeof(u8)); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci __set_bit(v->brvlan->msti, seen); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return sz; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciint br_mst_fill_info(struct sk_buff *skb, 26062306a36Sopenharmony_ci const struct net_bridge_vlan_group *vg) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci DECLARE_BITMAP(seen, VLAN_N_VID) = { 0 }; 26362306a36Sopenharmony_ci const struct net_bridge_vlan *v; 26462306a36Sopenharmony_ci struct nlattr *nest; 26562306a36Sopenharmony_ci int err = 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci list_for_each_entry(v, &vg->vlan_list, vlist) { 26862306a36Sopenharmony_ci if (test_bit(v->brvlan->msti, seen)) 26962306a36Sopenharmony_ci continue; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, IFLA_BRIDGE_MST_ENTRY); 27262306a36Sopenharmony_ci if (!nest || 27362306a36Sopenharmony_ci nla_put_u16(skb, IFLA_BRIDGE_MST_ENTRY_MSTI, v->brvlan->msti) || 27462306a36Sopenharmony_ci nla_put_u8(skb, IFLA_BRIDGE_MST_ENTRY_STATE, v->state)) { 27562306a36Sopenharmony_ci err = -EMSGSIZE; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci nla_nest_end(skb, nest); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci __set_bit(v->brvlan->msti, seen); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return err; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const struct nla_policy br_mst_nl_policy[IFLA_BRIDGE_MST_ENTRY_MAX + 1] = { 28762306a36Sopenharmony_ci [IFLA_BRIDGE_MST_ENTRY_MSTI] = NLA_POLICY_RANGE(NLA_U16, 28862306a36Sopenharmony_ci 1, /* 0 reserved for CST */ 28962306a36Sopenharmony_ci VLAN_N_VID - 1), 29062306a36Sopenharmony_ci [IFLA_BRIDGE_MST_ENTRY_STATE] = NLA_POLICY_RANGE(NLA_U8, 29162306a36Sopenharmony_ci BR_STATE_DISABLED, 29262306a36Sopenharmony_ci BR_STATE_BLOCKING), 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int br_mst_process_one(struct net_bridge_port *p, 29662306a36Sopenharmony_ci const struct nlattr *attr, 29762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct nlattr *tb[IFLA_BRIDGE_MST_ENTRY_MAX + 1]; 30062306a36Sopenharmony_ci u16 msti; 30162306a36Sopenharmony_ci u8 state; 30262306a36Sopenharmony_ci int err; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci err = nla_parse_nested(tb, IFLA_BRIDGE_MST_ENTRY_MAX, attr, 30562306a36Sopenharmony_ci br_mst_nl_policy, extack); 30662306a36Sopenharmony_ci if (err) 30762306a36Sopenharmony_ci return err; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (!tb[IFLA_BRIDGE_MST_ENTRY_MSTI]) { 31062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "MSTI not specified"); 31162306a36Sopenharmony_ci return -EINVAL; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (!tb[IFLA_BRIDGE_MST_ENTRY_STATE]) { 31562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "State not specified"); 31662306a36Sopenharmony_ci return -EINVAL; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci msti = nla_get_u16(tb[IFLA_BRIDGE_MST_ENTRY_MSTI]); 32062306a36Sopenharmony_ci state = nla_get_u8(tb[IFLA_BRIDGE_MST_ENTRY_STATE]); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return br_mst_set_state(p, msti, state, extack); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciint br_mst_process(struct net_bridge_port *p, const struct nlattr *mst_attr, 32662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct nlattr *attr; 32962306a36Sopenharmony_ci int err, msts = 0; 33062306a36Sopenharmony_ci int rem; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (!br_opt_get(p->br, BROPT_MST_ENABLED)) { 33362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can't modify MST state when MST is disabled"); 33462306a36Sopenharmony_ci return -EBUSY; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci nla_for_each_nested(attr, mst_attr, rem) { 33862306a36Sopenharmony_ci switch (nla_type(attr)) { 33962306a36Sopenharmony_ci case IFLA_BRIDGE_MST_ENTRY: 34062306a36Sopenharmony_ci err = br_mst_process_one(p, attr, extack); 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci default: 34362306a36Sopenharmony_ci continue; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci msts++; 34762306a36Sopenharmony_ci if (err) 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (!msts) { 35262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Found no MST entries to process"); 35362306a36Sopenharmony_ci err = -EINVAL; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return err; 35762306a36Sopenharmony_ci} 358