2005-04-16 15:20:36 -07:00
/*
* IPv6 over IPv4 tunnel device - Simple Internet Transition ( SIT )
* Linux INET6 implementation
*
* Authors :
2007-02-09 23:24:49 +09:00
* Pedro Roque < roque @ di . fc . ul . pt >
2005-04-16 15:20:36 -07:00
* Alexey Kuznetsov < kuznet @ ms2 . inr . ac . 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 .
*
* Changes :
* Roger Venning < r . venning @ telstra . com > : 6 to4 support
* Nate Thompson < nate @ thebog . net > : 6 to4 support
2008-03-11 18:35:59 -04:00
* Fred Templin < fred . l . templin @ boeing . com > : isatap support
2009-05-19 12:56:52 +00:00
* Sascha Hlusiak < mail @ saschahlusiak . de > : stateless autoconf for isatap
2005-04-16 15:20:36 -07:00
*/
# include <linux/module.h>
2006-01-11 12:17:47 -08:00
# include <linux/capability.h>
2005-04-16 15:20:36 -07:00
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/sockios.h>
# include <linux/net.h>
# include <linux/in6.h>
# include <linux/netdevice.h>
# include <linux/if_arp.h>
# include <linux/icmp.h>
# include <asm/uaccess.h>
# include <linux/init.h>
# include <linux/netfilter_ipv4.h>
2006-01-05 16:35:42 -08:00
# include <linux/if_ether.h>
2005-04-16 15:20:36 -07:00
# include <net/sock.h>
# include <net/snmp.h>
# include <net/ipv6.h>
# include <net/protocol.h>
# include <net/transp_v6.h>
# include <net/ip6_fib.h>
# include <net/ip6_route.h>
# include <net/ndisc.h>
# include <net/addrconf.h>
# include <net/ip.h>
# include <net/udp.h>
# include <net/icmp.h>
# include <net/ipip.h>
# include <net/inet_ecn.h>
# include <net/xfrm.h>
# include <net/dsfield.h>
2008-04-16 01:15:17 -07:00
# include <net/net_namespace.h>
# include <net/netns/generic.h>
2005-04-16 15:20:36 -07:00
/*
This version of net / ipv6 / sit . c is cloned of net / ipv4 / ip_gre . c
For comments look at net / ipv4 / ip_gre . c - - ANK
*/
# define HASH_SIZE 16
2006-11-14 20:56:00 -08:00
# define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
2005-04-16 15:20:36 -07:00
2008-11-20 20:33:56 -08:00
static void ipip6_fb_tunnel_init ( struct net_device * dev ) ;
static void ipip6_tunnel_init ( struct net_device * dev ) ;
2005-04-16 15:20:36 -07:00
static void ipip6_tunnel_setup ( struct net_device * dev ) ;
2008-04-16 01:15:17 -07:00
static int sit_net_id ;
struct sit_net {
2008-04-16 01:16:38 -07:00
struct ip_tunnel * tunnels_r_l [ HASH_SIZE ] ;
struct ip_tunnel * tunnels_r [ HASH_SIZE ] ;
struct ip_tunnel * tunnels_l [ HASH_SIZE ] ;
struct ip_tunnel * tunnels_wc [ 1 ] ;
struct ip_tunnel * * tunnels [ 4 ] ;
2008-04-16 01:16:18 -07:00
struct net_device * fb_tunnel_dev ;
2008-04-16 01:15:17 -07:00
} ;
2005-04-16 15:20:36 -07:00
static DEFINE_RWLOCK ( ipip6_lock ) ;
2008-04-16 01:15:39 -07:00
static struct ip_tunnel * ipip6_tunnel_lookup ( struct net * net ,
2009-05-19 12:56:49 +00:00
struct net_device * dev , __be32 remote , __be32 local )
2005-04-16 15:20:36 -07:00
{
unsigned h0 = HASH ( remote ) ;
unsigned h1 = HASH ( local ) ;
struct ip_tunnel * t ;
2008-04-16 01:16:38 -07:00
struct sit_net * sitn = net_generic ( net , sit_net_id ) ;
2005-04-16 15:20:36 -07:00
2008-04-16 01:16:38 -07:00
for ( t = sitn - > tunnels_r_l [ h0 ^ h1 ] ; t ; t = t - > next ) {
2005-04-16 15:20:36 -07:00
if ( local = = t - > parms . iph . saddr & &
2009-05-19 12:56:49 +00:00
remote = = t - > parms . iph . daddr & &
( ! dev | | ! t - > parms . link | | dev - > iflink = = t - > parms . link ) & &
( t - > dev - > flags & IFF_UP ) )
2005-04-16 15:20:36 -07:00
return t ;
}
2008-04-16 01:16:38 -07:00
for ( t = sitn - > tunnels_r [ h0 ] ; t ; t = t - > next ) {
2009-05-19 12:56:49 +00:00
if ( remote = = t - > parms . iph . daddr & &
( ! dev | | ! t - > parms . link | | dev - > iflink = = t - > parms . link ) & &
( t - > dev - > flags & IFF_UP ) )
2005-04-16 15:20:36 -07:00
return t ;
}
2008-04-16 01:16:38 -07:00
for ( t = sitn - > tunnels_l [ h1 ] ; t ; t = t - > next ) {
2009-05-19 12:56:49 +00:00
if ( local = = t - > parms . iph . saddr & &
( ! dev | | ! t - > parms . link | | dev - > iflink = = t - > parms . link ) & &
( t - > dev - > flags & IFF_UP ) )
2005-04-16 15:20:36 -07:00
return t ;
}
2009-05-19 12:56:49 +00:00
t = sitn - > tunnels_wc [ 0 ] ;
if ( ( t ! = NULL ) & & ( t - > dev - > flags & IFF_UP ) )
2005-04-16 15:20:36 -07:00
return t ;
return NULL ;
}
2008-04-16 01:15:39 -07:00
static struct ip_tunnel * * __ipip6_bucket ( struct sit_net * sitn ,
struct ip_tunnel_parm * parms )
2005-04-16 15:20:36 -07:00
{
2007-04-24 20:44:47 +09:00
__be32 remote = parms - > iph . daddr ;
__be32 local = parms - > iph . saddr ;
2005-04-16 15:20:36 -07:00
unsigned h = 0 ;
int prio = 0 ;
if ( remote ) {
prio | = 2 ;
h ^ = HASH ( remote ) ;
}
if ( local ) {
prio | = 1 ;
h ^ = HASH ( local ) ;
}
2008-04-16 01:16:38 -07:00
return & sitn - > tunnels [ prio ] [ h ] ;
2005-04-16 15:20:36 -07:00
}
2008-04-16 01:15:39 -07:00
static inline struct ip_tunnel * * ipip6_bucket ( struct sit_net * sitn ,
struct ip_tunnel * t )
2007-04-24 20:44:47 +09:00
{
2008-04-16 01:15:39 -07:00
return __ipip6_bucket ( sitn , & t - > parms ) ;
2007-04-24 20:44:47 +09:00
}
2008-04-16 01:15:39 -07:00
static void ipip6_tunnel_unlink ( struct sit_net * sitn , struct ip_tunnel * t )
2005-04-16 15:20:36 -07:00
{
struct ip_tunnel * * tp ;
2008-04-16 01:15:39 -07:00
for ( tp = ipip6_bucket ( sitn , t ) ; * tp ; tp = & ( * tp ) - > next ) {
2005-04-16 15:20:36 -07:00
if ( t = = * tp ) {
write_lock_bh ( & ipip6_lock ) ;
* tp = t - > next ;
write_unlock_bh ( & ipip6_lock ) ;
break ;
}
}
}
2008-04-16 01:15:39 -07:00
static void ipip6_tunnel_link ( struct sit_net * sitn , struct ip_tunnel * t )
2005-04-16 15:20:36 -07:00
{
2008-04-16 01:15:39 -07:00
struct ip_tunnel * * tp = ipip6_bucket ( sitn , t ) ;
2005-04-16 15:20:36 -07:00
t - > next = * tp ;
write_lock_bh ( & ipip6_lock ) ;
* tp = t ;
write_unlock_bh ( & ipip6_lock ) ;
}
2008-04-16 01:15:39 -07:00
static struct ip_tunnel * ipip6_tunnel_locate ( struct net * net ,
struct ip_tunnel_parm * parms , int create )
2005-04-16 15:20:36 -07:00
{
2006-11-14 20:56:00 -08:00
__be32 remote = parms - > iph . daddr ;
__be32 local = parms - > iph . saddr ;
2005-04-16 15:20:36 -07:00
struct ip_tunnel * t , * * tp , * nt ;
struct net_device * dev ;
char name [ IFNAMSIZ ] ;
2008-04-16 01:15:39 -07:00
struct sit_net * sitn = net_generic ( net , sit_net_id ) ;
2005-04-16 15:20:36 -07:00
2008-04-16 01:15:39 -07:00
for ( tp = __ipip6_bucket ( sitn , parms ) ; ( t = * tp ) ! = NULL ; tp = & t - > next ) {
2009-05-19 12:56:48 +00:00
if ( local = = t - > parms . iph . saddr & &
2009-05-19 12:56:49 +00:00
remote = = t - > parms . iph . daddr & &
parms - > link = = t - > parms . link ) {
2009-05-19 12:56:48 +00:00
if ( create )
return NULL ;
else
return t ;
}
2005-04-16 15:20:36 -07:00
}
if ( ! create )
goto failed ;
if ( parms - > name [ 0 ] )
strlcpy ( name , parms - > name , IFNAMSIZ ) ;
2008-02-23 20:19:20 -08:00
else
sprintf ( name , " sit%%d " ) ;
2005-04-16 15:20:36 -07:00
dev = alloc_netdev ( sizeof ( * t ) , name , ipip6_tunnel_setup ) ;
if ( dev = = NULL )
return NULL ;
2008-04-16 01:17:18 -07:00
dev_net_set ( dev , net ) ;
[INET]: Don't create tunnels with '%' in name.
Four tunnel drivers (ip_gre, ipip, ip6_tunnel and sit) can receive a
pre-defined name for a device from the userspace. Since these drivers
call the register_netdevice() (rtnl_lock, is held), which does _not_
generate the device's name, this name may contain a '%' character.
Not sure how bad is this to have a device with a '%' in its name, but
all the other places either use the register_netdev(), which call the
dev_alloc_name(), or explicitly call the dev_alloc_name() before
registering, i.e. do not allow for such names.
This had to be prior to the commit 34cc7b, but I forgot to number the
patches and this one got lost, sorry.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-02-26 23:51:04 -08:00
if ( strchr ( name , ' % ' ) ) {
if ( dev_alloc_name ( dev , name ) < 0 )
goto failed_free ;
}
2006-01-08 22:05:26 -08:00
nt = netdev_priv ( dev ) ;
2008-11-20 20:33:56 -08:00
2005-04-16 15:20:36 -07:00
nt - > parms = * parms ;
2009-03-18 18:56:54 -07:00
ipip6_tunnel_init ( dev ) ;
2005-04-16 15:20:36 -07:00
2007-11-29 22:11:40 +11:00
if ( parms - > i_flags & SIT_ISATAP )
dev - > priv_flags | = IFF_ISATAP ;
[INET]: Don't create tunnels with '%' in name.
Four tunnel drivers (ip_gre, ipip, ip6_tunnel and sit) can receive a
pre-defined name for a device from the userspace. Since these drivers
call the register_netdevice() (rtnl_lock, is held), which does _not_
generate the device's name, this name may contain a '%' character.
Not sure how bad is this to have a device with a '%' in its name, but
all the other places either use the register_netdev(), which call the
dev_alloc_name(), or explicitly call the dev_alloc_name() before
registering, i.e. do not allow for such names.
This had to be prior to the commit 34cc7b, but I forgot to number the
patches and this one got lost, sorry.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-02-26 23:51:04 -08:00
if ( register_netdevice ( dev ) < 0 )
goto failed_free ;
2005-04-16 15:20:36 -07:00
dev_hold ( dev ) ;
2008-04-16 01:15:39 -07:00
ipip6_tunnel_link ( sitn , nt ) ;
2005-04-16 15:20:36 -07:00
return nt ;
[INET]: Don't create tunnels with '%' in name.
Four tunnel drivers (ip_gre, ipip, ip6_tunnel and sit) can receive a
pre-defined name for a device from the userspace. Since these drivers
call the register_netdevice() (rtnl_lock, is held), which does _not_
generate the device's name, this name may contain a '%' character.
Not sure how bad is this to have a device with a '%' in its name, but
all the other places either use the register_netdev(), which call the
dev_alloc_name(), or explicitly call the dev_alloc_name() before
registering, i.e. do not allow for such names.
This had to be prior to the commit 34cc7b, but I forgot to number the
patches and this one got lost, sorry.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-02-26 23:51:04 -08:00
failed_free :
free_netdev ( dev ) ;
2005-04-16 15:20:36 -07:00
failed :
return NULL ;
}
2009-05-19 12:56:52 +00:00
static void ipip6_tunnel_rs_timer ( unsigned long data )
{
struct ip_tunnel_prl_entry * p = ( struct ip_tunnel_prl_entry * ) data ;
struct inet6_dev * ifp ;
struct inet6_ifaddr * addr ;
spin_lock ( & p - > lock ) ;
ifp = __in6_dev_get ( p - > tunnel - > dev ) ;
read_lock_bh ( & ifp - > lock ) ;
for ( addr = ifp - > addr_list ; addr ; addr = addr - > if_next ) {
struct in6_addr rtr ;
if ( ! ( ipv6_addr_type ( & addr - > addr ) & IPV6_ADDR_LINKLOCAL ) )
continue ;
/* Send RS to guessed linklocal address of router
*
* Better : send to ff02 : : 2 encapsuled in unicast directly
* to router - v4 instead of guessing the v6 address .
*
* Cisco / Windows seem to not set the u / l bit correctly ,
* so we won ' t guess right .
*/
ipv6_addr_set ( & rtr , htonl ( 0xFE800000 ) , 0 , 0 , 0 ) ;
if ( ! __ipv6_isatap_ifid ( rtr . s6_addr + 8 ,
p - > addr ) ) {
ndisc_send_rs ( p - > tunnel - > dev , & addr - > addr , & rtr ) ;
}
}
read_unlock_bh ( & ifp - > lock ) ;
mod_timer ( & p - > rs_timer , jiffies + HZ * p - > rs_delay ) ;
spin_unlock ( & p - > lock ) ;
return ;
}
2008-03-11 18:35:59 -04:00
static struct ip_tunnel_prl_entry *
2008-03-22 17:42:57 +09:00
__ipip6_tunnel_locate_prl ( struct ip_tunnel * t , __be32 addr )
2008-03-11 18:35:59 -04:00
{
struct ip_tunnel_prl_entry * p = ( struct ip_tunnel_prl_entry * ) NULL ;
for ( p = t - > prl ; p ; p = p - > next )
2008-03-24 18:28:39 +09:00
if ( p - > addr = = addr )
2008-03-11 18:35:59 -04:00
break ;
return p ;
}
2008-06-16 16:48:20 -07:00
static int ipip6_tunnel_get_prl ( struct ip_tunnel * t ,
struct ip_tunnel_prl __user * a )
2008-03-24 18:28:39 +09:00
{
2008-06-16 16:48:20 -07:00
struct ip_tunnel_prl kprl , * kp ;
2008-03-24 18:28:39 +09:00
struct ip_tunnel_prl_entry * prl ;
unsigned int cmax , c = 0 , ca , len ;
int ret = 0 ;
2008-06-16 16:48:20 -07:00
if ( copy_from_user ( & kprl , a , sizeof ( kprl ) ) )
return - EFAULT ;
cmax = kprl . datalen / sizeof ( kprl ) ;
if ( cmax > 1 & & kprl . addr ! = htonl ( INADDR_ANY ) )
2008-03-24 18:28:39 +09:00
cmax = 1 ;
/* For simple GET or for root users,
* we try harder to allocate .
*/
kp = ( cmax < = 1 | | capable ( CAP_NET_ADMIN ) ) ?
kcalloc ( cmax , sizeof ( * kp ) , GFP_KERNEL ) :
NULL ;
read_lock ( & ipip6_lock ) ;
ca = t - > prl_count < cmax ? t - > prl_count : cmax ;
if ( ! kp ) {
/* We don't try hard to allocate much memory for
* non - root users .
* For root users , retry allocating enough memory for
* the answer .
*/
kp = kcalloc ( ca , sizeof ( * kp ) , GFP_ATOMIC ) ;
if ( ! kp ) {
ret = - ENOMEM ;
goto out ;
}
}
c = 0 ;
for ( prl = t - > prl ; prl ; prl = prl - > next ) {
if ( c > cmax )
break ;
2008-06-16 16:48:20 -07:00
if ( kprl . addr ! = htonl ( INADDR_ANY ) & & prl - > addr ! = kprl . addr )
2008-03-24 18:28:39 +09:00
continue ;
kp [ c ] . addr = prl - > addr ;
kp [ c ] . flags = prl - > flags ;
2009-05-19 12:56:52 +00:00
kp [ c ] . rs_delay = prl - > rs_delay ;
2008-03-24 18:28:39 +09:00
c + + ;
2008-06-16 16:48:20 -07:00
if ( kprl . addr ! = htonl ( INADDR_ANY ) )
2008-03-24 18:28:39 +09:00
break ;
}
out :
read_unlock ( & ipip6_lock ) ;
len = sizeof ( * kp ) * c ;
2008-06-16 16:48:20 -07:00
ret = 0 ;
if ( ( len & & copy_to_user ( a + 1 , kp , len ) ) | | put_user ( len , & a - > datalen ) )
ret = - EFAULT ;
2008-03-24 18:28:39 +09:00
kfree ( kp ) ;
2008-06-16 16:48:20 -07:00
return ret ;
2008-03-24 18:28:39 +09:00
}
2008-03-11 18:35:59 -04:00
static int
ipip6_tunnel_add_prl ( struct ip_tunnel * t , struct ip_tunnel_prl * a , int chg )
{
struct ip_tunnel_prl_entry * p ;
2008-03-22 17:42:57 +09:00
int err = 0 ;
2008-03-22 17:50:59 +09:00
if ( a - > addr = = htonl ( INADDR_ANY ) )
return - EINVAL ;
2008-03-22 17:42:57 +09:00
write_lock ( & ipip6_lock ) ;
2008-03-11 18:35:59 -04:00
for ( p = t - > prl ; p ; p = p - > next ) {
2008-03-24 18:28:39 +09:00
if ( p - > addr = = a - > addr ) {
2008-03-22 17:42:57 +09:00
if ( chg )
goto update ;
err = - EEXIST ;
goto out ;
2008-03-11 18:35:59 -04:00
}
}
2008-03-22 17:42:57 +09:00
if ( chg ) {
err = - ENXIO ;
goto out ;
}
2008-03-11 18:35:59 -04:00
p = kzalloc ( sizeof ( struct ip_tunnel_prl_entry ) , GFP_KERNEL ) ;
2008-03-22 17:42:57 +09:00
if ( ! p ) {
err = - ENOBUFS ;
goto out ;
}
2008-03-11 18:35:59 -04:00
p - > next = t - > prl ;
2009-05-19 12:56:52 +00:00
p - > tunnel = t ;
2008-03-11 18:35:59 -04:00
t - > prl = p ;
2008-03-24 18:28:39 +09:00
t - > prl_count + + ;
2009-05-19 12:56:52 +00:00
spin_lock_init ( & p - > lock ) ;
setup_timer ( & p - > rs_timer , ipip6_tunnel_rs_timer , ( unsigned long ) p ) ;
2008-03-22 17:42:57 +09:00
update :
2008-03-24 18:28:39 +09:00
p - > addr = a - > addr ;
p - > flags = a - > flags ;
2009-05-19 12:56:52 +00:00
p - > rs_delay = a - > rs_delay ;
if ( p - > rs_delay = = 0 )
p - > rs_delay = IPTUNNEL_RS_DEFAULT_DELAY ;
spin_lock ( & p - > lock ) ;
del_timer ( & p - > rs_timer ) ;
if ( p - > flags & PRL_DEFAULT )
mod_timer ( & p - > rs_timer , jiffies + 1 ) ;
spin_unlock ( & p - > lock ) ;
2008-03-22 17:42:57 +09:00
out :
write_unlock ( & ipip6_lock ) ;
return err ;
2008-03-11 18:35:59 -04:00
}
static int
ipip6_tunnel_del_prl ( struct ip_tunnel * t , struct ip_tunnel_prl * a )
{
struct ip_tunnel_prl_entry * x , * * p ;
2008-03-22 17:42:57 +09:00
int err = 0 ;
write_lock ( & ipip6_lock ) ;
2008-03-11 18:35:59 -04:00
2008-03-22 17:50:59 +09:00
if ( a & & a - > addr ! = htonl ( INADDR_ANY ) ) {
2008-03-11 18:35:59 -04:00
for ( p = & t - > prl ; * p ; p = & ( * p ) - > next ) {
2008-03-24 18:28:39 +09:00
if ( ( * p ) - > addr = = a - > addr ) {
2008-03-11 18:35:59 -04:00
x = * p ;
* p = x - > next ;
2009-05-19 12:56:52 +00:00
spin_lock ( & x - > lock ) ;
del_timer ( & x - > rs_timer ) ;
spin_unlock ( & x - > lock ) ;
2008-03-11 18:35:59 -04:00
kfree ( x ) ;
2008-03-24 18:28:39 +09:00
t - > prl_count - - ;
2008-03-22 17:42:57 +09:00
goto out ;
2008-03-11 18:35:59 -04:00
}
}
2008-03-22 17:42:57 +09:00
err = - ENXIO ;
2008-03-11 18:35:59 -04:00
} else {
while ( t - > prl ) {
x = t - > prl ;
t - > prl = t - > prl - > next ;
2009-05-19 12:56:52 +00:00
spin_lock ( & x - > lock ) ;
del_timer ( & x - > rs_timer ) ;
spin_unlock ( & x - > lock ) ;
2008-03-11 18:35:59 -04:00
kfree ( x ) ;
2008-03-24 18:28:39 +09:00
t - > prl_count - - ;
2008-03-11 18:35:59 -04:00
}
}
2008-03-22 17:42:57 +09:00
out :
write_unlock ( & ipip6_lock ) ;
2009-05-19 12:56:50 +00:00
return err ;
2008-03-11 18:35:59 -04:00
}
static int
isatap_chksrc ( struct sk_buff * skb , struct iphdr * iph , struct ip_tunnel * t )
{
2008-03-22 17:42:57 +09:00
struct ip_tunnel_prl_entry * p ;
2008-03-11 18:35:59 -04:00
int ok = 1 ;
2008-03-22 17:42:57 +09:00
read_lock ( & ipip6_lock ) ;
p = __ipip6_tunnel_locate_prl ( t , iph - > saddr ) ;
2008-03-11 18:35:59 -04:00
if ( p ) {
2008-03-24 18:28:39 +09:00
if ( p - > flags & PRL_DEFAULT )
2008-03-11 18:35:59 -04:00
skb - > ndisc_nodetype = NDISC_NODETYPE_DEFAULT ;
else
skb - > ndisc_nodetype = NDISC_NODETYPE_NODEFAULT ;
} else {
struct in6_addr * addr6 = & ipv6_hdr ( skb ) - > saddr ;
if ( ipv6_addr_is_isatap ( addr6 ) & &
( addr6 - > s6_addr32 [ 3 ] = = iph - > saddr ) & &
2008-03-15 22:54:23 -04:00
ipv6_chk_prefix ( addr6 , t - > dev ) )
2008-03-11 18:35:59 -04:00
skb - > ndisc_nodetype = NDISC_NODETYPE_HOST ;
else
ok = 0 ;
}
2008-03-22 17:42:57 +09:00
read_unlock ( & ipip6_lock ) ;
2008-03-11 18:35:59 -04:00
return ok ;
}
2005-04-16 15:20:36 -07:00
static void ipip6_tunnel_uninit ( struct net_device * dev )
{
2008-04-16 01:15:39 -07:00
struct net * net = dev_net ( dev ) ;
struct sit_net * sitn = net_generic ( net , sit_net_id ) ;
2008-04-16 01:16:18 -07:00
if ( dev = = sitn - > fb_tunnel_dev ) {
2005-04-16 15:20:36 -07:00
write_lock_bh ( & ipip6_lock ) ;
2008-04-16 01:16:38 -07:00
sitn - > tunnels_wc [ 0 ] = NULL ;
2005-04-16 15:20:36 -07:00
write_unlock_bh ( & ipip6_lock ) ;
dev_put ( dev ) ;
} else {
2008-04-16 01:15:39 -07:00
ipip6_tunnel_unlink ( sitn , netdev_priv ( dev ) ) ;
2008-04-10 15:41:27 +09:00
ipip6_tunnel_del_prl ( netdev_priv ( dev ) , NULL ) ;
2005-04-16 15:20:36 -07:00
dev_put ( dev ) ;
}
}
2007-02-13 12:55:25 -08:00
static int ipip6_err ( struct sk_buff * skb , u32 info )
2005-04-16 15:20:36 -07:00
{
2008-05-21 17:47:54 -07:00
/* All the routers (except for Linux) return only
2005-04-16 15:20:36 -07:00
8 bytes of packet payload . It means , that precise relaying of
ICMP in the real Internet is absolutely infeasible .
*/
struct iphdr * iph = ( struct iphdr * ) skb - > data ;
2007-03-13 14:43:18 -03:00
const int type = icmp_hdr ( skb ) - > type ;
const int code = icmp_hdr ( skb ) - > code ;
2005-04-16 15:20:36 -07:00
struct ip_tunnel * t ;
2007-02-13 12:55:25 -08:00
int err ;
2005-04-16 15:20:36 -07:00
switch ( type ) {
default :
case ICMP_PARAMETERPROB :
2007-02-13 12:55:25 -08:00
return 0 ;
2005-04-16 15:20:36 -07:00
case ICMP_DEST_UNREACH :
switch ( code ) {
case ICMP_SR_FAILED :
case ICMP_PORT_UNREACH :
/* Impossible event. */
2007-02-13 12:55:25 -08:00
return 0 ;
2005-04-16 15:20:36 -07:00
case ICMP_FRAG_NEEDED :
/* Soft state for pmtu is maintained by IP core. */
2007-02-13 12:55:25 -08:00
return 0 ;
2005-04-16 15:20:36 -07:00
default :
/* All others are translated to HOST_UNREACH.
rfc2003 contains " deep thoughts " about NET_UNREACH ,
I believe they are just ether pollution . - - ANK
*/
break ;
}
break ;
case ICMP_TIME_EXCEEDED :
if ( code ! = ICMP_EXC_TTL )
2007-02-13 12:55:25 -08:00
return 0 ;
2005-04-16 15:20:36 -07:00
break ;
}
2007-02-13 12:55:25 -08:00
err = - ENOENT ;
2005-04-16 15:20:36 -07:00
read_lock ( & ipip6_lock ) ;
2009-05-19 12:56:49 +00:00
t = ipip6_tunnel_lookup ( dev_net ( skb - > dev ) ,
skb - > dev ,
iph - > daddr ,
iph - > saddr ) ;
2005-04-16 15:20:36 -07:00
if ( t = = NULL | | t - > parms . iph . daddr = = 0 )
goto out ;
2007-02-13 12:55:25 -08:00
err = 0 ;
2005-04-16 15:20:36 -07:00
if ( t - > parms . iph . ttl = = 0 & & type = = ICMP_TIME_EXCEEDED )
goto out ;
2009-02-24 23:37:19 -08:00
if ( time_before ( jiffies , t - > err_time + IPTUNNEL_ERR_TIMEO ) )
2005-04-16 15:20:36 -07:00
t - > err_count + + ;
else
t - > err_count = 1 ;
t - > err_time = jiffies ;
out :
read_unlock ( & ipip6_lock ) ;
2007-02-13 12:55:25 -08:00
return err ;
2005-04-16 15:20:36 -07:00
}
static inline void ipip6_ecn_decapsulate ( struct iphdr * iph , struct sk_buff * skb )
{
if ( INET_ECN_is_ce ( iph - > tos ) )
2007-04-25 17:54:47 -07:00
IP6_ECN_set_ce ( ipv6_hdr ( skb ) ) ;
2005-04-16 15:20:36 -07:00
}
static int ipip6_rcv ( struct sk_buff * skb )
{
struct iphdr * iph ;
struct ip_tunnel * tunnel ;
if ( ! pskb_may_pull ( skb , sizeof ( struct ipv6hdr ) ) )
goto out ;
2007-04-20 22:47:35 -07:00
iph = ip_hdr ( skb ) ;
2005-04-16 15:20:36 -07:00
read_lock ( & ipip6_lock ) ;
2009-05-19 12:56:49 +00:00
tunnel = ipip6_tunnel_lookup ( dev_net ( skb - > dev ) , skb - > dev ,
iph - > saddr , iph - > daddr ) ;
if ( tunnel ! = NULL ) {
2005-04-16 15:20:36 -07:00
secpath_reset ( skb ) ;
2007-04-10 21:21:55 -07:00
skb - > mac_header = skb - > network_header ;
2007-04-10 20:45:18 -07:00
skb_reset_network_header ( skb ) ;
2006-01-06 23:04:01 -08:00
IPCB ( skb ) - > flags = 0 ;
2005-04-16 15:20:36 -07:00
skb - > protocol = htons ( ETH_P_IPV6 ) ;
skb - > pkt_type = PACKET_HOST ;
2007-11-29 22:11:40 +11:00
if ( ( tunnel - > dev - > priv_flags & IFF_ISATAP ) & &
2008-03-11 18:35:59 -04:00
! isatap_chksrc ( skb , iph , tunnel ) ) {
2008-05-21 14:15:46 -07:00
tunnel - > dev - > stats . rx_errors + + ;
2007-11-29 22:11:40 +11:00
read_unlock ( & ipip6_lock ) ;
kfree_skb ( skb ) ;
return 0 ;
}
2008-05-21 14:15:46 -07:00
tunnel - > dev - > stats . rx_packets + + ;
tunnel - > dev - > stats . rx_bytes + = skb - > len ;
2005-04-16 15:20:36 -07:00
skb - > dev = tunnel - > dev ;
2009-06-02 05:19:30 +00:00
skb_dst_drop ( skb ) ;
2005-04-16 15:20:36 -07:00
nf_reset ( skb ) ;
ipip6_ecn_decapsulate ( iph , skb ) ;
netif_rx ( skb ) ;
read_unlock ( & ipip6_lock ) ;
return 0 ;
}
2006-04-05 22:31:19 -07:00
icmp_send ( skb , ICMP_DEST_UNREACH , ICMP_PORT_UNREACH , 0 ) ;
2005-04-16 15:20:36 -07:00
read_unlock ( & ipip6_lock ) ;
out :
2008-05-08 23:40:26 -07:00
kfree_skb ( skb ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/* Returns the embedded IPv4 address if the IPv6 address
comes from 6 to4 ( RFC 3056 ) addr space */
2006-11-14 20:56:00 -08:00
static inline __be32 try_6to4 ( struct in6_addr * v6dst )
2005-04-16 15:20:36 -07:00
{
2006-11-14 20:56:00 -08:00
__be32 dst = 0 ;
2005-04-16 15:20:36 -07:00
if ( v6dst - > s6_addr16 [ 0 ] = = htons ( 0x2002 ) ) {
2007-02-09 23:24:49 +09:00
/* 6to4 v6 addr has 16 bits prefix, 32 v4addr, 16 SLA, ... */
2005-04-16 15:20:36 -07:00
memcpy ( & dst , & v6dst - > s6_addr16 [ 1 ] , 4 ) ;
}
return dst ;
}
/*
* This function assumes it is being called from dev_queue_xmit ( )
* and that skb is filled properly by that function .
*/
static int ipip6_tunnel_xmit ( struct sk_buff * skb , struct net_device * dev )
{
2006-01-08 22:05:26 -08:00
struct ip_tunnel * tunnel = netdev_priv ( dev ) ;
2008-05-21 14:15:46 -07:00
struct net_device_stats * stats = & tunnel - > dev - > stats ;
2005-04-16 15:20:36 -07:00
struct iphdr * tiph = & tunnel - > parms . iph ;
2007-04-25 17:54:47 -07:00
struct ipv6hdr * iph6 = ipv6_hdr ( skb ) ;
2005-04-16 15:20:36 -07:00
u8 tos = tunnel - > parms . iph . tos ;
struct rtable * rt ; /* Route to the other host */
struct net_device * tdev ; /* Device to other host */
struct iphdr * iph ; /* Our new IP header */
2007-10-23 21:07:32 -07:00
unsigned int max_headroom ; /* The extra header space needed */
2006-11-14 20:56:00 -08:00
__be32 dst = tiph - > daddr ;
2005-04-16 15:20:36 -07:00
int mtu ;
2007-02-09 23:24:49 +09:00
struct in6_addr * addr6 ;
2005-04-16 15:20:36 -07:00
int addr_type ;
if ( tunnel - > recursion + + ) {
2008-05-21 14:15:46 -07:00
stats - > collisions + + ;
2005-04-16 15:20:36 -07:00
goto tx_error ;
}
if ( skb - > protocol ! = htons ( ETH_P_IPV6 ) )
goto tx_error ;
2007-11-29 22:11:40 +11:00
/* ISATAP (RFC4214) - must come before 6to4 */
if ( dev - > priv_flags & IFF_ISATAP ) {
struct neighbour * neigh = NULL ;
2009-06-02 05:19:30 +00:00
if ( skb_dst ( skb ) )
neigh = skb_dst ( skb ) - > neighbour ;
2007-11-29 22:11:40 +11:00
if ( neigh = = NULL ) {
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " sit: nexthop == NULL \n " ) ;
goto tx_error ;
}
addr6 = ( struct in6_addr * ) & neigh - > primary_key ;
addr_type = ipv6_addr_type ( addr6 ) ;
if ( ( addr_type & IPV6_ADDR_UNICAST ) & &
ipv6_addr_is_isatap ( addr6 ) )
dst = addr6 - > s6_addr32 [ 3 ] ;
else
goto tx_error ;
}
2005-04-16 15:20:36 -07:00
if ( ! dst )
dst = try_6to4 ( & iph6 - > daddr ) ;
if ( ! dst ) {
struct neighbour * neigh = NULL ;
2009-06-02 05:19:30 +00:00
if ( skb_dst ( skb ) )
neigh = skb_dst ( skb ) - > neighbour ;
2005-04-16 15:20:36 -07:00
if ( neigh = = NULL ) {
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " sit: nexthop == NULL \n " ) ;
goto tx_error ;
}
addr6 = ( struct in6_addr * ) & neigh - > primary_key ;
addr_type = ipv6_addr_type ( addr6 ) ;
if ( addr_type = = IPV6_ADDR_ANY ) {
2007-04-25 17:54:47 -07:00
addr6 = & ipv6_hdr ( skb ) - > daddr ;
2005-04-16 15:20:36 -07:00
addr_type = ipv6_addr_type ( addr6 ) ;
}
if ( ( addr_type & IPV6_ADDR_COMPATv4 ) = = 0 )
goto tx_error_icmp ;
dst = addr6 - > s6_addr32 [ 3 ] ;
}
{
struct flowi fl = { . nl_u = { . ip4_u =
{ . daddr = dst ,
. saddr = tiph - > saddr ,
. tos = RT_TOS ( tos ) } } ,
. oif = tunnel - > parms . link ,
. proto = IPPROTO_IPV6 } ;
2008-04-16 01:16:58 -07:00
if ( ip_route_output_key ( dev_net ( dev ) , & rt , & fl ) ) {
2008-05-21 14:15:46 -07:00
stats - > tx_carrier_errors + + ;
2005-04-16 15:20:36 -07:00
goto tx_error_icmp ;
}
}
if ( rt - > rt_type ! = RTN_UNICAST ) {
ip_rt_put ( rt ) ;
2008-05-21 14:15:46 -07:00
stats - > tx_carrier_errors + + ;
2005-04-16 15:20:36 -07:00
goto tx_error_icmp ;
}
tdev = rt - > u . dst . dev ;
if ( tdev = = dev ) {
ip_rt_put ( rt ) ;
2008-05-21 14:15:46 -07:00
stats - > collisions + + ;
2005-04-16 15:20:36 -07:00
goto tx_error ;
}
if ( tiph - > frag_off )
mtu = dst_mtu ( & rt - > u . dst ) - sizeof ( struct iphdr ) ;
else
2009-06-02 05:19:30 +00:00
mtu = skb_dst ( skb ) ? dst_mtu ( skb_dst ( skb ) ) : dev - > mtu ;
2005-04-16 15:20:36 -07:00
if ( mtu < 68 ) {
2008-05-21 14:15:46 -07:00
stats - > collisions + + ;
2005-04-16 15:20:36 -07:00
ip_rt_put ( rt ) ;
goto tx_error ;
}
if ( mtu < IPV6_MIN_MTU )
mtu = IPV6_MIN_MTU ;
2009-06-02 05:19:30 +00:00
if ( tunnel - > parms . iph . daddr & & skb_dst ( skb ) )
skb_dst ( skb ) - > ops - > update_pmtu ( skb_dst ( skb ) , mtu ) ;
2005-04-16 15:20:36 -07:00
if ( skb - > len > mtu ) {
icmpv6_send ( skb , ICMPV6_PKT_TOOBIG , 0 , mtu , dev ) ;
ip_rt_put ( rt ) ;
goto tx_error ;
}
if ( tunnel - > err_count > 0 ) {
2009-02-24 23:37:19 -08:00
if ( time_before ( jiffies ,
tunnel - > err_time + IPTUNNEL_ERR_TIMEO ) ) {
2005-04-16 15:20:36 -07:00
tunnel - > err_count - - ;
dst_link_failure ( skb ) ;
} else
tunnel - > err_count = 0 ;
}
/*
* Okay , now see if we can stuff it in the buffer as - is .
*/
max_headroom = LL_RESERVED_SPACE ( tdev ) + sizeof ( struct iphdr ) ;
2007-07-09 15:33:40 -07:00
if ( skb_headroom ( skb ) < max_headroom | | skb_shared ( skb ) | |
( skb_cloned ( skb ) & & ! skb_clone_writable ( skb , 0 ) ) ) {
2005-04-16 15:20:36 -07:00
struct sk_buff * new_skb = skb_realloc_headroom ( skb , max_headroom ) ;
if ( ! new_skb ) {
ip_rt_put ( rt ) ;
2007-02-09 23:24:49 +09:00
stats - > tx_dropped + + ;
2005-04-16 15:20:36 -07:00
dev_kfree_skb ( skb ) ;
tunnel - > recursion - - ;
return 0 ;
}
if ( skb - > sk )
skb_set_owner_w ( new_skb , skb - > sk ) ;
dev_kfree_skb ( skb ) ;
skb = new_skb ;
2007-04-25 17:54:47 -07:00
iph6 = ipv6_hdr ( skb ) ;
2005-04-16 15:20:36 -07:00
}
2007-04-10 21:21:55 -07:00
skb - > transport_header = skb - > network_header ;
2007-04-10 20:46:21 -07:00
skb_push ( skb , sizeof ( struct iphdr ) ) ;
skb_reset_network_header ( skb ) ;
2005-04-16 15:20:36 -07:00
memset ( & ( IPCB ( skb ) - > opt ) , 0 , sizeof ( IPCB ( skb ) - > opt ) ) ;
2006-01-06 23:04:01 -08:00
IPCB ( skb ) - > flags = 0 ;
2009-06-02 05:19:30 +00:00
skb_dst_drop ( skb ) ;
skb_dst_set ( skb , & rt - > u . dst ) ;
2005-04-16 15:20:36 -07:00
/*
* Push down and install the IPIP header .
*/
2007-04-20 22:47:35 -07:00
iph = ip_hdr ( skb ) ;
2005-04-16 15:20:36 -07:00
iph - > version = 4 ;
iph - > ihl = sizeof ( struct iphdr ) > > 2 ;
if ( mtu > IPV6_MIN_MTU )
iph - > frag_off = htons ( IP_DF ) ;
else
iph - > frag_off = 0 ;
iph - > protocol = IPPROTO_IPV6 ;
iph - > tos = INET_ECN_encapsulate ( tos , ipv6_get_dsfield ( iph6 ) ) ;
iph - > daddr = rt - > rt_dst ;
iph - > saddr = rt - > rt_src ;
if ( ( iph - > ttl = tiph - > ttl ) = = 0 )
iph - > ttl = iph6 - > hop_limit ;
nf_reset ( skb ) ;
IPTUNNEL_XMIT ( ) ;
tunnel - > recursion - - ;
return 0 ;
tx_error_icmp :
dst_link_failure ( skb ) ;
tx_error :
stats - > tx_errors + + ;
dev_kfree_skb ( skb ) ;
tunnel - > recursion - - ;
return 0 ;
}
2007-12-13 09:47:00 -08:00
static void ipip6_tunnel_bind_dev ( struct net_device * dev )
{
struct net_device * tdev = NULL ;
struct ip_tunnel * tunnel ;
struct iphdr * iph ;
tunnel = netdev_priv ( dev ) ;
iph = & tunnel - > parms . iph ;
if ( iph - > daddr ) {
struct flowi fl = { . nl_u = { . ip4_u =
{ . daddr = iph - > daddr ,
. saddr = iph - > saddr ,
. tos = RT_TOS ( iph - > tos ) } } ,
. oif = tunnel - > parms . link ,
. proto = IPPROTO_IPV6 } ;
struct rtable * rt ;
2008-04-16 01:16:58 -07:00
if ( ! ip_route_output_key ( dev_net ( dev ) , & rt , & fl ) ) {
2007-12-13 09:47:00 -08:00
tdev = rt - > u . dst . dev ;
ip_rt_put ( rt ) ;
}
dev - > flags | = IFF_POINTOPOINT ;
}
if ( ! tdev & & tunnel - > parms . link )
2008-04-16 01:16:58 -07:00
tdev = __dev_get_by_index ( dev_net ( dev ) , tunnel - > parms . link ) ;
2007-12-13 09:47:00 -08:00
if ( tdev ) {
dev - > hard_header_len = tdev - > hard_header_len + sizeof ( struct iphdr ) ;
dev - > mtu = tdev - > mtu - sizeof ( struct iphdr ) ;
if ( dev - > mtu < IPV6_MIN_MTU )
dev - > mtu = IPV6_MIN_MTU ;
}
dev - > iflink = tunnel - > parms . link ;
}
2005-04-16 15:20:36 -07:00
static int
ipip6_tunnel_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
{
int err = 0 ;
struct ip_tunnel_parm p ;
2008-03-11 18:35:59 -04:00
struct ip_tunnel_prl prl ;
2005-04-16 15:20:36 -07:00
struct ip_tunnel * t ;
2008-04-16 01:15:39 -07:00
struct net * net = dev_net ( dev ) ;
struct sit_net * sitn = net_generic ( net , sit_net_id ) ;
2005-04-16 15:20:36 -07:00
switch ( cmd ) {
case SIOCGETTUNNEL :
t = NULL ;
2008-04-16 01:16:18 -07:00
if ( dev = = sitn - > fb_tunnel_dev ) {
2005-04-16 15:20:36 -07:00
if ( copy_from_user ( & p , ifr - > ifr_ifru . ifru_data , sizeof ( p ) ) ) {
err = - EFAULT ;
break ;
}
2008-04-16 01:15:39 -07:00
t = ipip6_tunnel_locate ( net , & p , 0 ) ;
2005-04-16 15:20:36 -07:00
}
if ( t = = NULL )
2006-01-08 22:05:26 -08:00
t = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
memcpy ( & p , & t - > parms , sizeof ( p ) ) ;
if ( copy_to_user ( ifr - > ifr_ifru . ifru_data , & p , sizeof ( p ) ) )
err = - EFAULT ;
break ;
case SIOCADDTUNNEL :
case SIOCCHGTUNNEL :
err = - EPERM ;
if ( ! capable ( CAP_NET_ADMIN ) )
goto done ;
err = - EFAULT ;
if ( copy_from_user ( & p , ifr - > ifr_ifru . ifru_data , sizeof ( p ) ) )
goto done ;
err = - EINVAL ;
if ( p . iph . version ! = 4 | | p . iph . protocol ! = IPPROTO_IPV6 | |
p . iph . ihl ! = 5 | | ( p . iph . frag_off & htons ( ~ IP_DF ) ) )
goto done ;
if ( p . iph . ttl )
p . iph . frag_off | = htons ( IP_DF ) ;
2008-04-16 01:15:39 -07:00
t = ipip6_tunnel_locate ( net , & p , cmd = = SIOCADDTUNNEL ) ;
2005-04-16 15:20:36 -07:00
2008-04-16 01:16:18 -07:00
if ( dev ! = sitn - > fb_tunnel_dev & & cmd = = SIOCCHGTUNNEL ) {
2005-04-16 15:20:36 -07:00
if ( t ! = NULL ) {
if ( t - > dev ! = dev ) {
err = - EEXIST ;
break ;
}
} else {
if ( ( ( dev - > flags & IFF_POINTOPOINT ) & & ! p . iph . daddr ) | |
( ! ( dev - > flags & IFF_POINTOPOINT ) & & p . iph . daddr ) ) {
err = - EINVAL ;
break ;
}
2006-01-08 22:05:26 -08:00
t = netdev_priv ( dev ) ;
2008-04-16 01:15:39 -07:00
ipip6_tunnel_unlink ( sitn , t ) ;
2005-04-16 15:20:36 -07:00
t - > parms . iph . saddr = p . iph . saddr ;
t - > parms . iph . daddr = p . iph . daddr ;
memcpy ( dev - > dev_addr , & p . iph . saddr , 4 ) ;
memcpy ( dev - > broadcast , & p . iph . daddr , 4 ) ;
2008-04-16 01:15:39 -07:00
ipip6_tunnel_link ( sitn , t ) ;
2005-04-16 15:20:36 -07:00
netdev_state_change ( dev ) ;
}
}
if ( t ) {
err = 0 ;
if ( cmd = = SIOCCHGTUNNEL ) {
t - > parms . iph . ttl = p . iph . ttl ;
t - > parms . iph . tos = p . iph . tos ;
2007-12-13 09:47:00 -08:00
if ( t - > parms . link ! = p . link ) {
t - > parms . link = p . link ;
ipip6_tunnel_bind_dev ( dev ) ;
netdev_state_change ( dev ) ;
}
2005-04-16 15:20:36 -07:00
}
if ( copy_to_user ( ifr - > ifr_ifru . ifru_data , & t - > parms , sizeof ( p ) ) )
err = - EFAULT ;
} else
err = ( cmd = = SIOCADDTUNNEL ? - ENOBUFS : - ENOENT ) ;
break ;
case SIOCDELTUNNEL :
err = - EPERM ;
if ( ! capable ( CAP_NET_ADMIN ) )
goto done ;
2008-04-16 01:16:18 -07:00
if ( dev = = sitn - > fb_tunnel_dev ) {
2005-04-16 15:20:36 -07:00
err = - EFAULT ;
if ( copy_from_user ( & p , ifr - > ifr_ifru . ifru_data , sizeof ( p ) ) )
goto done ;
err = - ENOENT ;
2008-04-16 01:15:39 -07:00
if ( ( t = ipip6_tunnel_locate ( net , & p , 0 ) ) = = NULL )
2005-04-16 15:20:36 -07:00
goto done ;
err = - EPERM ;
2008-04-16 01:16:18 -07:00
if ( t = = netdev_priv ( sitn - > fb_tunnel_dev ) )
2005-04-16 15:20:36 -07:00
goto done ;
dev = t - > dev ;
}
2007-02-07 00:09:58 -08:00
unregister_netdevice ( dev ) ;
err = 0 ;
2005-04-16 15:20:36 -07:00
break ;
2008-03-24 18:28:39 +09:00
case SIOCGETPRL :
2008-06-16 16:48:20 -07:00
err = - EINVAL ;
if ( dev = = sitn - > fb_tunnel_dev )
goto done ;
err = - ENOENT ;
if ( ! ( t = netdev_priv ( dev ) ) )
goto done ;
err = ipip6_tunnel_get_prl ( t , ifr - > ifr_ifru . ifru_data ) ;
break ;
2008-03-11 18:35:59 -04:00
case SIOCADDPRL :
case SIOCDELPRL :
case SIOCCHGPRL :
err = - EPERM ;
2008-06-16 16:48:20 -07:00
if ( ! capable ( CAP_NET_ADMIN ) )
2008-03-11 18:35:59 -04:00
goto done ;
err = - EINVAL ;
2008-04-16 01:16:18 -07:00
if ( dev = = sitn - > fb_tunnel_dev )
2008-03-11 18:35:59 -04:00
goto done ;
err = - EFAULT ;
if ( copy_from_user ( & prl , ifr - > ifr_ifru . ifru_data , sizeof ( prl ) ) )
goto done ;
err = - ENOENT ;
if ( ! ( t = netdev_priv ( dev ) ) )
goto done ;
2008-03-24 18:28:39 +09:00
switch ( cmd ) {
case SIOCDELPRL :
2008-03-11 18:35:59 -04:00
err = ipip6_tunnel_del_prl ( t , & prl ) ;
2008-03-24 18:28:39 +09:00
break ;
case SIOCADDPRL :
case SIOCCHGPRL :
2008-03-11 18:35:59 -04:00
err = ipip6_tunnel_add_prl ( t , & prl , cmd = = SIOCCHGPRL ) ;
2008-03-24 18:28:39 +09:00
break ;
}
2008-06-16 16:48:20 -07:00
netdev_state_change ( dev ) ;
2008-03-11 18:35:59 -04:00
break ;
2005-04-16 15:20:36 -07:00
default :
err = - EINVAL ;
}
done :
return err ;
}
static int ipip6_tunnel_change_mtu ( struct net_device * dev , int new_mtu )
{
if ( new_mtu < IPV6_MIN_MTU | | new_mtu > 0xFFF8 - sizeof ( struct iphdr ) )
return - EINVAL ;
dev - > mtu = new_mtu ;
return 0 ;
}
2008-11-20 20:33:56 -08:00
static const struct net_device_ops ipip6_netdev_ops = {
. ndo_uninit = ipip6_tunnel_uninit ,
. ndo_start_xmit = ipip6_tunnel_xmit ,
. ndo_do_ioctl = ipip6_tunnel_ioctl ,
. ndo_change_mtu = ipip6_tunnel_change_mtu ,
} ;
2005-04-16 15:20:36 -07:00
static void ipip6_tunnel_setup ( struct net_device * dev )
{
2008-11-20 20:33:56 -08:00
dev - > netdev_ops = & ipip6_netdev_ops ;
2005-04-16 15:20:36 -07:00
dev - > destructor = free_netdev ;
dev - > type = ARPHRD_SIT ;
dev - > hard_header_len = LL_MAX_HEADER + sizeof ( struct iphdr ) ;
2006-01-05 16:35:42 -08:00
dev - > mtu = ETH_DATA_LEN - sizeof ( struct iphdr ) ;
2005-04-16 15:20:36 -07:00
dev - > flags = IFF_NOARP ;
dev - > iflink = 0 ;
dev - > addr_len = 4 ;
2008-04-16 01:17:18 -07:00
dev - > features | = NETIF_F_NETNS_LOCAL ;
2005-04-16 15:20:36 -07:00
}
2008-11-20 20:33:56 -08:00
static void ipip6_tunnel_init ( struct net_device * dev )
2005-04-16 15:20:36 -07:00
{
2008-11-20 20:33:56 -08:00
struct ip_tunnel * tunnel = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
tunnel - > dev = dev ;
strcpy ( tunnel - > parms . name , dev - > name ) ;
memcpy ( dev - > dev_addr , & tunnel - > parms . iph . saddr , 4 ) ;
memcpy ( dev - > broadcast , & tunnel - > parms . iph . daddr , 4 ) ;
2007-12-13 09:47:00 -08:00
ipip6_tunnel_bind_dev ( dev ) ;
2005-04-16 15:20:36 -07:00
}
2008-11-20 20:33:56 -08:00
static void ipip6_fb_tunnel_init ( struct net_device * dev )
2005-04-16 15:20:36 -07:00
{
2006-01-08 22:05:26 -08:00
struct ip_tunnel * tunnel = netdev_priv ( dev ) ;
2005-04-16 15:20:36 -07:00
struct iphdr * iph = & tunnel - > parms . iph ;
2008-04-16 01:16:38 -07:00
struct net * net = dev_net ( dev ) ;
struct sit_net * sitn = net_generic ( net , sit_net_id ) ;
2005-04-16 15:20:36 -07:00
tunnel - > dev = dev ;
strcpy ( tunnel - > parms . name , dev - > name ) ;
iph - > version = 4 ;
iph - > protocol = IPPROTO_IPV6 ;
iph - > ihl = 5 ;
iph - > ttl = 64 ;
dev_hold ( dev ) ;
2008-04-16 01:16:38 -07:00
sitn - > tunnels_wc [ 0 ] = tunnel ;
2005-04-16 15:20:36 -07:00
}
2007-02-13 12:55:25 -08:00
static struct xfrm_tunnel sit_handler = {
2005-04-16 15:20:36 -07:00
. handler = ipip6_rcv ,
. err_handler = ipip6_err ,
2007-02-13 12:55:25 -08:00
. priority = 1 ,
2005-04-16 15:20:36 -07:00
} ;
2008-04-16 01:16:38 -07:00
static void sit_destroy_tunnels ( struct sit_net * sitn )
2005-07-30 17:46:44 -07:00
{
int prio ;
for ( prio = 1 ; prio < 4 ; prio + + ) {
int h ;
for ( h = 0 ; h < HASH_SIZE ; h + + ) {
struct ip_tunnel * t ;
2008-04-16 01:16:38 -07:00
while ( ( t = sitn - > tunnels [ prio ] [ h ] ) ! = NULL )
2005-07-30 17:46:44 -07:00
unregister_netdevice ( t - > dev ) ;
}
}
}
2008-04-16 01:15:17 -07:00
static int sit_init_net ( struct net * net )
{
int err ;
struct sit_net * sitn ;
err = - ENOMEM ;
2008-04-16 01:16:38 -07:00
sitn = kzalloc ( sizeof ( struct sit_net ) , GFP_KERNEL ) ;
2008-04-16 01:15:17 -07:00
if ( sitn = = NULL )
goto err_alloc ;
err = net_assign_generic ( net , sit_net_id , sitn ) ;
if ( err < 0 )
goto err_assign ;
2008-04-16 01:16:38 -07:00
sitn - > tunnels [ 0 ] = sitn - > tunnels_wc ;
sitn - > tunnels [ 1 ] = sitn - > tunnels_l ;
sitn - > tunnels [ 2 ] = sitn - > tunnels_r ;
sitn - > tunnels [ 3 ] = sitn - > tunnels_r_l ;
2008-04-16 01:16:18 -07:00
sitn - > fb_tunnel_dev = alloc_netdev ( sizeof ( struct ip_tunnel ) , " sit0 " ,
ipip6_tunnel_setup ) ;
if ( ! sitn - > fb_tunnel_dev ) {
err = - ENOMEM ;
goto err_alloc_dev ;
}
2008-11-23 17:26:26 -08:00
dev_net_set ( sitn - > fb_tunnel_dev , net ) ;
2008-04-16 01:16:18 -07:00
2008-11-20 20:33:56 -08:00
ipip6_fb_tunnel_init ( sitn - > fb_tunnel_dev ) ;
2008-04-16 01:16:18 -07:00
if ( ( err = register_netdev ( sitn - > fb_tunnel_dev ) ) )
goto err_reg_dev ;
2008-04-16 01:15:17 -07:00
return 0 ;
2008-04-16 01:16:18 -07:00
err_reg_dev :
2008-11-20 20:33:56 -08:00
dev_put ( sitn - > fb_tunnel_dev ) ;
2008-04-16 01:16:18 -07:00
free_netdev ( sitn - > fb_tunnel_dev ) ;
err_alloc_dev :
/* nothing */
2008-04-16 01:15:17 -07:00
err_assign :
kfree ( sitn ) ;
err_alloc :
return err ;
}
static void sit_exit_net ( struct net * net )
{
struct sit_net * sitn ;
sitn = net_generic ( net , sit_net_id ) ;
2008-04-16 01:16:18 -07:00
rtnl_lock ( ) ;
2008-04-16 01:16:38 -07:00
sit_destroy_tunnels ( sitn ) ;
2008-04-16 01:16:18 -07:00
unregister_netdevice ( sitn - > fb_tunnel_dev ) ;
rtnl_unlock ( ) ;
2008-04-16 01:15:17 -07:00
kfree ( sitn ) ;
}
static struct pernet_operations sit_net_ops = {
. init = sit_init_net ,
. exit = sit_exit_net ,
} ;
2006-11-20 16:56:48 -08:00
static void __exit sit_cleanup ( void )
2005-04-16 15:20:36 -07:00
{
2007-02-13 12:55:25 -08:00
xfrm4_tunnel_deregister ( & sit_handler , AF_INET6 ) ;
2005-07-30 17:46:44 -07:00
2008-04-16 01:15:17 -07:00
unregister_pernet_gen_device ( sit_net_id , & sit_net_ops ) ;
2005-04-16 15:20:36 -07:00
}
2006-11-20 16:56:48 -08:00
static int __init sit_init ( void )
2005-04-16 15:20:36 -07:00
{
int err ;
printk ( KERN_INFO " IPv6 over IPv4 tunneling driver \n " ) ;
2007-02-13 12:55:25 -08:00
if ( xfrm4_tunnel_register ( & sit_handler , AF_INET6 ) < 0 ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_INFO " sit init: Can't add protocol \n " ) ;
return - EAGAIN ;
}
2008-04-16 01:15:17 -07:00
err = register_pernet_gen_device ( & sit_net_id , & sit_net_ops ) ;
if ( err < 0 )
2008-04-16 01:16:18 -07:00
xfrm4_tunnel_deregister ( & sit_handler , AF_INET6 ) ;
2008-04-16 01:15:17 -07:00
2005-04-16 15:20:36 -07:00
return err ;
}
2006-10-10 14:47:44 -07:00
module_init ( sit_init ) ;
module_exit ( sit_cleanup ) ;
2006-10-13 15:05:53 -07:00
MODULE_LICENSE ( " GPL " ) ;
2006-11-05 15:47:04 -08:00
MODULE_ALIAS ( " sit0 " ) ;