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 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"; 51745041e2SRobert Shearman case LWTUNNEL_ENCAP_IP6: 52745041e2SRobert Shearman case LWTUNNEL_ENCAP_IP: 53745041e2SRobert Shearman case LWTUNNEL_ENCAP_NONE: 54745041e2SRobert Shearman case __LWTUNNEL_ENCAP_MAX: 55745041e2SRobert Shearman /* should not have got here */ 56745041e2SRobert Shearman WARN_ON(1); 57745041e2SRobert Shearman break; 58745041e2SRobert Shearman } 59745041e2SRobert Shearman return NULL; 60745041e2SRobert Shearman } 61745041e2SRobert Shearman 62745041e2SRobert Shearman #endif /* CONFIG_MODULES */ 63745041e2SRobert Shearman 64499a2425SRoopa Prabhu struct lwtunnel_state *lwtunnel_state_alloc(int encap_len) 65499a2425SRoopa Prabhu { 66499a2425SRoopa Prabhu struct lwtunnel_state *lws; 67499a2425SRoopa Prabhu 68499a2425SRoopa Prabhu lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC); 69499a2425SRoopa Prabhu 70499a2425SRoopa Prabhu return lws; 71499a2425SRoopa Prabhu } 7208bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_state_alloc); 73499a2425SRoopa Prabhu 7492a99bf3SThomas Graf static const struct lwtunnel_encap_ops __rcu * 75499a2425SRoopa Prabhu lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly; 76499a2425SRoopa Prabhu 77499a2425SRoopa Prabhu int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops, 78499a2425SRoopa Prabhu unsigned int num) 79499a2425SRoopa Prabhu { 80499a2425SRoopa Prabhu if (num > LWTUNNEL_ENCAP_MAX) 81499a2425SRoopa Prabhu return -ERANGE; 82499a2425SRoopa Prabhu 83499a2425SRoopa Prabhu return !cmpxchg((const struct lwtunnel_encap_ops **) 84499a2425SRoopa Prabhu &lwtun_encaps[num], 85499a2425SRoopa Prabhu NULL, ops) ? 0 : -1; 86499a2425SRoopa Prabhu } 8708bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_encap_add_ops); 88499a2425SRoopa Prabhu 89499a2425SRoopa Prabhu int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops, 90499a2425SRoopa Prabhu unsigned int encap_type) 91499a2425SRoopa Prabhu { 92499a2425SRoopa Prabhu int ret; 93499a2425SRoopa Prabhu 94499a2425SRoopa Prabhu if (encap_type == LWTUNNEL_ENCAP_NONE || 95499a2425SRoopa Prabhu encap_type > LWTUNNEL_ENCAP_MAX) 96499a2425SRoopa Prabhu return -ERANGE; 97499a2425SRoopa Prabhu 98499a2425SRoopa Prabhu ret = (cmpxchg((const struct lwtunnel_encap_ops **) 99499a2425SRoopa Prabhu &lwtun_encaps[encap_type], 100499a2425SRoopa Prabhu ops, NULL) == ops) ? 0 : -1; 101499a2425SRoopa Prabhu 102499a2425SRoopa Prabhu synchronize_net(); 103499a2425SRoopa Prabhu 104499a2425SRoopa Prabhu return ret; 105499a2425SRoopa Prabhu } 10608bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_encap_del_ops); 107499a2425SRoopa Prabhu 108faee6769SAlexander Aring int lwtunnel_build_state(struct net *net, u16 encap_type, 109127eb7cdSTom Herbert struct nlattr *encap, unsigned int family, 1109ae28727SDavid Ahern const void *cfg, struct lwtunnel_state **lws, 1119ae28727SDavid Ahern struct netlink_ext_ack *extack) 112499a2425SRoopa Prabhu { 113499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 1149ae28727SDavid Ahern bool found = false; 115499a2425SRoopa Prabhu int ret = -EINVAL; 116499a2425SRoopa Prabhu 117499a2425SRoopa Prabhu if (encap_type == LWTUNNEL_ENCAP_NONE || 1189ae28727SDavid Ahern encap_type > LWTUNNEL_ENCAP_MAX) { 1199ae28727SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, encap, 1209ae28727SDavid Ahern "Unknown LWT encapsulation type"); 121499a2425SRoopa Prabhu return ret; 1229ae28727SDavid Ahern } 123499a2425SRoopa Prabhu 124499a2425SRoopa Prabhu ret = -EOPNOTSUPP; 125499a2425SRoopa Prabhu rcu_read_lock(); 126499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[encap_type]); 1273d25eabbSwenxu if (likely(ops && ops->build_state && try_module_get(ops->owner))) 1289ae28727SDavid Ahern found = true; 1293d25eabbSwenxu rcu_read_unlock(); 1303d25eabbSwenxu 1313d25eabbSwenxu if (found) { 132faee6769SAlexander Aring ret = ops->build_state(net, encap, family, cfg, lws, extack); 13385c81401SRobert Shearman if (ret) 13485c81401SRobert Shearman module_put(ops->owner); 1353d25eabbSwenxu } else { 1369ae28727SDavid Ahern /* don't rely on -EOPNOTSUPP to detect match as build_state 1379ae28727SDavid Ahern * handlers could return it 1389ae28727SDavid Ahern */ 1399ae28727SDavid Ahern NL_SET_ERR_MSG_ATTR(extack, encap, 1409ae28727SDavid Ahern "LWT encapsulation type not supported"); 1419ae28727SDavid Ahern } 1429ae28727SDavid Ahern 143499a2425SRoopa Prabhu return ret; 144499a2425SRoopa Prabhu } 14508bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_build_state); 146499a2425SRoopa Prabhu 147c255bd68SDavid Ahern int lwtunnel_valid_encap_type(u16 encap_type, struct netlink_ext_ack *extack) 1489ed59592SDavid Ahern { 1499ed59592SDavid Ahern const struct lwtunnel_encap_ops *ops; 1509ed59592SDavid Ahern int ret = -EINVAL; 1519ed59592SDavid Ahern 1529ed59592SDavid Ahern if (encap_type == LWTUNNEL_ENCAP_NONE || 153c255bd68SDavid Ahern encap_type > LWTUNNEL_ENCAP_MAX) { 154c255bd68SDavid Ahern NL_SET_ERR_MSG(extack, "Unknown lwt encapsulation type"); 1559ed59592SDavid Ahern return ret; 156c255bd68SDavid Ahern } 1579ed59592SDavid Ahern 1589ed59592SDavid Ahern rcu_read_lock(); 1599ed59592SDavid Ahern ops = rcu_dereference(lwtun_encaps[encap_type]); 1609ed59592SDavid Ahern rcu_read_unlock(); 1619ed59592SDavid Ahern #ifdef CONFIG_MODULES 1629ed59592SDavid Ahern if (!ops) { 1639ed59592SDavid Ahern const char *encap_type_str = lwtunnel_encap_str(encap_type); 1649ed59592SDavid Ahern 1659ed59592SDavid Ahern if (encap_type_str) { 1669ed59592SDavid Ahern __rtnl_unlock(); 1679ed59592SDavid Ahern request_module("rtnl-lwt-%s", encap_type_str); 1689ed59592SDavid Ahern rtnl_lock(); 1699ed59592SDavid Ahern 1709ed59592SDavid Ahern rcu_read_lock(); 1719ed59592SDavid Ahern ops = rcu_dereference(lwtun_encaps[encap_type]); 1729ed59592SDavid Ahern rcu_read_unlock(); 1739ed59592SDavid Ahern } 1749ed59592SDavid Ahern } 1759ed59592SDavid Ahern #endif 176c255bd68SDavid Ahern ret = ops ? 0 : -EOPNOTSUPP; 177c255bd68SDavid Ahern if (ret < 0) 178c255bd68SDavid Ahern NL_SET_ERR_MSG(extack, "lwt encapsulation type not supported"); 179c255bd68SDavid Ahern 180c255bd68SDavid Ahern return ret; 1819ed59592SDavid Ahern } 18208bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type); 1839ed59592SDavid Ahern 184c255bd68SDavid Ahern int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining, 185c255bd68SDavid Ahern struct netlink_ext_ack *extack) 1869ed59592SDavid Ahern { 1879ed59592SDavid Ahern struct rtnexthop *rtnh = (struct rtnexthop *)attr; 1889ed59592SDavid Ahern struct nlattr *nla_entype; 1899ed59592SDavid Ahern struct nlattr *attrs; 1909ed59592SDavid Ahern u16 encap_type; 1919ed59592SDavid Ahern int attrlen; 1929ed59592SDavid Ahern 1939ed59592SDavid Ahern while (rtnh_ok(rtnh, remaining)) { 1949ed59592SDavid Ahern attrlen = rtnh_attrlen(rtnh); 1959ed59592SDavid Ahern if (attrlen > 0) { 1969ed59592SDavid Ahern attrs = rtnh_attrs(rtnh); 1979ed59592SDavid Ahern nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 1989ed59592SDavid Ahern 1999ed59592SDavid Ahern if (nla_entype) { 200*8bda81a4SDavid Ahern if (nla_len(nla_entype) < sizeof(u16)) { 201*8bda81a4SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid RTA_ENCAP_TYPE"); 202*8bda81a4SDavid Ahern return -EINVAL; 203*8bda81a4SDavid Ahern } 2049ed59592SDavid Ahern encap_type = nla_get_u16(nla_entype); 2059ed59592SDavid Ahern 206c255bd68SDavid Ahern if (lwtunnel_valid_encap_type(encap_type, 207c255bd68SDavid Ahern extack) != 0) 2089ed59592SDavid Ahern return -EOPNOTSUPP; 2099ed59592SDavid Ahern } 2109ed59592SDavid Ahern } 2119ed59592SDavid Ahern rtnh = rtnh_next(rtnh, &remaining); 2129ed59592SDavid Ahern } 2139ed59592SDavid Ahern 2149ed59592SDavid Ahern return 0; 2159ed59592SDavid Ahern } 21608bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type_attr); 2179ed59592SDavid Ahern 2181104d9baSTom Herbert void lwtstate_free(struct lwtunnel_state *lws) 2191104d9baSTom Herbert { 2201104d9baSTom Herbert const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type]; 2211104d9baSTom Herbert 2221104d9baSTom Herbert if (ops->destroy_state) { 2231104d9baSTom Herbert ops->destroy_state(lws); 2241104d9baSTom Herbert kfree_rcu(lws, rcu); 2251104d9baSTom Herbert } else { 2261104d9baSTom Herbert kfree(lws); 2271104d9baSTom Herbert } 22885c81401SRobert Shearman module_put(ops->owner); 2291104d9baSTom Herbert } 23008bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtstate_free); 2311104d9baSTom Herbert 232ffa8ce54SDavid Ahern int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate, 233ffa8ce54SDavid Ahern int encap_attr, int encap_type_attr) 234499a2425SRoopa Prabhu { 235499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 236499a2425SRoopa Prabhu struct nlattr *nest; 23739f37095SDan Carpenter int ret; 238499a2425SRoopa Prabhu 239499a2425SRoopa Prabhu if (!lwtstate) 240499a2425SRoopa Prabhu return 0; 241499a2425SRoopa Prabhu 242499a2425SRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 243499a2425SRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 244499a2425SRoopa Prabhu return 0; 245499a2425SRoopa Prabhu 246ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, encap_attr); 247a50fe0ffSPan Bian if (!nest) 24839f37095SDan Carpenter return -EMSGSIZE; 24939f37095SDan Carpenter 25039f37095SDan Carpenter ret = -EOPNOTSUPP; 251499a2425SRoopa Prabhu rcu_read_lock(); 252499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 253499a2425SRoopa Prabhu if (likely(ops && ops->fill_encap)) 254499a2425SRoopa Prabhu ret = ops->fill_encap(skb, lwtstate); 255499a2425SRoopa Prabhu rcu_read_unlock(); 256499a2425SRoopa Prabhu 257499a2425SRoopa Prabhu if (ret) 258499a2425SRoopa Prabhu goto nla_put_failure; 259499a2425SRoopa Prabhu nla_nest_end(skb, nest); 260ffa8ce54SDavid Ahern ret = nla_put_u16(skb, encap_type_attr, lwtstate->type); 261499a2425SRoopa Prabhu if (ret) 262499a2425SRoopa Prabhu goto nla_put_failure; 263499a2425SRoopa Prabhu 264499a2425SRoopa Prabhu return 0; 265499a2425SRoopa Prabhu 266499a2425SRoopa Prabhu nla_put_failure: 267499a2425SRoopa Prabhu nla_nest_cancel(skb, nest); 268499a2425SRoopa Prabhu 269499a2425SRoopa Prabhu return (ret == -EOPNOTSUPP ? 0 : ret); 270499a2425SRoopa Prabhu } 27108bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_fill_encap); 272499a2425SRoopa Prabhu 273499a2425SRoopa Prabhu int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate) 274499a2425SRoopa Prabhu { 275499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 276499a2425SRoopa Prabhu int ret = 0; 277499a2425SRoopa Prabhu 278499a2425SRoopa Prabhu if (!lwtstate) 279499a2425SRoopa Prabhu return 0; 280499a2425SRoopa Prabhu 281499a2425SRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 282499a2425SRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 283499a2425SRoopa Prabhu return 0; 284499a2425SRoopa Prabhu 285499a2425SRoopa Prabhu rcu_read_lock(); 286499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 287499a2425SRoopa Prabhu if (likely(ops && ops->get_encap_size)) 288499a2425SRoopa Prabhu ret = nla_total_size(ops->get_encap_size(lwtstate)); 289499a2425SRoopa Prabhu rcu_read_unlock(); 290499a2425SRoopa Prabhu 291499a2425SRoopa Prabhu return ret; 292499a2425SRoopa Prabhu } 29308bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_get_encap_size); 294499a2425SRoopa Prabhu 295499a2425SRoopa Prabhu int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b) 296499a2425SRoopa Prabhu { 297499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 298499a2425SRoopa Prabhu int ret = 0; 299499a2425SRoopa Prabhu 300499a2425SRoopa Prabhu if (!a && !b) 301499a2425SRoopa Prabhu return 0; 302499a2425SRoopa Prabhu 303499a2425SRoopa Prabhu if (!a || !b) 304499a2425SRoopa Prabhu return 1; 305499a2425SRoopa Prabhu 306499a2425SRoopa Prabhu if (a->type != b->type) 307499a2425SRoopa Prabhu return 1; 308499a2425SRoopa Prabhu 309499a2425SRoopa Prabhu if (a->type == LWTUNNEL_ENCAP_NONE || 310499a2425SRoopa Prabhu a->type > LWTUNNEL_ENCAP_MAX) 311499a2425SRoopa Prabhu return 0; 312499a2425SRoopa Prabhu 313499a2425SRoopa Prabhu rcu_read_lock(); 314499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[a->type]); 315499a2425SRoopa Prabhu if (likely(ops && ops->cmp_encap)) 316499a2425SRoopa Prabhu ret = ops->cmp_encap(a, b); 317499a2425SRoopa Prabhu rcu_read_unlock(); 318499a2425SRoopa Prabhu 319499a2425SRoopa Prabhu return ret; 320499a2425SRoopa Prabhu } 32108bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_cmp_encap); 322ffce4196SRoopa Prabhu 323ede2059dSEric W. Biederman int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) 324ffce4196SRoopa Prabhu { 32561adedf3SJiri Benc struct dst_entry *dst = skb_dst(skb); 326ffce4196SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 32761adedf3SJiri Benc struct lwtunnel_state *lwtstate; 328ffce4196SRoopa Prabhu int ret = -EINVAL; 329ffce4196SRoopa Prabhu 33061adedf3SJiri Benc if (!dst) 331ffce4196SRoopa Prabhu goto drop; 33261adedf3SJiri Benc lwtstate = dst->lwtstate; 333ffce4196SRoopa Prabhu 334ffce4196SRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 335ffce4196SRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 336ffce4196SRoopa Prabhu return 0; 337ffce4196SRoopa Prabhu 338ffce4196SRoopa Prabhu ret = -EOPNOTSUPP; 339ffce4196SRoopa Prabhu rcu_read_lock(); 340ffce4196SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 341ffce4196SRoopa Prabhu if (likely(ops && ops->output)) 342ede2059dSEric W. Biederman ret = ops->output(net, sk, skb); 343ffce4196SRoopa Prabhu rcu_read_unlock(); 344ffce4196SRoopa Prabhu 345ffce4196SRoopa Prabhu if (ret == -EOPNOTSUPP) 346ffce4196SRoopa Prabhu goto drop; 347ffce4196SRoopa Prabhu 348ffce4196SRoopa Prabhu return ret; 349ffce4196SRoopa Prabhu 350ffce4196SRoopa Prabhu drop: 351e11f40b9SDan Carpenter kfree_skb(skb); 352ffce4196SRoopa Prabhu 353ffce4196SRoopa Prabhu return ret; 354ffce4196SRoopa Prabhu } 35508bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_output); 35625368623STom Herbert 35714972cbdSRoopa Prabhu int lwtunnel_xmit(struct sk_buff *skb) 35814972cbdSRoopa Prabhu { 35914972cbdSRoopa Prabhu struct dst_entry *dst = skb_dst(skb); 36014972cbdSRoopa Prabhu const struct lwtunnel_encap_ops *ops; 36114972cbdSRoopa Prabhu struct lwtunnel_state *lwtstate; 36214972cbdSRoopa Prabhu int ret = -EINVAL; 36314972cbdSRoopa Prabhu 36414972cbdSRoopa Prabhu if (!dst) 36514972cbdSRoopa Prabhu goto drop; 36614972cbdSRoopa Prabhu 36714972cbdSRoopa Prabhu lwtstate = dst->lwtstate; 36814972cbdSRoopa Prabhu 36914972cbdSRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 37014972cbdSRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 37114972cbdSRoopa Prabhu return 0; 37214972cbdSRoopa Prabhu 37314972cbdSRoopa Prabhu ret = -EOPNOTSUPP; 37414972cbdSRoopa Prabhu rcu_read_lock(); 37514972cbdSRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 37614972cbdSRoopa Prabhu if (likely(ops && ops->xmit)) 37714972cbdSRoopa Prabhu ret = ops->xmit(skb); 37814972cbdSRoopa Prabhu rcu_read_unlock(); 37914972cbdSRoopa Prabhu 38014972cbdSRoopa Prabhu if (ret == -EOPNOTSUPP) 38114972cbdSRoopa Prabhu goto drop; 38214972cbdSRoopa Prabhu 38314972cbdSRoopa Prabhu return ret; 38414972cbdSRoopa Prabhu 38514972cbdSRoopa Prabhu drop: 38614972cbdSRoopa Prabhu kfree_skb(skb); 38714972cbdSRoopa Prabhu 38814972cbdSRoopa Prabhu return ret; 38914972cbdSRoopa Prabhu } 39008bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_xmit); 39114972cbdSRoopa Prabhu 39261adedf3SJiri Benc int lwtunnel_input(struct sk_buff *skb) 39325368623STom Herbert { 39461adedf3SJiri Benc struct dst_entry *dst = skb_dst(skb); 39525368623STom Herbert const struct lwtunnel_encap_ops *ops; 39661adedf3SJiri Benc struct lwtunnel_state *lwtstate; 39725368623STom Herbert int ret = -EINVAL; 39825368623STom Herbert 39961adedf3SJiri Benc if (!dst) 40025368623STom Herbert goto drop; 40161adedf3SJiri Benc lwtstate = dst->lwtstate; 40225368623STom Herbert 40325368623STom Herbert if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 40425368623STom Herbert lwtstate->type > LWTUNNEL_ENCAP_MAX) 40525368623STom Herbert return 0; 40625368623STom Herbert 40725368623STom Herbert ret = -EOPNOTSUPP; 40825368623STom Herbert rcu_read_lock(); 40925368623STom Herbert ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 41025368623STom Herbert if (likely(ops && ops->input)) 41125368623STom Herbert ret = ops->input(skb); 41225368623STom Herbert rcu_read_unlock(); 41325368623STom Herbert 41425368623STom Herbert if (ret == -EOPNOTSUPP) 41525368623STom Herbert goto drop; 41625368623STom Herbert 41725368623STom Herbert return ret; 41825368623STom Herbert 41925368623STom Herbert drop: 42025368623STom Herbert kfree_skb(skb); 42125368623STom Herbert 42225368623STom Herbert return ret; 42325368623STom Herbert } 42408bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_input); 425