2005-04-17 02:20:36 +04:00
/*
* xfrm4_output . c - Common IPsec encapsulation code for IPv4 .
* Copyright ( c ) 2004 Herbert Xu < herbert @ gondor . apana . org . au >
2007-02-09 17:24:47 +03:00
*
2005-04-17 02:20:36 +04:00
* 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-06-22 14:08:03 +04:00
# include <linux/if_ether.h>
# include <linux/kernel.h>
2007-11-14 08:40:52 +03:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/skbuff.h>
2006-01-07 10:01:48 +03:00
# include <linux/netfilter_ipv4.h>
2007-11-14 08:40:52 +03:00
# include <net/dst.h>
2005-04-17 02:20:36 +04:00
# include <net/ip.h>
# include <net/xfrm.h>
# include <net/icmp.h>
static int xfrm4_tunnel_check_size ( struct sk_buff * skb )
{
int mtu , ret = 0 ;
struct dst_entry * dst ;
if ( IPCB ( skb ) - > flags & IPSKB_XFRM_TUNNEL_SIZE )
goto out ;
2007-04-21 09:47:35 +04:00
if ( ! ( ip_hdr ( skb ) - > frag_off & htons ( IP_DF ) ) | | skb - > local_df )
2005-04-17 02:20:36 +04:00
goto out ;
2009-06-02 09:19:30 +04:00
dst = skb_dst ( skb ) ;
2005-04-17 02:20:36 +04:00
mtu = dst_mtu ( dst ) ;
if ( skb - > len > mtu ) {
2011-06-30 03:20:41 +04:00
if ( skb - > sk )
ip_local_error ( skb - > sk , EMSGSIZE , ip_hdr ( skb ) - > daddr ,
inet_sk ( skb - > sk ) - > inet_dport , mtu ) ;
else
icmp_send ( skb , ICMP_DEST_UNREACH ,
ICMP_FRAG_NEEDED , htonl ( mtu ) ) ;
2005-04-17 02:20:36 +04:00
ret = - EMSGSIZE ;
}
out :
return ret ;
}
2007-11-14 08:40:52 +03:00
int xfrm4_extract_output ( struct xfrm_state * x , struct sk_buff * skb )
{
int err ;
err = xfrm4_tunnel_check_size ( skb ) ;
if ( err )
return err ;
2007-11-20 05:47:58 +03:00
XFRM_MODE_SKB_CB ( skb ) - > protocol = ip_hdr ( skb ) - > protocol ;
2007-11-14 08:40:52 +03:00
return xfrm4_extract_header ( skb ) ;
}
int xfrm4_prepare_output ( struct xfrm_state * x , struct sk_buff * skb )
{
int err ;
2008-03-25 00:51:51 +03:00
err = xfrm_inner_extract_output ( x , skb ) ;
2007-11-14 08:40:52 +03:00
if ( err )
return err ;
memset ( IPCB ( skb ) , 0 , sizeof ( * IPCB ( skb ) ) ) ;
2007-11-14 08:43:11 +03:00
IPCB ( skb ) - > flags | = IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED ;
2007-11-14 08:40:52 +03:00
skb - > protocol = htons ( ETH_P_IP ) ;
return x - > outer_mode - > output2 ( x , skb ) ;
}
EXPORT_SYMBOL ( xfrm4_prepare_output ) ;
2011-05-09 23:36:38 +04:00
int xfrm4_output_finish ( struct sk_buff * skb )
2006-06-22 14:08:03 +04:00
{
# ifdef CONFIG_NETFILTER
2009-06-02 09:19:30 +04:00
if ( ! skb_dst ( skb ) - > xfrm ) {
2006-06-22 14:08:03 +04:00
IPCB ( skb ) - > flags | = IPSKB_REROUTED ;
return dst_output ( skb ) ;
}
2007-11-14 08:43:11 +03:00
IPCB ( skb ) - > flags | = IPSKB_XFRM_TRANSFORMED ;
# endif
2006-06-22 14:08:03 +04:00
skb - > protocol = htons ( ETH_P_IP ) ;
2007-11-14 08:43:11 +03:00
return xfrm_output ( skb ) ;
2006-06-22 14:08:03 +04:00
}
2006-01-07 10:01:48 +03:00
int xfrm4_output ( struct sk_buff * skb )
{
2011-05-09 23:36:38 +04:00
struct dst_entry * dst = skb_dst ( skb ) ;
struct xfrm_state * x = dst - > xfrm ;
2010-03-23 06:07:29 +03:00
return NF_HOOK_COND ( NFPROTO_IPV4 , NF_INET_POST_ROUTING , skb ,
2011-05-09 23:36:38 +04:00
NULL , dst - > dev ,
x - > outer_mode - > afinfo - > output_finish ,
2006-02-16 02:10:22 +03:00
! ( IPCB ( skb ) - > flags & IPSKB_REROUTED ) ) ;
2006-01-07 10:01:48 +03:00
}