2005-04-17 02:20:36 +04:00
/*
2007-02-09 17:24:47 +03:00
* Linux NET3 : GRE over IP protocol decoder .
2005-04-17 02:20:36 +04:00
*
* Authors : 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 .
*
*/
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <asm/uaccess.h>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/in.h>
# include <linux/tcp.h>
# include <linux/udp.h>
# include <linux/if_arp.h>
# include <linux/mroute.h>
# include <linux/init.h>
# include <linux/in6.h>
# include <linux/inetdevice.h>
# include <linux/igmp.h>
# include <linux/netfilter_ipv4.h>
2008-10-09 23:00:17 +04:00
# include <linux/etherdevice.h>
2006-01-06 03:35:42 +03:00
# include <linux/if_ether.h>
2005-04-17 02:20:36 +04:00
# include <net/sock.h>
# include <net/ip.h>
# include <net/icmp.h>
# include <net/protocol.h>
# include <net/ipip.h>
# include <net/arp.h>
# include <net/checksum.h>
# include <net/dsfield.h>
# include <net/inet_ecn.h>
# include <net/xfrm.h>
2008-04-16 12:08:53 +04:00
# include <net/net_namespace.h>
# include <net/netns/generic.h>
2008-10-09 22:59:55 +04:00
# include <net/rtnetlink.h>
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_IPV6
# include <net/ipv6.h>
# include <net/ip6_fib.h>
# include <net/ip6_route.h>
# endif
/*
Problems & solutions
- - - - - - - - - - - - - - - - - - - -
1. The most important issue is detecting local dead loops .
They would cause complete host lockup in transmit , which
would be " resolved " by stack overflow or , if queueing is enabled ,
with infinite looping in net_bh .
We cannot track such dead loops during route installation ,
it is infeasible task . The most general solutions would be
to keep skb - > encapsulation counter ( sort of local ttl ) ,
and silently drop packet when it expires . It is the best
solution , but it supposes maintaing new variable in ALL
skb , even if no tunneling is used .
2009-09-23 14:28:33 +04:00
Current solution : HARD_TX_LOCK lock breaks dead loops .
2005-04-17 02:20:36 +04:00
2. Networking dead loops would not kill routers , but would really
kill network . IP hop limit plays role of " t->recursion " in this case ,
if we copy it from packet being encapsulated to upper header .
It is very good solution , but it introduces two problems :
- Routing protocols , using packets with ttl = 1 ( OSPF , RIP2 ) ,
do not work over tunnels .
- traceroute does not work . I planned to relay ICMP from tunnel ,
so that this problem would be solved and traceroute output
would even more informative . This idea appeared to be wrong :
only Linux complies to rfc1812 now ( yes , guys , Linux is the only
true router now : - ) ) , all routers ( at least , in neighbourhood of mine )
return only 8 bytes of payload . It is the end .
Hence , if we want that OSPF worked or traceroute said something reasonable ,
we should search for another solution .
One of them is to parse packet trying to detect inner encapsulation
made by our node . It is difficult or even impossible , especially ,
taking into account fragmentation . TO be short , tt is not solution at all .
Current solution : The solution was UNEXPECTEDLY SIMPLE .
We force DF flag on tunnels with preconfigured hop limit ,
that is ALL . : - ) Well , it does not remove the problem completely ,
but exponential growth of network traffic is changed to linear
( branches , that exceed pmtu are pruned ) and tunnel mtu
fastly degrades to value < 68 , where looping stops .
Yes , it is not good if there exists a router in the loop ,
which does not force DF , even when encapsulating packets have DF set .
But it is not our problem ! Nobody could accuse us , we made
all that we could make . Even if it is your gated who injected
fatal route to network , even if it were you who configured
fatal static route : you are innocent . : - )
3. Really , ipv4 / ipip . c , ipv4 / ip_gre . c and ipv6 / sit . c contain
practically identical code . It would be good to glue them
together , but it is not very evident , how to make them modular .
sit is integral part of IPv6 , ipip and gre are naturally modular .
We could extract common parts ( hash table , ioctl etc )
to a separate module ( ip_tunnel . c ) .
Alexey Kuznetsov .
*/
2008-10-09 22:59:55 +04:00
static struct rtnl_link_ops ipgre_link_ops __read_mostly ;
2005-04-17 02:20:36 +04:00
static int ipgre_tunnel_init ( struct net_device * dev ) ;
static void ipgre_tunnel_setup ( struct net_device * dev ) ;
2008-10-09 22:59:32 +04:00
static int ipgre_tunnel_bind_dev ( struct net_device * dev ) ;
2005-04-17 02:20:36 +04:00
/* Fallback tunnel: no source, no destination, no key, no options */
2008-04-16 12:10:26 +04:00
# define HASH_SIZE 16
2008-04-16 12:08:53 +04:00
static int ipgre_net_id ;
struct ipgre_net {
2008-04-16 12:10:26 +04:00
struct ip_tunnel * tunnels [ 4 ] [ HASH_SIZE ] ;
2008-04-16 12:10:05 +04:00
struct net_device * fb_tunnel_dev ;
2008-04-16 12:08:53 +04:00
} ;
2005-04-17 02:20:36 +04:00
/* Tunnel hash table */
/*
4 hash tables :
3 : ( remote , local )
2 : ( remote , * )
1 : ( * , local )
0 : ( * , * )
We require exact key match i . e . if a key is present in packet
it will match only tunnel with the same key ; if it is not present ,
it will match only keyless tunnel .
All keysless packets , if not matched configured keyless tunnels
will match fallback tunnel .
*/
2006-11-08 11:23:14 +03:00
# define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
2005-04-17 02:20:36 +04:00
2008-04-16 12:10:26 +04:00
# define tunnels_r_l tunnels[3]
# define tunnels_r tunnels[2]
# define tunnels_l tunnels[1]
# define tunnels_wc tunnels[0]
2009-10-23 10:14:38 +04:00
/*
* Locking : hash tables are protected by RCU and a spinlock
*/
static DEFINE_SPINLOCK ( ipgre_lock ) ;
2005-04-17 02:20:36 +04:00
2009-10-23 10:14:38 +04:00
# define for_each_ip_tunnel_rcu(start) \
for ( t = rcu_dereference ( start ) ; t ; t = rcu_dereference ( t - > next ) )
2005-04-17 02:20:36 +04:00
/* Given src, dst and key, find appropriate for input tunnel. */
2009-01-20 04:22:12 +03:00
static struct ip_tunnel * ipgre_tunnel_lookup ( struct net_device * dev ,
2008-10-09 23:00:17 +04:00
__be32 remote , __be32 local ,
__be32 key , __be16 gre_proto )
2005-04-17 02:20:36 +04:00
{
2009-01-20 04:22:12 +03:00
struct net * net = dev_net ( dev ) ;
int link = dev - > ifindex ;
2005-04-17 02:20:36 +04:00
unsigned h0 = HASH ( remote ) ;
unsigned h1 = HASH ( key ) ;
2009-01-27 07:56:10 +03:00
struct ip_tunnel * t , * cand = NULL ;
2008-04-16 12:10:05 +04:00
struct ipgre_net * ign = net_generic ( net , ipgre_net_id ) ;
2008-10-09 23:00:17 +04:00
int dev_type = ( gre_proto = = htons ( ETH_P_TEB ) ) ?
ARPHRD_ETHER : ARPHRD_IPGRE ;
2009-01-27 07:56:10 +03:00
int score , cand_score = 4 ;
2005-04-17 02:20:36 +04:00
2009-10-23 10:14:38 +04:00
for_each_ip_tunnel_rcu ( ign - > tunnels_r_l [ h0 ^ h1 ] ) {
2009-01-20 04:22:12 +03:00
if ( local ! = t - > parms . iph . saddr | |
remote ! = t - > parms . iph . daddr | |
key ! = t - > parms . i_key | |
! ( t - > dev - > flags & IFF_UP ) )
continue ;
if ( t - > dev - > type ! = ARPHRD_IPGRE & &
t - > dev - > type ! = dev_type )
continue ;
2009-01-27 07:56:10 +03:00
score = 0 ;
2009-01-20 04:22:12 +03:00
if ( t - > parms . link ! = link )
2009-01-27 07:56:10 +03:00
score | = 1 ;
2009-01-20 04:22:12 +03:00
if ( t - > dev - > type ! = dev_type )
2009-01-27 07:56:10 +03:00
score | = 2 ;
if ( score = = 0 )
2009-01-20 04:22:12 +03:00
return t ;
2009-01-27 07:56:10 +03:00
if ( score < cand_score ) {
cand = t ;
cand_score = score ;
}
2005-04-17 02:20:36 +04:00
}
2008-10-09 23:00:17 +04:00
2009-10-23 10:14:38 +04:00
for_each_ip_tunnel_rcu ( ign - > tunnels_r [ h0 ^ h1 ] ) {
2009-01-20 04:22:12 +03:00
if ( remote ! = t - > parms . iph . daddr | |
key ! = t - > parms . i_key | |
! ( t - > dev - > flags & IFF_UP ) )
continue ;
if ( t - > dev - > type ! = ARPHRD_IPGRE & &
t - > dev - > type ! = dev_type )
continue ;
2009-01-27 07:56:10 +03:00
score = 0 ;
2009-01-20 04:22:12 +03:00
if ( t - > parms . link ! = link )
2009-01-27 07:56:10 +03:00
score | = 1 ;
2009-01-20 04:22:12 +03:00
if ( t - > dev - > type ! = dev_type )
2009-01-27 07:56:10 +03:00
score | = 2 ;
if ( score = = 0 )
2009-01-20 04:22:12 +03:00
return t ;
2009-01-27 07:56:10 +03:00
if ( score < cand_score ) {
cand = t ;
cand_score = score ;
}
2005-04-17 02:20:36 +04:00
}
2008-10-09 23:00:17 +04:00
2009-10-23 10:14:38 +04:00
for_each_ip_tunnel_rcu ( ign - > tunnels_l [ h1 ] ) {
2009-01-20 04:22:12 +03:00
if ( ( local ! = t - > parms . iph . saddr & &
( local ! = t - > parms . iph . daddr | |
! ipv4_is_multicast ( local ) ) ) | |
key ! = t - > parms . i_key | |
! ( t - > dev - > flags & IFF_UP ) )
continue ;
if ( t - > dev - > type ! = ARPHRD_IPGRE & &
t - > dev - > type ! = dev_type )
continue ;
2009-01-27 07:56:10 +03:00
score = 0 ;
2009-01-20 04:22:12 +03:00
if ( t - > parms . link ! = link )
2009-01-27 07:56:10 +03:00
score | = 1 ;
2009-01-20 04:22:12 +03:00
if ( t - > dev - > type ! = dev_type )
2009-01-27 07:56:10 +03:00
score | = 2 ;
if ( score = = 0 )
2009-01-20 04:22:12 +03:00
return t ;
2009-01-27 07:56:10 +03:00
if ( score < cand_score ) {
cand = t ;
cand_score = score ;
}
2005-04-17 02:20:36 +04:00
}
2008-10-09 23:00:17 +04:00
2009-10-23 10:14:38 +04:00
for_each_ip_tunnel_rcu ( ign - > tunnels_wc [ h1 ] ) {
2009-01-20 04:22:12 +03:00
if ( t - > parms . i_key ! = key | |
! ( t - > dev - > flags & IFF_UP ) )
continue ;
if ( t - > dev - > type ! = ARPHRD_IPGRE & &
t - > dev - > type ! = dev_type )
continue ;
2009-01-27 07:56:10 +03:00
score = 0 ;
2009-01-20 04:22:12 +03:00
if ( t - > parms . link ! = link )
2009-01-27 07:56:10 +03:00
score | = 1 ;
2009-01-20 04:22:12 +03:00
if ( t - > dev - > type ! = dev_type )
2009-01-27 07:56:10 +03:00
score | = 2 ;
if ( score = = 0 )
2009-01-20 04:22:12 +03:00
return t ;
2009-01-27 07:56:10 +03:00
if ( score < cand_score ) {
cand = t ;
cand_score = score ;
}
2005-04-17 02:20:36 +04:00
}
2009-01-27 07:56:10 +03:00
if ( cand ! = NULL )
return cand ;
2008-10-09 23:00:17 +04:00
2009-10-23 10:14:38 +04:00
dev = ign - > fb_tunnel_dev ;
if ( dev - > flags & IFF_UP )
return netdev_priv ( dev ) ;
2009-01-20 04:22:12 +03:00
2005-04-17 02:20:36 +04:00
return NULL ;
}
2008-04-16 12:09:22 +04:00
static struct ip_tunnel * * __ipgre_bucket ( struct ipgre_net * ign ,
struct ip_tunnel_parm * parms )
2005-04-17 02:20:36 +04:00
{
2007-04-24 15:44:48 +04:00
__be32 remote = parms - > iph . daddr ;
__be32 local = parms - > iph . saddr ;
__be32 key = parms - > i_key ;
2005-04-17 02:20:36 +04:00
unsigned h = HASH ( key ) ;
int prio = 0 ;
if ( local )
prio | = 1 ;
2007-12-17 00:45:43 +03:00
if ( remote & & ! ipv4_is_multicast ( remote ) ) {
2005-04-17 02:20:36 +04:00
prio | = 2 ;
h ^ = HASH ( remote ) ;
}
2008-04-16 12:10:26 +04:00
return & ign - > tunnels [ prio ] [ h ] ;
2005-04-17 02:20:36 +04:00
}
2008-04-16 12:09:22 +04:00
static inline struct ip_tunnel * * ipgre_bucket ( struct ipgre_net * ign ,
struct ip_tunnel * t )
2007-04-24 15:44:48 +04:00
{
2008-04-16 12:09:22 +04:00
return __ipgre_bucket ( ign , & t - > parms ) ;
2007-04-24 15:44:48 +04:00
}
2008-04-16 12:09:22 +04:00
static void ipgre_tunnel_link ( struct ipgre_net * ign , struct ip_tunnel * t )
2005-04-17 02:20:36 +04:00
{
2008-04-16 12:09:22 +04:00
struct ip_tunnel * * tp = ipgre_bucket ( ign , t ) ;
2005-04-17 02:20:36 +04:00
2009-10-23 10:14:38 +04:00
spin_lock_bh ( & ipgre_lock ) ;
2005-04-17 02:20:36 +04:00
t - > next = * tp ;
2009-10-23 10:14:38 +04:00
rcu_assign_pointer ( * tp , t ) ;
spin_unlock_bh ( & ipgre_lock ) ;
2005-04-17 02:20:36 +04:00
}
2008-04-16 12:09:22 +04:00
static void ipgre_tunnel_unlink ( struct ipgre_net * ign , struct ip_tunnel * t )
2005-04-17 02:20:36 +04:00
{
struct ip_tunnel * * tp ;
2008-04-16 12:09:22 +04:00
for ( tp = ipgre_bucket ( ign , t ) ; * tp ; tp = & ( * tp ) - > next ) {
2005-04-17 02:20:36 +04:00
if ( t = = * tp ) {
2009-10-23 10:14:38 +04:00
spin_lock_bh ( & ipgre_lock ) ;
2005-04-17 02:20:36 +04:00
* tp = t - > next ;
2009-10-23 10:14:38 +04:00
spin_unlock_bh ( & ipgre_lock ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
}
2008-10-09 23:00:17 +04:00
static struct ip_tunnel * ipgre_tunnel_find ( struct net * net ,
struct ip_tunnel_parm * parms ,
int type )
2005-04-17 02:20:36 +04:00
{
2006-11-08 11:23:14 +03:00
__be32 remote = parms - > iph . daddr ;
__be32 local = parms - > iph . saddr ;
__be32 key = parms - > i_key ;
2009-01-20 04:22:12 +03:00
int link = parms - > link ;
2008-10-09 23:00:17 +04:00
struct ip_tunnel * t , * * tp ;
struct ipgre_net * ign = net_generic ( net , ipgre_net_id ) ;
for ( tp = __ipgre_bucket ( ign , parms ) ; ( t = * tp ) ! = NULL ; tp = & t - > next )
if ( local = = t - > parms . iph . saddr & &
remote = = t - > parms . iph . daddr & &
key = = t - > parms . i_key & &
2009-01-20 04:22:12 +03:00
link = = t - > parms . link & &
2008-10-09 23:00:17 +04:00
type = = t - > dev - > type )
break ;
return t ;
}
static struct ip_tunnel * ipgre_tunnel_locate ( struct net * net ,
struct ip_tunnel_parm * parms , int create )
{
struct ip_tunnel * t , * nt ;
2005-04-17 02:20:36 +04:00
struct net_device * dev ;
char name [ IFNAMSIZ ] ;
2008-04-16 12:09:22 +04:00
struct ipgre_net * ign = net_generic ( net , ipgre_net_id ) ;
2005-04-17 02:20:36 +04:00
2008-10-09 23:00:17 +04:00
t = ipgre_tunnel_find ( net , parms , ARPHRD_IPGRE ) ;
if ( t | | ! create )
return t ;
2005-04-17 02:20:36 +04:00
if ( parms - > name [ 0 ] )
strlcpy ( name , parms - > name , IFNAMSIZ ) ;
2008-02-24 07:19:20 +03:00
else
sprintf ( name , " gre%%d " ) ;
2005-04-17 02:20:36 +04:00
dev = alloc_netdev ( sizeof ( * t ) , name , ipgre_tunnel_setup ) ;
if ( ! dev )
return NULL ;
2008-04-16 12:11:13 +04: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-27 10:51:04 +03:00
if ( strchr ( name , ' % ' ) ) {
if ( dev_alloc_name ( dev , name ) < 0 )
goto failed_free ;
}
2006-01-09 09:05:26 +03:00
nt = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
nt - > parms = * parms ;
2008-10-09 22:59:55 +04:00
dev - > rtnl_link_ops = & ipgre_link_ops ;
2005-04-17 02:20:36 +04:00
2008-10-09 22:59:32 +04:00
dev - > mtu = ipgre_tunnel_bind_dev ( dev ) ;
[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-27 10:51:04 +03:00
if ( register_netdevice ( dev ) < 0 )
goto failed_free ;
2005-04-17 02:20:36 +04:00
dev_hold ( dev ) ;
2008-04-16 12:09:22 +04:00
ipgre_tunnel_link ( ign , nt ) ;
2005-04-17 02:20:36 +04: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-27 10:51:04 +03:00
failed_free :
free_netdev ( dev ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
static void ipgre_tunnel_uninit ( struct net_device * dev )
{
2008-04-16 12:09:22 +04:00
struct net * net = dev_net ( dev ) ;
struct ipgre_net * ign = net_generic ( net , ipgre_net_id ) ;
ipgre_tunnel_unlink ( ign , netdev_priv ( dev ) ) ;
2005-04-17 02:20:36 +04:00
dev_put ( dev ) ;
}
static void ipgre_err ( struct sk_buff * skb , u32 info )
{
2008-05-22 04:47:54 +04:00
/* All the routers (except for Linux) return only
2005-04-17 02:20:36 +04:00
8 bytes of packet payload . It means , that precise relaying of
ICMP in the real Internet is absolutely infeasible .
Moreover , Cisco " wise men " put GRE key to the third word
in GRE header . It makes impossible maintaining even soft state for keyed
GRE tunnels with enabled checksum . Tell them " thank you " .
Well , I wonder , rfc1812 was written by Cisco employee ,
what the hell these idiots break standrads established
by themself ? ? ?
*/
2008-11-03 11:25:16 +03:00
struct iphdr * iph = ( struct iphdr * ) skb - > data ;
2006-11-08 11:23:14 +03:00
__be16 * p = ( __be16 * ) ( skb - > data + ( iph - > ihl < < 2 ) ) ;
2005-04-17 02:20:36 +04:00
int grehlen = ( iph - > ihl < < 2 ) + 4 ;
2007-03-13 20:43:18 +03:00
const int type = icmp_hdr ( skb ) - > type ;
const int code = icmp_hdr ( skb ) - > code ;
2005-04-17 02:20:36 +04:00
struct ip_tunnel * t ;
2006-11-08 11:23:14 +03:00
__be16 flags ;
2005-04-17 02:20:36 +04:00
flags = p [ 0 ] ;
if ( flags & ( GRE_CSUM | GRE_KEY | GRE_SEQ | GRE_ROUTING | GRE_VERSION ) ) {
if ( flags & ( GRE_VERSION | GRE_ROUTING ) )
return ;
if ( flags & GRE_KEY ) {
grehlen + = 4 ;
if ( flags & GRE_CSUM )
grehlen + = 4 ;
}
}
/* If only 8 bytes returned, keyed message will be dropped here */
if ( skb_headlen ( skb ) < grehlen )
return ;
switch ( type ) {
default :
case ICMP_PARAMETERPROB :
return ;
case ICMP_DEST_UNREACH :
switch ( code ) {
case ICMP_SR_FAILED :
case ICMP_PORT_UNREACH :
/* Impossible event. */
return ;
case ICMP_FRAG_NEEDED :
/* Soft state for pmtu is maintained by IP core. */
return ;
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 )
return ;
break ;
}
2009-10-23 10:14:38 +04:00
rcu_read_lock ( ) ;
2009-01-20 04:22:12 +03:00
t = ipgre_tunnel_lookup ( skb - > dev , iph - > daddr , iph - > saddr ,
2008-10-09 23:00:17 +04:00
flags & GRE_KEY ?
* ( ( ( __be32 * ) p ) + ( grehlen / 4 ) - 1 ) : 0 ,
p [ 1 ] ) ;
2007-12-17 00:45:43 +03:00
if ( t = = NULL | | t - > parms . iph . daddr = = 0 | |
ipv4_is_multicast ( t - > parms . iph . daddr ) )
2005-04-17 02:20:36 +04:00
goto out ;
if ( t - > parms . iph . ttl = = 0 & & type = = ICMP_TIME_EXCEEDED )
goto out ;
2009-02-25 10:34:48 +03:00
if ( time_before ( jiffies , t - > err_time + IPTUNNEL_ERR_TIMEO ) )
2005-04-17 02:20:36 +04:00
t - > err_count + + ;
else
t - > err_count = 1 ;
t - > err_time = jiffies ;
out :
2009-10-23 10:14:38 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return ;
}
static inline void ipgre_ecn_decapsulate ( struct iphdr * iph , struct sk_buff * skb )
{
if ( INET_ECN_is_ce ( iph - > tos ) ) {
if ( skb - > protocol = = htons ( ETH_P_IP ) ) {
2007-04-21 09:47:35 +04:00
IP_ECN_set_ce ( ip_hdr ( skb ) ) ;
2005-04-17 02:20:36 +04:00
} else if ( skb - > protocol = = htons ( ETH_P_IPV6 ) ) {
2007-04-26 04:54:47 +04:00
IP6_ECN_set_ce ( ipv6_hdr ( skb ) ) ;
2005-04-17 02:20:36 +04:00
}
}
}
static inline u8
ipgre_ecn_encapsulate ( u8 tos , struct iphdr * old_iph , struct sk_buff * skb )
{
u8 inner = 0 ;
if ( skb - > protocol = = htons ( ETH_P_IP ) )
inner = old_iph - > tos ;
else if ( skb - > protocol = = htons ( ETH_P_IPV6 ) )
inner = ipv6_get_dsfield ( ( struct ipv6hdr * ) old_iph ) ;
return INET_ECN_encapsulate ( tos , inner ) ;
}
static int ipgre_rcv ( struct sk_buff * skb )
{
struct iphdr * iph ;
u8 * h ;
2006-11-08 11:23:14 +03:00
__be16 flags ;
2006-11-15 08:24:49 +03:00
__sum16 csum = 0 ;
2006-11-08 11:23:14 +03:00
__be32 key = 0 ;
2005-04-17 02:20:36 +04:00
u32 seqno = 0 ;
struct ip_tunnel * tunnel ;
int offset = 4 ;
2008-10-09 23:00:17 +04:00
__be16 gre_proto ;
2008-10-09 23:03:17 +04:00
unsigned int len ;
2005-04-17 02:20:36 +04:00
if ( ! pskb_may_pull ( skb , 16 ) )
goto drop_nolock ;
2007-04-21 09:47:35 +04:00
iph = ip_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
h = skb - > data ;
2006-11-08 11:23:14 +03:00
flags = * ( __be16 * ) h ;
2005-04-17 02:20:36 +04:00
if ( flags & ( GRE_CSUM | GRE_KEY | GRE_ROUTING | GRE_SEQ | GRE_VERSION ) ) {
/* - Version must be 0.
- We do not support routing headers .
*/
if ( flags & ( GRE_VERSION | GRE_ROUTING ) )
goto drop_nolock ;
if ( flags & GRE_CSUM ) {
2005-11-11 00:01:24 +03:00
switch ( skb - > ip_summed ) {
2006-08-30 03:44:56 +04:00
case CHECKSUM_COMPLETE :
2006-11-15 08:24:49 +03:00
csum = csum_fold ( skb - > csum ) ;
2005-11-11 00:01:24 +03:00
if ( ! csum )
break ;
/* fall through */
case CHECKSUM_NONE :
skb - > csum = 0 ;
csum = __skb_checksum_complete ( skb ) ;
2006-08-30 03:44:56 +04:00
skb - > ip_summed = CHECKSUM_COMPLETE ;
2005-04-17 02:20:36 +04:00
}
offset + = 4 ;
}
if ( flags & GRE_KEY ) {
2006-11-08 11:23:14 +03:00
key = * ( __be32 * ) ( h + offset ) ;
2005-04-17 02:20:36 +04:00
offset + = 4 ;
}
if ( flags & GRE_SEQ ) {
2006-11-08 11:23:14 +03:00
seqno = ntohl ( * ( __be32 * ) ( h + offset ) ) ;
2005-04-17 02:20:36 +04:00
offset + = 4 ;
}
}
2008-10-09 23:00:17 +04:00
gre_proto = * ( __be16 * ) ( h + 2 ) ;
2009-10-23 10:14:38 +04:00
rcu_read_lock ( ) ;
2009-01-20 04:22:12 +03:00
if ( ( tunnel = ipgre_tunnel_lookup ( skb - > dev ,
2008-10-09 23:00:17 +04:00
iph - > saddr , iph - > daddr , key ,
gre_proto ) ) ) {
2008-05-22 01:14:22 +04:00
struct net_device_stats * stats = & tunnel - > dev - > stats ;
2005-04-17 02:20:36 +04:00
secpath_reset ( skb ) ;
2008-10-09 23:00:17 +04:00
skb - > protocol = gre_proto ;
2005-04-17 02:20:36 +04:00
/* WCCP version 1 and 2 protocol decoding.
* - Change protocol to IP
* - When dealing with WCCPv2 , Skip extra 4 bytes in GRE header
*/
2008-10-09 23:00:17 +04:00
if ( flags = = 0 & & gre_proto = = htons ( ETH_P_WCCP ) ) {
2006-10-11 06:41:21 +04:00
skb - > protocol = htons ( ETH_P_IP ) ;
2007-02-09 17:24:47 +03:00
if ( ( * ( h + offset ) & 0xF0 ) ! = 0x40 )
2005-04-17 02:20:36 +04:00
offset + = 4 ;
}
2007-12-20 11:10:33 +03:00
skb - > mac_header = skb - > network_header ;
2007-03-11 00:42:03 +03:00
__pskb_pull ( skb , offset ) ;
2007-04-26 05:04:18 +04:00
skb_postpull_rcsum ( skb , skb_transport_header ( skb ) , offset ) ;
2005-04-17 02:20:36 +04:00
skb - > pkt_type = PACKET_HOST ;
# ifdef CONFIG_NET_IPGRE_BROADCAST
2007-12-17 00:45:43 +03:00
if ( ipv4_is_multicast ( iph - > daddr ) ) {
2005-04-17 02:20:36 +04:00
/* Looped back packet, drop it! */
2009-06-02 09:14:27 +04:00
if ( skb_rtable ( skb ) - > fl . iif = = 0 )
2005-04-17 02:20:36 +04:00
goto drop ;
2008-05-22 01:14:22 +04:00
stats - > multicast + + ;
2005-04-17 02:20:36 +04:00
skb - > pkt_type = PACKET_BROADCAST ;
}
# endif
if ( ( ( flags & GRE_CSUM ) & & csum ) | |
( ! ( flags & GRE_CSUM ) & & tunnel - > parms . i_flags & GRE_CSUM ) ) {
2008-05-22 01:14:22 +04:00
stats - > rx_crc_errors + + ;
stats - > rx_errors + + ;
2005-04-17 02:20:36 +04:00
goto drop ;
}
if ( tunnel - > parms . i_flags & GRE_SEQ ) {
if ( ! ( flags & GRE_SEQ ) | |
( tunnel - > i_seqno & & ( s32 ) ( seqno - tunnel - > i_seqno ) < 0 ) ) {
2008-05-22 01:14:22 +04:00
stats - > rx_fifo_errors + + ;
stats - > rx_errors + + ;
2005-04-17 02:20:36 +04:00
goto drop ;
}
tunnel - > i_seqno = seqno + 1 ;
}
2008-10-09 23:00:17 +04:00
2008-10-09 23:03:17 +04:00
len = skb - > len ;
2008-10-09 23:00:17 +04:00
/* Warning: All skb pointers will be invalidated! */
if ( tunnel - > dev - > type = = ARPHRD_ETHER ) {
if ( ! pskb_may_pull ( skb , ETH_HLEN ) ) {
stats - > rx_length_errors + + ;
stats - > rx_errors + + ;
goto drop ;
}
iph = ip_hdr ( skb ) ;
skb - > protocol = eth_type_trans ( skb , tunnel - > dev ) ;
skb_postpull_rcsum ( skb , eth_hdr ( skb ) , ETH_HLEN ) ;
}
2008-05-22 01:14:22 +04:00
stats - > rx_packets + + ;
2008-10-09 23:03:17 +04:00
stats - > rx_bytes + = len ;
2005-04-17 02:20:36 +04:00
skb - > dev = tunnel - > dev ;
2009-06-02 09:19:30 +04:00
skb_dst_drop ( skb ) ;
2005-04-17 02:20:36 +04:00
nf_reset ( skb ) ;
2008-10-09 23:00:17 +04:00
skb_reset_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
ipgre_ecn_decapsulate ( iph , skb ) ;
2008-10-09 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
netif_rx ( skb ) ;
2009-10-23 10:14:38 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return ( 0 ) ;
}
2006-04-06 09:31:19 +04:00
icmp_send ( skb , ICMP_DEST_UNREACH , ICMP_PORT_UNREACH , 0 ) ;
2005-04-17 02:20:36 +04:00
drop :
2009-10-23 10:14:38 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
drop_nolock :
kfree_skb ( skb ) ;
return ( 0 ) ;
}
2009-08-31 23:50:41 +04:00
static netdev_tx_t ipgre_tunnel_xmit ( struct sk_buff * skb , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2006-01-09 09:05:26 +03:00
struct ip_tunnel * tunnel = netdev_priv ( dev ) ;
2009-10-05 11:11:22 +04:00
struct net_device_stats * stats = & dev - > stats ;
struct netdev_queue * txq = netdev_get_tx_queue ( dev , 0 ) ;
2007-04-21 09:47:35 +04:00
struct iphdr * old_iph = ip_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
struct iphdr * tiph ;
u8 tos ;
2006-11-08 11:23:14 +03:00
__be16 df ;
2005-04-17 02:20:36 +04:00
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-24 08:07:32 +04:00
unsigned int max_headroom ; /* The extra header space needed */
2005-04-17 02:20:36 +04:00
int gre_hlen ;
2006-11-08 11:23:14 +03:00
__be32 dst ;
2005-04-17 02:20:36 +04:00
int mtu ;
2008-10-09 23:00:17 +04:00
if ( dev - > type = = ARPHRD_ETHER )
IPCB ( skb ) - > flags = 0 ;
if ( dev - > header_ops & & dev - > type = = ARPHRD_IPGRE ) {
2005-04-17 02:20:36 +04:00
gre_hlen = 0 ;
2008-11-03 11:25:16 +03:00
tiph = ( struct iphdr * ) skb - > data ;
2005-04-17 02:20:36 +04:00
} else {
gre_hlen = tunnel - > hlen ;
tiph = & tunnel - > parms . iph ;
}
if ( ( dst = tiph - > daddr ) = = 0 ) {
/* NBMA tunnel */
2009-06-02 09:19:30 +04:00
if ( skb_dst ( skb ) = = NULL ) {
2008-05-22 01:14:22 +04:00
stats - > tx_fifo_errors + + ;
2005-04-17 02:20:36 +04:00
goto tx_error ;
}
if ( skb - > protocol = = htons ( ETH_P_IP ) ) {
2009-06-02 09:14:27 +04:00
rt = skb_rtable ( skb ) ;
2005-04-17 02:20:36 +04:00
if ( ( dst = rt - > rt_gateway ) = = 0 )
goto tx_error_icmp ;
}
# ifdef CONFIG_IPV6
else if ( skb - > protocol = = htons ( ETH_P_IPV6 ) ) {
struct in6_addr * addr6 ;
int addr_type ;
2009-06-02 09:19:30 +04:00
struct neighbour * neigh = skb_dst ( skb ) - > neighbour ;
2005-04-17 02:20:36 +04:00
if ( neigh = = NULL )
goto tx_error ;
2008-11-03 11:25:16 +03:00
addr6 = ( struct in6_addr * ) & neigh - > primary_key ;
2005-04-17 02:20:36 +04:00
addr_type = ipv6_addr_type ( addr6 ) ;
if ( addr_type = = IPV6_ADDR_ANY ) {
2007-04-26 04:54:47 +04:00
addr6 = & ipv6_hdr ( skb ) - > daddr ;
2005-04-17 02:20:36 +04:00
addr_type = ipv6_addr_type ( addr6 ) ;
}
if ( ( addr_type & IPV6_ADDR_COMPATv4 ) = = 0 )
goto tx_error_icmp ;
dst = addr6 - > s6_addr32 [ 3 ] ;
}
# endif
else
goto tx_error ;
}
tos = tiph - > tos ;
2009-07-14 20:35:59 +04:00
if ( tos = = 1 ) {
tos = 0 ;
2005-04-17 02:20:36 +04:00
if ( skb - > protocol = = htons ( ETH_P_IP ) )
tos = old_iph - > tos ;
}
{
struct flowi fl = { . oif = tunnel - > parms . link ,
. nl_u = { . ip4_u =
{ . daddr = dst ,
. saddr = tiph - > saddr ,
. tos = RT_TOS ( tos ) } } ,
. proto = IPPROTO_GRE } ;
2008-04-16 12:10:44 +04:00
if ( ip_route_output_key ( dev_net ( dev ) , & rt , & fl ) ) {
2008-05-22 01:14:22 +04:00
stats - > tx_carrier_errors + + ;
2005-04-17 02:20:36 +04:00
goto tx_error ;
}
}
tdev = rt - > u . dst . dev ;
if ( tdev = = dev ) {
ip_rt_put ( rt ) ;
2008-05-22 01:14:22 +04:00
stats - > collisions + + ;
2005-04-17 02:20:36 +04:00
goto tx_error ;
}
df = tiph - > frag_off ;
if ( df )
2008-10-09 22:58:54 +04:00
mtu = dst_mtu ( & rt - > u . dst ) - dev - > hard_header_len - tunnel - > hlen ;
2005-04-17 02:20:36 +04:00
else
2009-06-02 09:19:30 +04:00
mtu = skb_dst ( skb ) ? dst_mtu ( skb_dst ( skb ) ) : dev - > mtu ;
2005-04-17 02:20:36 +04:00
2009-06-02 09:19:30 +04:00
if ( skb_dst ( skb ) )
skb_dst ( skb ) - > ops - > update_pmtu ( skb_dst ( skb ) , mtu ) ;
2005-04-17 02:20:36 +04:00
if ( skb - > protocol = = htons ( ETH_P_IP ) ) {
df | = ( old_iph - > frag_off & htons ( IP_DF ) ) ;
if ( ( old_iph - > frag_off & htons ( IP_DF ) ) & &
mtu < ntohs ( old_iph - > tot_len ) ) {
icmp_send ( skb , ICMP_DEST_UNREACH , ICMP_FRAG_NEEDED , htonl ( mtu ) ) ;
ip_rt_put ( rt ) ;
goto tx_error ;
}
}
# ifdef CONFIG_IPV6
else if ( skb - > protocol = = htons ( ETH_P_IPV6 ) ) {
2009-06-02 09:19:30 +04:00
struct rt6_info * rt6 = ( struct rt6_info * ) skb_dst ( skb ) ;
2005-04-17 02:20:36 +04:00
2009-06-02 09:19:30 +04:00
if ( rt6 & & mtu < dst_mtu ( skb_dst ( skb ) ) & & mtu > = IPV6_MIN_MTU ) {
2007-12-17 00:45:43 +03:00
if ( ( tunnel - > parms . iph . daddr & &
! ipv4_is_multicast ( tunnel - > parms . iph . daddr ) ) | |
2005-04-17 02:20:36 +04:00
rt6 - > rt6i_dst . plen = = 128 ) {
rt6 - > rt6i_flags | = RTF_MODIFIED ;
2009-06-02 09:19:30 +04:00
skb_dst ( skb ) - > metrics [ RTAX_MTU - 1 ] = mtu ;
2005-04-17 02:20:36 +04:00
}
}
if ( mtu > = IPV6_MIN_MTU & & mtu < skb - > len - tunnel - > hlen + gre_hlen ) {
icmpv6_send ( skb , ICMPV6_PKT_TOOBIG , 0 , mtu , dev ) ;
ip_rt_put ( rt ) ;
goto tx_error ;
}
}
# endif
if ( tunnel - > err_count > 0 ) {
2009-02-25 10:34:48 +03:00
if ( time_before ( jiffies ,
tunnel - > err_time + IPTUNNEL_ERR_TIMEO ) ) {
2005-04-17 02:20:36 +04:00
tunnel - > err_count - - ;
dst_link_failure ( skb ) ;
} else
tunnel - > err_count = 0 ;
}
max_headroom = LL_RESERVED_SPACE ( tdev ) + gre_hlen ;
2007-07-10 02:33:40 +04:00
if ( skb_headroom ( skb ) < max_headroom | | skb_shared ( skb ) | |
( skb_cloned ( skb ) & & ! skb_clone_writable ( skb , 0 ) ) ) {
2005-04-17 02:20:36 +04:00
struct sk_buff * new_skb = skb_realloc_headroom ( skb , max_headroom ) ;
if ( ! new_skb ) {
ip_rt_put ( rt ) ;
2009-10-05 11:11:22 +04:00
txq - > tx_dropped + + ;
2005-04-17 02:20:36 +04:00
dev_kfree_skb ( skb ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
if ( skb - > sk )
skb_set_owner_w ( new_skb , skb - > sk ) ;
dev_kfree_skb ( skb ) ;
skb = new_skb ;
2007-04-21 09:47:35 +04:00
old_iph = ip_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
}
2008-10-09 23:03:17 +04:00
skb_reset_transport_header ( skb ) ;
2007-04-11 07:46:21 +04:00
skb_push ( skb , gre_hlen ) ;
skb_reset_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
memset ( & ( IPCB ( skb ) - > opt ) , 0 , sizeof ( IPCB ( skb ) - > opt ) ) ;
2006-02-16 02:10:22 +03:00
IPCB ( skb ) - > flags & = ~ ( IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
IPSKB_REROUTED ) ;
2009-06-02 09:19:30 +04:00
skb_dst_drop ( skb ) ;
skb_dst_set ( skb , & rt - > u . dst ) ;
2005-04-17 02:20:36 +04:00
/*
* Push down and install the IPIP header .
*/
2007-04-21 09:47:35 +04:00
iph = ip_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
iph - > version = 4 ;
iph - > ihl = sizeof ( struct iphdr ) > > 2 ;
iph - > frag_off = df ;
iph - > protocol = IPPROTO_GRE ;
iph - > tos = ipgre_ecn_encapsulate ( tos , old_iph , skb ) ;
iph - > daddr = rt - > rt_dst ;
iph - > saddr = rt - > rt_src ;
if ( ( iph - > ttl = tiph - > ttl ) = = 0 ) {
if ( skb - > protocol = = htons ( ETH_P_IP ) )
iph - > ttl = old_iph - > ttl ;
# ifdef CONFIG_IPV6
else if ( skb - > protocol = = htons ( ETH_P_IPV6 ) )
2008-11-03 11:25:16 +03:00
iph - > ttl = ( ( struct ipv6hdr * ) old_iph ) - > hop_limit ;
2005-04-17 02:20:36 +04:00
# endif
else
iph - > ttl = dst_metric ( & rt - > u . dst , RTAX_HOPLIMIT ) ;
}
2008-10-09 23:00:17 +04:00
( ( __be16 * ) ( iph + 1 ) ) [ 0 ] = tunnel - > parms . o_flags ;
( ( __be16 * ) ( iph + 1 ) ) [ 1 ] = ( dev - > type = = ARPHRD_ETHER ) ?
htons ( ETH_P_TEB ) : skb - > protocol ;
2005-04-17 02:20:36 +04:00
if ( tunnel - > parms . o_flags & ( GRE_KEY | GRE_CSUM | GRE_SEQ ) ) {
2006-11-08 11:23:14 +03:00
__be32 * ptr = ( __be32 * ) ( ( ( u8 * ) iph ) + tunnel - > hlen - 4 ) ;
2005-04-17 02:20:36 +04:00
if ( tunnel - > parms . o_flags & GRE_SEQ ) {
+ + tunnel - > o_seqno ;
* ptr = htonl ( tunnel - > o_seqno ) ;
ptr - - ;
}
if ( tunnel - > parms . o_flags & GRE_KEY ) {
* ptr = tunnel - > parms . o_key ;
ptr - - ;
}
if ( tunnel - > parms . o_flags & GRE_CSUM ) {
* ptr = 0 ;
2006-11-15 08:36:54 +03:00
* ( __sum16 * ) ptr = ip_compute_csum ( ( void * ) ( iph + 1 ) , skb - > len - sizeof ( struct iphdr ) ) ;
2005-04-17 02:20:36 +04:00
}
}
nf_reset ( skb ) ;
IPTUNNEL_XMIT ( ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
tx_error_icmp :
dst_link_failure ( skb ) ;
tx_error :
stats - > tx_errors + + ;
dev_kfree_skb ( skb ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2008-10-09 22:59:32 +04:00
static int ipgre_tunnel_bind_dev ( struct net_device * dev )
2007-12-13 20:46:32 +03:00
{
struct net_device * tdev = NULL ;
struct ip_tunnel * tunnel ;
struct iphdr * iph ;
int hlen = LL_MAX_HEADER ;
int mtu = ETH_DATA_LEN ;
int addend = sizeof ( struct iphdr ) + 4 ;
tunnel = netdev_priv ( dev ) ;
iph = & tunnel - > parms . iph ;
2008-10-09 22:58:54 +04:00
/* Guess output device to choose reasonable mtu and needed_headroom */
2007-12-13 20:46:32 +03:00
if ( iph - > daddr ) {
struct flowi fl = { . oif = tunnel - > parms . link ,
. nl_u = { . ip4_u =
{ . daddr = iph - > daddr ,
. saddr = iph - > saddr ,
. tos = RT_TOS ( iph - > tos ) } } ,
. proto = IPPROTO_GRE } ;
struct rtable * rt ;
2008-04-16 12:10:44 +04:00
if ( ! ip_route_output_key ( dev_net ( dev ) , & rt , & fl ) ) {
2007-12-13 20:46:32 +03:00
tdev = rt - > u . dst . dev ;
ip_rt_put ( rt ) ;
}
2008-10-09 23:00:17 +04:00
if ( dev - > type ! = ARPHRD_ETHER )
dev - > flags | = IFF_POINTOPOINT ;
2007-12-13 20:46:32 +03:00
}
if ( ! tdev & & tunnel - > parms . link )
2008-04-16 12:10:44 +04:00
tdev = __dev_get_by_index ( dev_net ( dev ) , tunnel - > parms . link ) ;
2007-12-13 20:46:32 +03:00
if ( tdev ) {
2008-10-09 22:58:54 +04:00
hlen = tdev - > hard_header_len + tdev - > needed_headroom ;
2007-12-13 20:46:32 +03:00
mtu = tdev - > mtu ;
}
dev - > iflink = tunnel - > parms . link ;
/* Precalculate GRE options length */
if ( tunnel - > parms . o_flags & ( GRE_CSUM | GRE_KEY | GRE_SEQ ) ) {
if ( tunnel - > parms . o_flags & GRE_CSUM )
addend + = 4 ;
if ( tunnel - > parms . o_flags & GRE_KEY )
addend + = 4 ;
if ( tunnel - > parms . o_flags & GRE_SEQ )
addend + = 4 ;
}
2008-10-09 22:58:54 +04:00
dev - > needed_headroom = addend + hlen ;
2009-08-15 03:33:56 +04:00
mtu - = dev - > hard_header_len + addend ;
2008-10-09 22:59:32 +04:00
if ( mtu < 68 )
mtu = 68 ;
2007-12-13 20:46:32 +03:00
tunnel - > hlen = addend ;
2008-10-09 22:59:32 +04:00
return mtu ;
2007-12-13 20:46:32 +03:00
}
2005-04-17 02:20:36 +04:00
static int
ipgre_tunnel_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
{
int err = 0 ;
struct ip_tunnel_parm p ;
struct ip_tunnel * t ;
2008-04-16 12:09:22 +04:00
struct net * net = dev_net ( dev ) ;
struct ipgre_net * ign = net_generic ( net , ipgre_net_id ) ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case SIOCGETTUNNEL :
t = NULL ;
2008-04-16 12:10:05 +04:00
if ( dev = = ign - > fb_tunnel_dev ) {
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( & p , ifr - > ifr_ifru . ifru_data , sizeof ( p ) ) ) {
err = - EFAULT ;
break ;
}
2008-04-16 12:09:22 +04:00
t = ipgre_tunnel_locate ( net , & p , 0 ) ;
2005-04-17 02:20:36 +04:00
}
if ( t = = NULL )
2006-01-09 09:05:26 +03:00
t = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04: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_GRE | |
p . iph . ihl ! = 5 | | ( p . iph . frag_off & htons ( ~ IP_DF ) ) | |
( ( p . i_flags | p . o_flags ) & ( GRE_VERSION | GRE_ROUTING ) ) )
goto done ;
if ( p . iph . ttl )
p . iph . frag_off | = htons ( IP_DF ) ;
if ( ! ( p . i_flags & GRE_KEY ) )
p . i_key = 0 ;
if ( ! ( p . o_flags & GRE_KEY ) )
p . o_key = 0 ;
2008-04-16 12:09:22 +04:00
t = ipgre_tunnel_locate ( net , & p , cmd = = SIOCADDTUNNEL ) ;
2005-04-17 02:20:36 +04:00
2008-04-16 12:10:05 +04:00
if ( dev ! = ign - > fb_tunnel_dev & & cmd = = SIOCCHGTUNNEL ) {
2005-04-17 02:20:36 +04:00
if ( t ! = NULL ) {
if ( t - > dev ! = dev ) {
err = - EEXIST ;
break ;
}
} else {
2008-11-03 11:25:16 +03:00
unsigned nflags = 0 ;
2005-04-17 02:20:36 +04:00
2006-01-09 09:05:26 +03:00
t = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2007-12-17 00:45:43 +03:00
if ( ipv4_is_multicast ( p . iph . daddr ) )
2005-04-17 02:20:36 +04:00
nflags = IFF_BROADCAST ;
else if ( p . iph . daddr )
nflags = IFF_POINTOPOINT ;
if ( ( dev - > flags ^ nflags ) & ( IFF_POINTOPOINT | IFF_BROADCAST ) ) {
err = - EINVAL ;
break ;
}
2008-04-16 12:09:22 +04:00
ipgre_tunnel_unlink ( ign , t ) ;
2005-04-17 02:20:36 +04:00
t - > parms . iph . saddr = p . iph . saddr ;
t - > parms . iph . daddr = p . iph . daddr ;
t - > parms . i_key = p . i_key ;
t - > parms . o_key = p . o_key ;
memcpy ( dev - > dev_addr , & p . iph . saddr , 4 ) ;
memcpy ( dev - > broadcast , & p . iph . daddr , 4 ) ;
2008-04-16 12:09:22 +04:00
ipgre_tunnel_link ( ign , t ) ;
2005-04-17 02:20:36 +04: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 ;
t - > parms . iph . frag_off = p . iph . frag_off ;
2007-12-13 20:46:32 +03:00
if ( t - > parms . link ! = p . link ) {
t - > parms . link = p . link ;
2008-10-09 22:59:32 +04:00
dev - > mtu = ipgre_tunnel_bind_dev ( dev ) ;
2007-12-13 20:46:32 +03:00
netdev_state_change ( dev ) ;
}
2005-04-17 02:20:36 +04: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 12:10:05 +04:00
if ( dev = = ign - > fb_tunnel_dev ) {
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
if ( copy_from_user ( & p , ifr - > ifr_ifru . ifru_data , sizeof ( p ) ) )
goto done ;
err = - ENOENT ;
2008-04-16 12:09:22 +04:00
if ( ( t = ipgre_tunnel_locate ( net , & p , 0 ) ) = = NULL )
2005-04-17 02:20:36 +04:00
goto done ;
err = - EPERM ;
2008-04-16 12:10:05 +04:00
if ( t = = netdev_priv ( ign - > fb_tunnel_dev ) )
2005-04-17 02:20:36 +04:00
goto done ;
dev = t - > dev ;
}
2007-02-07 11:09:58 +03:00
unregister_netdevice ( dev ) ;
err = 0 ;
2005-04-17 02:20:36 +04:00
break ;
default :
err = - EINVAL ;
}
done :
return err ;
}
static int ipgre_tunnel_change_mtu ( struct net_device * dev , int new_mtu )
{
2006-01-09 09:05:26 +03:00
struct ip_tunnel * tunnel = netdev_priv ( dev ) ;
2008-10-09 22:58:54 +04:00
if ( new_mtu < 68 | |
new_mtu > 0xFFF8 - dev - > hard_header_len - tunnel - > hlen )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
dev - > mtu = new_mtu ;
return 0 ;
}
/* Nice toy. Unfortunately, useless in real life :-)
It allows to construct virtual multiprotocol broadcast " LAN "
over the Internet , provided multicast routing is tuned .
I have no idea was this bicycle invented before me ,
so that I had to set ARPHRD_IPGRE to a random value .
I have an impression , that Cisco could make something similar ,
but this feature is apparently missing in IOS < = 11.2 ( 8 ) .
2007-02-09 17:24:47 +03:00
2005-04-17 02:20:36 +04:00
I set up 10.66 .66 / 24 and fec0 : 6666 : 6666 : : 0 / 96 as virtual networks
with broadcast 224.66 .66 .66 . If you have access to mbone , play with me : - )
ping - t 255 224.66 .66 .66
If nobody answers , mbone does not work .
ip tunnel add Universe mode gre remote 224.66 .66 .66 local < Your_real_addr > ttl 255
ip addr add 10.66 .66 . < somewhat > / 24 dev Universe
ifconfig Universe up
ifconfig Universe add fe80 : : < Your_real_addr > / 10
ifconfig Universe add fec0 : 6666 : 6666 : : < Your_real_addr > / 96
ftp 10.66 .66 .66
. . .
ftp fec0 : 6666 : 6666 : : 193.233 .7 .65
. . .
*/
2007-10-09 12:40:57 +04:00
static int ipgre_header ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type ,
const void * daddr , const void * saddr , unsigned len )
2005-04-17 02:20:36 +04:00
{
2006-01-09 09:05:26 +03:00
struct ip_tunnel * t = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
struct iphdr * iph = ( struct iphdr * ) skb_push ( skb , t - > hlen ) ;
2006-11-08 11:23:14 +03:00
__be16 * p = ( __be16 * ) ( iph + 1 ) ;
2005-04-17 02:20:36 +04:00
memcpy ( iph , & t - > parms . iph , sizeof ( struct iphdr ) ) ;
p [ 0 ] = t - > parms . o_flags ;
p [ 1 ] = htons ( type ) ;
/*
2007-02-09 17:24:47 +03:00
* Set the source hardware address .
2005-04-17 02:20:36 +04:00
*/
2007-02-09 17:24:47 +03:00
2005-04-17 02:20:36 +04:00
if ( saddr )
memcpy ( & iph - > saddr , saddr , 4 ) ;
if ( daddr ) {
memcpy ( & iph - > daddr , daddr , 4 ) ;
return t - > hlen ;
}
2007-12-17 00:45:43 +03:00
if ( iph - > daddr & & ! ipv4_is_multicast ( iph - > daddr ) )
2005-04-17 02:20:36 +04:00
return t - > hlen ;
2007-02-09 17:24:47 +03:00
2005-04-17 02:20:36 +04:00
return - t - > hlen ;
}
2007-10-24 07:31:53 +04:00
static int ipgre_header_parse ( const struct sk_buff * skb , unsigned char * haddr )
{
2008-11-03 11:25:16 +03:00
struct iphdr * iph = ( struct iphdr * ) skb_mac_header ( skb ) ;
2007-10-24 07:31:53 +04:00
memcpy ( haddr , & iph - > saddr , 4 ) ;
return 4 ;
}
2007-10-09 12:40:57 +04:00
static const struct header_ops ipgre_header_ops = {
. create = ipgre_header ,
2007-10-24 07:31:53 +04:00
. parse = ipgre_header_parse ,
2007-10-09 12:40:57 +04:00
} ;
2007-10-24 07:31:53 +04:00
# ifdef CONFIG_NET_IPGRE_BROADCAST
2005-04-17 02:20:36 +04:00
static int ipgre_open ( struct net_device * dev )
{
2006-01-09 09:05:26 +03:00
struct ip_tunnel * t = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
2007-12-17 00:45:43 +03:00
if ( ipv4_is_multicast ( t - > parms . iph . daddr ) ) {
2005-04-17 02:20:36 +04:00
struct flowi fl = { . oif = t - > parms . link ,
. nl_u = { . ip4_u =
{ . daddr = t - > parms . iph . daddr ,
. saddr = t - > parms . iph . saddr ,
. tos = RT_TOS ( t - > parms . iph . tos ) } } ,
. proto = IPPROTO_GRE } ;
struct rtable * rt ;
2008-04-16 12:10:44 +04:00
if ( ip_route_output_key ( dev_net ( dev ) , & rt , & fl ) )
2005-04-17 02:20:36 +04:00
return - EADDRNOTAVAIL ;
dev = rt - > u . dst . dev ;
ip_rt_put ( rt ) ;
2005-10-04 01:35:55 +04:00
if ( __in_dev_get_rtnl ( dev ) = = NULL )
2005-04-17 02:20:36 +04:00
return - EADDRNOTAVAIL ;
t - > mlink = dev - > ifindex ;
2005-10-04 01:35:55 +04:00
ip_mc_inc_group ( __in_dev_get_rtnl ( dev ) , t - > parms . iph . daddr ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static int ipgre_close ( struct net_device * dev )
{
2006-01-09 09:05:26 +03:00
struct ip_tunnel * t = netdev_priv ( dev ) ;
2008-11-21 07:34:29 +03:00
2007-12-17 00:45:43 +03:00
if ( ipv4_is_multicast ( t - > parms . iph . daddr ) & & t - > mlink ) {
2008-01-22 04:32:38 +03:00
struct in_device * in_dev ;
2008-03-25 15:47:49 +03:00
in_dev = inetdev_by_index ( dev_net ( dev ) , t - > mlink ) ;
2005-04-17 02:20:36 +04:00
if ( in_dev ) {
ip_mc_dec_group ( in_dev , t - > parms . iph . daddr ) ;
in_dev_put ( in_dev ) ;
}
}
return 0 ;
}
# endif
2008-11-21 07:34:29 +03:00
static const struct net_device_ops ipgre_netdev_ops = {
. ndo_init = ipgre_tunnel_init ,
. ndo_uninit = ipgre_tunnel_uninit ,
# ifdef CONFIG_NET_IPGRE_BROADCAST
. ndo_open = ipgre_open ,
. ndo_stop = ipgre_close ,
# endif
. ndo_start_xmit = ipgre_tunnel_xmit ,
. ndo_do_ioctl = ipgre_tunnel_ioctl ,
. ndo_change_mtu = ipgre_tunnel_change_mtu ,
} ;
2005-04-17 02:20:36 +04:00
static void ipgre_tunnel_setup ( struct net_device * dev )
{
2008-11-21 07:34:29 +03:00
dev - > netdev_ops = & ipgre_netdev_ops ;
2005-04-17 02:20:36 +04:00
dev - > destructor = free_netdev ;
dev - > type = ARPHRD_IPGRE ;
2008-10-09 22:58:54 +04:00
dev - > needed_headroom = LL_MAX_HEADER + sizeof ( struct iphdr ) + 4 ;
2006-01-06 03:35:42 +03:00
dev - > mtu = ETH_DATA_LEN - sizeof ( struct iphdr ) - 4 ;
2005-04-17 02:20:36 +04:00
dev - > flags = IFF_NOARP ;
dev - > iflink = 0 ;
dev - > addr_len = 4 ;
2008-04-16 12:11:13 +04:00
dev - > features | = NETIF_F_NETNS_LOCAL ;
2009-05-29 02:35:10 +04:00
dev - > priv_flags & = ~ IFF_XMIT_DST_RELEASE ;
2005-04-17 02:20:36 +04:00
}
static int ipgre_tunnel_init ( struct net_device * dev )
{
struct ip_tunnel * tunnel ;
struct iphdr * iph ;
2006-01-09 09:05:26 +03:00
tunnel = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
iph = & tunnel - > parms . iph ;
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 ) ;
if ( iph - > daddr ) {
# ifdef CONFIG_NET_IPGRE_BROADCAST
2007-12-17 00:45:43 +03:00
if ( ipv4_is_multicast ( iph - > daddr ) ) {
2005-04-17 02:20:36 +04:00
if ( ! iph - > saddr )
return - EINVAL ;
dev - > flags = IFF_BROADCAST ;
2007-10-09 12:40:57 +04:00
dev - > header_ops = & ipgre_header_ops ;
2005-04-17 02:20:36 +04:00
}
# endif
2007-12-13 20:46:32 +03:00
} else
2007-10-24 07:31:53 +04:00
dev - > header_ops = & ipgre_header_ops ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-11-21 07:34:29 +03:00
static void ipgre_fb_tunnel_init ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2006-01-09 09:05:26 +03:00
struct ip_tunnel * tunnel = netdev_priv ( dev ) ;
2005-04-17 02:20:36 +04:00
struct iphdr * iph = & tunnel - > parms . iph ;
2008-04-16 12:10:26 +04:00
struct ipgre_net * ign = net_generic ( dev_net ( dev ) , ipgre_net_id ) ;
2005-04-17 02:20:36 +04:00
tunnel - > dev = dev ;
strcpy ( tunnel - > parms . name , dev - > name ) ;
iph - > version = 4 ;
iph - > protocol = IPPROTO_GRE ;
iph - > ihl = 5 ;
tunnel - > hlen = sizeof ( struct iphdr ) + 4 ;
dev_hold ( dev ) ;
2008-04-16 12:10:26 +04:00
ign - > tunnels_wc [ 0 ] = tunnel ;
2005-04-17 02:20:36 +04:00
}
2009-09-14 16:21:47 +04:00
static const struct net_protocol ipgre_protocol = {
2005-04-17 02:20:36 +04:00
. handler = ipgre_rcv ,
. err_handler = ipgre_err ,
2008-04-16 12:11:36 +04:00
. netns_ok = 1 ,
2005-04-17 02:20:36 +04:00
} ;
2008-04-16 12:10:26 +04:00
static void ipgre_destroy_tunnels ( struct ipgre_net * ign )
{
int prio ;
for ( prio = 0 ; prio < 4 ; prio + + ) {
int h ;
for ( h = 0 ; h < HASH_SIZE ; h + + ) {
struct ip_tunnel * t ;
while ( ( t = ign - > tunnels [ prio ] [ h ] ) ! = NULL )
unregister_netdevice ( t - > dev ) ;
}
}
}
2008-04-16 12:08:53 +04:00
static int ipgre_init_net ( struct net * net )
{
int err ;
struct ipgre_net * ign ;
err = - ENOMEM ;
2008-04-16 12:10:26 +04:00
ign = kzalloc ( sizeof ( struct ipgre_net ) , GFP_KERNEL ) ;
2008-04-16 12:08:53 +04:00
if ( ign = = NULL )
goto err_alloc ;
err = net_assign_generic ( net , ipgre_net_id , ign ) ;
if ( err < 0 )
goto err_assign ;
2008-04-16 12:10:05 +04:00
ign - > fb_tunnel_dev = alloc_netdev ( sizeof ( struct ip_tunnel ) , " gre0 " ,
ipgre_tunnel_setup ) ;
if ( ! ign - > fb_tunnel_dev ) {
err = - ENOMEM ;
goto err_alloc_dev ;
}
2008-11-24 04:26:26 +03:00
dev_net_set ( ign - > fb_tunnel_dev , net ) ;
2008-04-16 12:10:05 +04:00
2008-11-21 07:34:29 +03:00
ipgre_fb_tunnel_init ( ign - > fb_tunnel_dev ) ;
2008-10-09 22:59:55 +04:00
ign - > fb_tunnel_dev - > rtnl_link_ops = & ipgre_link_ops ;
2008-04-16 12:10:05 +04:00
if ( ( err = register_netdev ( ign - > fb_tunnel_dev ) ) )
goto err_reg_dev ;
2008-04-16 12:08:53 +04:00
return 0 ;
2008-04-16 12:10:05 +04:00
err_reg_dev :
free_netdev ( ign - > fb_tunnel_dev ) ;
err_alloc_dev :
/* nothing */
2008-04-16 12:08:53 +04:00
err_assign :
kfree ( ign ) ;
err_alloc :
return err ;
}
static void ipgre_exit_net ( struct net * net )
{
struct ipgre_net * ign ;
ign = net_generic ( net , ipgre_net_id ) ;
2008-04-16 12:10:05 +04:00
rtnl_lock ( ) ;
2008-04-16 12:10:26 +04:00
ipgre_destroy_tunnels ( ign ) ;
2008-04-16 12:10:05 +04:00
rtnl_unlock ( ) ;
2008-04-16 12:08:53 +04:00
kfree ( ign ) ;
}
static struct pernet_operations ipgre_net_ops = {
. init = ipgre_init_net ,
. exit = ipgre_exit_net ,
} ;
2005-04-17 02:20:36 +04:00
2008-10-09 22:59:55 +04:00
static int ipgre_tunnel_validate ( struct nlattr * tb [ ] , struct nlattr * data [ ] )
{
__be16 flags ;
if ( ! data )
return 0 ;
flags = 0 ;
if ( data [ IFLA_GRE_IFLAGS ] )
flags | = nla_get_be16 ( data [ IFLA_GRE_IFLAGS ] ) ;
if ( data [ IFLA_GRE_OFLAGS ] )
flags | = nla_get_be16 ( data [ IFLA_GRE_OFLAGS ] ) ;
if ( flags & ( GRE_VERSION | GRE_ROUTING ) )
return - EINVAL ;
return 0 ;
}
2008-10-09 23:00:17 +04:00
static int ipgre_tap_validate ( struct nlattr * tb [ ] , struct nlattr * data [ ] )
{
__be32 daddr ;
if ( tb [ IFLA_ADDRESS ] ) {
if ( nla_len ( tb [ IFLA_ADDRESS ] ) ! = ETH_ALEN )
return - EINVAL ;
if ( ! is_valid_ether_addr ( nla_data ( tb [ IFLA_ADDRESS ] ) ) )
return - EADDRNOTAVAIL ;
}
if ( ! data )
goto out ;
if ( data [ IFLA_GRE_REMOTE ] ) {
memcpy ( & daddr , nla_data ( data [ IFLA_GRE_REMOTE ] ) , 4 ) ;
if ( ! daddr )
return - EINVAL ;
}
out :
return ipgre_tunnel_validate ( tb , data ) ;
}
2008-10-09 22:59:55 +04:00
static void ipgre_netlink_parms ( struct nlattr * data [ ] ,
struct ip_tunnel_parm * parms )
{
2008-10-11 23:20:15 +04:00
memset ( parms , 0 , sizeof ( * parms ) ) ;
2008-10-09 22:59:55 +04:00
parms - > iph . protocol = IPPROTO_GRE ;
if ( ! data )
return ;
if ( data [ IFLA_GRE_LINK ] )
parms - > link = nla_get_u32 ( data [ IFLA_GRE_LINK ] ) ;
if ( data [ IFLA_GRE_IFLAGS ] )
parms - > i_flags = nla_get_be16 ( data [ IFLA_GRE_IFLAGS ] ) ;
if ( data [ IFLA_GRE_OFLAGS ] )
parms - > o_flags = nla_get_be16 ( data [ IFLA_GRE_OFLAGS ] ) ;
if ( data [ IFLA_GRE_IKEY ] )
parms - > i_key = nla_get_be32 ( data [ IFLA_GRE_IKEY ] ) ;
if ( data [ IFLA_GRE_OKEY ] )
parms - > o_key = nla_get_be32 ( data [ IFLA_GRE_OKEY ] ) ;
if ( data [ IFLA_GRE_LOCAL ] )
2008-10-10 23:11:06 +04:00
parms - > iph . saddr = nla_get_be32 ( data [ IFLA_GRE_LOCAL ] ) ;
2008-10-09 22:59:55 +04:00
if ( data [ IFLA_GRE_REMOTE ] )
2008-10-10 23:11:06 +04:00
parms - > iph . daddr = nla_get_be32 ( data [ IFLA_GRE_REMOTE ] ) ;
2008-10-09 22:59:55 +04:00
if ( data [ IFLA_GRE_TTL ] )
parms - > iph . ttl = nla_get_u8 ( data [ IFLA_GRE_TTL ] ) ;
if ( data [ IFLA_GRE_TOS ] )
parms - > iph . tos = nla_get_u8 ( data [ IFLA_GRE_TOS ] ) ;
if ( ! data [ IFLA_GRE_PMTUDISC ] | | nla_get_u8 ( data [ IFLA_GRE_PMTUDISC ] ) )
parms - > iph . frag_off = htons ( IP_DF ) ;
}
2008-10-09 23:00:17 +04:00
static int ipgre_tap_init ( struct net_device * dev )
{
struct ip_tunnel * tunnel ;
tunnel = netdev_priv ( dev ) ;
tunnel - > dev = dev ;
strcpy ( tunnel - > parms . name , dev - > name ) ;
ipgre_tunnel_bind_dev ( dev ) ;
return 0 ;
}
2008-11-21 07:34:29 +03:00
static const struct net_device_ops ipgre_tap_netdev_ops = {
. ndo_init = ipgre_tap_init ,
. ndo_uninit = ipgre_tunnel_uninit ,
. ndo_start_xmit = ipgre_tunnel_xmit ,
. ndo_set_mac_address = eth_mac_addr ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_change_mtu = ipgre_tunnel_change_mtu ,
} ;
2008-10-09 23:00:17 +04:00
static void ipgre_tap_setup ( struct net_device * dev )
{
ether_setup ( dev ) ;
2008-11-21 07:34:29 +03:00
dev - > netdev_ops = & ipgre_netdev_ops ;
2008-10-09 23:00:17 +04:00
dev - > destructor = free_netdev ;
dev - > iflink = 0 ;
dev - > features | = NETIF_F_NETNS_LOCAL ;
}
2008-10-09 22:59:55 +04:00
static int ipgre_newlink ( struct net_device * dev , struct nlattr * tb [ ] ,
struct nlattr * data [ ] )
{
struct ip_tunnel * nt ;
struct net * net = dev_net ( dev ) ;
struct ipgre_net * ign = net_generic ( net , ipgre_net_id ) ;
int mtu ;
int err ;
nt = netdev_priv ( dev ) ;
ipgre_netlink_parms ( data , & nt - > parms ) ;
2008-10-09 23:00:17 +04:00
if ( ipgre_tunnel_find ( net , & nt - > parms , dev - > type ) )
2008-10-09 22:59:55 +04:00
return - EEXIST ;
2008-10-09 23:00:17 +04:00
if ( dev - > type = = ARPHRD_ETHER & & ! tb [ IFLA_ADDRESS ] )
random_ether_addr ( dev - > dev_addr ) ;
2008-10-09 22:59:55 +04:00
mtu = ipgre_tunnel_bind_dev ( dev ) ;
if ( ! tb [ IFLA_MTU ] )
dev - > mtu = mtu ;
err = register_netdevice ( dev ) ;
if ( err )
goto out ;
dev_hold ( dev ) ;
ipgre_tunnel_link ( ign , nt ) ;
out :
return err ;
}
static int ipgre_changelink ( struct net_device * dev , struct nlattr * tb [ ] ,
struct nlattr * data [ ] )
{
struct ip_tunnel * t , * nt ;
struct net * net = dev_net ( dev ) ;
struct ipgre_net * ign = net_generic ( net , ipgre_net_id ) ;
struct ip_tunnel_parm p ;
int mtu ;
if ( dev = = ign - > fb_tunnel_dev )
return - EINVAL ;
nt = netdev_priv ( dev ) ;
ipgre_netlink_parms ( data , & p ) ;
t = ipgre_tunnel_locate ( net , & p , 0 ) ;
if ( t ) {
if ( t - > dev ! = dev )
return - EEXIST ;
} else {
unsigned nflags = 0 ;
t = nt ;
if ( ipv4_is_multicast ( p . iph . daddr ) )
nflags = IFF_BROADCAST ;
else if ( p . iph . daddr )
nflags = IFF_POINTOPOINT ;
if ( ( dev - > flags ^ nflags ) &
( IFF_POINTOPOINT | IFF_BROADCAST ) )
return - EINVAL ;
ipgre_tunnel_unlink ( ign , t ) ;
t - > parms . iph . saddr = p . iph . saddr ;
t - > parms . iph . daddr = p . iph . daddr ;
t - > parms . i_key = p . i_key ;
memcpy ( dev - > dev_addr , & p . iph . saddr , 4 ) ;
memcpy ( dev - > broadcast , & p . iph . daddr , 4 ) ;
ipgre_tunnel_link ( ign , t ) ;
netdev_state_change ( dev ) ;
}
t - > parms . o_key = p . o_key ;
t - > parms . iph . ttl = p . iph . ttl ;
t - > parms . iph . tos = p . iph . tos ;
t - > parms . iph . frag_off = p . iph . frag_off ;
if ( t - > parms . link ! = p . link ) {
t - > parms . link = p . link ;
mtu = ipgre_tunnel_bind_dev ( dev ) ;
if ( ! tb [ IFLA_MTU ] )
dev - > mtu = mtu ;
netdev_state_change ( dev ) ;
}
return 0 ;
}
static size_t ipgre_get_size ( const struct net_device * dev )
{
return
/* IFLA_GRE_LINK */
nla_total_size ( 4 ) +
/* IFLA_GRE_IFLAGS */
nla_total_size ( 2 ) +
/* IFLA_GRE_OFLAGS */
nla_total_size ( 2 ) +
/* IFLA_GRE_IKEY */
nla_total_size ( 4 ) +
/* IFLA_GRE_OKEY */
nla_total_size ( 4 ) +
/* IFLA_GRE_LOCAL */
nla_total_size ( 4 ) +
/* IFLA_GRE_REMOTE */
nla_total_size ( 4 ) +
/* IFLA_GRE_TTL */
nla_total_size ( 1 ) +
/* IFLA_GRE_TOS */
nla_total_size ( 1 ) +
/* IFLA_GRE_PMTUDISC */
nla_total_size ( 1 ) +
0 ;
}
static int ipgre_fill_info ( struct sk_buff * skb , const struct net_device * dev )
{
struct ip_tunnel * t = netdev_priv ( dev ) ;
struct ip_tunnel_parm * p = & t - > parms ;
NLA_PUT_U32 ( skb , IFLA_GRE_LINK , p - > link ) ;
NLA_PUT_BE16 ( skb , IFLA_GRE_IFLAGS , p - > i_flags ) ;
NLA_PUT_BE16 ( skb , IFLA_GRE_OFLAGS , p - > o_flags ) ;
2008-10-10 23:10:30 +04:00
NLA_PUT_BE32 ( skb , IFLA_GRE_IKEY , p - > i_key ) ;
NLA_PUT_BE32 ( skb , IFLA_GRE_OKEY , p - > o_key ) ;
2008-10-10 23:11:06 +04:00
NLA_PUT_BE32 ( skb , IFLA_GRE_LOCAL , p - > iph . saddr ) ;
NLA_PUT_BE32 ( skb , IFLA_GRE_REMOTE , p - > iph . daddr ) ;
2008-10-09 22:59:55 +04:00
NLA_PUT_U8 ( skb , IFLA_GRE_TTL , p - > iph . ttl ) ;
NLA_PUT_U8 ( skb , IFLA_GRE_TOS , p - > iph . tos ) ;
NLA_PUT_U8 ( skb , IFLA_GRE_PMTUDISC , ! ! ( p - > iph . frag_off & htons ( IP_DF ) ) ) ;
return 0 ;
nla_put_failure :
return - EMSGSIZE ;
}
static const struct nla_policy ipgre_policy [ IFLA_GRE_MAX + 1 ] = {
[ IFLA_GRE_LINK ] = { . type = NLA_U32 } ,
[ IFLA_GRE_IFLAGS ] = { . type = NLA_U16 } ,
[ IFLA_GRE_OFLAGS ] = { . type = NLA_U16 } ,
[ IFLA_GRE_IKEY ] = { . type = NLA_U32 } ,
[ IFLA_GRE_OKEY ] = { . type = NLA_U32 } ,
2008-10-10 23:11:06 +04:00
[ IFLA_GRE_LOCAL ] = { . len = FIELD_SIZEOF ( struct iphdr , saddr ) } ,
[ IFLA_GRE_REMOTE ] = { . len = FIELD_SIZEOF ( struct iphdr , daddr ) } ,
2008-10-09 22:59:55 +04:00
[ IFLA_GRE_TTL ] = { . type = NLA_U8 } ,
[ IFLA_GRE_TOS ] = { . type = NLA_U8 } ,
[ IFLA_GRE_PMTUDISC ] = { . type = NLA_U8 } ,
} ;
static struct rtnl_link_ops ipgre_link_ops __read_mostly = {
. kind = " gre " ,
. maxtype = IFLA_GRE_MAX ,
. policy = ipgre_policy ,
. priv_size = sizeof ( struct ip_tunnel ) ,
. setup = ipgre_tunnel_setup ,
. validate = ipgre_tunnel_validate ,
. newlink = ipgre_newlink ,
. changelink = ipgre_changelink ,
. get_size = ipgre_get_size ,
. fill_info = ipgre_fill_info ,
} ;
2008-10-09 23:00:17 +04:00
static struct rtnl_link_ops ipgre_tap_ops __read_mostly = {
. kind = " gretap " ,
. maxtype = IFLA_GRE_MAX ,
. policy = ipgre_policy ,
. priv_size = sizeof ( struct ip_tunnel ) ,
. setup = ipgre_tap_setup ,
. validate = ipgre_tap_validate ,
. newlink = ipgre_newlink ,
. changelink = ipgre_changelink ,
. get_size = ipgre_get_size ,
. fill_info = ipgre_fill_info ,
} ;
2005-04-17 02:20:36 +04:00
/*
* And now the modules code and kernel interface .
*/
static int __init ipgre_init ( void )
{
int err ;
printk ( KERN_INFO " GRE over IPv4 tunneling driver \n " ) ;
if ( inet_add_protocol ( & ipgre_protocol , IPPROTO_GRE ) < 0 ) {
printk ( KERN_INFO " ipgre init: can't add protocol \n " ) ;
return - EAGAIN ;
}
2008-04-16 12:08:53 +04:00
err = register_pernet_gen_device ( & ipgre_net_id , & ipgre_net_ops ) ;
if ( err < 0 )
2008-10-09 22:59:55 +04:00
goto gen_device_failed ;
2008-04-16 12:10:05 +04:00
2008-10-09 22:59:55 +04:00
err = rtnl_link_register ( & ipgre_link_ops ) ;
if ( err < 0 )
goto rtnl_link_failed ;
2008-10-09 23:00:17 +04:00
err = rtnl_link_register ( & ipgre_tap_ops ) ;
if ( err < 0 )
goto tap_ops_failed ;
2008-10-09 22:59:55 +04:00
out :
2005-04-17 02:20:36 +04:00
return err ;
2008-10-09 22:59:55 +04:00
2008-10-09 23:00:17 +04:00
tap_ops_failed :
rtnl_link_unregister ( & ipgre_link_ops ) ;
2008-10-09 22:59:55 +04:00
rtnl_link_failed :
unregister_pernet_gen_device ( ipgre_net_id , & ipgre_net_ops ) ;
gen_device_failed :
inet_del_protocol ( & ipgre_protocol , IPPROTO_GRE ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2005-07-31 04:46:44 +04:00
static void __exit ipgre_fini ( void )
2005-04-17 02:20:36 +04:00
{
2008-10-09 23:00:17 +04:00
rtnl_link_unregister ( & ipgre_tap_ops ) ;
2008-10-09 22:59:55 +04:00
rtnl_link_unregister ( & ipgre_link_ops ) ;
unregister_pernet_gen_device ( ipgre_net_id , & ipgre_net_ops ) ;
2005-04-17 02:20:36 +04:00
if ( inet_del_protocol ( & ipgre_protocol , IPPROTO_GRE ) < 0 )
printk ( KERN_INFO " ipgre close: can't remove protocol \n " ) ;
}
module_init ( ipgre_init ) ;
module_exit ( ipgre_fini ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-10-10 23:11:06 +04:00
MODULE_ALIAS_RTNL_LINK ( " gre " ) ;
MODULE_ALIAS_RTNL_LINK ( " gretap " ) ;