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