• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
rtnl_link_vlan_flags2str(int flags,char * buf,size_t len)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  
rtnl_link_vlan_str2flags(const char * name)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  
vlan_alloc(struct rtnl_link * link)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  
vlan_parse(struct rtnl_link * link,struct nlattr * data,struct nlattr * xstats)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  
vlan_free(struct rtnl_link * link)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  
vlan_dump_line(struct rtnl_link * link,struct nl_dump_params * p)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  
vlan_dump_details(struct rtnl_link * link,struct nl_dump_params * p)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  
vlan_clone(struct rtnl_link * dst,struct rtnl_link * src)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  
vlan_put_attrs(struct nl_msg * msg,struct rtnl_link * link)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  
rtnl_link_vlan_set_id(struct rtnl_link * link,int id)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  
rtnl_link_vlan_get_id(struct rtnl_link * link)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  
rtnl_link_vlan_set_flags(struct rtnl_link * link,unsigned int flags)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  
rtnl_link_vlan_unset_flags(struct rtnl_link * link,unsigned int flags)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  
rtnl_link_vlan_get_flags(struct rtnl_link * link)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  
rtnl_link_vlan_set_ingress_map(struct rtnl_link * link,int from,uint32_t to)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  
rtnl_link_vlan_get_ingress_map(struct rtnl_link * link)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  
rtnl_link_vlan_set_egress_map(struct rtnl_link * link,uint32_t from,int to)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  
rtnl_link_vlan_get_egress_map(struct rtnl_link * link,int * negress)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  
vlan_init(void)481  static void __init vlan_init(void)
482  {
483  	rtnl_link_register_info(&vlan_info_ops);
484  }
485  
vlan_exit(void)486  static void __exit vlan_exit(void)
487  {
488  	rtnl_link_unregister_info(&vlan_info_ops);
489  }
490  
491  /** @} */
492