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