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