2006-05-28 10:05:54 +04:00
/*
* xfrm6_mode_tunnel . c - Tunnel mode encapsulation for IPv6 .
*
* Copyright ( C ) 2002 USAGI / WIDE Project
* Copyright ( c ) 2004 - 2006 Herbert Xu < herbert @ gondor . apana . org . au >
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/skbuff.h>
# include <linux/stringify.h>
# include <net/dsfield.h>
# include <net/dst.h>
# include <net/inet_ecn.h>
# include <net/ipv6.h>
# include <net/xfrm.h>
static inline void ipip6_ecn_decapsulate ( struct sk_buff * skb )
{
2007-04-26 04:54:47 +04:00
struct ipv6hdr * outer_iph = ipv6_hdr ( skb ) ;
2007-04-11 08:06:25 +04:00
struct ipv6hdr * inner_iph = ipipv6_hdr ( skb ) ;
2006-05-28 10:05:54 +04:00
if ( INET_ECN_is_ce ( ipv6_get_dsfield ( outer_iph ) ) )
IP6_ECN_set_ce ( inner_iph ) ;
}
2007-02-07 01:27:32 +03:00
static inline void ip6ip_ecn_decapsulate ( struct sk_buff * skb )
{
2007-04-26 04:54:47 +04:00
if ( INET_ECN_is_ce ( ipv6_get_dsfield ( ipv6_hdr ( skb ) ) ) )
2007-04-26 05:02:22 +04:00
IP_ECN_set_ce ( ipip_hdr ( skb ) ) ;
2007-02-07 01:27:32 +03:00
}
2006-05-28 10:05:54 +04:00
/* Add encapsulation header.
*
2007-10-11 02:45:52 +04:00
* The top IP header will be constructed per RFC 2401.
2006-05-28 10:05:54 +04:00
*/
2006-09-01 04:42:59 +04:00
static int xfrm6_tunnel_output ( struct xfrm_state * x , struct sk_buff * skb )
2006-05-28 10:05:54 +04:00
{
struct dst_entry * dst = skb - > dst ;
2007-02-07 01:27:32 +03:00
struct xfrm_dst * xdst = ( struct xfrm_dst * ) dst ;
2006-05-28 10:05:54 +04:00
struct ipv6hdr * iph , * top_iph ;
int dsfield ;
2007-04-26 04:54:47 +04:00
iph = ipv6_hdr ( skb ) ;
2006-05-28 10:05:54 +04:00
2007-10-11 02:44:06 +04:00
skb_set_network_header ( skb , - x - > props . header_len ) ;
2007-10-11 02:44:44 +04:00
skb - > mac_header = skb - > network_header +
offsetof ( struct ipv6hdr , nexthdr ) ;
skb - > transport_header = skb - > network_header + sizeof ( * iph ) ;
2007-04-26 04:54:47 +04:00
top_iph = ipv6_hdr ( skb ) ;
2006-05-28 10:05:54 +04:00
top_iph - > version = 6 ;
2007-02-07 01:27:32 +03:00
if ( xdst - > route - > ops - > family = = AF_INET6 ) {
top_iph - > priority = iph - > priority ;
top_iph - > flow_lbl [ 0 ] = iph - > flow_lbl [ 0 ] ;
top_iph - > flow_lbl [ 1 ] = iph - > flow_lbl [ 1 ] ;
top_iph - > flow_lbl [ 2 ] = iph - > flow_lbl [ 2 ] ;
top_iph - > nexthdr = IPPROTO_IPV6 ;
} else {
top_iph - > priority = 0 ;
top_iph - > flow_lbl [ 0 ] = 0 ;
top_iph - > flow_lbl [ 1 ] = 0 ;
top_iph - > flow_lbl [ 2 ] = 0 ;
top_iph - > nexthdr = IPPROTO_IPIP ;
}
2006-05-28 10:05:54 +04:00
dsfield = ipv6_get_dsfield ( top_iph ) ;
dsfield = INET_ECN_encapsulate ( dsfield , dsfield ) ;
if ( x - > props . flags & XFRM_STATE_NOECN )
dsfield & = ~ INET_ECN_MASK ;
ipv6_change_dsfield ( top_iph , 0 , dsfield ) ;
top_iph - > hop_limit = dst_metric ( dst - > child , RTAX_HOPLIMIT ) ;
ipv6_addr_copy ( & top_iph - > saddr , ( struct in6_addr * ) & x - > props . saddr ) ;
ipv6_addr_copy ( & top_iph - > daddr , ( struct in6_addr * ) & x - > id . daddr ) ;
2007-05-30 00:03:17 +04:00
skb - > protocol = htons ( ETH_P_IPV6 ) ;
2006-05-28 10:05:54 +04:00
return 0 ;
}
static int xfrm6_tunnel_input ( struct xfrm_state * x , struct sk_buff * skb )
{
int err = - EINVAL ;
2007-03-10 18:40:27 +03:00
const unsigned char * old_mac ;
2007-04-11 07:50:43 +04:00
const unsigned char * nh = skb_network_header ( skb ) ;
2006-05-28 10:05:54 +04:00
2007-04-11 07:50:43 +04:00
if ( nh [ IP6CB ( skb ) - > nhoff ] ! = IPPROTO_IPV6 & &
nh [ IP6CB ( skb ) - > nhoff ] ! = IPPROTO_IPIP )
2006-05-28 10:05:54 +04:00
goto out ;
if ( ! pskb_may_pull ( skb , sizeof ( struct ipv6hdr ) ) )
goto out ;
if ( skb_cloned ( skb ) & &
( err = pskb_expand_head ( skb , 0 , 0 , GFP_ATOMIC ) ) )
goto out ;
2007-04-11 07:50:43 +04:00
nh = skb_network_header ( skb ) ;
if ( nh [ IP6CB ( skb ) - > nhoff ] = = IPPROTO_IPV6 ) {
2007-02-07 01:27:32 +03:00
if ( x - > props . flags & XFRM_STATE_DECAP_DSCP )
2007-04-11 08:06:25 +04:00
ipv6_copy_dscp ( ipv6_hdr ( skb ) , ipipv6_hdr ( skb ) ) ;
2007-02-07 01:27:32 +03:00
if ( ! ( x - > props . flags & XFRM_STATE_NOECN ) )
ipip6_ecn_decapsulate ( skb ) ;
} else {
if ( ! ( x - > props . flags & XFRM_STATE_NOECN ) )
ip6ip_ecn_decapsulate ( skb ) ;
skb - > protocol = htons ( ETH_P_IP ) ;
}
2007-03-20 01:33:04 +03:00
old_mac = skb_mac_header ( skb ) ;
2007-03-10 18:40:27 +03:00
skb_set_mac_header ( skb , - skb - > mac_len ) ;
2007-03-20 01:33:04 +03:00
memmove ( skb_mac_header ( skb ) , old_mac , skb - > mac_len ) ;
2007-04-11 07:45:18 +04:00
skb_reset_network_header ( skb ) ;
2006-05-28 10:05:54 +04:00
err = 0 ;
out :
return err ;
}
static struct xfrm_mode xfrm6_tunnel_mode = {
. input = xfrm6_tunnel_input ,
. output = xfrm6_tunnel_output ,
. owner = THIS_MODULE ,
. encap = XFRM_MODE_TUNNEL ,
} ;
static int __init xfrm6_tunnel_init ( void )
{
return xfrm_register_mode ( & xfrm6_tunnel_mode , AF_INET6 ) ;
}
static void __exit xfrm6_tunnel_exit ( void )
{
int err ;
err = xfrm_unregister_mode ( & xfrm6_tunnel_mode , AF_INET6 ) ;
BUG_ON ( err ) ;
}
module_init ( xfrm6_tunnel_init ) ;
module_exit ( xfrm6_tunnel_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_XFRM_MODE ( AF_INET6 , XFRM_MODE_TUNNEL ) ;