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