libnl  3.2.24-rc1
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-2013 Thomas Graf <tgraf@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 
42 struct vlan_info
43 {
44  uint16_t vi_vlan_id;
45  uint32_t vi_flags;
46  uint32_t vi_flags_mask;
47  uint32_t vi_ingress_qos[VLAN_PRIO_MAX+1];
48  uint32_t vi_negress;
49  uint32_t vi_egress_size;
50  struct vlan_map * vi_egress_qos;
51  uint32_t vi_mask;
52 };
53 
54 /** @endcond */
55 
56 static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = {
57  [IFLA_VLAN_ID] = { .type = NLA_U16 },
58  [IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) },
59  [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
60  [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED },
61 };
62 
63 static int vlan_alloc(struct rtnl_link *link)
64 {
65  struct vlan_info *vi;
66 
67  if ((vi = calloc(1, sizeof(*vi))) == NULL)
68  return -NLE_NOMEM;
69 
70  link->l_info = vi;
71 
72  return 0;
73 }
74 
75 static int vlan_parse(struct rtnl_link *link, struct nlattr *data,
76  struct nlattr *xstats)
77 {
78  struct nlattr *tb[IFLA_VLAN_MAX+1];
79  struct vlan_info *vi;
80  int err;
81 
82  NL_DBG(3, "Parsing VLAN link info");
83 
84  if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0)
85  goto errout;
86 
87  if ((err = vlan_alloc(link)) < 0)
88  goto errout;
89 
90  vi = link->l_info;
91 
92  if (tb[IFLA_VLAN_ID]) {
93  vi->vi_vlan_id = nla_get_u16(tb[IFLA_VLAN_ID]);
94  vi->vi_mask |= VLAN_HAS_ID;
95  }
96 
97  if (tb[IFLA_VLAN_FLAGS]) {
98  struct ifla_vlan_flags flags;
99  nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags));
100 
101  vi->vi_flags = flags.flags;
102  vi->vi_mask |= VLAN_HAS_FLAGS;
103  }
104 
105  if (tb[IFLA_VLAN_INGRESS_QOS]) {
106  struct ifla_vlan_qos_mapping *map;
107  struct nlattr *nla;
108  int remaining;
109 
110  memset(vi->vi_ingress_qos, 0, sizeof(vi->vi_ingress_qos));
111 
112  nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) {
113  if (nla_len(nla) < sizeof(*map))
114  return -NLE_INVAL;
115 
116  map = nla_data(nla);
117  if (map->from > VLAN_PRIO_MAX) {
118  return -NLE_INVAL;
119  }
120 
121  vi->vi_ingress_qos[map->from] = map->to;
122  }
123 
124  vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
125  }
126 
127  if (tb[IFLA_VLAN_EGRESS_QOS]) {
128  struct ifla_vlan_qos_mapping *map;
129  struct nlattr *nla;
130  int remaining, i = 0;
131 
132  nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
133  if (nla_len(nla) < sizeof(*map))
134  return -NLE_INVAL;
135  i++;
136  }
137 
138  /* align to have a little reserve */
139  vi->vi_egress_size = (i + 32) & ~31;
140  vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map));
141  if (vi->vi_egress_qos == NULL)
142  return -NLE_NOMEM;
143 
144  i = 0;
145  nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
146  map = nla_data(nla);
147  NL_DBG(4, "Assigning egress qos mapping %d\n", i);
148  vi->vi_egress_qos[i].vm_from = map->from;
149  vi->vi_egress_qos[i++].vm_to = map->to;
150  }
151 
152  vi->vi_negress = i;
153  vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
154  }
155 
156  err = 0;
157 errout:
158  return err;
159 }
160 
161 static void vlan_free(struct rtnl_link *link)
162 {
163  struct vlan_info *vi = link->l_info;
164 
165  if (vi) {
166  free(vi->vi_egress_qos);
167  vi->vi_egress_qos = NULL;
168  }
169 
170  free(vi);
171  link->l_info = NULL;
172 }
173 
174 static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
175 {
176  struct vlan_info *vi = link->l_info;
177 
178  nl_dump(p, "vlan-id %d", vi->vi_vlan_id);
179 }
180 
181 static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
182 {
183  struct vlan_info *vi = link->l_info;
184  int printed;
185  uint32_t i;
186  char buf[64];
187 
188  rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
189  nl_dump_line(p, " vlan-info id %d <%s>\n", vi->vi_vlan_id, buf);
190 
191  if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
192  nl_dump_line(p,
193  " ingress vlan prio -> qos/socket prio mapping:\n");
194  for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) {
195  if (vi->vi_ingress_qos[i]) {
196  if (printed == 0)
197  nl_dump_line(p, " ");
198  nl_dump(p, "%x -> %#08x, ",
199  i, vi->vi_ingress_qos[i]);
200  if (printed++ == 3) {
201  nl_dump(p, "\n");
202  printed = 0;
203  }
204  }
205  }
206 
207  if (printed > 0 && printed != 4)
208  nl_dump(p, "\n");
209  }
210 
211  if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
212  nl_dump_line(p,
213  " egress qos/socket prio -> vlan prio mapping:\n");
214  for (i = 0, printed = 0; i < vi->vi_negress; i++) {
215  if (printed == 0)
216  nl_dump_line(p, " ");
217  nl_dump(p, "%#08x -> %x, ",
218  vi->vi_egress_qos[i].vm_from,
219  vi->vi_egress_qos[i].vm_to);
220  if (printed++ == 3) {
221  nl_dump(p, "\n");
222  printed = 0;
223  }
224  }
225 
226  if (printed > 0 && printed != 4)
227  nl_dump(p, "\n");
228  }
229 }
230 
231 static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
232 {
233  struct vlan_info *vdst, *vsrc = src->l_info;
234  int err;
235 
236  dst->l_info = NULL;
237  if ((err = rtnl_link_set_type(dst, "vlan")) < 0)
238  return err;
239  vdst = dst->l_info;
240 
241  vdst->vi_egress_qos = calloc(vsrc->vi_egress_size,
242  sizeof(struct vlan_map));
243  if (!vdst->vi_egress_qos)
244  return -NLE_NOMEM;
245 
246  memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
247  vsrc->vi_egress_size * sizeof(struct vlan_map));
248 
249  return 0;
250 }
251 
252 static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
253 {
254  struct vlan_info *vi = link->l_info;
255  struct nlattr *data;
256 
257  if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
258  return -NLE_MSGSIZE;
259 
260  if (vi->vi_mask & VLAN_HAS_ID)
261  NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
262 
263  if (vi->vi_mask & VLAN_HAS_FLAGS) {
264  struct ifla_vlan_flags flags = {
265  .flags = vi->vi_flags,
266  .mask = vi->vi_flags_mask,
267  };
268 
269  NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
270  }
271 
272  if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
273  struct ifla_vlan_qos_mapping map;
274  struct nlattr *qos;
275  int i;
276 
277  if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
278  goto nla_put_failure;
279 
280  for (i = 0; i <= VLAN_PRIO_MAX; i++) {
281  if (vi->vi_ingress_qos[i]) {
282  map.from = i;
283  map.to = vi->vi_ingress_qos[i];
284 
285  NLA_PUT(msg, i, sizeof(map), &map);
286  }
287  }
288 
289  nla_nest_end(msg, qos);
290  }
291 
292  if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
293  struct ifla_vlan_qos_mapping map;
294  struct nlattr *qos;
295  uint32_t i;
296 
297  if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
298  goto nla_put_failure;
299 
300  for (i = 0; i < vi->vi_negress; i++) {
301  map.from = vi->vi_egress_qos[i].vm_from;
302  map.to = vi->vi_egress_qos[i].vm_to;
303 
304  NLA_PUT(msg, i, sizeof(map), &map);
305  }
306 
307  nla_nest_end(msg, qos);
308  }
309 
310  nla_nest_end(msg, data);
311 
312 nla_put_failure:
313 
314  return 0;
315 }
316 
317 static struct rtnl_link_info_ops vlan_info_ops = {
318  .io_name = "vlan",
319  .io_alloc = vlan_alloc,
320  .io_parse = vlan_parse,
321  .io_dump = {
322  [NL_DUMP_LINE] = vlan_dump_line,
323  [NL_DUMP_DETAILS] = vlan_dump_details,
324  },
325  .io_clone = vlan_clone,
326  .io_put_attrs = vlan_put_attrs,
327  .io_free = vlan_free,
328 };
329 
330 /** @cond SKIP */
331 #define IS_VLAN_LINK_ASSERT(link) \
332  if ((link)->l_info_ops != &vlan_info_ops) { \
333  APPBUG("Link is not a vlan link. set type \"vlan\" first."); \
334  return -NLE_OPNOTSUPP; \
335  }
336 /** @endcond */
337 
338 /**
339  * @name VLAN Object
340  * @{
341  */
342 
343 /**
344  * Allocate link object of type VLAN
345  *
346  * @return Allocated link object or NULL.
347  */
349 {
350  struct rtnl_link *link;
351  int err;
352 
353  if (!(link = rtnl_link_alloc()))
354  return NULL;
355 
356  if ((err = rtnl_link_set_type(link, "vlan")) < 0) {
357  rtnl_link_put(link);
358  return NULL;
359  }
360 
361  return link;
362 }
363 
364 /**
365  * Check if link is a VLAN link
366  * @arg link Link object
367  *
368  * @return True if link is a VLAN link, otherwise false is returned.
369  */
370 int rtnl_link_is_vlan(struct rtnl_link *link)
371 {
372  return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vlan");
373 }
374 
375 /**
376  * Set VLAN ID
377  * @arg link Link object
378  * @arg id VLAN identifier
379  *
380  * @return 0 on success or a negative error code
381  */
382 int rtnl_link_vlan_set_id(struct rtnl_link *link, uint16_t id)
383 {
384  struct vlan_info *vi = link->l_info;
385 
386  IS_VLAN_LINK_ASSERT(link);
387 
388  vi->vi_vlan_id = id;
389  vi->vi_mask |= VLAN_HAS_ID;
390 
391  return 0;
392 }
393 
394 /**
395  * Get VLAN Id
396  * @arg link Link object
397  *
398  * @return VLAN id, 0 if not set or a negative error code.
399  */
401 {
402  struct vlan_info *vi = link->l_info;
403 
404  IS_VLAN_LINK_ASSERT(link);
405 
406  if (vi->vi_mask & VLAN_HAS_ID)
407  return vi->vi_vlan_id;
408  else
409  return 0;
410 }
411 
412 /**
413  * Set VLAN flags
414  * @arg link Link object
415  * @arg flags VLAN flags
416  *
417  * @return 0 on success or a negative error code.
418  */
419 int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
420 {
421  struct vlan_info *vi = link->l_info;
422 
423  IS_VLAN_LINK_ASSERT(link);
424 
425  vi->vi_flags_mask |= flags;
426  vi->vi_flags |= flags;
427  vi->vi_mask |= VLAN_HAS_FLAGS;
428 
429  return 0;
430 }
431 
432 /**
433  * Unset VLAN flags
434  * @arg link Link object
435  * @arg flags VLAN flags
436  *
437  * @return 0 on success or a negative error code.
438  */
439 int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
440 {
441  struct vlan_info *vi = link->l_info;
442 
443  IS_VLAN_LINK_ASSERT(link);
444 
445  vi->vi_flags_mask |= flags;
446  vi->vi_flags &= ~flags;
447  vi->vi_mask |= VLAN_HAS_FLAGS;
448 
449  return 0;
450 }
451 
452 /**
453  * Get VLAN flags
454  * @arg link Link object
455  *
456  * @return VLAN flags, 0 if none set, or a negative error code.
457  */
459 {
460  struct vlan_info *vi = link->l_info;
461 
462  IS_VLAN_LINK_ASSERT(link);
463 
464  return vi->vi_flags;
465 }
466 
467 /** @} */
468 
469 /**
470  * @name Quality of Service
471  * @{
472  */
473 
474 int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
475  uint32_t to)
476 {
477  struct vlan_info *vi = link->l_info;
478 
479  IS_VLAN_LINK_ASSERT(link);
480 
481  if (from < 0 || from > VLAN_PRIO_MAX)
482  return -NLE_INVAL;
483 
484  vi->vi_ingress_qos[from] = to;
485  vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
486 
487  return 0;
488 }
489 
490 uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link)
491 {
492  struct vlan_info *vi = link->l_info;
493 
494  if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
495  return NULL;
496 
497  if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
498  return vi->vi_ingress_qos;
499  else
500  return NULL;
501 }
502 
503 int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to)
504 {
505  struct vlan_info *vi = link->l_info;
506 
507  if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
508  return -NLE_OPNOTSUPP;
509 
510  if (to < 0 || to > VLAN_PRIO_MAX)
511  return -NLE_INVAL;
512 
513  if (vi->vi_negress >= vi->vi_egress_size) {
514  int new_size = vi->vi_egress_size + 32;
515  void *ptr;
516 
517  ptr = realloc(vi->vi_egress_qos, new_size);
518  if (!ptr)
519  return -NLE_NOMEM;
520 
521  vi->vi_egress_qos = ptr;
522  vi->vi_egress_size = new_size;
523  }
524 
525  vi->vi_egress_qos[vi->vi_negress].vm_from = from;
526  vi->vi_egress_qos[vi->vi_negress].vm_to = to;
527  vi->vi_negress++;
528  vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
529 
530  return 0;
531 }
532 
533 struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
534  int *negress)
535 {
536  struct vlan_info *vi = link->l_info;
537 
538  if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
539  return NULL;
540 
541  if (negress == NULL)
542  return NULL;
543 
544  if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
545  *negress = vi->vi_negress;
546  return vi->vi_egress_qos;
547  } else {
548  *negress = 0;
549  return NULL;
550  }
551 }
552 
553 /** @} */
554 
555 static const struct trans_tbl vlan_flags[] = {
556  __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
557 };
558 
559 /**
560  * @name Flag Translation
561  * @{
562  */
563 
564 char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
565 {
566  return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
567 }
568 
569 int rtnl_link_vlan_str2flags(const char *name)
570 {
571  return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
572 }
573 
574 /** @} */
575 
576 
577 static void __init vlan_init(void)
578 {
579  rtnl_link_register_info(&vlan_info_ops);
580 }
581 
582 static void __exit vlan_exit(void)
583 {
584  rtnl_link_unregister_info(&vlan_info_ops);
585 }
586 
587 /** @} */