2005-04-17 02:20:36 +04: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 17:24:49 +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 .
*/
2007-10-09 04:16:30 +04:00
# include <linux/if_ether.h>
2007-11-14 08:40:52 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/skbuff.h>
# include <linux/icmpv6.h>
2006-01-07 10:01:48 +03:00
# include <linux/netfilter_ipv6.h>
2007-11-14 08:40:52 +03:00
# include <net/dst.h>
2005-04-17 02:20:36 +04:00
# include <net/ipv6.h>
2010-12-17 14:42:42 +03:00
# include <net/ip6_route.h>
2005-04-17 02:20:36 +04:00
# include <net/xfrm.h>
2006-08-24 04:57:28 +04:00
int xfrm6_find_1stfragopt ( struct xfrm_state * x , struct sk_buff * skb ,
u8 * * prevhdr )
{
return ip6_find_1stfragopt ( skb , prevhdr ) ;
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( xfrm6_find_1stfragopt ) ;
2011-10-11 05:44:30 +04:00
static int xfrm6_local_dontfrag ( struct sk_buff * skb )
{
int proto ;
struct sock * sk = skb - > sk ;
if ( sk ) {
2013-08-14 15:05:23 +04:00
if ( sk - > sk_family ! = AF_INET6 )
return 0 ;
2011-10-11 05:44:30 +04:00
2013-08-14 15:05:23 +04:00
proto = sk - > sk_protocol ;
2011-10-11 05:44:30 +04:00
if ( proto = = IPPROTO_UDP | | proto = = IPPROTO_RAW )
return inet6_sk ( sk ) - > dontfrag ;
}
return 0 ;
}
static void xfrm6_local_rxpmtu ( struct sk_buff * skb , u32 mtu )
{
struct flowi6 fl6 ;
struct sock * sk = skb - > sk ;
fl6 . flowi6_oif = sk - > sk_bound_dev_if ;
2011-11-21 07:39:03 +04:00
fl6 . daddr = ipv6_hdr ( skb ) - > daddr ;
2011-10-11 05:44:30 +04:00
ipv6_local_rxpmtu ( sk , & fl6 , mtu ) ;
}
2013-08-14 15:05:23 +04:00
void xfrm6_local_error ( struct sk_buff * skb , u32 mtu )
2011-10-11 05:44:30 +04:00
{
struct flowi6 fl6 ;
2013-08-18 15:46:57 +04:00
const struct ipv6hdr * hdr ;
2011-10-11 05:44:30 +04:00
struct sock * sk = skb - > sk ;
2013-08-18 15:46:57 +04:00
hdr = skb - > encapsulation ? inner_ipv6_hdr ( skb ) : ipv6_hdr ( skb ) ;
2011-10-11 05:44:30 +04:00
fl6 . fl6_dport = inet_sk ( sk ) - > inet_dport ;
2013-08-18 15:46:57 +04:00
fl6 . daddr = hdr - > daddr ;
2011-10-11 05:44:30 +04:00
ipv6_local_error ( sk , EMSGSIZE , & fl6 , mtu ) ;
}
2005-04-17 02:20:36 +04:00
static int xfrm6_tunnel_check_size ( struct sk_buff * skb )
{
int mtu , ret = 0 ;
2009-06-02 09:19:30 +04:00
struct dst_entry * dst = skb_dst ( skb ) ;
2005-04-17 02:20:36 +04:00
2017-04-14 11:06:10 +03:00
if ( skb - > ignore_df )
goto out ;
2005-04-17 02:20:36 +04:00
mtu = dst_mtu ( dst ) ;
if ( mtu < IPV6_MIN_MTU )
mtu = IPV6_MIN_MTU ;
2017-04-14 11:06:10 +03:00
if ( ( ! skb_is_gso ( skb ) & & skb - > len > mtu ) | |
( skb_is_gso ( skb ) & &
2018-03-01 09:13:39 +03:00
! skb_gso_validate_network_len ( skb , ip6_skb_dst_mtu ( skb ) ) ) ) {
2005-05-24 00:11:07 +04:00
skb - > dev = dst - > dev ;
2015-10-19 11:30:05 +03:00
skb - > protocol = htons ( ETH_P_IPV6 ) ;
2011-10-11 05:44:30 +04:00
if ( xfrm6_local_dontfrag ( skb ) )
xfrm6_local_rxpmtu ( skb , mtu ) ;
else if ( skb - > sk )
2013-08-14 15:05:23 +04:00
xfrm_local_error ( skb , mtu ) ;
2011-10-11 05:44:30 +04:00
else
icmpv6_send ( skb , ICMPV6_PKT_TOOBIG , 0 , mtu ) ;
2005-04-17 02:20:36 +04:00
ret = - EMSGSIZE ;
}
2017-04-14 11:06:10 +03:00
out :
2005-04-17 02:20:36 +04:00
return ret ;
}
2007-11-14 08:40:52 +03:00
int xfrm6_extract_output ( struct xfrm_state * x , struct sk_buff * skb )
{
int err ;
err = xfrm6_tunnel_check_size ( skb ) ;
if ( err )
return err ;
2007-11-20 05:47:58 +03:00
XFRM_MODE_SKB_CB ( skb ) - > protocol = ipv6_hdr ( skb ) - > nexthdr ;
2007-11-14 08:40:52 +03:00
return xfrm6_extract_header ( skb ) ;
}
2015-04-06 05:19:04 +03:00
int xfrm6_output_finish ( struct sock * sk , struct sk_buff * skb )
2006-06-22 14:08:03 +04:00
{
2014-04-07 10:08:52 +04:00
memset ( IP6CB ( skb ) , 0 , sizeof ( * IP6CB ( skb ) ) ) ;
2007-11-14 08:43:11 +03:00
# ifdef CONFIG_NETFILTER
IP6CB ( skb ) - > flags | = IP6SKB_XFRM_TRANSFORMED ;
# endif
2006-06-22 14:08:03 +04:00
2015-04-06 05:19:04 +03:00
return xfrm_output ( sk , skb ) ;
2006-06-22 14:08:03 +04:00
}
2019-03-29 23:16:30 +03:00
static int __xfrm6_output_state_finish ( struct xfrm_state * x , struct sock * sk ,
struct sk_buff * skb )
{
const struct xfrm_state_afinfo * afinfo ;
int ret = - EAFNOSUPPORT ;
rcu_read_lock ( ) ;
2019-03-29 23:16:32 +03:00
afinfo = xfrm_state_afinfo_get_rcu ( x - > outer_mode . family ) ;
2019-03-29 23:16:30 +03:00
if ( likely ( afinfo ) )
ret = afinfo - > output_finish ( sk , skb ) ;
else
kfree_skb ( skb ) ;
rcu_read_unlock ( ) ;
return ret ;
}
2015-06-13 06:12:04 +03:00
static int __xfrm6_output_finish ( struct net * net , struct sock * sk , struct sk_buff * skb )
{
struct xfrm_state * x = skb_dst ( skb ) - > xfrm ;
2015-10-01 19:41:42 +03:00
2019-03-29 23:16:30 +03:00
return __xfrm6_output_state_finish ( x , sk , skb ) ;
2015-06-13 06:12:04 +03:00
}
2015-09-16 04:04:18 +03:00
static int __xfrm6_output ( struct net * net , struct sock * sk , struct sk_buff * skb )
2010-12-17 14:42:42 +03:00
{
struct dst_entry * dst = skb_dst ( skb ) ;
struct xfrm_state * x = dst - > xfrm ;
2013-08-26 14:31:19 +04:00
int mtu ;
2015-09-04 08:21:06 +03:00
bool toobig ;
2013-08-13 06:35:58 +04:00
2014-04-07 10:08:52 +04:00
# ifdef CONFIG_NETFILTER
if ( ! x ) {
IP6CB ( skb ) - > flags | = IP6SKB_REROUTED ;
2015-10-08 00:48:35 +03:00
return dst_output ( net , sk , skb ) ;
2014-04-07 10:08:52 +04:00
}
# endif
2015-09-04 08:21:06 +03:00
if ( x - > props . mode ! = XFRM_MODE_TUNNEL )
goto skip_frag ;
2013-08-26 14:31:19 +04:00
if ( skb - > protocol = = htons ( ETH_P_IPV6 ) )
mtu = ip6_skb_dst_mtu ( skb ) ;
else
mtu = dst_mtu ( skb_dst ( skb ) ) ;
2011-10-11 05:44:30 +04:00
2015-09-04 08:21:06 +03:00
toobig = skb - > len > mtu & & ! skb_is_gso ( skb ) ;
if ( toobig & & xfrm6_local_dontfrag ( skb ) ) {
2011-10-11 05:44:30 +04:00
xfrm6_local_rxpmtu ( skb , mtu ) ;
2018-08-31 14:38:49 +03:00
kfree_skb ( skb ) ;
2011-10-11 05:44:30 +04:00
return - EMSGSIZE ;
2015-09-04 08:21:06 +03:00
} else if ( ! skb - > ignore_df & & toobig & & skb - > sk ) {
2013-08-14 15:05:23 +04:00
xfrm_local_error ( skb , mtu ) ;
2018-08-31 14:38:49 +03:00
kfree_skb ( skb ) ;
2011-10-11 05:44:30 +04:00
return - EMSGSIZE ;
}
2010-12-17 14:42:42 +03:00
2015-09-04 08:21:06 +03:00
if ( toobig | | dst_allfrag ( skb_dst ( skb ) ) )
2015-06-13 06:12:04 +03:00
return ip6_fragment ( net , sk , skb ,
__xfrm6_output_finish ) ;
2015-09-04 08:21:06 +03:00
skip_frag :
2019-03-29 23:16:30 +03:00
return __xfrm6_output_state_finish ( x , sk , skb ) ;
2010-12-17 14:42:42 +03:00
}
2015-10-08 00:48:47 +03:00
int xfrm6_output ( struct net * net , struct sock * sk , struct sk_buff * skb )
2006-01-07 10:01:48 +03:00
{
2015-09-16 04:04:16 +03:00
return NF_HOOK_COND ( NFPROTO_IPV6 , NF_INET_POST_ROUTING ,
net , sk , skb , NULL , skb_dst ( skb ) - > dev ,
__xfrm6_output ,
2014-04-07 10:08:52 +04:00
! ( IP6CB ( skb ) - > flags & IP6SKB_REROUTED ) ) ;
2006-01-07 10:01:48 +03:00
}