1efa5356bSRoopa Prabhu /*
2efa5356bSRoopa Prabhu  *	Bridge per vlan tunnel port dst_metadata netlink control interface
3efa5356bSRoopa Prabhu  *
4efa5356bSRoopa Prabhu  *	Authors:
5efa5356bSRoopa Prabhu  *	Roopa Prabhu		<roopa@cumulusnetworks.com>
6efa5356bSRoopa Prabhu  *
7efa5356bSRoopa Prabhu  *	This program is free software; you can redistribute it and/or
8efa5356bSRoopa Prabhu  *	modify it under the terms of the GNU General Public License
9efa5356bSRoopa Prabhu  *	as published by the Free Software Foundation; either version
10efa5356bSRoopa Prabhu  *	2 of the License, or (at your option) any later version.
11efa5356bSRoopa Prabhu  */
12efa5356bSRoopa Prabhu 
13efa5356bSRoopa Prabhu #include <linux/kernel.h>
14efa5356bSRoopa Prabhu #include <linux/slab.h>
15efa5356bSRoopa Prabhu #include <linux/etherdevice.h>
16efa5356bSRoopa Prabhu #include <net/rtnetlink.h>
17efa5356bSRoopa Prabhu #include <net/net_namespace.h>
18efa5356bSRoopa Prabhu #include <net/sock.h>
19efa5356bSRoopa Prabhu #include <uapi/linux/if_bridge.h>
20efa5356bSRoopa Prabhu #include <net/dst_metadata.h>
21efa5356bSRoopa Prabhu 
22efa5356bSRoopa Prabhu #include "br_private.h"
23efa5356bSRoopa Prabhu #include "br_private_tunnel.h"
24efa5356bSRoopa Prabhu 
25efa5356bSRoopa Prabhu static size_t __get_vlan_tinfo_size(void)
26efa5356bSRoopa Prabhu {
27efa5356bSRoopa Prabhu 	return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
28efa5356bSRoopa Prabhu 		  nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
29efa5356bSRoopa Prabhu 		  nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
30efa5356bSRoopa Prabhu 		  nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
31efa5356bSRoopa Prabhu }
32efa5356bSRoopa Prabhu 
33efa5356bSRoopa Prabhu static bool vlan_tunnel_id_isrange(struct net_bridge_vlan *v,
34efa5356bSRoopa Prabhu 				   struct net_bridge_vlan *v_end)
35efa5356bSRoopa Prabhu {
36efa5356bSRoopa Prabhu 	__be32 tunid_curr = tunnel_id_to_key32(v->tinfo.tunnel_id);
37efa5356bSRoopa Prabhu 	__be32 tunid_end = tunnel_id_to_key32(v_end->tinfo.tunnel_id);
38efa5356bSRoopa Prabhu 
39efa5356bSRoopa Prabhu 	return (be32_to_cpu(tunid_curr) - be32_to_cpu(tunid_end)) == 1;
40efa5356bSRoopa Prabhu }
41efa5356bSRoopa Prabhu 
42efa5356bSRoopa Prabhu static int __get_num_vlan_tunnel_infos(struct net_bridge_vlan_group *vg)
43efa5356bSRoopa Prabhu {
44efa5356bSRoopa Prabhu 	struct net_bridge_vlan *v, *v_start = NULL, *v_end = NULL;
45efa5356bSRoopa Prabhu 	int num_tinfos = 0;
46efa5356bSRoopa Prabhu 
47efa5356bSRoopa Prabhu 	/* Count number of vlan infos */
48efa5356bSRoopa Prabhu 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
49efa5356bSRoopa Prabhu 		/* only a context, bridge vlan not activated */
50efa5356bSRoopa Prabhu 		if (!br_vlan_should_use(v) || !v->tinfo.tunnel_id)
51efa5356bSRoopa Prabhu 			continue;
52efa5356bSRoopa Prabhu 
53efa5356bSRoopa Prabhu 		if (!v_start) {
54efa5356bSRoopa Prabhu 			goto initvars;
55efa5356bSRoopa Prabhu 		} else if ((v->vid - v_end->vid) == 1 &&
56efa5356bSRoopa Prabhu 			   vlan_tunnel_id_isrange(v_end, v) == 1) {
57efa5356bSRoopa Prabhu 			v_end = v;
58efa5356bSRoopa Prabhu 			continue;
59efa5356bSRoopa Prabhu 		} else {
60efa5356bSRoopa Prabhu 			if ((v_end->vid - v->vid) > 0 &&
61efa5356bSRoopa Prabhu 			    vlan_tunnel_id_isrange(v_end, v) > 0)
62efa5356bSRoopa Prabhu 				num_tinfos += 2;
63efa5356bSRoopa Prabhu 			else
64efa5356bSRoopa Prabhu 				num_tinfos += 1;
65efa5356bSRoopa Prabhu 		}
66efa5356bSRoopa Prabhu initvars:
67efa5356bSRoopa Prabhu 		v_start = v;
68efa5356bSRoopa Prabhu 		v_end = v;
69efa5356bSRoopa Prabhu 	}
70efa5356bSRoopa Prabhu 
71efa5356bSRoopa Prabhu 	if (v_start) {
72efa5356bSRoopa Prabhu 		if ((v_end->vid - v->vid) > 0 &&
73efa5356bSRoopa Prabhu 		    vlan_tunnel_id_isrange(v_end, v) > 0)
74efa5356bSRoopa Prabhu 			num_tinfos += 2;
75efa5356bSRoopa Prabhu 		else
76efa5356bSRoopa Prabhu 			num_tinfos += 1;
77efa5356bSRoopa Prabhu 	}
78efa5356bSRoopa Prabhu 
79efa5356bSRoopa Prabhu 	return num_tinfos;
80efa5356bSRoopa Prabhu }
81efa5356bSRoopa Prabhu 
82efa5356bSRoopa Prabhu int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg)
83efa5356bSRoopa Prabhu {
84efa5356bSRoopa Prabhu 	int num_tinfos;
85efa5356bSRoopa Prabhu 
86efa5356bSRoopa Prabhu 	if (!vg)
87efa5356bSRoopa Prabhu 		return 0;
88efa5356bSRoopa Prabhu 
89efa5356bSRoopa Prabhu 	rcu_read_lock();
90efa5356bSRoopa Prabhu 	num_tinfos = __get_num_vlan_tunnel_infos(vg);
91efa5356bSRoopa Prabhu 	rcu_read_unlock();
92efa5356bSRoopa Prabhu 
93efa5356bSRoopa Prabhu 	return num_tinfos * __get_vlan_tinfo_size();
94efa5356bSRoopa Prabhu }
95efa5356bSRoopa Prabhu 
96efa5356bSRoopa Prabhu static int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
97efa5356bSRoopa Prabhu 			      __be64 tunnel_id, u16 flags)
98efa5356bSRoopa Prabhu {
99efa5356bSRoopa Prabhu 	__be32 tid = tunnel_id_to_key32(tunnel_id);
100efa5356bSRoopa Prabhu 	struct nlattr *tmap;
101efa5356bSRoopa Prabhu 
102efa5356bSRoopa Prabhu 	tmap = nla_nest_start(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
103efa5356bSRoopa Prabhu 	if (!tmap)
104efa5356bSRoopa Prabhu 		return -EMSGSIZE;
105efa5356bSRoopa Prabhu 	if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
106efa5356bSRoopa Prabhu 			be32_to_cpu(tid)))
107efa5356bSRoopa Prabhu 		goto nla_put_failure;
108efa5356bSRoopa Prabhu 	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
109efa5356bSRoopa Prabhu 			vid))
110efa5356bSRoopa Prabhu 		goto nla_put_failure;
111efa5356bSRoopa Prabhu 	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
112efa5356bSRoopa Prabhu 			flags))
113efa5356bSRoopa Prabhu 		goto nla_put_failure;
114efa5356bSRoopa Prabhu 	nla_nest_end(skb, tmap);
115efa5356bSRoopa Prabhu 
116efa5356bSRoopa Prabhu 	return 0;
117efa5356bSRoopa Prabhu 
118efa5356bSRoopa Prabhu nla_put_failure:
119efa5356bSRoopa Prabhu 	nla_nest_cancel(skb, tmap);
120efa5356bSRoopa Prabhu 
121efa5356bSRoopa Prabhu 	return -EMSGSIZE;
122efa5356bSRoopa Prabhu }
123efa5356bSRoopa Prabhu 
124efa5356bSRoopa Prabhu static int br_fill_vlan_tinfo_range(struct sk_buff *skb,
125efa5356bSRoopa Prabhu 				    struct net_bridge_vlan *vtbegin,
126efa5356bSRoopa Prabhu 				    struct net_bridge_vlan *vtend)
127efa5356bSRoopa Prabhu {
128efa5356bSRoopa Prabhu 	int err;
129efa5356bSRoopa Prabhu 
130efa5356bSRoopa Prabhu 	if (vtbegin && vtend && (vtend->vid - vtbegin->vid) > 0) {
131efa5356bSRoopa Prabhu 		/* add range to skb */
132efa5356bSRoopa Prabhu 		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
133efa5356bSRoopa Prabhu 					 vtbegin->tinfo.tunnel_id,
134efa5356bSRoopa Prabhu 					 BRIDGE_VLAN_INFO_RANGE_BEGIN);
135efa5356bSRoopa Prabhu 		if (err)
136efa5356bSRoopa Prabhu 			return err;
137efa5356bSRoopa Prabhu 
138efa5356bSRoopa Prabhu 		err = br_fill_vlan_tinfo(skb, vtend->vid,
139efa5356bSRoopa Prabhu 					 vtend->tinfo.tunnel_id,
140efa5356bSRoopa Prabhu 					 BRIDGE_VLAN_INFO_RANGE_END);
141efa5356bSRoopa Prabhu 		if (err)
142efa5356bSRoopa Prabhu 			return err;
143efa5356bSRoopa Prabhu 	} else {
144efa5356bSRoopa Prabhu 		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
145efa5356bSRoopa Prabhu 					 vtbegin->tinfo.tunnel_id,
146efa5356bSRoopa Prabhu 					 0);
147efa5356bSRoopa Prabhu 		if (err)
148efa5356bSRoopa Prabhu 			return err;
149efa5356bSRoopa Prabhu 	}
150efa5356bSRoopa Prabhu 
151efa5356bSRoopa Prabhu 	return 0;
152efa5356bSRoopa Prabhu }
153efa5356bSRoopa Prabhu 
154efa5356bSRoopa Prabhu int br_fill_vlan_tunnel_info(struct sk_buff *skb,
155efa5356bSRoopa Prabhu 			     struct net_bridge_vlan_group *vg)
156efa5356bSRoopa Prabhu {
157efa5356bSRoopa Prabhu 	struct net_bridge_vlan *vtbegin = NULL;
158efa5356bSRoopa Prabhu 	struct net_bridge_vlan *vtend = NULL;
159efa5356bSRoopa Prabhu 	struct net_bridge_vlan *v;
160efa5356bSRoopa Prabhu 	int err;
161efa5356bSRoopa Prabhu 
162efa5356bSRoopa Prabhu 	/* Count number of vlan infos */
163efa5356bSRoopa Prabhu 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
164efa5356bSRoopa Prabhu 		/* only a context, bridge vlan not activated */
165efa5356bSRoopa Prabhu 		if (!br_vlan_should_use(v))
166efa5356bSRoopa Prabhu 			continue;
167efa5356bSRoopa Prabhu 
168efa5356bSRoopa Prabhu 		if (!v->tinfo.tunnel_dst)
169efa5356bSRoopa Prabhu 			continue;
170efa5356bSRoopa Prabhu 
171efa5356bSRoopa Prabhu 		if (!vtbegin) {
172efa5356bSRoopa Prabhu 			goto initvars;
173efa5356bSRoopa Prabhu 		} else if ((v->vid - vtend->vid) == 1 &&
174efa5356bSRoopa Prabhu 			    vlan_tunnel_id_isrange(v, vtend)) {
175efa5356bSRoopa Prabhu 			vtend = v;
176efa5356bSRoopa Prabhu 			continue;
177efa5356bSRoopa Prabhu 		} else {
178efa5356bSRoopa Prabhu 			err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
179efa5356bSRoopa Prabhu 			if (err)
180efa5356bSRoopa Prabhu 				return err;
181efa5356bSRoopa Prabhu 		}
182efa5356bSRoopa Prabhu initvars:
183efa5356bSRoopa Prabhu 		vtbegin = v;
184efa5356bSRoopa Prabhu 		vtend = v;
185efa5356bSRoopa Prabhu 	}
186efa5356bSRoopa Prabhu 
187efa5356bSRoopa Prabhu 	if (vtbegin) {
188efa5356bSRoopa Prabhu 		err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
189efa5356bSRoopa Prabhu 		if (err)
190efa5356bSRoopa Prabhu 			return err;
191efa5356bSRoopa Prabhu 	}
192efa5356bSRoopa Prabhu 
193efa5356bSRoopa Prabhu 	return 0;
194efa5356bSRoopa Prabhu }
195efa5356bSRoopa Prabhu 
196efa5356bSRoopa Prabhu static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
197efa5356bSRoopa Prabhu 	[IFLA_BRIDGE_VLAN_TUNNEL_ID] = { .type = NLA_U32 },
198efa5356bSRoopa Prabhu 	[IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
199efa5356bSRoopa Prabhu 	[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
200efa5356bSRoopa Prabhu };
201efa5356bSRoopa Prabhu 
202efa5356bSRoopa Prabhu static int br_vlan_tunnel_info(struct net_bridge_port *p, int cmd,
203efa5356bSRoopa Prabhu 			       u16 vid, u32 tun_id)
204efa5356bSRoopa Prabhu {
205efa5356bSRoopa Prabhu 	int err = 0;
206efa5356bSRoopa Prabhu 
207efa5356bSRoopa Prabhu 	if (!p)
208efa5356bSRoopa Prabhu 		return -EINVAL;
209efa5356bSRoopa Prabhu 
210efa5356bSRoopa Prabhu 	switch (cmd) {
211efa5356bSRoopa Prabhu 	case RTM_SETLINK:
212efa5356bSRoopa Prabhu 		err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
213efa5356bSRoopa Prabhu 		break;
214efa5356bSRoopa Prabhu 	case RTM_DELLINK:
215efa5356bSRoopa Prabhu 		nbp_vlan_tunnel_info_delete(p, vid);
216efa5356bSRoopa Prabhu 		break;
217efa5356bSRoopa Prabhu 	}
218efa5356bSRoopa Prabhu 
219efa5356bSRoopa Prabhu 	return err;
220efa5356bSRoopa Prabhu }
221efa5356bSRoopa Prabhu 
222efa5356bSRoopa Prabhu int br_parse_vlan_tunnel_info(struct nlattr *attr,
223efa5356bSRoopa Prabhu 			      struct vtunnel_info *tinfo)
224efa5356bSRoopa Prabhu {
225efa5356bSRoopa Prabhu 	struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
226efa5356bSRoopa Prabhu 	u32 tun_id;
227efa5356bSRoopa Prabhu 	u16 vid, flags = 0;
228efa5356bSRoopa Prabhu 	int err;
229efa5356bSRoopa Prabhu 
230efa5356bSRoopa Prabhu 	memset(tinfo, 0, sizeof(*tinfo));
231efa5356bSRoopa Prabhu 
232efa5356bSRoopa Prabhu 	if (!tb[IFLA_BRIDGE_VLAN_TUNNEL_ID] ||
233efa5356bSRoopa Prabhu 	    !tb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
234efa5356bSRoopa Prabhu 		return -EINVAL;
235efa5356bSRoopa Prabhu 
236efa5356bSRoopa Prabhu 	err = nla_parse_nested(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
237efa5356bSRoopa Prabhu 			       attr, vlan_tunnel_policy);
238efa5356bSRoopa Prabhu 	if (err < 0)
239efa5356bSRoopa Prabhu 		return err;
240efa5356bSRoopa Prabhu 
241efa5356bSRoopa Prabhu 	tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
242efa5356bSRoopa Prabhu 	vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
243efa5356bSRoopa Prabhu 	if (vid >= VLAN_VID_MASK)
244efa5356bSRoopa Prabhu 		return -ERANGE;
245efa5356bSRoopa Prabhu 
246efa5356bSRoopa Prabhu 	if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
247efa5356bSRoopa Prabhu 		flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
248efa5356bSRoopa Prabhu 
249efa5356bSRoopa Prabhu 	tinfo->tunid = tun_id;
250efa5356bSRoopa Prabhu 	tinfo->vid = vid;
251efa5356bSRoopa Prabhu 	tinfo->flags = flags;
252efa5356bSRoopa Prabhu 
253efa5356bSRoopa Prabhu 	return 0;
254efa5356bSRoopa Prabhu }
255efa5356bSRoopa Prabhu 
256efa5356bSRoopa Prabhu int br_process_vlan_tunnel_info(struct net_bridge *br,
257efa5356bSRoopa Prabhu 				struct net_bridge_port *p, int cmd,
258efa5356bSRoopa Prabhu 				struct vtunnel_info *tinfo_curr,
259efa5356bSRoopa Prabhu 				struct vtunnel_info *tinfo_last)
260efa5356bSRoopa Prabhu {
261efa5356bSRoopa Prabhu 	int err;
262efa5356bSRoopa Prabhu 
263efa5356bSRoopa Prabhu 	if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
264efa5356bSRoopa Prabhu 		if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
265efa5356bSRoopa Prabhu 			return -EINVAL;
266efa5356bSRoopa Prabhu 		memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
267efa5356bSRoopa Prabhu 	} else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
268efa5356bSRoopa Prabhu 		int t, v;
269efa5356bSRoopa Prabhu 
270efa5356bSRoopa Prabhu 		if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
271efa5356bSRoopa Prabhu 			return -EINVAL;
272efa5356bSRoopa Prabhu 		if ((tinfo_curr->vid - tinfo_last->vid) !=
273efa5356bSRoopa Prabhu 		    (tinfo_curr->tunid - tinfo_last->tunid))
274efa5356bSRoopa Prabhu 			return -EINVAL;
275efa5356bSRoopa Prabhu 		t = tinfo_last->tunid;
276efa5356bSRoopa Prabhu 		for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
277efa5356bSRoopa Prabhu 			err = br_vlan_tunnel_info(p, cmd, v, t);
278efa5356bSRoopa Prabhu 			if (err)
279efa5356bSRoopa Prabhu 				return err;
280efa5356bSRoopa Prabhu 			t++;
281efa5356bSRoopa Prabhu 		}
282efa5356bSRoopa Prabhu 		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
283efa5356bSRoopa Prabhu 		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
284efa5356bSRoopa Prabhu 	} else {
285efa5356bSRoopa Prabhu 		if (tinfo_last->flags)
286efa5356bSRoopa Prabhu 			return -EINVAL;
287efa5356bSRoopa Prabhu 		err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid,
288efa5356bSRoopa Prabhu 					  tinfo_curr->tunid);
289efa5356bSRoopa Prabhu 		if (err)
290efa5356bSRoopa Prabhu 			return err;
291efa5356bSRoopa Prabhu 		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
292efa5356bSRoopa Prabhu 		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
293efa5356bSRoopa Prabhu 	}
294efa5356bSRoopa Prabhu 
295efa5356bSRoopa Prabhu 	return 0;
296efa5356bSRoopa Prabhu }
297