xref: /openbmc/linux/net/core/lwtunnel.c (revision 79a93295)
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_BPF:
45 		return "BPF";
46 	case LWTUNNEL_ENCAP_IP6:
47 	case LWTUNNEL_ENCAP_IP:
48 	case LWTUNNEL_ENCAP_NONE:
49 	case __LWTUNNEL_ENCAP_MAX:
50 		/* should not have got here */
51 		WARN_ON(1);
52 		break;
53 	}
54 	return NULL;
55 }
56 
57 #endif /* CONFIG_MODULES */
58 
59 struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
60 {
61 	struct lwtunnel_state *lws;
62 
63 	lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC);
64 
65 	return lws;
66 }
67 EXPORT_SYMBOL(lwtunnel_state_alloc);
68 
69 static const struct lwtunnel_encap_ops __rcu *
70 		lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
71 
72 int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
73 			   unsigned int num)
74 {
75 	if (num > LWTUNNEL_ENCAP_MAX)
76 		return -ERANGE;
77 
78 	return !cmpxchg((const struct lwtunnel_encap_ops **)
79 			&lwtun_encaps[num],
80 			NULL, ops) ? 0 : -1;
81 }
82 EXPORT_SYMBOL(lwtunnel_encap_add_ops);
83 
84 int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops,
85 			   unsigned int encap_type)
86 {
87 	int ret;
88 
89 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
90 	    encap_type > LWTUNNEL_ENCAP_MAX)
91 		return -ERANGE;
92 
93 	ret = (cmpxchg((const struct lwtunnel_encap_ops **)
94 		       &lwtun_encaps[encap_type],
95 		       ops, NULL) == ops) ? 0 : -1;
96 
97 	synchronize_net();
98 
99 	return ret;
100 }
101 EXPORT_SYMBOL(lwtunnel_encap_del_ops);
102 
103 int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
104 			 struct nlattr *encap, unsigned int family,
105 			 const void *cfg, struct lwtunnel_state **lws)
106 {
107 	const struct lwtunnel_encap_ops *ops;
108 	int ret = -EINVAL;
109 
110 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
111 	    encap_type > LWTUNNEL_ENCAP_MAX)
112 		return ret;
113 
114 	ret = -EOPNOTSUPP;
115 	rcu_read_lock();
116 	ops = rcu_dereference(lwtun_encaps[encap_type]);
117 #ifdef CONFIG_MODULES
118 	if (!ops) {
119 		const char *encap_type_str = lwtunnel_encap_str(encap_type);
120 
121 		if (encap_type_str) {
122 			rcu_read_unlock();
123 			request_module("rtnl-lwt-%s", encap_type_str);
124 			rcu_read_lock();
125 			ops = rcu_dereference(lwtun_encaps[encap_type]);
126 		}
127 	}
128 #endif
129 	if (likely(ops && ops->build_state))
130 		ret = ops->build_state(dev, encap, family, cfg, lws);
131 	rcu_read_unlock();
132 
133 	return ret;
134 }
135 EXPORT_SYMBOL(lwtunnel_build_state);
136 
137 void lwtstate_free(struct lwtunnel_state *lws)
138 {
139 	const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type];
140 
141 	if (ops->destroy_state) {
142 		ops->destroy_state(lws);
143 		kfree_rcu(lws, rcu);
144 	} else {
145 		kfree(lws);
146 	}
147 }
148 EXPORT_SYMBOL(lwtstate_free);
149 
150 int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate)
151 {
152 	const struct lwtunnel_encap_ops *ops;
153 	struct nlattr *nest;
154 	int ret = -EINVAL;
155 
156 	if (!lwtstate)
157 		return 0;
158 
159 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
160 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
161 		return 0;
162 
163 	ret = -EOPNOTSUPP;
164 	nest = nla_nest_start(skb, RTA_ENCAP);
165 	rcu_read_lock();
166 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
167 	if (likely(ops && ops->fill_encap))
168 		ret = ops->fill_encap(skb, lwtstate);
169 	rcu_read_unlock();
170 
171 	if (ret)
172 		goto nla_put_failure;
173 	nla_nest_end(skb, nest);
174 	ret = nla_put_u16(skb, RTA_ENCAP_TYPE, lwtstate->type);
175 	if (ret)
176 		goto nla_put_failure;
177 
178 	return 0;
179 
180 nla_put_failure:
181 	nla_nest_cancel(skb, nest);
182 
183 	return (ret == -EOPNOTSUPP ? 0 : ret);
184 }
185 EXPORT_SYMBOL(lwtunnel_fill_encap);
186 
187 int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate)
188 {
189 	const struct lwtunnel_encap_ops *ops;
190 	int ret = 0;
191 
192 	if (!lwtstate)
193 		return 0;
194 
195 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
196 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
197 		return 0;
198 
199 	rcu_read_lock();
200 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
201 	if (likely(ops && ops->get_encap_size))
202 		ret = nla_total_size(ops->get_encap_size(lwtstate));
203 	rcu_read_unlock();
204 
205 	return ret;
206 }
207 EXPORT_SYMBOL(lwtunnel_get_encap_size);
208 
209 int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
210 {
211 	const struct lwtunnel_encap_ops *ops;
212 	int ret = 0;
213 
214 	if (!a && !b)
215 		return 0;
216 
217 	if (!a || !b)
218 		return 1;
219 
220 	if (a->type != b->type)
221 		return 1;
222 
223 	if (a->type == LWTUNNEL_ENCAP_NONE ||
224 	    a->type > LWTUNNEL_ENCAP_MAX)
225 		return 0;
226 
227 	rcu_read_lock();
228 	ops = rcu_dereference(lwtun_encaps[a->type]);
229 	if (likely(ops && ops->cmp_encap))
230 		ret = ops->cmp_encap(a, b);
231 	rcu_read_unlock();
232 
233 	return ret;
234 }
235 EXPORT_SYMBOL(lwtunnel_cmp_encap);
236 
237 int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
238 {
239 	struct dst_entry *dst = skb_dst(skb);
240 	const struct lwtunnel_encap_ops *ops;
241 	struct lwtunnel_state *lwtstate;
242 	int ret = -EINVAL;
243 
244 	if (!dst)
245 		goto drop;
246 	lwtstate = dst->lwtstate;
247 
248 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
249 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
250 		return 0;
251 
252 	ret = -EOPNOTSUPP;
253 	rcu_read_lock();
254 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
255 	if (likely(ops && ops->output))
256 		ret = ops->output(net, sk, skb);
257 	rcu_read_unlock();
258 
259 	if (ret == -EOPNOTSUPP)
260 		goto drop;
261 
262 	return ret;
263 
264 drop:
265 	kfree_skb(skb);
266 
267 	return ret;
268 }
269 EXPORT_SYMBOL(lwtunnel_output);
270 
271 int lwtunnel_xmit(struct sk_buff *skb)
272 {
273 	struct dst_entry *dst = skb_dst(skb);
274 	const struct lwtunnel_encap_ops *ops;
275 	struct lwtunnel_state *lwtstate;
276 	int ret = -EINVAL;
277 
278 	if (!dst)
279 		goto drop;
280 
281 	lwtstate = dst->lwtstate;
282 
283 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
284 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
285 		return 0;
286 
287 	ret = -EOPNOTSUPP;
288 	rcu_read_lock();
289 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
290 	if (likely(ops && ops->xmit))
291 		ret = ops->xmit(skb);
292 	rcu_read_unlock();
293 
294 	if (ret == -EOPNOTSUPP)
295 		goto drop;
296 
297 	return ret;
298 
299 drop:
300 	kfree_skb(skb);
301 
302 	return ret;
303 }
304 EXPORT_SYMBOL(lwtunnel_xmit);
305 
306 int lwtunnel_input(struct sk_buff *skb)
307 {
308 	struct dst_entry *dst = skb_dst(skb);
309 	const struct lwtunnel_encap_ops *ops;
310 	struct lwtunnel_state *lwtstate;
311 	int ret = -EINVAL;
312 
313 	if (!dst)
314 		goto drop;
315 	lwtstate = dst->lwtstate;
316 
317 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
318 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
319 		return 0;
320 
321 	ret = -EOPNOTSUPP;
322 	rcu_read_lock();
323 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
324 	if (likely(ops && ops->input))
325 		ret = ops->input(skb);
326 	rcu_read_unlock();
327 
328 	if (ret == -EOPNOTSUPP)
329 		goto drop;
330 
331 	return ret;
332 
333 drop:
334 	kfree_skb(skb);
335 
336 	return ret;
337 }
338 EXPORT_SYMBOL(lwtunnel_input);
339