2006-08-04 14:39:02 +04:00
/*
* net / ipv6 / fib6_rules . c IPv6 Routing Policy Rules
*
* Copyright ( C ) 2003 - 2006 Helsinki University of Technology
* Copyright ( C ) 2003 - 2006 USAGI / WIDE Project
*
* 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 , version 2.
*
* Authors
* Thomas Graf < tgraf @ suug . ch >
* Ville Nuorvala < vnuorval @ tcs . hut . fi >
*/
# include <linux/netdevice.h>
2011-07-15 19:47:34 +04:00
# include <linux/export.h>
2006-08-04 14:39:02 +04:00
# include <net/fib_rules.h>
# include <net/ipv6.h>
2007-04-06 22:45:39 +04:00
# include <net/addrconf.h>
2006-08-04 14:39:02 +04:00
# include <net/ip6_route.h>
# include <net/netlink.h>
2012-04-01 11:49:06 +04:00
struct fib6_rule {
2006-08-04 14:39:02 +04:00
struct fib_rule common ;
struct rt6key src ;
struct rt6key dst ;
u8 tclass ;
} ;
2011-03-13 00:22:43 +03:00
struct dst_entry * fib6_rule_lookup ( struct net * net , struct flowi6 * fl6 ,
2008-03-04 10:25:27 +03:00
int flags , pol_lookup_t lookup )
2006-08-04 14:39:02 +04:00
{
struct fib_lookup_arg arg = {
. lookup_ptr = lookup ,
2010-10-13 06:45:40 +04:00
. flags = FIB_LOOKUP_NOREF ,
2006-08-04 14:39:02 +04:00
} ;
2011-03-13 00:22:43 +03:00
fib_rules_lookup ( net - > ipv6 . fib6_rules_ops ,
flowi6_to_flowi ( fl6 ) , flags , & arg ) ;
2006-08-04 14:39:02 +04:00
2006-08-09 03:44:17 +04:00
if ( arg . result )
2006-10-19 08:50:09 +04:00
return arg . result ;
2006-08-09 03:44:17 +04:00
2010-06-11 10:31:35 +04:00
dst_hold ( & net - > ipv6 . ip6_null_entry - > dst ) ;
return & net - > ipv6 . ip6_null_entry - > dst ;
2006-08-04 14:39:02 +04:00
}
2006-08-08 08:50:48 +04:00
static int fib6_rule_action ( struct fib_rule * rule , struct flowi * flp ,
int flags , struct fib_lookup_arg * arg )
2006-08-04 14:39:02 +04:00
{
2011-03-13 00:22:43 +03:00
struct flowi6 * flp6 = & flp - > u . ip6 ;
2006-08-04 14:39:02 +04:00
struct rt6_info * rt = NULL ;
struct fib6_table * table ;
2008-03-05 00:48:30 +03:00
struct net * net = rule - > fr_net ;
2006-08-04 14:39:02 +04:00
pol_lookup_t lookup = arg - > lookup_ptr ;
2013-08-01 10:54:47 +04:00
int err = 0 ;
2006-08-04 14:39:02 +04:00
switch ( rule - > action ) {
case FR_ACT_TO_TBL :
break ;
case FR_ACT_UNREACHABLE :
2013-08-01 10:54:47 +04:00
err = - ENETUNREACH ;
2008-03-05 00:48:30 +03:00
rt = net - > ipv6 . ip6_null_entry ;
2006-08-04 14:39:02 +04:00
goto discard_pkt ;
default :
case FR_ACT_BLACKHOLE :
2013-08-01 10:54:47 +04:00
err = - EINVAL ;
2008-03-05 00:48:30 +03:00
rt = net - > ipv6 . ip6_blk_hole_entry ;
2006-08-04 14:39:02 +04:00
goto discard_pkt ;
case FR_ACT_PROHIBIT :
2013-08-01 10:54:47 +04:00
err = - EACCES ;
2008-03-05 00:48:30 +03:00
rt = net - > ipv6 . ip6_prohibit_entry ;
2006-08-04 14:39:02 +04:00
goto discard_pkt ;
}
2008-03-05 00:48:30 +03:00
table = fib6_get_table ( net , rule - > table ) ;
2013-08-01 10:54:47 +04:00
if ( ! table ) {
err = - EAGAIN ;
goto out ;
}
2006-08-04 14:39:02 +04:00
2013-08-01 10:54:47 +04:00
rt = lookup ( net , table , flp6 , flags ) ;
2008-03-05 00:48:30 +03:00
if ( rt ! = net - > ipv6 . ip6_null_entry ) {
2007-04-06 22:45:39 +04:00
struct fib6_rule * r = ( struct fib6_rule * ) rule ;
/*
* If we need to find a source address for this traffic ,
* we check the result if it meets requirement of the rule .
*/
if ( ( rule - > flags & FIB_RULE_FIND_SADDR ) & &
r - > src . plen & & ! ( flags & RT6_LOOKUP_F_HAS_SADDR ) ) {
struct in6_addr saddr ;
2008-03-25 03:37:42 +03:00
2008-08-15 02:33:21 +04:00
if ( ipv6_dev_get_saddr ( net ,
2010-06-11 10:31:35 +04:00
ip6_dst_idev ( & rt - > dst ) - > dev ,
2011-03-13 00:22:43 +03:00
& flp6 - > daddr ,
2010-03-07 03:14:44 +03:00
rt6_flags2srcprefs ( flags ) ,
2008-03-25 03:37:42 +03:00
& saddr ) )
2007-04-06 22:45:39 +04:00
goto again ;
if ( ! ipv6_prefix_equal ( & saddr , & r - > src . addr ,
r - > src . plen ) )
goto again ;
2011-11-21 07:39:03 +04:00
flp6 - > saddr = saddr ;
2007-04-06 22:45:39 +04:00
}
2006-08-04 14:39:02 +04:00
goto out ;
2007-04-06 22:45:39 +04:00
}
again :
2012-10-29 04:13:19 +04:00
ip6_rt_put ( rt ) ;
2013-08-01 10:54:47 +04:00
err = - EAGAIN ;
2006-08-07 09:24:08 +04:00
rt = NULL ;
goto out ;
2006-08-04 14:39:02 +04:00
discard_pkt :
2010-06-11 10:31:35 +04:00
dst_hold ( & rt - > dst ) ;
2006-08-04 14:39:02 +04:00
out :
arg - > result = rt ;
2013-08-01 10:54:47 +04:00
return err ;
2006-08-04 14:39:02 +04:00
}
2013-08-01 04:17:15 +04:00
static bool fib6_rule_suppress ( struct fib_rule * rule , struct fib_lookup_arg * arg )
{
struct rt6_info * rt = ( struct rt6_info * ) arg - > result ;
2013-08-02 19:19:56 +04:00
struct net_device * dev = rt - > rt6i_idev - > dev ;
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 ( rt - > rt6i_dst . plen < = 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 ;
return false ;
suppress_route :
2013-08-01 04:17:15 +04:00
ip6_rt_put ( rt ) ;
return true ;
}
2006-08-04 14:39:02 +04:00
static int fib6_rule_match ( struct fib_rule * rule , struct flowi * fl , int flags )
{
struct fib6_rule * r = ( struct fib6_rule * ) rule ;
2011-03-13 00:22:43 +03:00
struct flowi6 * fl6 = & fl - > u . ip6 ;
2006-08-04 14:39:02 +04:00
2006-10-14 02:01:03 +04:00
if ( r - > dst . plen & &
2011-03-13 00:22:43 +03:00
! ipv6_prefix_equal ( & fl6 - > daddr , & r - > dst . addr , r - > dst . plen ) )
2006-08-04 14:39:02 +04:00
return 0 ;
2007-04-06 22:45:39 +04:00
/*
* If FIB_RULE_FIND_SADDR is set and we do not have a
* source address for the traffic , we defer check for
* source address .
*/
2006-10-14 02:01:03 +04:00
if ( r - > src . plen ) {
2007-04-06 22:45:39 +04:00
if ( flags & RT6_LOOKUP_F_HAS_SADDR ) {
2011-03-13 00:22:43 +03:00
if ( ! ipv6_prefix_equal ( & fl6 - > saddr , & r - > src . addr ,
2007-04-06 22:45:39 +04:00
r - > src . plen ) )
return 0 ;
} else if ( ! ( r - > common . flags & FIB_RULE_FIND_SADDR ) )
2006-10-14 02:01:03 +04:00
return 0 ;
}
2006-08-04 14:39:02 +04:00
2011-03-13 00:22:43 +03:00
if ( r - > tclass & & r - > tclass ! = ( ( ntohl ( fl6 - > flowlabel ) > > 20 ) & 0xff ) )
2006-08-21 14:18:57 +04:00
return 0 ;
2006-08-04 14:39:02 +04:00
return 1 ;
}
2007-06-05 23:38:30 +04:00
static const struct nla_policy fib6_rule_policy [ FRA_MAX + 1 ] = {
2006-11-10 02:22:48 +03:00
FRA_GENERIC_POLICY ,
2006-08-04 14:39:02 +04:00
} ;
static int fib6_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:02 +04:00
struct nlattr * * tb )
{
int err = - EINVAL ;
2008-03-25 20:26:21 +03:00
struct net * net = sock_net ( skb - > sk ) ;
2006-08-04 14:39:02 +04:00
struct fib6_rule * rule6 = ( struct fib6_rule * ) rule ;
if ( rule - > action = = FR_ACT_TO_TBL ) {
if ( rule - > table = = RT6_TABLE_UNSPEC )
goto errout ;
2008-03-04 10:33:08 +03:00
if ( fib6_new_table ( net , rule - > table ) = = NULL ) {
2006-08-04 14:39:02 +04:00
err = - ENOBUFS ;
goto errout ;
}
}
2007-03-24 22:46:02 +03:00
if ( frh - > src_len )
2006-08-04 14:39:02 +04:00
nla_memcpy ( & rule6 - > src . addr , tb [ FRA_SRC ] ,
sizeof ( struct in6_addr ) ) ;
2007-03-24 22:46:02 +03:00
if ( frh - > dst_len )
2006-08-04 14:39:02 +04:00
nla_memcpy ( & rule6 - > dst . addr , tb [ FRA_DST ] ,
sizeof ( struct in6_addr ) ) ;
rule6 - > src . plen = frh - > src_len ;
rule6 - > dst . plen = frh - > dst_len ;
rule6 - > tclass = frh - > tos ;
err = 0 ;
errout :
return err ;
}
static int fib6_rule_compare ( struct fib_rule * rule , struct fib_rule_hdr * frh ,
struct nlattr * * tb )
{
struct fib6_rule * rule6 = ( struct fib6_rule * ) rule ;
if ( frh - > src_len & & ( rule6 - > src . plen ! = frh - > src_len ) )
return 0 ;
if ( frh - > dst_len & & ( rule6 - > dst . plen ! = frh - > dst_len ) )
return 0 ;
if ( frh - > tos & & ( rule6 - > tclass ! = frh - > tos ) )
return 0 ;
2007-03-24 22:46:02 +03:00
if ( frh - > src_len & &
2006-08-04 14:39:02 +04:00
nla_memcmp ( tb [ FRA_SRC ] , & rule6 - > src . addr , sizeof ( struct in6_addr ) ) )
return 0 ;
2007-03-24 22:46:02 +03:00
if ( frh - > dst_len & &
2006-08-04 14:39:02 +04:00
nla_memcmp ( tb [ FRA_DST ] , & rule6 - > dst . addr , sizeof ( struct in6_addr ) ) )
return 0 ;
return 1 ;
}
static int fib6_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:02 +04:00
{
struct fib6_rule * rule6 = ( struct fib6_rule * ) rule ;
frh - > dst_len = rule6 - > dst . plen ;
frh - > src_len = rule6 - > src . plen ;
frh - > tos = rule6 - > tclass ;
2012-04-02 04:27:33 +04:00
if ( ( rule6 - > dst . plen & &
nla_put ( skb , FRA_DST , sizeof ( struct in6_addr ) ,
& rule6 - > dst . addr ) ) | |
( rule6 - > src . plen & &
nla_put ( skb , FRA_SRC , sizeof ( struct in6_addr ) ,
& rule6 - > src . addr ) ) )
goto nla_put_failure ;
2006-08-04 14:39:02 +04:00
return 0 ;
nla_put_failure :
return - ENOBUFS ;
}
2008-01-10 14:18:25 +03:00
static u32 fib6_rule_default_pref ( struct fib_rules_ops * ops )
2006-08-04 14:39:02 +04:00
{
return 0x3FFF ;
}
2006-11-11 01:10:15 +03:00
static size_t fib6_rule_nlmsg_payload ( struct fib_rule * rule )
{
return nla_total_size ( 16 ) /* dst */
+ nla_total_size ( 16 ) ; /* src */
}
2012-10-05 04:12:11 +04:00
static const struct fib_rules_ops __net_initconst fib6_rules_ops_template = {
2010-04-26 18:02:05 +04:00
. family = AF_INET6 ,
2006-08-04 14:39:02 +04:00
. rule_size = sizeof ( struct fib6_rule ) ,
2007-03-24 22:46:02 +03:00
. addr_size = sizeof ( struct in6_addr ) ,
2006-08-04 14:39:02 +04:00
. action = fib6_rule_action ,
. match = fib6_rule_match ,
2013-08-01 04:17:15 +04:00
. suppress = fib6_rule_suppress ,
2006-08-04 14:39:02 +04:00
. configure = fib6_rule_configure ,
. compare = fib6_rule_compare ,
. fill = fib6_rule_fill ,
. default_pref = fib6_rule_default_pref ,
2006-11-11 01:10:15 +03:00
. nlmsg_payload = fib6_rule_nlmsg_payload ,
2006-08-04 14:39:02 +04:00
. nlgroup = RTNLGRP_IPV6_RULE ,
. policy = fib6_rule_policy ,
. owner = THIS_MODULE ,
2008-01-21 03:46:01 +03:00
. fro_net = & init_net ,
2006-08-04 14:39:02 +04:00
} ;
2010-01-17 06:35:32 +03:00
static int __net_init fib6_rules_net_init ( struct net * net )
2006-08-04 14:39:02 +04:00
{
2009-12-03 23:22:55 +03:00
struct fib_rules_ops * ops ;
2008-03-04 10:33:08 +03:00
int err = - ENOMEM ;
2007-11-11 09:12:03 +03:00
2009-12-03 23:22:55 +03:00
ops = fib_rules_register ( & fib6_rules_ops_template , net ) ;
if ( IS_ERR ( ops ) )
return PTR_ERR ( ops ) ;
net - > ipv6 . fib6_rules_ops = ops ;
2008-03-04 10:32:30 +03:00
2008-03-04 10:33:08 +03:00
err = fib_default_rule_add ( net - > ipv6 . fib6_rules_ops , 0 ,
2009-12-03 04:25:57 +03:00
RT6_TABLE_LOCAL , 0 ) ;
2008-03-04 10:33:08 +03:00
if ( err )
goto out_fib6_rules_ops ;
2007-12-07 11:42:52 +03:00
2008-03-04 10:33:08 +03:00
err = fib_default_rule_add ( net - > ipv6 . fib6_rules_ops ,
0x7FFE , RT6_TABLE_MAIN , 0 ) ;
if ( err )
2009-12-03 23:22:55 +03:00
goto out_fib6_rules_ops ;
2007-12-07 11:42:52 +03:00
out :
2008-03-04 10:33:08 +03:00
return err ;
2007-12-07 11:42:52 +03:00
2008-03-04 10:33:08 +03:00
out_fib6_rules_ops :
2009-12-03 23:22:55 +03:00
fib_rules_unregister ( ops ) ;
2007-12-07 11:42:52 +03:00
goto out ;
2006-08-04 14:39:02 +04:00
}
2010-01-17 06:35:32 +03:00
static void __net_exit fib6_rules_net_exit ( struct net * net )
2008-03-04 10:33:08 +03:00
{
fib_rules_unregister ( net - > ipv6 . fib6_rules_ops ) ;
}
static struct pernet_operations fib6_rules_net_ops = {
. init = fib6_rules_net_init ,
. exit = fib6_rules_net_exit ,
} ;
int __init fib6_rules_init ( void )
{
return register_pernet_subsys ( & fib6_rules_net_ops ) ;
}
2006-08-04 14:39:02 +04:00
void fib6_rules_cleanup ( void )
{
2008-04-10 10:41:28 +04:00
unregister_pernet_subsys ( & fib6_rules_net_ops ) ;
2006-08-04 14:39:02 +04:00
}