xref: /openbmc/linux/net/core/lwtunnel.c (revision d83f7040)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2499a2425SRoopa Prabhu /*
3499a2425SRoopa Prabhu  * lwtunnel	Infrastructure for light weight tunnels like mpls
4499a2425SRoopa Prabhu  *
5499a2425SRoopa Prabhu  * Authors:	Roopa Prabhu, <roopa@cumulusnetworks.com>
6499a2425SRoopa Prabhu  */
7499a2425SRoopa Prabhu 
8499a2425SRoopa Prabhu #include <linux/capability.h>
9499a2425SRoopa Prabhu #include <linux/module.h>
10499a2425SRoopa Prabhu #include <linux/types.h>
11499a2425SRoopa Prabhu #include <linux/kernel.h>
12499a2425SRoopa Prabhu #include <linux/slab.h>
13499a2425SRoopa Prabhu #include <linux/uaccess.h>
14499a2425SRoopa Prabhu #include <linux/skbuff.h>
15499a2425SRoopa Prabhu #include <linux/netdevice.h>
16499a2425SRoopa Prabhu #include <linux/lwtunnel.h>
17499a2425SRoopa Prabhu #include <linux/in.h>
18499a2425SRoopa Prabhu #include <linux/init.h>
19499a2425SRoopa Prabhu #include <linux/err.h>
20499a2425SRoopa Prabhu 
21499a2425SRoopa Prabhu #include <net/lwtunnel.h>
22499a2425SRoopa Prabhu #include <net/rtnetlink.h>
23ffce4196SRoopa Prabhu #include <net/ip6_fib.h>
243c618c1dSDavid Ahern #include <net/rtnh.h>
25499a2425SRoopa Prabhu 
267a3f5b0dSRyoga Saito DEFINE_STATIC_KEY_FALSE(nf_hooks_lwtunnel_enabled);
277a3f5b0dSRyoga Saito EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_enabled);
287a3f5b0dSRyoga Saito 
29745041e2SRobert Shearman #ifdef CONFIG_MODULES
30745041e2SRobert Shearman 
lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)31745041e2SRobert Shearman static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
32745041e2SRobert Shearman {
33745041e2SRobert Shearman 	/* Only lwt encaps implemented without using an interface for
34745041e2SRobert Shearman 	 * the encap need to return a string here.
35745041e2SRobert Shearman 	 */
36745041e2SRobert Shearman 	switch (encap_type) {
37745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_MPLS:
38745041e2SRobert Shearman 		return "MPLS";
39745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_ILA:
40745041e2SRobert Shearman 		return "ILA";
416c8702c6SDavid Lebrun 	case LWTUNNEL_ENCAP_SEG6:
426c8702c6SDavid Lebrun 		return "SEG6";
433a0af8fdSThomas Graf 	case LWTUNNEL_ENCAP_BPF:
443a0af8fdSThomas Graf 		return "BPF";
45d1df6fd8SDavid Lebrun 	case LWTUNNEL_ENCAP_SEG6_LOCAL:
46d1df6fd8SDavid Lebrun 		return "SEG6LOCAL";
47a7a29f9cSAlexander Aring 	case LWTUNNEL_ENCAP_RPL:
48a7a29f9cSAlexander Aring 		return "RPL";
493edede08SJustin Iurman 	case LWTUNNEL_ENCAP_IOAM6:
503edede08SJustin Iurman 		return "IOAM6";
51*d83f7040SEyal Birger 	case LWTUNNEL_ENCAP_XFRM:
52*d83f7040SEyal Birger 		/* module autoload not supported for encap type */
53*d83f7040SEyal Birger 		return NULL;
54745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_IP6:
55745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_IP:
56745041e2SRobert Shearman 	case LWTUNNEL_ENCAP_NONE:
57745041e2SRobert Shearman 	case __LWTUNNEL_ENCAP_MAX:
58745041e2SRobert Shearman 		/* should not have got here */
59745041e2SRobert Shearman 		WARN_ON(1);
60745041e2SRobert Shearman 		break;
61745041e2SRobert Shearman 	}
62745041e2SRobert Shearman 	return NULL;
63745041e2SRobert Shearman }
64745041e2SRobert Shearman 
65745041e2SRobert Shearman #endif /* CONFIG_MODULES */
66745041e2SRobert Shearman 
lwtunnel_state_alloc(int encap_len)67499a2425SRoopa Prabhu struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
68499a2425SRoopa Prabhu {
69499a2425SRoopa Prabhu 	struct lwtunnel_state *lws;
70499a2425SRoopa Prabhu 
71499a2425SRoopa Prabhu 	lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC);
72499a2425SRoopa Prabhu 
73499a2425SRoopa Prabhu 	return lws;
74499a2425SRoopa Prabhu }
7508bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_state_alloc);
76499a2425SRoopa Prabhu 
7792a99bf3SThomas Graf static const struct lwtunnel_encap_ops __rcu *
78499a2425SRoopa Prabhu 		lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
79499a2425SRoopa Prabhu 
lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops * ops,unsigned int num)80499a2425SRoopa Prabhu int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
81499a2425SRoopa Prabhu 			   unsigned int num)
82499a2425SRoopa Prabhu {
83499a2425SRoopa Prabhu 	if (num > LWTUNNEL_ENCAP_MAX)
84499a2425SRoopa Prabhu 		return -ERANGE;
85499a2425SRoopa Prabhu 
86499a2425SRoopa Prabhu 	return !cmpxchg((const struct lwtunnel_encap_ops **)
87499a2425SRoopa Prabhu 			&lwtun_encaps[num],
88499a2425SRoopa Prabhu 			NULL, ops) ? 0 : -1;
89499a2425SRoopa Prabhu }
9008bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_encap_add_ops);
91499a2425SRoopa Prabhu 
lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops * ops,unsigned int encap_type)92499a2425SRoopa Prabhu int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops,
93499a2425SRoopa Prabhu 			   unsigned int encap_type)
94499a2425SRoopa Prabhu {
95499a2425SRoopa Prabhu 	int ret;
96499a2425SRoopa Prabhu 
97499a2425SRoopa Prabhu 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
98499a2425SRoopa Prabhu 	    encap_type > LWTUNNEL_ENCAP_MAX)
99499a2425SRoopa Prabhu 		return -ERANGE;
100499a2425SRoopa Prabhu 
101499a2425SRoopa Prabhu 	ret = (cmpxchg((const struct lwtunnel_encap_ops **)
102499a2425SRoopa Prabhu 		       &lwtun_encaps[encap_type],
103499a2425SRoopa Prabhu 		       ops, NULL) == ops) ? 0 : -1;
104499a2425SRoopa Prabhu 
105499a2425SRoopa Prabhu 	synchronize_net();
106499a2425SRoopa Prabhu 
107499a2425SRoopa Prabhu 	return ret;
108499a2425SRoopa Prabhu }
10908bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_encap_del_ops);
110499a2425SRoopa Prabhu 
lwtunnel_build_state(struct net * net,u16 encap_type,struct nlattr * encap,unsigned int family,const void * cfg,struct lwtunnel_state ** lws,struct netlink_ext_ack * extack)111faee6769SAlexander Aring int lwtunnel_build_state(struct net *net, u16 encap_type,
112127eb7cdSTom Herbert 			 struct nlattr *encap, unsigned int family,
1139ae28727SDavid Ahern 			 const void *cfg, struct lwtunnel_state **lws,
1149ae28727SDavid Ahern 			 struct netlink_ext_ack *extack)
115499a2425SRoopa Prabhu {
116499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
1179ae28727SDavid Ahern 	bool found = false;
118499a2425SRoopa Prabhu 	int ret = -EINVAL;
119499a2425SRoopa Prabhu 
120499a2425SRoopa Prabhu 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
1219ae28727SDavid Ahern 	    encap_type > LWTUNNEL_ENCAP_MAX) {
1229ae28727SDavid Ahern 		NL_SET_ERR_MSG_ATTR(extack, encap,
1239ae28727SDavid Ahern 				    "Unknown LWT encapsulation type");
124499a2425SRoopa Prabhu 		return ret;
1259ae28727SDavid Ahern 	}
126499a2425SRoopa Prabhu 
127499a2425SRoopa Prabhu 	ret = -EOPNOTSUPP;
128499a2425SRoopa Prabhu 	rcu_read_lock();
129499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[encap_type]);
1303d25eabbSwenxu 	if (likely(ops && ops->build_state && try_module_get(ops->owner)))
1319ae28727SDavid Ahern 		found = true;
1323d25eabbSwenxu 	rcu_read_unlock();
1333d25eabbSwenxu 
1343d25eabbSwenxu 	if (found) {
135faee6769SAlexander Aring 		ret = ops->build_state(net, encap, family, cfg, lws, extack);
13685c81401SRobert Shearman 		if (ret)
13785c81401SRobert Shearman 			module_put(ops->owner);
1383d25eabbSwenxu 	} else {
1399ae28727SDavid Ahern 		/* don't rely on -EOPNOTSUPP to detect match as build_state
1409ae28727SDavid Ahern 		 * handlers could return it
1419ae28727SDavid Ahern 		 */
1429ae28727SDavid Ahern 		NL_SET_ERR_MSG_ATTR(extack, encap,
1439ae28727SDavid Ahern 				    "LWT encapsulation type not supported");
1449ae28727SDavid Ahern 	}
1459ae28727SDavid Ahern 
146499a2425SRoopa Prabhu 	return ret;
147499a2425SRoopa Prabhu }
14808bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_build_state);
149499a2425SRoopa Prabhu 
lwtunnel_valid_encap_type(u16 encap_type,struct netlink_ext_ack * extack)150c255bd68SDavid Ahern int lwtunnel_valid_encap_type(u16 encap_type, struct netlink_ext_ack *extack)
1519ed59592SDavid Ahern {
1529ed59592SDavid Ahern 	const struct lwtunnel_encap_ops *ops;
1539ed59592SDavid Ahern 	int ret = -EINVAL;
1549ed59592SDavid Ahern 
1559ed59592SDavid Ahern 	if (encap_type == LWTUNNEL_ENCAP_NONE ||
156c255bd68SDavid Ahern 	    encap_type > LWTUNNEL_ENCAP_MAX) {
157c255bd68SDavid Ahern 		NL_SET_ERR_MSG(extack, "Unknown lwt encapsulation type");
1589ed59592SDavid Ahern 		return ret;
159c255bd68SDavid Ahern 	}
1609ed59592SDavid Ahern 
1619ed59592SDavid Ahern 	rcu_read_lock();
1629ed59592SDavid Ahern 	ops = rcu_dereference(lwtun_encaps[encap_type]);
1639ed59592SDavid Ahern 	rcu_read_unlock();
1649ed59592SDavid Ahern #ifdef CONFIG_MODULES
1659ed59592SDavid Ahern 	if (!ops) {
1669ed59592SDavid Ahern 		const char *encap_type_str = lwtunnel_encap_str(encap_type);
1679ed59592SDavid Ahern 
1689ed59592SDavid Ahern 		if (encap_type_str) {
1699ed59592SDavid Ahern 			__rtnl_unlock();
1709ed59592SDavid Ahern 			request_module("rtnl-lwt-%s", encap_type_str);
1719ed59592SDavid Ahern 			rtnl_lock();
1729ed59592SDavid Ahern 
1739ed59592SDavid Ahern 			rcu_read_lock();
1749ed59592SDavid Ahern 			ops = rcu_dereference(lwtun_encaps[encap_type]);
1759ed59592SDavid Ahern 			rcu_read_unlock();
1769ed59592SDavid Ahern 		}
1779ed59592SDavid Ahern 	}
1789ed59592SDavid Ahern #endif
179c255bd68SDavid Ahern 	ret = ops ? 0 : -EOPNOTSUPP;
180c255bd68SDavid Ahern 	if (ret < 0)
181c255bd68SDavid Ahern 		NL_SET_ERR_MSG(extack, "lwt encapsulation type not supported");
182c255bd68SDavid Ahern 
183c255bd68SDavid Ahern 	return ret;
1849ed59592SDavid Ahern }
18508bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type);
1869ed59592SDavid Ahern 
lwtunnel_valid_encap_type_attr(struct nlattr * attr,int remaining,struct netlink_ext_ack * extack)187c255bd68SDavid Ahern int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining,
188c255bd68SDavid Ahern 				   struct netlink_ext_ack *extack)
1899ed59592SDavid Ahern {
1909ed59592SDavid Ahern 	struct rtnexthop *rtnh = (struct rtnexthop *)attr;
1919ed59592SDavid Ahern 	struct nlattr *nla_entype;
1929ed59592SDavid Ahern 	struct nlattr *attrs;
1939ed59592SDavid Ahern 	u16 encap_type;
1949ed59592SDavid Ahern 	int attrlen;
1959ed59592SDavid Ahern 
1969ed59592SDavid Ahern 	while (rtnh_ok(rtnh, remaining)) {
1979ed59592SDavid Ahern 		attrlen = rtnh_attrlen(rtnh);
1989ed59592SDavid Ahern 		if (attrlen > 0) {
1999ed59592SDavid Ahern 			attrs = rtnh_attrs(rtnh);
2009ed59592SDavid Ahern 			nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE);
2019ed59592SDavid Ahern 
2029ed59592SDavid Ahern 			if (nla_entype) {
2038bda81a4SDavid Ahern 				if (nla_len(nla_entype) < sizeof(u16)) {
2048bda81a4SDavid Ahern 					NL_SET_ERR_MSG(extack, "Invalid RTA_ENCAP_TYPE");
2058bda81a4SDavid Ahern 					return -EINVAL;
2068bda81a4SDavid Ahern 				}
2079ed59592SDavid Ahern 				encap_type = nla_get_u16(nla_entype);
2089ed59592SDavid Ahern 
209c255bd68SDavid Ahern 				if (lwtunnel_valid_encap_type(encap_type,
210c255bd68SDavid Ahern 							      extack) != 0)
2119ed59592SDavid Ahern 					return -EOPNOTSUPP;
2129ed59592SDavid Ahern 			}
2139ed59592SDavid Ahern 		}
2149ed59592SDavid Ahern 		rtnh = rtnh_next(rtnh, &remaining);
2159ed59592SDavid Ahern 	}
2169ed59592SDavid Ahern 
2179ed59592SDavid Ahern 	return 0;
2189ed59592SDavid Ahern }
21908bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type_attr);
2209ed59592SDavid Ahern 
lwtstate_free(struct lwtunnel_state * lws)2211104d9baSTom Herbert void lwtstate_free(struct lwtunnel_state *lws)
2221104d9baSTom Herbert {
2231104d9baSTom Herbert 	const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type];
2241104d9baSTom Herbert 
2251104d9baSTom Herbert 	if (ops->destroy_state) {
2261104d9baSTom Herbert 		ops->destroy_state(lws);
2271104d9baSTom Herbert 		kfree_rcu(lws, rcu);
2281104d9baSTom Herbert 	} else {
2291104d9baSTom Herbert 		kfree(lws);
2301104d9baSTom Herbert 	}
23185c81401SRobert Shearman 	module_put(ops->owner);
2321104d9baSTom Herbert }
23308bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtstate_free);
2341104d9baSTom Herbert 
lwtunnel_fill_encap(struct sk_buff * skb,struct lwtunnel_state * lwtstate,int encap_attr,int encap_type_attr)235ffa8ce54SDavid Ahern int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate,
236ffa8ce54SDavid Ahern 			int encap_attr, int encap_type_attr)
237499a2425SRoopa Prabhu {
238499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
239499a2425SRoopa Prabhu 	struct nlattr *nest;
24039f37095SDan Carpenter 	int ret;
241499a2425SRoopa Prabhu 
242499a2425SRoopa Prabhu 	if (!lwtstate)
243499a2425SRoopa Prabhu 		return 0;
244499a2425SRoopa Prabhu 
245499a2425SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
246499a2425SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
247499a2425SRoopa Prabhu 		return 0;
248499a2425SRoopa Prabhu 
249ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, encap_attr);
250a50fe0ffSPan Bian 	if (!nest)
25139f37095SDan Carpenter 		return -EMSGSIZE;
25239f37095SDan Carpenter 
25339f37095SDan Carpenter 	ret = -EOPNOTSUPP;
254499a2425SRoopa Prabhu 	rcu_read_lock();
255499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
256499a2425SRoopa Prabhu 	if (likely(ops && ops->fill_encap))
257499a2425SRoopa Prabhu 		ret = ops->fill_encap(skb, lwtstate);
258499a2425SRoopa Prabhu 	rcu_read_unlock();
259499a2425SRoopa Prabhu 
260499a2425SRoopa Prabhu 	if (ret)
261499a2425SRoopa Prabhu 		goto nla_put_failure;
262499a2425SRoopa Prabhu 	nla_nest_end(skb, nest);
263ffa8ce54SDavid Ahern 	ret = nla_put_u16(skb, encap_type_attr, lwtstate->type);
264499a2425SRoopa Prabhu 	if (ret)
265499a2425SRoopa Prabhu 		goto nla_put_failure;
266499a2425SRoopa Prabhu 
267499a2425SRoopa Prabhu 	return 0;
268499a2425SRoopa Prabhu 
269499a2425SRoopa Prabhu nla_put_failure:
270499a2425SRoopa Prabhu 	nla_nest_cancel(skb, nest);
271499a2425SRoopa Prabhu 
272499a2425SRoopa Prabhu 	return (ret == -EOPNOTSUPP ? 0 : ret);
273499a2425SRoopa Prabhu }
27408bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_fill_encap);
275499a2425SRoopa Prabhu 
lwtunnel_get_encap_size(struct lwtunnel_state * lwtstate)276499a2425SRoopa Prabhu int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate)
277499a2425SRoopa Prabhu {
278499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
279499a2425SRoopa Prabhu 	int ret = 0;
280499a2425SRoopa Prabhu 
281499a2425SRoopa Prabhu 	if (!lwtstate)
282499a2425SRoopa Prabhu 		return 0;
283499a2425SRoopa Prabhu 
284499a2425SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
285499a2425SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
286499a2425SRoopa Prabhu 		return 0;
287499a2425SRoopa Prabhu 
288499a2425SRoopa Prabhu 	rcu_read_lock();
289499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
290499a2425SRoopa Prabhu 	if (likely(ops && ops->get_encap_size))
291499a2425SRoopa Prabhu 		ret = nla_total_size(ops->get_encap_size(lwtstate));
292499a2425SRoopa Prabhu 	rcu_read_unlock();
293499a2425SRoopa Prabhu 
294499a2425SRoopa Prabhu 	return ret;
295499a2425SRoopa Prabhu }
29608bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_get_encap_size);
297499a2425SRoopa Prabhu 
lwtunnel_cmp_encap(struct lwtunnel_state * a,struct lwtunnel_state * b)298499a2425SRoopa Prabhu int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
299499a2425SRoopa Prabhu {
300499a2425SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
301499a2425SRoopa Prabhu 	int ret = 0;
302499a2425SRoopa Prabhu 
303499a2425SRoopa Prabhu 	if (!a && !b)
304499a2425SRoopa Prabhu 		return 0;
305499a2425SRoopa Prabhu 
306499a2425SRoopa Prabhu 	if (!a || !b)
307499a2425SRoopa Prabhu 		return 1;
308499a2425SRoopa Prabhu 
309499a2425SRoopa Prabhu 	if (a->type != b->type)
310499a2425SRoopa Prabhu 		return 1;
311499a2425SRoopa Prabhu 
312499a2425SRoopa Prabhu 	if (a->type == LWTUNNEL_ENCAP_NONE ||
313499a2425SRoopa Prabhu 	    a->type > LWTUNNEL_ENCAP_MAX)
314499a2425SRoopa Prabhu 		return 0;
315499a2425SRoopa Prabhu 
316499a2425SRoopa Prabhu 	rcu_read_lock();
317499a2425SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[a->type]);
318499a2425SRoopa Prabhu 	if (likely(ops && ops->cmp_encap))
319499a2425SRoopa Prabhu 		ret = ops->cmp_encap(a, b);
320499a2425SRoopa Prabhu 	rcu_read_unlock();
321499a2425SRoopa Prabhu 
322499a2425SRoopa Prabhu 	return ret;
323499a2425SRoopa Prabhu }
32408bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_cmp_encap);
325ffce4196SRoopa Prabhu 
lwtunnel_output(struct net * net,struct sock * sk,struct sk_buff * skb)326ede2059dSEric W. Biederman int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
327ffce4196SRoopa Prabhu {
32861adedf3SJiri Benc 	struct dst_entry *dst = skb_dst(skb);
329ffce4196SRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
33061adedf3SJiri Benc 	struct lwtunnel_state *lwtstate;
331ffce4196SRoopa Prabhu 	int ret = -EINVAL;
332ffce4196SRoopa Prabhu 
33361adedf3SJiri Benc 	if (!dst)
334ffce4196SRoopa Prabhu 		goto drop;
33561adedf3SJiri Benc 	lwtstate = dst->lwtstate;
336ffce4196SRoopa Prabhu 
337ffce4196SRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
338ffce4196SRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
339ffce4196SRoopa Prabhu 		return 0;
340ffce4196SRoopa Prabhu 
341ffce4196SRoopa Prabhu 	ret = -EOPNOTSUPP;
342ffce4196SRoopa Prabhu 	rcu_read_lock();
343ffce4196SRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
344ffce4196SRoopa Prabhu 	if (likely(ops && ops->output))
345ede2059dSEric W. Biederman 		ret = ops->output(net, sk, skb);
346ffce4196SRoopa Prabhu 	rcu_read_unlock();
347ffce4196SRoopa Prabhu 
348ffce4196SRoopa Prabhu 	if (ret == -EOPNOTSUPP)
349ffce4196SRoopa Prabhu 		goto drop;
350ffce4196SRoopa Prabhu 
351ffce4196SRoopa Prabhu 	return ret;
352ffce4196SRoopa Prabhu 
353ffce4196SRoopa Prabhu drop:
354e11f40b9SDan Carpenter 	kfree_skb(skb);
355ffce4196SRoopa Prabhu 
356ffce4196SRoopa Prabhu 	return ret;
357ffce4196SRoopa Prabhu }
35808bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_output);
35925368623STom Herbert 
lwtunnel_xmit(struct sk_buff * skb)36014972cbdSRoopa Prabhu int lwtunnel_xmit(struct sk_buff *skb)
36114972cbdSRoopa Prabhu {
36214972cbdSRoopa Prabhu 	struct dst_entry *dst = skb_dst(skb);
36314972cbdSRoopa Prabhu 	const struct lwtunnel_encap_ops *ops;
36414972cbdSRoopa Prabhu 	struct lwtunnel_state *lwtstate;
36514972cbdSRoopa Prabhu 	int ret = -EINVAL;
36614972cbdSRoopa Prabhu 
36714972cbdSRoopa Prabhu 	if (!dst)
36814972cbdSRoopa Prabhu 		goto drop;
36914972cbdSRoopa Prabhu 
37014972cbdSRoopa Prabhu 	lwtstate = dst->lwtstate;
37114972cbdSRoopa Prabhu 
37214972cbdSRoopa Prabhu 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
37314972cbdSRoopa Prabhu 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
37414972cbdSRoopa Prabhu 		return 0;
37514972cbdSRoopa Prabhu 
37614972cbdSRoopa Prabhu 	ret = -EOPNOTSUPP;
37714972cbdSRoopa Prabhu 	rcu_read_lock();
37814972cbdSRoopa Prabhu 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
37914972cbdSRoopa Prabhu 	if (likely(ops && ops->xmit))
38014972cbdSRoopa Prabhu 		ret = ops->xmit(skb);
38114972cbdSRoopa Prabhu 	rcu_read_unlock();
38214972cbdSRoopa Prabhu 
38314972cbdSRoopa Prabhu 	if (ret == -EOPNOTSUPP)
38414972cbdSRoopa Prabhu 		goto drop;
38514972cbdSRoopa Prabhu 
38614972cbdSRoopa Prabhu 	return ret;
38714972cbdSRoopa Prabhu 
38814972cbdSRoopa Prabhu drop:
38914972cbdSRoopa Prabhu 	kfree_skb(skb);
39014972cbdSRoopa Prabhu 
39114972cbdSRoopa Prabhu 	return ret;
39214972cbdSRoopa Prabhu }
39308bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_xmit);
39414972cbdSRoopa Prabhu 
lwtunnel_input(struct sk_buff * skb)39561adedf3SJiri Benc int lwtunnel_input(struct sk_buff *skb)
39625368623STom Herbert {
39761adedf3SJiri Benc 	struct dst_entry *dst = skb_dst(skb);
39825368623STom Herbert 	const struct lwtunnel_encap_ops *ops;
39961adedf3SJiri Benc 	struct lwtunnel_state *lwtstate;
40025368623STom Herbert 	int ret = -EINVAL;
40125368623STom Herbert 
40261adedf3SJiri Benc 	if (!dst)
40325368623STom Herbert 		goto drop;
40461adedf3SJiri Benc 	lwtstate = dst->lwtstate;
40525368623STom Herbert 
40625368623STom Herbert 	if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
40725368623STom Herbert 	    lwtstate->type > LWTUNNEL_ENCAP_MAX)
40825368623STom Herbert 		return 0;
40925368623STom Herbert 
41025368623STom Herbert 	ret = -EOPNOTSUPP;
41125368623STom Herbert 	rcu_read_lock();
41225368623STom Herbert 	ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
41325368623STom Herbert 	if (likely(ops && ops->input))
41425368623STom Herbert 		ret = ops->input(skb);
41525368623STom Herbert 	rcu_read_unlock();
41625368623STom Herbert 
41725368623STom Herbert 	if (ret == -EOPNOTSUPP)
41825368623STom Herbert 		goto drop;
41925368623STom Herbert 
42025368623STom Herbert 	return ret;
42125368623STom Herbert 
42225368623STom Herbert drop:
42325368623STom Herbert 	kfree_skb(skb);
42425368623STom Herbert 
42525368623STom Herbert 	return ret;
42625368623STom Herbert }
42708bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_input);
428