net: dsa: sja1105: Avoid error message for unknown PHY mode on disabled ports
[linux-block.git] / net / bridge / br_vlan_options.c
CommitLineData
7a53e718
NA
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>
188c67dd 7#include <net/ip_tunnels.h>
7a53e718
NA
8
9#include "br_private.h"
188c67dd
NA
10#include "br_private_tunnel.h"
11
12static bool __vlan_tun_put(struct sk_buff *skb, const struct net_bridge_vlan *v)
13{
14 __be32 tid = tunnel_id_to_key32(v->tinfo.tunnel_id);
15
16 if (!v->tinfo.tunnel_dst)
17 return true;
18
19 return !nla_put_u32(skb, BRIDGE_VLANDB_ENTRY_TUNNEL_ID,
20 be32_to_cpu(tid));
21}
22
23static bool __vlan_tun_can_enter_range(const struct net_bridge_vlan *v_curr,
24 const struct net_bridge_vlan *range_end)
25{
26 return (!v_curr->tinfo.tunnel_dst && !range_end->tinfo.tunnel_dst) ||
27 vlan_tunid_inrange(v_curr, range_end);
28}
7a53e718 29
99f7c5e0
NA
30/* check if the options' state of v_curr allow it to enter the range */
31bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr,
32 const struct net_bridge_vlan *range_end)
7a53e718 33{
188c67dd
NA
34 return v_curr->state == range_end->state &&
35 __vlan_tun_can_enter_range(v_curr, range_end);
7a53e718
NA
36}
37
38bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v)
39{
a580c76d 40 return !nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE,
188c67dd
NA
41 br_vlan_get_state(v)) &&
42 __vlan_tun_put(skb, v);
7a53e718
NA
43}
44
45size_t br_vlan_opts_nl_size(void)
46{
188c67dd
NA
47 return nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_STATE */
48 + nla_total_size(sizeof(u32)); /* BRIDGE_VLANDB_ENTRY_TUNNEL_ID */
a580c76d
NA
49}
50
51static int br_vlan_modify_state(struct net_bridge_vlan_group *vg,
52 struct net_bridge_vlan *v,
53 u8 state,
54 bool *changed,
55 struct netlink_ext_ack *extack)
56{
57 struct net_bridge *br;
58
59 ASSERT_RTNL();
60
61 if (state > BR_STATE_BLOCKING) {
62 NL_SET_ERR_MSG_MOD(extack, "Invalid vlan state");
63 return -EINVAL;
64 }
65
66 if (br_vlan_is_brentry(v))
67 br = v->br;
68 else
69 br = v->port->br;
70
71 if (br->stp_enabled == BR_KERNEL_STP) {
72 NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state when using kernel STP");
73 return -EBUSY;
74 }
75
76 if (v->state == state)
77 return 0;
78
79 if (v->vid == br_get_pvid(vg))
80 br_vlan_set_pvid_state(vg, state);
81
82 br_vlan_set_state(v, state);
83 *changed = true;
84
7a53e718
NA
85 return 0;
86}
a5d29ae2 87
569da082
NA
88static int br_vlan_modify_tunnel(const struct net_bridge_port *p,
89 struct net_bridge_vlan *v,
90 struct nlattr **tb,
91 bool *changed,
92 struct netlink_ext_ack *extack)
93{
94 struct bridge_vlan_info *vinfo;
95 int cmdmap;
96 u32 tun_id;
97
98 if (!p) {
99 NL_SET_ERR_MSG_MOD(extack, "Can't modify tunnel mapping of non-port vlans");
100 return -EINVAL;
101 }
102 if (!(p->flags & BR_VLAN_TUNNEL)) {
103 NL_SET_ERR_MSG_MOD(extack, "Port doesn't have tunnel flag set");
104 return -EINVAL;
105 }
106
107 /* vlan info attribute is guaranteed by br_vlan_rtm_process_one */
108 vinfo = nla_data(tb[BRIDGE_VLANDB_ENTRY_INFO]);
109 cmdmap = vinfo->flags & BRIDGE_VLAN_INFO_REMOVE_TUN ? RTM_DELLINK :
110 RTM_SETLINK;
111 /* when working on vlan ranges this represents the starting tunnel id */
112 tun_id = nla_get_u32(tb[BRIDGE_VLANDB_ENTRY_TUNNEL_ID]);
113 /* tunnel ids are mapped to each vlan in increasing order,
114 * the starting vlan is in BRIDGE_VLANDB_ENTRY_INFO and v is the
115 * current vlan, so we compute: tun_id + v - vinfo->vid
116 */
117 tun_id += v->vid - vinfo->vid;
118
119 return br_vlan_tunnel_info(p, cmdmap, v->vid, tun_id, changed);
120}
121
a5d29ae2
NA
122static int br_vlan_process_one_opts(const struct net_bridge *br,
123 const struct net_bridge_port *p,
124 struct net_bridge_vlan_group *vg,
125 struct net_bridge_vlan *v,
126 struct nlattr **tb,
127 bool *changed,
128 struct netlink_ext_ack *extack)
129{
a580c76d
NA
130 int err;
131
a5d29ae2 132 *changed = false;
a580c76d
NA
133 if (tb[BRIDGE_VLANDB_ENTRY_STATE]) {
134 u8 state = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_STATE]);
135
136 err = br_vlan_modify_state(vg, v, state, changed, extack);
137 if (err)
138 return err;
139 }
569da082
NA
140 if (tb[BRIDGE_VLANDB_ENTRY_TUNNEL_ID]) {
141 err = br_vlan_modify_tunnel(p, v, tb, changed, extack);
142 if (err)
143 return err;
144 }
a580c76d 145
a5d29ae2
NA
146 return 0;
147}
148
149int br_vlan_process_options(const struct net_bridge *br,
150 const struct net_bridge_port *p,
151 struct net_bridge_vlan *range_start,
152 struct net_bridge_vlan *range_end,
153 struct nlattr **tb,
154 struct netlink_ext_ack *extack)
155{
156 struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL;
157 struct net_bridge_vlan_group *vg;
158 int vid, err = 0;
159 u16 pvid;
160
161 if (p)
162 vg = nbp_vlan_group(p);
163 else
164 vg = br_vlan_group(br);
165
166 if (!range_start || !br_vlan_should_use(range_start)) {
167 NL_SET_ERR_MSG_MOD(extack, "Vlan range start doesn't exist, can't process options");
168 return -ENOENT;
169 }
170 if (!range_end || !br_vlan_should_use(range_end)) {
171 NL_SET_ERR_MSG_MOD(extack, "Vlan range end doesn't exist, can't process options");
172 return -ENOENT;
173 }
174
175 pvid = br_get_pvid(vg);
176 for (vid = range_start->vid; vid <= range_end->vid; vid++) {
177 bool changed = false;
178
179 v = br_vlan_find(vg, vid);
180 if (!v || !br_vlan_should_use(v)) {
181 NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process options");
182 err = -ENOENT;
183 break;
184 }
185
186 err = br_vlan_process_one_opts(br, p, vg, v, tb, &changed,
187 extack);
188 if (err)
189 break;
190
191 if (changed) {
192 /* vlan options changed, check for range */
193 if (!curr_start) {
194 curr_start = v;
195 curr_end = v;
196 continue;
197 }
198
199 if (v->vid == pvid ||
200 !br_vlan_can_enter_range(v, curr_end)) {
201 br_vlan_notify(br, p, curr_start->vid,
202 curr_end->vid, RTM_NEWVLAN);
203 curr_start = v;
204 }
205 curr_end = v;
206 } else {
207 /* nothing changed and nothing to notify yet */
208 if (!curr_start)
209 continue;
210
211 br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
212 RTM_NEWVLAN);
213 curr_start = NULL;
214 curr_end = NULL;
215 }
216 }
217 if (curr_start)
218 br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
219 RTM_NEWVLAN);
220
221 return err;
222}