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