2005-04-16 15:20:36 -07:00
/*
* xfrm_policy . c
*
* Changes :
* Mitsuru KANDA @ USAGI
* Kazunori MIYAZAWA @ USAGI
* Kunihiro Ishiguro < kunihiro @ ipinfusion . com >
* IPv6 support
* Kazunori MIYAZAWA @ USAGI
* YOSHIFUJI Hideaki
* Split up af - specific portion
* Derek Atkins < derek @ ihtfp . com > Add the post_input processor
*
*/
# include <asm/bug.h>
# include <linux/config.h>
# include <linux/slab.h>
# include <linux/kmod.h>
# include <linux/list.h>
# include <linux/spinlock.h>
# include <linux/workqueue.h>
# include <linux/notifier.h>
# include <linux/netdevice.h>
# include <linux/module.h>
# include <net/xfrm.h>
# include <net/ip.h>
DECLARE_MUTEX ( xfrm_cfg_sem ) ;
EXPORT_SYMBOL ( xfrm_cfg_sem ) ;
static DEFINE_RWLOCK ( xfrm_policy_lock ) ;
struct xfrm_policy * xfrm_policy_list [ XFRM_POLICY_MAX * 2 ] ;
EXPORT_SYMBOL ( xfrm_policy_list ) ;
static DEFINE_RWLOCK ( xfrm_policy_afinfo_lock ) ;
static struct xfrm_policy_afinfo * xfrm_policy_afinfo [ NPROTO ] ;
2005-08-26 12:05:31 -07:00
static kmem_cache_t * xfrm_dst_cache __read_mostly ;
2005-04-16 15:20:36 -07:00
static struct work_struct xfrm_policy_gc_work ;
static struct list_head xfrm_policy_gc_list =
LIST_HEAD_INIT ( xfrm_policy_gc_list ) ;
static DEFINE_SPINLOCK ( xfrm_policy_gc_lock ) ;
static struct xfrm_policy_afinfo * xfrm_policy_get_afinfo ( unsigned short family ) ;
static void xfrm_policy_put_afinfo ( struct xfrm_policy_afinfo * afinfo ) ;
int xfrm_register_type ( struct xfrm_type * type , unsigned short family )
{
struct xfrm_policy_afinfo * afinfo = xfrm_policy_get_afinfo ( family ) ;
struct xfrm_type_map * typemap ;
int err = 0 ;
if ( unlikely ( afinfo = = NULL ) )
return - EAFNOSUPPORT ;
typemap = afinfo - > type_map ;
write_lock ( & typemap - > lock ) ;
if ( likely ( typemap - > map [ type - > proto ] = = NULL ) )
typemap - > map [ type - > proto ] = type ;
else
err = - EEXIST ;
write_unlock ( & typemap - > lock ) ;
xfrm_policy_put_afinfo ( afinfo ) ;
return err ;
}
EXPORT_SYMBOL ( xfrm_register_type ) ;
int xfrm_unregister_type ( struct xfrm_type * type , unsigned short family )
{
struct xfrm_policy_afinfo * afinfo = xfrm_policy_get_afinfo ( family ) ;
struct xfrm_type_map * typemap ;
int err = 0 ;
if ( unlikely ( afinfo = = NULL ) )
return - EAFNOSUPPORT ;
typemap = afinfo - > type_map ;
write_lock ( & typemap - > lock ) ;
if ( unlikely ( typemap - > map [ type - > proto ] ! = type ) )
err = - ENOENT ;
else
typemap - > map [ type - > proto ] = NULL ;
write_unlock ( & typemap - > lock ) ;
xfrm_policy_put_afinfo ( afinfo ) ;
return err ;
}
EXPORT_SYMBOL ( xfrm_unregister_type ) ;
struct xfrm_type * xfrm_get_type ( u8 proto , unsigned short family )
{
struct xfrm_policy_afinfo * afinfo ;
struct xfrm_type_map * typemap ;
struct xfrm_type * type ;
int modload_attempted = 0 ;
retry :
afinfo = xfrm_policy_get_afinfo ( family ) ;
if ( unlikely ( afinfo = = NULL ) )
return NULL ;
typemap = afinfo - > type_map ;
read_lock ( & typemap - > lock ) ;
type = typemap - > map [ proto ] ;
if ( unlikely ( type & & ! try_module_get ( type - > owner ) ) )
type = NULL ;
read_unlock ( & typemap - > lock ) ;
if ( ! type & & ! modload_attempted ) {
xfrm_policy_put_afinfo ( afinfo ) ;
request_module ( " xfrm-type-%d-%d " ,
( int ) family , ( int ) proto ) ;
modload_attempted = 1 ;
goto retry ;
}
xfrm_policy_put_afinfo ( afinfo ) ;
return type ;
}
int xfrm_dst_lookup ( struct xfrm_dst * * dst , struct flowi * fl ,
unsigned short family )
{
struct xfrm_policy_afinfo * afinfo = xfrm_policy_get_afinfo ( family ) ;
int err = 0 ;
if ( unlikely ( afinfo = = NULL ) )
return - EAFNOSUPPORT ;
if ( likely ( afinfo - > dst_lookup ! = NULL ) )
err = afinfo - > dst_lookup ( dst , fl ) ;
else
err = - EINVAL ;
xfrm_policy_put_afinfo ( afinfo ) ;
return err ;
}
EXPORT_SYMBOL ( xfrm_dst_lookup ) ;
void xfrm_put_type ( struct xfrm_type * type )
{
module_put ( type - > owner ) ;
}
static inline unsigned long make_jiffies ( long secs )
{
if ( secs > = ( MAX_SCHEDULE_TIMEOUT - 1 ) / HZ )
return MAX_SCHEDULE_TIMEOUT - 1 ;
else
return secs * HZ ;
}
static void xfrm_policy_timer ( unsigned long data )
{
struct xfrm_policy * xp = ( struct xfrm_policy * ) data ;
unsigned long now = ( unsigned long ) xtime . tv_sec ;
long next = LONG_MAX ;
int warn = 0 ;
int dir ;
read_lock ( & xp - > lock ) ;
if ( xp - > dead )
goto out ;
dir = xp - > index & 7 ;
if ( xp - > lft . hard_add_expires_seconds ) {
long tmo = xp - > lft . hard_add_expires_seconds +
xp - > curlft . add_time - now ;
if ( tmo < = 0 )
goto expired ;
if ( tmo < next )
next = tmo ;
}
if ( xp - > lft . hard_use_expires_seconds ) {
long tmo = xp - > lft . hard_use_expires_seconds +
( xp - > curlft . use_time ? : xp - > curlft . add_time ) - now ;
if ( tmo < = 0 )
goto expired ;
if ( tmo < next )
next = tmo ;
}
if ( xp - > lft . soft_add_expires_seconds ) {
long tmo = xp - > lft . soft_add_expires_seconds +
xp - > curlft . add_time - now ;
if ( tmo < = 0 ) {
warn = 1 ;
tmo = XFRM_KM_TIMEOUT ;
}
if ( tmo < next )
next = tmo ;
}
if ( xp - > lft . soft_use_expires_seconds ) {
long tmo = xp - > lft . soft_use_expires_seconds +
( xp - > curlft . use_time ? : xp - > curlft . add_time ) - now ;
if ( tmo < = 0 ) {
warn = 1 ;
tmo = XFRM_KM_TIMEOUT ;
}
if ( tmo < next )
next = tmo ;
}
if ( warn )
km_policy_expired ( xp , dir , 0 ) ;
if ( next ! = LONG_MAX & &
! mod_timer ( & xp - > timer , jiffies + make_jiffies ( next ) ) )
xfrm_pol_hold ( xp ) ;
out :
read_unlock ( & xp - > lock ) ;
xfrm_pol_put ( xp ) ;
return ;
expired :
read_unlock ( & xp - > lock ) ;
2005-06-18 22:43:22 -07:00
if ( ! xfrm_policy_delete ( xp , dir ) )
km_policy_expired ( xp , dir , 1 ) ;
2005-04-16 15:20:36 -07:00
xfrm_pol_put ( xp ) ;
}
/* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2
* SPD calls .
*/
struct xfrm_policy * xfrm_policy_alloc ( int gfp )
{
struct xfrm_policy * policy ;
policy = kmalloc ( sizeof ( struct xfrm_policy ) , gfp ) ;
if ( policy ) {
memset ( policy , 0 , sizeof ( struct xfrm_policy ) ) ;
atomic_set ( & policy - > refcnt , 1 ) ;
rwlock_init ( & policy - > lock ) ;
init_timer ( & policy - > timer ) ;
policy - > timer . data = ( unsigned long ) policy ;
policy - > timer . function = xfrm_policy_timer ;
}
return policy ;
}
EXPORT_SYMBOL ( xfrm_policy_alloc ) ;
/* Destroy xfrm_policy: descendant resources must be released to this moment. */
void __xfrm_policy_destroy ( struct xfrm_policy * policy )
{
if ( ! policy - > dead )
BUG ( ) ;
if ( policy - > bundles )
BUG ( ) ;
if ( del_timer ( & policy - > timer ) )
BUG ( ) ;
kfree ( policy ) ;
}
EXPORT_SYMBOL ( __xfrm_policy_destroy ) ;
static void xfrm_policy_gc_kill ( struct xfrm_policy * policy )
{
struct dst_entry * dst ;
while ( ( dst = policy - > bundles ) ! = NULL ) {
policy - > bundles = dst - > next ;
dst_free ( dst ) ;
}
if ( del_timer ( & policy - > timer ) )
atomic_dec ( & policy - > refcnt ) ;
if ( atomic_read ( & policy - > refcnt ) > 1 )
flow_cache_flush ( ) ;
xfrm_pol_put ( policy ) ;
}
static void xfrm_policy_gc_task ( void * data )
{
struct xfrm_policy * policy ;
struct list_head * entry , * tmp ;
struct list_head gc_list = LIST_HEAD_INIT ( gc_list ) ;
spin_lock_bh ( & xfrm_policy_gc_lock ) ;
list_splice_init ( & xfrm_policy_gc_list , & gc_list ) ;
spin_unlock_bh ( & xfrm_policy_gc_lock ) ;
list_for_each_safe ( entry , tmp , & gc_list ) {
policy = list_entry ( entry , struct xfrm_policy , list ) ;
xfrm_policy_gc_kill ( policy ) ;
}
}
/* Rule must be locked. Release descentant resources, announce
* entry dead . The rule must be unlinked from lists to the moment .
*/
static void xfrm_policy_kill ( struct xfrm_policy * policy )
{
int dead ;
write_lock_bh ( & policy - > lock ) ;
dead = policy - > dead ;
policy - > dead = 1 ;
write_unlock_bh ( & policy - > lock ) ;
if ( unlikely ( dead ) ) {
WARN_ON ( 1 ) ;
return ;
}
spin_lock ( & xfrm_policy_gc_lock ) ;
list_add ( & policy - > list , & xfrm_policy_gc_list ) ;
spin_unlock ( & xfrm_policy_gc_lock ) ;
schedule_work ( & xfrm_policy_gc_work ) ;
}
/* Generate new index... KAME seems to generate them ordered by cost
* of an absolute inpredictability of ordering of rules . This will not pass . */
static u32 xfrm_gen_index ( int dir )
{
u32 idx ;
struct xfrm_policy * p ;
static u32 idx_generator ;
for ( ; ; ) {
idx = ( idx_generator | dir ) ;
idx_generator + = 8 ;
if ( idx = = 0 )
idx = 8 ;
for ( p = xfrm_policy_list [ dir ] ; p ; p = p - > next ) {
if ( p - > index = = idx )
break ;
}
if ( ! p )
return idx ;
}
}
int xfrm_policy_insert ( int dir , struct xfrm_policy * policy , int excl )
{
struct xfrm_policy * pol , * * p ;
struct xfrm_policy * delpol = NULL ;
struct xfrm_policy * * newpos = NULL ;
write_lock_bh ( & xfrm_policy_lock ) ;
for ( p = & xfrm_policy_list [ dir ] ; ( pol = * p ) ! = NULL ; ) {
if ( ! delpol & & memcmp ( & policy - > selector , & pol - > selector , sizeof ( pol - > selector ) ) = = 0 ) {
if ( excl ) {
write_unlock_bh ( & xfrm_policy_lock ) ;
return - EEXIST ;
}
* p = pol - > next ;
delpol = pol ;
if ( policy - > priority > pol - > priority )
continue ;
} else if ( policy - > priority > = pol - > priority ) {
p = & pol - > next ;
continue ;
}
if ( ! newpos )
newpos = p ;
if ( delpol )
break ;
p = & pol - > next ;
}
if ( newpos )
p = newpos ;
xfrm_pol_hold ( policy ) ;
policy - > next = * p ;
* p = policy ;
atomic_inc ( & flow_cache_genid ) ;
policy - > index = delpol ? delpol - > index : xfrm_gen_index ( dir ) ;
policy - > curlft . add_time = ( unsigned long ) xtime . tv_sec ;
policy - > curlft . use_time = 0 ;
if ( ! mod_timer ( & policy - > timer , jiffies + HZ ) )
xfrm_pol_hold ( policy ) ;
write_unlock_bh ( & xfrm_policy_lock ) ;
if ( delpol ) {
xfrm_policy_kill ( delpol ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( xfrm_policy_insert ) ;
struct xfrm_policy * xfrm_policy_bysel ( int dir , struct xfrm_selector * sel ,
int delete )
{
struct xfrm_policy * pol , * * p ;
write_lock_bh ( & xfrm_policy_lock ) ;
for ( p = & xfrm_policy_list [ dir ] ; ( pol = * p ) ! = NULL ; p = & pol - > next ) {
if ( memcmp ( sel , & pol - > selector , sizeof ( * sel ) ) = = 0 ) {
xfrm_pol_hold ( pol ) ;
if ( delete )
* p = pol - > next ;
break ;
}
}
write_unlock_bh ( & xfrm_policy_lock ) ;
if ( pol & & delete ) {
atomic_inc ( & flow_cache_genid ) ;
xfrm_policy_kill ( pol ) ;
}
return pol ;
}
EXPORT_SYMBOL ( xfrm_policy_bysel ) ;
struct xfrm_policy * xfrm_policy_byid ( int dir , u32 id , int delete )
{
struct xfrm_policy * pol , * * p ;
write_lock_bh ( & xfrm_policy_lock ) ;
for ( p = & xfrm_policy_list [ id & 7 ] ; ( pol = * p ) ! = NULL ; p = & pol - > next ) {
if ( pol - > index = = id ) {
xfrm_pol_hold ( pol ) ;
if ( delete )
* p = pol - > next ;
break ;
}
}
write_unlock_bh ( & xfrm_policy_lock ) ;
if ( pol & & delete ) {
atomic_inc ( & flow_cache_genid ) ;
xfrm_policy_kill ( pol ) ;
}
return pol ;
}
EXPORT_SYMBOL ( xfrm_policy_byid ) ;
void xfrm_policy_flush ( void )
{
struct xfrm_policy * xp ;
int dir ;
write_lock_bh ( & xfrm_policy_lock ) ;
for ( dir = 0 ; dir < XFRM_POLICY_MAX ; dir + + ) {
while ( ( xp = xfrm_policy_list [ dir ] ) ! = NULL ) {
xfrm_policy_list [ dir ] = xp - > next ;
write_unlock_bh ( & xfrm_policy_lock ) ;
xfrm_policy_kill ( xp ) ;
write_lock_bh ( & xfrm_policy_lock ) ;
}
}
atomic_inc ( & flow_cache_genid ) ;
write_unlock_bh ( & xfrm_policy_lock ) ;
}
EXPORT_SYMBOL ( xfrm_policy_flush ) ;
int xfrm_policy_walk ( int ( * func ) ( struct xfrm_policy * , int , int , void * ) ,
void * data )
{
struct xfrm_policy * xp ;
int dir ;
int count = 0 ;
int error = 0 ;
read_lock_bh ( & xfrm_policy_lock ) ;
for ( dir = 0 ; dir < 2 * XFRM_POLICY_MAX ; dir + + ) {
for ( xp = xfrm_policy_list [ dir ] ; xp ; xp = xp - > next )
count + + ;
}
if ( count = = 0 ) {
error = - ENOENT ;
goto out ;
}
for ( dir = 0 ; dir < 2 * XFRM_POLICY_MAX ; dir + + ) {
for ( xp = xfrm_policy_list [ dir ] ; xp ; xp = xp - > next ) {
error = func ( xp , dir % XFRM_POLICY_MAX , - - count , data ) ;
if ( error )
goto out ;
}
}
out :
read_unlock_bh ( & xfrm_policy_lock ) ;
return error ;
}
EXPORT_SYMBOL ( xfrm_policy_walk ) ;
/* Find policy to apply to this flow. */
static void xfrm_policy_lookup ( struct flowi * fl , u16 family , u8 dir ,
void * * objp , atomic_t * * obj_refp )
{
struct xfrm_policy * pol ;
read_lock_bh ( & xfrm_policy_lock ) ;
for ( pol = xfrm_policy_list [ dir ] ; pol ; pol = pol - > next ) {
struct xfrm_selector * sel = & pol - > selector ;
int match ;
if ( pol - > family ! = family )
continue ;
match = xfrm_selector_match ( sel , fl , family ) ;
if ( match ) {
xfrm_pol_hold ( pol ) ;
break ;
}
}
read_unlock_bh ( & xfrm_policy_lock ) ;
if ( ( * objp = ( void * ) pol ) ! = NULL )
* obj_refp = & pol - > refcnt ;
}
static struct xfrm_policy * xfrm_sk_policy_lookup ( struct sock * sk , int dir , struct flowi * fl )
{
struct xfrm_policy * pol ;
read_lock_bh ( & xfrm_policy_lock ) ;
if ( ( pol = sk - > sk_policy [ dir ] ) ! = NULL ) {
int match = xfrm_selector_match ( & pol - > selector , fl ,
sk - > sk_family ) ;
if ( match )
xfrm_pol_hold ( pol ) ;
else
pol = NULL ;
}
read_unlock_bh ( & xfrm_policy_lock ) ;
return pol ;
}
static void __xfrm_policy_link ( struct xfrm_policy * pol , int dir )
{
pol - > next = xfrm_policy_list [ dir ] ;
xfrm_policy_list [ dir ] = pol ;
xfrm_pol_hold ( pol ) ;
}
static struct xfrm_policy * __xfrm_policy_unlink ( struct xfrm_policy * pol ,
int dir )
{
struct xfrm_policy * * polp ;
for ( polp = & xfrm_policy_list [ dir ] ;
* polp ! = NULL ; polp = & ( * polp ) - > next ) {
if ( * polp = = pol ) {
* polp = pol - > next ;
return pol ;
}
}
return NULL ;
}
2005-06-18 22:43:22 -07:00
int xfrm_policy_delete ( struct xfrm_policy * pol , int dir )
2005-04-16 15:20:36 -07:00
{
write_lock_bh ( & xfrm_policy_lock ) ;
pol = __xfrm_policy_unlink ( pol , dir ) ;
write_unlock_bh ( & xfrm_policy_lock ) ;
if ( pol ) {
if ( dir < XFRM_POLICY_MAX )
atomic_inc ( & flow_cache_genid ) ;
xfrm_policy_kill ( pol ) ;
2005-06-18 22:43:22 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2005-06-18 22:43:22 -07:00
return - ENOENT ;
2005-04-16 15:20:36 -07:00
}
int xfrm_sk_policy_insert ( struct sock * sk , int dir , struct xfrm_policy * pol )
{
struct xfrm_policy * old_pol ;
write_lock_bh ( & xfrm_policy_lock ) ;
old_pol = sk - > sk_policy [ dir ] ;
sk - > sk_policy [ dir ] = pol ;
if ( pol ) {
pol - > curlft . add_time = ( unsigned long ) xtime . tv_sec ;
pol - > index = xfrm_gen_index ( XFRM_POLICY_MAX + dir ) ;
__xfrm_policy_link ( pol , XFRM_POLICY_MAX + dir ) ;
}
if ( old_pol )
__xfrm_policy_unlink ( old_pol , XFRM_POLICY_MAX + dir ) ;
write_unlock_bh ( & xfrm_policy_lock ) ;
if ( old_pol ) {
xfrm_policy_kill ( old_pol ) ;
}
return 0 ;
}
static struct xfrm_policy * clone_policy ( struct xfrm_policy * old , int dir )
{
struct xfrm_policy * newp = xfrm_policy_alloc ( GFP_ATOMIC ) ;
if ( newp ) {
newp - > selector = old - > selector ;
newp - > lft = old - > lft ;
newp - > curlft = old - > curlft ;
newp - > action = old - > action ;
newp - > flags = old - > flags ;
newp - > xfrm_nr = old - > xfrm_nr ;
newp - > index = old - > index ;
memcpy ( newp - > xfrm_vec , old - > xfrm_vec ,
newp - > xfrm_nr * sizeof ( struct xfrm_tmpl ) ) ;
write_lock_bh ( & xfrm_policy_lock ) ;
__xfrm_policy_link ( newp , XFRM_POLICY_MAX + dir ) ;
write_unlock_bh ( & xfrm_policy_lock ) ;
xfrm_pol_put ( newp ) ;
}
return newp ;
}
int __xfrm_sk_clone_policy ( struct sock * sk )
{
struct xfrm_policy * p0 = sk - > sk_policy [ 0 ] ,
* p1 = sk - > sk_policy [ 1 ] ;
sk - > sk_policy [ 0 ] = sk - > sk_policy [ 1 ] = NULL ;
if ( p0 & & ( sk - > sk_policy [ 0 ] = clone_policy ( p0 , 0 ) ) = = NULL )
return - ENOMEM ;
if ( p1 & & ( sk - > sk_policy [ 1 ] = clone_policy ( p1 , 1 ) ) = = NULL )
return - ENOMEM ;
return 0 ;
}
/* Resolve list of templates for the flow, given policy. */
static int
xfrm_tmpl_resolve ( struct xfrm_policy * policy , struct flowi * fl ,
struct xfrm_state * * xfrm ,
unsigned short family )
{
int nx ;
int i , error ;
xfrm_address_t * daddr = xfrm_flowi_daddr ( fl , family ) ;
xfrm_address_t * saddr = xfrm_flowi_saddr ( fl , family ) ;
for ( nx = 0 , i = 0 ; i < policy - > xfrm_nr ; i + + ) {
struct xfrm_state * x ;
xfrm_address_t * remote = daddr ;
xfrm_address_t * local = saddr ;
struct xfrm_tmpl * tmpl = & policy - > xfrm_vec [ i ] ;
if ( tmpl - > mode ) {
remote = & tmpl - > id . daddr ;
local = & tmpl - > saddr ;
}
x = xfrm_state_find ( remote , local , fl , tmpl , policy , & error , family ) ;
if ( x & & x - > km . state = = XFRM_STATE_VALID ) {
xfrm [ nx + + ] = x ;
daddr = remote ;
saddr = local ;
continue ;
}
if ( x ) {
error = ( x - > km . state = = XFRM_STATE_ERROR ?
- EINVAL : - EAGAIN ) ;
xfrm_state_put ( x ) ;
}
if ( ! tmpl - > optional )
goto fail ;
}
return nx ;
fail :
for ( nx - - ; nx > = 0 ; nx - - )
xfrm_state_put ( xfrm [ nx ] ) ;
return error ;
}
/* Check that the bundle accepts the flow and its components are
* still valid .
*/
static struct dst_entry *
xfrm_find_bundle ( struct flowi * fl , struct xfrm_policy * policy , unsigned short family )
{
struct dst_entry * x ;
struct xfrm_policy_afinfo * afinfo = xfrm_policy_get_afinfo ( family ) ;
if ( unlikely ( afinfo = = NULL ) )
return ERR_PTR ( - EINVAL ) ;
x = afinfo - > find_bundle ( fl , policy ) ;
xfrm_policy_put_afinfo ( afinfo ) ;
return x ;
}
/* Allocate chain of dst_entry's, attach known xfrm's, calculate
* all the metrics . . . Shortly , bundle a bundle .
*/
static int
xfrm_bundle_create ( struct xfrm_policy * policy , struct xfrm_state * * xfrm , int nx ,
struct flowi * fl , struct dst_entry * * dst_p ,
unsigned short family )
{
int err ;
struct xfrm_policy_afinfo * afinfo = xfrm_policy_get_afinfo ( family ) ;
if ( unlikely ( afinfo = = NULL ) )
return - EINVAL ;
err = afinfo - > bundle_create ( policy , xfrm , nx , fl , dst_p ) ;
xfrm_policy_put_afinfo ( afinfo ) ;
return err ;
}
static inline int policy_to_flow_dir ( int dir )
{
if ( XFRM_POLICY_IN = = FLOW_DIR_IN & &
XFRM_POLICY_OUT = = FLOW_DIR_OUT & &
XFRM_POLICY_FWD = = FLOW_DIR_FWD )
return dir ;
switch ( dir ) {
default :
case XFRM_POLICY_IN :
return FLOW_DIR_IN ;
case XFRM_POLICY_OUT :
return FLOW_DIR_OUT ;
case XFRM_POLICY_FWD :
return FLOW_DIR_FWD ;
} ;
}
static int stale_bundle ( struct dst_entry * dst ) ;
/* Main function: finds/creates a bundle for given flow.
*
* At the moment we eat a raw IP route . Mostly to speed up lookups
* on interfaces with disabled IPsec .
*/
int xfrm_lookup ( struct dst_entry * * dst_p , struct flowi * fl ,
struct sock * sk , int flags )
{
struct xfrm_policy * policy ;
struct xfrm_state * xfrm [ XFRM_MAX_DEPTH ] ;
struct dst_entry * dst , * dst_orig = * dst_p ;
int nx = 0 ;
int err ;
u32 genid ;
u16 family = dst_orig - > ops - > family ;
restart :
genid = atomic_read ( & flow_cache_genid ) ;
policy = NULL ;
if ( sk & & sk - > sk_policy [ 1 ] )
policy = xfrm_sk_policy_lookup ( sk , XFRM_POLICY_OUT , fl ) ;
if ( ! policy ) {
/* To accelerate a bit... */
if ( ( dst_orig - > flags & DST_NOXFRM ) | | ! xfrm_policy_list [ XFRM_POLICY_OUT ] )
return 0 ;
policy = flow_cache_lookup ( fl , family ,
policy_to_flow_dir ( XFRM_POLICY_OUT ) ,
xfrm_policy_lookup ) ;
}
if ( ! policy )
return 0 ;
policy - > curlft . use_time = ( unsigned long ) xtime . tv_sec ;
switch ( policy - > action ) {
case XFRM_POLICY_BLOCK :
/* Prohibit the flow */
xfrm_pol_put ( policy ) ;
return - EPERM ;
case XFRM_POLICY_ALLOW :
if ( policy - > xfrm_nr = = 0 ) {
/* Flow passes not transformed. */
xfrm_pol_put ( policy ) ;
return 0 ;
}
/* Try to find matching bundle.
*
* LATER : help from flow cache . It is optional , this
* is required only for output policy .
*/
dst = xfrm_find_bundle ( fl , policy , family ) ;
if ( IS_ERR ( dst ) ) {
xfrm_pol_put ( policy ) ;
return PTR_ERR ( dst ) ;
}
if ( dst )
break ;
nx = xfrm_tmpl_resolve ( policy , fl , xfrm , family ) ;
if ( unlikely ( nx < 0 ) ) {
err = nx ;
if ( err = = - EAGAIN & & flags ) {
DECLARE_WAITQUEUE ( wait , current ) ;
add_wait_queue ( & km_waitq , & wait ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
schedule ( ) ;
set_current_state ( TASK_RUNNING ) ;
remove_wait_queue ( & km_waitq , & wait ) ;
nx = xfrm_tmpl_resolve ( policy , fl , xfrm , family ) ;
if ( nx = = - EAGAIN & & signal_pending ( current ) ) {
err = - ERESTART ;
goto error ;
}
if ( nx = = - EAGAIN | |
genid ! = atomic_read ( & flow_cache_genid ) ) {
xfrm_pol_put ( policy ) ;
goto restart ;
}
err = nx ;
}
if ( err < 0 )
goto error ;
}
if ( nx = = 0 ) {
/* Flow passes not transformed. */
xfrm_pol_put ( policy ) ;
return 0 ;
}
dst = dst_orig ;
err = xfrm_bundle_create ( policy , xfrm , nx , fl , & dst , family ) ;
if ( unlikely ( err ) ) {
int i ;
for ( i = 0 ; i < nx ; i + + )
xfrm_state_put ( xfrm [ i ] ) ;
goto error ;
}
write_lock_bh ( & policy - > lock ) ;
if ( unlikely ( policy - > dead | | stale_bundle ( dst ) ) ) {
/* Wow! While we worked on resolving, this
* policy has gone . Retry . It is not paranoia ,
* we just cannot enlist new bundle to dead object .
* We can ' t enlist stable bundles either .
*/
write_unlock_bh ( & policy - > lock ) ;
xfrm_pol_put ( policy ) ;
if ( dst )
dst_free ( dst ) ;
goto restart ;
}
dst - > next = policy - > bundles ;
policy - > bundles = dst ;
dst_hold ( dst ) ;
write_unlock_bh ( & policy - > lock ) ;
}
* dst_p = dst ;
dst_release ( dst_orig ) ;
xfrm_pol_put ( policy ) ;
return 0 ;
error :
dst_release ( dst_orig ) ;
xfrm_pol_put ( policy ) ;
* dst_p = NULL ;
return err ;
}
EXPORT_SYMBOL ( xfrm_lookup ) ;
/* When skb is transformed back to its "native" form, we have to
* check policy restrictions . At the moment we make this in maximally
* stupid way . Shame on me . : - ) Of course , connected sockets must
* have policy cached at them .
*/
static inline int
xfrm_state_ok ( struct xfrm_tmpl * tmpl , struct xfrm_state * x ,
unsigned short family )
{
if ( xfrm_state_kern ( x ) )
return tmpl - > optional & & ! xfrm_state_addr_cmp ( tmpl , x , family ) ;
return x - > id . proto = = tmpl - > id . proto & &
( x - > id . spi = = tmpl - > id . spi | | ! tmpl - > id . spi ) & &
( x - > props . reqid = = tmpl - > reqid | | ! tmpl - > reqid ) & &
x - > props . mode = = tmpl - > mode & &
( tmpl - > aalgos & ( 1 < < x - > props . aalgo ) ) & &
! ( x - > props . mode & & xfrm_state_addr_cmp ( tmpl , x , family ) ) ;
}
static inline int
xfrm_policy_ok ( struct xfrm_tmpl * tmpl , struct sec_path * sp , int start ,
unsigned short family )
{
int idx = start ;
if ( tmpl - > optional ) {
if ( ! tmpl - > mode )
return start ;
} else
start = - 1 ;
for ( ; idx < sp - > len ; idx + + ) {
if ( xfrm_state_ok ( tmpl , sp - > x [ idx ] . xvec , family ) )
return + + idx ;
if ( sp - > x [ idx ] . xvec - > props . mode )
break ;
}
return start ;
}
static int
_decode_session ( struct sk_buff * skb , struct flowi * fl , unsigned short family )
{
struct xfrm_policy_afinfo * afinfo = xfrm_policy_get_afinfo ( family ) ;
if ( unlikely ( afinfo = = NULL ) )
return - EAFNOSUPPORT ;
afinfo - > decode_session ( skb , fl ) ;
xfrm_policy_put_afinfo ( afinfo ) ;
return 0 ;
}
static inline int secpath_has_tunnel ( struct sec_path * sp , int k )
{
for ( ; k < sp - > len ; k + + ) {
if ( sp - > x [ k ] . xvec - > props . mode )
return 1 ;
}
return 0 ;
}
int __xfrm_policy_check ( struct sock * sk , int dir , struct sk_buff * skb ,
unsigned short family )
{
struct xfrm_policy * pol ;
struct flowi fl ;
if ( _decode_session ( skb , & fl , family ) < 0 )
return 0 ;
/* First, check used SA against their selectors. */
if ( skb - > sp ) {
int i ;
for ( i = skb - > sp - > len - 1 ; i > = 0 ; i - - ) {
struct sec_decap_state * xvec = & ( skb - > sp - > x [ i ] ) ;
if ( ! xfrm_selector_match ( & xvec - > xvec - > sel , & fl , family ) )
return 0 ;
/* If there is a post_input processor, try running it */
if ( xvec - > xvec - > type - > post_input & &
( xvec - > xvec - > type - > post_input ) ( xvec - > xvec ,
& ( xvec - > decap ) ,
skb ) ! = 0 )
return 0 ;
}
}
pol = NULL ;
if ( sk & & sk - > sk_policy [ dir ] )
pol = xfrm_sk_policy_lookup ( sk , dir , & fl ) ;
if ( ! pol )
pol = flow_cache_lookup ( & fl , family ,
policy_to_flow_dir ( dir ) ,
xfrm_policy_lookup ) ;
if ( ! pol )
return ! skb - > sp | | ! secpath_has_tunnel ( skb - > sp , 0 ) ;
pol - > curlft . use_time = ( unsigned long ) xtime . tv_sec ;
if ( pol - > action = = XFRM_POLICY_ALLOW ) {
struct sec_path * sp ;
static struct sec_path dummy ;
int i , k ;
if ( ( sp = skb - > sp ) = = NULL )
sp = & dummy ;
/* For each tunnel xfrm, find the first matching tmpl.
* For each tmpl before that , find corresponding xfrm .
* Order is _important_ . Later we will implement
* some barriers , but at the moment barriers
* are implied between each two transformations .
*/
for ( i = pol - > xfrm_nr - 1 , k = 0 ; i > = 0 ; i - - ) {
k = xfrm_policy_ok ( pol - > xfrm_vec + i , sp , k , family ) ;
if ( k < 0 )
goto reject ;
}
if ( secpath_has_tunnel ( sp , k ) )
goto reject ;
xfrm_pol_put ( pol ) ;
return 1 ;
}
reject :
xfrm_pol_put ( pol ) ;
return 0 ;
}
EXPORT_SYMBOL ( __xfrm_policy_check ) ;
int __xfrm_route_forward ( struct sk_buff * skb , unsigned short family )
{
struct flowi fl ;
if ( _decode_session ( skb , & fl , family ) < 0 )
return 0 ;
return xfrm_lookup ( & skb - > dst , & fl , NULL , 0 ) = = 0 ;
}
EXPORT_SYMBOL ( __xfrm_route_forward ) ;
/* Optimize later using cookies and generation ids. */
static struct dst_entry * xfrm_dst_check ( struct dst_entry * dst , u32 cookie )
{
if ( ! stale_bundle ( dst ) )
return dst ;
return NULL ;
}
static int stale_bundle ( struct dst_entry * dst )
{
return ! xfrm_bundle_ok ( ( struct xfrm_dst * ) dst , NULL , AF_UNSPEC ) ;
}
2005-05-03 16:27:10 -07:00
void xfrm_dst_ifdown ( struct dst_entry * dst , struct net_device * dev )
2005-04-16 15:20:36 -07:00
{
while ( ( dst = dst - > child ) & & dst - > xfrm & & dst - > dev = = dev ) {
dst - > dev = & loopback_dev ;
dev_hold ( & loopback_dev ) ;
dev_put ( dev ) ;
}
}
2005-05-03 16:27:10 -07:00
EXPORT_SYMBOL ( xfrm_dst_ifdown ) ;
2005-04-16 15:20:36 -07:00
static void xfrm_link_failure ( struct sk_buff * skb )
{
/* Impossible. Such dst must be popped before reaches point of failure. */
return ;
}
static struct dst_entry * xfrm_negative_advice ( struct dst_entry * dst )
{
if ( dst ) {
if ( dst - > obsolete ) {
dst_release ( dst ) ;
dst = NULL ;
}
}
return dst ;
}
static void xfrm_prune_bundles ( int ( * func ) ( struct dst_entry * ) )
{
int i ;
struct xfrm_policy * pol ;
struct dst_entry * dst , * * dstp , * gc_list = NULL ;
read_lock_bh ( & xfrm_policy_lock ) ;
for ( i = 0 ; i < 2 * XFRM_POLICY_MAX ; i + + ) {
for ( pol = xfrm_policy_list [ i ] ; pol ; pol = pol - > next ) {
write_lock ( & pol - > lock ) ;
dstp = & pol - > bundles ;
while ( ( dst = * dstp ) ! = NULL ) {
if ( func ( dst ) ) {
* dstp = dst - > next ;
dst - > next = gc_list ;
gc_list = dst ;
} else {
dstp = & dst - > next ;
}
}
write_unlock ( & pol - > lock ) ;
}
}
read_unlock_bh ( & xfrm_policy_lock ) ;
while ( gc_list ) {
dst = gc_list ;
gc_list = dst - > next ;
dst_free ( dst ) ;
}
}
static int unused_bundle ( struct dst_entry * dst )
{
return ! atomic_read ( & dst - > __refcnt ) ;
}
static void __xfrm_garbage_collect ( void )
{
xfrm_prune_bundles ( unused_bundle ) ;
}
int xfrm_flush_bundles ( void )
{
xfrm_prune_bundles ( stale_bundle ) ;
return 0 ;
}
void xfrm_init_pmtu ( struct dst_entry * dst )
{
do {
struct xfrm_dst * xdst = ( struct xfrm_dst * ) dst ;
u32 pmtu , route_mtu_cached ;
pmtu = dst_mtu ( dst - > child ) ;
xdst - > child_mtu_cached = pmtu ;
pmtu = xfrm_state_mtu ( dst - > xfrm , pmtu ) ;
route_mtu_cached = dst_mtu ( xdst - > route ) ;
xdst - > route_mtu_cached = route_mtu_cached ;
if ( pmtu > route_mtu_cached )
pmtu = route_mtu_cached ;
dst - > metrics [ RTAX_MTU - 1 ] = pmtu ;
} while ( ( dst = dst - > next ) ) ;
}
EXPORT_SYMBOL ( xfrm_init_pmtu ) ;
/* Check that the bundle accepts the flow and its components are
* still valid .
*/
int xfrm_bundle_ok ( struct xfrm_dst * first , struct flowi * fl , int family )
{
struct dst_entry * dst = & first - > u . dst ;
struct xfrm_dst * last ;
u32 mtu ;
2005-05-26 12:58:04 -07:00
if ( ! dst_check ( dst - > path , ( ( struct xfrm_dst * ) dst ) - > path_cookie ) | |
2005-04-16 15:20:36 -07:00
( dst - > dev & & ! netif_running ( dst - > dev ) ) )
return 0 ;
last = NULL ;
do {
struct xfrm_dst * xdst = ( struct xfrm_dst * ) dst ;
if ( fl & & ! xfrm_selector_match ( & dst - > xfrm - > sel , fl , family ) )
return 0 ;
if ( dst - > xfrm - > km . state ! = XFRM_STATE_VALID )
return 0 ;
mtu = dst_mtu ( dst - > child ) ;
if ( xdst - > child_mtu_cached ! = mtu ) {
last = xdst ;
xdst - > child_mtu_cached = mtu ;
}
2005-05-26 12:58:04 -07:00
if ( ! dst_check ( xdst - > route , xdst - > route_cookie ) )
2005-04-16 15:20:36 -07:00
return 0 ;
mtu = dst_mtu ( xdst - > route ) ;
if ( xdst - > route_mtu_cached ! = mtu ) {
last = xdst ;
xdst - > route_mtu_cached = mtu ;
}
dst = dst - > child ;
} while ( dst - > xfrm ) ;
if ( likely ( ! last ) )
return 1 ;
mtu = last - > child_mtu_cached ;
for ( ; ; ) {
dst = & last - > u . dst ;
mtu = xfrm_state_mtu ( dst - > xfrm , mtu ) ;
if ( mtu > last - > route_mtu_cached )
mtu = last - > route_mtu_cached ;
dst - > metrics [ RTAX_MTU - 1 ] = mtu ;
if ( last = = first )
break ;
last = last - > u . next ;
last - > child_mtu_cached = mtu ;
}
return 1 ;
}
EXPORT_SYMBOL ( xfrm_bundle_ok ) ;
/* Well... that's _TASK_. We need to scan through transformation
* list and figure out what mss tcp should generate in order to
* final datagram fit to mtu . Mama mia . . . : - )
*
* Apparently , some easy way exists , but we used to choose the most
* bizarre ones . : - ) So , raising Kalashnikov . . . tra - ta - ta .
*
* Consider this function as something like dark humour . : - )
*/
static int xfrm_get_mss ( struct dst_entry * dst , u32 mtu )
{
int res = mtu - dst - > header_len ;
for ( ; ; ) {
struct dst_entry * d = dst ;
int m = res ;
do {
struct xfrm_state * x = d - > xfrm ;
if ( x ) {
spin_lock_bh ( & x - > lock ) ;
if ( x - > km . state = = XFRM_STATE_VALID & &
x - > type & & x - > type - > get_max_size )
m = x - > type - > get_max_size ( d - > xfrm , m ) ;
else
m + = x - > props . header_len ;
spin_unlock_bh ( & x - > lock ) ;
}
} while ( ( d = d - > child ) ! = NULL ) ;
if ( m < = mtu )
break ;
res - = ( m - mtu ) ;
if ( res < 88 )
return mtu ;
}
return res + dst - > header_len ;
}
int xfrm_policy_register_afinfo ( struct xfrm_policy_afinfo * afinfo )
{
int err = 0 ;
if ( unlikely ( afinfo = = NULL ) )
return - EINVAL ;
if ( unlikely ( afinfo - > family > = NPROTO ) )
return - EAFNOSUPPORT ;
write_lock ( & xfrm_policy_afinfo_lock ) ;
if ( unlikely ( xfrm_policy_afinfo [ afinfo - > family ] ! = NULL ) )
err = - ENOBUFS ;
else {
struct dst_ops * dst_ops = afinfo - > dst_ops ;
if ( likely ( dst_ops - > kmem_cachep = = NULL ) )
dst_ops - > kmem_cachep = xfrm_dst_cache ;
if ( likely ( dst_ops - > check = = NULL ) )
dst_ops - > check = xfrm_dst_check ;
if ( likely ( dst_ops - > negative_advice = = NULL ) )
dst_ops - > negative_advice = xfrm_negative_advice ;
if ( likely ( dst_ops - > link_failure = = NULL ) )
dst_ops - > link_failure = xfrm_link_failure ;
if ( likely ( dst_ops - > get_mss = = NULL ) )
dst_ops - > get_mss = xfrm_get_mss ;
if ( likely ( afinfo - > garbage_collect = = NULL ) )
afinfo - > garbage_collect = __xfrm_garbage_collect ;
xfrm_policy_afinfo [ afinfo - > family ] = afinfo ;
}
write_unlock ( & xfrm_policy_afinfo_lock ) ;
return err ;
}
EXPORT_SYMBOL ( xfrm_policy_register_afinfo ) ;
int xfrm_policy_unregister_afinfo ( struct xfrm_policy_afinfo * afinfo )
{
int err = 0 ;
if ( unlikely ( afinfo = = NULL ) )
return - EINVAL ;
if ( unlikely ( afinfo - > family > = NPROTO ) )
return - EAFNOSUPPORT ;
write_lock ( & xfrm_policy_afinfo_lock ) ;
if ( likely ( xfrm_policy_afinfo [ afinfo - > family ] ! = NULL ) ) {
if ( unlikely ( xfrm_policy_afinfo [ afinfo - > family ] ! = afinfo ) )
err = - EINVAL ;
else {
struct dst_ops * dst_ops = afinfo - > dst_ops ;
xfrm_policy_afinfo [ afinfo - > family ] = NULL ;
dst_ops - > kmem_cachep = NULL ;
dst_ops - > check = NULL ;
dst_ops - > negative_advice = NULL ;
dst_ops - > link_failure = NULL ;
dst_ops - > get_mss = NULL ;
afinfo - > garbage_collect = NULL ;
}
}
write_unlock ( & xfrm_policy_afinfo_lock ) ;
return err ;
}
EXPORT_SYMBOL ( xfrm_policy_unregister_afinfo ) ;
static struct xfrm_policy_afinfo * xfrm_policy_get_afinfo ( unsigned short family )
{
struct xfrm_policy_afinfo * afinfo ;
if ( unlikely ( family > = NPROTO ) )
return NULL ;
read_lock ( & xfrm_policy_afinfo_lock ) ;
afinfo = xfrm_policy_afinfo [ family ] ;
if ( likely ( afinfo ! = NULL ) )
read_lock ( & afinfo - > lock ) ;
read_unlock ( & xfrm_policy_afinfo_lock ) ;
return afinfo ;
}
static void xfrm_policy_put_afinfo ( struct xfrm_policy_afinfo * afinfo )
{
if ( unlikely ( afinfo = = NULL ) )
return ;
read_unlock ( & afinfo - > lock ) ;
}
static int xfrm_dev_event ( struct notifier_block * this , unsigned long event , void * ptr )
{
switch ( event ) {
case NETDEV_DOWN :
xfrm_flush_bundles ( ) ;
}
return NOTIFY_DONE ;
}
static struct notifier_block xfrm_dev_notifier = {
xfrm_dev_event ,
NULL ,
0
} ;
static void __init xfrm_policy_init ( void )
{
xfrm_dst_cache = kmem_cache_create ( " xfrm_dst_cache " ,
sizeof ( struct xfrm_dst ) ,
0 , SLAB_HWCACHE_ALIGN ,
NULL , NULL ) ;
if ( ! xfrm_dst_cache )
panic ( " XFRM: failed to allocate xfrm_dst_cache \n " ) ;
INIT_WORK ( & xfrm_policy_gc_work , xfrm_policy_gc_task , NULL ) ;
register_netdevice_notifier ( & xfrm_dev_notifier ) ;
}
void __init xfrm_init ( void )
{
xfrm_state_init ( ) ;
xfrm_policy_init ( ) ;
xfrm_input_init ( ) ;
}