1a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf/*
2a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf * lib/route/link/vlan.c	VLAN Link Info
3a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf *
4a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf *	This library is free software; you can redistribute it and/or
5a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf *	modify it under the terms of the GNU Lesser General Public
6a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf *	License as published by the Free Software Foundation version 2.1
7a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf *	of the License.
8a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf *
98a3efffa5b3fde252675239914118664d36a2c24Thomas Graf * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
10a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf */
11a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
12a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf/**
13a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf * @ingroup link_info
14a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf * @defgroup vlan VLAN
15a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf * @brief
16a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf *
17a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf * @{
18a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf */
19a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
20a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#include <netlink-local.h>
21a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#include <netlink/netlink.h>
22a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#include <netlink/attr.h>
23a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#include <netlink/utils.h>
24a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#include <netlink/object.h>
25a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#include <netlink/route/rtnl.h>
26a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#include <netlink/route/link/info-api.h>
27a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#include <netlink/route/link/vlan.h>
28a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
29a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#include <linux/if_vlan.h>
30a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
31a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf/** @cond SKIP */
32a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#define VLAN_HAS_ID		(1<<0)
33a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#define VLAN_HAS_FLAGS		(1<<1)
34a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#define VLAN_HAS_INGRESS_QOS	(1<<2)
35a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf#define VLAN_HAS_EGRESS_QOS	(1<<3)
36a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
37a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstruct vlan_info
38a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
39a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	uint16_t		vi_vlan_id;
40a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	uint32_t		vi_flags;
41a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	uint32_t		vi_flags_mask;
42a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	uint32_t		vi_ingress_qos[VLAN_PRIO_MAX+1];
43a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	uint32_t		vi_negress;
44a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	uint32_t		vi_egress_size;
45a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_map * 	vi_egress_qos;
46a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	uint32_t		vi_mask;
47a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf};
48a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf/** @endcond */
49a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
50a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic struct trans_tbl vlan_flags[] = {
51a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	__ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
52a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf};
53a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
54a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafchar *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
55a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
56a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
57a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
58a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
59a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafint rtnl_link_vlan_str2flags(const char *name)
60a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
61a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
62a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
63a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
64a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = {
65a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	[IFLA_VLAN_ID]		= { .type = NLA_U16 },
66a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	[IFLA_VLAN_FLAGS]	= { .minlen = sizeof(struct ifla_vlan_flags) },
67a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	[IFLA_VLAN_INGRESS_QOS]	= { .type = NLA_NESTED },
68a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	[IFLA_VLAN_EGRESS_QOS]	= { .type = NLA_NESTED },
69a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf};
70a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
71a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic int vlan_alloc(struct rtnl_link *link)
72a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
73a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi;
74a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
75a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if ((vi = calloc(1, sizeof(*vi))) == NULL)
768a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_NOMEM;
77a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
78a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	link->l_info = vi;
79a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
80a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return 0;
81a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
82a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
83a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic int vlan_parse(struct rtnl_link *link, struct nlattr *data,
84a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		      struct nlattr *xstats)
85a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
86a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct nlattr *tb[IFLA_VLAN_MAX+1];
87a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi;
88a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	int err;
89a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
90a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	NL_DBG(3, "Parsing VLAN link info");
91a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
92a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if ((err = nla_parse_nested(tb, IFLA_VLAN_MAX, data, vlan_policy)) < 0)
93a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		goto errout;
94a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
95a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if ((err = vlan_alloc(link)) < 0)
96a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		goto errout;
97a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
98a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi = link->l_info;
99a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
100a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (tb[IFLA_VLAN_ID]) {
101a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_vlan_id = nla_get_u16(tb[IFLA_VLAN_ID]);
102a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_mask |= VLAN_HAS_ID;
103a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
104a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
105a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (tb[IFLA_VLAN_FLAGS]) {
106a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct ifla_vlan_flags flags;
107a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags));
108a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
109a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_flags = flags.flags;
110a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_mask |= VLAN_HAS_FLAGS;
111a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
112a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
113a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (tb[IFLA_VLAN_INGRESS_QOS]) {
114a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct ifla_vlan_qos_mapping *map;
115a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct nlattr *nla;
116a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		int remaining;
117a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
118a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		memset(vi->vi_ingress_qos, 0, sizeof(vi->vi_ingress_qos));
119a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
120a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		nla_for_each_nested(nla, tb[IFLA_VLAN_INGRESS_QOS], remaining) {
121a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			if (nla_len(nla) < sizeof(*map))
1228a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				return -NLE_INVAL;
123a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
124a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			map = nla_data(nla);
125a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			if (map->from < 0 || map->from > VLAN_PRIO_MAX) {
1268a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				return -NLE_INVAL;
127a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			}
128a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
129a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			vi->vi_ingress_qos[map->from] = map->to;
130a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		}
131a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
132a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
133a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
134a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
135a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (tb[IFLA_VLAN_EGRESS_QOS]) {
136a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct ifla_vlan_qos_mapping *map;
137a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct nlattr *nla;
138a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		int remaining, i = 0;
139a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
140a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
141a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			if (nla_len(nla) < sizeof(*map))
1428a3efffa5b3fde252675239914118664d36a2c24Thomas Graf				return -NLE_INVAL;
143a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			i++;
144a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		}
145a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
146a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		/* align to have a little reserve */
147a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_egress_size = (i + 32) & ~31;
148a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map));
149a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		if (vi->vi_egress_qos == NULL)
1508a3efffa5b3fde252675239914118664d36a2c24Thomas Graf			return -NLE_NOMEM;
151a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
152a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		i = 0;
153a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		nla_for_each_nested(nla, tb[IFLA_VLAN_EGRESS_QOS], remaining) {
154a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			map = nla_data(nla);
155a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			NL_DBG(4, "Assigning egress qos mapping %d\n", i);
156a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			vi->vi_egress_qos[i].vm_from = map->from;
157a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			vi->vi_egress_qos[i++].vm_to = map->to;
158a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		}
159a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
160a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_negress = i;
161a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
162a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
163a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
164a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	err = 0;
165a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graferrout:
166a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return err;
167a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
168a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
169a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic void vlan_free(struct rtnl_link *link)
170a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
171a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
172a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
173a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi) {
174a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		free(vi->vi_egress_qos);
175a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_egress_qos = NULL;
176a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
177a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
178a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	free(vi);
179a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	link->l_info = NULL;
180a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
181a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
182d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Grafstatic void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
183a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
184a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
185a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
186d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf	nl_dump(p, "vlan-id %d", vi->vi_vlan_id);
187a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
188a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
189d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Grafstatic void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
190a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
191a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
192a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	int i, printed;
193a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	char buf[64];
194a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
195a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
196d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf	nl_dump_line(p, "    vlan-info id %d <%s>\n", vi->vi_vlan_id, buf);
197a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
198a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
199d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf		nl_dump_line(p,
200a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		"      ingress vlan prio -> qos/socket prio mapping:\n");
201a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		for (i = 0, printed = 0; i <= VLAN_PRIO_MAX; i++) {
202a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			if (vi->vi_ingress_qos[i]) {
203d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf				if (printed == 0)
204d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf					nl_dump_line(p, "      ");
205d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf				nl_dump(p, "%x -> %#08x, ",
206a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf					i, vi->vi_ingress_qos[i]);
207a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				if (printed++ == 3) {
208d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf					nl_dump(p, "\n");
209a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf					printed = 0;
210a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				}
211a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			}
212a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		}
213a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
214a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		if (printed > 0 && printed != 4)
215d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf			nl_dump(p, "\n");
216a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
217a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
218a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
219d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf		nl_dump_line(p,
220a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		"      egress qos/socket prio -> vlan prio mapping:\n");
221a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		for (i = 0, printed = 0; i < vi->vi_negress; i++) {
222d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf			if (printed == 0)
223d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf				nl_dump_line(p, "      ");
224d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf			nl_dump(p, "%#08x -> %x, ",
225a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				vi->vi_egress_qos[i].vm_from,
226a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				vi->vi_egress_qos[i].vm_to);
227a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			if (printed++ == 3) {
228d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf				nl_dump(p, "\n");
229a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				printed = 0;
230a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			}
231a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		}
232a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
233a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		if (printed > 0 && printed != 4)
234d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf			nl_dump(p, "\n");
235a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
236a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
237a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
238a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
239a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
240a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vdst, *vsrc = src->l_info;
241a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	int err;
242a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
243a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	dst->l_info = NULL;
244a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if ((err = rtnl_link_set_info_type(dst, "vlan")) < 0)
245a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return err;
246a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vdst = dst->l_info;
247a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
248a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vdst->vi_egress_qos = calloc(vsrc->vi_egress_size,
249a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				     sizeof(struct vlan_map));
250a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (!vdst->vi_egress_qos)
2518a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_NOMEM;
252a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
253a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	memcpy(vdst->vi_egress_qos, vsrc->vi_egress_qos,
254a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	       vsrc->vi_egress_size * sizeof(struct vlan_map));
255a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
256a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return 0;
257a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
258a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
259a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
260a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
261a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
262a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct nlattr *data;
263a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
264a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
2658a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_MSGSIZE;
266a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
267a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_mask & VLAN_HAS_ID)
268a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		NLA_PUT_U16(msg, IFLA_VLAN_ID, vi->vi_vlan_id);
269a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
270a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_mask & VLAN_HAS_FLAGS) {
271a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct ifla_vlan_flags flags = {
272a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			.flags = vi->vi_flags,
273a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			.mask = vi->vi_flags_mask,
274a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		};
275a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
276a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags);
277a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
278a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
279a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
280a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct ifla_vlan_qos_mapping map;
281a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct nlattr *qos;
282a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		int i;
283a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
284a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS)))
285a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			goto nla_put_failure;
286a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
287a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		for (i = 0; i <= VLAN_PRIO_MAX; i++) {
288a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			if (vi->vi_ingress_qos[i]) {
289a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				map.from = i;
290a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				map.to = vi->vi_ingress_qos[i];
291a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
292a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				NLA_PUT(msg, i, sizeof(map), &map);
293a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			}
294a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		}
295a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
296a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		nla_nest_end(msg, qos);
297a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
298a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
299a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
300a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct ifla_vlan_qos_mapping map;
301a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		struct nlattr *qos;
302a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		int i;
303a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
304a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
305a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			goto nla_put_failure;
306a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
307a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		for (i = 0; i < vi->vi_negress; i++) {
308a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			map.from = vi->vi_egress_qos[i].vm_from;
309a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			map.to = vi->vi_egress_qos[i].vm_to;
310a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
311a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf			NLA_PUT(msg, i, sizeof(map), &map);
312a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		}
313a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
314a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		nla_nest_end(msg, qos);
315a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
316a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
317a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	nla_nest_end(msg, data);
318a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
319a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafnla_put_failure:
320a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
321a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return 0;
322a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
323a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
324a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic struct rtnl_link_info_ops vlan_info_ops = {
325a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	.io_name		= "vlan",
326a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	.io_alloc		= vlan_alloc,
327a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	.io_parse		= vlan_parse,
328d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf	.io_dump = {
329d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf	    [NL_DUMP_LINE]	= vlan_dump_line,
330d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf	    [NL_DUMP_DETAILS]	= vlan_dump_details,
331d84430702496f617c01c5e2d27d0e82e02390bb7Thomas Graf	},
332a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	.io_clone		= vlan_clone,
333ddbe8f6c417ddba11f32e1f36bbfbdee8a598a36Thomas Graf	.io_put_attrs		= vlan_put_attrs,
334a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	.io_free		= vlan_free,
335a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf};
336a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
337a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafint rtnl_link_vlan_set_id(struct rtnl_link *link, int id)
338a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
339a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
340a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
341a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
3428a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_OPNOTSUPP;
343a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
344a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_vlan_id = id;
345a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_mask |= VLAN_HAS_ID;
346a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
347a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return 0;
348a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
349a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
350a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafint rtnl_link_vlan_get_id(struct rtnl_link *link)
351a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
352a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
353a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
354a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
3558a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_OPNOTSUPP;
356a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
357a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_mask & VLAN_HAS_ID)
358a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return vi->vi_vlan_id;
359a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	else
360a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return 0;
361a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
362a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
363a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafint rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
364a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
365a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
366a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
367a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
3688a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_OPNOTSUPP;
369a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
370a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_flags_mask |= flags;
371a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_flags |= flags;
372a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_mask |= VLAN_HAS_FLAGS;
373a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
374a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return 0;
375a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
376a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
377a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafint rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
378a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
379a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
380a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
381a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
3828a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_OPNOTSUPP;
383a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
384a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_flags_mask |= flags;
385a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_flags &= ~flags;
386a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_mask |= VLAN_HAS_FLAGS;
387a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
388a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return 0;
389a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
390a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
391a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafunsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link)
392a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
393a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
394a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
395a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
3968a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_OPNOTSUPP;
397a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
398a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return vi->vi_flags;
399a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
400a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
401a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafint rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
402a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf				   uint32_t to)
403a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
404a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
405a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
406a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
4078a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_OPNOTSUPP;
408a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
409a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (from < 0 || from > VLAN_PRIO_MAX)
4108a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_INVAL;
411a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
412a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_ingress_qos[from] = to;
413a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_mask |= VLAN_HAS_INGRESS_QOS;
414a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
415a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return 0;
416a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
417a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
418a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafuint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *link)
419a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
420a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
421a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
4228a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
423a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return NULL;
424a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
425a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_mask & VLAN_HAS_INGRESS_QOS)
426a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return vi->vi_ingress_qos;
427a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	else
428a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return NULL;
429a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
430a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
431a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafint rtnl_link_vlan_set_egress_map(struct rtnl_link *link, uint32_t from, int to)
432a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
433a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
434a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
435a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
4368a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_OPNOTSUPP;
437a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
438a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (to < 0 || to > VLAN_PRIO_MAX)
4398a3efffa5b3fde252675239914118664d36a2c24Thomas Graf		return -NLE_INVAL;
440a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
441a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_negress >= vi->vi_egress_size) {
442a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		int new_size = vi->vi_egress_size + 32;
443a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		void *ptr;
444a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
445a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		ptr = realloc(vi->vi_egress_qos, new_size);
446a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		if (!ptr)
4478a3efffa5b3fde252675239914118664d36a2c24Thomas Graf			return -NLE_NOMEM;
448a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
449a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_egress_qos = ptr;
450a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		vi->vi_egress_size = new_size;
451a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
452a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
453a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_egress_qos[vi->vi_negress].vm_from = from;
454a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_egress_qos[vi->vi_negress].vm_to = to;
455a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_negress++;
456a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	vi->vi_mask |= VLAN_HAS_EGRESS_QOS;
457a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
458a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	return 0;
459a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
460a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
461a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstruct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
462a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf					       int *negress)
463a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
464a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	struct vlan_info *vi = link->l_info;
465a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
4668a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
467a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return NULL;
468a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
4698a3efffa5b3fde252675239914118664d36a2c24Thomas Graf	if (negress == NULL)
470a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return NULL;
471a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
472a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
473a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		*negress = vi->vi_negress;
474a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return vi->vi_egress_qos;
475a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	} else {
476a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		*negress = 0;
477a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf		return NULL;
478a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	}
479a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
480a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
481a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic void __init vlan_init(void)
482a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
483a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	rtnl_link_register_info(&vlan_info_ops);
484a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
485a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
486a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Grafstatic void __exit vlan_exit(void)
487a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf{
488a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf	rtnl_link_unregister_info(&vlan_info_ops);
489a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf}
490a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf
491a7469ce758fac3631df6ce72eb3f89150070e7f8Thomas Graf/** @} */
492