2005-04-16 15:20:36 -07:00
/*
* xfrm4_output . c - Common IPsec encapsulation code for IPv4 .
* Copyright ( c ) 2004 Herbert Xu < herbert @ gondor . apana . org . au >
*
* 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-06 23:01:48 -08:00
# include <linux/compiler.h>
2005-04-16 15:20:36 -07:00
# include <linux/skbuff.h>
# include <linux/spinlock.h>
2006-01-06 23:01:48 -08:00
# include <linux/netfilter_ipv4.h>
2005-04-16 15:20:36 -07: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 ;
struct iphdr * iph = skb - > nh . iph ;
if ( IPCB ( skb ) - > flags & IPSKB_XFRM_TUNNEL_SIZE )
goto out ;
IPCB ( skb ) - > flags | = IPSKB_XFRM_TUNNEL_SIZE ;
if ( ! ( iph - > frag_off & htons ( IP_DF ) ) | | skb - > local_df )
goto out ;
dst = skb - > dst ;
mtu = dst_mtu ( dst ) ;
if ( skb - > len > mtu ) {
icmp_send ( skb , ICMP_DEST_UNREACH , ICMP_FRAG_NEEDED , htonl ( mtu ) ) ;
ret = - EMSGSIZE ;
}
out :
return ret ;
}
2006-01-06 23:01:48 -08:00
static int xfrm4_output_one ( struct sk_buff * skb )
2005-04-16 15:20:36 -07:00
{
struct dst_entry * dst = skb - > dst ;
struct xfrm_state * x = dst - > xfrm ;
int err ;
if ( skb - > ip_summed = = CHECKSUM_HW ) {
err = skb_checksum_help ( skb , 0 ) ;
if ( err )
goto error_nolock ;
}
if ( x - > props . mode ) {
err = xfrm4_tunnel_check_size ( skb ) ;
if ( err )
goto error_nolock ;
}
2006-01-06 23:01:48 -08:00
do {
spin_lock_bh ( & x - > lock ) ;
err = xfrm_state_check ( x , skb ) ;
if ( err )
goto error ;
2005-04-16 15:20:36 -07:00
2006-05-27 23:05:54 -07:00
err = x - > mode - > output ( skb ) ;
if ( err )
goto error ;
2005-04-16 15:20:36 -07:00
2006-01-06 23:01:48 -08:00
err = x - > type - > output ( x , skb ) ;
if ( err )
goto error ;
2005-04-16 15:20:36 -07:00
2006-01-06 23:01:48 -08:00
x - > curlft . bytes + = skb - > len ;
x - > curlft . packets + + ;
2005-04-16 15:20:36 -07:00
2006-01-06 23:01:48 -08:00
spin_unlock_bh ( & x - > lock ) ;
2005-04-16 15:20:36 -07:00
2006-01-06 23:01:48 -08:00
if ( ! ( skb - > dst = dst_pop ( dst ) ) ) {
err = - EHOSTUNREACH ;
goto error_nolock ;
}
dst = skb - > dst ;
x = dst - > xfrm ;
} while ( x & & ! x - > props . mode ) ;
2006-01-06 23:04:54 -08:00
IPCB ( skb ) - > flags | = IPSKB_XFRM_TRANSFORMED ;
2006-01-06 23:01:48 -08:00
err = 0 ;
2005-04-16 15:20:36 -07:00
out_exit :
return err ;
error :
spin_unlock_bh ( & x - > lock ) ;
error_nolock :
kfree_skb ( skb ) ;
goto out_exit ;
}
2006-01-06 23:01:48 -08:00
2006-02-15 15:10:22 -08:00
static int xfrm4_output_finish ( struct sk_buff * skb )
2006-01-06 23:01:48 -08:00
{
int err ;
2006-02-15 15:10:22 -08:00
# ifdef CONFIG_NETFILTER
if ( ! skb - > dst - > xfrm ) {
IPCB ( skb ) - > flags | = IPSKB_REROUTED ;
return dst_output ( skb ) ;
}
# endif
2006-01-06 23:01:48 -08:00
while ( likely ( ( err = xfrm4_output_one ( skb ) ) = = 0 ) ) {
nf_reset ( skb ) ;
err = nf_hook ( PF_INET , NF_IP_LOCAL_OUT , & skb , NULL ,
skb - > dst - > dev , dst_output ) ;
if ( unlikely ( err ! = 1 ) )
break ;
if ( ! skb - > dst - > xfrm )
return dst_output ( skb ) ;
err = nf_hook ( PF_INET , NF_IP_POST_ROUTING , & skb , NULL ,
skb - > dst - > dev , xfrm4_output_finish ) ;
if ( unlikely ( err ! = 1 ) )
break ;
}
return err ;
}
int xfrm4_output ( struct sk_buff * skb )
{
2006-02-15 15:10:22 -08:00
return NF_HOOK_COND ( PF_INET , NF_IP_POST_ROUTING , skb , NULL , skb - > dst - > dev ,
xfrm4_output_finish ,
! ( IPCB ( skb ) - > flags & IPSKB_REROUTED ) ) ;
2006-01-06 23:01:48 -08:00
}