• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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