1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
4 */
5
6 /**
7 * @ingroup link
8 * @defgroup vlan VLAN
9 * Virtual LAN link module
10 *
11 * @details
12 * \b Link Type Name: "vlan"
13 *
14 * @route_doc{link_vlan, VLAN Documentation}
15 *
16 * @{
17 */
18
19 #include <netlink-private/netlink.h>
20 #include <netlink/netlink.h>
21 #include <netlink/attr.h>
22 #include <netlink/utils.h>
23 #include <netlink/object.h>
24 #include <netlink/route/rtnl.h>
25 #include <netlink-private/route/link/api.h>
26 #include <netlink/route/link/vlan.h>
27
28 #include <linux/if_vlan.h>
29
30 /** @cond SKIP */
31 #define VLAN_HAS_ID (1<<0)
32 #define VLAN_HAS_FLAGS (1<<1)
33 #define VLAN_HAS_INGRESS_QOS (1<<2)
34 #define VLAN_HAS_EGRESS_QOS (1<<3)
35 #define VLAN_HAS_PROTOCOL (1<<4)
36
37 struct vlan_info
38 {
39 uint16_t vi_vlan_id;
40 uint16_t vi_protocol;
41 unsigned int vi_ingress_qos_mask:(VLAN_PRIO_MAX+1);
42 uint32_t vi_flags;
43 uint32_t vi_flags_mask;
44 uint32_t vi_ingress_qos[VLAN_PRIO_MAX+1];
45 uint32_t vi_negress;
46 uint32_t vi_egress_size;
47 struct vlan_map * vi_egress_qos;
48 uint32_t vi_mask;
49 };
50
51 /** @endcond */
52
53 static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = {
54 [IFLA_VLAN_ID] = { .type = NLA_U16 },
55 [IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) },
56 [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
57 [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED },
58 [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 },
59 };
60
vlan_alloc(struct rtnl_link * link)61 static int vlan_alloc(struct rtnl_link *link)
62 {
63 struct vlan_info *vi;
64
65 if (link->l_info) {
66 vi = link->l_info;
67 free(vi->vi_egress_qos);
68 memset(link->l_info, 0, sizeof(*vi));
69 } else {
70 if ((vi = calloc(1, sizeof(*vi))) == NULL)
71 return -NLE_NOMEM;
72
73 link->l_info = vi;
74 }
75
76 return 0;
77 }
78
vlan_parse(struct rtnl_link * link,struct nlattr * data,struct nlattr * xstats)79 static int vlan_parse(struct rtnl_link *link, struct nlattr *data,
80 struct nlattr *xstats)
81 {
82 struct nlattr *tb[IFLA_VLAN_MAX+1];
83 struct vlan_info *vi;
84 int err;
85
86 NL_DBG(3, "Parsing VLAN link info\n");
87
88 if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0)
89 goto errout;
90
91 if ((err = vlan_alloc(link)) < 0)
92 goto errout;
93
94 vi = link->l_info;
95
96 if (tb[IFLA_VLAN_ID]) {
97 vi->vi_vlan_id = nla_get_u16(tb[IFLA_VLAN_ID]);
98 vi->vi_mask |= VLAN_HAS_ID;
99 }
100
101 if (tb[IFLA_VLAN_PROTOCOL]) {
102 vi->vi_protocol = nla_get_u16(tb[IFLA_VLAN_PROTOCOL]);
103 vi->vi_mask |= VLAN_HAS_PROTOCOL;
104 }
105
106 if (tb[IFLA_VLAN_FLAGS]) {
107 struct ifla_vlan_flags flags;
108 nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags));
109
110 vi->vi_flags = flags.flags;
111 vi->vi_mask |= VLAN_HAS_FLAGS;
112 }
113
114 if (tb[IFLA_VLAN_INGRESS_QOS]) {
115 struct ifla_vlan_qos_mapping *map;
116 struct nlattr *nla;
117 int remaining;
118
119 vi->vi_ingress_qos_mask = 0;
120 memset(vi->vi_ingress_qos, 0, sizeof(vi->vi_ingress_qos));
121
122 nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) {
123 if (nla_len(nla) < sizeof(*map))
124 return -NLE_INVAL;
125
126 map = nla_data(nla);
127 if (map->from > VLAN_PRIO_MAX) {
128 return -NLE_INVAL;
129 }
130
131 /* Kernel will not explicitly serialize mappings with "to" zero
132 * (although they are implicitly set).
133 *
134 * Thus we only mark those as "set" which are explicitly sent.
135 * That is similar to what we do with the egress map and it preserves
136 * previous behavior before NL_CAPABILITY_RTNL_LINK_VLAN_INGRESS_MAP_CLEAR.
137 *
138 * It matters only when a received object is send back to kernel to modify
139 * the link.
140 */
141 vi->vi_ingress_qos_mask |= (1 << map->from);
142 vi->vi_ingress_qos[map->from] = map->to;
143 }
144
145 vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
146 }
147
148 if (tb[IFLA_VLAN_EGRESS_QOS]) {
149 struct ifla_vlan_qos_mapping *map;
150 struct nlattr *nla;
151 int remaining, i = 0;
152
153 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
154 if (nla_len(nla) < sizeof(*map))
155 return -NLE_INVAL;
156 i++;
157 }
158
159 /* align to have a little reserve */
160 vi->vi_egress_size = (i + 32) & ~31;
161 vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*vi->vi_egress_qos));
162 if (vi->vi_egress_qos == NULL)
163 return -NLE_NOMEM;
164
165 i = 0;
166 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
167 map = nla_data(nla);
168 NL_DBG(4, "Assigning egress qos mapping %d\n", i);
169 vi->vi_egress_qos[i].vm_from = map->from;
170 vi->vi_egress_qos[i++].vm_to = map->to;
171 }
172
173 vi->vi_negress = i;
174 vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
175 }
176
177 err = 0;
178 errout:
179 return err;
180 }
181
vlan_free(struct rtnl_link * link)182 static void vlan_free(struct rtnl_link *link)
183 {
184 struct vlan_info *vi = link->l_info;
185
186 if (vi) {
187 free(vi->vi_egress_qos);
188 vi->vi_egress_qos = NULL;
189 }
190
191 free(vi);
192 link->l_info = NULL;
193 }
194
vlan_dump_line(struct rtnl_link * link,struct nl_dump_params * p)195 static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
196 {
197 struct vlan_info *vi = link->l_info;
198
199 nl_dump(p, "vlan-id %d", vi->vi_vlan_id);
200 }
201
vlan_dump_details(struct rtnl_link * link,struct nl_dump_params * p)202 static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
203 {
204 struct vlan_info *vi = link->l_info;
205 int printed;
206 uint32_t i;
207 char buf[64];
208
209 rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
210 nl_dump_line(p, " vlan-info id %d <%s>", vi->vi_vlan_id, buf);
211
212 if (vi->vi_mask & VLAN_HAS_PROTOCOL)
213 nl_dump_line(p, " vlan protocol <%d>", ntohs(vi->vi_protocol));
214
215 nl_dump(p, "\n");
216
217 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
218 nl_dump_line(p,
219 " ingress vlan prio -> qos/socket prio mapping:\n");
220 for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) {
221 if (vi->vi_ingress_qos_mask & (1 << i)) {
222 if (printed == 0)
223 nl_dump_line(p, " ");
224 nl_dump(p, "%x -> %#08x, ",
225 i, vi->vi_ingress_qos[i]);
226 if (printed++ == 3) {
227 nl_dump(p, "\n");
228 printed = 0;
229 }
230 }
231 }
232
233 if (printed > 0 && printed != 4)
234 nl_dump(p, "\n");
235 }
236
237 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
238 nl_dump_line(p,
239 " egress qos/socket prio -> vlan prio mapping:\n");
240 for (i = 0, printed = 0; i < vi->vi_negress; i++) {
241 if (printed == 0)
242 nl_dump_line(p, " ");
243 nl_dump(p, "%#08x -> %x, ",
244 vi->vi_egress_qos[i].vm_from,
245 vi->vi_egress_qos[i].vm_to);
246 if (printed++ == 3) {
247 nl_dump(p, "\n");
248 printed = 0;
249 }
250 }
251
252 if (printed > 0 && printed != 4)
253 nl_dump(p, "\n");
254 }
255 }
256
vlan_clone(struct rtnl_link * dst,struct rtnl_link * src)257 static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
258 {
259 struct vlan_info *vdst, *vsrc = src->l_info;
260 int err;
261 struct vlan_map *p = NULL;
262
263 dst->l_info = NULL;
264 if ((err = rtnl_link_set_type(dst, "vlan")) < 0)
265 return err;
266 vdst = dst->l_info;
267
268 if (vsrc->vi_negress) {
269 p = calloc(vsrc->vi_negress,
270 sizeof(struct vlan_map));
271 if (!p)
272 return -NLE_NOMEM;
273 }
274
275 *vdst = *vsrc;
276
277 if (vsrc->vi_negress) {
278 vdst->vi_egress_size = vsrc->vi_negress;
279 vdst->vi_egress_qos = p;
280 memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
281 vsrc->vi_negress * sizeof(struct vlan_map));
282 }
283
284 return 0;
285 }
286
vlan_put_attrs(struct nl_msg * msg,struct rtnl_link * link)287 static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
288 {
289 struct vlan_info *vi = link->l_info;
290 struct nlattr *data;
291
292 if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
293 return -NLE_MSGSIZE;
294
295 if (vi->vi_mask & VLAN_HAS_ID)
296 NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
297
298 if (vi->vi_mask & VLAN_HAS_PROTOCOL)
299 NLA_PUT_U16(msg, IFLA_VLAN_PROTOCOL, vi->vi_protocol);
300
301 if (vi->vi_mask & VLAN_HAS_FLAGS) {
302 struct ifla_vlan_flags flags = {
303 .flags = vi->vi_flags,
304 .mask = vi->vi_flags_mask,
305 };
306
307 NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
308 }
309
310 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
311 struct ifla_vlan_qos_mapping map;
312 struct nlattr *qos;
313 int i;
314
315 if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
316 goto nla_put_failure;
317
318 for (i = 0; i <= VLAN_PRIO_MAX; i++) {
319 if (vi->vi_ingress_qos_mask & (1 << i)) {
320 map.from = i;
321 map.to = vi->vi_ingress_qos[i];
322
323 NLA_PUT(msg, i, sizeof(map), &map);
324 }
325 }
326
327 nla_nest_end(msg, qos);
328 }
329
330 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
331 struct ifla_vlan_qos_mapping map;
332 struct nlattr *qos;
333 uint32_t i;
334
335 if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
336 goto nla_put_failure;
337
338 for (i = 0; i < vi->vi_negress; i++) {
339 map.from = vi->vi_egress_qos[i].vm_from;
340 map.to = vi->vi_egress_qos[i].vm_to;
341
342 NLA_PUT(msg, i, sizeof(map), &map);
343 }
344
345 nla_nest_end(msg, qos);
346 }
347
348 nla_nest_end(msg, data);
349
350 nla_put_failure:
351
352 return 0;
353 }
354
355 static struct rtnl_link_info_ops vlan_info_ops = {
356 .io_name = "vlan",
357 .io_alloc = vlan_alloc,
358 .io_parse = vlan_parse,
359 .io_dump = {
360 [NL_DUMP_LINE] = vlan_dump_line,
361 [NL_DUMP_DETAILS] = vlan_dump_details,
362 },
363 .io_clone = vlan_clone,
364 .io_put_attrs = vlan_put_attrs,
365 .io_free = vlan_free,
366 };
367
368 /** @cond SKIP */
369 #define IS_VLAN_LINK_ASSERT(link) \
370 if ((link)->l_info_ops != &vlan_info_ops) { \
371 APPBUG("Link is not a vlan link. set type \"vlan\" first."); \
372 return -NLE_OPNOTSUPP; \
373 }
374 /** @endcond */
375
376 /**
377 * @name VLAN Object
378 * @{
379 */
380
381 /**
382 * Allocate link object of type VLAN
383 *
384 * @return Allocated link object or NULL.
385 */
rtnl_link_vlan_alloc(void)386 struct rtnl_link *rtnl_link_vlan_alloc(void)
387 {
388 struct rtnl_link *link;
389
390 if (!(link = rtnl_link_alloc()))
391 return NULL;
392
393 if (rtnl_link_set_type(link, "vlan") < 0) {
394 rtnl_link_put(link);
395 return NULL;
396 }
397
398 return link;
399 }
400
401 /**
402 * Check if link is a VLAN link
403 * @arg link Link object
404 *
405 * @return True if link is a VLAN link, otherwise false is returned.
406 */
rtnl_link_is_vlan(struct rtnl_link * link)407 int rtnl_link_is_vlan(struct rtnl_link *link)
408 {
409 return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vlan");
410 }
411
412 /**
413 * Set VLAN ID
414 * @arg link Link object
415 * @arg id VLAN identifier
416 *
417 * @return 0 on success or a negative error code
418 */
rtnl_link_vlan_set_id(struct rtnl_link * link,uint16_t id)419 int rtnl_link_vlan_set_id(struct rtnl_link *link, uint16_t id)
420 {
421 struct vlan_info *vi = link->l_info;
422
423 IS_VLAN_LINK_ASSERT(link);
424
425 vi->vi_vlan_id = id;
426 vi->vi_mask |= VLAN_HAS_ID;
427
428 return 0;
429 }
430
431 /**
432 * Get VLAN Id
433 * @arg link Link object
434 *
435 * @return VLAN id, 0 if not set or a negative error code.
436 */
rtnl_link_vlan_get_id(struct rtnl_link * link)437 int rtnl_link_vlan_get_id(struct rtnl_link *link)
438 {
439 struct vlan_info *vi = link->l_info;
440
441 IS_VLAN_LINK_ASSERT(link);
442
443 if (vi->vi_mask & VLAN_HAS_ID)
444 return vi->vi_vlan_id;
445 else
446 return 0;
447 }
448
449 /**
450 * Set VLAN protocol
451 * @arg link Link object
452 * @arg protocol VLAN protocol in network byte order.
453 * Probably you want to set it to something like htons(ETH_P_8021Q).
454 *
455 * @return 0 on success or a negative error code
456 */
rtnl_link_vlan_set_protocol(struct rtnl_link * link,uint16_t protocol)457 int rtnl_link_vlan_set_protocol(struct rtnl_link *link, uint16_t protocol)
458 {
459 struct vlan_info *vi = link->l_info;
460
461 IS_VLAN_LINK_ASSERT(link);
462
463 vi->vi_protocol = protocol;
464 vi->vi_mask |= VLAN_HAS_PROTOCOL;
465
466 return 0;
467 }
468
469 /**
470 * Get VLAN protocol
471 * @arg link Link object
472 *
473 * @return VLAN protocol in network byte order like htons(ETH_P_8021Q),
474 * 0 if not set or a negative error code.
475 */
rtnl_link_vlan_get_protocol(struct rtnl_link * link)476 int rtnl_link_vlan_get_protocol(struct rtnl_link *link)
477 {
478 struct vlan_info *vi = link->l_info;
479
480 IS_VLAN_LINK_ASSERT(link);
481
482 if (vi->vi_mask & VLAN_HAS_PROTOCOL)
483 return vi->vi_protocol;
484 else
485 return 0;
486 }
487
488 /**
489 * Set VLAN flags
490 * @arg link Link object
491 * @arg flags VLAN flags
492 *
493 * @return 0 on success or a negative error code.
494 */
rtnl_link_vlan_set_flags(struct rtnl_link * link,unsigned int flags)495 int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
496 {
497 struct vlan_info *vi = link->l_info;
498
499 IS_VLAN_LINK_ASSERT(link);
500
501 vi->vi_flags_mask |= flags;
502 vi->vi_flags |= flags;
503 vi->vi_mask |= VLAN_HAS_FLAGS;
504
505 return 0;
506 }
507
508 /**
509 * Unset VLAN flags
510 * @arg link Link object
511 * @arg flags VLAN flags
512 *
513 * @return 0 on success or a negative error code.
514 */
rtnl_link_vlan_unset_flags(struct rtnl_link * link,unsigned int flags)515 int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
516 {
517 struct vlan_info *vi = link->l_info;
518
519 IS_VLAN_LINK_ASSERT(link);
520
521 vi->vi_flags_mask |= flags;
522 vi->vi_flags &= ~flags;
523 vi->vi_mask |= VLAN_HAS_FLAGS;
524
525 return 0;
526 }
527
528 /**
529 * Get VLAN flags
530 * @arg link Link object
531 *
532 * @return VLAN flags, 0 if none set, or a negative error code.
533 */
rtnl_link_vlan_get_flags(struct rtnl_link * link)534 int rtnl_link_vlan_get_flags(struct rtnl_link *link)
535 {
536 struct vlan_info *vi = link->l_info;
537
538 IS_VLAN_LINK_ASSERT(link);
539
540 return vi->vi_flags;
541 }
542
543 /** @} */
544
545 /**
546 * @name Quality of Service
547 * @{
548 */
549
rtnl_link_vlan_set_ingress_map(struct rtnl_link * link,int from,uint32_t to)550 int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
551 uint32_t to)
552 {
553 struct vlan_info *vi = link->l_info;
554
555 IS_VLAN_LINK_ASSERT(link);
556
557 if (from < 0 || from > VLAN_PRIO_MAX)
558 return -NLE_INVAL;
559
560 vi->vi_ingress_qos_mask |= (1 << from);
561 vi->vi_ingress_qos[from] = to;
562 vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
563
564 return 0;
565 }
566
rtnl_link_vlan_get_ingress_map(struct rtnl_link * link)567 uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link)
568 {
569 struct vlan_info *vi = link->l_info;
570
571 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
572 return NULL;
573
574 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
575 return vi->vi_ingress_qos;
576 else
577 return NULL;
578 }
579
rtnl_link_vlan_set_egress_map(struct rtnl_link * link,uint32_t from,int to)580 int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to)
581 {
582 struct vlan_info *vi = link->l_info;
583
584 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
585 return -NLE_OPNOTSUPP;
586
587 if (to < 0 || to > VLAN_PRIO_MAX)
588 return -NLE_INVAL;
589
590 if (vi->vi_negress >= vi->vi_egress_size) {
591 uint32_t new_size = vi->vi_egress_size + 1 + vi->vi_egress_size / 2;
592 size_t bytes;
593 void *ptr;
594
595 if (new_size < vi->vi_egress_size)
596 return -NLE_NOMEM;
597 bytes = (size_t) new_size * sizeof(struct vlan_map);
598 if (bytes / sizeof (struct vlan_map) != new_size)
599 return -NLE_NOMEM;
600 ptr = realloc(vi->vi_egress_qos, bytes);
601 if (!ptr)
602 return -NLE_NOMEM;
603
604 vi->vi_egress_qos = ptr;
605 vi->vi_egress_size = new_size;
606 }
607
608 vi->vi_egress_qos[vi->vi_negress].vm_from = from;
609 vi->vi_egress_qos[vi->vi_negress].vm_to = to;
610 vi->vi_negress++;
611 vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
612
613 return 0;
614 }
615
rtnl_link_vlan_get_egress_map(struct rtnl_link * link,int * negress)616 struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
617 int *negress)
618 {
619 struct vlan_info *vi = link->l_info;
620
621 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
622 return NULL;
623
624 if (negress == NULL)
625 return NULL;
626
627 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
628 *negress = vi->vi_negress;
629 return vi->vi_egress_qos;
630 } else {
631 *negress = 0;
632 return NULL;
633 }
634 }
635
636 /** @} */
637
638 static const struct trans_tbl vlan_flags[] = {
639 __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr),
640 __ADD(VLAN_FLAG_GVRP, gvrp),
641 __ADD(VLAN_FLAG_LOOSE_BINDING, loose_binding),
642 __ADD(VLAN_FLAG_MVRP, mvrp),
643 __ADD(VLAN_FLAG_BRIDGE_BINDING, bridge_binding),
644 };
645
646 /**
647 * @name Flag Translation
648 * @{
649 */
650
rtnl_link_vlan_flags2str(int flags,char * buf,size_t len)651 char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
652 {
653 return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
654 }
655
rtnl_link_vlan_str2flags(const char * name)656 int rtnl_link_vlan_str2flags(const char *name)
657 {
658 return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
659 }
660
661 /** @} */
662
663
vlan_init(void)664 static void __init vlan_init(void)
665 {
666 rtnl_link_register_info(&vlan_info_ops);
667 }
668
vlan_exit(void)669 static void __exit vlan_exit(void)
670 {
671 rtnl_link_unregister_info(&vlan_info_ops);
672 }
673
674 /** @} */
675