xref: /openbmc/linux/net/core/lwtunnel.c (revision 3d25eabb)
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";
47d1df6fd8SDavid Lebrun 	case LWTUNNEL_ENCAP_SEG6_LOCAL:
48d1df6fd8SDavid Lebrun 		return "SEG6LOCAL";
49745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_IP6:
50745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_IP:
51745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_NONE:
52745041e2SRobert Shearman 	case __LWTUNNEL_ENCAP_MAX:
53745041e2SRobert Shearman 		/* should not have got here */
54745041e2SRobert Shearman 		WARN_ON(1);
55745041e2SRobert Shearman 		break;
56745041e2SRobert Shearman 	}
57745041e2SRobert Shearman 	return NULL;
58745041e2SRobert Shearman }
59745041e2SRobert Shearman 
60745041e2SRobert Shearman #endif /* CONFIG_MODULES */
61745041e2SRobert Shearman 
62499a2425SRoopa Prabhu struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
63499a2425SRoopa Prabhu {
64499a2425SRoopa Prabhu 	struct lwtunnel_state *lws;
65499a2425SRoopa Prabhu 
66499a2425SRoopa Prabhu 	lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC);
67499a2425SRoopa Prabhu 
68499a2425SRoopa Prabhu 	return lws;
69499a2425SRoopa Prabhu }
7008bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_state_alloc);
71499a2425SRoopa Prabhu 
7292a99bf3SThomas Graf static const struct lwtunnel_encap_ops __rcu *
73499a2425SRoopa Prabhu 		lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
74499a2425SRoopa Prabhu 
75499a2425SRoopa Prabhu int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
76499a2425SRoopa Prabhu 			   unsigned int num)
77499a2425SRoopa Prabhu {
78499a2425SRoopa Prabhu 	if (num > LWTUNNEL_ENCAP_MAX)
79499a2425SRoopa Prabhu 		return -ERANGE;
80499a2425SRoopa Prabhu 
81499a2425SRoopa Prabhu 	return !cmpxchg((const struct lwtunnel_encap_ops **)
82499a2425SRoopa Prabhu 			&lwtun_encaps[num],
83499a2425SRoopa Prabhu 			NULL, ops) ? 0 : -1;
84499a2425SRoopa Prabhu }
8508bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_encap_add_ops);
86499a2425SRoopa Prabhu 
87499a2425SRoopa Prabhu int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops,
88499a2425SRoopa Prabhu 			   unsigned int encap_type)
89499a2425SRoopa Prabhu {
90499a2425SRoopa Prabhu 	int ret;
91499a2425SRoopa Prabhu 
92499a2425SRoopa Prabhu 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
93499a2425SRoopa Prabhu 	    encap_type > LWTUNNEL_ENCAP_MAX)
94499a2425SRoopa Prabhu 		return -ERANGE;
95499a2425SRoopa Prabhu 
96499a2425SRoopa Prabhu 	ret = (cmpxchg((const struct lwtunnel_encap_ops **)
97499a2425SRoopa Prabhu 		       &lwtun_encaps[encap_type],
98499a2425SRoopa Prabhu 		       ops, NULL) == ops) ? 0 : -1;
99499a2425SRoopa Prabhu 
100499a2425SRoopa Prabhu 	synchronize_net();
101499a2425SRoopa Prabhu 
102499a2425SRoopa Prabhu 	return ret;
103499a2425SRoopa Prabhu }
10408bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_encap_del_ops);
105499a2425SRoopa Prabhu 
10630357d7dSDavid Ahern int lwtunnel_build_state(u16 encap_type,
107127eb7cdSTom Herbert 			 struct nlattr *encap, unsigned int family,
1089ae28727SDavid Ahern 			 const void *cfg, struct lwtunnel_state **lws,
1099ae28727SDavid Ahern 			 struct netlink_ext_ack *extack)
110499a2425SRoopa Prabhu {
111499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
1129ae28727SDavid Ahern 	bool found = false;
113499a2425SRoopa Prabhu 	int ret = -EINVAL;
114499a2425SRoopa Prabhu 
115499a2425SRoopa Prabhu 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
1169ae28727SDavid Ahern 	    encap_type > LWTUNNEL_ENCAP_MAX) {
1179ae28727SDavid Ahern 		NL_SET_ERR_MSG_ATTR(extack, encap,
1189ae28727SDavid Ahern 				    "Unknown LWT encapsulation type");
119499a2425SRoopa Prabhu 		return ret;
1209ae28727SDavid Ahern 	}
121499a2425SRoopa Prabhu 
122499a2425SRoopa Prabhu 	ret = -EOPNOTSUPP;
123499a2425SRoopa Prabhu 	rcu_read_lock();
124499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[encap_type]);
1253d25eabbSwenxu 	if (likely(ops && ops->build_state && try_module_get(ops->owner)))
1269ae28727SDavid Ahern 		found = true;
1273d25eabbSwenxu 	rcu_read_unlock();
1283d25eabbSwenxu 
1293d25eabbSwenxu 	if (found) {
1309ae28727SDavid Ahern 		ret = ops->build_state(encap, family, cfg, lws, extack);
13185c81401SRobert Shearman 		if (ret)
13285c81401SRobert Shearman 			module_put(ops->owner);
1333d25eabbSwenxu 	} else {
1349ae28727SDavid Ahern 		/* don't rely on -EOPNOTSUPP to detect match as build_state
1359ae28727SDavid Ahern 		 * handlers could return it
1369ae28727SDavid Ahern 		 */
1379ae28727SDavid Ahern 		NL_SET_ERR_MSG_ATTR(extack, encap,
1389ae28727SDavid Ahern 				    "LWT encapsulation type not supported");
1399ae28727SDavid Ahern 	}
1409ae28727SDavid Ahern 
141499a2425SRoopa Prabhu 	return ret;
142499a2425SRoopa Prabhu }
14308bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_build_state);
144499a2425SRoopa Prabhu 
145c255bd68SDavid Ahern int lwtunnel_valid_encap_type(u16 encap_type, struct netlink_ext_ack *extack)
1469ed59592SDavid Ahern {
1479ed59592SDavid Ahern 	const struct lwtunnel_encap_ops *ops;
1489ed59592SDavid Ahern 	int ret = -EINVAL;
1499ed59592SDavid Ahern 
1509ed59592SDavid Ahern 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
151c255bd68SDavid Ahern 	    encap_type > LWTUNNEL_ENCAP_MAX) {
152c255bd68SDavid Ahern 		NL_SET_ERR_MSG(extack, "Unknown lwt encapsulation type");
1539ed59592SDavid Ahern 		return ret;
154c255bd68SDavid Ahern 	}
1559ed59592SDavid Ahern 
1569ed59592SDavid Ahern 	rcu_read_lock();
1579ed59592SDavid Ahern 	ops = rcu_dereference(lwtun_encaps[encap_type]);
1589ed59592SDavid Ahern 	rcu_read_unlock();
1599ed59592SDavid Ahern #ifdef CONFIG_MODULES
1609ed59592SDavid Ahern 	if (!ops) {
1619ed59592SDavid Ahern 		const char *encap_type_str = lwtunnel_encap_str(encap_type);
1629ed59592SDavid Ahern 
1639ed59592SDavid Ahern 		if (encap_type_str) {
1649ed59592SDavid Ahern 			__rtnl_unlock();
1659ed59592SDavid Ahern 			request_module("rtnl-lwt-%s", encap_type_str);
1669ed59592SDavid Ahern 			rtnl_lock();
1679ed59592SDavid Ahern 
1689ed59592SDavid Ahern 			rcu_read_lock();
1699ed59592SDavid Ahern 			ops = rcu_dereference(lwtun_encaps[encap_type]);
1709ed59592SDavid Ahern 			rcu_read_unlock();
1719ed59592SDavid Ahern 		}
1729ed59592SDavid Ahern 	}
1739ed59592SDavid Ahern #endif
174c255bd68SDavid Ahern 	ret = ops ? 0 : -EOPNOTSUPP;
175c255bd68SDavid Ahern 	if (ret < 0)
176c255bd68SDavid Ahern 		NL_SET_ERR_MSG(extack, "lwt encapsulation type not supported");
177c255bd68SDavid Ahern 
178c255bd68SDavid Ahern 	return ret;
1799ed59592SDavid Ahern }
18008bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type);
1819ed59592SDavid Ahern 
182c255bd68SDavid Ahern int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining,
183c255bd68SDavid Ahern 				   struct netlink_ext_ack *extack)
1849ed59592SDavid Ahern {
1859ed59592SDavid Ahern 	struct rtnexthop *rtnh = (struct rtnexthop *)attr;
1869ed59592SDavid Ahern 	struct nlattr *nla_entype;
1879ed59592SDavid Ahern 	struct nlattr *attrs;
1889ed59592SDavid Ahern 	u16 encap_type;
1899ed59592SDavid Ahern 	int attrlen;
1909ed59592SDavid Ahern 
1919ed59592SDavid Ahern 	while (rtnh_ok(rtnh, remaining)) {
1929ed59592SDavid Ahern 		attrlen = rtnh_attrlen(rtnh);
1939ed59592SDavid Ahern 		if (attrlen > 0) {
1949ed59592SDavid Ahern 			attrs = rtnh_attrs(rtnh);
1959ed59592SDavid Ahern 			nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
1969ed59592SDavid Ahern 
1979ed59592SDavid Ahern 			if (nla_entype) {
1989ed59592SDavid Ahern 				encap_type = nla_get_u16(nla_entype);
1999ed59592SDavid Ahern 
200c255bd68SDavid Ahern 				if (lwtunnel_valid_encap_type(encap_type,
201c255bd68SDavid Ahern 							      extack) != 0)
2029ed59592SDavid Ahern 					return -EOPNOTSUPP;
2039ed59592SDavid Ahern 			}
2049ed59592SDavid Ahern 		}
2059ed59592SDavid Ahern 		rtnh = rtnh_next(rtnh, &remaining);
2069ed59592SDavid Ahern 	}
2079ed59592SDavid Ahern 
2089ed59592SDavid Ahern 	return 0;
2099ed59592SDavid Ahern }
21008bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type_attr);
2119ed59592SDavid Ahern 
2121104d9baSTom Herbert void lwtstate_free(struct lwtunnel_state *lws)
2131104d9baSTom Herbert {
2141104d9baSTom Herbert 	const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type];
2151104d9baSTom Herbert 
2161104d9baSTom Herbert 	if (ops->destroy_state) {
2171104d9baSTom Herbert 		ops->destroy_state(lws);
2181104d9baSTom Herbert 		kfree_rcu(lws, rcu);
2191104d9baSTom Herbert 	} else {
2201104d9baSTom Herbert 		kfree(lws);
2211104d9baSTom Herbert 	}
22285c81401SRobert Shearman 	module_put(ops->owner);
2231104d9baSTom Herbert }
22408bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtstate_free);
2251104d9baSTom Herbert 
226499a2425SRoopa Prabhu int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate)
227499a2425SRoopa Prabhu {
228499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
229499a2425SRoopa Prabhu 	struct nlattr *nest;
23039f37095SDan Carpenter 	int ret;
231499a2425SRoopa Prabhu 
232499a2425SRoopa Prabhu 	if (!lwtstate)
233499a2425SRoopa Prabhu 		return 0;
234499a2425SRoopa Prabhu 
235499a2425SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
236499a2425SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
237499a2425SRoopa Prabhu 		return 0;
238499a2425SRoopa Prabhu 
239499a2425SRoopa Prabhu 	nest = nla_nest_start(skb, RTA_ENCAP);
240a50fe0ffSPan Bian 	if (!nest)
24139f37095SDan Carpenter 		return -EMSGSIZE;
24239f37095SDan Carpenter 
24339f37095SDan Carpenter 	ret = -EOPNOTSUPP;
244499a2425SRoopa Prabhu 	rcu_read_lock();
245499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
246499a2425SRoopa Prabhu 	if (likely(ops && ops->fill_encap))
247499a2425SRoopa Prabhu 		ret = ops->fill_encap(skb, lwtstate);
248499a2425SRoopa Prabhu 	rcu_read_unlock();
249499a2425SRoopa Prabhu 
250499a2425SRoopa Prabhu 	if (ret)
251499a2425SRoopa Prabhu 		goto nla_put_failure;
252499a2425SRoopa Prabhu 	nla_nest_end(skb, nest);
253499a2425SRoopa Prabhu 	ret = nla_put_u16(skb, RTA_ENCAP_TYPE, lwtstate->type);
254499a2425SRoopa Prabhu 	if (ret)
255499a2425SRoopa Prabhu 		goto nla_put_failure;
256499a2425SRoopa Prabhu 
257499a2425SRoopa Prabhu 	return 0;
258499a2425SRoopa Prabhu 
259499a2425SRoopa Prabhu nla_put_failure:
260499a2425SRoopa Prabhu 	nla_nest_cancel(skb, nest);
261499a2425SRoopa Prabhu 
262499a2425SRoopa Prabhu 	return (ret == -EOPNOTSUPP ? 0 : ret);
263499a2425SRoopa Prabhu }
26408bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_fill_encap);
265499a2425SRoopa Prabhu 
266499a2425SRoopa Prabhu int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate)
267499a2425SRoopa Prabhu {
268499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
269499a2425SRoopa Prabhu 	int ret = 0;
270499a2425SRoopa Prabhu 
271499a2425SRoopa Prabhu 	if (!lwtstate)
272499a2425SRoopa Prabhu 		return 0;
273499a2425SRoopa Prabhu 
274499a2425SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
275499a2425SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
276499a2425SRoopa Prabhu 		return 0;
277499a2425SRoopa Prabhu 
278499a2425SRoopa Prabhu 	rcu_read_lock();
279499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
280499a2425SRoopa Prabhu 	if (likely(ops && ops->get_encap_size))
281499a2425SRoopa Prabhu 		ret = nla_total_size(ops->get_encap_size(lwtstate));
282499a2425SRoopa Prabhu 	rcu_read_unlock();
283499a2425SRoopa Prabhu 
284499a2425SRoopa Prabhu 	return ret;
285499a2425SRoopa Prabhu }
28608bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_get_encap_size);
287499a2425SRoopa Prabhu 
288499a2425SRoopa Prabhu int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
289499a2425SRoopa Prabhu {
290499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
291499a2425SRoopa Prabhu 	int ret = 0;
292499a2425SRoopa Prabhu 
293499a2425SRoopa Prabhu 	if (!a && !b)
294499a2425SRoopa Prabhu 		return 0;
295499a2425SRoopa Prabhu 
296499a2425SRoopa Prabhu 	if (!a || !b)
297499a2425SRoopa Prabhu 		return 1;
298499a2425SRoopa Prabhu 
299499a2425SRoopa Prabhu 	if (a->type != b->type)
300499a2425SRoopa Prabhu 		return 1;
301499a2425SRoopa Prabhu 
302499a2425SRoopa Prabhu 	if (a->type == LWTUNNEL_ENCAP_NONE ||
303499a2425SRoopa Prabhu 	    a->type > LWTUNNEL_ENCAP_MAX)
304499a2425SRoopa Prabhu 		return 0;
305499a2425SRoopa Prabhu 
306499a2425SRoopa Prabhu 	rcu_read_lock();
307499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[a->type]);
308499a2425SRoopa Prabhu 	if (likely(ops && ops->cmp_encap))
309499a2425SRoopa Prabhu 		ret = ops->cmp_encap(a, b);
310499a2425SRoopa Prabhu 	rcu_read_unlock();
311499a2425SRoopa Prabhu 
312499a2425SRoopa Prabhu 	return ret;
313499a2425SRoopa Prabhu }
31408bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_cmp_encap);
315ffce4196SRoopa Prabhu 
316ede2059dSEric W. Biederman int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
317ffce4196SRoopa Prabhu {
31861adedf3SJiri Benc 	struct dst_entry *dst = skb_dst(skb);
319ffce4196SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
32061adedf3SJiri Benc 	struct lwtunnel_state *lwtstate;
321ffce4196SRoopa Prabhu 	int ret = -EINVAL;
322ffce4196SRoopa Prabhu 
32361adedf3SJiri Benc 	if (!dst)
324ffce4196SRoopa Prabhu 		goto drop;
32561adedf3SJiri Benc 	lwtstate = dst->lwtstate;
326ffce4196SRoopa Prabhu 
327ffce4196SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
328ffce4196SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
329ffce4196SRoopa Prabhu 		return 0;
330ffce4196SRoopa Prabhu 
331ffce4196SRoopa Prabhu 	ret = -EOPNOTSUPP;
332ffce4196SRoopa Prabhu 	rcu_read_lock();
333ffce4196SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
334ffce4196SRoopa Prabhu 	if (likely(ops && ops->output))
335ede2059dSEric W. Biederman 		ret = ops->output(net, sk, skb);
336ffce4196SRoopa Prabhu 	rcu_read_unlock();
337ffce4196SRoopa Prabhu 
338ffce4196SRoopa Prabhu 	if (ret == -EOPNOTSUPP)
339ffce4196SRoopa Prabhu 		goto drop;
340ffce4196SRoopa Prabhu 
341ffce4196SRoopa Prabhu 	return ret;
342ffce4196SRoopa Prabhu 
343ffce4196SRoopa Prabhu drop:
344e11f40b9SDan Carpenter 	kfree_skb(skb);
345ffce4196SRoopa Prabhu 
346ffce4196SRoopa Prabhu 	return ret;
347ffce4196SRoopa Prabhu }
34808bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_output);
34925368623STom Herbert 
35014972cbdSRoopa Prabhu int lwtunnel_xmit(struct sk_buff *skb)
35114972cbdSRoopa Prabhu {
35214972cbdSRoopa Prabhu 	struct dst_entry *dst = skb_dst(skb);
35314972cbdSRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
35414972cbdSRoopa Prabhu 	struct lwtunnel_state *lwtstate;
35514972cbdSRoopa Prabhu 	int ret = -EINVAL;
35614972cbdSRoopa Prabhu 
35714972cbdSRoopa Prabhu 	if (!dst)
35814972cbdSRoopa Prabhu 		goto drop;
35914972cbdSRoopa Prabhu 
36014972cbdSRoopa Prabhu 	lwtstate = dst->lwtstate;
36114972cbdSRoopa Prabhu 
36214972cbdSRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
36314972cbdSRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
36414972cbdSRoopa Prabhu 		return 0;
36514972cbdSRoopa Prabhu 
36614972cbdSRoopa Prabhu 	ret = -EOPNOTSUPP;
36714972cbdSRoopa Prabhu 	rcu_read_lock();
36814972cbdSRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
36914972cbdSRoopa Prabhu 	if (likely(ops && ops->xmit))
37014972cbdSRoopa Prabhu 		ret = ops->xmit(skb);
37114972cbdSRoopa Prabhu 	rcu_read_unlock();
37214972cbdSRoopa Prabhu 
37314972cbdSRoopa Prabhu 	if (ret == -EOPNOTSUPP)
37414972cbdSRoopa Prabhu 		goto drop;
37514972cbdSRoopa Prabhu 
37614972cbdSRoopa Prabhu 	return ret;
37714972cbdSRoopa Prabhu 
37814972cbdSRoopa Prabhu drop:
37914972cbdSRoopa Prabhu 	kfree_skb(skb);
38014972cbdSRoopa Prabhu 
38114972cbdSRoopa Prabhu 	return ret;
38214972cbdSRoopa Prabhu }
38308bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_xmit);
38414972cbdSRoopa Prabhu 
38561adedf3SJiri Benc int lwtunnel_input(struct sk_buff *skb)
38625368623STom Herbert {
38761adedf3SJiri Benc 	struct dst_entry *dst = skb_dst(skb);
38825368623STom Herbert 	const struct lwtunnel_encap_ops *ops;
38961adedf3SJiri Benc 	struct lwtunnel_state *lwtstate;
39025368623STom Herbert 	int ret = -EINVAL;
39125368623STom Herbert 
39261adedf3SJiri Benc 	if (!dst)
39325368623STom Herbert 		goto drop;
39461adedf3SJiri Benc 	lwtstate = dst->lwtstate;
39525368623STom Herbert 
39625368623STom Herbert 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
39725368623STom Herbert 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
39825368623STom Herbert 		return 0;
39925368623STom Herbert 
40025368623STom Herbert 	ret = -EOPNOTSUPP;
40125368623STom Herbert 	rcu_read_lock();
40225368623STom Herbert 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
40325368623STom Herbert 	if (likely(ops && ops->input))
40425368623STom Herbert 		ret = ops->input(skb);
40525368623STom Herbert 	rcu_read_unlock();
40625368623STom Herbert 
40725368623STom Herbert 	if (ret == -EOPNOTSUPP)
40825368623STom Herbert 		goto drop;
40925368623STom Herbert 
41025368623STom Herbert 	return ret;
41125368623STom Herbert 
41225368623STom Herbert drop:
41325368623STom Herbert 	kfree_skb(skb);
41425368623STom Herbert 
41525368623STom Herbert 	return ret;
41625368623STom Herbert }
41708bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_input);
418