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