xref: /linux/net/core/lwtunnel.c (revision 1fc31357ad194fb98691f3d122bcd47e59239e83)
1 /*
2  * lwtunnel	Infrastructure for light weight tunnels like mpls
3  *
4  * Authors:	Roopa Prabhu, <roopa@cumulusnetworks.com>
5  *
6  *		This program is free software; you can redistribute it and/or
7  *		modify it under the terms of the GNU General Public License
8  *		as published by the Free Software Foundation; either version
9  *		2 of the License, or (at your option) any later version.
10  *
11  */
12 
13 #include <linux/capability.h>
14 #include <linux/module.h>
15 #include <linux/types.h>
16 #include <linux/kernel.h>
17 #include <linux/slab.h>
18 #include <linux/uaccess.h>
19 #include <linux/skbuff.h>
20 #include <linux/netdevice.h>
21 #include <linux/lwtunnel.h>
22 #include <linux/in.h>
23 #include <linux/init.h>
24 #include <linux/err.h>
25 
26 #include <net/lwtunnel.h>
27 #include <net/rtnetlink.h>
28 #include <net/ip6_fib.h>
29 
30 #ifdef CONFIG_MODULES
31 
32 static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
33 {
34 	/* Only lwt encaps implemented without using an interface for
35 	 * the encap need to return a string here.
36 	 */
37 	switch (encap_type) {
38 	case LWTUNNEL_ENCAP_MPLS:
39 		return "MPLS";
40 	case LWTUNNEL_ENCAP_ILA:
41 		return "ILA";
42 	case LWTUNNEL_ENCAP_SEG6:
43 		return "SEG6";
44 	case LWTUNNEL_ENCAP_IP6:
45 	case LWTUNNEL_ENCAP_IP:
46 	case LWTUNNEL_ENCAP_NONE:
47 	case __LWTUNNEL_ENCAP_MAX:
48 		/* should not have got here */
49 		WARN_ON(1);
50 		break;
51 	}
52 	return NULL;
53 }
54 
55 #endif /* CONFIG_MODULES */
56 
57 struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
58 {
59 	struct lwtunnel_state *lws;
60 
61 	lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC);
62 
63 	return lws;
64 }
65 EXPORT_SYMBOL(lwtunnel_state_alloc);
66 
67 static const struct lwtunnel_encap_ops __rcu *
68 		lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
69 
70 int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
71 			   unsigned int num)
72 {
73 	if (num > LWTUNNEL_ENCAP_MAX)
74 		return -ERANGE;
75 
76 	return !cmpxchg((const struct lwtunnel_encap_ops **)
77 			&lwtun_encaps[num],
78 			NULL, ops) ? 0 : -1;
79 }
80 EXPORT_SYMBOL(lwtunnel_encap_add_ops);
81 
82 int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops,
83 			   unsigned int encap_type)
84 {
85 	int ret;
86 
87 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
88 	    encap_type > LWTUNNEL_ENCAP_MAX)
89 		return -ERANGE;
90 
91 	ret = (cmpxchg((const struct lwtunnel_encap_ops **)
92 		       &lwtun_encaps[encap_type],
93 		       ops, NULL) == ops) ? 0 : -1;
94 
95 	synchronize_net();
96 
97 	return ret;
98 }
99 EXPORT_SYMBOL(lwtunnel_encap_del_ops);
100 
101 int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
102 			 struct nlattr *encap, unsigned int family,
103 			 const void *cfg, struct lwtunnel_state **lws)
104 {
105 	const struct lwtunnel_encap_ops *ops;
106 	int ret = -EINVAL;
107 
108 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
109 	    encap_type > LWTUNNEL_ENCAP_MAX)
110 		return ret;
111 
112 	ret = -EOPNOTSUPP;
113 	rcu_read_lock();
114 	ops = rcu_dereference(lwtun_encaps[encap_type]);
115 #ifdef CONFIG_MODULES
116 	if (!ops) {
117 		const char *encap_type_str = lwtunnel_encap_str(encap_type);
118 
119 		if (encap_type_str) {
120 			rcu_read_unlock();
121 			request_module("rtnl-lwt-%s", encap_type_str);
122 			rcu_read_lock();
123 			ops = rcu_dereference(lwtun_encaps[encap_type]);
124 		}
125 	}
126 #endif
127 	if (likely(ops && ops->build_state))
128 		ret = ops->build_state(dev, encap, family, cfg, lws);
129 	rcu_read_unlock();
130 
131 	return ret;
132 }
133 EXPORT_SYMBOL(lwtunnel_build_state);
134 
135 void lwtstate_free(struct lwtunnel_state *lws)
136 {
137 	const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type];
138 
139 	if (ops->destroy_state) {
140 		ops->destroy_state(lws);
141 		kfree_rcu(lws, rcu);
142 	} else {
143 		kfree(lws);
144 	}
145 }
146 EXPORT_SYMBOL(lwtstate_free);
147 
148 int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate)
149 {
150 	const struct lwtunnel_encap_ops *ops;
151 	struct nlattr *nest;
152 	int ret = -EINVAL;
153 
154 	if (!lwtstate)
155 		return 0;
156 
157 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
158 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
159 		return 0;
160 
161 	ret = -EOPNOTSUPP;
162 	nest = nla_nest_start(skb, RTA_ENCAP);
163 	rcu_read_lock();
164 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
165 	if (likely(ops && ops->fill_encap))
166 		ret = ops->fill_encap(skb, lwtstate);
167 	rcu_read_unlock();
168 
169 	if (ret)
170 		goto nla_put_failure;
171 	nla_nest_end(skb, nest);
172 	ret = nla_put_u16(skb, RTA_ENCAP_TYPE, lwtstate->type);
173 	if (ret)
174 		goto nla_put_failure;
175 
176 	return 0;
177 
178 nla_put_failure:
179 	nla_nest_cancel(skb, nest);
180 
181 	return (ret == -EOPNOTSUPP ? 0 : ret);
182 }
183 EXPORT_SYMBOL(lwtunnel_fill_encap);
184 
185 int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate)
186 {
187 	const struct lwtunnel_encap_ops *ops;
188 	int ret = 0;
189 
190 	if (!lwtstate)
191 		return 0;
192 
193 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
194 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
195 		return 0;
196 
197 	rcu_read_lock();
198 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
199 	if (likely(ops && ops->get_encap_size))
200 		ret = nla_total_size(ops->get_encap_size(lwtstate));
201 	rcu_read_unlock();
202 
203 	return ret;
204 }
205 EXPORT_SYMBOL(lwtunnel_get_encap_size);
206 
207 int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
208 {
209 	const struct lwtunnel_encap_ops *ops;
210 	int ret = 0;
211 
212 	if (!a && !b)
213 		return 0;
214 
215 	if (!a || !b)
216 		return 1;
217 
218 	if (a->type != b->type)
219 		return 1;
220 
221 	if (a->type == LWTUNNEL_ENCAP_NONE ||
222 	    a->type > LWTUNNEL_ENCAP_MAX)
223 		return 0;
224 
225 	rcu_read_lock();
226 	ops = rcu_dereference(lwtun_encaps[a->type]);
227 	if (likely(ops && ops->cmp_encap))
228 		ret = ops->cmp_encap(a, b);
229 	rcu_read_unlock();
230 
231 	return ret;
232 }
233 EXPORT_SYMBOL(lwtunnel_cmp_encap);
234 
235 int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
236 {
237 	struct dst_entry *dst = skb_dst(skb);
238 	const struct lwtunnel_encap_ops *ops;
239 	struct lwtunnel_state *lwtstate;
240 	int ret = -EINVAL;
241 
242 	if (!dst)
243 		goto drop;
244 	lwtstate = dst->lwtstate;
245 
246 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
247 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
248 		return 0;
249 
250 	ret = -EOPNOTSUPP;
251 	rcu_read_lock();
252 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
253 	if (likely(ops && ops->output))
254 		ret = ops->output(net, sk, skb);
255 	rcu_read_unlock();
256 
257 	if (ret == -EOPNOTSUPP)
258 		goto drop;
259 
260 	return ret;
261 
262 drop:
263 	kfree_skb(skb);
264 
265 	return ret;
266 }
267 EXPORT_SYMBOL(lwtunnel_output);
268 
269 int lwtunnel_xmit(struct sk_buff *skb)
270 {
271 	struct dst_entry *dst = skb_dst(skb);
272 	const struct lwtunnel_encap_ops *ops;
273 	struct lwtunnel_state *lwtstate;
274 	int ret = -EINVAL;
275 
276 	if (!dst)
277 		goto drop;
278 
279 	lwtstate = dst->lwtstate;
280 
281 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
282 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
283 		return 0;
284 
285 	ret = -EOPNOTSUPP;
286 	rcu_read_lock();
287 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
288 	if (likely(ops && ops->xmit))
289 		ret = ops->xmit(skb);
290 	rcu_read_unlock();
291 
292 	if (ret == -EOPNOTSUPP)
293 		goto drop;
294 
295 	return ret;
296 
297 drop:
298 	kfree_skb(skb);
299 
300 	return ret;
301 }
302 EXPORT_SYMBOL(lwtunnel_xmit);
303 
304 int lwtunnel_input(struct sk_buff *skb)
305 {
306 	struct dst_entry *dst = skb_dst(skb);
307 	const struct lwtunnel_encap_ops *ops;
308 	struct lwtunnel_state *lwtstate;
309 	int ret = -EINVAL;
310 
311 	if (!dst)
312 		goto drop;
313 	lwtstate = dst->lwtstate;
314 
315 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
316 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
317 		return 0;
318 
319 	ret = -EOPNOTSUPP;
320 	rcu_read_lock();
321 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
322 	if (likely(ops && ops->input))
323 		ret = ops->input(skb);
324 	rcu_read_unlock();
325 
326 	if (ret == -EOPNOTSUPP)
327 		goto drop;
328 
329 	return ret;
330 
331 drop:
332 	kfree_skb(skb);
333 
334 	return ret;
335 }
336 EXPORT_SYMBOL(lwtunnel_input);
337