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"; 426c8702c6SDavid Lebrun case LWTUNNEL_ENCAP_SEG6: 436c8702c6SDavid Lebrun return "SEG6"; 443a0af8fdSThomas Graf case LWTUNNEL_ENCAP_BPF: 453a0af8fdSThomas Graf return "BPF"; 46745041e2SRobert Shearman case LWTUNNEL_ENCAP_IP6: 47745041e2SRobert Shearman case LWTUNNEL_ENCAP_IP: 48745041e2SRobert Shearman case LWTUNNEL_ENCAP_NONE: 49745041e2SRobert Shearman case __LWTUNNEL_ENCAP_MAX: 50745041e2SRobert Shearman /* should not have got here */ 51745041e2SRobert Shearman WARN_ON(1); 52745041e2SRobert Shearman break; 53745041e2SRobert Shearman } 54745041e2SRobert Shearman return NULL; 55745041e2SRobert Shearman } 56745041e2SRobert Shearman 57745041e2SRobert Shearman #endif /* CONFIG_MODULES */ 58745041e2SRobert Shearman 59499a2425SRoopa Prabhu struct lwtunnel_state *lwtunnel_state_alloc(int encap_len) 60499a2425SRoopa Prabhu { 61499a2425SRoopa Prabhu struct lwtunnel_state *lws; 62499a2425SRoopa Prabhu 63499a2425SRoopa Prabhu lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC); 64499a2425SRoopa Prabhu 65499a2425SRoopa Prabhu return lws; 66499a2425SRoopa Prabhu } 67499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_state_alloc); 68499a2425SRoopa Prabhu 6992a99bf3SThomas Graf static const struct lwtunnel_encap_ops __rcu * 70499a2425SRoopa Prabhu lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly; 71499a2425SRoopa Prabhu 72499a2425SRoopa Prabhu int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops, 73499a2425SRoopa Prabhu unsigned int num) 74499a2425SRoopa Prabhu { 75499a2425SRoopa Prabhu if (num > LWTUNNEL_ENCAP_MAX) 76499a2425SRoopa Prabhu return -ERANGE; 77499a2425SRoopa Prabhu 78499a2425SRoopa Prabhu return !cmpxchg((const struct lwtunnel_encap_ops **) 79499a2425SRoopa Prabhu &lwtun_encaps[num], 80499a2425SRoopa Prabhu NULL, ops) ? 0 : -1; 81499a2425SRoopa Prabhu } 82499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_encap_add_ops); 83499a2425SRoopa Prabhu 84499a2425SRoopa Prabhu int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops, 85499a2425SRoopa Prabhu unsigned int encap_type) 86499a2425SRoopa Prabhu { 87499a2425SRoopa Prabhu int ret; 88499a2425SRoopa Prabhu 89499a2425SRoopa Prabhu if (encap_type == LWTUNNEL_ENCAP_NONE || 90499a2425SRoopa Prabhu encap_type > LWTUNNEL_ENCAP_MAX) 91499a2425SRoopa Prabhu return -ERANGE; 92499a2425SRoopa Prabhu 93499a2425SRoopa Prabhu ret = (cmpxchg((const struct lwtunnel_encap_ops **) 94499a2425SRoopa Prabhu &lwtun_encaps[encap_type], 95499a2425SRoopa Prabhu ops, NULL) == ops) ? 0 : -1; 96499a2425SRoopa Prabhu 97499a2425SRoopa Prabhu synchronize_net(); 98499a2425SRoopa Prabhu 99499a2425SRoopa Prabhu return ret; 100499a2425SRoopa Prabhu } 101499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_encap_del_ops); 102499a2425SRoopa Prabhu 103499a2425SRoopa Prabhu int lwtunnel_build_state(struct net_device *dev, u16 encap_type, 104127eb7cdSTom Herbert struct nlattr *encap, unsigned int family, 105127eb7cdSTom Herbert const void *cfg, struct lwtunnel_state **lws) 106499a2425SRoopa Prabhu { 107499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 108499a2425SRoopa Prabhu int ret = -EINVAL; 109499a2425SRoopa Prabhu 110499a2425SRoopa Prabhu if (encap_type == LWTUNNEL_ENCAP_NONE || 111499a2425SRoopa Prabhu encap_type > LWTUNNEL_ENCAP_MAX) 112499a2425SRoopa Prabhu return ret; 113499a2425SRoopa Prabhu 114499a2425SRoopa Prabhu ret = -EOPNOTSUPP; 115499a2425SRoopa Prabhu rcu_read_lock(); 116499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[encap_type]); 117745041e2SRobert Shearman #ifdef CONFIG_MODULES 118745041e2SRobert Shearman if (!ops) { 119745041e2SRobert Shearman const char *encap_type_str = lwtunnel_encap_str(encap_type); 120745041e2SRobert Shearman 121745041e2SRobert Shearman if (encap_type_str) { 122745041e2SRobert Shearman rcu_read_unlock(); 123745041e2SRobert Shearman request_module("rtnl-lwt-%s", encap_type_str); 124745041e2SRobert Shearman rcu_read_lock(); 125745041e2SRobert Shearman ops = rcu_dereference(lwtun_encaps[encap_type]); 126745041e2SRobert Shearman } 127745041e2SRobert Shearman } 128745041e2SRobert Shearman #endif 129499a2425SRoopa Prabhu if (likely(ops && ops->build_state)) 130127eb7cdSTom Herbert ret = ops->build_state(dev, encap, family, cfg, lws); 131499a2425SRoopa Prabhu rcu_read_unlock(); 132499a2425SRoopa Prabhu 133499a2425SRoopa Prabhu return ret; 134499a2425SRoopa Prabhu } 135499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_build_state); 136499a2425SRoopa Prabhu 1371104d9baSTom Herbert void lwtstate_free(struct lwtunnel_state *lws) 1381104d9baSTom Herbert { 1391104d9baSTom Herbert const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type]; 1401104d9baSTom Herbert 1411104d9baSTom Herbert if (ops->destroy_state) { 1421104d9baSTom Herbert ops->destroy_state(lws); 1431104d9baSTom Herbert kfree_rcu(lws, rcu); 1441104d9baSTom Herbert } else { 1451104d9baSTom Herbert kfree(lws); 1461104d9baSTom Herbert } 1471104d9baSTom Herbert } 1481104d9baSTom Herbert EXPORT_SYMBOL(lwtstate_free); 1491104d9baSTom Herbert 150499a2425SRoopa Prabhu int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate) 151499a2425SRoopa Prabhu { 152499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 153499a2425SRoopa Prabhu struct nlattr *nest; 154499a2425SRoopa Prabhu int ret = -EINVAL; 155499a2425SRoopa Prabhu 156499a2425SRoopa Prabhu if (!lwtstate) 157499a2425SRoopa Prabhu return 0; 158499a2425SRoopa Prabhu 159499a2425SRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 160499a2425SRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 161499a2425SRoopa Prabhu return 0; 162499a2425SRoopa Prabhu 163499a2425SRoopa Prabhu ret = -EOPNOTSUPP; 164499a2425SRoopa Prabhu nest = nla_nest_start(skb, RTA_ENCAP); 165499a2425SRoopa Prabhu rcu_read_lock(); 166499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 167499a2425SRoopa Prabhu if (likely(ops && ops->fill_encap)) 168499a2425SRoopa Prabhu ret = ops->fill_encap(skb, lwtstate); 169499a2425SRoopa Prabhu rcu_read_unlock(); 170499a2425SRoopa Prabhu 171499a2425SRoopa Prabhu if (ret) 172499a2425SRoopa Prabhu goto nla_put_failure; 173499a2425SRoopa Prabhu nla_nest_end(skb, nest); 174499a2425SRoopa Prabhu ret = nla_put_u16(skb, RTA_ENCAP_TYPE, lwtstate->type); 175499a2425SRoopa Prabhu if (ret) 176499a2425SRoopa Prabhu goto nla_put_failure; 177499a2425SRoopa Prabhu 178499a2425SRoopa Prabhu return 0; 179499a2425SRoopa Prabhu 180499a2425SRoopa Prabhu nla_put_failure: 181499a2425SRoopa Prabhu nla_nest_cancel(skb, nest); 182499a2425SRoopa Prabhu 183499a2425SRoopa Prabhu return (ret == -EOPNOTSUPP ? 0 : ret); 184499a2425SRoopa Prabhu } 185499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_fill_encap); 186499a2425SRoopa Prabhu 187499a2425SRoopa Prabhu int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate) 188499a2425SRoopa Prabhu { 189499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 190499a2425SRoopa Prabhu int ret = 0; 191499a2425SRoopa Prabhu 192499a2425SRoopa Prabhu if (!lwtstate) 193499a2425SRoopa Prabhu return 0; 194499a2425SRoopa Prabhu 195499a2425SRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 196499a2425SRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 197499a2425SRoopa Prabhu return 0; 198499a2425SRoopa Prabhu 199499a2425SRoopa Prabhu rcu_read_lock(); 200499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 201499a2425SRoopa Prabhu if (likely(ops && ops->get_encap_size)) 202499a2425SRoopa Prabhu ret = nla_total_size(ops->get_encap_size(lwtstate)); 203499a2425SRoopa Prabhu rcu_read_unlock(); 204499a2425SRoopa Prabhu 205499a2425SRoopa Prabhu return ret; 206499a2425SRoopa Prabhu } 207499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_get_encap_size); 208499a2425SRoopa Prabhu 209499a2425SRoopa Prabhu int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b) 210499a2425SRoopa Prabhu { 211499a2425SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 212499a2425SRoopa Prabhu int ret = 0; 213499a2425SRoopa Prabhu 214499a2425SRoopa Prabhu if (!a && !b) 215499a2425SRoopa Prabhu return 0; 216499a2425SRoopa Prabhu 217499a2425SRoopa Prabhu if (!a || !b) 218499a2425SRoopa Prabhu return 1; 219499a2425SRoopa Prabhu 220499a2425SRoopa Prabhu if (a->type != b->type) 221499a2425SRoopa Prabhu return 1; 222499a2425SRoopa Prabhu 223499a2425SRoopa Prabhu if (a->type == LWTUNNEL_ENCAP_NONE || 224499a2425SRoopa Prabhu a->type > LWTUNNEL_ENCAP_MAX) 225499a2425SRoopa Prabhu return 0; 226499a2425SRoopa Prabhu 227499a2425SRoopa Prabhu rcu_read_lock(); 228499a2425SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[a->type]); 229499a2425SRoopa Prabhu if (likely(ops && ops->cmp_encap)) 230499a2425SRoopa Prabhu ret = ops->cmp_encap(a, b); 231499a2425SRoopa Prabhu rcu_read_unlock(); 232499a2425SRoopa Prabhu 233499a2425SRoopa Prabhu return ret; 234499a2425SRoopa Prabhu } 235499a2425SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_cmp_encap); 236ffce4196SRoopa Prabhu 237ede2059dSEric W. Biederman int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) 238ffce4196SRoopa Prabhu { 23961adedf3SJiri Benc struct dst_entry *dst = skb_dst(skb); 240ffce4196SRoopa Prabhu const struct lwtunnel_encap_ops *ops; 24161adedf3SJiri Benc struct lwtunnel_state *lwtstate; 242ffce4196SRoopa Prabhu int ret = -EINVAL; 243ffce4196SRoopa Prabhu 24461adedf3SJiri Benc if (!dst) 245ffce4196SRoopa Prabhu goto drop; 24661adedf3SJiri Benc lwtstate = dst->lwtstate; 247ffce4196SRoopa Prabhu 248ffce4196SRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 249ffce4196SRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 250ffce4196SRoopa Prabhu return 0; 251ffce4196SRoopa Prabhu 252ffce4196SRoopa Prabhu ret = -EOPNOTSUPP; 253ffce4196SRoopa Prabhu rcu_read_lock(); 254ffce4196SRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 255ffce4196SRoopa Prabhu if (likely(ops && ops->output)) 256ede2059dSEric W. Biederman ret = ops->output(net, sk, skb); 257ffce4196SRoopa Prabhu rcu_read_unlock(); 258ffce4196SRoopa Prabhu 259ffce4196SRoopa Prabhu if (ret == -EOPNOTSUPP) 260ffce4196SRoopa Prabhu goto drop; 261ffce4196SRoopa Prabhu 262ffce4196SRoopa Prabhu return ret; 263ffce4196SRoopa Prabhu 264ffce4196SRoopa Prabhu drop: 265e11f40b9SDan Carpenter kfree_skb(skb); 266ffce4196SRoopa Prabhu 267ffce4196SRoopa Prabhu return ret; 268ffce4196SRoopa Prabhu } 269ffce4196SRoopa Prabhu EXPORT_SYMBOL(lwtunnel_output); 27025368623STom Herbert 27114972cbdSRoopa Prabhu int lwtunnel_xmit(struct sk_buff *skb) 27214972cbdSRoopa Prabhu { 27314972cbdSRoopa Prabhu struct dst_entry *dst = skb_dst(skb); 27414972cbdSRoopa Prabhu const struct lwtunnel_encap_ops *ops; 27514972cbdSRoopa Prabhu struct lwtunnel_state *lwtstate; 27614972cbdSRoopa Prabhu int ret = -EINVAL; 27714972cbdSRoopa Prabhu 27814972cbdSRoopa Prabhu if (!dst) 27914972cbdSRoopa Prabhu goto drop; 28014972cbdSRoopa Prabhu 28114972cbdSRoopa Prabhu lwtstate = dst->lwtstate; 28214972cbdSRoopa Prabhu 28314972cbdSRoopa Prabhu if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 28414972cbdSRoopa Prabhu lwtstate->type > LWTUNNEL_ENCAP_MAX) 28514972cbdSRoopa Prabhu return 0; 28614972cbdSRoopa Prabhu 28714972cbdSRoopa Prabhu ret = -EOPNOTSUPP; 28814972cbdSRoopa Prabhu rcu_read_lock(); 28914972cbdSRoopa Prabhu ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 29014972cbdSRoopa Prabhu if (likely(ops && ops->xmit)) 29114972cbdSRoopa Prabhu ret = ops->xmit(skb); 29214972cbdSRoopa Prabhu rcu_read_unlock(); 29314972cbdSRoopa Prabhu 29414972cbdSRoopa Prabhu if (ret == -EOPNOTSUPP) 29514972cbdSRoopa Prabhu goto drop; 29614972cbdSRoopa Prabhu 29714972cbdSRoopa Prabhu return ret; 29814972cbdSRoopa Prabhu 29914972cbdSRoopa Prabhu drop: 30014972cbdSRoopa Prabhu kfree_skb(skb); 30114972cbdSRoopa Prabhu 30214972cbdSRoopa Prabhu return ret; 30314972cbdSRoopa Prabhu } 30414972cbdSRoopa Prabhu EXPORT_SYMBOL(lwtunnel_xmit); 30514972cbdSRoopa Prabhu 30661adedf3SJiri Benc int lwtunnel_input(struct sk_buff *skb) 30725368623STom Herbert { 30861adedf3SJiri Benc struct dst_entry *dst = skb_dst(skb); 30925368623STom Herbert const struct lwtunnel_encap_ops *ops; 31061adedf3SJiri Benc struct lwtunnel_state *lwtstate; 31125368623STom Herbert int ret = -EINVAL; 31225368623STom Herbert 31361adedf3SJiri Benc if (!dst) 31425368623STom Herbert goto drop; 31561adedf3SJiri Benc lwtstate = dst->lwtstate; 31625368623STom Herbert 31725368623STom Herbert if (lwtstate->type == LWTUNNEL_ENCAP_NONE || 31825368623STom Herbert lwtstate->type > LWTUNNEL_ENCAP_MAX) 31925368623STom Herbert return 0; 32025368623STom Herbert 32125368623STom Herbert ret = -EOPNOTSUPP; 32225368623STom Herbert rcu_read_lock(); 32325368623STom Herbert ops = rcu_dereference(lwtun_encaps[lwtstate->type]); 32425368623STom Herbert if (likely(ops && ops->input)) 32525368623STom Herbert ret = ops->input(skb); 32625368623STom Herbert rcu_read_unlock(); 32725368623STom Herbert 32825368623STom Herbert if (ret == -EOPNOTSUPP) 32925368623STom Herbert goto drop; 33025368623STom Herbert 33125368623STom Herbert return ret; 33225368623STom Herbert 33325368623STom Herbert drop: 33425368623STom Herbert kfree_skb(skb); 33525368623STom Herbert 33625368623STom Herbert return ret; 33725368623STom Herbert } 33825368623STom Herbert EXPORT_SYMBOL(lwtunnel_input); 339