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 } 6808bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 8308bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 10208bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 14108bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 17808bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 20808bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 22208bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 26208bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 28408bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 31208bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 34608bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 38108bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(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 } 41508bd10ffSRoopa Prabhu EXPORT_SYMBOL_GPL(lwtunnel_input); 416