libnl  1.1
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 
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 
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 
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 
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 nl_errno(ENOMEM);
77 
78  link->l_info = vi;
79 
80  return 0;
81 }
82 
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 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;
166 errout:
167  return err;
168 }
169 
170 static 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 
183 static 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 
193 static 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 
250 static 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 
271 static 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 
331 nla_put_failure:
332 
333  return 0;
334 }
335 
336 static 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 
347 int 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 
360 int 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 
373 int 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 
387 int 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 
401 unsigned 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 
411 int 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 
429 uint32_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 
444 int 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 
475 struct 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 
499 static void __init vlan_init(void)
500 {
501  rtnl_link_register_info(&vlan_info_ops);
502 }
503 
504 static void __exit vlan_exit(void)
505 {
506  rtnl_link_unregister_info(&vlan_info_ops);
507 }
508 
509 /** @} */