2005-04-17 02:20:36 +04:00
/*
* INET An implementation of the TCP / IP protocol suite for the LINUX
* operating system . INET is implemented using the BSD Socket
* interface as the means of communication with the user level .
*
* IPv4 Forwarding Information Base : policy rules .
*
* Authors : Alexey Kuznetsov , < kuznet @ ms2 . inr . ac . ru >
2006-08-04 14:39:22 +04:00
* Thomas Graf < tgraf @ suug . ch >
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 .
*
* Fixes :
* Rani Assaf : local_rule cannot be deleted
* Marc Boucher : routing by fwmark
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/netlink.h>
2006-08-04 14:39:22 +04:00
# include <linux/inetdevice.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
2006-03-21 04:18:53 +03:00
# include <linux/list.h>
# include <linux/rcupdate.h>
2005-04-17 02:20:36 +04:00
# include <net/ip.h>
# include <net/route.h>
# include <net/tcp.h>
# include <net/ip_fib.h>
2006-08-04 14:39:22 +04:00
# include <net/fib_rules.h>
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
static struct fib_rules_ops fib4_rules_ops ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
struct fib4_rule
2005-04-17 02:20:36 +04:00
{
2006-08-04 14:39:22 +04:00
struct fib_rule common ;
u8 dst_len ;
u8 src_len ;
u8 tos ;
u32 src ;
u32 srcmask ;
u32 dst ;
u32 dstmask ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_IP_ROUTE_FWMARK
2006-08-04 14:39:22 +04:00
u32 fwmark ;
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_NET_CLS_ROUTE
2006-08-04 14:39:22 +04:00
u32 tclassid ;
2005-04-17 02:20:36 +04:00
# endif
} ;
2006-08-04 14:39:22 +04:00
static struct fib4_rule default_rule = {
. common = {
. refcnt = ATOMIC_INIT ( 2 ) ,
. pref = 0x7FFF ,
. table = RT_TABLE_DEFAULT ,
. action = FR_ACT_TO_TBL ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2006-08-04 14:39:22 +04:00
static struct fib4_rule main_rule = {
. common = {
. refcnt = ATOMIC_INIT ( 2 ) ,
. pref = 0x7FFE ,
. table = RT_TABLE_MAIN ,
. action = FR_ACT_TO_TBL ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2006-08-04 14:39:22 +04:00
static struct fib4_rule local_rule = {
. common = {
. refcnt = ATOMIC_INIT ( 2 ) ,
. table = RT_TABLE_LOCAL ,
. action = FR_ACT_TO_TBL ,
. flags = FIB_RULE_PERMANENT ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2006-08-04 14:39:22 +04:00
static LIST_HEAD ( fib4_rules ) ;
# ifdef CONFIG_NET_CLS_ROUTE
u32 fib_rules_tclass ( struct fib_result * res )
{
return res - > r ? ( ( struct fib4_rule * ) res - > r ) - > tclassid : 0 ;
}
# endif
2006-03-21 04:18:53 +03:00
2006-08-04 14:39:22 +04:00
int fib_lookup ( struct flowi * flp , struct fib_result * res )
{
struct fib_lookup_arg arg = {
. result = res ,
} ;
int err ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
err = fib_rules_lookup ( & fib4_rules_ops , flp , 0 , & arg ) ;
res - > r = arg . rule ;
2006-03-23 12:16:06 +03:00
2006-08-04 14:39:22 +04:00
return err ;
}
int fib4_rule_action ( struct fib_rule * rule , struct flowi * flp , int flags ,
struct fib_lookup_arg * arg )
2005-04-17 02:20:36 +04:00
{
2006-08-04 14:39:22 +04:00
int err = - EAGAIN ;
struct fib_table * tbl ;
switch ( rule - > action ) {
case FR_ACT_TO_TBL :
break ;
case FR_ACT_UNREACHABLE :
err = - ENETUNREACH ;
goto errout ;
case FR_ACT_PROHIBIT :
err = - EACCES ;
goto errout ;
case FR_ACT_BLACKHOLE :
default :
err = - EINVAL ;
goto errout ;
2005-04-17 02:20:36 +04:00
}
2006-08-04 14:39:22 +04:00
if ( ( tbl = fib_get_table ( rule - > table ) ) = = NULL )
goto errout ;
err = tbl - > tb_lookup ( tbl , flp , ( struct fib_result * ) arg - > result ) ;
if ( err > 0 )
err = - EAGAIN ;
errout :
2005-04-17 02:20:36 +04:00
return err ;
}
2006-08-04 14:39:22 +04:00
void fib_select_default ( const struct flowi * flp , struct fib_result * res )
{
if ( res - > r & & res - > r - > action = = FR_ACT_TO_TBL & &
FIB_RES_GW ( * res ) & & FIB_RES_NH ( * res ) . nh_scope = = RT_SCOPE_LINK ) {
struct fib_table * tb ;
if ( ( tb = fib_get_table ( res - > r - > table ) ) ! = NULL )
tb - > tb_select_default ( tb , flp , res ) ;
}
}
static int fib4_rule_match ( struct fib_rule * rule , struct flowi * fl , int flags )
{
struct fib4_rule * r = ( struct fib4_rule * ) rule ;
u32 daddr = fl - > fl4_dst ;
u32 saddr = fl - > fl4_src ;
if ( ( ( saddr ^ r - > src ) & r - > srcmask ) | |
( ( daddr ^ r - > dst ) & r - > dstmask ) )
return 0 ;
if ( r - > tos & & ( r - > tos ! = fl - > fl4_tos ) )
return 0 ;
# ifdef CONFIG_IP_ROUTE_FWMARK
if ( r - > fwmark & & ( r - > fwmark ! = fl - > fl4_fwmark ) )
return 0 ;
# endif
return 1 ;
}
2005-04-17 02:20:36 +04:00
static struct fib_table * fib_empty_table ( void )
{
int id ;
for ( id = 1 ; id < = RT_TABLE_MAX ; id + + )
if ( fib_tables [ id ] = = NULL )
return __fib_new_table ( id ) ;
return NULL ;
}
2006-08-04 14:39:22 +04:00
static struct nla_policy fib4_rule_policy [ FRA_MAX + 1 ] __read_mostly = {
[ FRA_IFNAME ] = { . type = NLA_STRING } ,
[ FRA_PRIORITY ] = { . type = NLA_U32 } ,
[ FRA_SRC ] = { . type = NLA_U32 } ,
[ FRA_DST ] = { . type = NLA_U32 } ,
[ FRA_FWMARK ] = { . type = NLA_U32 } ,
[ FRA_FLOW ] = { . type = NLA_U32 } ,
} ;
2006-03-21 04:18:53 +03:00
2006-08-04 14:39:22 +04:00
static int fib4_rule_configure ( struct fib_rule * rule , struct sk_buff * skb ,
struct nlmsghdr * nlh , struct fib_rule_hdr * frh ,
struct nlattr * * tb )
2005-04-17 02:20:36 +04:00
{
2006-08-04 14:39:22 +04:00
int err = - EINVAL ;
struct fib4_rule * rule4 = ( struct fib4_rule * ) rule ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
if ( frh - > src_len > 32 | | frh - > dst_len > 32 | |
( frh - > tos & ~ IPTOS_TOS_MASK ) )
goto errout ;
2006-03-21 04:18:53 +03:00
2006-08-04 14:39:22 +04:00
if ( rule - > table = = RT_TABLE_UNSPEC ) {
if ( rule - > action = = FR_ACT_TO_TBL ) {
struct fib_table * table ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
table = fib_empty_table ( ) ;
if ( table = = NULL ) {
err = - ENOBUFS ;
goto errout ;
}
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
rule - > table = table - > tb_id ;
2005-04-17 02:20:36 +04:00
}
}
2006-08-04 14:39:22 +04:00
if ( tb [ FRA_SRC ] )
rule4 - > src = nla_get_u32 ( tb [ FRA_SRC ] ) ;
2006-03-21 04:18:53 +03:00
2006-08-04 14:39:22 +04:00
if ( tb [ FRA_DST ] )
rule4 - > dst = nla_get_u32 ( tb [ FRA_DST ] ) ;
2006-03-21 04:18:53 +03:00
2006-08-04 14:39:22 +04:00
# ifdef CONFIG_IP_ROUTE_FWMARK
if ( tb [ FRA_FWMARK ] )
rule4 - > fwmark = nla_get_u32 ( tb [ FRA_FWMARK ] ) ;
# endif
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_CLS_ROUTE
2006-08-04 14:39:22 +04:00
if ( tb [ FRA_FLOW ] )
rule4 - > tclassid = nla_get_u32 ( tb [ FRA_FLOW ] ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-08-04 14:39:22 +04:00
rule4 - > src_len = frh - > src_len ;
rule4 - > srcmask = inet_make_mask ( rule4 - > src_len ) ;
rule4 - > dst_len = frh - > dst_len ;
rule4 - > dstmask = inet_make_mask ( rule4 - > dst_len ) ;
rule4 - > tos = frh - > tos ;
2006-03-21 04:18:53 +03:00
2006-08-04 14:39:22 +04:00
err = 0 ;
errout :
return err ;
2005-04-17 02:20:36 +04:00
}
2006-08-04 14:39:22 +04:00
static int fib4_rule_compare ( struct fib_rule * rule , struct fib_rule_hdr * frh ,
struct nlattr * * tb )
2005-04-17 02:20:36 +04:00
{
2006-08-04 14:39:22 +04:00
struct fib4_rule * rule4 = ( struct fib4_rule * ) rule ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
if ( frh - > src_len & & ( rule4 - > src_len ! = frh - > src_len ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
if ( frh - > dst_len & & ( rule4 - > dst_len ! = frh - > dst_len ) )
return 0 ;
2006-03-21 04:18:53 +03:00
2006-08-04 14:39:22 +04:00
if ( frh - > tos & & ( rule4 - > tos ! = frh - > tos ) )
return 0 ;
2006-03-21 04:18:53 +03:00
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_IP_ROUTE_FWMARK
2006-08-04 14:39:22 +04:00
if ( tb [ FRA_FWMARK ] & & ( rule4 - > fwmark ! = nla_get_u32 ( tb [ FRA_FWMARK ] ) ) )
return 0 ;
2005-04-17 02:20:36 +04:00
# endif
2006-08-04 14:39:22 +04:00
# ifdef CONFIG_NET_CLS_ROUTE
if ( tb [ FRA_FLOW ] & & ( rule4 - > tclassid ! = nla_get_u32 ( tb [ FRA_FLOW ] ) ) )
return 0 ;
# endif
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
if ( tb [ FRA_SRC ] & & ( rule4 - > src ! = nla_get_u32 ( tb [ FRA_SRC ] ) ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
if ( tb [ FRA_DST ] & & ( rule4 - > dst ! = nla_get_u32 ( tb [ FRA_DST ] ) ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2006-08-04 14:39:22 +04:00
static int fib4_rule_fill ( struct fib_rule * rule , struct sk_buff * skb ,
struct nlmsghdr * nlh , struct fib_rule_hdr * frh )
{
struct fib4_rule * rule4 = ( struct fib4_rule * ) rule ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
frh - > family = AF_INET ;
frh - > dst_len = rule4 - > dst_len ;
frh - > src_len = rule4 - > src_len ;
frh - > tos = rule4 - > tos ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_IP_ROUTE_FWMARK
2006-08-04 14:39:22 +04:00
if ( rule4 - > fwmark )
NLA_PUT_U32 ( skb , FRA_FWMARK , rule4 - > fwmark ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-08-04 14:39:22 +04:00
if ( rule4 - > dst_len )
NLA_PUT_U32 ( skb , FRA_DST , rule4 - > dst ) ;
if ( rule4 - > src_len )
NLA_PUT_U32 ( skb , FRA_SRC , rule4 - > src ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_NET_CLS_ROUTE
2006-08-04 14:39:22 +04:00
if ( rule4 - > tclassid )
NLA_PUT_U32 ( skb , FRA_FLOW , rule4 - > tclassid ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-08-04 14:39:22 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
nla_put_failure :
return - ENOBUFS ;
2005-04-17 02:20:36 +04:00
}
2006-08-04 14:39:22 +04:00
int fib4_rules_dump ( struct sk_buff * skb , struct netlink_callback * cb )
{
return fib_rules_dump ( skb , cb , AF_INET ) ;
}
2006-03-21 04:18:53 +03:00
2006-08-04 14:39:22 +04:00
static u32 fib4_rule_default_pref ( void )
2006-03-23 12:16:06 +03:00
{
2006-08-04 14:39:22 +04:00
struct list_head * pos ;
struct fib_rule * rule ;
if ( ! list_empty ( & fib4_rules ) ) {
pos = fib4_rules . next ;
if ( pos - > next ! = & fib4_rules ) {
rule = list_entry ( pos - > next , struct fib_rule , list ) ;
if ( rule - > pref )
return rule - > pref - 1 ;
}
2006-03-23 12:16:06 +03:00
}
2006-08-04 14:39:22 +04:00
return 0 ;
2006-03-23 12:16:06 +03:00
}
2006-08-04 14:39:22 +04:00
static struct fib_rules_ops fib4_rules_ops = {
. family = AF_INET ,
. rule_size = sizeof ( struct fib4_rule ) ,
. action = fib4_rule_action ,
. match = fib4_rule_match ,
. configure = fib4_rule_configure ,
. compare = fib4_rule_compare ,
. fill = fib4_rule_fill ,
. default_pref = fib4_rule_default_pref ,
. nlgroup = RTNLGRP_IPV4_RULE ,
. policy = fib4_rule_policy ,
. rules_list = & fib4_rules ,
. owner = THIS_MODULE ,
} ;
void __init fib4_rules_init ( void )
2005-04-17 02:20:36 +04:00
{
2006-08-04 14:39:22 +04:00
list_add_tail ( & local_rule . common . list , & fib4_rules ) ;
list_add_tail ( & main_rule . common . list , & fib4_rules ) ;
list_add_tail ( & default_rule . common . list , & fib4_rules ) ;
2005-04-17 02:20:36 +04:00
2006-08-04 14:39:22 +04:00
fib_rules_register ( & fib4_rules_ops ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-04 14:39:22 +04:00
void __exit fib4_rules_cleanup ( void )
2005-04-17 02:20:36 +04:00
{
2006-08-04 14:39:22 +04:00
fib_rules_unregister ( & fib4_rules_ops ) ;
2005-04-17 02:20:36 +04:00
}