at v3.8-rc7 144 lines 3.1 kB view raw
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