Blame view

net/core/lwtunnel.c 8.8 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
499a24256   Roopa Prabhu   lwtunnel: infrast...
2
3
4
5
  /*
   * lwtunnel	Infrastructure for light weight tunnels like mpls
   *
   * Authors:	Roopa Prabhu, <roopa@cumulusnetworks.com>
499a24256   Roopa Prabhu   lwtunnel: infrast...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   */
  
  #include <linux/capability.h>
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/uaccess.h>
  #include <linux/skbuff.h>
  #include <linux/netdevice.h>
  #include <linux/lwtunnel.h>
  #include <linux/in.h>
  #include <linux/init.h>
  #include <linux/err.h>
  
  #include <net/lwtunnel.h>
  #include <net/rtnetlink.h>
ffce41962   Roopa Prabhu   lwtunnel: support...
23
  #include <net/ip6_fib.h>
3c618c1db   David Ahern   net: Rename net/n...
24
  #include <net/rtnh.h>
499a24256   Roopa Prabhu   lwtunnel: infrast...
25

745041e2a   Robert Shearman   lwtunnel: autoloa...
26
27
28
29
30
31
32
33
34
35
36
37
  #ifdef CONFIG_MODULES
  
  static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
  {
  	/* Only lwt encaps implemented without using an interface for
  	 * the encap need to return a string here.
  	 */
  	switch (encap_type) {
  	case LWTUNNEL_ENCAP_MPLS:
  		return "MPLS";
  	case LWTUNNEL_ENCAP_ILA:
  		return "ILA";
6c8702c60   David Lebrun   ipv6: sr: add sup...
38
39
  	case LWTUNNEL_ENCAP_SEG6:
  		return "SEG6";
3a0af8fd6   Thomas Graf   bpf: BPF for ligh...
40
41
  	case LWTUNNEL_ENCAP_BPF:
  		return "BPF";
d1df6fd8a   David Lebrun   ipv6: sr: define ...
42
43
  	case LWTUNNEL_ENCAP_SEG6_LOCAL:
  		return "SEG6LOCAL";
a7a29f9c3   Alexander Aring   net: ipv6: add rp...
44
45
  	case LWTUNNEL_ENCAP_RPL:
  		return "RPL";
745041e2a   Robert Shearman   lwtunnel: autoloa...
46
47
48
49
50
51
52
53
54
55
56
57
  	case LWTUNNEL_ENCAP_IP6:
  	case LWTUNNEL_ENCAP_IP:
  	case LWTUNNEL_ENCAP_NONE:
  	case __LWTUNNEL_ENCAP_MAX:
  		/* should not have got here */
  		WARN_ON(1);
  		break;
  	}
  	return NULL;
  }
  
  #endif /* CONFIG_MODULES */
499a24256   Roopa Prabhu   lwtunnel: infrast...
58
59
60
61
62
63
64
65
  struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
  {
  	struct lwtunnel_state *lws;
  
  	lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC);
  
  	return lws;
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
66
  EXPORT_SYMBOL_GPL(lwtunnel_state_alloc);
499a24256   Roopa Prabhu   lwtunnel: infrast...
67

92a99bf3b   Thomas Graf   lwtunnel: Make lw...
68
  static const struct lwtunnel_encap_ops __rcu *
499a24256   Roopa Prabhu   lwtunnel: infrast...
69
70
71
72
73
74
75
76
77
78
79
80
  		lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
  
  int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
  			   unsigned int num)
  {
  	if (num > LWTUNNEL_ENCAP_MAX)
  		return -ERANGE;
  
  	return !cmpxchg((const struct lwtunnel_encap_ops **)
  			&lwtun_encaps[num],
  			NULL, ops) ? 0 : -1;
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
81
  EXPORT_SYMBOL_GPL(lwtunnel_encap_add_ops);
499a24256   Roopa Prabhu   lwtunnel: infrast...
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  
  int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops,
  			   unsigned int encap_type)
  {
  	int ret;
  
  	if (encap_type == LWTUNNEL_ENCAP_NONE ||
  	    encap_type > LWTUNNEL_ENCAP_MAX)
  		return -ERANGE;
  
  	ret = (cmpxchg((const struct lwtunnel_encap_ops **)
  		       &lwtun_encaps[encap_type],
  		       ops, NULL) == ops) ? 0 : -1;
  
  	synchronize_net();
  
  	return ret;
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
100
  EXPORT_SYMBOL_GPL(lwtunnel_encap_del_ops);
499a24256   Roopa Prabhu   lwtunnel: infrast...
101

faee67694   Alexander Aring   net: add net avai...
102
  int lwtunnel_build_state(struct net *net, u16 encap_type,
127eb7cd3   Tom Herbert   lwt: Add cfg argu...
103
  			 struct nlattr *encap, unsigned int family,
9ae287274   David Ahern   net: add extack a...
104
105
  			 const void *cfg, struct lwtunnel_state **lws,
  			 struct netlink_ext_ack *extack)
499a24256   Roopa Prabhu   lwtunnel: infrast...
106
107
  {
  	const struct lwtunnel_encap_ops *ops;
9ae287274   David Ahern   net: add extack a...
108
  	bool found = false;
499a24256   Roopa Prabhu   lwtunnel: infrast...
109
110
111
  	int ret = -EINVAL;
  
  	if (encap_type == LWTUNNEL_ENCAP_NONE ||
9ae287274   David Ahern   net: add extack a...
112
113
114
  	    encap_type > LWTUNNEL_ENCAP_MAX) {
  		NL_SET_ERR_MSG_ATTR(extack, encap,
  				    "Unknown LWT encapsulation type");
499a24256   Roopa Prabhu   lwtunnel: infrast...
115
  		return ret;
9ae287274   David Ahern   net: add extack a...
116
  	}
499a24256   Roopa Prabhu   lwtunnel: infrast...
117
118
119
120
  
  	ret = -EOPNOTSUPP;
  	rcu_read_lock();
  	ops = rcu_dereference(lwtun_encaps[encap_type]);
3d25eabbb   wenxu   ip_tunnel: Add ds...
121
  	if (likely(ops && ops->build_state && try_module_get(ops->owner)))
9ae287274   David Ahern   net: add extack a...
122
  		found = true;
3d25eabbb   wenxu   ip_tunnel: Add ds...
123
124
125
  	rcu_read_unlock();
  
  	if (found) {
faee67694   Alexander Aring   net: add net avai...
126
  		ret = ops->build_state(net, encap, family, cfg, lws, extack);
85c814016   Robert Shearman   lwtunnel: Fix oop...
127
128
  		if (ret)
  			module_put(ops->owner);
3d25eabbb   wenxu   ip_tunnel: Add ds...
129
130
131
132
  	} else {
  		/* don't rely on -EOPNOTSUPP to detect match as build_state
  		 * handlers could return it
  		 */
9ae287274   David Ahern   net: add extack a...
133
134
135
  		NL_SET_ERR_MSG_ATTR(extack, encap,
  				    "LWT encapsulation type not supported");
  	}
9ed59592e   David Ahern   lwtunnel: fix aut...
136
137
  	return ret;
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
138
  EXPORT_SYMBOL_GPL(lwtunnel_build_state);
9ed59592e   David Ahern   lwtunnel: fix aut...
139

c255bd681   David Ahern   net: lwtunnel: Ad...
140
  int lwtunnel_valid_encap_type(u16 encap_type, struct netlink_ext_ack *extack)
9ed59592e   David Ahern   lwtunnel: fix aut...
141
142
143
144
145
  {
  	const struct lwtunnel_encap_ops *ops;
  	int ret = -EINVAL;
  
  	if (encap_type == LWTUNNEL_ENCAP_NONE ||
c255bd681   David Ahern   net: lwtunnel: Ad...
146
147
  	    encap_type > LWTUNNEL_ENCAP_MAX) {
  		NL_SET_ERR_MSG(extack, "Unknown lwt encapsulation type");
9ed59592e   David Ahern   lwtunnel: fix aut...
148
  		return ret;
c255bd681   David Ahern   net: lwtunnel: Ad...
149
  	}
9ed59592e   David Ahern   lwtunnel: fix aut...
150
151
152
153
  
  	rcu_read_lock();
  	ops = rcu_dereference(lwtun_encaps[encap_type]);
  	rcu_read_unlock();
745041e2a   Robert Shearman   lwtunnel: autoloa...
154
155
156
157
158
  #ifdef CONFIG_MODULES
  	if (!ops) {
  		const char *encap_type_str = lwtunnel_encap_str(encap_type);
  
  		if (encap_type_str) {
9ed59592e   David Ahern   lwtunnel: fix aut...
159
  			__rtnl_unlock();
745041e2a   Robert Shearman   lwtunnel: autoloa...
160
  			request_module("rtnl-lwt-%s", encap_type_str);
9ed59592e   David Ahern   lwtunnel: fix aut...
161
  			rtnl_lock();
745041e2a   Robert Shearman   lwtunnel: autoloa...
162
163
  			rcu_read_lock();
  			ops = rcu_dereference(lwtun_encaps[encap_type]);
9ed59592e   David Ahern   lwtunnel: fix aut...
164
  			rcu_read_unlock();
745041e2a   Robert Shearman   lwtunnel: autoloa...
165
166
167
  		}
  	}
  #endif
c255bd681   David Ahern   net: lwtunnel: Ad...
168
169
170
171
172
  	ret = ops ? 0 : -EOPNOTSUPP;
  	if (ret < 0)
  		NL_SET_ERR_MSG(extack, "lwt encapsulation type not supported");
  
  	return ret;
9ed59592e   David Ahern   lwtunnel: fix aut...
173
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
174
  EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type);
499a24256   Roopa Prabhu   lwtunnel: infrast...
175

c255bd681   David Ahern   net: lwtunnel: Ad...
176
177
  int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining,
  				   struct netlink_ext_ack *extack)
9ed59592e   David Ahern   lwtunnel: fix aut...
178
179
180
181
  {
  	struct rtnexthop *rtnh = (struct rtnexthop *)attr;
  	struct nlattr *nla_entype;
  	struct nlattr *attrs;
9ed59592e   David Ahern   lwtunnel: fix aut...
182
183
184
185
186
187
188
  	u16 encap_type;
  	int attrlen;
  
  	while (rtnh_ok(rtnh, remaining)) {
  		attrlen = rtnh_attrlen(rtnh);
  		if (attrlen > 0) {
  			attrs = rtnh_attrs(rtnh);
9ed59592e   David Ahern   lwtunnel: fix aut...
189
190
191
192
  			nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
  
  			if (nla_entype) {
  				encap_type = nla_get_u16(nla_entype);
c255bd681   David Ahern   net: lwtunnel: Ad...
193
194
  				if (lwtunnel_valid_encap_type(encap_type,
  							      extack) != 0)
9ed59592e   David Ahern   lwtunnel: fix aut...
195
196
197
198
199
200
201
  					return -EOPNOTSUPP;
  			}
  		}
  		rtnh = rtnh_next(rtnh, &remaining);
  	}
  
  	return 0;
499a24256   Roopa Prabhu   lwtunnel: infrast...
202
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
203
  EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type_attr);
499a24256   Roopa Prabhu   lwtunnel: infrast...
204

1104d9ba4   Tom Herbert   lwtunnel: Add des...
205
206
207
208
209
210
211
212
213
214
  void lwtstate_free(struct lwtunnel_state *lws)
  {
  	const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type];
  
  	if (ops->destroy_state) {
  		ops->destroy_state(lws);
  		kfree_rcu(lws, rcu);
  	} else {
  		kfree(lws);
  	}
85c814016   Robert Shearman   lwtunnel: Fix oop...
215
  	module_put(ops->owner);
1104d9ba4   Tom Herbert   lwtunnel: Add des...
216
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
217
  EXPORT_SYMBOL_GPL(lwtstate_free);
1104d9ba4   Tom Herbert   lwtunnel: Add des...
218

ffa8ce54b   David Ahern   lwtunnel: Pass en...
219
220
  int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate,
  			int encap_attr, int encap_type_attr)
499a24256   Roopa Prabhu   lwtunnel: infrast...
221
222
223
  {
  	const struct lwtunnel_encap_ops *ops;
  	struct nlattr *nest;
39f370959   Dan Carpenter   lwtunnel: fix err...
224
  	int ret;
499a24256   Roopa Prabhu   lwtunnel: infrast...
225
226
227
228
229
230
231
  
  	if (!lwtstate)
  		return 0;
  
  	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
  	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
  		return 0;
ae0be8de9   Michal Kubecek   netlink: make nla...
232
  	nest = nla_nest_start_noflag(skb, encap_attr);
a50fe0ffd   Pan Bian   lwtunnel: check r...
233
  	if (!nest)
39f370959   Dan Carpenter   lwtunnel: fix err...
234
235
236
  		return -EMSGSIZE;
  
  	ret = -EOPNOTSUPP;
499a24256   Roopa Prabhu   lwtunnel: infrast...
237
238
239
240
241
242
243
244
245
  	rcu_read_lock();
  	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
  	if (likely(ops && ops->fill_encap))
  		ret = ops->fill_encap(skb, lwtstate);
  	rcu_read_unlock();
  
  	if (ret)
  		goto nla_put_failure;
  	nla_nest_end(skb, nest);
ffa8ce54b   David Ahern   lwtunnel: Pass en...
246
  	ret = nla_put_u16(skb, encap_type_attr, lwtstate->type);
499a24256   Roopa Prabhu   lwtunnel: infrast...
247
248
249
250
251
252
253
254
255
256
  	if (ret)
  		goto nla_put_failure;
  
  	return 0;
  
  nla_put_failure:
  	nla_nest_cancel(skb, nest);
  
  	return (ret == -EOPNOTSUPP ? 0 : ret);
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
257
  EXPORT_SYMBOL_GPL(lwtunnel_fill_encap);
499a24256   Roopa Prabhu   lwtunnel: infrast...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  
  int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate)
  {
  	const struct lwtunnel_encap_ops *ops;
  	int ret = 0;
  
  	if (!lwtstate)
  		return 0;
  
  	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
  	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
  		return 0;
  
  	rcu_read_lock();
  	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
  	if (likely(ops && ops->get_encap_size))
  		ret = nla_total_size(ops->get_encap_size(lwtstate));
  	rcu_read_unlock();
  
  	return ret;
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
279
  EXPORT_SYMBOL_GPL(lwtunnel_get_encap_size);
499a24256   Roopa Prabhu   lwtunnel: infrast...
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  
  int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
  {
  	const struct lwtunnel_encap_ops *ops;
  	int ret = 0;
  
  	if (!a && !b)
  		return 0;
  
  	if (!a || !b)
  		return 1;
  
  	if (a->type != b->type)
  		return 1;
  
  	if (a->type == LWTUNNEL_ENCAP_NONE ||
  	    a->type > LWTUNNEL_ENCAP_MAX)
  		return 0;
  
  	rcu_read_lock();
  	ops = rcu_dereference(lwtun_encaps[a->type]);
  	if (likely(ops && ops->cmp_encap))
  		ret = ops->cmp_encap(a, b);
  	rcu_read_unlock();
  
  	return ret;
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
307
  EXPORT_SYMBOL_GPL(lwtunnel_cmp_encap);
ffce41962   Roopa Prabhu   lwtunnel: support...
308

ede2059db   Eric W. Biederman   dst: Pass net int...
309
  int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
ffce41962   Roopa Prabhu   lwtunnel: support...
310
  {
61adedf3e   Jiri Benc   route: move lwtun...
311
  	struct dst_entry *dst = skb_dst(skb);
ffce41962   Roopa Prabhu   lwtunnel: support...
312
  	const struct lwtunnel_encap_ops *ops;
61adedf3e   Jiri Benc   route: move lwtun...
313
  	struct lwtunnel_state *lwtstate;
ffce41962   Roopa Prabhu   lwtunnel: support...
314
  	int ret = -EINVAL;
61adedf3e   Jiri Benc   route: move lwtun...
315
  	if (!dst)
ffce41962   Roopa Prabhu   lwtunnel: support...
316
  		goto drop;
61adedf3e   Jiri Benc   route: move lwtun...
317
  	lwtstate = dst->lwtstate;
ffce41962   Roopa Prabhu   lwtunnel: support...
318
319
320
321
322
323
324
325
326
  
  	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
  	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
  		return 0;
  
  	ret = -EOPNOTSUPP;
  	rcu_read_lock();
  	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
  	if (likely(ops && ops->output))
ede2059db   Eric W. Biederman   dst: Pass net int...
327
  		ret = ops->output(net, sk, skb);
ffce41962   Roopa Prabhu   lwtunnel: support...
328
329
330
331
332
333
334
335
  	rcu_read_unlock();
  
  	if (ret == -EOPNOTSUPP)
  		goto drop;
  
  	return ret;
  
  drop:
e11f40b93   Dan Carpenter   lwtunnel: use kfr...
336
  	kfree_skb(skb);
ffce41962   Roopa Prabhu   lwtunnel: support...
337
338
339
  
  	return ret;
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
340
  EXPORT_SYMBOL_GPL(lwtunnel_output);
253686231   Tom Herbert   lwt: Add support ...
341

14972cbd3   Roopa Prabhu   net: lwtunnel: Ha...
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  int lwtunnel_xmit(struct sk_buff *skb)
  {
  	struct dst_entry *dst = skb_dst(skb);
  	const struct lwtunnel_encap_ops *ops;
  	struct lwtunnel_state *lwtstate;
  	int ret = -EINVAL;
  
  	if (!dst)
  		goto drop;
  
  	lwtstate = dst->lwtstate;
  
  	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
  	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
  		return 0;
  
  	ret = -EOPNOTSUPP;
  	rcu_read_lock();
  	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
  	if (likely(ops && ops->xmit))
  		ret = ops->xmit(skb);
  	rcu_read_unlock();
  
  	if (ret == -EOPNOTSUPP)
  		goto drop;
  
  	return ret;
  
  drop:
  	kfree_skb(skb);
  
  	return ret;
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
375
  EXPORT_SYMBOL_GPL(lwtunnel_xmit);
14972cbd3   Roopa Prabhu   net: lwtunnel: Ha...
376

61adedf3e   Jiri Benc   route: move lwtun...
377
  int lwtunnel_input(struct sk_buff *skb)
253686231   Tom Herbert   lwt: Add support ...
378
  {
61adedf3e   Jiri Benc   route: move lwtun...
379
  	struct dst_entry *dst = skb_dst(skb);
253686231   Tom Herbert   lwt: Add support ...
380
  	const struct lwtunnel_encap_ops *ops;
61adedf3e   Jiri Benc   route: move lwtun...
381
  	struct lwtunnel_state *lwtstate;
253686231   Tom Herbert   lwt: Add support ...
382
  	int ret = -EINVAL;
61adedf3e   Jiri Benc   route: move lwtun...
383
  	if (!dst)
253686231   Tom Herbert   lwt: Add support ...
384
  		goto drop;
61adedf3e   Jiri Benc   route: move lwtun...
385
  	lwtstate = dst->lwtstate;
253686231   Tom Herbert   lwt: Add support ...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
  
  	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
  	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
  		return 0;
  
  	ret = -EOPNOTSUPP;
  	rcu_read_lock();
  	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
  	if (likely(ops && ops->input))
  		ret = ops->input(skb);
  	rcu_read_unlock();
  
  	if (ret == -EOPNOTSUPP)
  		goto drop;
  
  	return ret;
  
  drop:
  	kfree_skb(skb);
  
  	return ret;
  }
08bd10ffb   Roopa Prabhu   lwtunnel: replace...
408
  EXPORT_SYMBOL_GPL(lwtunnel_input);