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> 29499a2425SRoopa Prabhu 30745041e2SRobert Shearman #ifdef CONFIG_MODULES 31745041e2SRobert Shearman 32745041e2SRobert Shearman static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type) 33745041e2SRobert Shearman { 34745041e2SRobert Shearman /* Only lwt encaps implemented without using an interface for 35745041e2SRobert Shearman * the encap need to return a string here. 36745041e2SRobert Shearman */ 37745041e2SRobert Shearman switch (encap_type) { 38745041e2SRobert Shearman case LWTUNNEL_ENCAP_MPLS: 39745041e2SRobert Shearman return "MPLS"; 40745041e2SRobert Shearman case LWTUNNEL_ENCAP_ILA: 41745041e2SRobert Shearman return "ILA"; 42745041e2SRobert Shearman case LWTUNNEL_ENCAP_IP6: 43745041e2SRobert Shearman case LWTUNNEL_ENCAP_IP: 44745041e2SRobert Shearman case LWTUNNEL_ENCAP_NONE: 45745041e2SRobert Shearman case __LWTUNNEL_ENCAP_MAX: 46745041e2SRobert Shearman /* should not have got here */ 47745041e2SRobert Shearman WARN_ON(1); 48745041e2SRobert Shearman break; 49745041e2SRobert Shearman } 50745041e2SRobert Shearman return NULL; 51745041e2SRobert Shearman } 52745041e2SRobert Shearman 53745041e2SRobert Shearman #endif /* CONFIG_MODULES */ 54745041e2SRobert Shearman 55499a2425SRoopa Prabhu struct lwtunnel_state *lwtunnel_state_alloc(int encap_len) 56499a2425SRoopa Prabhu { 57499a2425SRoopa Prabhu struct lwtunnel_state *lws; 58499a2425SRoopa Prabhu 59499a2425SRoopa Prabhu lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC); 60499a2425SRoopa Prabhu 61499a2425SRoopa Prabhu return lws; 62499a2425SRoopa Prabhu } 63499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_state_alloc); 64499a2425SRoopa Prabhu 6592a99bf3SThomas Graf static const struct lwtunnel_encap_ops __rcu * 66499a2425SRoopa Prabhu lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly; 67499a2425SRoopa Prabhu 68499a2425SRoopa Prabhu int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops, 69499a2425SRoopa Prabhu unsigned int num) 70499a2425SRoopa Prabhu { 71499a2425SRoopa Prabhu if (num > LWTUNNEL_ENCAP_MAX) 72499a2425SRoopa Prabhu return -ERANGE; 73499a2425SRoopa Prabhu 74499a2425SRoopa Prabhu return !cmpxchg((const struct lwtunnel_encap_ops **) 75499a2425SRoopa Prabhu &lwtun_encaps[num], 76499a2425SRoopa Prabhu NULL, ops) ? 0 : -1; 77499a2425SRoopa Prabhu } 78499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_encap_add_ops); 79499a2425SRoopa Prabhu 80499a2425SRoopa Prabhu int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops, 81499a2425SRoopa Prabhu unsigned int encap_type) 82499a2425SRoopa Prabhu { 83499a2425SRoopa Prabhu int ret; 84499a2425SRoopa Prabhu 85499a2425SRoopa Prabhu if (encap_type == LWTUNNEL_ENCAP_NONE || 86499a2425SRoopa Prabhu encap_type > LWTUNNEL_ENCAP_MAX) 87499a2425SRoopa Prabhu return -ERANGE; 88499a2425SRoopa Prabhu 89499a2425SRoopa Prabhu ret = (cmpxchg((const struct lwtunnel_encap_ops **) 90499a2425SRoopa Prabhu &lwtun_encaps[encap_type], 91499a2425SRoopa Prabhu ops, NULL) == ops) ? 0 : -1; 92499a2425SRoopa Prabhu 93499a2425SRoopa Prabhu synchronize_net(); 94499a2425SRoopa Prabhu 95499a2425SRoopa Prabhu return ret; 96499a2425SRoopa Prabhu } 97499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_encap_del_ops); 98499a2425SRoopa Prabhu 99499a2425SRoopa Prabhu int lwtunnel_build_state(struct net_device *dev, u16 encap_type, 100127eb7cdSTom Herbert struct nlattr *encap, unsigned int family, 101127eb7cdSTom Herbert const void *cfg, struct lwtunnel_state **lws) 102499a2425SRoopa Prabhu { 103499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 104499a2425SRoopa Prabhu int ret = -EINVAL; 105499a2425SRoopa Prabhu 106499a2425SRoopa Prabhu if (encap_type == LWTUNNEL_ENCAP_NONE || 107499a2425SRoopa Prabhu encap_type > LWTUNNEL_ENCAP_MAX) 108499a2425SRoopa Prabhu return ret; 109499a2425SRoopa Prabhu 110499a2425SRoopa Prabhu ret = -EOPNOTSUPP; 111499a2425SRoopa Prabhu rcu_read_lock(); 112499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[encap_type]); 113745041e2SRobert Shearman #ifdef CONFIG_MODULES 114745041e2SRobert Shearman if (!ops) { 115745041e2SRobert Shearman const char *encap_type_str = lwtunnel_encap_str(encap_type); 116745041e2SRobert Shearman 117745041e2SRobert Shearman if (encap_type_str) { 118745041e2SRobert Shearman rcu_read_unlock(); 119745041e2SRobert Shearman request_module("rtnl-lwt-%s", encap_type_str); 120745041e2SRobert Shearman rcu_read_lock(); 121745041e2SRobert Shearman ops = rcu_dereference(lwtun_encaps[encap_type]); 122745041e2SRobert Shearman } 123745041e2SRobert Shearman } 124745041e2SRobert Shearman #endif 125499a2425SRoopa Prabhu if (likely(ops && ops->build_state)) 126127eb7cdSTom Herbert ret = ops->build_state(dev, encap, family, cfg, lws); 127499a2425SRoopa Prabhu rcu_read_unlock(); 128499a2425SRoopa Prabhu 129499a2425SRoopa Prabhu return ret; 130499a2425SRoopa Prabhu } 131499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_build_state); 132499a2425SRoopa Prabhu 133499a2425SRoopa Prabhu int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate) 134499a2425SRoopa Prabhu { 135499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 136499a2425SRoopa Prabhu struct nlattr *nest; 137499a2425SRoopa Prabhu int ret = -EINVAL; 138499a2425SRoopa Prabhu 139499a2425SRoopa Prabhu if (!lwtstate) 140499a2425SRoopa Prabhu return 0; 141499a2425SRoopa Prabhu 142499a2425SRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 143499a2425SRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 144499a2425SRoopa Prabhu return 0; 145499a2425SRoopa Prabhu 146499a2425SRoopa Prabhu ret = -EOPNOTSUPP; 147499a2425SRoopa Prabhu nest = nla_nest_start(skb, RTA_ENCAP); 148499a2425SRoopa Prabhu rcu_read_lock(); 149499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 150499a2425SRoopa Prabhu if (likely(ops && ops->fill_encap)) 151499a2425SRoopa Prabhu ret = ops->fill_encap(skb, lwtstate); 152499a2425SRoopa Prabhu rcu_read_unlock(); 153499a2425SRoopa Prabhu 154499a2425SRoopa Prabhu if (ret) 155499a2425SRoopa Prabhu goto nla_put_failure; 156499a2425SRoopa Prabhu nla_nest_end(skb, nest); 157499a2425SRoopa Prabhu ret = nla_put_u16(skb, RTA_ENCAP_TYPE, lwtstate->type); 158499a2425SRoopa Prabhu if (ret) 159499a2425SRoopa Prabhu goto nla_put_failure; 160499a2425SRoopa Prabhu 161499a2425SRoopa Prabhu return 0; 162499a2425SRoopa Prabhu 163499a2425SRoopa Prabhu nla_put_failure: 164499a2425SRoopa Prabhu nla_nest_cancel(skb, nest); 165499a2425SRoopa Prabhu 166499a2425SRoopa Prabhu return (ret == -EOPNOTSUPP ? 0 : ret); 167499a2425SRoopa Prabhu } 168499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_fill_encap); 169499a2425SRoopa Prabhu 170499a2425SRoopa Prabhu int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate) 171499a2425SRoopa Prabhu { 172499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 173499a2425SRoopa Prabhu int ret = 0; 174499a2425SRoopa Prabhu 175499a2425SRoopa Prabhu if (!lwtstate) 176499a2425SRoopa Prabhu return 0; 177499a2425SRoopa Prabhu 178499a2425SRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 179499a2425SRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 180499a2425SRoopa Prabhu return 0; 181499a2425SRoopa Prabhu 182499a2425SRoopa Prabhu rcu_read_lock(); 183499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 184499a2425SRoopa Prabhu if (likely(ops && ops->get_encap_size)) 185499a2425SRoopa Prabhu ret = nla_total_size(ops->get_encap_size(lwtstate)); 186499a2425SRoopa Prabhu rcu_read_unlock(); 187499a2425SRoopa Prabhu 188499a2425SRoopa Prabhu return ret; 189499a2425SRoopa Prabhu } 190499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_get_encap_size); 191499a2425SRoopa Prabhu 192499a2425SRoopa Prabhu int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b) 193499a2425SRoopa Prabhu { 194499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 195499a2425SRoopa Prabhu int ret = 0; 196499a2425SRoopa Prabhu 197499a2425SRoopa Prabhu if (!a && !b) 198499a2425SRoopa Prabhu return 0; 199499a2425SRoopa Prabhu 200499a2425SRoopa Prabhu if (!a || !b) 201499a2425SRoopa Prabhu return 1; 202499a2425SRoopa Prabhu 203499a2425SRoopa Prabhu if (a->type != b->type) 204499a2425SRoopa Prabhu return 1; 205499a2425SRoopa Prabhu 206499a2425SRoopa Prabhu if (a->type == LWTUNNEL_ENCAP_NONE || 207499a2425SRoopa Prabhu a->type > LWTUNNEL_ENCAP_MAX) 208499a2425SRoopa Prabhu return 0; 209499a2425SRoopa Prabhu 210499a2425SRoopa Prabhu rcu_read_lock(); 211499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[a->type]); 212499a2425SRoopa Prabhu if (likely(ops && ops->cmp_encap)) 213499a2425SRoopa Prabhu ret = ops->cmp_encap(a, b); 214499a2425SRoopa Prabhu rcu_read_unlock(); 215499a2425SRoopa Prabhu 216499a2425SRoopa Prabhu return ret; 217499a2425SRoopa Prabhu } 218499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_cmp_encap); 219ffce4196SRoopa Prabhu 220ede2059dSEric W. Biederman int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) 221ffce4196SRoopa Prabhu { 22261adedf3SJiri Benc struct dst_entry *dst = skb_dst(skb); 223ffce4196SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 22461adedf3SJiri Benc struct lwtunnel_state *lwtstate; 225ffce4196SRoopa Prabhu int ret = -EINVAL; 226ffce4196SRoopa Prabhu 22761adedf3SJiri Benc if (!dst) 228ffce4196SRoopa Prabhu goto drop; 22961adedf3SJiri Benc lwtstate = dst->lwtstate; 230ffce4196SRoopa Prabhu 231ffce4196SRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 232ffce4196SRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 233ffce4196SRoopa Prabhu return 0; 234ffce4196SRoopa Prabhu 235ffce4196SRoopa Prabhu ret = -EOPNOTSUPP; 236ffce4196SRoopa Prabhu rcu_read_lock(); 237ffce4196SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 238ffce4196SRoopa Prabhu if (likely(ops && ops->output)) 239ede2059dSEric W. Biederman ret = ops->output(net, sk, skb); 240ffce4196SRoopa Prabhu rcu_read_unlock(); 241ffce4196SRoopa Prabhu 242ffce4196SRoopa Prabhu if (ret == -EOPNOTSUPP) 243ffce4196SRoopa Prabhu goto drop; 244ffce4196SRoopa Prabhu 245ffce4196SRoopa Prabhu return ret; 246ffce4196SRoopa Prabhu 247ffce4196SRoopa Prabhu drop: 248e11f40b9SDan Carpenter kfree_skb(skb); 249ffce4196SRoopa Prabhu 250ffce4196SRoopa Prabhu return ret; 251ffce4196SRoopa Prabhu } 252ffce4196SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_output); 25325368623STom Herbert 25414972cbdSRoopa Prabhu int lwtunnel_xmit(struct sk_buff *skb) 25514972cbdSRoopa Prabhu { 25614972cbdSRoopa Prabhu struct dst_entry *dst = skb_dst(skb); 25714972cbdSRoopa Prabhu const struct lwtunnel_encap_ops *ops; 25814972cbdSRoopa Prabhu struct lwtunnel_state *lwtstate; 25914972cbdSRoopa Prabhu int ret = -EINVAL; 26014972cbdSRoopa Prabhu 26114972cbdSRoopa Prabhu if (!dst) 26214972cbdSRoopa Prabhu goto drop; 26314972cbdSRoopa Prabhu 26414972cbdSRoopa Prabhu lwtstate = dst->lwtstate; 26514972cbdSRoopa Prabhu 26614972cbdSRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 26714972cbdSRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 26814972cbdSRoopa Prabhu return 0; 26914972cbdSRoopa Prabhu 27014972cbdSRoopa Prabhu ret = -EOPNOTSUPP; 27114972cbdSRoopa Prabhu rcu_read_lock(); 27214972cbdSRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 27314972cbdSRoopa Prabhu if (likely(ops && ops->xmit)) 27414972cbdSRoopa Prabhu ret = ops->xmit(skb); 27514972cbdSRoopa Prabhu rcu_read_unlock(); 27614972cbdSRoopa Prabhu 27714972cbdSRoopa Prabhu if (ret == -EOPNOTSUPP) 27814972cbdSRoopa Prabhu goto drop; 27914972cbdSRoopa Prabhu 28014972cbdSRoopa Prabhu return ret; 28114972cbdSRoopa Prabhu 28214972cbdSRoopa Prabhu drop: 28314972cbdSRoopa Prabhu kfree_skb(skb); 28414972cbdSRoopa Prabhu 28514972cbdSRoopa Prabhu return ret; 28614972cbdSRoopa Prabhu } 28714972cbdSRoopa Prabhu EXPORT_SYMBOL(lwtunnel_xmit); 28814972cbdSRoopa Prabhu 28961adedf3SJiri Benc int lwtunnel_input(struct sk_buff *skb) 29025368623STom Herbert { 29161adedf3SJiri Benc struct dst_entry *dst = skb_dst(skb); 29225368623STom Herbert const struct lwtunnel_encap_ops *ops; 29361adedf3SJiri Benc struct lwtunnel_state *lwtstate; 29425368623STom Herbert int ret = -EINVAL; 29525368623STom Herbert 29661adedf3SJiri Benc if (!dst) 29725368623STom Herbert goto drop; 29861adedf3SJiri Benc lwtstate = dst->lwtstate; 29925368623STom Herbert 30025368623STom Herbert if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 30125368623STom Herbert lwtstate->type > LWTUNNEL_ENCAP_MAX) 30225368623STom Herbert return 0; 30325368623STom Herbert 30425368623STom Herbert ret = -EOPNOTSUPP; 30525368623STom Herbert rcu_read_lock(); 30625368623STom Herbert ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 30725368623STom Herbert if (likely(ops && ops->input)) 30825368623STom Herbert ret = ops->input(skb); 30925368623STom Herbert rcu_read_unlock(); 31025368623STom Herbert 31125368623STom Herbert if (ret == -EOPNOTSUPP) 31225368623STom Herbert goto drop; 31325368623STom Herbert 31425368623STom Herbert return ret; 31525368623STom Herbert 31625368623STom Herbert drop: 31725368623STom Herbert kfree_skb(skb); 31825368623STom Herbert 31925368623STom Herbert return ret; 32025368623STom Herbert } 32125368623STom Herbert EXPORT_SYMBOL(lwtunnel_input); 322