2005-04-17 02:20:36 +04:00
/*
* xfrm6_policy . c : based on xfrm4_policy . c
*
* Authors :
* Mitsuru KANDA @ USAGI
* Kazunori MIYAZAWA @ USAGI
* Kunihiro Ishiguro < kunihiro @ ipinfusion . com >
* IPv6 support
* YOSHIFUJI Hideaki
* Split up af - specific portion
2007-02-09 17:24:49 +03:00
*
2005-04-17 02:20:36 +04:00
*/
2007-11-14 08:37:28 +03:00
# include <linux/err.h>
# include <linux/kernel.h>
2005-05-04 03:27:10 +04:00
# include <linux/netdevice.h>
# include <net/addrconf.h>
2007-11-14 08:35:32 +03:00
# include <net/dst.h>
2005-04-17 02:20:36 +04:00
# include <net/xfrm.h>
# include <net/ip.h>
# include <net/ipv6.h>
# include <net/ip6_route.h>
2007-06-27 10:56:32 +04:00
# if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
2006-08-24 07:39:03 +04:00
# include <net/mip6.h>
# endif
2005-04-17 02:20:36 +04:00
static struct xfrm_policy_afinfo xfrm6_policy_afinfo ;
2008-11-26 04:51:25 +03:00
static struct dst_entry * xfrm6_dst_lookup ( struct net * net , int tos ,
xfrm_address_t * saddr ,
2007-11-14 08:37:28 +03:00
xfrm_address_t * daddr )
2005-04-17 02:20:36 +04:00
{
2007-11-14 08:37:28 +03:00
struct flowi fl = { } ;
struct dst_entry * dst ;
int err ;
memcpy ( & fl . fl6_dst , daddr , sizeof ( fl . fl6_dst ) ) ;
if ( saddr )
memcpy ( & fl . fl6_src , saddr , sizeof ( fl . fl6_src ) ) ;
2008-11-26 04:51:25 +03:00
dst = ip6_route_output ( net , NULL , & fl ) ;
2007-11-14 08:37:28 +03:00
err = dst - > error ;
if ( dst - > error ) {
2006-10-17 09:10:05 +04:00
dst_release ( dst ) ;
2007-11-14 08:37:28 +03:00
dst = ERR_PTR ( err ) ;
}
return dst ;
2005-04-17 02:20:36 +04:00
}
2008-11-26 04:56:49 +03:00
static int xfrm6_get_saddr ( struct net * net ,
xfrm_address_t * saddr , xfrm_address_t * daddr )
2006-09-19 23:57:34 +04:00
{
2007-11-14 08:37:28 +03:00
struct dst_entry * dst ;
2008-08-15 02:33:21 +04:00
struct net_device * dev ;
2007-11-14 08:37:28 +03:00
2008-11-26 04:56:49 +03:00
dst = xfrm6_dst_lookup ( net , 0 , NULL , daddr ) ;
2007-11-14 08:37:28 +03:00
if ( IS_ERR ( dst ) )
return - EHOSTUNREACH ;
2008-08-15 02:33:21 +04:00
dev = ip6_dst_idev ( dst ) - > dev ;
ipv6_dev_get_saddr ( dev_net ( dev ) , dev ,
2008-03-25 03:37:42 +03:00
( struct in6_addr * ) & daddr - > a6 , 0 ,
2008-03-03 15:44:34 +03:00
( struct in6_addr * ) & saddr - > a6 ) ;
2007-11-14 08:37:28 +03:00
dst_release ( dst ) ;
return 0 ;
2006-09-19 23:57:34 +04:00
}
2007-12-11 20:32:34 +03:00
static int xfrm6_get_tos ( struct flowi * fl )
2005-04-17 02:20:36 +04:00
{
2007-12-11 20:32:34 +03:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2007-12-21 07:41:12 +03:00
static int xfrm6_init_path ( struct xfrm_dst * path , struct dst_entry * dst ,
int nfheader_len )
{
if ( dst - > ops - > family = = AF_INET6 ) {
struct rt6_info * rt = ( struct rt6_info * ) dst ;
if ( rt - > rt6i_node )
path - > path_cookie = rt - > rt6i_node - > fn_sernum ;
}
path - > u . rt6 . rt6i_nfheader_len = nfheader_len ;
return 0 ;
}
2010-03-02 05:51:56 +03:00
static int xfrm6_fill_dst ( struct xfrm_dst * xdst , struct net_device * dev ,
struct flowi * fl )
2007-12-11 20:32:34 +03:00
{
struct rt6_info * rt = ( struct rt6_info * ) xdst - > route ;
2005-04-17 02:20:36 +04:00
2007-12-11 20:32:34 +03:00
xdst - > u . dst . dev = dev ;
dev_hold ( dev ) ;
2005-04-17 02:20:36 +04:00
2010-04-22 03:25:30 +04:00
xdst - > u . rt6 . rt6i_idev = in6_dev_get ( dev ) ;
2007-12-11 20:32:34 +03:00
if ( ! xdst - > u . rt6 . rt6i_idev )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2007-12-11 20:32:34 +03:00
/* Sheit... I remember I did this right. Apparently,
* it was magically lost , so this code needs audit */
xdst - > u . rt6 . rt6i_flags = rt - > rt6i_flags & ( RTF_ANYCAST |
RTF_LOCAL ) ;
xdst - > u . rt6 . rt6i_metric = rt - > rt6i_metric ;
xdst - > u . rt6 . rt6i_node = rt - > rt6i_node ;
2007-12-21 07:41:12 +03:00
if ( rt - > rt6i_node )
xdst - > route_cookie = rt - > rt6i_node - > fn_sernum ;
2007-12-11 20:32:34 +03:00
xdst - > u . rt6 . rt6i_gateway = rt - > rt6i_gateway ;
xdst - > u . rt6 . rt6i_dst = rt - > rt6i_dst ;
xdst - > u . rt6 . rt6i_src = rt - > rt6i_src ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static inline void
2007-12-12 21:44:16 +03:00
_decode_session6 ( struct sk_buff * skb , struct flowi * fl , int reverse )
2005-04-17 02:20:36 +04:00
{
2008-11-02 07:12:07 +03:00
int onlyproto = 0 ;
2007-03-16 23:26:39 +03:00
u16 offset = skb_network_header_len ( skb ) ;
2007-04-26 04:54:47 +04:00
struct ipv6hdr * hdr = ipv6_hdr ( skb ) ;
2006-04-19 01:46:52 +04:00
struct ipv6_opt_hdr * exthdr ;
2007-04-11 07:50:43 +04:00
const unsigned char * nh = skb_network_header ( skb ) ;
u8 nexthdr = nh [ IP6CB ( skb ) - > nhoff ] ;
2005-04-17 02:20:36 +04:00
memset ( fl , 0 , sizeof ( struct flowi ) ) ;
2010-07-02 11:47:55 +04:00
fl - > mark = skb - > mark ;
2007-12-12 21:44:16 +03:00
ipv6_addr_copy ( & fl - > fl6_dst , reverse ? & hdr - > saddr : & hdr - > daddr ) ;
ipv6_addr_copy ( & fl - > fl6_src , reverse ? & hdr - > daddr : & hdr - > saddr ) ;
2005-04-17 02:20:36 +04:00
2009-07-02 20:59:49 +04:00
while ( nh + offset + 1 < skb - > data | |
pskb_may_pull ( skb , nh + offset + 1 - skb - > data ) ) {
2007-04-11 07:50:43 +04:00
nh = skb_network_header ( skb ) ;
exthdr = ( struct ipv6_opt_hdr * ) ( nh + offset ) ;
2006-04-19 01:46:52 +04:00
2005-04-17 02:20:36 +04:00
switch ( nexthdr ) {
2008-11-02 07:12:07 +03:00
case NEXTHDR_FRAGMENT :
onlyproto = 1 ;
2005-04-17 02:20:36 +04:00
case NEXTHDR_ROUTING :
case NEXTHDR_HOP :
case NEXTHDR_DEST :
offset + = ipv6_optlen ( exthdr ) ;
nexthdr = exthdr - > nexthdr ;
2007-04-11 07:50:43 +04:00
exthdr = ( struct ipv6_opt_hdr * ) ( nh + offset ) ;
2005-04-17 02:20:36 +04:00
break ;
case IPPROTO_UDP :
2006-11-27 22:10:57 +03:00
case IPPROTO_UDPLITE :
2005-04-17 02:20:36 +04:00
case IPPROTO_TCP :
case IPPROTO_SCTP :
2005-12-20 01:03:46 +03:00
case IPPROTO_DCCP :
2009-07-02 20:59:49 +04:00
if ( ! onlyproto & & ( nh + offset + 4 < skb - > data | |
pskb_may_pull ( skb , nh + offset + 4 - skb - > data ) ) ) {
2006-11-08 11:20:21 +03:00
__be16 * ports = ( __be16 * ) exthdr ;
2005-04-17 02:20:36 +04:00
2007-12-12 21:44:16 +03:00
fl - > fl_ip_sport = ports [ ! ! reverse ] ;
fl - > fl_ip_dport = ports [ ! reverse ] ;
2005-04-17 02:20:36 +04:00
}
fl - > proto = nexthdr ;
return ;
case IPPROTO_ICMPV6 :
2008-11-02 07:12:07 +03:00
if ( ! onlyproto & & pskb_may_pull ( skb , nh + offset + 2 - skb - > data ) ) {
2005-04-17 02:20:36 +04:00
u8 * icmp = ( u8 * ) exthdr ;
fl - > fl_icmp_type = icmp [ 0 ] ;
fl - > fl_icmp_code = icmp [ 1 ] ;
}
fl - > proto = nexthdr ;
return ;
2007-06-27 10:56:32 +04:00
# if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
2006-08-24 07:39:03 +04:00
case IPPROTO_MH :
2008-11-02 07:12:07 +03:00
if ( ! onlyproto & & pskb_may_pull ( skb , nh + offset + 3 - skb - > data ) ) {
2006-08-24 07:39:03 +04:00
struct ip6_mh * mh ;
mh = ( struct ip6_mh * ) exthdr ;
fl - > fl_mh_type = mh - > ip6mh_type ;
}
fl - > proto = nexthdr ;
return ;
# endif
2005-04-17 02:20:36 +04:00
/* XXX Why are there these headers? */
case IPPROTO_AH :
case IPPROTO_ESP :
case IPPROTO_COMP :
default :
fl - > fl_ipsec_spi = 0 ;
fl - > proto = nexthdr ;
return ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
}
}
2008-01-18 14:56:57 +03:00
static inline int xfrm6_garbage_collect ( struct dst_ops * ops )
2005-04-17 02:20:36 +04:00
{
2010-01-25 09:47:53 +03:00
struct net * net = container_of ( ops , struct net , xfrm . xfrm6_dst_ops ) ;
xfrm6_policy_afinfo . garbage_collect ( net ) ;
return ( atomic_read ( & ops - > entries ) > ops - > gc_thresh * 2 ) ;
2005-04-17 02:20:36 +04:00
}
static void xfrm6_update_pmtu ( struct dst_entry * dst , u32 mtu )
{
struct xfrm_dst * xdst = ( struct xfrm_dst * ) dst ;
struct dst_entry * path = xdst - > route ;
path - > ops - > update_pmtu ( path , mtu ) ;
}
2005-05-04 03:27:10 +04:00
static void xfrm6_dst_destroy ( struct dst_entry * dst )
{
struct xfrm_dst * xdst = ( struct xfrm_dst * ) dst ;
if ( likely ( xdst - > u . rt6 . rt6i_idev ) )
in6_dev_put ( xdst - > u . rt6 . rt6i_idev ) ;
xfrm_dst_destroy ( xdst ) ;
}
static void xfrm6_dst_ifdown ( struct dst_entry * dst , struct net_device * dev ,
int unregister )
{
struct xfrm_dst * xdst ;
if ( ! unregister )
return ;
xdst = ( struct xfrm_dst * ) dst ;
if ( xdst - > u . rt6 . rt6i_idev - > dev = = dev ) {
2007-12-07 11:38:10 +03:00
struct inet6_dev * loopback_idev =
2008-03-25 15:47:49 +03:00
in6_dev_get ( dev_net ( dev ) - > loopback_dev ) ;
2005-05-04 03:27:10 +04:00
BUG_ON ( ! loopback_idev ) ;
do {
in6_dev_put ( xdst - > u . rt6 . rt6i_idev ) ;
xdst - > u . rt6 . rt6i_idev = loopback_idev ;
in6_dev_hold ( loopback_idev ) ;
xdst = ( struct xfrm_dst * ) xdst - > u . dst . child ;
} while ( xdst - > u . dst . xfrm ) ;
__in6_dev_put ( loopback_idev ) ;
}
xfrm_dst_ifdown ( dst , dev ) ;
}
2005-04-17 02:20:36 +04:00
static struct dst_ops xfrm6_dst_ops = {
. family = AF_INET6 ,
2009-02-01 11:45:17 +03:00
. protocol = cpu_to_be16 ( ETH_P_IPV6 ) ,
2005-04-17 02:20:36 +04:00
. gc = xfrm6_garbage_collect ,
. update_pmtu = xfrm6_update_pmtu ,
2005-05-04 03:27:10 +04:00
. destroy = xfrm6_dst_destroy ,
. ifdown = xfrm6_dst_ifdown ,
2007-11-14 08:43:11 +03:00
. local_out = __ip6_local_out ,
2005-04-17 02:20:36 +04:00
. gc_thresh = 1024 ,
2008-01-31 07:07:45 +03:00
. entries = ATOMIC_INIT ( 0 ) ,
2005-04-17 02:20:36 +04:00
} ;
static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
. family = AF_INET6 ,
. dst_ops = & xfrm6_dst_ops ,
. dst_lookup = xfrm6_dst_lookup ,
2006-09-19 23:57:34 +04:00
. get_saddr = xfrm6_get_saddr ,
2005-04-17 02:20:36 +04:00
. decode_session = _decode_session6 ,
2007-12-11 20:32:34 +03:00
. get_tos = xfrm6_get_tos ,
2007-12-21 07:41:12 +03:00
. init_path = xfrm6_init_path ,
2007-12-11 20:32:34 +03:00
. fill_dst = xfrm6_fill_dst ,
2005-04-17 02:20:36 +04:00
} ;
2007-12-07 11:42:11 +03:00
static int __init xfrm6_policy_init ( void )
2005-04-17 02:20:36 +04:00
{
2007-12-07 11:42:11 +03:00
return xfrm_policy_register_afinfo ( & xfrm6_policy_afinfo ) ;
2005-04-17 02:20:36 +04:00
}
static void xfrm6_policy_fini ( void )
{
xfrm_policy_unregister_afinfo ( & xfrm6_policy_afinfo ) ;
}
2009-08-05 07:32:16 +04:00
# ifdef CONFIG_SYSCTL
2009-07-27 12:22:46 +04:00
static struct ctl_table xfrm6_policy_table [ ] = {
{
. procname = " xfrm6_gc_thresh " ,
2010-01-25 09:47:53 +03:00
. data = & init_net . xfrm . xfrm6_dst_ops . gc_thresh ,
2009-07-27 12:22:46 +04:00
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
. proc_handler = proc_dointvec ,
} ,
{ }
} ;
static struct ctl_table_header * sysctl_hdr ;
2009-08-05 07:32:16 +04:00
# endif
2009-07-27 12:22:46 +04:00
2007-12-07 11:42:11 +03:00
int __init xfrm6_init ( void )
2005-04-17 02:20:36 +04:00
{
2007-12-07 11:42:11 +03:00
int ret ;
2009-07-31 05:52:15 +04:00
unsigned int gc_thresh ;
2007-12-07 11:42:11 +03:00
2009-07-31 05:52:15 +04:00
/*
* We need a good default value for the xfrm6 gc threshold .
* In ipv4 we set it to the route hash table size * 8 , which
* is half the size of the maximaum route cache for ipv4 . It
* would be good to do the same thing for v6 , except the table is
* constructed differently here . Here each table for a net namespace
* can have FIB_TABLE_HASHSZ entries , so lets go with the same
* computation that we used for ipv4 here . Also , lets keep the initial
* gc_thresh to a minimum of 1024 , since , the ipv6 route cache defaults
* to that as a minimum as well
*/
gc_thresh = FIB6_TABLE_HASHSZ * 8 ;
xfrm6_dst_ops . gc_thresh = ( gc_thresh < 1024 ) ? 1024 : gc_thresh ;
2010-01-25 09:47:53 +03:00
ret = xfrm6_policy_init ( ) ;
if ( ret )
goto out ;
ret = xfrm6_state_init ( ) ;
if ( ret )
goto out_policy ;
2009-08-05 07:32:16 +04:00
# ifdef CONFIG_SYSCTL
2009-07-27 12:22:46 +04:00
sysctl_hdr = register_net_sysctl_table ( & init_net , net_ipv6_ctl_path ,
xfrm6_policy_table ) ;
2009-08-05 07:32:16 +04:00
# endif
2007-12-07 11:42:11 +03:00
out :
return ret ;
out_policy :
xfrm6_policy_fini ( ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
void xfrm6_fini ( void )
{
2009-08-05 07:32:16 +04:00
# ifdef CONFIG_SYSCTL
2009-07-27 12:22:46 +04:00
if ( sysctl_hdr )
unregister_net_sysctl_table ( sysctl_hdr ) ;
2009-08-05 07:32:16 +04:00
# endif
2005-04-17 02:20:36 +04:00
//xfrm6_input_fini();
xfrm6_policy_fini ( ) ;
xfrm6_state_fini ( ) ;
}