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