xref: /linux/net/bridge/br_mst.c (revision c2c2ccfd4ba72718266a56f3ecc34c989cb5b7a0)
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 
14 DEFINE_STATIC_KEY_FALSE(br_mst_used);
15 
br_mst_enabled(const struct net_device * dev)16 bool 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 }
23 EXPORT_SYMBOL_GPL(br_mst_enabled);
24 
br_mst_uninit(struct net_bridge * br)25 void 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 
br_mst_get_info(const struct net_device * dev,u16 msti,unsigned long * vids)31 int 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 }
55 EXPORT_SYMBOL_GPL(br_mst_get_info);
56 
br_mst_get_state(const struct net_device * dev,u16 msti,u8 * state)57 int 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 }
80 EXPORT_SYMBOL_GPL(br_mst_get_state);
81 
br_mst_vlan_set_state(struct net_bridge_vlan_group * vg,struct net_bridge_vlan * v,u8 state)82 static 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 
br_mst_set_state(struct net_bridge_port * p,u16 msti,u8 state,struct netlink_ext_ack * extack)95 int 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 
132 out:
133 	rcu_read_unlock();
134 	return err;
135 }
136 
br_mst_vlan_sync_state(struct net_bridge_vlan * pv,u16 msti)137 static 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 
br_mst_vlan_set_msti(struct net_bridge_vlan * mv,u16 msti)157 int 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 
br_mst_vlan_init_state(struct net_bridge_vlan * v)192 void 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 
br_mst_set_enabled(struct net_bridge * br,bool on,struct netlink_ext_ack * extack)203 int 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 
br_mst_info_size(const struct net_bridge_vlan_group * vg)242 size_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 
br_mst_fill_info(struct sk_buff * skb,const struct net_bridge_vlan_group * vg)268 int 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 
295 static 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 
br_mst_process_one(struct net_bridge_port * p,const struct nlattr * attr,struct netlink_ext_ack * extack)304 static 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 
br_mst_process(struct net_bridge_port * p,const struct nlattr * mst_attr,struct netlink_ext_ack * extack)334 int 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 }
367