blob: 03976e939818c9e9bdb868103903b3170c0f537d [file] [log] [blame]
Roopa Prabhu499a2422015-07-21 10:43:46 +02001/*
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>
Roopa Prabhuffce4192015-07-21 10:43:49 +020028#include <net/ip6_fib.h>
Roopa Prabhu499a2422015-07-21 10:43:46 +020029
Robert Shearman745041e2016-02-19 09:43:16 +000030#ifdef CONFIG_MODULES
31
32static 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";
David Lebrun6c8702c2016-11-08 14:57:41 +010042 case LWTUNNEL_ENCAP_SEG6:
43 return "SEG6";
Robert Shearman745041e2016-02-19 09:43:16 +000044 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
Roopa Prabhu499a2422015-07-21 10:43:46 +020057struct 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}
65EXPORT_SYMBOL(lwtunnel_state_alloc);
66
Thomas Graf92a99bf2015-07-29 09:45:40 +020067static const struct lwtunnel_encap_ops __rcu *
Roopa Prabhu499a2422015-07-21 10:43:46 +020068 lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
69
70int 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}
80EXPORT_SYMBOL(lwtunnel_encap_add_ops);
81
82int 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}
99EXPORT_SYMBOL(lwtunnel_encap_del_ops);
100
101int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
Tom Herbert127eb7c2015-08-24 09:45:41 -0700102 struct nlattr *encap, unsigned int family,
103 const void *cfg, struct lwtunnel_state **lws)
Roopa Prabhu499a2422015-07-21 10:43:46 +0200104{
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]);
Robert Shearman745041e2016-02-19 09:43:16 +0000115#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
Roopa Prabhu499a2422015-07-21 10:43:46 +0200127 if (likely(ops && ops->build_state))
Tom Herbert127eb7c2015-08-24 09:45:41 -0700128 ret = ops->build_state(dev, encap, family, cfg, lws);
Roopa Prabhu499a2422015-07-21 10:43:46 +0200129 rcu_read_unlock();
130
131 return ret;
132}
133EXPORT_SYMBOL(lwtunnel_build_state);
134
Tom Herbert1104d9b2016-10-14 11:25:36 -0700135void 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}
146EXPORT_SYMBOL(lwtstate_free);
147
Roopa Prabhu499a2422015-07-21 10:43:46 +0200148int 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
178nla_put_failure:
179 nla_nest_cancel(skb, nest);
180
181 return (ret == -EOPNOTSUPP ? 0 : ret);
182}
183EXPORT_SYMBOL(lwtunnel_fill_encap);
184
185int 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}
205EXPORT_SYMBOL(lwtunnel_get_encap_size);
206
207int 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}
233EXPORT_SYMBOL(lwtunnel_cmp_encap);
Roopa Prabhuffce4192015-07-21 10:43:49 +0200234
Eric W. Biedermanede20592015-10-07 16:48:47 -0500235int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
Roopa Prabhuffce4192015-07-21 10:43:49 +0200236{
Jiri Benc61adedf2015-08-20 13:56:25 +0200237 struct dst_entry *dst = skb_dst(skb);
Roopa Prabhuffce4192015-07-21 10:43:49 +0200238 const struct lwtunnel_encap_ops *ops;
Jiri Benc61adedf2015-08-20 13:56:25 +0200239 struct lwtunnel_state *lwtstate;
Roopa Prabhuffce4192015-07-21 10:43:49 +0200240 int ret = -EINVAL;
241
Jiri Benc61adedf2015-08-20 13:56:25 +0200242 if (!dst)
Roopa Prabhuffce4192015-07-21 10:43:49 +0200243 goto drop;
Jiri Benc61adedf2015-08-20 13:56:25 +0200244 lwtstate = dst->lwtstate;
Roopa Prabhuffce4192015-07-21 10:43:49 +0200245
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))
Eric W. Biedermanede20592015-10-07 16:48:47 -0500254 ret = ops->output(net, sk, skb);
Roopa Prabhuffce4192015-07-21 10:43:49 +0200255 rcu_read_unlock();
256
257 if (ret == -EOPNOTSUPP)
258 goto drop;
259
260 return ret;
261
262drop:
Dan Carpentere11f40b2015-07-27 11:07:47 +0300263 kfree_skb(skb);
Roopa Prabhuffce4192015-07-21 10:43:49 +0200264
265 return ret;
266}
Roopa Prabhuffce4192015-07-21 10:43:49 +0200267EXPORT_SYMBOL(lwtunnel_output);
Tom Herbert25368622015-08-17 13:42:24 -0700268
Roopa Prabhu14972cb2016-08-24 20:10:43 -0700269int 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
297drop:
298 kfree_skb(skb);
299
300 return ret;
301}
302EXPORT_SYMBOL(lwtunnel_xmit);
303
Jiri Benc61adedf2015-08-20 13:56:25 +0200304int lwtunnel_input(struct sk_buff *skb)
Tom Herbert25368622015-08-17 13:42:24 -0700305{
Jiri Benc61adedf2015-08-20 13:56:25 +0200306 struct dst_entry *dst = skb_dst(skb);
Tom Herbert25368622015-08-17 13:42:24 -0700307 const struct lwtunnel_encap_ops *ops;
Jiri Benc61adedf2015-08-20 13:56:25 +0200308 struct lwtunnel_state *lwtstate;
Tom Herbert25368622015-08-17 13:42:24 -0700309 int ret = -EINVAL;
310
Jiri Benc61adedf2015-08-20 13:56:25 +0200311 if (!dst)
Tom Herbert25368622015-08-17 13:42:24 -0700312 goto drop;
Jiri Benc61adedf2015-08-20 13:56:25 +0200313 lwtstate = dst->lwtstate;
Tom Herbert25368622015-08-17 13:42:24 -0700314
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
331drop:
332 kfree_skb(skb);
333
334 return ret;
335}
Tom Herbert25368622015-08-17 13:42:24 -0700336EXPORT_SYMBOL(lwtunnel_input);