File Index
Files and directories
Sign in
FILE

br_mst.c

File preview with syntax highlighting.

linux-6.19.14/net/bridge/br_mst.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *	Bridge Multiple Spanning Tree Support
  4 *
  5 *	Authors:
  6 *	Tobias Waldekranz		<tobias@waldekranz.com>
  7 */
  8
  9#include <linux/kernel.h>
 10#include <net/switchdev.h>
 11
 12#include "br_private.h"
 13
 14DEFINE_STATIC_KEY_FALSE(br_mst_used);
 15
 16bool br_mst_enabled(const struct net_device *dev)
 17{
 18	if (!netif_is_bridge_master(dev))
 19		return false;
 20
 21	return br_opt_get(netdev_priv(dev), BROPT_MST_ENABLED);
 22}
 23EXPORT_SYMBOL_GPL(br_mst_enabled);
 24
 25void br_mst_uninit(struct net_bridge *br)
 26{
 27	if (br_opt_get(br, BROPT_MST_ENABLED))
 28		static_branch_dec(&br_mst_used);
 29}
 30
 31int br_mst_get_info(const struct net_device *dev, u16 msti, unsigned long *vids)
 32{
 33	const struct net_bridge_vlan_group *vg;
 34	const struct net_bridge_vlan *v;
 35	const struct net_bridge *br;
 36
 37	ASSERT_RTNL();
 38
 39	if (!netif_is_bridge_master(dev))
 40		return -EINVAL;
 41
 42	br = netdev_priv(dev);
 43	if (!br_opt_get(br, BROPT_MST_ENABLED))
 44		return -EINVAL;
 45
 46	vg = br_vlan_group(br);
 47
 48	list_for_each_entry(v, &vg->vlan_list, vlist) {
 49		if (v->msti == msti)
 50			__set_bit(v->vid, vids);
 51	}
 52
 53	return 0;
 54}
 55EXPORT_SYMBOL_GPL(br_mst_get_info);
 56
 57int br_mst_get_state(const struct net_device *dev, u16 msti, u8 *state)
 58{
 59	const struct net_bridge_port *p = NULL;
 60	const struct net_bridge_vlan_group *vg;
 61	const struct net_bridge_vlan *v;
 62
 63	ASSERT_RTNL();
 64
 65	p = br_port_get_check_rtnl(dev);
 66	if (!p || !br_opt_get(p->br, BROPT_MST_ENABLED))
 67		return -EINVAL;
 68
 69	vg = nbp_vlan_group(p);
 70
 71	list_for_each_entry(v, &vg->vlan_list, vlist) {
 72		if (v->brvlan->msti == msti) {
 73			*state = v->state;
 74			return 0;
 75		}
 76	}
 77
 78	return -ENOENT;
 79}
 80EXPORT_SYMBOL_GPL(br_mst_get_state);
 81
 82static void br_mst_vlan_set_state(struct net_bridge_vlan_group *vg,
 83				  struct net_bridge_vlan *v,
 84				  u8 state)
 85{
 86	if (br_vlan_get_state(v) == state)
 87		return;
 88
 89	if (v->vid == vg->pvid)
 90		br_vlan_set_pvid_state(vg, state);
 91
 92	br_vlan_set_state(v, state);
 93}
 94
 95int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
 96		     struct netlink_ext_ack *extack)
 97{
 98	struct switchdev_attr attr = {
 99		.id = SWITCHDEV_ATTR_ID_PORT_MST_STATE,
100		.orig_dev = p->dev,
101		.u.mst_state = {
102			.msti = msti,
103			.state = state,
104		},
105	};
106	struct net_bridge_vlan_group *vg;
107	struct net_bridge_vlan *v;
108	int err = 0;
109
110	rcu_read_lock();
111	vg = nbp_vlan_group_rcu(p);
112	if (!vg)
113		goto out;
114
115	/* MSTI 0 (CST) state changes are notified via the regular
116	 * SWITCHDEV_ATTR_ID_PORT_STP_STATE.
117	 */
118	if (msti) {
119		err = switchdev_port_attr_set(p->dev, &attr, extack);
120		if (err && err != -EOPNOTSUPP)
121			goto out;
122	}
123
124	err = 0;
125	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
126		if (v->brvlan->msti != msti)
127			continue;
128
129		br_mst_vlan_set_state(vg, v, state);
130	}
131
132out:
133	rcu_read_unlock();
134	return err;
135}
136
137static void br_mst_vlan_sync_state(struct net_bridge_vlan *pv, u16 msti)
138{
139	struct net_bridge_vlan_group *vg = nbp_vlan_group(pv->port);
140	struct net_bridge_vlan *v;
141
142	list_for_each_entry(v, &vg->vlan_list, vlist) {
143		/* If this port already has a defined state in this
144		 * MSTI (through some other VLAN membership), inherit
145		 * it.
146		 */
147		if (v != pv && v->brvlan->msti == msti) {
148			br_mst_vlan_set_state(vg, pv, v->state);
149			return;
150		}
151	}
152
153	/* Otherwise, start out in a new MSTI with all ports disabled. */
154	return br_mst_vlan_set_state(vg, pv, BR_STATE_DISABLED);
155}
156
157int br_mst_vlan_set_msti(struct net_bridge_vlan *mv, u16 msti)
158{
159	struct switchdev_attr attr = {
160		.id = SWITCHDEV_ATTR_ID_VLAN_MSTI,
161		.orig_dev = mv->br->dev,
162		.u.vlan_msti = {
163			.vid = mv->vid,
164			.msti = msti,
165		},
166	};
167	struct net_bridge_vlan_group *vg;
168	struct net_bridge_vlan *pv;
169	struct net_bridge_port *p;
170	int err;
171
172	if (mv->msti == msti)
173		return 0;
174
175	err = switchdev_port_attr_set(mv->br->dev, &attr, NULL);
176	if (err && err != -EOPNOTSUPP)
177		return err;
178
179	mv->msti = msti;
180
181	list_for_each_entry(p, &mv->br->port_list, list) {
182		vg = nbp_vlan_group(p);
183
184		pv = br_vlan_find(vg, mv->vid);
185		if (pv)
186			br_mst_vlan_sync_state(pv, msti);
187	}
188
189	return 0;
190}
191
192void br_mst_vlan_init_state(struct net_bridge_vlan *v)
193{
194	/* VLANs always start out in MSTI 0 (CST) */
195	v->msti = 0;
196
197	if (br_vlan_is_master(v))
198		v->state = BR_STATE_FORWARDING;
199	else
200		v->state = v->port->state;
201}
202
203int br_mst_set_enabled(struct net_bridge *br, bool on,
204		       struct netlink_ext_ack *extack)
205{
206	struct switchdev_attr attr = {
207		.id = SWITCHDEV_ATTR_ID_BRIDGE_MST,
208		.orig_dev = br->dev,
209		.u.mst = on,
210	};
211	struct net_bridge_vlan_group *vg;
212	struct net_bridge_port *p;
213	int err;
214
215	list_for_each_entry(p, &br->port_list, list) {
216		vg = nbp_vlan_group(p);
217
218		if (!vg->num_vlans)
219			continue;
220
221		NL_SET_ERR_MSG(extack,
222			       "MST mode can't be changed while VLANs exist");
223		return -EBUSY;
224	}
225
226	if (br_opt_get(br, BROPT_MST_ENABLED) == on)
227		return 0;
228
229	err = switchdev_port_attr_set(br->dev, &attr, extack);
230	if (err && err != -EOPNOTSUPP)
231		return err;
232
233	if (on)
234		static_branch_inc(&br_mst_used);
235	else
236		static_branch_dec(&br_mst_used);
237
238	br_opt_toggle(br, BROPT_MST_ENABLED, on);
239	return 0;
240}
241
242size_t br_mst_info_size(const struct net_bridge_vlan_group *vg)
243{
244	DECLARE_BITMAP(seen, VLAN_N_VID) = { 0 };
245	const struct net_bridge_vlan *v;
246	size_t sz;
247
248	/* IFLA_BRIDGE_MST */
249	sz = nla_total_size(0);
250
251	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
252		if (test_bit(v->brvlan->msti, seen))
253			continue;
254
255		/* IFLA_BRIDGE_MST_ENTRY */
256		sz += nla_total_size(0) +
257			/* IFLA_BRIDGE_MST_ENTRY_MSTI */
258			nla_total_size(sizeof(u16)) +
259			/* IFLA_BRIDGE_MST_ENTRY_STATE */
260			nla_total_size(sizeof(u8));
261
262		__set_bit(v->brvlan->msti, seen);
263	}
264
265	return sz;
266}
267
268int br_mst_fill_info(struct sk_buff *skb,
269		     const struct net_bridge_vlan_group *vg)
270{
271	DECLARE_BITMAP(seen, VLAN_N_VID) = { 0 };
272	const struct net_bridge_vlan *v;
273	struct nlattr *nest;
274	int err = 0;
275
276	list_for_each_entry(v, &vg->vlan_list, vlist) {
277		if (test_bit(v->brvlan->msti, seen))
278			continue;
279
280		nest = nla_nest_start_noflag(skb, IFLA_BRIDGE_MST_ENTRY);
281		if (!nest ||
282		    nla_put_u16(skb, IFLA_BRIDGE_MST_ENTRY_MSTI, v->brvlan->msti) ||
283		    nla_put_u8(skb, IFLA_BRIDGE_MST_ENTRY_STATE, v->state)) {
284			err = -EMSGSIZE;
285			break;
286		}
287		nla_nest_end(skb, nest);
288
289		__set_bit(v->brvlan->msti, seen);
290	}
291
292	return err;
293}
294
295static const struct nla_policy br_mst_nl_policy[IFLA_BRIDGE_MST_ENTRY_MAX + 1] = {
296	[IFLA_BRIDGE_MST_ENTRY_MSTI] = NLA_POLICY_RANGE(NLA_U16,
297						   1, /* 0 reserved for CST */
298						   VLAN_N_VID - 1),
299	[IFLA_BRIDGE_MST_ENTRY_STATE] = NLA_POLICY_RANGE(NLA_U8,
300						    BR_STATE_DISABLED,
301						    BR_STATE_BLOCKING),
302};
303
304static int br_mst_process_one(struct net_bridge_port *p,
305			      const struct nlattr *attr,
306			      struct netlink_ext_ack *extack)
307{
308	struct nlattr *tb[IFLA_BRIDGE_MST_ENTRY_MAX + 1];
309	u16 msti;
310	u8 state;
311	int err;
312
313	err = nla_parse_nested(tb, IFLA_BRIDGE_MST_ENTRY_MAX, attr,
314			       br_mst_nl_policy, extack);
315	if (err)
316		return err;
317
318	if (!tb[IFLA_BRIDGE_MST_ENTRY_MSTI]) {
319		NL_SET_ERR_MSG_MOD(extack, "MSTI not specified");
320		return -EINVAL;
321	}
322
323	if (!tb[IFLA_BRIDGE_MST_ENTRY_STATE]) {
324		NL_SET_ERR_MSG_MOD(extack, "State not specified");
325		return -EINVAL;
326	}
327
328	msti = nla_get_u16(tb[IFLA_BRIDGE_MST_ENTRY_MSTI]);
329	state = nla_get_u8(tb[IFLA_BRIDGE_MST_ENTRY_STATE]);
330
331	return br_mst_set_state(p, msti, state, extack);
332}
333
334int br_mst_process(struct net_bridge_port *p, const struct nlattr *mst_attr,
335		   struct netlink_ext_ack *extack)
336{
337	struct nlattr *attr;
338	int err, msts = 0;
339	int rem;
340
341	if (!br_opt_get(p->br, BROPT_MST_ENABLED)) {
342		NL_SET_ERR_MSG_MOD(extack, "Can't modify MST state when MST is disabled");
343		return -EBUSY;
344	}
345
346	nla_for_each_nested(attr, mst_attr, rem) {
347		switch (nla_type(attr)) {
348		case IFLA_BRIDGE_MST_ENTRY:
349			err = br_mst_process_one(p, attr, extack);
350			break;
351		default:
352			continue;
353		}
354
355		msts++;
356		if (err)
357			break;
358	}
359
360	if (!msts) {
361		NL_SET_ERR_MSG_MOD(extack, "Found no MST entries to process");
362		err = -EINVAL;
363	}
364
365	return err;
366}
~/ Read-only file preview.