2018-03-27 18:22:00 -07:00
/*
* Copyright ( c ) 2018 Cumulus Networks . All rights reserved .
* Copyright ( c ) 2018 David Ahern < dsa @ cumulusnetworks . com >
*
* This software is licensed under the GNU General License Version 2 ,
* June 1991 as shown in the file COPYING in the top - level directory of this
* source tree .
*
* THE COPYRIGHT HOLDERS AND / OR OTHER PARTIES PROVIDE THE PROGRAM " AS IS "
* WITHOUT WARRANTY OF ANY KIND , EITHER EXPRESSED OR IMPLIED , INCLUDING ,
* BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE . THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
* OF THE PROGRAM IS WITH YOU . SHOULD THE PROGRAM PROVE DEFECTIVE , YOU ASSUME
* THE COST OF ALL NECESSARY SERVICING , REPAIR OR CORRECTION .
*/
# include <net/fib_notifier.h>
# include <net/ip_fib.h>
# include <net/ip6_fib.h>
# include <net/fib_rules.h>
2019-08-06 12:15:17 -07:00
# include <net/netns/generic.h>
2018-03-27 18:22:00 -07:00
# include "netdevsim.h"
struct nsim_fib_entry {
u64 max ;
u64 num ;
} ;
struct nsim_per_fib_data {
struct nsim_fib_entry fib ;
struct nsim_fib_entry rules ;
} ;
struct nsim_fib_data {
struct nsim_per_fib_data ipv4 ;
struct nsim_per_fib_data ipv6 ;
} ;
2019-08-06 12:15:17 -07:00
static unsigned int nsim_fib_net_id ;
u64 nsim_fib_get_val ( struct net * net , enum nsim_resource_id res_id , bool max )
2018-03-27 18:22:00 -07:00
{
2019-08-06 12:15:17 -07:00
struct nsim_fib_data * fib_data = net_generic ( net , nsim_fib_net_id ) ;
2018-03-27 18:22:00 -07:00
struct nsim_fib_entry * entry ;
switch ( res_id ) {
case NSIM_RESOURCE_IPV4_FIB :
entry = & fib_data - > ipv4 . fib ;
break ;
case NSIM_RESOURCE_IPV4_FIB_RULES :
entry = & fib_data - > ipv4 . rules ;
break ;
case NSIM_RESOURCE_IPV6_FIB :
entry = & fib_data - > ipv6 . fib ;
break ;
case NSIM_RESOURCE_IPV6_FIB_RULES :
entry = & fib_data - > ipv6 . rules ;
break ;
default :
return 0 ;
}
return max ? entry - > max : entry - > num ;
}
2019-08-06 12:15:17 -07:00
int nsim_fib_set_max ( struct net * net , enum nsim_resource_id res_id , u64 val ,
2018-06-05 08:14:10 -07:00
struct netlink_ext_ack * extack )
2018-03-27 18:22:00 -07:00
{
2019-08-06 12:15:17 -07:00
struct nsim_fib_data * fib_data = net_generic ( net , nsim_fib_net_id ) ;
2018-03-27 18:22:00 -07:00
struct nsim_fib_entry * entry ;
int err = 0 ;
switch ( res_id ) {
case NSIM_RESOURCE_IPV4_FIB :
entry = & fib_data - > ipv4 . fib ;
break ;
case NSIM_RESOURCE_IPV4_FIB_RULES :
entry = & fib_data - > ipv4 . rules ;
break ;
case NSIM_RESOURCE_IPV6_FIB :
entry = & fib_data - > ipv6 . fib ;
break ;
case NSIM_RESOURCE_IPV6_FIB_RULES :
entry = & fib_data - > ipv6 . rules ;
break ;
default :
return 0 ;
}
/* not allowing a new max to be less than curren occupancy
* - - > no means of evicting entries
*/
2018-06-05 08:14:10 -07:00
if ( val < entry - > num ) {
NL_SET_ERR_MSG_MOD ( extack , " New size is less than current occupancy " ) ;
2018-03-27 18:22:00 -07:00
err = - EINVAL ;
2018-06-05 08:14:10 -07:00
} else {
2018-03-27 18:22:00 -07:00
entry - > max = val ;
2018-06-05 08:14:10 -07:00
}
2018-03-27 18:22:00 -07:00
return err ;
}
static int nsim_fib_rule_account ( struct nsim_fib_entry * entry , bool add ,
struct netlink_ext_ack * extack )
{
int err = 0 ;
if ( add ) {
if ( entry - > num < entry - > max ) {
entry - > num + + ;
} else {
err = - ENOSPC ;
NL_SET_ERR_MSG_MOD ( extack , " Exceeded number of supported fib rule entries " ) ;
}
} else {
entry - > num - - ;
}
return err ;
}
2019-08-06 12:15:17 -07:00
static int nsim_fib_rule_event ( struct fib_notifier_info * info , bool add )
2018-03-27 18:22:00 -07:00
{
2019-08-06 12:15:17 -07:00
struct nsim_fib_data * data = net_generic ( info - > net , nsim_fib_net_id ) ;
2018-03-27 18:22:00 -07:00
struct netlink_ext_ack * extack = info - > extack ;
int err = 0 ;
switch ( info - > family ) {
case AF_INET :
err = nsim_fib_rule_account ( & data - > ipv4 . rules , add , extack ) ;
break ;
case AF_INET6 :
err = nsim_fib_rule_account ( & data - > ipv6 . rules , add , extack ) ;
break ;
}
return err ;
}
static int nsim_fib_account ( struct nsim_fib_entry * entry , bool add ,
struct netlink_ext_ack * extack )
{
int err = 0 ;
if ( add ) {
if ( entry - > num < entry - > max ) {
entry - > num + + ;
} else {
err = - ENOSPC ;
NL_SET_ERR_MSG_MOD ( extack , " Exceeded number of supported fib entries " ) ;
}
} else {
entry - > num - - ;
}
return err ;
}
2019-08-06 12:15:17 -07:00
static int nsim_fib_event ( struct fib_notifier_info * info , bool add )
2018-03-27 18:22:00 -07:00
{
2019-08-06 12:15:17 -07:00
struct nsim_fib_data * data = net_generic ( info - > net , nsim_fib_net_id ) ;
2018-03-27 18:22:00 -07:00
struct netlink_ext_ack * extack = info - > extack ;
int err = 0 ;
switch ( info - > family ) {
case AF_INET :
err = nsim_fib_account ( & data - > ipv4 . fib , add , extack ) ;
break ;
case AF_INET6 :
err = nsim_fib_account ( & data - > ipv6 . fib , add , extack ) ;
break ;
}
return err ;
}
static int nsim_fib_event_nb ( struct notifier_block * nb , unsigned long event ,
void * ptr )
{
struct fib_notifier_info * info = ptr ;
int err = 0 ;
switch ( event ) {
case FIB_EVENT_RULE_ADD : /* fall through */
case FIB_EVENT_RULE_DEL :
2019-08-06 12:15:17 -07:00
err = nsim_fib_rule_event ( info , event = = FIB_EVENT_RULE_ADD ) ;
2018-03-27 18:22:00 -07:00
break ;
case FIB_EVENT_ENTRY_ADD : /* fall through */
case FIB_EVENT_ENTRY_DEL :
2019-08-06 12:15:17 -07:00
err = nsim_fib_event ( info , event = = FIB_EVENT_ENTRY_ADD ) ;
2018-03-27 18:22:00 -07:00
break ;
}
return notifier_from_errno ( err ) ;
}
/* inconsistent dump, trying again */
static void nsim_fib_dump_inconsistent ( struct notifier_block * nb )
{
2019-08-06 12:15:17 -07:00
struct nsim_fib_data * data ;
struct net * net ;
rcu_read_lock ( ) ;
for_each_net_rcu ( net ) {
data = net_generic ( net , nsim_fib_net_id ) ;
data - > ipv4 . fib . num = 0ULL ;
data - > ipv4 . rules . num = 0ULL ;
2018-03-27 18:22:00 -07:00
2019-08-06 12:15:17 -07:00
data - > ipv6 . fib . num = 0ULL ;
data - > ipv6 . rules . num = 0ULL ;
}
rcu_read_unlock ( ) ;
2018-03-27 18:22:00 -07:00
}
2019-08-06 12:15:17 -07:00
static struct notifier_block nsim_fib_nb = {
. notifier_call = nsim_fib_event_nb ,
} ;
2019-04-25 15:59:42 +02:00
2019-08-06 12:15:17 -07:00
/* Initialize per network namespace state */
static int __net_init nsim_fib_netns_init ( struct net * net )
{
struct nsim_fib_data * data = net_generic ( net , nsim_fib_net_id ) ;
2018-03-27 18:22:00 -07:00
data - > ipv4 . fib . max = ( u64 ) - 1 ;
data - > ipv4 . rules . max = ( u64 ) - 1 ;
data - > ipv6 . fib . max = ( u64 ) - 1 ;
data - > ipv6 . rules . max = ( u64 ) - 1 ;
2019-08-06 12:15:17 -07:00
return 0 ;
}
2018-03-27 18:22:00 -07:00
2019-08-06 12:15:17 -07:00
static struct pernet_operations nsim_fib_net_ops = {
. init = nsim_fib_netns_init ,
. id = & nsim_fib_net_id ,
. size = sizeof ( struct nsim_fib_data ) ,
} ;
2019-04-25 15:59:42 +02:00
2019-08-06 12:15:17 -07:00
void nsim_fib_exit ( void )
{
unregister_fib_notifier ( & nsim_fib_nb ) ;
2019-10-11 17:46:53 +08:00
unregister_pernet_subsys ( & nsim_fib_net_ops ) ;
2019-04-25 15:59:42 +02:00
}
2019-08-06 12:15:17 -07:00
int nsim_fib_init ( void )
2019-04-25 15:59:42 +02:00
{
2019-08-06 12:15:17 -07:00
int err ;
err = register_pernet_subsys ( & nsim_fib_net_ops ) ;
if ( err < 0 ) {
pr_err ( " Failed to register pernet subsystem \n " ) ;
goto err_out ;
}
err = register_fib_notifier ( & nsim_fib_nb , nsim_fib_dump_inconsistent ) ;
if ( err < 0 ) {
pr_err ( " Failed to register fib notifier \n " ) ;
2019-10-11 17:46:53 +08:00
unregister_pernet_subsys ( & nsim_fib_net_ops ) ;
2019-08-06 12:15:17 -07:00
goto err_out ;
}
err_out :
return err ;
2018-03-27 18:22:00 -07:00
}