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,