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@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 
rtnl_link_vlan_flags2str(int flags,char * buf,size_t len)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 
rtnl_link_vlan_str2flags(const char * name)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 
vlan_alloc(struct rtnl_link * link)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 
vlan_parse(struct rtnl_link * link,struct nlattr * data,struct nlattr * xstats)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 
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 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 
vlan_clone(struct rtnl_link * dst,struct rtnl_link * src)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 
vlan_put_attrs(struct nl_msg * msg,struct rtnl_link * link)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 
rtnl_link_vlan_set_id(struct rtnl_link * link,int id)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 
rtnl_link_vlan_get_id(struct rtnl_link * link)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 
rtnl_link_vlan_set_flags(struct rtnl_link * link,unsigned int flags)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 
rtnl_link_vlan_unset_flags(struct rtnl_link * link,unsigned int flags)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 
rtnl_link_vlan_get_flags(struct rtnl_link * link)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 
rtnl_link_vlan_set_ingress_map(struct rtnl_link * link,int from,uint32_t to)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 
rtnl_link_vlan_get_ingress_map(struct rtnl_link * link)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 
rtnl_link_vlan_set_egress_map(struct rtnl_link * link,uint32_t from,int to)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 
rtnl_link_vlan_get_egress_map(struct rtnl_link * link,int * negress)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 
vlan_init(void)481 static void __init vlan_init(void)
482 {
483 	rtnl_link_register_info(&vlan_info_ops);
484 }
485 
vlan_exit(void)486 static void __exit vlan_exit(void)
487 {
488 	rtnl_link_unregister_info(&vlan_info_ops);
489 }
490 
491 /** @} */
492