Home | History | Annotate | Download | only in link
      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 (at) 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 
     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 
     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 
    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 
    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 
    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 
    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 
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    627 static void __init vlan_init(void)
    628 {
    629 	rtnl_link_register_info(&vlan_info_ops);
    630 }
    631 
    632 static void __exit vlan_exit(void)
    633 {
    634 	rtnl_link_unregister_info(&vlan_info_ops);
    635 }
    636 
    637 /** @} */
    638