diff options
Diffstat (limited to 'driver')
-rw-r--r-- | driver/gre.c | 136 |
1 files changed, 108 insertions, 28 deletions
diff --git a/driver/gre.c b/driver/gre.c index cda0576..77886d5 100644 --- a/driver/gre.c +++ b/driver/gre.c @@ -1,6 +1,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/kmod.h> +#include <linux/init.h> #include <linux/skbuff.h> #include <linux/in.h> #include <linux/netdevice.h> @@ -10,50 +10,85 @@ #include "gre.h" - struct gre_protocol *gre_proto[GREPROTO_MAX] ____cacheline_aligned_in_smp; -static DEFINE_RWLOCK(gre_proto_lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +static rwlock_t gre_proto_lock=RW_LOCK_UNLOCKED; +#else +static DEFINE_SPINLOCK(gre_proto_lock); +#endif int gre_add_protocol(struct gre_protocol *proto, u8 version) { int ret; if (version >= GREPROTO_MAX) - return -1; + return -EINVAL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) write_lock_bh(&gre_proto_lock); +#else + spin_lock(&gre_proto_lock); +#endif if (gre_proto[version]) { - ret = -1; + ret = -EAGAIN; } else { - gre_proto[version]=proto; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + gre_proto[version] = proto; +#else + rcu_assign_pointer(gre_proto[version], proto); +#endif ret = 0; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) write_unlock_bh(&gre_proto_lock); +#else + spin_unlock(&gre_proto_lock); +#endif return ret; } + int gre_del_protocol(struct gre_protocol *proto, u8 version) { - int ret; - if (version >= GREPROTO_MAX) - return -1; + goto out_err; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) write_lock_bh(&gre_proto_lock); - if (gre_proto[version] == proto) { +#else + spin_lock(&gre_proto_lock); +#endif + if (gre_proto[version] == proto) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) gre_proto[version] = NULL; - ret = 0; - } else { - ret = -1; - } +#else + rcu_assign_pointer(gre_proto[version], NULL); +#endif + else + goto out_err_unlock; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) write_unlock_bh(&gre_proto_lock); +#else + spin_unlock(&gre_proto_lock); + synchronize_rcu(); +#endif + return 0; - return ret; +out_err_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + write_unlock_bh(&gre_proto_lock); +#else + spin_unlock(&gre_proto_lock); +#endif +out_err: + return -EINVAL; } + static int gre_rcv(struct sk_buff *skb) { u8 ver; int ret; + struct gre_protocol *proto; if (!pskb_may_pull(skb, 12)) goto drop_nolock; @@ -62,71 +97,117 @@ static int gre_rcv(struct sk_buff *skb) if (ver >= GREPROTO_MAX) goto drop_nolock; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_lock(&gre_proto_lock); - if (!gre_proto[ver] || !gre_proto[ver]->handler) + proto = gre_proto[ver]; +#else + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); +#endif + if (!proto || !proto->handler) goto drop; - ret = gre_proto[ver]->handler(skb); + ret = proto->handler(skb); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_unlock(&gre_proto_lock); +#else + rcu_read_unlock(); +#endif return ret; drop: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_unlock(&gre_proto_lock); +#else + rcu_read_unlock(); +#endif drop_nolock: kfree_skb(skb); return NET_RX_DROP; } + static void gre_err(struct sk_buff *skb, u32 info) { u8 ver; - - printk("err\n"); + struct gre_protocol *proto; if (!pskb_may_pull(skb, 12)) goto drop_nolock; - ver=skb->data[1]; + ver=skb->data[1]&0x7f; if (ver>=GREPROTO_MAX) goto drop_nolock; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_lock(&gre_proto_lock); - if (!gre_proto[ver] || !gre_proto[ver]->err_handler) + proto = gre_proto[ver]; +#else + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); +#endif + if (!proto || !proto->err_handler) goto drop; - gre_proto[ver]->err_handler(skb,info); + proto->err_handler(skb, info); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_unlock(&gre_proto_lock); +#else + rcu_read_unlock(); +#endif return; drop: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_unlock(&gre_proto_lock); +#else + rcu_read_unlock(); +#endif drop_nolock: kfree_skb(skb); } - +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +static struct inet_protocol net_gre_protocol = { + .handler = gre_rcv, + .err_handler = gre_err, + .protocol = IPPROTO_GRE, + .name = "GRE", +}; +#else static struct net_protocol net_gre_protocol = { .handler = gre_rcv, - .err_handler = gre_err, -// .netns_ok=1, + .err_handler = gre_err, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,24) + .netns_ok=1, +#endif }; +#endif static int __init gre_init(void) { printk(KERN_INFO "GRE over IPv4 demultiplexor driver"); - + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + inet_add_protocol(&net_gre_protocol); +#else if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { printk(KERN_INFO "gre: can't add protocol\n"); return -EAGAIN; } - +#endif return 0; } static void __exit gre_exit(void) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + inet_del_protocol(&net_gre_protocol); +#else inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); +#endif } module_init(gre_init); @@ -137,4 +218,3 @@ MODULE_AUTHOR("Kozlov D. (xeb@mail.ru)"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL_GPL(gre_add_protocol); EXPORT_SYMBOL_GPL(gre_del_protocol); - |