xref: /openbmc/linux/net/core/lwtunnel.c (revision 9ae28727)
1499a2425SRoopa Prabhu /*
2499a2425SRoopa Prabhu  * lwtunnel	Infrastructure for light weight tunnels like mpls
3499a2425SRoopa Prabhu  *
4499a2425SRoopa Prabhu  * Authors:	Roopa Prabhu, <roopa@cumulusnetworks.com>
5499a2425SRoopa Prabhu  *
6499a2425SRoopa Prabhu  *		This program is free software; you can redistribute it and/or
7499a2425SRoopa Prabhu  *		modify it under the terms of the GNU General Public License
8499a2425SRoopa Prabhu  *		as published by the Free Software Foundation; either version
9499a2425SRoopa Prabhu  *		2 of the License, or (at your option) any later version.
10499a2425SRoopa Prabhu  *
11499a2425SRoopa Prabhu  */
12499a2425SRoopa Prabhu 
13499a2425SRoopa Prabhu #include <linux/capability.h>
14499a2425SRoopa Prabhu #include <linux/module.h>
15499a2425SRoopa Prabhu #include <linux/types.h>
16499a2425SRoopa Prabhu #include <linux/kernel.h>
17499a2425SRoopa Prabhu #include <linux/slab.h>
18499a2425SRoopa Prabhu #include <linux/uaccess.h>
19499a2425SRoopa Prabhu #include <linux/skbuff.h>
20499a2425SRoopa Prabhu #include <linux/netdevice.h>
21499a2425SRoopa Prabhu #include <linux/lwtunnel.h>
22499a2425SRoopa Prabhu #include <linux/in.h>
23499a2425SRoopa Prabhu #include <linux/init.h>
24499a2425SRoopa Prabhu #include <linux/err.h>
25499a2425SRoopa Prabhu 
26499a2425SRoopa Prabhu #include <net/lwtunnel.h>
27499a2425SRoopa Prabhu #include <net/rtnetlink.h>
28ffce4196SRoopa Prabhu #include <net/ip6_fib.h>
299ed59592SDavid Ahern #include <net/nexthop.h>
30499a2425SRoopa Prabhu 
31745041e2SRobert Shearman #ifdef CONFIG_MODULES
32745041e2SRobert Shearman 
33745041e2SRobert Shearman static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
34745041e2SRobert Shearman {
35745041e2SRobert Shearman 	/* Only lwt encaps implemented without using an interface for
36745041e2SRobert Shearman 	 * the encap need to return a string here.
37745041e2SRobert Shearman 	 */
38745041e2SRobert Shearman 	switch (encap_type) {
39745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_MPLS:
40745041e2SRobert Shearman 		return "MPLS";
41745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_ILA:
42745041e2SRobert Shearman 		return "ILA";
436c8702c6SDavid Lebrun 	case LWTUNNEL_ENCAP_SEG6:
446c8702c6SDavid Lebrun 		return "SEG6";
453a0af8fdSThomas Graf 	case LWTUNNEL_ENCAP_BPF:
463a0af8fdSThomas Graf 		return "BPF";
47745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_IP6:
48745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_IP:
49745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_NONE:
50745041e2SRobert Shearman 	case __LWTUNNEL_ENCAP_MAX:
51745041e2SRobert Shearman 		/* should not have got here */
52745041e2SRobert Shearman 		WARN_ON(1);
53745041e2SRobert Shearman 		break;
54745041e2SRobert Shearman 	}
55745041e2SRobert Shearman 	return NULL;
56745041e2SRobert Shearman }
57745041e2SRobert Shearman 
58745041e2SRobert Shearman #endif /* CONFIG_MODULES */
59745041e2SRobert Shearman 
60499a2425SRoopa Prabhu struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
61499a2425SRoopa Prabhu {
62499a2425SRoopa Prabhu 	struct lwtunnel_state *lws;
63499a2425SRoopa Prabhu 
64499a2425SRoopa Prabhu 	lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC);
65499a2425SRoopa Prabhu 
66499a2425SRoopa Prabhu 	return lws;
67499a2425SRoopa Prabhu }
68499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_state_alloc);
69499a2425SRoopa Prabhu 
7092a99bf3SThomas Graf static const struct lwtunnel_encap_ops __rcu *
71499a2425SRoopa Prabhu 		lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
72499a2425SRoopa Prabhu 
73499a2425SRoopa Prabhu int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
74499a2425SRoopa Prabhu 			   unsigned int num)
75499a2425SRoopa Prabhu {
76499a2425SRoopa Prabhu 	if (num > LWTUNNEL_ENCAP_MAX)
77499a2425SRoopa Prabhu 		return -ERANGE;
78499a2425SRoopa Prabhu 
79499a2425SRoopa Prabhu 	return !cmpxchg((const struct lwtunnel_encap_ops **)
80499a2425SRoopa Prabhu 			&lwtun_encaps[num],
81499a2425SRoopa Prabhu 			NULL, ops) ? 0 : -1;
82499a2425SRoopa Prabhu }
83499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_encap_add_ops);
84499a2425SRoopa Prabhu 
85499a2425SRoopa Prabhu int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops,
86499a2425SRoopa Prabhu 			   unsigned int encap_type)
87499a2425SRoopa Prabhu {
88499a2425SRoopa Prabhu 	int ret;
89499a2425SRoopa Prabhu 
90499a2425SRoopa Prabhu 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
91499a2425SRoopa Prabhu 	    encap_type > LWTUNNEL_ENCAP_MAX)
92499a2425SRoopa Prabhu 		return -ERANGE;
93499a2425SRoopa Prabhu 
94499a2425SRoopa Prabhu 	ret = (cmpxchg((const struct lwtunnel_encap_ops **)
95499a2425SRoopa Prabhu 		       &lwtun_encaps[encap_type],
96499a2425SRoopa Prabhu 		       ops, NULL) == ops) ? 0 : -1;
97499a2425SRoopa Prabhu 
98499a2425SRoopa Prabhu 	synchronize_net();
99499a2425SRoopa Prabhu 
100499a2425SRoopa Prabhu 	return ret;
101499a2425SRoopa Prabhu }
102499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_encap_del_ops);
103499a2425SRoopa Prabhu 
10430357d7dSDavid Ahern int lwtunnel_build_state(u16 encap_type,
105127eb7cdSTom Herbert 			 struct nlattr *encap, unsigned int family,
1069ae28727SDavid Ahern 			 const void *cfg, struct lwtunnel_state **lws,
1079ae28727SDavid Ahern 			 struct netlink_ext_ack *extack)
108499a2425SRoopa Prabhu {
109499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
1109ae28727SDavid Ahern 	bool found = false;
111499a2425SRoopa Prabhu 	int ret = -EINVAL;
112499a2425SRoopa Prabhu 
113499a2425SRoopa Prabhu 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
1149ae28727SDavid Ahern 	    encap_type > LWTUNNEL_ENCAP_MAX) {
1159ae28727SDavid Ahern 		NL_SET_ERR_MSG_ATTR(extack, encap,
1169ae28727SDavid Ahern 				    "Unknown LWT encapsulation type");
117499a2425SRoopa Prabhu 		return ret;
1189ae28727SDavid Ahern 	}
119499a2425SRoopa Prabhu 
120499a2425SRoopa Prabhu 	ret = -EOPNOTSUPP;
121499a2425SRoopa Prabhu 	rcu_read_lock();
122499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[encap_type]);
12385c81401SRobert Shearman 	if (likely(ops && ops->build_state && try_module_get(ops->owner))) {
1249ae28727SDavid Ahern 		found = true;
1259ae28727SDavid Ahern 		ret = ops->build_state(encap, family, cfg, lws, extack);
12685c81401SRobert Shearman 		if (ret)
12785c81401SRobert Shearman 			module_put(ops->owner);
12885c81401SRobert Shearman 	}
129499a2425SRoopa Prabhu 	rcu_read_unlock();
130499a2425SRoopa Prabhu 
1319ae28727SDavid Ahern 	/* don't rely on -EOPNOTSUPP to detect match as build_state
1329ae28727SDavid Ahern 	 * handlers could return it
1339ae28727SDavid Ahern 	 */
1349ae28727SDavid Ahern 	if (!found) {
1359ae28727SDavid Ahern 		NL_SET_ERR_MSG_ATTR(extack, encap,
1369ae28727SDavid Ahern 				    "LWT encapsulation type not supported");
1379ae28727SDavid Ahern 	}
1389ae28727SDavid Ahern 
139499a2425SRoopa Prabhu 	return ret;
140499a2425SRoopa Prabhu }
141499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_build_state);
142499a2425SRoopa Prabhu 
143c255bd68SDavid Ahern int lwtunnel_valid_encap_type(u16 encap_type, struct netlink_ext_ack *extack)
1449ed59592SDavid Ahern {
1459ed59592SDavid Ahern 	const struct lwtunnel_encap_ops *ops;
1469ed59592SDavid Ahern 	int ret = -EINVAL;
1479ed59592SDavid Ahern 
1489ed59592SDavid Ahern 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
149c255bd68SDavid Ahern 	    encap_type > LWTUNNEL_ENCAP_MAX) {
150c255bd68SDavid Ahern 		NL_SET_ERR_MSG(extack, "Unknown lwt encapsulation type");
1519ed59592SDavid Ahern 		return ret;
152c255bd68SDavid Ahern 	}
1539ed59592SDavid Ahern 
1549ed59592SDavid Ahern 	rcu_read_lock();
1559ed59592SDavid Ahern 	ops = rcu_dereference(lwtun_encaps[encap_type]);
1569ed59592SDavid Ahern 	rcu_read_unlock();
1579ed59592SDavid Ahern #ifdef CONFIG_MODULES
1589ed59592SDavid Ahern 	if (!ops) {
1599ed59592SDavid Ahern 		const char *encap_type_str = lwtunnel_encap_str(encap_type);
1609ed59592SDavid Ahern 
1619ed59592SDavid Ahern 		if (encap_type_str) {
1629ed59592SDavid Ahern 			__rtnl_unlock();
1639ed59592SDavid Ahern 			request_module("rtnl-lwt-%s", encap_type_str);
1649ed59592SDavid Ahern 			rtnl_lock();
1659ed59592SDavid Ahern 
1669ed59592SDavid Ahern 			rcu_read_lock();
1679ed59592SDavid Ahern 			ops = rcu_dereference(lwtun_encaps[encap_type]);
1689ed59592SDavid Ahern 			rcu_read_unlock();
1699ed59592SDavid Ahern 		}
1709ed59592SDavid Ahern 	}
1719ed59592SDavid Ahern #endif
172c255bd68SDavid Ahern 	ret = ops ? 0 : -EOPNOTSUPP;
173c255bd68SDavid Ahern 	if (ret < 0)
174c255bd68SDavid Ahern 		NL_SET_ERR_MSG(extack, "lwt encapsulation type not supported");
175c255bd68SDavid Ahern 
176c255bd68SDavid Ahern 	return ret;
1779ed59592SDavid Ahern }
1789ed59592SDavid Ahern EXPORT_SYMBOL(lwtunnel_valid_encap_type);
1799ed59592SDavid Ahern 
180c255bd68SDavid Ahern int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining,
181c255bd68SDavid Ahern 				   struct netlink_ext_ack *extack)
1829ed59592SDavid Ahern {
1839ed59592SDavid Ahern 	struct rtnexthop *rtnh = (struct rtnexthop *)attr;
1849ed59592SDavid Ahern 	struct nlattr *nla_entype;
1859ed59592SDavid Ahern 	struct nlattr *attrs;
1869ed59592SDavid Ahern 	u16 encap_type;
1879ed59592SDavid Ahern 	int attrlen;
1889ed59592SDavid Ahern 
1899ed59592SDavid Ahern 	while (rtnh_ok(rtnh, remaining)) {
1909ed59592SDavid Ahern 		attrlen = rtnh_attrlen(rtnh);
1919ed59592SDavid Ahern 		if (attrlen > 0) {
1929ed59592SDavid Ahern 			attrs = rtnh_attrs(rtnh);
1939ed59592SDavid Ahern 			nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
1949ed59592SDavid Ahern 
1959ed59592SDavid Ahern 			if (nla_entype) {
1969ed59592SDavid Ahern 				encap_type = nla_get_u16(nla_entype);
1979ed59592SDavid Ahern 
198c255bd68SDavid Ahern 				if (lwtunnel_valid_encap_type(encap_type,
199c255bd68SDavid Ahern 							      extack) != 0)
2009ed59592SDavid Ahern 					return -EOPNOTSUPP;
2019ed59592SDavid Ahern 			}
2029ed59592SDavid Ahern 		}
2039ed59592SDavid Ahern 		rtnh = rtnh_next(rtnh, &remaining);
2049ed59592SDavid Ahern 	}
2059ed59592SDavid Ahern 
2069ed59592SDavid Ahern 	return 0;
2079ed59592SDavid Ahern }
2089ed59592SDavid Ahern EXPORT_SYMBOL(lwtunnel_valid_encap_type_attr);
2099ed59592SDavid Ahern 
2101104d9baSTom Herbert void lwtstate_free(struct lwtunnel_state *lws)
2111104d9baSTom Herbert {
2121104d9baSTom Herbert 	const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type];
2131104d9baSTom Herbert 
2141104d9baSTom Herbert 	if (ops->destroy_state) {
2151104d9baSTom Herbert 		ops->destroy_state(lws);
2161104d9baSTom Herbert 		kfree_rcu(lws, rcu);
2171104d9baSTom Herbert 	} else {
2181104d9baSTom Herbert 		kfree(lws);
2191104d9baSTom Herbert 	}
22085c81401SRobert Shearman 	module_put(ops->owner);
2211104d9baSTom Herbert }
2221104d9baSTom Herbert EXPORT_SYMBOL(lwtstate_free);
2231104d9baSTom Herbert 
224499a2425SRoopa Prabhu int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate)
225499a2425SRoopa Prabhu {
226499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
227499a2425SRoopa Prabhu 	struct nlattr *nest;
22839f37095SDan Carpenter 	int ret;
229499a2425SRoopa Prabhu 
230499a2425SRoopa Prabhu 	if (!lwtstate)
231499a2425SRoopa Prabhu 		return 0;
232499a2425SRoopa Prabhu 
233499a2425SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
234499a2425SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
235499a2425SRoopa Prabhu 		return 0;
236499a2425SRoopa Prabhu 
237499a2425SRoopa Prabhu 	nest = nla_nest_start(skb, RTA_ENCAP);
238a50fe0ffSPan Bian 	if (!nest)
23939f37095SDan Carpenter 		return -EMSGSIZE;
24039f37095SDan Carpenter 
24139f37095SDan Carpenter 	ret = -EOPNOTSUPP;
242499a2425SRoopa Prabhu 	rcu_read_lock();
243499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
244499a2425SRoopa Prabhu 	if (likely(ops && ops->fill_encap))
245499a2425SRoopa Prabhu 		ret = ops->fill_encap(skb, lwtstate);
246499a2425SRoopa Prabhu 	rcu_read_unlock();
247499a2425SRoopa Prabhu 
248499a2425SRoopa Prabhu 	if (ret)
249499a2425SRoopa Prabhu 		goto nla_put_failure;
250499a2425SRoopa Prabhu 	nla_nest_end(skb, nest);
251499a2425SRoopa Prabhu 	ret = nla_put_u16(skb, RTA_ENCAP_TYPE, lwtstate->type);
252499a2425SRoopa Prabhu 	if (ret)
253499a2425SRoopa Prabhu 		goto nla_put_failure;
254499a2425SRoopa Prabhu 
255499a2425SRoopa Prabhu 	return 0;
256499a2425SRoopa Prabhu 
257499a2425SRoopa Prabhu nla_put_failure:
258499a2425SRoopa Prabhu 	nla_nest_cancel(skb, nest);
259499a2425SRoopa Prabhu 
260499a2425SRoopa Prabhu 	return (ret == -EOPNOTSUPP ? 0 : ret);
261499a2425SRoopa Prabhu }
262499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_fill_encap);
263499a2425SRoopa Prabhu 
264499a2425SRoopa Prabhu int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate)
265499a2425SRoopa Prabhu {
266499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
267499a2425SRoopa Prabhu 	int ret = 0;
268499a2425SRoopa Prabhu 
269499a2425SRoopa Prabhu 	if (!lwtstate)
270499a2425SRoopa Prabhu 		return 0;
271499a2425SRoopa Prabhu 
272499a2425SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
273499a2425SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
274499a2425SRoopa Prabhu 		return 0;
275499a2425SRoopa Prabhu 
276499a2425SRoopa Prabhu 	rcu_read_lock();
277499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
278499a2425SRoopa Prabhu 	if (likely(ops && ops->get_encap_size))
279499a2425SRoopa Prabhu 		ret = nla_total_size(ops->get_encap_size(lwtstate));
280499a2425SRoopa Prabhu 	rcu_read_unlock();
281499a2425SRoopa Prabhu 
282499a2425SRoopa Prabhu 	return ret;
283499a2425SRoopa Prabhu }
284499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_get_encap_size);
285499a2425SRoopa Prabhu 
286499a2425SRoopa Prabhu int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
287499a2425SRoopa Prabhu {
288499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
289499a2425SRoopa Prabhu 	int ret = 0;
290499a2425SRoopa Prabhu 
291499a2425SRoopa Prabhu 	if (!a && !b)
292499a2425SRoopa Prabhu 		return 0;
293499a2425SRoopa Prabhu 
294499a2425SRoopa Prabhu 	if (!a || !b)
295499a2425SRoopa Prabhu 		return 1;
296499a2425SRoopa Prabhu 
297499a2425SRoopa Prabhu 	if (a->type != b->type)
298499a2425SRoopa Prabhu 		return 1;
299499a2425SRoopa Prabhu 
300499a2425SRoopa Prabhu 	if (a->type == LWTUNNEL_ENCAP_NONE ||
301499a2425SRoopa Prabhu 	    a->type > LWTUNNEL_ENCAP_MAX)
302499a2425SRoopa Prabhu 		return 0;
303499a2425SRoopa Prabhu 
304499a2425SRoopa Prabhu 	rcu_read_lock();
305499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[a->type]);
306499a2425SRoopa Prabhu 	if (likely(ops && ops->cmp_encap))
307499a2425SRoopa Prabhu 		ret = ops->cmp_encap(a, b);
308499a2425SRoopa Prabhu 	rcu_read_unlock();
309499a2425SRoopa Prabhu 
310499a2425SRoopa Prabhu 	return ret;
311499a2425SRoopa Prabhu }
312499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_cmp_encap);
313ffce4196SRoopa Prabhu 
314ede2059dSEric W. Biederman int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
315ffce4196SRoopa Prabhu {
31661adedf3SJiri Benc 	struct dst_entry *dst = skb_dst(skb);
317ffce4196SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
31861adedf3SJiri Benc 	struct lwtunnel_state *lwtstate;
319ffce4196SRoopa Prabhu 	int ret = -EINVAL;
320ffce4196SRoopa Prabhu 
32161adedf3SJiri Benc 	if (!dst)
322ffce4196SRoopa Prabhu 		goto drop;
32361adedf3SJiri Benc 	lwtstate = dst->lwtstate;
324ffce4196SRoopa Prabhu 
325ffce4196SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
326ffce4196SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
327ffce4196SRoopa Prabhu 		return 0;
328ffce4196SRoopa Prabhu 
329ffce4196SRoopa Prabhu 	ret = -EOPNOTSUPP;
330ffce4196SRoopa Prabhu 	rcu_read_lock();
331ffce4196SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
332ffce4196SRoopa Prabhu 	if (likely(ops && ops->output))
333ede2059dSEric W. Biederman 		ret = ops->output(net, sk, skb);
334ffce4196SRoopa Prabhu 	rcu_read_unlock();
335ffce4196SRoopa Prabhu 
336ffce4196SRoopa Prabhu 	if (ret == -EOPNOTSUPP)
337ffce4196SRoopa Prabhu 		goto drop;
338ffce4196SRoopa Prabhu 
339ffce4196SRoopa Prabhu 	return ret;
340ffce4196SRoopa Prabhu 
341ffce4196SRoopa Prabhu drop:
342e11f40b9SDan Carpenter 	kfree_skb(skb);
343ffce4196SRoopa Prabhu 
344ffce4196SRoopa Prabhu 	return ret;
345ffce4196SRoopa Prabhu }
346ffce4196SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_output);
34725368623STom Herbert 
34814972cbdSRoopa Prabhu int lwtunnel_xmit(struct sk_buff *skb)
34914972cbdSRoopa Prabhu {
35014972cbdSRoopa Prabhu 	struct dst_entry *dst = skb_dst(skb);
35114972cbdSRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
35214972cbdSRoopa Prabhu 	struct lwtunnel_state *lwtstate;
35314972cbdSRoopa Prabhu 	int ret = -EINVAL;
35414972cbdSRoopa Prabhu 
35514972cbdSRoopa Prabhu 	if (!dst)
35614972cbdSRoopa Prabhu 		goto drop;
35714972cbdSRoopa Prabhu 
35814972cbdSRoopa Prabhu 	lwtstate = dst->lwtstate;
35914972cbdSRoopa Prabhu 
36014972cbdSRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
36114972cbdSRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
36214972cbdSRoopa Prabhu 		return 0;
36314972cbdSRoopa Prabhu 
36414972cbdSRoopa Prabhu 	ret = -EOPNOTSUPP;
36514972cbdSRoopa Prabhu 	rcu_read_lock();
36614972cbdSRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
36714972cbdSRoopa Prabhu 	if (likely(ops && ops->xmit))
36814972cbdSRoopa Prabhu 		ret = ops->xmit(skb);
36914972cbdSRoopa Prabhu 	rcu_read_unlock();
37014972cbdSRoopa Prabhu 
37114972cbdSRoopa Prabhu 	if (ret == -EOPNOTSUPP)
37214972cbdSRoopa Prabhu 		goto drop;
37314972cbdSRoopa Prabhu 
37414972cbdSRoopa Prabhu 	return ret;
37514972cbdSRoopa Prabhu 
37614972cbdSRoopa Prabhu drop:
37714972cbdSRoopa Prabhu 	kfree_skb(skb);
37814972cbdSRoopa Prabhu 
37914972cbdSRoopa Prabhu 	return ret;
38014972cbdSRoopa Prabhu }
38114972cbdSRoopa Prabhu EXPORT_SYMBOL(lwtunnel_xmit);
38214972cbdSRoopa Prabhu 
38361adedf3SJiri Benc int lwtunnel_input(struct sk_buff *skb)
38425368623STom Herbert {
38561adedf3SJiri Benc 	struct dst_entry *dst = skb_dst(skb);
38625368623STom Herbert 	const struct lwtunnel_encap_ops *ops;
38761adedf3SJiri Benc 	struct lwtunnel_state *lwtstate;
38825368623STom Herbert 	int ret = -EINVAL;
38925368623STom Herbert 
39061adedf3SJiri Benc 	if (!dst)
39125368623STom Herbert 		goto drop;
39261adedf3SJiri Benc 	lwtstate = dst->lwtstate;
39325368623STom Herbert 
39425368623STom Herbert 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
39525368623STom Herbert 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
39625368623STom Herbert 		return 0;
39725368623STom Herbert 
39825368623STom Herbert 	ret = -EOPNOTSUPP;
39925368623STom Herbert 	rcu_read_lock();
40025368623STom Herbert 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
40125368623STom Herbert 	if (likely(ops && ops->input))
40225368623STom Herbert 		ret = ops->input(skb);
40325368623STom Herbert 	rcu_read_unlock();
40425368623STom Herbert 
40525368623STom Herbert 	if (ret == -EOPNOTSUPP)
40625368623STom Herbert 		goto drop;
40725368623STom Herbert 
40825368623STom Herbert 	return ret;
40925368623STom Herbert 
41025368623STom Herbert drop:
41125368623STom Herbert 	kfree_skb(skb);
41225368623STom Herbert 
41325368623STom Herbert 	return ret;
41425368623STom Herbert }
41525368623STom Herbert EXPORT_SYMBOL(lwtunnel_input);
416