2005-04-16 15:20:36 -07:00
/*
* xfrm6_output . c - Common IPsec encapsulation code for IPv6 .
* Copyright ( C ) 2002 USAGI / WIDE Project
* Copyright ( c ) 2004 Herbert Xu < herbert @ gondor . apana . org . au >
2007-02-09 23:24:49 +09:00
*
2005-04-16 15:20:36 -07: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 .
*/
2007-10-08 17:16:30 -07:00
# include <linux/if_ether.h>
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/icmpv6.h>
2006-01-06 23:01:48 -08:00
# include <linux/netfilter_ipv6.h>
2005-04-16 15:20:36 -07:00
# include <net/ipv6.h>
# include <net/xfrm.h>
2006-08-23 17:57:28 -07:00
int xfrm6_find_1stfragopt ( struct xfrm_state * x , struct sk_buff * skb ,
u8 * * prevhdr )
{
return ip6_find_1stfragopt ( skb , prevhdr ) ;
}
2007-02-22 22:05:40 +09:00
EXPORT_SYMBOL ( xfrm6_find_1stfragopt ) ;
2005-04-16 15:20:36 -07:00
static int xfrm6_tunnel_check_size ( struct sk_buff * skb )
{
int mtu , ret = 0 ;
struct dst_entry * dst = skb - > dst ;
mtu = dst_mtu ( dst ) ;
if ( mtu < IPV6_MIN_MTU )
mtu = IPV6_MIN_MTU ;
if ( skb - > len > mtu ) {
2005-05-23 13:11:07 -07:00
skb - > dev = dst - > dev ;
2005-04-16 15:20:36 -07:00
icmpv6_send ( skb , ICMPV6_PKT_TOOBIG , 0 , mtu , skb - > dev ) ;
ret = - EMSGSIZE ;
}
return ret ;
}
2007-10-08 17:16:30 -07:00
static inline int xfrm6_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 ;
2007-10-10 15:45:52 -07:00
struct ipv6hdr * iph ;
2005-04-16 15:20:36 -07:00
int err ;
2007-02-09 23:24:49 +09:00
2006-09-22 15:05:15 -07:00
if ( x - > props . mode = = XFRM_MODE_TUNNEL ) {
2005-04-16 15:20:36 -07:00
err = xfrm6_tunnel_check_size ( skb ) ;
if ( err )
goto error_nolock ;
}
2007-10-08 17:16:30 -07:00
err = xfrm_output ( skb ) ;
if ( err )
goto error_nolock ;
2006-01-06 23:01:48 -08:00
2007-10-10 15:45:52 -07:00
iph = ipv6_hdr ( skb ) ;
iph - > payload_len = htons ( skb - > len - sizeof ( * iph ) ) ;
2006-01-06 23:04:54 -08:00
IP6CB ( skb ) - > flags | = IP6SKB_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_nolock :
kfree_skb ( skb ) ;
goto out_exit ;
}
2006-01-06 23:01:48 -08:00
2006-06-22 03:08:03 -07:00
static int xfrm6_output_finish2 ( struct sk_buff * skb )
2006-01-06 23:01:48 -08:00
{
int err ;
while ( likely ( ( err = xfrm6_output_one ( skb ) ) = = 0 ) ) {
nf_reset ( skb ) ;
2007-02-09 23:24:49 +09:00
2007-10-15 00:53:15 -07:00
err = nf_hook ( PF_INET6 , NF_IP6_LOCAL_OUT , skb , NULL ,
2006-01-06 23:01:48 -08:00
skb - > dst - > dev , dst_output ) ;
if ( unlikely ( err ! = 1 ) )
break ;
if ( ! skb - > dst - > xfrm )
return dst_output ( skb ) ;
2007-10-15 00:53:15 -07:00
err = nf_hook ( PF_INET6 , NF_IP6_POST_ROUTING , skb , NULL ,
2006-06-22 03:08:03 -07:00
skb - > dst - > dev , xfrm6_output_finish2 ) ;
2006-01-06 23:01:48 -08:00
if ( unlikely ( err ! = 1 ) )
break ;
}
return err ;
}
2006-06-22 03:08:03 -07:00
static int xfrm6_output_finish ( struct sk_buff * skb )
{
struct sk_buff * segs ;
2006-07-08 13:34:32 -07:00
if ( ! skb_is_gso ( skb ) )
2006-06-22 03:08:03 -07:00
return xfrm6_output_finish2 ( skb ) ;
2006-07-30 20:19:11 -07:00
skb - > protocol = htons ( ETH_P_IPV6 ) ;
2006-06-22 03:08:03 -07:00
segs = skb_gso_segment ( skb , 0 ) ;
kfree_skb ( skb ) ;
if ( unlikely ( IS_ERR ( segs ) ) )
return PTR_ERR ( segs ) ;
do {
struct sk_buff * nskb = segs - > next ;
int err ;
segs - > next = NULL ;
err = xfrm6_output_finish2 ( segs ) ;
if ( unlikely ( err ) ) {
while ( ( segs = nskb ) ) {
nskb = segs - > next ;
segs - > next = NULL ;
kfree_skb ( segs ) ;
}
return err ;
}
segs = nskb ;
} while ( segs ) ;
return 0 ;
}
2006-01-06 23:01:48 -08:00
int xfrm6_output ( struct sk_buff * skb )
{
return NF_HOOK ( PF_INET6 , NF_IP6_POST_ROUTING , skb , NULL , skb - > dst - > dev ,
xfrm6_output_finish ) ;
}