xref: /linux/net/bridge/br_mst.c (revision 546ceb1d)
1ec7328b5STobias Waldekranz // SPDX-License-Identifier: GPL-2.0-or-later
2ec7328b5STobias Waldekranz /*
3ec7328b5STobias Waldekranz  *	Bridge Multiple Spanning Tree Support
4ec7328b5STobias Waldekranz  *
5ec7328b5STobias Waldekranz  *	Authors:
6ec7328b5STobias Waldekranz  *	Tobias Waldekranz		<tobias@waldekranz.com>
7ec7328b5STobias Waldekranz  */
8ec7328b5STobias Waldekranz 
9ec7328b5STobias Waldekranz #include <linux/kernel.h>
1087c167bbSTobias Waldekranz #include <net/switchdev.h>
11ec7328b5STobias Waldekranz 
12ec7328b5STobias Waldekranz #include "br_private.h"
13ec7328b5STobias Waldekranz 
14ec7328b5STobias Waldekranz DEFINE_STATIC_KEY_FALSE(br_mst_used);
15ec7328b5STobias Waldekranz 
br_mst_enabled(const struct net_device * dev)1648d57b2eSTobias Waldekranz bool br_mst_enabled(const struct net_device *dev)
1748d57b2eSTobias Waldekranz {
1848d57b2eSTobias Waldekranz 	if (!netif_is_bridge_master(dev))
1948d57b2eSTobias Waldekranz 		return false;
2048d57b2eSTobias Waldekranz 
2148d57b2eSTobias Waldekranz 	return br_opt_get(netdev_priv(dev), BROPT_MST_ENABLED);
2248d57b2eSTobias Waldekranz }
2348d57b2eSTobias Waldekranz EXPORT_SYMBOL_GPL(br_mst_enabled);
2448d57b2eSTobias Waldekranz 
br_mst_get_info(const struct net_device * dev,u16 msti,unsigned long * vids)25cceac97aSTobias Waldekranz int br_mst_get_info(const struct net_device *dev, u16 msti, unsigned long *vids)
26cceac97aSTobias Waldekranz {
27cceac97aSTobias Waldekranz 	const struct net_bridge_vlan_group *vg;
28cceac97aSTobias Waldekranz 	const struct net_bridge_vlan *v;
29cceac97aSTobias Waldekranz 	const struct net_bridge *br;
30cceac97aSTobias Waldekranz 
31cceac97aSTobias Waldekranz 	ASSERT_RTNL();
32cceac97aSTobias Waldekranz 
33cceac97aSTobias Waldekranz 	if (!netif_is_bridge_master(dev))
34cceac97aSTobias Waldekranz 		return -EINVAL;
35cceac97aSTobias Waldekranz 
36cceac97aSTobias Waldekranz 	br = netdev_priv(dev);
37cceac97aSTobias Waldekranz 	if (!br_opt_get(br, BROPT_MST_ENABLED))
38cceac97aSTobias Waldekranz 		return -EINVAL;
39cceac97aSTobias Waldekranz 
40cceac97aSTobias Waldekranz 	vg = br_vlan_group(br);
41cceac97aSTobias Waldekranz 
42cceac97aSTobias Waldekranz 	list_for_each_entry(v, &vg->vlan_list, vlist) {
43cceac97aSTobias Waldekranz 		if (v->msti == msti)
44cceac97aSTobias Waldekranz 			__set_bit(v->vid, vids);
45cceac97aSTobias Waldekranz 	}
46cceac97aSTobias Waldekranz 
47cceac97aSTobias Waldekranz 	return 0;
48cceac97aSTobias Waldekranz }
49cceac97aSTobias Waldekranz EXPORT_SYMBOL_GPL(br_mst_get_info);
50cceac97aSTobias Waldekranz 
br_mst_get_state(const struct net_device * dev,u16 msti,u8 * state)51f54fd0e1STobias Waldekranz int br_mst_get_state(const struct net_device *dev, u16 msti, u8 *state)
52f54fd0e1STobias Waldekranz {
53f54fd0e1STobias Waldekranz 	const struct net_bridge_port *p = NULL;
54f54fd0e1STobias Waldekranz 	const struct net_bridge_vlan_group *vg;
55f54fd0e1STobias Waldekranz 	const struct net_bridge_vlan *v;
56f54fd0e1STobias Waldekranz 
57f54fd0e1STobias Waldekranz 	ASSERT_RTNL();
58f54fd0e1STobias Waldekranz 
59f54fd0e1STobias Waldekranz 	p = br_port_get_check_rtnl(dev);
60f54fd0e1STobias Waldekranz 	if (!p || !br_opt_get(p->br, BROPT_MST_ENABLED))
61f54fd0e1STobias Waldekranz 		return -EINVAL;
62f54fd0e1STobias Waldekranz 
63f54fd0e1STobias Waldekranz 	vg = nbp_vlan_group(p);
64f54fd0e1STobias Waldekranz 
65f54fd0e1STobias Waldekranz 	list_for_each_entry(v, &vg->vlan_list, vlist) {
66f54fd0e1STobias Waldekranz 		if (v->brvlan->msti == msti) {
67f54fd0e1STobias Waldekranz 			*state = v->state;
68f54fd0e1STobias Waldekranz 			return 0;
69f54fd0e1STobias Waldekranz 		}
70f54fd0e1STobias Waldekranz 	}
71f54fd0e1STobias Waldekranz 
72f54fd0e1STobias Waldekranz 	return -ENOENT;
73f54fd0e1STobias Waldekranz }
74f54fd0e1STobias Waldekranz EXPORT_SYMBOL_GPL(br_mst_get_state);
75f54fd0e1STobias Waldekranz 
br_mst_vlan_set_state(struct net_bridge_vlan_group * vg,struct net_bridge_vlan * v,u8 state)7636c92936SNikolay Aleksandrov static void br_mst_vlan_set_state(struct net_bridge_vlan_group *vg,
7736c92936SNikolay Aleksandrov 				  struct net_bridge_vlan *v,
78ec7328b5STobias Waldekranz 				  u8 state)
79ec7328b5STobias Waldekranz {
803a7c1661SNikolay Aleksandrov 	if (br_vlan_get_state(v) == state)
81ec7328b5STobias Waldekranz 		return;
82ec7328b5STobias Waldekranz 
83ec7328b5STobias Waldekranz 	br_vlan_set_state(v, state);
84ec7328b5STobias Waldekranz 
85ec7328b5STobias Waldekranz 	if (v->vid == vg->pvid)
86ec7328b5STobias Waldekranz 		br_vlan_set_pvid_state(vg, state);
87ec7328b5STobias Waldekranz }
88ec7328b5STobias Waldekranz 
br_mst_set_state(struct net_bridge_port * p,u16 msti,u8 state,struct netlink_ext_ack * extack)89ec7328b5STobias Waldekranz int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
90ec7328b5STobias Waldekranz 		     struct netlink_ext_ack *extack)
91ec7328b5STobias Waldekranz {
927ae9147fSTobias Waldekranz 	struct switchdev_attr attr = {
937ae9147fSTobias Waldekranz 		.id = SWITCHDEV_ATTR_ID_PORT_MST_STATE,
947ae9147fSTobias Waldekranz 		.orig_dev = p->dev,
957ae9147fSTobias Waldekranz 		.u.mst_state = {
967ae9147fSTobias Waldekranz 			.msti = msti,
977ae9147fSTobias Waldekranz 			.state = state,
987ae9147fSTobias Waldekranz 		},
997ae9147fSTobias Waldekranz 	};
100ec7328b5STobias Waldekranz 	struct net_bridge_vlan_group *vg;
101ec7328b5STobias Waldekranz 	struct net_bridge_vlan *v;
1023a7c1661SNikolay Aleksandrov 	int err = 0;
103ec7328b5STobias Waldekranz 
1043a7c1661SNikolay Aleksandrov 	rcu_read_lock();
105*546ceb1dSNikolay Aleksandrov 	vg = nbp_vlan_group_rcu(p);
106ec7328b5STobias Waldekranz 	if (!vg)
1073a7c1661SNikolay Aleksandrov 		goto out;
108ec7328b5STobias Waldekranz 
1097ae9147fSTobias Waldekranz 	/* MSTI 0 (CST) state changes are notified via the regular
1107ae9147fSTobias Waldekranz 	 * SWITCHDEV_ATTR_ID_PORT_STP_STATE.
1117ae9147fSTobias Waldekranz 	 */
1127ae9147fSTobias Waldekranz 	if (msti) {
1137ae9147fSTobias Waldekranz 		err = switchdev_port_attr_set(p->dev, &attr, extack);
1147ae9147fSTobias Waldekranz 		if (err && err != -EOPNOTSUPP)
1153a7c1661SNikolay Aleksandrov 			goto out;
1167ae9147fSTobias Waldekranz 	}
1177ae9147fSTobias Waldekranz 
1183a7c1661SNikolay Aleksandrov 	err = 0;
1193a7c1661SNikolay Aleksandrov 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
120ec7328b5STobias Waldekranz 		if (v->brvlan->msti != msti)
121ec7328b5STobias Waldekranz 			continue;
122ec7328b5STobias Waldekranz 
12336c92936SNikolay Aleksandrov 		br_mst_vlan_set_state(vg, v, state);
124ec7328b5STobias Waldekranz 	}
125ec7328b5STobias Waldekranz 
1263a7c1661SNikolay Aleksandrov out:
1273a7c1661SNikolay Aleksandrov 	rcu_read_unlock();
1283a7c1661SNikolay Aleksandrov 	return err;
129ec7328b5STobias Waldekranz }
130ec7328b5STobias Waldekranz 
br_mst_vlan_sync_state(struct net_bridge_vlan * pv,u16 msti)1318c678d60STobias Waldekranz static void br_mst_vlan_sync_state(struct net_bridge_vlan *pv, u16 msti)
1328c678d60STobias Waldekranz {
1338c678d60STobias Waldekranz 	struct net_bridge_vlan_group *vg = nbp_vlan_group(pv->port);
1348c678d60STobias Waldekranz 	struct net_bridge_vlan *v;
1358c678d60STobias Waldekranz 
1368c678d60STobias Waldekranz 	list_for_each_entry(v, &vg->vlan_list, vlist) {
1378c678d60STobias Waldekranz 		/* If this port already has a defined state in this
1388c678d60STobias Waldekranz 		 * MSTI (through some other VLAN membership), inherit
1398c678d60STobias Waldekranz 		 * it.
1408c678d60STobias Waldekranz 		 */
1418c678d60STobias Waldekranz 		if (v != pv && v->brvlan->msti == msti) {
14236c92936SNikolay Aleksandrov 			br_mst_vlan_set_state(vg, pv, v->state);
1438c678d60STobias Waldekranz 			return;
1448c678d60STobias Waldekranz 		}
1458c678d60STobias Waldekranz 	}
1468c678d60STobias Waldekranz 
1478c678d60STobias Waldekranz 	/* Otherwise, start out in a new MSTI with all ports disabled. */
14836c92936SNikolay Aleksandrov 	return br_mst_vlan_set_state(vg, pv, BR_STATE_DISABLED);
1498c678d60STobias Waldekranz }
1508c678d60STobias Waldekranz 
br_mst_vlan_set_msti(struct net_bridge_vlan * mv,u16 msti)1518c678d60STobias Waldekranz int br_mst_vlan_set_msti(struct net_bridge_vlan *mv, u16 msti)
1528c678d60STobias Waldekranz {
1536284c723STobias Waldekranz 	struct switchdev_attr attr = {
1546284c723STobias Waldekranz 		.id = SWITCHDEV_ATTR_ID_VLAN_MSTI,
1556284c723STobias Waldekranz 		.orig_dev = mv->br->dev,
1566284c723STobias Waldekranz 		.u.vlan_msti = {
1576284c723STobias Waldekranz 			.vid = mv->vid,
1586284c723STobias Waldekranz 			.msti = msti,
1596284c723STobias Waldekranz 		},
1606284c723STobias Waldekranz 	};
1618c678d60STobias Waldekranz 	struct net_bridge_vlan_group *vg;
1628c678d60STobias Waldekranz 	struct net_bridge_vlan *pv;
1638c678d60STobias Waldekranz 	struct net_bridge_port *p;
1646284c723STobias Waldekranz 	int err;
1658c678d60STobias Waldekranz 
1668c678d60STobias Waldekranz 	if (mv->msti == msti)
1678c678d60STobias Waldekranz 		return 0;
1688c678d60STobias Waldekranz 
1696284c723STobias Waldekranz 	err = switchdev_port_attr_set(mv->br->dev, &attr, NULL);
1706284c723STobias Waldekranz 	if (err && err != -EOPNOTSUPP)
1716284c723STobias Waldekranz 		return err;
1726284c723STobias Waldekranz 
1738c678d60STobias Waldekranz 	mv->msti = msti;
1748c678d60STobias Waldekranz 
1758c678d60STobias Waldekranz 	list_for_each_entry(p, &mv->br->port_list, list) {
1768c678d60STobias Waldekranz 		vg = nbp_vlan_group(p);
1778c678d60STobias Waldekranz 
1788c678d60STobias Waldekranz 		pv = br_vlan_find(vg, mv->vid);
1798c678d60STobias Waldekranz 		if (pv)
1808c678d60STobias Waldekranz 			br_mst_vlan_sync_state(pv, msti);
1818c678d60STobias Waldekranz 	}
1828c678d60STobias Waldekranz 
1838c678d60STobias Waldekranz 	return 0;
1848c678d60STobias Waldekranz }
1858c678d60STobias Waldekranz 
br_mst_vlan_init_state(struct net_bridge_vlan * v)186ec7328b5STobias Waldekranz void br_mst_vlan_init_state(struct net_bridge_vlan *v)
187ec7328b5STobias Waldekranz {
188ec7328b5STobias Waldekranz 	/* VLANs always start out in MSTI 0 (CST) */
189ec7328b5STobias Waldekranz 	v->msti = 0;
190ec7328b5STobias Waldekranz 
191ec7328b5STobias Waldekranz 	if (br_vlan_is_master(v))
192ec7328b5STobias Waldekranz 		v->state = BR_STATE_FORWARDING;
193ec7328b5STobias Waldekranz 	else
194ec7328b5STobias Waldekranz 		v->state = v->port->state;
195ec7328b5STobias Waldekranz }
196ec7328b5STobias Waldekranz 
br_mst_set_enabled(struct net_bridge * br,bool on,struct netlink_ext_ack * extack)197ec7328b5STobias Waldekranz int br_mst_set_enabled(struct net_bridge *br, bool on,
198ec7328b5STobias Waldekranz 		       struct netlink_ext_ack *extack)
199ec7328b5STobias Waldekranz {
20087c167bbSTobias Waldekranz 	struct switchdev_attr attr = {
20187c167bbSTobias Waldekranz 		.id = SWITCHDEV_ATTR_ID_BRIDGE_MST,
20287c167bbSTobias Waldekranz 		.orig_dev = br->dev,
20387c167bbSTobias Waldekranz 		.u.mst = on,
20487c167bbSTobias Waldekranz 	};
205ec7328b5STobias Waldekranz 	struct net_bridge_vlan_group *vg;
206ec7328b5STobias Waldekranz 	struct net_bridge_port *p;
20787c167bbSTobias Waldekranz 	int err;
208ec7328b5STobias Waldekranz 
209ec7328b5STobias Waldekranz 	list_for_each_entry(p, &br->port_list, list) {
210ec7328b5STobias Waldekranz 		vg = nbp_vlan_group(p);
211ec7328b5STobias Waldekranz 
212ec7328b5STobias Waldekranz 		if (!vg->num_vlans)
213ec7328b5STobias Waldekranz 			continue;
214ec7328b5STobias Waldekranz 
215ec7328b5STobias Waldekranz 		NL_SET_ERR_MSG(extack,
216ec7328b5STobias Waldekranz 			       "MST mode can't be changed while VLANs exist");
217ec7328b5STobias Waldekranz 		return -EBUSY;
218ec7328b5STobias Waldekranz 	}
219ec7328b5STobias Waldekranz 
220ec7328b5STobias Waldekranz 	if (br_opt_get(br, BROPT_MST_ENABLED) == on)
221ec7328b5STobias Waldekranz 		return 0;
222ec7328b5STobias Waldekranz 
22387c167bbSTobias Waldekranz 	err = switchdev_port_attr_set(br->dev, &attr, extack);
22487c167bbSTobias Waldekranz 	if (err && err != -EOPNOTSUPP)
22587c167bbSTobias Waldekranz 		return err;
22687c167bbSTobias Waldekranz 
227ec7328b5STobias Waldekranz 	if (on)
228ec7328b5STobias Waldekranz 		static_branch_enable(&br_mst_used);
229ec7328b5STobias Waldekranz 	else
230ec7328b5STobias Waldekranz 		static_branch_disable(&br_mst_used);
231ec7328b5STobias Waldekranz 
232ec7328b5STobias Waldekranz 	br_opt_toggle(br, BROPT_MST_ENABLED, on);
233ec7328b5STobias Waldekranz 	return 0;
234ec7328b5STobias Waldekranz }
235122c2948STobias Waldekranz 
br_mst_info_size(const struct net_bridge_vlan_group * vg)236122c2948STobias Waldekranz size_t br_mst_info_size(const struct net_bridge_vlan_group *vg)
237122c2948STobias Waldekranz {
238122c2948STobias Waldekranz 	DECLARE_BITMAP(seen, VLAN_N_VID) = { 0 };
239122c2948STobias Waldekranz 	const struct net_bridge_vlan *v;
240122c2948STobias Waldekranz 	size_t sz;
241122c2948STobias Waldekranz 
242122c2948STobias Waldekranz 	/* IFLA_BRIDGE_MST */
243122c2948STobias Waldekranz 	sz = nla_total_size(0);
244122c2948STobias Waldekranz 
245122c2948STobias Waldekranz 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
246122c2948STobias Waldekranz 		if (test_bit(v->brvlan->msti, seen))
247122c2948STobias Waldekranz 			continue;
248122c2948STobias Waldekranz 
249122c2948STobias Waldekranz 		/* IFLA_BRIDGE_MST_ENTRY */
250122c2948STobias Waldekranz 		sz += nla_total_size(0) +
251122c2948STobias Waldekranz 			/* IFLA_BRIDGE_MST_ENTRY_MSTI */
252122c2948STobias Waldekranz 			nla_total_size(sizeof(u16)) +
253122c2948STobias Waldekranz 			/* IFLA_BRIDGE_MST_ENTRY_STATE */
254122c2948STobias Waldekranz 			nla_total_size(sizeof(u8));
255122c2948STobias Waldekranz 
256122c2948STobias Waldekranz 		__set_bit(v->brvlan->msti, seen);
257122c2948STobias Waldekranz 	}
258122c2948STobias Waldekranz 
259122c2948STobias Waldekranz 	return sz;
260122c2948STobias Waldekranz }
261122c2948STobias Waldekranz 
br_mst_fill_info(struct sk_buff * skb,const struct net_bridge_vlan_group * vg)262122c2948STobias Waldekranz int br_mst_fill_info(struct sk_buff *skb,
263122c2948STobias Waldekranz 		     const struct net_bridge_vlan_group *vg)
264122c2948STobias Waldekranz {
265122c2948STobias Waldekranz 	DECLARE_BITMAP(seen, VLAN_N_VID) = { 0 };
266122c2948STobias Waldekranz 	const struct net_bridge_vlan *v;
267122c2948STobias Waldekranz 	struct nlattr *nest;
268122c2948STobias Waldekranz 	int err = 0;
269122c2948STobias Waldekranz 
270122c2948STobias Waldekranz 	list_for_each_entry(v, &vg->vlan_list, vlist) {
271122c2948STobias Waldekranz 		if (test_bit(v->brvlan->msti, seen))
272122c2948STobias Waldekranz 			continue;
273122c2948STobias Waldekranz 
274122c2948STobias Waldekranz 		nest = nla_nest_start_noflag(skb, IFLA_BRIDGE_MST_ENTRY);
275122c2948STobias Waldekranz 		if (!nest ||
276122c2948STobias Waldekranz 		    nla_put_u16(skb, IFLA_BRIDGE_MST_ENTRY_MSTI, v->brvlan->msti) ||
277122c2948STobias Waldekranz 		    nla_put_u8(skb, IFLA_BRIDGE_MST_ENTRY_STATE, v->state)) {
278122c2948STobias Waldekranz 			err = -EMSGSIZE;
279122c2948STobias Waldekranz 			break;
280122c2948STobias Waldekranz 		}
281122c2948STobias Waldekranz 		nla_nest_end(skb, nest);
282122c2948STobias Waldekranz 
283122c2948STobias Waldekranz 		__set_bit(v->brvlan->msti, seen);
284122c2948STobias Waldekranz 	}
285122c2948STobias Waldekranz 
286122c2948STobias Waldekranz 	return err;
287122c2948STobias Waldekranz }
288122c2948STobias Waldekranz 
289122c2948STobias Waldekranz static const struct nla_policy br_mst_nl_policy[IFLA_BRIDGE_MST_ENTRY_MAX + 1] = {
290122c2948STobias Waldekranz 	[IFLA_BRIDGE_MST_ENTRY_MSTI] = NLA_POLICY_RANGE(NLA_U16,
291122c2948STobias Waldekranz 						   1, /* 0 reserved for CST */
292122c2948STobias Waldekranz 						   VLAN_N_VID - 1),
293122c2948STobias Waldekranz 	[IFLA_BRIDGE_MST_ENTRY_STATE] = NLA_POLICY_RANGE(NLA_U8,
294122c2948STobias Waldekranz 						    BR_STATE_DISABLED,
295122c2948STobias Waldekranz 						    BR_STATE_BLOCKING),
296122c2948STobias Waldekranz };
297122c2948STobias Waldekranz 
br_mst_process_one(struct net_bridge_port * p,const struct nlattr * attr,struct netlink_ext_ack * extack)298122c2948STobias Waldekranz static int br_mst_process_one(struct net_bridge_port *p,
299122c2948STobias Waldekranz 			      const struct nlattr *attr,
300122c2948STobias Waldekranz 			      struct netlink_ext_ack *extack)
301122c2948STobias Waldekranz {
302122c2948STobias Waldekranz 	struct nlattr *tb[IFLA_BRIDGE_MST_ENTRY_MAX + 1];
303122c2948STobias Waldekranz 	u16 msti;
304122c2948STobias Waldekranz 	u8 state;
305122c2948STobias Waldekranz 	int err;
306122c2948STobias Waldekranz 
307122c2948STobias Waldekranz 	err = nla_parse_nested(tb, IFLA_BRIDGE_MST_ENTRY_MAX, attr,
308122c2948STobias Waldekranz 			       br_mst_nl_policy, extack);
309122c2948STobias Waldekranz 	if (err)
310122c2948STobias Waldekranz 		return err;
311122c2948STobias Waldekranz 
312122c2948STobias Waldekranz 	if (!tb[IFLA_BRIDGE_MST_ENTRY_MSTI]) {
313122c2948STobias Waldekranz 		NL_SET_ERR_MSG_MOD(extack, "MSTI not specified");
314122c2948STobias Waldekranz 		return -EINVAL;
315122c2948STobias Waldekranz 	}
316122c2948STobias Waldekranz 
317122c2948STobias Waldekranz 	if (!tb[IFLA_BRIDGE_MST_ENTRY_STATE]) {
318122c2948STobias Waldekranz 		NL_SET_ERR_MSG_MOD(extack, "State not specified");
319122c2948STobias Waldekranz 		return -EINVAL;
320122c2948STobias Waldekranz 	}
321122c2948STobias Waldekranz 
322122c2948STobias Waldekranz 	msti = nla_get_u16(tb[IFLA_BRIDGE_MST_ENTRY_MSTI]);
323122c2948STobias Waldekranz 	state = nla_get_u8(tb[IFLA_BRIDGE_MST_ENTRY_STATE]);
324122c2948STobias Waldekranz 
325122c2948STobias Waldekranz 	return br_mst_set_state(p, msti, state, extack);
326122c2948STobias Waldekranz }
327122c2948STobias Waldekranz 
br_mst_process(struct net_bridge_port * p,const struct nlattr * mst_attr,struct netlink_ext_ack * extack)328122c2948STobias Waldekranz int br_mst_process(struct net_bridge_port *p, const struct nlattr *mst_attr,
329122c2948STobias Waldekranz 		   struct netlink_ext_ack *extack)
330122c2948STobias Waldekranz {
331122c2948STobias Waldekranz 	struct nlattr *attr;
332122c2948STobias Waldekranz 	int err, msts = 0;
333122c2948STobias Waldekranz 	int rem;
334122c2948STobias Waldekranz 
335122c2948STobias Waldekranz 	if (!br_opt_get(p->br, BROPT_MST_ENABLED)) {
336122c2948STobias Waldekranz 		NL_SET_ERR_MSG_MOD(extack, "Can't modify MST state when MST is disabled");
337122c2948STobias Waldekranz 		return -EBUSY;
338122c2948STobias Waldekranz 	}
339122c2948STobias Waldekranz 
340122c2948STobias Waldekranz 	nla_for_each_nested(attr, mst_attr, rem) {
341122c2948STobias Waldekranz 		switch (nla_type(attr)) {
342122c2948STobias Waldekranz 		case IFLA_BRIDGE_MST_ENTRY:
343122c2948STobias Waldekranz 			err = br_mst_process_one(p, attr, extack);
344122c2948STobias Waldekranz 			break;
345122c2948STobias Waldekranz 		default:
346122c2948STobias Waldekranz 			continue;
347122c2948STobias Waldekranz 		}
348122c2948STobias Waldekranz 
349122c2948STobias Waldekranz 		msts++;
350122c2948STobias Waldekranz 		if (err)
351122c2948STobias Waldekranz 			break;
352122c2948STobias Waldekranz 	}
353122c2948STobias Waldekranz 
354122c2948STobias Waldekranz 	if (!msts) {
355122c2948STobias Waldekranz 		NL_SET_ERR_MSG_MOD(extack, "Found no MST entries to process");
356122c2948STobias Waldekranz 		err = -EINVAL;
357122c2948STobias Waldekranz 	}
358122c2948STobias Waldekranz 
359122c2948STobias Waldekranz 	return err;
360122c2948STobias Waldekranz }
361