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-2008 Thomas Graf <tgraf (at) suug.ch>
     10  */
     11 
     12 /**
     13  * @ingroup link_info
     14  * @defgroup vlan VLAN
     15  * @brief
     16  *
     17  * @{
     18  */
     19 
     20 #include <netlink-local.h>
     21 #include <netlink/netlink.h>
     22 #include <netlink/attr.h>
     23 #include <netlink/utils.h>
     24 #include <netlink/object.h>
     25 #include <netlink/route/rtnl.h>
     26 #include <netlink/route/link/info-api.h>
     27 #include <netlink/route/link/vlan.h>
     28 
     29 #include <linux/if_vlan.h>
     30 
     31 /** @cond SKIP */
     32 #define VLAN_HAS_ID		(1<<0)
     33 #define VLAN_HAS_FLAGS		(1<<1)
     34 #define VLAN_HAS_INGRESS_QOS	(1<<2)
     35 #define VLAN_HAS_EGRESS_QOS	(1<<3)
     36 
     37 struct vlan_info
     38 {
     39 	uint16_t		vi_vlan_id;
     40 	uint32_t		vi_flags;
     41 	uint32_t		vi_flags_mask;
     42 	uint32_t		vi_ingress_qos[VLAN_PRIO_MAX+1];
     43 	uint32_t		vi_negress;
     44 	uint32_t		vi_egress_size;
     45 	struct vlan_map * 	vi_egress_qos;
     46 	uint32_t		vi_mask;
     47 };
     48 /** @endcond */
     49 
     50 static struct trans_tbl vlan_flags[] = {
     51 	__ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
     52 };
     53 
     54 char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
     55 {
     56 	return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
     57 }
     58 
     59 int rtnl_link_vlan_str2flags(const char *name)
     60 {
     61 	return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
     62 }
     63 
     64 static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = {
     65 	[IFLA_VLAN_ID]		= { .type = NLA_U16 },
     66 	[IFLA_VLAN_FLAGS]	= { .minlen = sizeof(struct ifla_vlan_flags) },
     67 	[IFLA_VLAN_INGRESS_QOS]	= { .type = NLA_NESTED },
     68 	[IFLA_VLAN_EGRESS_QOS]	= { .type = NLA_NESTED },
     69 };
     70 
     71 static int vlan_alloc(struct rtnl_link *link)
     72 {
     73 	struct vlan_info *vi;
     74 
     75 	if ((vi = calloc(1, sizeof(*vi))) == NULL)
     76 		return -NLE_NOMEM;
     77 
     78 	link->l_info = vi;
     79 
     80 	return 0;
     81 }
     82 
     83 static int vlan_parse(struct rtnl_link *link, struct nlattr *data,
     84 		      struct nlattr *xstats)
     85 {
     86 	struct nlattr *tb[IFLA_VLAN_MAX+1];
     87 	struct vlan_info *vi;
     88 	int err;
     89 
     90 	NL_DBG(3, "Parsing VLAN link info");
     91 
     92 	if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0)
     93 		goto errout;
     94 
     95 	if ((err = vlan_alloc(link)) < 0)
     96 		goto errout;
     97 
     98 	vi = link->l_info;
     99 
    100 	if (tb[IFLA_VLAN_ID]) {
    101 		vi->vi_vlan_id = nla_get_u16(tb[IFLA_VLAN_ID]);
    102 		vi->vi_mask |= VLAN_HAS_ID;
    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 < 0 || 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(*map));
    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 i, printed;
    193 	char buf[64];
    194 
    195 	rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
    196 	nl_dump_line(p, "    vlan-info id %d <%s>\n", vi->vi_vlan_id, buf);
    197 
    198 	if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
    199 		nl_dump_line(p,
    200 		"      ingress vlan prio -> qos/socket prio mapping:\n");
    201 		for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) {
    202 			if (vi->vi_ingress_qos[i]) {
    203 				if (printed == 0)
    204 					nl_dump_line(p, "      ");
    205 				nl_dump(p, "%x -> %#08x, ",
    206 					i, vi->vi_ingress_qos[i]);
    207 				if (printed++ == 3) {
    208 					nl_dump(p, "\n");
    209 					printed = 0;
    210 				}
    211 			}
    212 		}
    213 
    214 		if (printed > 0 && printed != 4)
    215 			nl_dump(p, "\n");
    216 	}
    217 
    218 	if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
    219 		nl_dump_line(p,
    220 		"      egress qos/socket prio -> vlan prio mapping:\n");
    221 		for (i = 0, printed = 0; i < vi->vi_negress; i++) {
    222 			if (printed == 0)
    223 				nl_dump_line(p, "      ");
    224 			nl_dump(p, "%#08x -> %x, ",
    225 				vi->vi_egress_qos[i].vm_from,
    226 				vi->vi_egress_qos[i].vm_to);
    227 			if (printed++ == 3) {
    228 				nl_dump(p, "\n");
    229 				printed = 0;
    230 			}
    231 		}
    232 
    233 		if (printed > 0 && printed != 4)
    234 			nl_dump(p, "\n");
    235 	}
    236 }
    237 
    238 static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
    239 {
    240 	struct vlan_info *vdst, *vsrc = src->l_info;
    241 	int err;
    242 
    243 	dst->l_info = NULL;
    244 	if ((err = rtnl_link_set_info_type(dst, "vlan")) < 0)
    245 		return err;
    246 	vdst = dst->l_info;
    247 
    248 	vdst->vi_egress_qos = calloc(vsrc->vi_egress_size,
    249 				     sizeof(struct vlan_map));
    250 	if (!vdst->vi_egress_qos)
    251 		return -NLE_NOMEM;
    252 
    253 	memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
    254 	       vsrc->vi_egress_size * sizeof(struct vlan_map));
    255 
    256 	return 0;
    257 }
    258 
    259 static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
    260 {
    261 	struct vlan_info *vi = link->l_info;
    262 	struct nlattr *data;
    263 
    264 	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
    265 		return -NLE_MSGSIZE;
    266 
    267 	if (vi->vi_mask & VLAN_HAS_ID)
    268 		NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
    269 
    270 	if (vi->vi_mask & VLAN_HAS_FLAGS) {
    271 		struct ifla_vlan_flags flags = {
    272 			.flags = vi->vi_flags,
    273 			.mask = vi->vi_flags_mask,
    274 		};
    275 
    276 		NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
    277 	}
    278 
    279 	if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
    280 		struct ifla_vlan_qos_mapping map;
    281 		struct nlattr *qos;
    282 		int i;
    283 
    284 		if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
    285 			goto nla_put_failure;
    286 
    287 		for (i = 0; i <= VLAN_PRIO_MAX; i++) {
    288 			if (vi->vi_ingress_qos[i]) {
    289 				map.from = i;
    290 				map.to = vi->vi_ingress_qos[i];
    291 
    292 				NLA_PUT(msg, i, sizeof(map), &map);
    293 			}
    294 		}
    295 
    296 		nla_nest_end(msg, qos);
    297 	}
    298 
    299 	if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
    300 		struct ifla_vlan_qos_mapping map;
    301 		struct nlattr *qos;
    302 		int i;
    303 
    304 		if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
    305 			goto nla_put_failure;
    306 
    307 		for (i = 0; i < vi->vi_negress; i++) {
    308 			map.from = vi->vi_egress_qos[i].vm_from;
    309 			map.to = vi->vi_egress_qos[i].vm_to;
    310 
    311 			NLA_PUT(msg, i, sizeof(map), &map);
    312 		}
    313 
    314 		nla_nest_end(msg, qos);
    315 	}
    316 
    317 	nla_nest_end(msg, data);
    318 
    319 nla_put_failure:
    320 
    321 	return 0;
    322 }
    323 
    324 static struct rtnl_link_info_ops vlan_info_ops = {
    325 	.io_name		= "vlan",
    326 	.io_alloc		= vlan_alloc,
    327 	.io_parse		= vlan_parse,
    328 	.io_dump = {
    329 	    [NL_DUMP_LINE]	= vlan_dump_line,
    330 	    [NL_DUMP_DETAILS]	= vlan_dump_details,
    331 	},
    332 	.io_clone		= vlan_clone,
    333 	.io_put_attrs		= vlan_put_attrs,
    334 	.io_free		= vlan_free,
    335 };
    336 
    337 int rtnl_link_vlan_set_id(struct rtnl_link *link, int id)
    338 {
    339 	struct vlan_info *vi = link->l_info;
    340 
    341 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
    342 		return -NLE_OPNOTSUPP;
    343 
    344 	vi->vi_vlan_id = id;
    345 	vi->vi_mask |= VLAN_HAS_ID;
    346 
    347 	return 0;
    348 }
    349 
    350 int rtnl_link_vlan_get_id(struct rtnl_link *link)
    351 {
    352 	struct vlan_info *vi = link->l_info;
    353 
    354 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
    355 		return -NLE_OPNOTSUPP;
    356 
    357 	if (vi->vi_mask & VLAN_HAS_ID)
    358 		return vi->vi_vlan_id;
    359 	else
    360 		return 0;
    361 }
    362 
    363 int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
    364 {
    365 	struct vlan_info *vi = link->l_info;
    366 
    367 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
    368 		return -NLE_OPNOTSUPP;
    369 
    370 	vi->vi_flags_mask |= flags;
    371 	vi->vi_flags |= flags;
    372 	vi->vi_mask |= VLAN_HAS_FLAGS;
    373 
    374 	return 0;
    375 }
    376 
    377 int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
    378 {
    379 	struct vlan_info *vi = link->l_info;
    380 
    381 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
    382 		return -NLE_OPNOTSUPP;
    383 
    384 	vi->vi_flags_mask |= flags;
    385 	vi->vi_flags &= ~flags;
    386 	vi->vi_mask |= VLAN_HAS_FLAGS;
    387 
    388 	return 0;
    389 }
    390 
    391 unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link)
    392 {
    393 	struct vlan_info *vi = link->l_info;
    394 
    395 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
    396 		return -NLE_OPNOTSUPP;
    397 
    398 	return vi->vi_flags;
    399 }
    400 
    401 int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
    402 				   uint32_t to)
    403 {
    404 	struct vlan_info *vi = link->l_info;
    405 
    406 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
    407 		return -NLE_OPNOTSUPP;
    408 
    409 	if (from < 0 || from > VLAN_PRIO_MAX)
    410 		return -NLE_INVAL;
    411 
    412 	vi->vi_ingress_qos[from] = to;
    413 	vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
    414 
    415 	return 0;
    416 }
    417 
    418 uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link)
    419 {
    420 	struct vlan_info *vi = link->l_info;
    421 
    422 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
    423 		return NULL;
    424 
    425 	if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
    426 		return vi->vi_ingress_qos;
    427 	else
    428 		return NULL;
    429 }
    430 
    431 int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to)
    432 {
    433 	struct vlan_info *vi = link->l_info;
    434 
    435 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
    436 		return -NLE_OPNOTSUPP;
    437 
    438 	if (to < 0 || to > VLAN_PRIO_MAX)
    439 		return -NLE_INVAL;
    440 
    441 	if (vi->vi_negress >= vi->vi_egress_size) {
    442 		int new_size = vi->vi_egress_size + 32;
    443 		void *ptr;
    444 
    445 		ptr = realloc(vi->vi_egress_qos, new_size);
    446 		if (!ptr)
    447 			return -NLE_NOMEM;
    448 
    449 		vi->vi_egress_qos = ptr;
    450 		vi->vi_egress_size = new_size;
    451 	}
    452 
    453 	vi->vi_egress_qos[vi->vi_negress].vm_from = from;
    454 	vi->vi_egress_qos[vi->vi_negress].vm_to = to;
    455 	vi->vi_negress++;
    456 	vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
    457 
    458 	return 0;
    459 }
    460 
    461 struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
    462 					       int *negress)
    463 {
    464 	struct vlan_info *vi = link->l_info;
    465 
    466 	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
    467 		return NULL;
    468 
    469 	if (negress == NULL)
    470 		return NULL;
    471 
    472 	if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
    473 		*negress = vi->vi_negress;
    474 		return vi->vi_egress_qos;
    475 	} else {
    476 		*negress = 0;
    477 		return NULL;
    478 	}
    479 }
    480 
    481 static void __init vlan_init(void)
    482 {
    483 	rtnl_link_register_info(&vlan_info_ops);
    484 }
    485 
    486 static void __exit vlan_exit(void)
    487 {
    488 	rtnl_link_unregister_info(&vlan_info_ops);
    489 }
    490 
    491 /** @} */
    492