libnl  3.2.11
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 > 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  * Check if link is a VLAN link
345  * @arg link Link object
346  *
347  * @return True if link is a VLAN link, otherwise false is returned.
348  */
349 int rtnl_link_is_vlan(struct rtnl_link *link)
350 {
351  return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vlan");
352 }
353 
354 /**
355  * Set VLAN ID
356  * @arg link Link object
357  * @arg id VLAN identifier
358  *
359  * @return 0 on success or a negative error code
360  */
361 int rtnl_link_vlan_set_id(struct rtnl_link *link, uint16_t id)
362 {
363  struct vlan_info *vi = link->l_info;
364 
365  IS_VLAN_LINK_ASSERT(link);
366 
367  vi->vi_vlan_id = id;
368  vi->vi_mask |= VLAN_HAS_ID;
369 
370  return 0;
371 }
372 
373 /**
374  * Get VLAN Id
375  * @arg link Link object
376  *
377  * @return VLAN id, 0 if not set or a negative error code.
378  */
380 {
381  struct vlan_info *vi = link->l_info;
382 
383  IS_VLAN_LINK_ASSERT(link);
384 
385  if (vi->vi_mask & VLAN_HAS_ID)
386  return vi->vi_vlan_id;
387  else
388  return 0;
389 }
390 
391 /**
392  * Set VLAN flags
393  * @arg link Link object
394  * @arg flags VLAN flags
395  *
396  * @return 0 on success or a negative error code.
397  */
398 int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
399 {
400  struct vlan_info *vi = link->l_info;
401 
402  IS_VLAN_LINK_ASSERT(link);
403 
404  vi->vi_flags_mask |= flags;
405  vi->vi_flags |= flags;
406  vi->vi_mask |= VLAN_HAS_FLAGS;
407 
408  return 0;
409 }
410 
411 /**
412  * Unset VLAN flags
413  * @arg link Link object
414  * @arg flags VLAN flags
415  *
416  * @return 0 on success or a negative error code.
417  */
418 int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
419 {
420  struct vlan_info *vi = link->l_info;
421 
422  IS_VLAN_LINK_ASSERT(link);
423 
424  vi->vi_flags_mask |= flags;
425  vi->vi_flags &= ~flags;
426  vi->vi_mask |= VLAN_HAS_FLAGS;
427 
428  return 0;
429 }
430 
431 /**
432  * Get VLAN flags
433  * @arg link Link object
434  *
435  * @return VLAN flags, 0 if none set, or a negative error code.
436  */
438 {
439  struct vlan_info *vi = link->l_info;
440 
441  IS_VLAN_LINK_ASSERT(link);
442 
443  return vi->vi_flags;
444 }
445 
446 /** @} */
447 
448 /**
449  * @name Quality of Service
450  * @{
451  */
452 
453 int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
454  uint32_t to)
455 {
456  struct vlan_info *vi = link->l_info;
457 
458  IS_VLAN_LINK_ASSERT(link);
459 
460  if (from < 0 || from > VLAN_PRIO_MAX)
461  return -NLE_INVAL;
462 
463  vi->vi_ingress_qos[from] = to;
464  vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
465 
466  return 0;
467 }
468 
469 uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link)
470 {
471  struct vlan_info *vi = link->l_info;
472 
473  if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
474  return NULL;
475 
476  if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
477  return vi->vi_ingress_qos;
478  else
479  return NULL;
480 }
481 
482 int rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to)
483 {
484  struct vlan_info *vi = link->l_info;
485 
486  if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
487  return -NLE_OPNOTSUPP;
488 
489  if (to < 0 || to > VLAN_PRIO_MAX)
490  return -NLE_INVAL;
491 
492  if (vi->vi_negress >= vi->vi_egress_size) {
493  int new_size = vi->vi_egress_size + 32;
494  void *ptr;
495 
496  ptr = realloc(vi->vi_egress_qos, new_size);
497  if (!ptr)
498  return -NLE_NOMEM;
499 
500  vi->vi_egress_qos = ptr;
501  vi->vi_egress_size = new_size;
502  }
503 
504  vi->vi_egress_qos[vi->vi_negress].vm_from = from;
505  vi->vi_egress_qos[vi->vi_negress].vm_to = to;
506  vi->vi_negress++;
507  vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
508 
509  return 0;
510 }
511 
512 struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
513  int *negress)
514 {
515  struct vlan_info *vi = link->l_info;
516 
517  if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
518  return NULL;
519 
520  if (negress == NULL)
521  return NULL;
522 
523  if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
524  *negress = vi->vi_negress;
525  return vi->vi_egress_qos;
526  } else {
527  *negress = 0;
528  return NULL;
529  }
530 }
531 
532 /** @} */
533 
534 static const struct trans_tbl vlan_flags[] = {
535  __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
536 };
537 
538 /**
539  * @name Flag Translation
540  * @{
541  */
542 
543 char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
544 {
545  return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
546 }
547 
548 int rtnl_link_vlan_str2flags(const char *name)
549 {
550  return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
551 }
552 
553 /** @} */
554 
555 
556 static void __init vlan_init(void)
557 {
558  rtnl_link_register_info(&vlan_info_ops);
559 }
560 
561 static void __exit vlan_exit(void)
562 {
563  rtnl_link_unregister_info(&vlan_info_ops);
564 }
565 
566 /** @} */