libnl 1.1.4
vlan.c
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-2007 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
37struct 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
50static struct trans_tbl vlan_flags[] = {
51 __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
52};
53
54char *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
59int rtnl_link_vlan_str2flags(const char *name)
60{
61 return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
62}
63
64static 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
71static int vlan_alloc(struct rtnl_link *link)
72{
73 struct vlan_info *vi;
74
75 if ((vi = calloc(1, sizeof(*vi))) == NULL)
76 return nl_errno(ENOMEM);
77
78 link->l_info = vi;
79
80 return 0;
81}
82
83static 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 nl_error(EINVAL, "Malformed mapping");
123
124 map = nla_data(nla);
125 if (map->from < 0 || map->from > VLAN_PRIO_MAX) {
126 return nl_error(EINVAL, "VLAN prio %d out of "
127 "range", map->from);
128 }
129
130 vi->vi_ingress_qos[map->from] = map->to;
131 }
132
133 vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
134 }
135
136 if (tb[IFLA_VLAN_EGRESS_QOS]) {
137 struct ifla_vlan_qos_mapping *map;
138 struct nlattr *nla;
139 int remaining, i = 0;
140
141 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
142 if (nla_len(nla) < sizeof(*map))
143 return nl_error(EINVAL, "Malformed mapping");
144 i++;
145 }
146
147 /* align to have a little reserve */
148 vi->vi_egress_size = (i + 32) & ~31;
149 vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map));
150 if (vi->vi_egress_qos == NULL)
151 return nl_errno(ENOMEM);
152
153 i = 0;
154 nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
155 map = nla_data(nla);
156 NL_DBG(4, "Assigning egress qos mapping %d\n", i);
157 vi->vi_egress_qos[i].vm_from = map->from;
158 vi->vi_egress_qos[i++].vm_to = map->to;
159 }
160
161 vi->vi_negress = i;
162 vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
163 }
164
165 err = 0;
166errout:
167 return err;
168}
169
170static void vlan_free(struct rtnl_link *link)
171{
172 struct vlan_info *vi = link->l_info;
173
174 if (vi) {
175 free(vi->vi_egress_qos);
176 vi->vi_egress_qos = NULL;
177 }
178
179 free(vi);
180 link->l_info = NULL;
181}
182
183static int vlan_dump_brief(struct rtnl_link *link, struct nl_dump_params *p,
184 int line)
185{
186 struct vlan_info *vi = link->l_info;
187
188 dp_dump(p, "vlan-id %d", vi->vi_vlan_id);
189
190 return line;
191}
192
193static int vlan_dump_full(struct rtnl_link *link, struct nl_dump_params *p,
194 int line)
195{
196 struct vlan_info *vi = link->l_info;
197 int i, printed;
198 char buf[64];
199
200 rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
201 dp_dump_line(p, line++, " vlan-info id %d <%s>\n",
202 vi->vi_vlan_id, buf);
203
204 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
205 dp_dump_line(p, line++,
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 dp_new_line(p, line);
211 dp_dump(p, " ");
212 }
213 dp_dump(p, "%x -> %#08x, ",
214 i, vi->vi_ingress_qos[i]);
215 if (printed++ == 3) {
216 dp_dump(p, "\n");
217 printed = 0;
218 }
219 }
220 }
221
222 if (printed > 0 && printed != 4)
223 dp_dump(p, "\n");
224 }
225
226 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
227 dp_dump_line(p, line++,
228 " egress qos/socket prio -> vlan prio mapping:\n");
229 for (i = 0, printed = 0; i < vi->vi_negress; i++) {
230 if (printed == 0) {
231 dp_new_line(p, line);
232 dp_dump(p, " ");
233 }
234 dp_dump(p, "%#08x -> %x, ",
235 vi->vi_egress_qos[i].vm_from,
236 vi->vi_egress_qos[i].vm_to);
237 if (printed++ == 3) {
238 dp_dump(p, "\n");
239 printed = 0;
240 }
241 }
242
243 if (printed > 0 && printed != 4)
244 dp_dump(p, "\n");
245 }
246
247 return line;
248}
249
250static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
251{
252 struct vlan_info *vdst, *vsrc = src->l_info;
253 int err;
254
255 dst->l_info = NULL;
256 if ((err = rtnl_link_set_info_type(dst, "vlan")) < 0)
257 return err;
258 vdst = dst->l_info;
259
260 vdst->vi_egress_qos = calloc(vsrc->vi_egress_size,
261 sizeof(struct vlan_map));
262 if (!vdst->vi_egress_qos)
263 return nl_errno(ENOMEM);
264
265 memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
266 vsrc->vi_egress_size * sizeof(struct vlan_map));
267
268 return 0;
269}
270
271static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
272{
273 struct vlan_info *vi = link->l_info;
274 struct nlattr *data;
275
276 if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
277 return nl_errno(ENOBUFS);
278
279 if (vi->vi_mask & VLAN_HAS_ID)
280 NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
281
282 if (vi->vi_mask & VLAN_HAS_FLAGS) {
283 struct ifla_vlan_flags flags = {
284 .flags = vi->vi_flags,
285 .mask = vi->vi_flags_mask,
286 };
287
288 NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
289 }
290
291 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
292 struct ifla_vlan_qos_mapping map;
293 struct nlattr *qos;
294 int i;
295
296 if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
297 goto nla_put_failure;
298
299 for (i = 0; i <= VLAN_PRIO_MAX; i++) {
300 if (vi->vi_ingress_qos[i]) {
301 map.from = i;
302 map.to = vi->vi_ingress_qos[i];
303
304 NLA_PUT(msg, i, sizeof(map), &map);
305 }
306 }
307
308 nla_nest_end(msg, qos);
309 }
310
311 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
312 struct ifla_vlan_qos_mapping map;
313 struct nlattr *qos;
314 int i;
315
316 if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
317 goto nla_put_failure;
318
319 for (i = 0; i < vi->vi_negress; i++) {
320 map.from = vi->vi_egress_qos[i].vm_from;
321 map.to = vi->vi_egress_qos[i].vm_to;
322
323 NLA_PUT(msg, i, sizeof(map), &map);
324 }
325
326 nla_nest_end(msg, qos);
327 }
328
329 nla_nest_end(msg, data);
330
331nla_put_failure:
332
333 return 0;
334}
335
336static struct rtnl_link_info_ops vlan_info_ops = {
337 .io_name = "vlan",
338 .io_alloc = vlan_alloc,
339 .io_parse = vlan_parse,
340 .io_dump[NL_DUMP_BRIEF] = vlan_dump_brief,
341 .io_dump[NL_DUMP_FULL] = vlan_dump_full,
342 .io_clone = vlan_clone,
343 .io_put_attrs = vlan_put_attrs,
344 .io_free = vlan_free,
345};
346
347int rtnl_link_vlan_set_id(struct rtnl_link *link, int id)
348{
349 struct vlan_info *vi = link->l_info;
350
351 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
352 return nl_error(EOPNOTSUPP, "Not a VLAN link");
353
354 vi->vi_vlan_id = id;
355 vi->vi_mask |= VLAN_HAS_ID;
356
357 return 0;
358}
359
360int rtnl_link_vlan_get_id(struct rtnl_link *link)
361{
362 struct vlan_info *vi = link->l_info;
363
364 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
365 return nl_error(EOPNOTSUPP, "Not a VLAN link");
366
367 if (vi->vi_mask & VLAN_HAS_ID)
368 return vi->vi_vlan_id;
369 else
370 return 0;
371}
372
373int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
374{
375 struct vlan_info *vi = link->l_info;
376
377 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
378 return nl_error(EOPNOTSUPP, "Not a VLAN link");
379
380 vi->vi_flags_mask |= flags;
381 vi->vi_flags |= flags;
382 vi->vi_mask |= VLAN_HAS_FLAGS;
383
384 return 0;
385}
386
387int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
388{
389 struct vlan_info *vi = link->l_info;
390
391 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
392 return nl_error(EOPNOTSUPP, "Not a VLAN link");
393
394 vi->vi_flags_mask |= flags;
395 vi->vi_flags &= ~flags;
396 vi->vi_mask |= VLAN_HAS_FLAGS;
397
398 return 0;
399}
400
401unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link)
402{
403 struct vlan_info *vi = link->l_info;
404
405 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
406 return nl_error(EOPNOTSUPP, "Not a VLAN link");
407
408 return vi->vi_flags;
409}
410
411int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
412 uint32_t to)
413{
414 struct vlan_info *vi = link->l_info;
415
416 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
417 return nl_error(EOPNOTSUPP, "Not a VLAN link");
418
419 if (from < 0 || from > VLAN_PRIO_MAX)
420 return nl_error(EINVAL, "Invalid vlan prio 0..%d",
421 VLAN_PRIO_MAX);
422
423 vi->vi_ingress_qos[from] = to;
424 vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
425
426 return 0;
427}
428
429uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link)
430{
431 struct vlan_info *vi = link->l_info;
432
433 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) {
434 nl_error(EOPNOTSUPP, "Not a VLAN link");
435 return NULL;
436 }
437
438 if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
439 return vi->vi_ingress_qos;
440 else
441 return NULL;
442}
443
444int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to)
445{
446 struct vlan_info *vi = link->l_info;
447
448 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
449 return nl_error(EOPNOTSUPP, "Not a VLAN link");
450
451 if (to < 0 || to > VLAN_PRIO_MAX)
452 return nl_error(EINVAL, "Invalid vlan prio 0..%d",
453 VLAN_PRIO_MAX);
454
455 if (vi->vi_negress >= vi->vi_egress_size) {
456 int new_size = vi->vi_egress_size + 32;
457 void *ptr;
458
459 ptr = realloc(vi->vi_egress_qos, new_size);
460 if (!ptr)
461 return nl_errno(ENOMEM);
462
463 vi->vi_egress_qos = ptr;
464 vi->vi_egress_size = new_size;
465 }
466
467 vi->vi_egress_qos[vi->vi_negress].vm_from = from;
468 vi->vi_egress_qos[vi->vi_negress].vm_to = to;
469 vi->vi_negress++;
470 vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
471
472 return 0;
473}
474
475struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
476 int *negress)
477{
478 struct vlan_info *vi = link->l_info;
479
480 if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops) {
481 nl_error(EOPNOTSUPP, "Not a VLAN link");
482 return NULL;
483 }
484
485 if (negress == NULL) {
486 nl_error(EINVAL, "Require pointer to store negress");
487 return NULL;
488 }
489
490 if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
491 *negress = vi->vi_negress;
492 return vi->vi_egress_qos;
493 } else {
494 *negress = 0;
495 return NULL;
496 }
497}
498
499static void __init vlan_init(void)
500{
501 rtnl_link_register_info(&vlan_info_ops);
502}
503
504static void __exit vlan_exit(void)
505{
506 rtnl_link_unregister_info(&vlan_info_ops);
507}
508
509/** @} */
int nla_nest_end(struct nl_msg *n, struct nlattr *start)
Finalize nesting of attributes.
Definition: attr.c:675
void * nla_data(const struct nlattr *nla)
head of payload
Definition: attr.c:151
uint16_t nla_get_u16(struct nlattr *nla)
Return payload of u16 attribute.
Definition: attr.c:702
int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, struct nla_policy *policy)
parse nested attributes
Definition: attr.c:307
#define NLA_PUT(n, attrtype, attrlen, data)
Add a netlink attribute to a netlink message.
Definition: attr.h:157
#define nla_for_each_nested(pos, nla, rem)
iterate over a stream of nested attributes
Definition: attr.h:276
int nla_len(const struct nlattr *nla)
length of payload
Definition: attr.c:160
#define NLA_PUT_U16(n, attrtype, value)
Add a u16 netlink attribute to a netlink message.
Definition: attr.h:192
struct nlattr * nla_nest_start(struct nl_msg *n, int attrtype)
Start a new level of nested attributes.
Definition: attr.c:655
int nla_memcpy(void *dest, struct nlattr *src, int count)
Copy a netlink attribute into another memory area.
Definition: attr.c:382
@ NLA_U16
16bit integer
Definition: attr.h:38
@ NLA_NESTED
nested attributes
Definition: attr.h:44
@ NL_DUMP_FULL
Dump all attributes but no statistics.
Definition: types.h:23
@ NL_DUMP_BRIEF
Dump object in a brief one-liner.
Definition: types.h:22
Dumping parameters.
Definition: types.h:37
attribute validation policy
Definition: attr.h:73
uint16_t type
Type of attribute or NLA_UNSPEC.
Definition: attr.h:75
Definition: vlan.h:23