2007-02-09 23:24:47 +09:00
/*
2005-04-16 15:20:36 -07:00
* xfrm4_policy . c
*
* Changes :
* Kazunori MIYAZAWA @ USAGI
* YOSHIFUJI Hideaki @ USAGI
* Split up af - specific portion
2007-02-09 23:24:47 +09:00
*
2005-04-16 15:20:36 -07:00
*/
2007-11-13 21:37:28 -08:00
# include <linux/err.h>
# include <linux/kernel.h>
2005-05-03 16:27:10 -07:00
# include <linux/inetdevice.h>
2007-11-13 21:35:32 -08:00
# include <net/dst.h>
2005-04-16 15:20:36 -07:00
# include <net/xfrm.h>
# include <net/ip.h>
static struct xfrm_policy_afinfo xfrm4_policy_afinfo ;
2008-11-25 17:51:25 -08:00
static struct dst_entry * xfrm4_dst_lookup ( struct net * net , int tos ,
xfrm_address_t * saddr ,
2007-11-13 21:37:28 -08:00
xfrm_address_t * daddr )
2005-04-16 15:20:36 -07:00
{
2007-11-13 21:37:28 -08:00
struct flowi fl = {
2006-09-19 12:57:34 -07:00
. nl_u = {
. ip4_u = {
2007-11-13 21:37:28 -08:00
. tos = tos ,
2006-09-19 12:57:34 -07:00
. daddr = daddr - > a4 ,
} ,
} ,
} ;
2007-11-13 21:37:28 -08:00
struct dst_entry * dst ;
struct rtable * rt ;
int err ;
2006-09-19 12:57:34 -07:00
2007-11-13 21:37:28 -08:00
if ( saddr )
fl . fl4_src = saddr - > a4 ;
2008-11-25 17:51:25 -08:00
err = __ip_route_output_key ( net , & rt , & fl ) ;
2007-11-13 21:37:28 -08:00
dst = & rt - > u . dst ;
if ( err )
dst = ERR_PTR ( err ) ;
return dst ;
}
2008-11-25 17:56:49 -08:00
static int xfrm4_get_saddr ( struct net * net ,
xfrm_address_t * saddr , xfrm_address_t * daddr )
2007-11-13 21:37:28 -08:00
{
struct dst_entry * dst ;
struct rtable * rt ;
2008-11-25 17:56:49 -08:00
dst = xfrm4_dst_lookup ( net , 0 , NULL , daddr ) ;
2007-11-13 21:37:28 -08:00
if ( IS_ERR ( dst ) )
return - EHOSTUNREACH ;
rt = ( struct rtable * ) dst ;
saddr - > a4 = rt - > rt_src ;
dst_release ( dst ) ;
return 0 ;
2006-09-19 12:57:34 -07:00
}
2007-12-11 09:32:34 -08:00
static int xfrm4_get_tos ( struct flowi * fl )
2005-04-16 15:20:36 -07:00
{
2007-12-11 09:32:34 -08:00
return fl - > fl4_tos ;
}
2005-04-16 15:20:36 -07:00
2007-12-20 20:41:12 -08:00
static int xfrm4_init_path ( struct xfrm_dst * path , struct dst_entry * dst ,
int nfheader_len )
{
return 0 ;
}
2010-03-02 02:51:56 +00:00
static int xfrm4_fill_dst ( struct xfrm_dst * xdst , struct net_device * dev ,
struct flowi * fl )
2007-12-11 09:32:34 -08:00
{
struct rtable * rt = ( struct rtable * ) xdst - > route ;
2005-04-16 15:20:36 -07:00
2010-03-02 02:51:56 +00:00
xdst - > u . rt . fl = * fl ;
2005-04-16 15:20:36 -07:00
2007-12-11 09:32:34 -08:00
xdst - > u . dst . dev = dev ;
dev_hold ( dev ) ;
2007-02-06 14:27:32 -08:00
2007-12-11 09:32:34 -08:00
xdst - > u . rt . idev = in_dev_get ( dev ) ;
if ( ! xdst - > u . rt . idev )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
2007-12-11 09:32:34 -08:00
xdst - > u . rt . peer = rt - > peer ;
if ( rt - > peer )
atomic_inc ( & rt - > peer - > refcnt ) ;
2007-11-13 21:37:28 -08:00
2007-12-11 09:32:34 -08: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-16 15:20:36 -07:00
return 0 ;
}
static void
2007-12-12 10:44:16 -08:00
_decode_session4 ( struct sk_buff * skb , struct flowi * fl , int reverse )
2005-04-16 15:20:36 -07:00
{
2007-04-20 22:47:35 -07:00
struct iphdr * iph = ip_hdr ( skb ) ;
2007-04-10 20:50:43 -07:00
u8 * xprth = skb_network_header ( skb ) + iph - > ihl * 4 ;
2005-04-16 15:20:36 -07:00
memset ( fl , 0 , sizeof ( struct flowi ) ) ;
2010-07-02 07:47:55 +00:00
fl - > mark = skb - > mark ;
2005-04-16 15:20:36 -07:00
if ( ! ( iph - > frag_off & htons ( IP_MF | IP_OFFSET ) ) ) {
switch ( iph - > protocol ) {
case IPPROTO_UDP :
2006-11-27 11:10:57 -08:00
case IPPROTO_UDPLITE :
2005-04-16 15:20:36 -07:00
case IPPROTO_TCP :
case IPPROTO_SCTP :
2005-12-19 14:03:46 -08:00
case IPPROTO_DCCP :
2009-07-02 16:57:23 +00:00
if ( xprth + 4 < skb - > data | |
pskb_may_pull ( skb , xprth + 4 - skb - > data ) ) {
2006-11-08 00:20:21 -08:00
__be16 * ports = ( __be16 * ) xprth ;
2005-04-16 15:20:36 -07:00
2007-12-12 10:44:16 -08:00
fl - > fl_ip_sport = ports [ ! ! reverse ] ;
fl - > fl_ip_dport = ports [ ! reverse ] ;
2005-04-16 15:20:36 -07: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-27 18:49:07 -07:00
__be32 * ehdr = ( __be32 * ) xprth ;
2005-04-16 15:20:36 -07:00
fl - > fl_ipsec_spi = ehdr [ 0 ] ;
}
break ;
case IPPROTO_AH :
if ( pskb_may_pull ( skb , xprth + 8 - skb - > data ) ) {
2006-09-27 18:49:07 -07:00
__be32 * ah_hdr = ( __be32 * ) xprth ;
2005-04-16 15:20:36 -07:00
fl - > fl_ipsec_spi = ah_hdr [ 1 ] ;
}
break ;
case IPPROTO_COMP :
if ( pskb_may_pull ( skb , xprth + 4 - skb - > data ) ) {
2006-09-27 18:49:07 -07:00
__be16 * ipcomp_hdr = ( __be16 * ) xprth ;
2005-04-16 15:20:36 -07:00
2006-05-22 16:53:22 -07:00
fl - > fl_ipsec_spi = htonl ( ntohs ( ipcomp_hdr [ 1 ] ) ) ;
2005-04-16 15:20:36 -07:00
}
break ;
default :
fl - > fl_ipsec_spi = 0 ;
break ;
2007-04-20 17:09:22 -07:00
}
2005-04-16 15:20:36 -07:00
}
fl - > proto = iph - > protocol ;
2007-12-12 10:44:16 -08:00
fl - > fl4_dst = reverse ? iph - > saddr : iph - > daddr ;
fl - > fl4_src = reverse ? iph - > daddr : iph - > saddr ;
2006-02-23 16:19:26 -08:00
fl - > fl4_tos = iph - > tos ;
2005-04-16 15:20:36 -07:00
}
2008-01-18 03:56:57 -08:00
static inline int xfrm4_garbage_collect ( struct dst_ops * ops )
2005-04-16 15:20:36 -07:00
{
2010-01-24 22:47:53 -08:00
struct net * net = container_of ( ops , struct net , xfrm . xfrm4_dst_ops ) ;
xfrm4_policy_afinfo . garbage_collect ( net ) ;
return ( atomic_read ( & ops - > entries ) > ops - > gc_thresh * 2 ) ;
2005-04-16 15:20:36 -07: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-03 16:27:10 -07: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-17 21:34:46 -07:00
if ( likely ( xdst - > u . rt . peer ) )
2006-12-06 23:45:15 -08:00
inet_putpeer ( xdst - > u . rt . peer ) ;
2005-05-03 16:27:10 -07: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 00:38:10 -08:00
struct in_device * loopback_idev =
2008-03-25 21:47:49 +09:00
in_dev_get ( dev_net ( dev ) - > loopback_dev ) ;
2005-05-03 16:27:10 -07: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-16 15:20:36 -07:00
static struct dst_ops xfrm4_dst_ops = {
. family = AF_INET ,
2009-02-01 00:45:17 -08:00
. protocol = cpu_to_be16 ( ETH_P_IP ) ,
2005-04-16 15:20:36 -07:00
. gc = xfrm4_garbage_collect ,
. update_pmtu = xfrm4_update_pmtu ,
2005-05-03 16:27:10 -07:00
. destroy = xfrm4_dst_destroy ,
. ifdown = xfrm4_dst_ifdown ,
2007-11-13 21:43:11 -08:00
. local_out = __ip_local_out ,
2005-04-16 15:20:36 -07:00
. gc_thresh = 1024 ,
2008-01-30 20:07:45 -08:00
. entries = ATOMIC_INIT ( 0 ) ,
2005-04-16 15:20:36 -07:00
} ;
static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
. family = AF_INET ,
. dst_ops = & xfrm4_dst_ops ,
. dst_lookup = xfrm4_dst_lookup ,
2006-09-19 12:57:34 -07:00
. get_saddr = xfrm4_get_saddr ,
2005-04-16 15:20:36 -07:00
. decode_session = _decode_session4 ,
2007-12-11 09:32:34 -08:00
. get_tos = xfrm4_get_tos ,
2007-12-20 20:41:12 -08:00
. init_path = xfrm4_init_path ,
2007-12-11 09:32:34 -08:00
. fill_dst = xfrm4_fill_dst ,
2005-04-16 15:20:36 -07:00
} ;
2009-08-04 20:18:33 -07:00
# ifdef CONFIG_SYSCTL
2009-07-27 08:22:46 +00:00
static struct ctl_table xfrm4_policy_table [ ] = {
{
. procname = " xfrm4_gc_thresh " ,
2010-01-24 22:47:53 -08:00
. data = & init_net . xfrm . xfrm4_dst_ops . gc_thresh ,
2009-07-27 08:22:46 +00:00
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
. proc_handler = proc_dointvec ,
} ,
{ }
} ;
static struct ctl_table_header * sysctl_hdr ;
2009-08-04 20:18:33 -07:00
# endif
2009-07-27 08:22:46 +00:00
2005-04-16 15:20:36 -07:00
static void __init xfrm4_policy_init ( void )
{
xfrm_policy_register_afinfo ( & xfrm4_policy_afinfo ) ;
}
static void __exit xfrm4_policy_fini ( void )
{
2009-08-04 20:18:33 -07:00
# ifdef CONFIG_SYSCTL
2009-07-27 08:22:46 +00:00
if ( sysctl_hdr )
unregister_net_sysctl_table ( sysctl_hdr ) ;
2009-08-04 20:18:33 -07:00
# endif
2005-04-16 15:20:36 -07:00
xfrm_policy_unregister_afinfo ( & xfrm4_policy_afinfo ) ;
}
2009-07-30 18:52:15 -07:00
void __init xfrm4_init ( int rt_max_size )
2005-04-16 15:20:36 -07:00
{
2009-07-30 18:52:15 -07: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-01-24 22:47:53 -08:00
xfrm4_state_init ( ) ;
xfrm4_policy_init ( ) ;
2009-08-04 20:18:33 -07:00
# ifdef CONFIG_SYSCTL
2009-07-27 08:22:46 +00:00
sysctl_hdr = register_net_sysctl_table ( & init_net , net_ipv4_ctl_path ,
xfrm4_policy_table ) ;
2009-08-04 20:18:33 -07:00
# endif
2005-04-16 15:20:36 -07:00
}