linux/net/ipv4/gre.c
<<
>>
Prefs
   1/*
   2 *      GRE over IPv4 demultiplexer driver
   3 *
   4 *      Authors: Dmitry Kozlov (xeb@mail.ru)
   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#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14
  15#include <linux/module.h>
  16#include <linux/kernel.h>
  17#include <linux/kmod.h>
  18#include <linux/skbuff.h>
  19#include <linux/in.h>
  20#include <linux/ip.h>
  21#include <linux/netdevice.h>
  22#include <linux/spinlock.h>
  23#include <net/protocol.h>
  24#include <net/gre.h>
  25
  26
  27static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
  28static DEFINE_SPINLOCK(gre_proto_lock);
  29
  30int gre_add_protocol(const struct gre_protocol *proto, u8 version)
  31{
  32        if (version >= GREPROTO_MAX)
  33                goto err_out;
  34
  35        spin_lock(&gre_proto_lock);
  36        if (gre_proto[version])
  37                goto err_out_unlock;
  38
  39        RCU_INIT_POINTER(gre_proto[version], proto);
  40        spin_unlock(&gre_proto_lock);
  41        return 0;
  42
  43err_out_unlock:
  44        spin_unlock(&gre_proto_lock);
  45err_out:
  46        return -1;
  47}
  48EXPORT_SYMBOL_GPL(gre_add_protocol);
  49
  50int gre_del_protocol(const struct gre_protocol *proto, u8 version)
  51{
  52        if (version >= GREPROTO_MAX)
  53                goto err_out;
  54
  55        spin_lock(&gre_proto_lock);
  56        if (rcu_dereference_protected(gre_proto[version],
  57                        lockdep_is_held(&gre_proto_lock)) != proto)
  58                goto err_out_unlock;
  59        RCU_INIT_POINTER(gre_proto[version], NULL);
  60        spin_unlock(&gre_proto_lock);
  61        synchronize_rcu();
  62        return 0;
  63
  64err_out_unlock:
  65        spin_unlock(&gre_proto_lock);
  66err_out:
  67        return -1;
  68}
  69EXPORT_SYMBOL_GPL(gre_del_protocol);
  70
  71static int gre_rcv(struct sk_buff *skb)
  72{
  73        const struct gre_protocol *proto;
  74        u8 ver;
  75        int ret;
  76
  77        if (!pskb_may_pull(skb, 12))
  78                goto drop;
  79
  80        ver = skb->data[1]&0x7f;
  81        if (ver >= GREPROTO_MAX)
  82                goto drop;
  83
  84        rcu_read_lock();
  85        proto = rcu_dereference(gre_proto[ver]);
  86        if (!proto || !proto->handler)
  87                goto drop_unlock;
  88        ret = proto->handler(skb);
  89        rcu_read_unlock();
  90        return ret;
  91
  92drop_unlock:
  93        rcu_read_unlock();
  94drop:
  95        kfree_skb(skb);
  96        return NET_RX_DROP;
  97}
  98
  99static void gre_err(struct sk_buff *skb, u32 info)
 100{
 101        const struct gre_protocol *proto;
 102        const struct iphdr *iph = (const struct iphdr *)skb->data;
 103        u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
 104
 105        if (ver >= GREPROTO_MAX)
 106                return;
 107
 108        rcu_read_lock();
 109        proto = rcu_dereference(gre_proto[ver]);
 110        if (proto && proto->err_handler)
 111                proto->err_handler(skb, info);
 112        rcu_read_unlock();
 113}
 114
 115static const struct net_protocol net_gre_protocol = {
 116        .handler     = gre_rcv,
 117        .err_handler = gre_err,
 118        .netns_ok    = 1,
 119};
 120
 121static int __init gre_init(void)
 122{
 123        pr_info("GRE over IPv4 demultiplexor driver\n");
 124
 125        if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
 126                pr_err("can't add protocol\n");
 127                return -EAGAIN;
 128        }
 129
 130        return 0;
 131}
 132
 133static void __exit gre_exit(void)
 134{
 135        inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
 136}
 137
 138module_init(gre_init);
 139module_exit(gre_exit);
 140
 141MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
 142MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
 143MODULE_LICENSE("GPL");
 144
 145
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.