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