Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/bridge/br_mst.c
49642 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Bridge Multiple Spanning Tree Support
4
*
5
* Authors:
6
* Tobias Waldekranz <[email protected]>
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
368