2010-08-22 10:05:39 +04:00
/*
* GRE over IPv4 demultiplexer driver
*
* Authors : Dmitry Kozlov ( xeb @ mail . ru )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
*/
2012-03-12 11:03:32 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-08-22 10:05:39 +04:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/kmod.h>
# include <linux/skbuff.h>
# include <linux/in.h>
2011-07-23 00:49:40 +04:00
# include <linux/ip.h>
2010-08-22 10:05:39 +04:00
# include <linux/netdevice.h>
# include <linux/spinlock.h>
# include <net/protocol.h>
# include <net/gre.h>
2010-10-25 01:33:16 +04:00
static const struct gre_protocol __rcu * gre_proto [ GREPROTO_MAX ] __read_mostly ;
2010-08-22 10:05:39 +04:00
static DEFINE_SPINLOCK ( gre_proto_lock ) ;
int gre_add_protocol ( const struct gre_protocol * proto , u8 version )
{
if ( version > = GREPROTO_MAX )
goto err_out ;
spin_lock ( & gre_proto_lock ) ;
if ( gre_proto [ version ] )
goto err_out_unlock ;
2011-08-01 20:19:00 +04:00
RCU_INIT_POINTER ( gre_proto [ version ] , proto ) ;
2010-08-22 10:05:39 +04:00
spin_unlock ( & gre_proto_lock ) ;
return 0 ;
err_out_unlock :
spin_unlock ( & gre_proto_lock ) ;
err_out :
return - 1 ;
}
EXPORT_SYMBOL_GPL ( gre_add_protocol ) ;
int gre_del_protocol ( const struct gre_protocol * proto , u8 version )
{
if ( version > = GREPROTO_MAX )
goto err_out ;
spin_lock ( & gre_proto_lock ) ;
2010-10-25 01:33:16 +04:00
if ( rcu_dereference_protected ( gre_proto [ version ] ,
lockdep_is_held ( & gre_proto_lock ) ) ! = proto )
2010-08-22 10:05:39 +04:00
goto err_out_unlock ;
2011-08-01 20:19:00 +04:00
RCU_INIT_POINTER ( gre_proto [ version ] , NULL ) ;
2010-08-22 10:05:39 +04:00
spin_unlock ( & gre_proto_lock ) ;
synchronize_rcu ( ) ;
return 0 ;
err_out_unlock :
spin_unlock ( & gre_proto_lock ) ;
err_out :
return - 1 ;
}
EXPORT_SYMBOL_GPL ( gre_del_protocol ) ;
static int gre_rcv ( struct sk_buff * skb )
{
const struct gre_protocol * proto ;
u8 ver ;
int ret ;
if ( ! pskb_may_pull ( skb , 12 ) )
goto drop ;
ver = skb - > data [ 1 ] & 0x7f ;
if ( ver > = GREPROTO_MAX )
goto drop ;
rcu_read_lock ( ) ;
proto = rcu_dereference ( gre_proto [ ver ] ) ;
if ( ! proto | | ! proto - > handler )
goto drop_unlock ;
ret = proto - > handler ( skb ) ;
rcu_read_unlock ( ) ;
return ret ;
drop_unlock :
rcu_read_unlock ( ) ;
drop :
kfree_skb ( skb ) ;
return NET_RX_DROP ;
}
static void gre_err ( struct sk_buff * skb , u32 info )
{
const struct gre_protocol * proto ;
2011-07-23 00:49:40 +04:00
const struct iphdr * iph = ( const struct iphdr * ) skb - > data ;
u8 ver = skb - > data [ ( iph - > ihl < < 2 ) + 1 ] & 0x7f ;
2010-08-22 10:05:39 +04:00
if ( ver > = GREPROTO_MAX )
2011-07-23 00:49:40 +04:00
return ;
2010-08-22 10:05:39 +04:00
rcu_read_lock ( ) ;
proto = rcu_dereference ( gre_proto [ ver ] ) ;
2011-07-23 00:49:40 +04:00
if ( proto & & proto - > err_handler )
proto - > err_handler ( skb , info ) ;
2010-08-22 10:05:39 +04:00
rcu_read_unlock ( ) ;
}
static const struct net_protocol net_gre_protocol = {
. handler = gre_rcv ,
. err_handler = gre_err ,
. netns_ok = 1 ,
} ;
static int __init gre_init ( void )
{
2012-03-12 11:03:32 +04:00
pr_info ( " GRE over IPv4 demultiplexor driver \n " ) ;
2010-08-22 10:05:39 +04:00
if ( inet_add_protocol ( & net_gre_protocol , IPPROTO_GRE ) < 0 ) {
2012-03-12 11:03:32 +04:00
pr_err ( " can't add protocol \n " ) ;
2010-08-22 10:05:39 +04:00
return - EAGAIN ;
}
return 0 ;
}
static void __exit gre_exit ( void )
{
inet_del_protocol ( & net_gre_protocol , IPPROTO_GRE ) ;
}
module_init ( gre_init ) ;
module_exit ( gre_exit ) ;
MODULE_DESCRIPTION ( " GRE over IPv4 demultiplexer driver " ) ;
MODULE_AUTHOR ( " D. Kozlov (xeb@mail.ru) " ) ;
MODULE_LICENSE ( " GPL " ) ;