linux/net/core/lwtunnel.c
<<
>>
Prefs
   1/*
   2 * lwtunnel     Infrastructure for light weight tunnels like mpls
   3 *
   4 * Authors:     Roopa Prabhu, <roopa@cumulusnetworks.com>
   5 *
   6 *              This program is free software; you can redistribute it and/or
   7 *              modify it under the terms of the GNU General Public License
   8 *              as published by the Free Software Foundation; either version
   9 *              2 of the License, or (at your option) any later version.
  10 *
  11 */
  12
  13#include <linux/capability.h>
  14#include <linux/module.h>
  15#include <linux/types.h>
  16#include <linux/kernel.h>
  17#include <linux/slab.h>
  18#include <linux/uaccess.h>
  19#include <linux/skbuff.h>
  20#include <linux/netdevice.h>
  21#include <linux/lwtunnel.h>
  22#include <linux/in.h>
  23#include <linux/init.h>
  24#include <linux/err.h>
  25
  26#include <net/lwtunnel.h>
  27#include <net/rtnetlink.h>
  28#include <net/ip6_fib.h>
  29
  30#ifdef CONFIG_MODULES
  31
  32static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
  33{
  34        /* Only lwt encaps implemented without using an interface for
  35         * the encap need to return a string here.
  36         */
  37        switch (encap_type) {
  38        case LWTUNNEL_ENCAP_MPLS:
  39                return "MPLS";
  40        case LWTUNNEL_ENCAP_ILA:
  41                return "ILA";
  42        case LWTUNNEL_ENCAP_IP6:
  43        case LWTUNNEL_ENCAP_IP:
  44        case LWTUNNEL_ENCAP_NONE:
  45        case __LWTUNNEL_ENCAP_MAX:
  46                /* should not have got here */
  47                WARN_ON(1);
  48                break;
  49        }
  50        return NULL;
  51}
  52
  53#endif /* CONFIG_MODULES */
  54
  55struct lwtunnel_state *lwtunnel_state_alloc(int encap_len)
  56{
  57        struct lwtunnel_state *lws;
  58
  59        lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC);
  60
  61        return lws;
  62}
  63EXPORT_SYMBOL(lwtunnel_state_alloc);
  64
  65static const struct lwtunnel_encap_ops __rcu *
  66                lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly;
  67
  68int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops,
  69                           unsigned int num)
  70{
  71        if (num > LWTUNNEL_ENCAP_MAX)
  72                return -ERANGE;
  73
  74        return !cmpxchg((const struct lwtunnel_encap_ops **)
  75                        &lwtun_encaps[num],
  76                        NULL, ops) ? 0 : -1;
  77}
  78EXPORT_SYMBOL(lwtunnel_encap_add_ops);
  79
  80int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops,
  81                           unsigned int encap_type)
  82{
  83        int ret;
  84
  85        if (encap_type == LWTUNNEL_ENCAP_NONE ||
  86            encap_type > LWTUNNEL_ENCAP_MAX)
  87                return -ERANGE;
  88
  89        ret = (cmpxchg((const struct lwtunnel_encap_ops **)
  90                       &lwtun_encaps[encap_type],
  91                       ops, NULL) == ops) ? 0 : -1;
  92
  93        synchronize_net();
  94
  95        return ret;
  96}
  97EXPORT_SYMBOL(lwtunnel_encap_del_ops);
  98
  99int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
 100                         struct nlattr *encap, unsigned int family,
 101                         const void *cfg, struct lwtunnel_state **lws)
 102{
 103        const struct lwtunnel_encap_ops *ops;
 104        int ret = -EINVAL;
 105
 106        if (encap_type == LWTUNNEL_ENCAP_NONE ||
 107            encap_type > LWTUNNEL_ENCAP_MAX)
 108                return ret;
 109
 110        ret = -EOPNOTSUPP;
 111        rcu_read_lock();
 112        ops = rcu_dereference(lwtun_encaps[encap_type]);
 113#ifdef CONFIG_MODULES
 114        if (!ops) {
 115                const char *encap_type_str = lwtunnel_encap_str(encap_type);
 116
 117                if (encap_type_str) {
 118                        rcu_read_unlock();
 119                        request_module("rtnl-lwt-%s", encap_type_str);
 120                        rcu_read_lock();
 121                        ops = rcu_dereference(lwtun_encaps[encap_type]);
 122                }
 123        }
 124#endif
 125        if (likely(ops && ops->build_state))
 126                ret = ops->build_state(dev, encap, family, cfg, lws);
 127        rcu_read_unlock();
 128
 129        return ret;
 130}
 131EXPORT_SYMBOL(lwtunnel_build_state);
 132
 133int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate)
 134{
 135        const struct lwtunnel_encap_ops *ops;
 136        struct nlattr *nest;
 137        int ret = -EINVAL;
 138
 139        if (!lwtstate)
 140                return 0;
 141
 142        if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
 143            lwtstate->type > LWTUNNEL_ENCAP_MAX)
 144                return 0;
 145
 146        ret = -EOPNOTSUPP;
 147        nest = nla_nest_start(skb, RTA_ENCAP);
 148        rcu_read_lock();
 149        ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
 150        if (likely(ops && ops->fill_encap))
 151                ret = ops->fill_encap(skb, lwtstate);
 152        rcu_read_unlock();
 153
 154        if (ret)
 155                goto nla_put_failure;
 156        nla_nest_end(skb, nest);
 157        ret = nla_put_u16(skb, RTA_ENCAP_TYPE, lwtstate->type);
 158        if (ret)
 159                goto nla_put_failure;
 160
 161        return 0;
 162
 163nla_put_failure:
 164        nla_nest_cancel(skb, nest);
 165
 166        return (ret == -EOPNOTSUPP ? 0 : ret);
 167}
 168EXPORT_SYMBOL(lwtunnel_fill_encap);
 169
 170int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate)
 171{
 172        const struct lwtunnel_encap_ops *ops;
 173        int ret = 0;
 174
 175        if (!lwtstate)
 176                return 0;
 177
 178        if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
 179            lwtstate->type > LWTUNNEL_ENCAP_MAX)
 180                return 0;
 181
 182        rcu_read_lock();
 183        ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
 184        if (likely(ops && ops->get_encap_size))
 185                ret = nla_total_size(ops->get_encap_size(lwtstate));
 186        rcu_read_unlock();
 187
 188        return ret;
 189}
 190EXPORT_SYMBOL(lwtunnel_get_encap_size);
 191
 192int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b)
 193{
 194        const struct lwtunnel_encap_ops *ops;
 195        int ret = 0;
 196
 197        if (!a && !b)
 198                return 0;
 199
 200        if (!a || !b)
 201                return 1;
 202
 203        if (a->type != b->type)
 204                return 1;
 205
 206        if (a->type == LWTUNNEL_ENCAP_NONE ||
 207            a->type > LWTUNNEL_ENCAP_MAX)
 208                return 0;
 209
 210        rcu_read_lock();
 211        ops = rcu_dereference(lwtun_encaps[a->type]);
 212        if (likely(ops && ops->cmp_encap))
 213                ret = ops->cmp_encap(a, b);
 214        rcu_read_unlock();
 215
 216        return ret;
 217}
 218EXPORT_SYMBOL(lwtunnel_cmp_encap);
 219
 220int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 221{
 222        struct dst_entry *dst = skb_dst(skb);
 223        const struct lwtunnel_encap_ops *ops;
 224        struct lwtunnel_state *lwtstate;
 225        int ret = -EINVAL;
 226
 227        if (!dst)
 228                goto drop;
 229        lwtstate = dst->lwtstate;
 230
 231        if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
 232            lwtstate->type > LWTUNNEL_ENCAP_MAX)
 233                return 0;
 234
 235        ret = -EOPNOTSUPP;
 236        rcu_read_lock();
 237        ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
 238        if (likely(ops && ops->output))
 239                ret = ops->output(net, sk, skb);
 240        rcu_read_unlock();
 241
 242        if (ret == -EOPNOTSUPP)
 243                goto drop;
 244
 245        return ret;
 246
 247drop:
 248        kfree_skb(skb);
 249
 250        return ret;
 251}
 252EXPORT_SYMBOL(lwtunnel_output);
 253
 254int lwtunnel_input(struct sk_buff *skb)
 255{
 256        struct dst_entry *dst = skb_dst(skb);
 257        const struct lwtunnel_encap_ops *ops;
 258        struct lwtunnel_state *lwtstate;
 259        int ret = -EINVAL;
 260
 261        if (!dst)
 262                goto drop;
 263        lwtstate = dst->lwtstate;
 264
 265        if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
 266            lwtstate->type > LWTUNNEL_ENCAP_MAX)
 267                return 0;
 268
 269        ret = -EOPNOTSUPP;
 270        rcu_read_lock();
 271        ops = rcu_dereference(lwtun_encaps[lwtstate->type]);
 272        if (likely(ops && ops->input))
 273                ret = ops->input(skb);
 274        rcu_read_unlock();
 275
 276        if (ret == -EOPNOTSUPP)
 277                goto drop;
 278
 279        return ret;
 280
 281drop:
 282        kfree_skb(skb);
 283
 284        return ret;
 285}
 286EXPORT_SYMBOL(lwtunnel_input);
 287