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 >
2010-10-05 00:00:18 +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 :
2010-10-05 00:00:18 +04:00
* Rani Assaf : local_rule cannot be deleted
2005-04-17 02:20:36 +04:00
* 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>
2011-07-15 19:47:34 +04:00
# include <linux/export.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
2010-10-05 00:00:18 +04:00
struct fib4_rule {
2006-08-04 14:39:22 +04:00
struct fib_rule common ;
u8 dst_len ;
u8 src_len ;
u8 tos ;
2006-09-28 05:40:00 +04:00
__be32 src ;
__be32 srcmask ;
__be32 dst ;
__be32 dstmask ;
2011-01-14 15:36:42 +03:00
# ifdef CONFIG_IP_ROUTE_CLASSID
2006-08-04 14:39:22 +04:00
u32 tclassid ;
2005-04-17 02:20:36 +04:00
# endif
} ;
2012-07-06 09:13:13 +04:00
int __fib_lookup ( struct net * net , struct flowi4 * flp , struct fib_result * res )
2006-08-04 14:39:22 +04:00
{
struct fib_lookup_arg arg = {
. result = res ,
2010-10-05 14:41:36 +04:00
. flags = FIB_LOOKUP_NOREF ,
2006-08-04 14:39:22 +04:00
} ;
int err ;
2005-04-17 02:20:36 +04:00
2011-03-12 03:54:08 +03:00
err = fib_rules_lookup ( net - > ipv4 . rules_ops , flowi4_to_flowi ( flp ) , 0 , & arg ) ;
2012-07-13 19:21:29 +04:00
# ifdef CONFIG_IP_ROUTE_CLASSID
if ( arg . rule )
res - > tclassid = ( ( struct fib4_rule * ) arg . rule ) - > tclassid ;
else
res - > tclassid = 0 ;
# endif
2014-11-14 14:14:32 +03:00
if ( err = = - ESRCH )
err = - ENETUNREACH ;
2006-08-04 14:39:22 +04:00
return err ;
}
2012-07-06 09:13:13 +04:00
EXPORT_SYMBOL_GPL ( __fib_lookup ) ;
2006-08-04 14:39:22 +04:00
2006-08-08 08:50:48 +04:00
static 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 :
2014-12-31 21:56:24 +03:00
return - ENETUNREACH ;
2006-08-04 14:39:22 +04:00
case FR_ACT_PROHIBIT :
2014-12-31 21:56:24 +03:00
return - EACCES ;
2006-08-04 14:39:22 +04:00
case FR_ACT_BLACKHOLE :
default :
2014-12-31 21:56:24 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-08-04 14:39:22 +04:00
2014-12-31 21:56:24 +03:00
rcu_read_lock ( ) ;
2010-10-05 00:00:18 +04:00
tbl = fib_get_table ( rule - > fr_net , rule - > table ) ;
2014-12-31 21:56:24 +03:00
if ( tbl )
err = fib_table_lookup ( tbl , & flp - > u . ip4 ,
( struct fib_result * ) arg - > result ,
arg - > flags ) ;
2006-08-04 14:39:22 +04:00
2014-12-31 21:56:24 +03:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2013-08-01 04:17:15 +04:00
static bool fib4_rule_suppress ( struct fib_rule * rule , struct fib_lookup_arg * arg )
{
2013-08-02 19:19:56 +04:00
struct fib_result * result = ( struct fib_result * ) arg - > result ;
2013-12-11 02:21:25 +04:00
struct net_device * dev = NULL ;
if ( result - > fi )
dev = result - > fi - > fib_dev ;
2013-08-02 19:19:56 +04:00
2013-08-01 04:17:15 +04:00
/* do not accept result if the route does
* not meet the required prefix length
*/
2013-08-03 16:14:43 +04:00
if ( result - > prefixlen < = rule - > suppress_prefixlen )
2013-08-02 19:19:56 +04:00
goto suppress_route ;
/* do not accept result if the route uses a device
* belonging to a forbidden interface group
*/
if ( rule - > suppress_ifgroup ! = - 1 & & dev & & dev - > group = = rule - > suppress_ifgroup )
goto suppress_route ;
2013-08-01 04:17:15 +04:00
return false ;
2013-08-02 19:19:56 +04:00
suppress_route :
if ( ! ( arg - > flags & FIB_LOOKUP_NOREF ) )
fib_info_put ( result - > fi ) ;
return true ;
2013-08-01 04:17:15 +04:00
}
2006-08-04 14:39:22 +04:00
static int fib4_rule_match ( struct fib_rule * rule , struct flowi * fl , int flags )
{
struct fib4_rule * r = ( struct fib4_rule * ) rule ;
2011-03-12 10:02:42 +03:00
struct flowi4 * fl4 = & fl - > u . ip4 ;
__be32 daddr = fl4 - > daddr ;
__be32 saddr = fl4 - > saddr ;
2006-08-04 14:39:22 +04:00
if ( ( ( saddr ^ r - > src ) & r - > srcmask ) | |
( ( daddr ^ r - > dst ) & r - > dstmask ) )
return 0 ;
2011-03-12 10:02:42 +03:00
if ( r - > tos & & ( r - > tos ! = fl4 - > flowi4_tos ) )
2006-08-04 14:39:22 +04:00
return 0 ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
2008-01-10 14:24:11 +03:00
static struct fib_table * fib_empty_table ( struct net * net )
2005-04-17 02:20:36 +04:00
{
2006-08-11 10:08:33 +04:00
u32 id ;
2005-04-17 02:20:36 +04:00
for ( id = 1 ; id < = RT_TABLE_MAX ; id + + )
2008-01-10 14:24:11 +03:00
if ( fib_get_table ( net , id ) = = NULL )
return fib_new_table ( net , id ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2007-06-05 23:38:30 +04:00
static const struct nla_policy fib4_rule_policy [ FRA_MAX + 1 ] = {
2006-11-10 02:22:48 +03:00
FRA_GENERIC_POLICY ,
2006-08-04 14:39:22 +04:00
[ 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 ,
2009-05-11 09:52:49 +04:00
struct fib_rule_hdr * frh ,
2006-08-04 14:39:22 +04:00
struct nlattr * * tb )
2005-04-17 02:20:36 +04:00
{
2008-03-25 20:26:21 +03:00
struct net * net = sock_net ( skb - > sk ) ;
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
2007-03-24 22:46:02 +03:00
if ( frh - > tos & ~ IPTOS_TOS_MASK )
2006-08-04 14:39:22 +04:00
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
2008-01-10 14:27:51 +03:00
table = fib_empty_table ( net ) ;
2006-08-04 14:39:22 +04:00
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
}
}
2007-03-24 22:46:02 +03:00
if ( frh - > src_len )
2006-09-28 05:40:27 +04:00
rule4 - > src = nla_get_be32 ( tb [ FRA_SRC ] ) ;
2006-03-21 04:18:53 +03:00
2007-03-24 22:46:02 +03:00
if ( frh - > dst_len )
2006-09-28 05:40:27 +04:00
rule4 - > dst = nla_get_be32 ( tb [ FRA_DST ] ) ;
2006-03-21 04:18:53 +03:00
2011-01-14 15:36:42 +03:00
# ifdef CONFIG_IP_ROUTE_CLASSID
2012-06-29 12:32:45 +04:00
if ( tb [ FRA_FLOW ] ) {
2006-08-04 14:39:22 +04:00
rule4 - > tclassid = nla_get_u32 ( tb [ FRA_FLOW ] ) ;
2012-06-29 12:32:45 +04:00
if ( rule4 - > tclassid )
2012-07-06 09:13:13 +04:00
net - > ipv4 . fib_num_tclassid_users + + ;
2012-06-29 12:32:45 +04:00
}
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
2012-07-06 09:13:13 +04:00
net - > ipv4 . fib_has_custom_rules = true ;
2006-08-04 14:39:22 +04:00
err = 0 ;
errout :
return err ;
2005-04-17 02:20:36 +04:00
}
2012-06-29 12:32:45 +04:00
static void fib4_rule_delete ( struct fib_rule * rule )
{
2012-07-06 09:13:13 +04:00
struct net * net = rule - > fr_net ;
2012-06-29 12:32:45 +04:00
# ifdef CONFIG_IP_ROUTE_CLASSID
struct fib4_rule * rule4 = ( struct fib4_rule * ) rule ;
if ( rule4 - > tclassid )
2012-07-06 09:13:13 +04:00
net - > ipv4 . fib_num_tclassid_users - - ;
2012-06-29 12:32:45 +04:00
# endif
2012-07-06 09:13:13 +04:00
net - > ipv4 . fib_has_custom_rules = true ;
2012-06-29 12:32:45 +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
2011-01-14 15:36:42 +03:00
# ifdef CONFIG_IP_ROUTE_CLASSID
2006-08-04 14:39:22 +04:00
if ( tb [ FRA_FLOW ] & & ( rule4 - > tclassid ! = nla_get_u32 ( tb [ FRA_FLOW ] ) ) )
return 0 ;
# endif
2005-04-17 02:20:36 +04:00
2007-03-24 22:46:02 +03:00
if ( frh - > src_len & & ( rule4 - > src ! = nla_get_be32 ( tb [ FRA_SRC ] ) ) )
2006-08-04 14:39:22 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2007-03-24 22:46:02 +03:00
if ( frh - > dst_len & & ( rule4 - > dst ! = nla_get_be32 ( tb [ FRA_DST ] ) ) )
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
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 ,
2009-05-21 04:26:23 +04:00
struct fib_rule_hdr * frh )
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
frh - > dst_len = rule4 - > dst_len ;
frh - > src_len = rule4 - > src_len ;
frh - > tos = rule4 - > tos ;
2005-04-17 02:20:36 +04:00
2012-04-02 04:39:02 +04:00
if ( ( rule4 - > dst_len & &
nla_put_be32 ( skb , FRA_DST , rule4 - > dst ) ) | |
( rule4 - > src_len & &
nla_put_be32 ( skb , FRA_SRC , rule4 - > src ) ) )
goto nla_put_failure ;
2011-01-14 15:36:42 +03:00
# ifdef CONFIG_IP_ROUTE_CLASSID
2012-04-02 04:39:02 +04:00
if ( rule4 - > tclassid & &
nla_put_u32 ( skb , FRA_FLOW , rule4 - > tclassid ) )
goto nla_put_failure ;
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-11-11 01:10:15 +03:00
static size_t fib4_rule_nlmsg_payload ( struct fib_rule * rule )
{
return nla_total_size ( 4 ) /* dst */
+ nla_total_size ( 4 ) /* src */
+ nla_total_size ( 4 ) ; /* flow */
}
2008-07-06 06:01:28 +04:00
static void fib4_rule_flush_cache ( struct fib_rules_ops * ops )
2007-03-28 00:56:52 +04:00
{
2012-09-07 04:45:29 +04:00
rt_cache_flush ( ops - > fro_net ) ;
2007-03-28 00:56:52 +04:00
}
2012-10-05 04:12:11 +04:00
static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = {
2010-04-26 18:02:05 +04:00
. family = AF_INET ,
2006-08-04 14:39:22 +04:00
. rule_size = sizeof ( struct fib4_rule ) ,
2007-03-24 22:46:02 +03:00
. addr_size = sizeof ( u32 ) ,
2006-08-04 14:39:22 +04:00
. action = fib4_rule_action ,
2013-08-01 04:17:15 +04:00
. suppress = fib4_rule_suppress ,
2006-08-04 14:39:22 +04:00
. match = fib4_rule_match ,
. configure = fib4_rule_configure ,
2012-06-29 12:32:45 +04:00
. delete = fib4_rule_delete ,
2006-08-04 14:39:22 +04:00
. compare = fib4_rule_compare ,
. fill = fib4_rule_fill ,
2010-04-13 09:03:15 +04:00
. default_pref = fib_default_rule_pref ,
2006-11-11 01:10:15 +03:00
. nlmsg_payload = fib4_rule_nlmsg_payload ,
2007-03-28 00:56:52 +04:00
. flush_cache = fib4_rule_flush_cache ,
2006-08-04 14:39:22 +04:00
. nlgroup = RTNLGRP_IPV4_RULE ,
. policy = fib4_rule_policy ,
. owner = THIS_MODULE ,
} ;
2008-01-10 14:27:51 +03:00
static int fib_default_rules_init ( struct fib_rules_ops * ops )
2005-04-17 02:20:36 +04:00
{
2007-11-11 09:12:03 +03:00
int err ;
2009-12-03 04:25:57 +03:00
err = fib_default_rule_add ( ops , 0 , RT_TABLE_LOCAL , 0 ) ;
2007-11-11 09:12:03 +03:00
if ( err < 0 )
return err ;
2008-01-10 14:27:51 +03:00
err = fib_default_rule_add ( ops , 0x7FFE , RT_TABLE_MAIN , 0 ) ;
2007-11-11 09:12:03 +03:00
if ( err < 0 )
return err ;
2008-01-10 14:27:51 +03:00
err = fib_default_rule_add ( ops , 0x7FFF , RT_TABLE_DEFAULT , 0 ) ;
2007-11-11 09:12:03 +03:00
if ( err < 0 )
return err ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-01-10 14:22:17 +03:00
int __net_init fib4_rules_init ( struct net * net )
2007-11-11 09:12:03 +03:00
{
2008-01-10 14:21:49 +03:00
int err ;
2008-01-10 14:27:51 +03:00
struct fib_rules_ops * ops ;
2009-12-03 23:22:55 +03:00
ops = fib_rules_register ( & fib4_rules_ops_template , net ) ;
if ( IS_ERR ( ops ) )
return PTR_ERR ( ops ) ;
2008-01-10 14:21:49 +03:00
2008-01-10 14:27:51 +03:00
err = fib_default_rules_init ( ops ) ;
2008-01-10 14:21:49 +03:00
if ( err < 0 )
goto fail ;
2008-01-10 14:27:51 +03:00
net - > ipv4 . rules_ops = ops ;
2012-07-06 09:13:13 +04:00
net - > ipv4 . fib_has_custom_rules = false ;
2008-01-10 14:21:49 +03:00
return 0 ;
fail :
/* also cleans all rules already added */
2008-01-21 03:46:41 +03:00
fib_rules_unregister ( ops ) ;
2008-01-10 14:21:49 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
2008-01-10 14:22:17 +03:00
void __net_exit fib4_rules_exit ( struct net * net )
{
2008-01-21 03:46:41 +03:00
fib_rules_unregister ( net - > ipv4 . rules_ops ) ;
2008-01-10 14:22:17 +03:00
}