2006-03-28 13:12:13 +04:00
/*
* Copyright ( C ) 2003 , 2004 USAGI / WIDE Project
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* Authors Mitsuru KANDA < mk @ linux - ipv6 . org >
* YOSHIFUJI Hideaki < yoshfuji @ linux - ipv6 . org >
*/
2006-04-05 00:50:45 +04:00
# include <linux/icmpv6.h>
2006-03-28 13:12:13 +04:00
# include <linux/init.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/netdevice.h>
# include <linux/skbuff.h>
2006-04-05 00:50:45 +04:00
# include <net/ipv6.h>
2006-03-28 13:12:13 +04:00
# include <net/protocol.h>
# include <net/xfrm.h>
static struct xfrm6_tunnel * tunnel6_handlers ;
2007-02-13 23:55:55 +03:00
static struct xfrm6_tunnel * tunnel46_handlers ;
2006-03-28 13:12:13 +04:00
static DEFINE_MUTEX ( tunnel6_mutex ) ;
2007-02-13 23:55:55 +03:00
int xfrm6_tunnel_register ( struct xfrm6_tunnel * handler , unsigned short family )
2006-03-28 13:12:13 +04:00
{
struct xfrm6_tunnel * * pprev ;
int ret = - EEXIST ;
int priority = handler - > priority ;
mutex_lock ( & tunnel6_mutex ) ;
2007-02-13 23:55:55 +03:00
for ( pprev = ( family = = AF_INET6 ) ? & tunnel6_handlers : & tunnel46_handlers ;
* pprev ; pprev = & ( * pprev ) - > next ) {
2006-03-28 13:12:13 +04:00
if ( ( * pprev ) - > priority > priority )
break ;
if ( ( * pprev ) - > priority = = priority )
goto err ;
}
handler - > next = * pprev ;
* pprev = handler ;
ret = 0 ;
err :
mutex_unlock ( & tunnel6_mutex ) ;
return ret ;
}
EXPORT_SYMBOL ( xfrm6_tunnel_register ) ;
2007-02-13 23:55:55 +03:00
int xfrm6_tunnel_deregister ( struct xfrm6_tunnel * handler , unsigned short family )
2006-03-28 13:12:13 +04:00
{
struct xfrm6_tunnel * * pprev ;
int ret = - ENOENT ;
mutex_lock ( & tunnel6_mutex ) ;
2007-02-13 23:55:55 +03:00
for ( pprev = ( family = = AF_INET6 ) ? & tunnel6_handlers : & tunnel46_handlers ;
* pprev ; pprev = & ( * pprev ) - > next ) {
2006-03-28 13:12:13 +04:00
if ( * pprev = = handler ) {
* pprev = handler - > next ;
ret = 0 ;
break ;
}
}
mutex_unlock ( & tunnel6_mutex ) ;
synchronize_net ( ) ;
return ret ;
}
EXPORT_SYMBOL ( xfrm6_tunnel_deregister ) ;
2007-10-15 23:50:28 +04:00
static int tunnel6_rcv ( struct sk_buff * skb )
2006-03-28 13:12:13 +04:00
{
struct xfrm6_tunnel * handler ;
2006-04-05 00:50:45 +04:00
if ( ! pskb_may_pull ( skb , sizeof ( struct ipv6hdr ) ) )
goto drop ;
2006-03-28 13:12:13 +04:00
for ( handler = tunnel6_handlers ; handler ; handler = handler - > next )
if ( ! handler - > handler ( skb ) )
return 0 ;
2006-04-05 00:50:45 +04:00
icmpv6_send ( skb , ICMPV6_DEST_UNREACH , ICMPV6_PORT_UNREACH , 0 , skb - > dev ) ;
drop :
2006-03-28 13:12:13 +04:00
kfree_skb ( skb ) ;
return 0 ;
}
2007-10-15 23:50:28 +04:00
static int tunnel46_rcv ( struct sk_buff * skb )
2007-02-13 23:55:55 +03:00
{
struct xfrm6_tunnel * handler ;
2008-05-26 20:04:43 +04:00
if ( ! pskb_may_pull ( skb , sizeof ( struct iphdr ) ) )
2007-02-13 23:55:55 +03:00
goto drop ;
for ( handler = tunnel46_handlers ; handler ; handler = handler - > next )
if ( ! handler - > handler ( skb ) )
return 0 ;
icmpv6_send ( skb , ICMPV6_DEST_UNREACH , ICMPV6_PORT_UNREACH , 0 , skb - > dev ) ;
drop :
kfree_skb ( skb ) ;
return 0 ;
}
2006-03-28 13:12:13 +04:00
static void tunnel6_err ( struct sk_buff * skb , struct inet6_skb_parm * opt ,
2009-06-23 15:31:07 +04:00
u8 type , u8 code , int offset , __be32 info )
2006-03-28 13:12:13 +04:00
{
struct xfrm6_tunnel * handler ;
for ( handler = tunnel6_handlers ; handler ; handler = handler - > next )
if ( ! handler - > err_handler ( skb , opt , type , code , offset , info ) )
break ;
}
2009-09-14 16:22:28 +04:00
static const struct inet6_protocol tunnel6_protocol = {
2006-03-28 13:12:13 +04:00
. handler = tunnel6_rcv ,
. err_handler = tunnel6_err ,
. flags = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL ,
} ;
2009-09-14 16:22:28 +04:00
static const struct inet6_protocol tunnel46_protocol = {
2007-02-13 23:55:55 +03:00
. handler = tunnel46_rcv ,
. err_handler = tunnel6_err ,
. flags = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL ,
} ;
2006-03-28 13:12:13 +04:00
static int __init tunnel6_init ( void )
{
if ( inet6_add_protocol ( & tunnel6_protocol , IPPROTO_IPV6 ) ) {
printk ( KERN_ERR " tunnel6 init(): can't add protocol \n " ) ;
return - EAGAIN ;
}
2007-02-13 23:55:55 +03:00
if ( inet6_add_protocol ( & tunnel46_protocol , IPPROTO_IPIP ) ) {
printk ( KERN_ERR " tunnel6 init(): can't add protocol \n " ) ;
inet6_del_protocol ( & tunnel6_protocol , IPPROTO_IPV6 ) ;
return - EAGAIN ;
}
2006-03-28 13:12:13 +04:00
return 0 ;
}
static void __exit tunnel6_fini ( void )
{
2007-02-13 23:55:55 +03:00
if ( inet6_del_protocol ( & tunnel46_protocol , IPPROTO_IPIP ) )
printk ( KERN_ERR " tunnel6 close: can't remove protocol \n " ) ;
2006-03-28 13:12:13 +04:00
if ( inet6_del_protocol ( & tunnel6_protocol , IPPROTO_IPV6 ) )
printk ( KERN_ERR " tunnel6 close: can't remove protocol \n " ) ;
}
module_init ( tunnel6_init ) ;
module_exit ( tunnel6_fini ) ;
MODULE_LICENSE ( " GPL " ) ;