2007-02-09 17:24:47 +03:00
/*
2005-04-17 02:20:36 +04:00
* xfrm4_policy . c
*
* Changes :
* Kazunori MIYAZAWA @ USAGI
* YOSHIFUJI Hideaki @ USAGI
* Split up af - specific portion
2007-02-09 17:24:47 +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/inetdevice.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>
static struct xfrm_policy_afinfo xfrm4_policy_afinfo ;
2008-11-26 04:51:25 +03:00
static struct dst_entry * xfrm4_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 = {
2006-09-19 23:57:34 +04:00
. nl_u = {
. ip4_u = {
2007-11-14 08:37:28 +03:00
. tos = tos ,
2006-09-19 23:57:34 +04:00
. daddr = daddr - > a4 ,
} ,
} ,
} ;
2007-11-14 08:37:28 +03:00
struct dst_entry * dst ;
struct rtable * rt ;
int err ;
2006-09-19 23:57:34 +04:00
2007-11-14 08:37:28 +03:00
if ( saddr )
fl . fl4_src = saddr - > a4 ;
2008-11-26 04:51:25 +03:00
err = __ip_route_output_key ( net , & rt , & fl ) ;
2010-06-11 10:31:35 +04:00
dst = & rt - > dst ;
2007-11-14 08:37:28 +03:00
if ( err )
dst = ERR_PTR ( err ) ;
return dst ;
}
2008-11-26 04:56:49 +03:00
static int xfrm4_get_saddr ( struct net * net ,
xfrm_address_t * saddr , xfrm_address_t * daddr )
2007-11-14 08:37:28 +03:00
{
struct dst_entry * dst ;
struct rtable * rt ;
2008-11-26 04:56:49 +03:00
dst = xfrm4_dst_lookup ( net , 0 , NULL , daddr ) ;
2007-11-14 08:37:28 +03:00
if ( IS_ERR ( dst ) )
return - EHOSTUNREACH ;
rt = ( struct rtable * ) dst ;
saddr - > a4 = rt - > rt_src ;
dst_release ( dst ) ;
return 0 ;
2006-09-19 23:57:34 +04:00
}
2007-12-11 20:32:34 +03:00
static int xfrm4_get_tos ( struct flowi * fl )
2005-04-17 02:20:36 +04:00
{
2010-09-22 10:45:11 +04:00
return IPTOS_RT_MASK & fl - > fl4_tos ; /* Strip ECN bits */
2007-12-11 20:32:34 +03:00
}
2005-04-17 02:20:36 +04:00
2007-12-21 07:41:12 +03:00
static int xfrm4_init_path ( struct xfrm_dst * path , struct dst_entry * dst ,
int nfheader_len )
{
return 0 ;
}
2010-03-02 05:51:56 +03:00
static int xfrm4_fill_dst ( struct xfrm_dst * xdst , struct net_device * dev ,
struct flowi * fl )
2007-12-11 20:32:34 +03:00
{
struct rtable * rt = ( struct rtable * ) xdst - > route ;
2005-04-17 02:20:36 +04:00
2010-03-02 05:51:56 +03:00
xdst - > u . rt . fl = * fl ;
2005-04-17 02:20:36 +04:00
2007-12-11 20:32:34 +03:00
xdst - > u . dst . dev = dev ;
dev_hold ( dev ) ;
2007-02-07 01:27:32 +03:00
2007-12-11 20:32:34 +03:00
xdst - > u . rt . idev = in_dev_get ( dev ) ;
if ( ! xdst - > u . rt . idev )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2007-12-11 20:32:34 +03:00
xdst - > u . rt . peer = rt - > peer ;
if ( rt - > peer )
atomic_inc ( & rt - > peer - > refcnt ) ;
2007-11-14 08:37:28 +03: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 . rt . rt_flags = rt - > rt_flags & ( RTCF_BROADCAST | RTCF_MULTICAST |
RTCF_LOCAL ) ;
xdst - > u . rt . rt_type = rt - > rt_type ;
xdst - > u . rt . rt_src = rt - > rt_src ;
xdst - > u . rt . rt_dst = rt - > rt_dst ;
xdst - > u . rt . rt_gateway = rt - > rt_gateway ;
xdst - > u . rt . rt_spec_dst = rt - > rt_spec_dst ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void
2007-12-12 21:44:16 +03:00
_decode_session4 ( struct sk_buff * skb , struct flowi * fl , int reverse )
2005-04-17 02:20:36 +04:00
{
2007-04-21 09:47:35 +04:00
struct iphdr * iph = ip_hdr ( skb ) ;
2007-04-11 07:50:43 +04:00
u8 * xprth = skb_network_header ( skb ) + iph - > ihl * 4 ;
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 ;
2005-04-17 02:20:36 +04:00
if ( ! ( iph - > frag_off & htons ( IP_MF | IP_OFFSET ) ) ) {
switch ( iph - > protocol ) {
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:57:23 +04:00
if ( xprth + 4 < skb - > data | |
pskb_may_pull ( skb , xprth + 4 - skb - > data ) ) {
2006-11-08 11:20:21 +03:00
__be16 * ports = ( __be16 * ) xprth ;
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
}
break ;
case IPPROTO_ICMP :
if ( pskb_may_pull ( skb , xprth + 2 - skb - > data ) ) {
u8 * icmp = xprth ;
fl - > fl_icmp_type = icmp [ 0 ] ;
fl - > fl_icmp_code = icmp [ 1 ] ;
}
break ;
case IPPROTO_ESP :
if ( pskb_may_pull ( skb , xprth + 4 - skb - > data ) ) {
2006-09-28 05:49:07 +04:00
__be32 * ehdr = ( __be32 * ) xprth ;
2005-04-17 02:20:36 +04:00
fl - > fl_ipsec_spi = ehdr [ 0 ] ;
}
break ;
case IPPROTO_AH :
if ( pskb_may_pull ( skb , xprth + 8 - skb - > data ) ) {
2006-09-28 05:49:07 +04:00
__be32 * ah_hdr = ( __be32 * ) xprth ;
2005-04-17 02:20:36 +04:00
fl - > fl_ipsec_spi = ah_hdr [ 1 ] ;
}
break ;
case IPPROTO_COMP :
if ( pskb_may_pull ( skb , xprth + 4 - skb - > data ) ) {
2006-09-28 05:49:07 +04:00
__be16 * ipcomp_hdr = ( __be16 * ) xprth ;
2005-04-17 02:20:36 +04:00
2006-05-23 03:53:22 +04:00
fl - > fl_ipsec_spi = htonl ( ntohs ( ipcomp_hdr [ 1 ] ) ) ;
2005-04-17 02:20:36 +04:00
}
break ;
default :
fl - > fl_ipsec_spi = 0 ;
break ;
2007-04-21 04:09:22 +04:00
}
2005-04-17 02:20:36 +04:00
}
fl - > proto = iph - > protocol ;
2007-12-12 21:44:16 +03:00
fl - > fl4_dst = reverse ? iph - > saddr : iph - > daddr ;
fl - > fl4_src = reverse ? iph - > daddr : iph - > saddr ;
2006-02-24 03:19:26 +03:00
fl - > fl4_tos = iph - > tos ;
2005-04-17 02:20:36 +04:00
}
2008-01-18 14:56:57 +03:00
static inline int xfrm4_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 . xfrm4_dst_ops ) ;
xfrm4_policy_afinfo . garbage_collect ( net ) ;
2010-10-08 10:37:34 +04:00
return ( dst_entries_get_slow ( ops ) > ops - > gc_thresh * 2 ) ;
2005-04-17 02:20:36 +04:00
}
static void xfrm4_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 xfrm4_dst_destroy ( struct dst_entry * dst )
{
struct xfrm_dst * xdst = ( struct xfrm_dst * ) dst ;
if ( likely ( xdst - > u . rt . idev ) )
in_dev_put ( xdst - > u . rt . idev ) ;
2007-10-18 08:34:46 +04:00
if ( likely ( xdst - > u . rt . peer ) )
2006-12-07 10:45:15 +03:00
inet_putpeer ( xdst - > u . rt . peer ) ;
2005-05-04 03:27:10 +04:00
xfrm_dst_destroy ( xdst ) ;
}
static void xfrm4_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 . rt . idev - > dev = = dev ) {
2007-12-07 11:38:10 +03:00
struct in_device * loopback_idev =
2008-03-25 15:47:49 +03:00
in_dev_get ( dev_net ( dev ) - > loopback_dev ) ;
2005-05-04 03:27:10 +04:00
BUG_ON ( ! loopback_idev ) ;
do {
in_dev_put ( xdst - > u . rt . idev ) ;
xdst - > u . rt . idev = loopback_idev ;
in_dev_hold ( loopback_idev ) ;
xdst = ( struct xfrm_dst * ) xdst - > u . dst . child ;
} while ( xdst - > u . dst . xfrm ) ;
__in_dev_put ( loopback_idev ) ;
}
xfrm_dst_ifdown ( dst , dev ) ;
}
2005-04-17 02:20:36 +04:00
static struct dst_ops xfrm4_dst_ops = {
. family = AF_INET ,
2009-02-01 11:45:17 +03:00
. protocol = cpu_to_be16 ( ETH_P_IP ) ,
2005-04-17 02:20:36 +04:00
. gc = xfrm4_garbage_collect ,
. update_pmtu = xfrm4_update_pmtu ,
2005-05-04 03:27:10 +04:00
. destroy = xfrm4_dst_destroy ,
. ifdown = xfrm4_dst_ifdown ,
2007-11-14 08:43:11 +03:00
. local_out = __ip_local_out ,
2005-04-17 02:20:36 +04:00
. gc_thresh = 1024 ,
} ;
static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
. family = AF_INET ,
. dst_ops = & xfrm4_dst_ops ,
. dst_lookup = xfrm4_dst_lookup ,
2006-09-19 23:57:34 +04:00
. get_saddr = xfrm4_get_saddr ,
2005-04-17 02:20:36 +04:00
. decode_session = _decode_session4 ,
2007-12-11 20:32:34 +03:00
. get_tos = xfrm4_get_tos ,
2007-12-21 07:41:12 +03:00
. init_path = xfrm4_init_path ,
2007-12-11 20:32:34 +03:00
. fill_dst = xfrm4_fill_dst ,
2005-04-17 02:20:36 +04:00
} ;
2009-08-05 07:18:33 +04:00
# ifdef CONFIG_SYSCTL
2009-07-27 12:22:46 +04:00
static struct ctl_table xfrm4_policy_table [ ] = {
{
. procname = " xfrm4_gc_thresh " ,
2010-01-25 09:47:53 +03:00
. data = & init_net . xfrm . xfrm4_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:18:33 +04:00
# endif
2009-07-27 12:22:46 +04:00
2005-04-17 02:20:36 +04:00
static void __init xfrm4_policy_init ( void )
{
xfrm_policy_register_afinfo ( & xfrm4_policy_afinfo ) ;
}
static void __exit xfrm4_policy_fini ( void )
{
2009-08-05 07:18:33 +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:18:33 +04:00
# endif
2005-04-17 02:20:36 +04:00
xfrm_policy_unregister_afinfo ( & xfrm4_policy_afinfo ) ;
}
2009-07-31 05:52:15 +04:00
void __init xfrm4_init ( int rt_max_size )
2005-04-17 02:20:36 +04:00
{
2009-07-31 05:52:15 +04:00
/*
* Select a default value for the gc_thresh based on the main route
* table hash size . It seems to me the worst case scenario is when
* we have ipsec operating in transport mode , in which we create a
* dst_entry per socket . The xfrm gc algorithm starts trying to remove
* entries at gc_thresh , and prevents new allocations as 2 * gc_thresh
* so lets set an initial xfrm gc_thresh value at the rt_max_size / 2.
* That will let us store an ipsec connection per route table entry ,
* and start cleaning when were 1 / 2 full
*/
xfrm4_dst_ops . gc_thresh = rt_max_size / 2 ;
2010-10-08 10:37:34 +04:00
dst_entries_init ( & xfrm4_dst_ops ) ;
2010-01-25 09:47:53 +03:00
xfrm4_state_init ( ) ;
xfrm4_policy_init ( ) ;
2009-08-05 07:18:33 +04:00
# ifdef CONFIG_SYSCTL
2009-07-27 12:22:46 +04:00
sysctl_hdr = register_net_sysctl_table ( & init_net , net_ipv4_ctl_path ,
xfrm4_policy_table ) ;
2009-08-05 07:18:33 +04:00
# endif
2005-04-17 02:20:36 +04:00
}