Tizen 2.1 base
[platform/upstream/libnl2.git] / lib / route / link / 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-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
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 -NLE_NOMEM;
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 -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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
481 static void __init vlan_init(void)
482 {
483         rtnl_link_register_info(&vlan_info_ops);
484 }
485
486 static void __exit vlan_exit(void)
487 {
488         rtnl_link_unregister_info(&vlan_info_ops);
489 }
490
491 /** @} */