libnl  3.2.7
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-2010 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-local.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/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 < 0 || 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 i, printed;
185  char buf[64];
186 
187  rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
188  nl_dump_line(p, " vlan-info id %d <%s>\n", vi->vi_vlan_id, buf);
189 
190  if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
191  nl_dump_line(p,
192  " ingress vlan prio -> qos/socket prio mapping:\n");
193  for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) {
194  if (vi->vi_ingress_qos[i]) {
195  if (printed == 0)
196  nl_dump_line(p, " ");
197  nl_dump(p, "%x -> %#08x, ",
198  i, vi->vi_ingress_qos[i]);
199  if (printed++ == 3) {
200  nl_dump(p, "\n");
201  printed = 0;
202  }
203  }
204  }
205 
206  if (printed > 0 && printed != 4)
207  nl_dump(p, "\n");
208  }
209 
210  if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
211  nl_dump_line(p,
212  " egress qos/socket prio -> vlan prio mapping:\n");
213  for (i = 0, printed = 0; i < vi->vi_negress; i++) {
214  if (printed == 0)
215  nl_dump_line(p, " ");
216  nl_dump(p, "%#08x -> %x, ",
217  vi->vi_egress_qos[i].vm_from,
218  vi->vi_egress_qos[i].vm_to);
219  if (printed++ == 3) {
220  nl_dump(p, "\n");
221  printed = 0;
222  }
223  }
224 
225  if (printed > 0 && printed != 4)
226  nl_dump(p, "\n");
227  }
228 }
229 
230 static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
231 {
232  struct vlan_info *vdst, *vsrc = src->l_info;
233  int err;
234 
235  dst->l_info = NULL;
236  if ((err = rtnl_link_set_type(dst, "vlan")) < 0)
237  return err;
238  vdst = dst->l_info;
239 
240  vdst->vi_egress_qos = calloc(vsrc->vi_egress_size,
241  sizeof(struct vlan_map));
242  if (!vdst->vi_egress_qos)
243  return -NLE_NOMEM;
244 
245  memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
246  vsrc->vi_egress_size * sizeof(struct vlan_map));
247 
248  return 0;
249 }
250 
251 static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
252 {
253  struct vlan_info *vi = link->l_info;
254  struct nlattr *data;
255 
256  if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
257  return -NLE_MSGSIZE;
258 
259  if (vi->vi_mask & VLAN_HAS_ID)
260  NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
261 
262  if (vi->vi_mask & VLAN_HAS_FLAGS) {
263  struct ifla_vlan_flags flags = {
264  .flags = vi->vi_flags,
265  .mask = vi->vi_flags_mask,
266  };
267 
268  NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
269  }
270 
271  if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
272  struct ifla_vlan_qos_mapping map;
273  struct nlattr *qos;
274  int i;
275 
276  if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
277  goto nla_put_failure;
278 
279  for (i = 0; i <= VLAN_PRIO_MAX; i++) {
280  if (vi->vi_ingress_qos[i]) {
281  map.from = i;
282  map.to = vi->vi_ingress_qos[i];
283 
284  NLA_PUT(msg, i, sizeof(map), &map);
285  }
286  }
287 
288  nla_nest_end(msg, qos);
289  }
290 
291  if (vi->vi_mask & VLAN_HAS_EGRESS_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_EGRESS_QOS)))
297  goto nla_put_failure;
298 
299  for (i = 0; i < vi->vi_negress; i++) {
300  map.from = vi->vi_egress_qos[i].vm_from;
301  map.to = vi->vi_egress_qos[i].vm_to;
302 
303  NLA_PUT(msg, i, sizeof(map), &map);
304  }
305 
306  nla_nest_end(msg, qos);
307  }
308 
309  nla_nest_end(msg, data);
310 
311 nla_put_failure:
312 
313  return 0;
314 }
315 
316 static struct rtnl_link_info_ops vlan_info_ops = {
317  .io_name = "vlan",
318  .io_alloc = vlan_alloc,
319  .io_parse = vlan_parse,
320  .io_dump = {
321  [NL_DUMP_LINE] = vlan_dump_line,
322  [NL_DUMP_DETAILS] = vlan_dump_details,
323  },
324  .io_clone = vlan_clone,
325  .io_put_attrs = vlan_put_attrs,
326  .io_free = vlan_free,
327 };
328 
329 /** @cond SKIP */
330 #define IS_VLAN_LINK_ASSERT(link) \
331  if ((link)->l_info_ops != &vlan_info_ops) { \
332  APPBUG("Link is not a vlan link. set type \"vlan\" first."); \
333  return -NLE_OPNOTSUPP; \
334  }
335 /** @endcond */
336 
337 /**
338  * @name VLAN Object
339  * @{
340  */
341 
342 /**
343  * Check if link is a VLAN link
344  * @arg link Link object
345  *
346  * @return True if link is a VLAN link, otherwise false is returned.
347  */
348 int rtnl_link_is_vlan(struct rtnl_link *link)
349 {
350  return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vlan");
351 }
352 
353 /**
354  * Set VLAN ID
355  * @arg link Link object
356  * @arg id VLAN identifier
357  *
358  * @return 0 on success or a negative error code
359  */
360 int rtnl_link_vlan_set_id(struct rtnl_link *link, uint16_t id)
361 {
362  struct vlan_info *vi = link->l_info;
363 
364  IS_VLAN_LINK_ASSERT(link);
365 
366  vi->vi_vlan_id = id;
367  vi->vi_mask |= VLAN_HAS_ID;
368 
369  return 0;
370 }
371 
372 /**
373  * Get VLAN Id
374  * @arg link Link object
375  *
376  * @return VLAN id, 0 if not set or a negative error code.
377  */
379 {
380  struct vlan_info *vi = link->l_info;
381 
382  IS_VLAN_LINK_ASSERT(link);
383 
384  if (vi->vi_mask & VLAN_HAS_ID)
385  return vi->vi_vlan_id;
386  else
387  return 0;
388 }
389 
390 /**
391  * Set VLAN flags
392  * @arg link Link object
393  * @arg flags VLAN flags
394  *
395  * @return 0 on success or a negative error code.
396  */
397 int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
398 {
399  struct vlan_info *vi = link->l_info;
400 
401  IS_VLAN_LINK_ASSERT(link);
402 
403  vi->vi_flags_mask |= flags;
404  vi->vi_flags |= flags;
405  vi->vi_mask |= VLAN_HAS_FLAGS;
406 
407  return 0;
408 }
409 
410 /**
411  * Unset VLAN flags
412  * @arg link Link object
413  * @arg flags VLAN flags
414  *
415  * @return 0 on success or a negative error code.
416  */
417 int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
418 {
419  struct vlan_info *vi = link->l_info;
420 
421  IS_VLAN_LINK_ASSERT(link);
422 
423  vi->vi_flags_mask |= flags;
424  vi->vi_flags &= ~flags;
425  vi->vi_mask |= VLAN_HAS_FLAGS;
426 
427  return 0;
428 }
429 
430 /**
431  * Get VLAN flags
432  * @arg link Link object
433  *
434  * @return VLAN flags, 0 if none set, or a negative error code.
435  */
437 {
438  struct vlan_info *vi = link->l_info;
439 
440  IS_VLAN_LINK_ASSERT(link);
441 
442  return vi->vi_flags;
443 }
444 
445 /** @} */
446 
447 /**
448  * @name Quality of Service
449  * @{
450  */
451 
452 int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
453  uint32_t to)
454 {
455  struct vlan_info *vi = link->l_info;
456 
457  IS_VLAN_LINK_ASSERT(link);
458 
459  if (from < 0 || from > VLAN_PRIO_MAX)
460  return -NLE_INVAL;
461 
462  vi->vi_ingress_qos[from] = to;
463  vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
464 
465  return 0;
466 }
467 
468 uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link)
469 {
470  struct vlan_info *vi = link->l_info;
471 
472  if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
473  return NULL;
474 
475  if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
476  return vi->vi_ingress_qos;
477  else
478  return NULL;
479 }
480 
481 int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to)
482 {
483  struct vlan_info *vi = link->l_info;
484 
485  if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
486  return -NLE_OPNOTSUPP;
487 
488  if (to < 0 || to > VLAN_PRIO_MAX)
489  return -NLE_INVAL;
490 
491  if (vi->vi_negress >= vi->vi_egress_size) {
492  int new_size = vi->vi_egress_size + 32;
493  void *ptr;
494 
495  ptr = realloc(vi->vi_egress_qos, new_size);
496  if (!ptr)
497  return -NLE_NOMEM;
498 
499  vi->vi_egress_qos = ptr;
500  vi->vi_egress_size = new_size;
501  }
502 
503  vi->vi_egress_qos[vi->vi_negress].vm_from = from;
504  vi->vi_egress_qos[vi->vi_negress].vm_to = to;
505  vi->vi_negress++;
506  vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
507 
508  return 0;
509 }
510 
511 struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
512  int *negress)
513 {
514  struct vlan_info *vi = link->l_info;
515 
516  if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
517  return NULL;
518 
519  if (negress == NULL)
520  return NULL;
521 
522  if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
523  *negress = vi->vi_negress;
524  return vi->vi_egress_qos;
525  } else {
526  *negress = 0;
527  return NULL;
528  }
529 }
530 
531 /** @} */
532 
533 static const struct trans_tbl vlan_flags[] = {
534  __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
535 };
536 
537 /**
538  * @name Flag Translation
539  * @{
540  */
541 
542 char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
543 {
544  return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
545 }
546 
547 int rtnl_link_vlan_str2flags(const char *name)
548 {
549  return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
550 }
551 
552 /** @} */
553 
554 
555 static void __init vlan_init(void)
556 {
557  rtnl_link_register_info(&vlan_info_ops);
558 }
559 
560 static void __exit vlan_exit(void)
561 {
562  rtnl_link_unregister_info(&vlan_info_ops);
563 }
564 
565 /** @} */