1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * Copyright (c) 2018 Volodymyr Bendiuga <volodymyr.bendiuga@gmail.com>
4 */
5
6 /**
7 * @ingroup act
8 * @defgroup act_vlan VLAN Manipulation
9 *
10 * @{
11 */
12
13 #include <netlink-private/netlink.h>
14 #include <netlink-private/tc.h>
15 #include <netlink/netlink.h>
16 #include <netlink/attr.h>
17 #include <netlink/utils.h>
18 #include <netlink-private/route/tc-api.h>
19 #include <netlink/route/act/vlan.h>
20
21
22 #define VLAN_F_VID (1 << 0)
23 #define VLAN_F_PROTO (1 << 1)
24 #define VLAN_F_PRIO (1 << 2)
25 #define VLAN_F_ACT (1 << 3)
26 #define VLAN_F_MODE (1 << 4)
27
28 static struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
29 [TCA_VLAN_PARMS] = { .minlen = sizeof(struct tc_vlan) },
30 [TCA_VLAN_PUSH_VLAN_ID] = { .type = NLA_U16 },
31 [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NLA_U16 },
32 [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NLA_U8 },
33 };
34
vlan_msg_parser(struct rtnl_tc * tc,void * data)35 static int vlan_msg_parser(struct rtnl_tc *tc, void *data)
36 {
37 struct rtnl_vlan *v = data;
38 struct nlattr *tb[TCA_VLAN_MAX + 1];
39 int err;
40
41 err = tca_parse(tb, TCA_VLAN_MAX, tc, vlan_policy);
42 if (err < 0)
43 return err;
44
45 v->v_flags = 0;
46 if (!tb[TCA_VLAN_PARMS])
47 return -NLE_MISSING_ATTR;
48 else {
49 nla_memcpy(&v->v_parm, tb[TCA_VLAN_PARMS], sizeof(v->v_parm));
50 v->v_flags |= VLAN_F_ACT;
51 v->v_flags |= VLAN_F_MODE;
52 }
53
54 if (tb[TCA_VLAN_PUSH_VLAN_ID]) {
55 v->v_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
56 v->v_flags |= VLAN_F_VID;
57 }
58
59 if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) {
60 v->v_proto = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]);
61 v->v_flags |= VLAN_F_PROTO;
62 }
63
64 if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY]) {
65 v->v_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]);
66 v->v_flags |= VLAN_F_PRIO;
67 }
68
69 return 0;
70 }
71
vlan_msg_fill(struct rtnl_tc * tc,void * data,struct nl_msg * msg)72 static int vlan_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
73 {
74 struct rtnl_vlan *v = data;
75
76 if (!v)
77 return 0;
78 if (!(v->v_flags & VLAN_F_MODE))
79 return -NLE_MISSING_ATTR;
80
81 NLA_PUT(msg, TCA_VLAN_PARMS, sizeof(v->v_parm), &v->v_parm);
82
83 /* vid is required for PUSH & MODIFY modes */
84 if ((v->v_parm.v_action != TCA_VLAN_ACT_POP) && !(v->v_flags & VLAN_F_VID))
85 return -NLE_MISSING_ATTR;
86
87 if (v->v_flags & VLAN_F_VID)
88 NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_ID, v->v_vid);
89
90 if (v->v_flags & VLAN_F_PROTO)
91 NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_PROTOCOL, v->v_proto);
92
93 if (v->v_flags & VLAN_F_PRIO)
94 NLA_PUT_U8(msg, TCA_VLAN_PUSH_VLAN_PRIORITY, v->v_prio);
95
96 return 0;
97
98 nla_put_failure:
99 return -NLE_NOMEM;
100 }
101
vlan_free_data(struct rtnl_tc * tc,void * data)102 static void vlan_free_data(struct rtnl_tc *tc, void *data)
103 {
104 }
105
vlan_dump_line(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)106 static void vlan_dump_line(struct rtnl_tc *tc, void *data,
107 struct nl_dump_params *p)
108 {
109 struct rtnl_vlan *v = data;
110
111 if (!v)
112 return;
113
114 if (!(v->v_flags & VLAN_F_ACT))
115 return;
116
117 if (TC_ACT_EXT_CMP(v->v_parm.action, TC_ACT_GOTO_CHAIN))
118 nl_dump(p, " goto chain %u", v->v_parm.action & TC_ACT_EXT_VAL_MASK);
119
120 if (TC_ACT_EXT_CMP(v->v_parm.action, TC_ACT_JUMP))
121 nl_dump(p, " jump %u", v->v_parm.action & TC_ACT_EXT_VAL_MASK);
122
123 switch(v->v_parm.action){
124 case TC_ACT_UNSPEC:
125 nl_dump(p, " unspecified");
126 break;
127 case TC_ACT_PIPE:
128 nl_dump(p, " pipe");
129 break;
130 case TC_ACT_STOLEN:
131 nl_dump(p, " stolen");
132 break;
133 case TC_ACT_SHOT:
134 nl_dump(p, " shot");
135 break;
136 case TC_ACT_QUEUED:
137 nl_dump(p, " queued");
138 break;
139 case TC_ACT_REPEAT:
140 nl_dump(p, " repeat");
141 break;
142 }
143 }
144
vlan_dump_details(struct rtnl_tc * tc,void * data,struct nl_dump_params * p)145 static void vlan_dump_details(struct rtnl_tc *tc, void *data,
146 struct nl_dump_params *p)
147 {
148 struct rtnl_vlan *v = data;
149
150 if (!v)
151 return;
152
153 if (v->v_flags & VLAN_F_MODE) {
154 switch (v->v_parm.v_action) {
155 case TCA_VLAN_ACT_POP:
156 nl_dump(p, " mode POP");
157 break;
158 case TCA_VLAN_ACT_PUSH:
159 nl_dump(p, " mode PUSH");
160 break;
161 case TCA_VLAN_ACT_MODIFY:
162 nl_dump(p, " mode MODIFY");
163 break;
164 }
165 }
166
167 if (v->v_flags & VLAN_F_VID)
168 nl_dump(p, " vlan id %u", v->v_vid);
169
170 if (v->v_flags & VLAN_F_PRIO)
171 nl_dump(p, " priority %u", v->v_prio);
172
173 if (v->v_flags & VLAN_F_PROTO)
174 nl_dump(p, " protocol %u", v->v_proto);
175 }
176
177 /**
178 * @name Attribute Modifications
179 * @{
180 */
181
182 /**
183 * Set vlan mode
184 * @arg act vlan action
185 * @arg mode one of (TCA_VLAN_ACT_*: POP, PUSH, MODIFY)
186 * @return 0 on success or a negative error code.
187 */
rtnl_vlan_set_mode(struct rtnl_act * act,int mode)188 int rtnl_vlan_set_mode(struct rtnl_act *act, int mode)
189 {
190 struct rtnl_vlan *v;
191
192 if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
193 return -NLE_NOMEM;
194
195 if (mode > TCA_VLAN_ACT_MODIFY)
196 return -NLE_RANGE;
197
198 v->v_parm.v_action = mode;
199 v->v_flags |= VLAN_F_MODE;
200
201 return 0;
202 }
203
204 /**
205 * Get vlan mode
206 * @arg act vlan action
207 * @arg out_mode vlan mode output paramter
208 * @return 0 on success if the vlan mode was returned or a negative error code.
209 */
rtnl_vlan_get_mode(struct rtnl_act * act,int * out_mode)210 int rtnl_vlan_get_mode(struct rtnl_act *act, int *out_mode)
211 {
212 struct rtnl_vlan *v;
213
214 if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
215 return -NLE_INVAL;
216
217 if (!(v->v_flags & VLAN_F_MODE))
218 return -NLE_MISSING_ATTR;
219
220 *out_mode = v->v_parm.v_action;
221 return 0;
222 }
223
224 /**
225 * Set general action
226 * @arg act vlan action
227 * @arg action one of (TCA_ACT_*: PIPE, SHOT, GOTO_CHAIN, etc)
228 * @return 0 on success or a negative error code.
229 */
rtnl_vlan_set_action(struct rtnl_act * act,int action)230 int rtnl_vlan_set_action(struct rtnl_act *act, int action)
231 {
232 struct rtnl_vlan *v;
233
234 if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
235 return -NLE_NOMEM;
236
237 v->v_parm.action = action;
238 v->v_flags |= VLAN_F_ACT;
239
240 return 0;
241 }
242
243 /**
244 * Get general action
245 * @arg act vlan action
246 * @arg out_action output parameter
247 * @return general 0 if out_action was set or a negative error code.
248 */
rtnl_vlan_get_action(struct rtnl_act * act,int * out_action)249 int rtnl_vlan_get_action(struct rtnl_act *act, int *out_action)
250 {
251 struct rtnl_vlan *v;
252
253 if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
254 return -NLE_INVAL;
255
256 if (!(v->v_flags & VLAN_F_ACT))
257 return -NLE_MISSING_ATTR;
258
259 *out_action = v->v_parm.action;
260 return 0;
261 }
262
263 /**
264 * Set protocol
265 * @arg act vlan action
266 * @arg protocol one of (ETH_P_8021Q || ETH_P_8021AD)
267 * @return 0 on success or a negative error code.
268 */
rtnl_vlan_set_protocol(struct rtnl_act * act,uint16_t protocol)269 int rtnl_vlan_set_protocol(struct rtnl_act *act, uint16_t protocol)
270 {
271 struct rtnl_vlan *v;
272
273 if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
274 return -NLE_NOMEM;
275
276 v->v_proto = protocol;
277 v->v_flags |= VLAN_F_PROTO;
278
279 return 0;
280 }
281
282 /**
283 * Get protocol
284 * @arg act vlan action
285 * @arg out_protocol protocol output argument
286 * @return 0 if the protocol was returned or a negative error code.
287 */
rtnl_vlan_get_protocol(struct rtnl_act * act,uint16_t * out_protocol)288 int rtnl_vlan_get_protocol(struct rtnl_act *act, uint16_t *out_protocol)
289 {
290 struct rtnl_vlan *v;
291
292 if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
293 return -NLE_INVAL;
294
295 if (!(v->v_flags & VLAN_F_PROTO))
296 return -NLE_MISSING_ATTR;
297
298 *out_protocol = v->v_proto;
299 return 0;
300 }
301
302 /**
303 * Set vlan id
304 * @arg act vlan action
305 * @arg vid vlan id
306 * @return 0 on success or a negative error code.
307 */
rtnl_vlan_set_vlan_id(struct rtnl_act * act,uint16_t vid)308 int rtnl_vlan_set_vlan_id(struct rtnl_act *act, uint16_t vid)
309 {
310 struct rtnl_vlan *v;
311
312 if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
313 return -NLE_NOMEM;
314
315 if (vid > 4095)
316 return -NLE_RANGE;
317
318 v->v_vid = vid;
319 v->v_flags |= VLAN_F_VID;
320
321 return 0;
322 }
323
324 /**
325 * Get vlan id
326 * @arg act vlan action
327 * @arg out_vid output vlan id
328 * @return 0 if the vlan id was returned or a negative error code.
329 */
rtnl_vlan_get_vlan_id(struct rtnl_act * act,uint16_t * out_vid)330 int rtnl_vlan_get_vlan_id(struct rtnl_act *act, uint16_t *out_vid)
331 {
332 struct rtnl_vlan *v;
333
334 if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
335 return -NLE_INVAL;
336
337 if (!(v->v_flags & VLAN_F_VID))
338 return -NLE_MISSING_ATTR;
339
340 *out_vid = v->v_vid;
341 return 0;
342 }
343
344 /**
345 * Set vlan prio
346 * @arg act vlan action
347 * @arg prio vlan priority (0 - 7)
348 * @return 0 on success or a negative error code.
349 */
rtnl_vlan_set_vlan_prio(struct rtnl_act * act,uint8_t prio)350 int rtnl_vlan_set_vlan_prio(struct rtnl_act *act, uint8_t prio)
351 {
352 struct rtnl_vlan *v;
353
354 if (!(v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act))))
355 return -NLE_NOMEM;
356
357 if (prio > 7)
358 return -NLE_RANGE;
359
360 v->v_prio = prio;
361 v->v_flags |= VLAN_F_PRIO;
362
363 return 0;
364 }
365
366 /**
367 * Get vlan prio
368 * @arg act vlan action
369 * @arg out_prio the output vlan prio
370 * @return 0 if the vlan prio was returned or a negative error code.
371 */
rtnl_vlan_get_vlan_prio(struct rtnl_act * act,uint8_t * out_prio)372 int rtnl_vlan_get_vlan_prio(struct rtnl_act *act, uint8_t *out_prio)
373 {
374 struct rtnl_vlan *v;
375
376 if (!(v = (struct rtnl_vlan *) rtnl_tc_data_peek(TC_CAST(act))))
377 return -NLE_INVAL;
378
379 if (!(v->v_flags & VLAN_F_PRIO))
380 return -NLE_MISSING_ATTR;
381
382 *out_prio = v->v_prio;
383 return 0;
384 }
385
386 /** @} */
387
388 static struct rtnl_tc_ops vlan_ops = {
389 .to_kind = "vlan",
390 .to_type = RTNL_TC_TYPE_ACT,
391 .to_size = sizeof(struct rtnl_vlan),
392 .to_msg_parser = vlan_msg_parser,
393 .to_free_data = vlan_free_data,
394 .to_clone = NULL,
395 .to_msg_fill = vlan_msg_fill,
396 .to_dump = {
397 [NL_DUMP_LINE] = vlan_dump_line,
398 [NL_DUMP_DETAILS] = vlan_dump_details,
399 },
400 };
401
vlan_init(void)402 static void __init vlan_init(void)
403 {
404 rtnl_tc_register(&vlan_ops);
405 }
406
vlan_exit(void)407 static void __exit vlan_exit(void)
408 {
409 rtnl_tc_unregister(&vlan_ops);
410 }
411
412 /** @} */
413