1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright (c) 2020, Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
3 #include <linux/kernel.h>
4 #include <linux/netdevice.h>
5 #include <linux/rtnetlink.h>
6 #include <linux/slab.h>
8 #include "br_private.h"
10 /* check if the options between two vlans are equal */
11 bool br_vlan_opts_eq(const struct net_bridge_vlan
*v1
,
12 const struct net_bridge_vlan
*v2
)
14 return v1
->state
== v2
->state
;
17 bool br_vlan_opts_fill(struct sk_buff
*skb
, const struct net_bridge_vlan
*v
)
19 return !nla_put_u8(skb
, BRIDGE_VLANDB_ENTRY_STATE
,
20 br_vlan_get_state(v
));
23 size_t br_vlan_opts_nl_size(void)
25 return nla_total_size(sizeof(u8
)); /* BRIDGE_VLANDB_ENTRY_STATE */
28 static int br_vlan_modify_state(struct net_bridge_vlan_group
*vg
,
29 struct net_bridge_vlan
*v
,
32 struct netlink_ext_ack
*extack
)
34 struct net_bridge
*br
;
38 if (state
> BR_STATE_BLOCKING
) {
39 NL_SET_ERR_MSG_MOD(extack
, "Invalid vlan state");
43 if (br_vlan_is_brentry(v
))
48 if (br
->stp_enabled
== BR_KERNEL_STP
) {
49 NL_SET_ERR_MSG_MOD(extack
, "Can't modify vlan state when using kernel STP");
53 if (v
->state
== state
)
56 if (v
->vid
== br_get_pvid(vg
))
57 br_vlan_set_pvid_state(vg
, state
);
59 br_vlan_set_state(v
, state
);
65 static int br_vlan_process_one_opts(const struct net_bridge
*br
,
66 const struct net_bridge_port
*p
,
67 struct net_bridge_vlan_group
*vg
,
68 struct net_bridge_vlan
*v
,
71 struct netlink_ext_ack
*extack
)
76 if (tb
[BRIDGE_VLANDB_ENTRY_STATE
]) {
77 u8 state
= nla_get_u8(tb
[BRIDGE_VLANDB_ENTRY_STATE
]);
79 err
= br_vlan_modify_state(vg
, v
, state
, changed
, extack
);
87 int br_vlan_process_options(const struct net_bridge
*br
,
88 const struct net_bridge_port
*p
,
89 struct net_bridge_vlan
*range_start
,
90 struct net_bridge_vlan
*range_end
,
92 struct netlink_ext_ack
*extack
)
94 struct net_bridge_vlan
*v
, *curr_start
= NULL
, *curr_end
= NULL
;
95 struct net_bridge_vlan_group
*vg
;
100 vg
= nbp_vlan_group(p
);
102 vg
= br_vlan_group(br
);
104 if (!range_start
|| !br_vlan_should_use(range_start
)) {
105 NL_SET_ERR_MSG_MOD(extack
, "Vlan range start doesn't exist, can't process options");
108 if (!range_end
|| !br_vlan_should_use(range_end
)) {
109 NL_SET_ERR_MSG_MOD(extack
, "Vlan range end doesn't exist, can't process options");
113 pvid
= br_get_pvid(vg
);
114 for (vid
= range_start
->vid
; vid
<= range_end
->vid
; vid
++) {
115 bool changed
= false;
117 v
= br_vlan_find(vg
, vid
);
118 if (!v
|| !br_vlan_should_use(v
)) {
119 NL_SET_ERR_MSG_MOD(extack
, "Vlan in range doesn't exist, can't process options");
124 err
= br_vlan_process_one_opts(br
, p
, vg
, v
, tb
, &changed
,
130 /* vlan options changed, check for range */
137 if (v
->vid
== pvid
||
138 !br_vlan_can_enter_range(v
, curr_end
)) {
139 br_vlan_notify(br
, p
, curr_start
->vid
,
140 curr_end
->vid
, RTM_NEWVLAN
);
145 /* nothing changed and nothing to notify yet */
149 br_vlan_notify(br
, p
, curr_start
->vid
, curr_end
->vid
,
156 br_vlan_notify(br
, p
, curr_start
->vid
, curr_end
->vid
,