2006-05-28 10:05:54 +04:00
/*
* xfrm4_mode_tunnel . c - Tunnel mode encapsulation for IPv4 .
*
* 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/dst.h>
# include <net/inet_ecn.h>
# include <net/ip.h>
# include <net/xfrm.h>
static inline void ipip_ecn_decapsulate ( struct sk_buff * skb )
{
2007-04-26 05:02:22 +04:00
struct iphdr * inner_iph = ipip_hdr ( skb ) ;
2006-05-28 10:05:54 +04:00
2007-11-14 08:41:28 +03:00
if ( INET_ECN_is_ce ( XFRM_MODE_SKB_CB ( skb ) - > tos ) )
2006-05-28 10:05:54 +04:00
IP_ECN_set_ce ( inner_iph ) ;
}
/* 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
*/
2007-12-21 00:53:40 +03:00
static int xfrm4_mode_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-11-14 08:40:52 +03:00
struct iphdr * top_iph ;
2006-05-28 10:05:54 +04:00
int flags ;
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 iphdr , protocol ) ;
2007-11-14 08:40:52 +03:00
skb - > transport_header = skb - > network_header + sizeof ( * top_iph ) ;
2007-04-21 09:47:35 +04:00
top_iph = ip_hdr ( skb ) ;
2006-05-28 10:05:54 +04:00
top_iph - > ihl = 5 ;
top_iph - > version = 4 ;
2008-03-25 00:51:51 +03:00
top_iph - > protocol = xfrm_af2proto ( skb - > dst - > ops - > family ) ;
2007-02-07 01:27:02 +03:00
2006-05-28 10:05:54 +04:00
/* DS disclosed */
2007-11-14 08:40:52 +03:00
top_iph - > tos = INET_ECN_encapsulate ( XFRM_MODE_SKB_CB ( skb ) - > tos ,
XFRM_MODE_SKB_CB ( skb ) - > tos ) ;
2006-05-28 10:05:54 +04:00
2007-11-14 08:40:52 +03:00
flags = x - > props . flags ;
2006-05-28 10:05:54 +04:00
if ( flags & XFRM_STATE_NOECN )
IP_ECN_clear ( top_iph ) ;
2007-11-14 08:40:52 +03:00
top_iph - > frag_off = ( flags & XFRM_STATE_NOPMTUDISC ) ?
2008-06-18 03:37:13 +04:00
0 : ( XFRM_MODE_SKB_CB ( skb ) - > frag_off & htons ( IP_DF ) ) ;
2007-11-14 08:40:52 +03:00
ip_select_ident ( top_iph , dst - > child , NULL ) ;
2006-05-28 10:05:54 +04:00
top_iph - > ttl = dst_metric ( dst - > child , RTAX_HOPLIMIT ) ;
top_iph - > saddr = x - > props . saddr . a4 ;
top_iph - > daddr = x - > id . daddr . a4 ;
return 0 ;
}
2007-12-21 00:53:40 +03:00
static int xfrm4_mode_tunnel_input ( struct xfrm_state * x , struct sk_buff * skb )
2006-05-28 10:05:54 +04:00
{
2007-03-10 18:40:27 +03:00
const unsigned char * old_mac ;
2006-05-28 10:05:54 +04:00
int err = - EINVAL ;
2007-11-14 08:41:28 +03:00
if ( XFRM_MODE_SKB_CB ( skb ) - > protocol ! = IPPROTO_IPIP )
goto out ;
2007-02-07 01:27:02 +03:00
2006-05-28 10:05:54 +04:00
if ( ! pskb_may_pull ( skb , sizeof ( struct iphdr ) ) )
goto out ;
if ( skb_cloned ( skb ) & &
( err = pskb_expand_head ( skb , 0 , 0 , GFP_ATOMIC ) ) )
goto out ;
2007-11-14 08:41:28 +03:00
if ( x - > props . flags & XFRM_STATE_DECAP_DSCP )
ipv4_copy_dscp ( XFRM_MODE_SKB_CB ( skb ) - > tos , ipip_hdr ( skb ) ) ;
if ( ! ( x - > props . flags & XFRM_STATE_NOECN ) )
ipip_ecn_decapsulate ( skb ) ;
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 xfrm4_tunnel_mode = {
2007-12-21 00:53:40 +03:00
. input2 = xfrm4_mode_tunnel_input ,
2007-11-14 08:41:28 +03:00
. input = xfrm_prepare_input ,
2007-12-21 00:53:40 +03:00
. output2 = xfrm4_mode_tunnel_output ,
2007-11-14 08:40:52 +03:00
. output = xfrm4_prepare_output ,
2006-05-28 10:05:54 +04:00
. owner = THIS_MODULE ,
. encap = XFRM_MODE_TUNNEL ,
2007-10-18 08:31:50 +04:00
. flags = XFRM_MODE_FLAG_TUNNEL ,
2006-05-28 10:05:54 +04:00
} ;
2007-12-21 00:53:40 +03:00
static int __init xfrm4_mode_tunnel_init ( void )
2006-05-28 10:05:54 +04:00
{
return xfrm_register_mode ( & xfrm4_tunnel_mode , AF_INET ) ;
}
2007-12-21 00:53:40 +03:00
static void __exit xfrm4_mode_tunnel_exit ( void )
2006-05-28 10:05:54 +04:00
{
int err ;
err = xfrm_unregister_mode ( & xfrm4_tunnel_mode , AF_INET ) ;
BUG_ON ( err ) ;
}
2007-12-21 00:53:40 +03:00
module_init ( xfrm4_mode_tunnel_init ) ;
module_exit ( xfrm4_mode_tunnel_exit ) ;
2006-05-28 10:05:54 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_XFRM_MODE ( AF_INET , XFRM_MODE_TUNNEL ) ;