2017-08-03 14:28:11 +03:00
# include <linux/rtnetlink.h>
# include <linux/notifier.h>
# include <linux/rcupdate.h>
# include <linux/kernel.h>
2017-09-01 12:15:17 +03:00
# include <linux/module.h>
2017-08-03 14:28:11 +03:00
# include <linux/init.h>
# include <net/net_namespace.h>
2019-09-05 21:06:56 +03:00
# include <net/netns/generic.h>
2017-08-03 14:28:11 +03:00
# include <net/fib_notifier.h>
2019-09-05 21:06:56 +03:00
static unsigned int fib_notifier_net_id ;
struct fib_notifier_net {
struct list_head fib_notifier_ops ;
2019-10-03 12:49:27 +03:00
struct atomic_notifier_head fib_chain ;
2019-09-05 21:06:56 +03:00
} ;
2019-10-03 12:49:27 +03:00
int call_fib_notifier ( struct notifier_block * nb ,
2017-08-03 14:28:11 +03:00
enum fib_event_type event_type ,
struct fib_notifier_info * info )
{
2018-03-28 04:21:55 +03:00
int err ;
err = nb - > notifier_call ( nb , event_type , info ) ;
return notifier_to_errno ( err ) ;
2017-08-03 14:28:11 +03:00
}
EXPORT_SYMBOL ( call_fib_notifier ) ;
int call_fib_notifiers ( struct net * net , enum fib_event_type event_type ,
struct fib_notifier_info * info )
{
2019-10-03 12:49:27 +03:00
struct fib_notifier_net * fn_net = net_generic ( net , fib_notifier_net_id ) ;
2018-03-28 04:21:55 +03:00
int err ;
2019-10-03 12:49:27 +03:00
err = atomic_notifier_call_chain ( & fn_net - > fib_chain , event_type , info ) ;
2018-03-28 04:21:55 +03:00
return notifier_to_errno ( err ) ;
2017-08-03 14:28:11 +03:00
}
EXPORT_SYMBOL ( call_fib_notifiers ) ;
2019-10-03 12:49:27 +03:00
static unsigned int fib_seq_sum ( struct net * net )
2017-08-03 14:28:11 +03:00
{
2019-10-03 12:49:27 +03:00
struct fib_notifier_net * fn_net = net_generic ( net , fib_notifier_net_id ) ;
2017-08-03 14:28:11 +03:00
struct fib_notifier_ops * ops ;
unsigned int fib_seq = 0 ;
rtnl_lock ( ) ;
2019-10-03 12:49:27 +03:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( ops , & fn_net - > fib_notifier_ops , list ) {
if ( ! try_module_get ( ops - > owner ) )
continue ;
fib_seq + = ops - > fib_seq_read ( net ) ;
module_put ( ops - > owner ) ;
2017-08-03 14:28:11 +03:00
}
2019-10-03 12:49:27 +03:00
rcu_read_unlock ( ) ;
2017-08-03 14:28:11 +03:00
rtnl_unlock ( ) ;
return fib_seq ;
}
2019-10-03 12:49:30 +03:00
static int fib_net_dump ( struct net * net , struct notifier_block * nb ,
struct netlink_ext_ack * extack )
2017-08-03 14:28:11 +03:00
{
2019-09-05 21:06:56 +03:00
struct fib_notifier_net * fn_net = net_generic ( net , fib_notifier_net_id ) ;
2017-08-03 14:28:11 +03:00
struct fib_notifier_ops * ops ;
2019-10-03 12:49:27 +03:00
int err = 0 ;
2017-08-03 14:28:11 +03:00
2019-10-03 12:49:27 +03:00
rcu_read_lock ( ) ;
2019-09-05 21:06:56 +03:00
list_for_each_entry_rcu ( ops , & fn_net - > fib_notifier_ops , list ) {
2017-09-01 12:15:17 +03:00
if ( ! try_module_get ( ops - > owner ) )
continue ;
2019-10-03 12:49:30 +03:00
err = ops - > fib_dump ( net , nb , extack ) ;
2017-09-01 12:15:17 +03:00
module_put ( ops - > owner ) ;
2017-08-03 14:28:11 +03:00
if ( err )
2019-10-03 12:49:27 +03:00
goto unlock ;
2017-08-03 14:28:11 +03:00
}
2019-10-03 12:49:27 +03:00
unlock :
rcu_read_unlock ( ) ;
return err ;
2017-08-03 14:28:11 +03:00
}
2019-10-03 12:49:27 +03:00
static bool fib_dump_is_consistent ( struct net * net , struct notifier_block * nb ,
2017-08-03 14:28:11 +03:00
void ( * cb ) ( struct notifier_block * nb ) ,
unsigned int fib_seq )
{
2019-10-03 12:49:27 +03:00
struct fib_notifier_net * fn_net = net_generic ( net , fib_notifier_net_id ) ;
atomic_notifier_chain_register ( & fn_net - > fib_chain , nb ) ;
if ( fib_seq = = fib_seq_sum ( net ) )
2017-08-03 14:28:11 +03:00
return true ;
2019-10-03 12:49:27 +03:00
atomic_notifier_chain_unregister ( & fn_net - > fib_chain , nb ) ;
2017-08-03 14:28:11 +03:00
if ( cb )
cb ( nb ) ;
return false ;
}
# define FIB_DUMP_MAX_RETRIES 5
2019-10-03 12:49:27 +03:00
int register_fib_notifier ( struct net * net , struct notifier_block * nb ,
2019-10-03 12:49:30 +03:00
void ( * cb ) ( struct notifier_block * nb ) ,
struct netlink_ext_ack * extack )
2017-08-03 14:28:11 +03:00
{
int retries = 0 ;
int err ;
do {
2019-10-03 12:49:27 +03:00
unsigned int fib_seq = fib_seq_sum ( net ) ;
2019-10-03 12:49:30 +03:00
err = fib_net_dump ( net , nb , extack ) ;
2019-10-03 12:49:27 +03:00
if ( err )
return err ;
if ( fib_dump_is_consistent ( net , nb , cb , fib_seq ) )
2017-08-03 14:28:11 +03:00
return 0 ;
} while ( + + retries < FIB_DUMP_MAX_RETRIES ) ;
return - EBUSY ;
}
EXPORT_SYMBOL ( register_fib_notifier ) ;
2019-10-03 12:49:27 +03:00
int unregister_fib_notifier ( struct net * net , struct notifier_block * nb )
2017-08-03 14:28:11 +03:00
{
2019-10-03 12:49:27 +03:00
struct fib_notifier_net * fn_net = net_generic ( net , fib_notifier_net_id ) ;
return atomic_notifier_chain_unregister ( & fn_net - > fib_chain , nb ) ;
2017-08-03 14:28:11 +03:00
}
EXPORT_SYMBOL ( unregister_fib_notifier ) ;
static int __fib_notifier_ops_register ( struct fib_notifier_ops * ops ,
struct net * net )
{
2019-09-05 21:06:56 +03:00
struct fib_notifier_net * fn_net = net_generic ( net , fib_notifier_net_id ) ;
2017-08-03 14:28:11 +03:00
struct fib_notifier_ops * o ;
2019-09-05 21:06:56 +03:00
list_for_each_entry ( o , & fn_net - > fib_notifier_ops , list )
2017-08-03 14:28:11 +03:00
if ( ops - > family = = o - > family )
return - EEXIST ;
2019-09-05 21:06:56 +03:00
list_add_tail_rcu ( & ops - > list , & fn_net - > fib_notifier_ops ) ;
2017-08-03 14:28:11 +03:00
return 0 ;
}
struct fib_notifier_ops *
fib_notifier_ops_register ( const struct fib_notifier_ops * tmpl , struct net * net )
{
struct fib_notifier_ops * ops ;
int err ;
ops = kmemdup ( tmpl , sizeof ( * ops ) , GFP_KERNEL ) ;
if ( ! ops )
return ERR_PTR ( - ENOMEM ) ;
err = __fib_notifier_ops_register ( ops , net ) ;
if ( err )
goto err_register ;
return ops ;
err_register :
kfree ( ops ) ;
return ERR_PTR ( err ) ;
}
EXPORT_SYMBOL ( fib_notifier_ops_register ) ;
void fib_notifier_ops_unregister ( struct fib_notifier_ops * ops )
{
list_del_rcu ( & ops - > list ) ;
kfree_rcu ( ops , rcu ) ;
}
EXPORT_SYMBOL ( fib_notifier_ops_unregister ) ;
static int __net_init fib_notifier_net_init ( struct net * net )
{
2019-09-05 21:06:56 +03:00
struct fib_notifier_net * fn_net = net_generic ( net , fib_notifier_net_id ) ;
INIT_LIST_HEAD ( & fn_net - > fib_notifier_ops ) ;
2019-10-03 12:49:27 +03:00
ATOMIC_INIT_NOTIFIER_HEAD ( & fn_net - > fib_chain ) ;
2017-08-03 14:28:11 +03:00
return 0 ;
}
2017-11-12 22:29:33 +03:00
static void __net_exit fib_notifier_net_exit ( struct net * net )
{
2019-09-05 21:06:56 +03:00
struct fib_notifier_net * fn_net = net_generic ( net , fib_notifier_net_id ) ;
WARN_ON_ONCE ( ! list_empty ( & fn_net - > fib_notifier_ops ) ) ;
2017-11-12 22:29:33 +03:00
}
2017-08-03 14:28:11 +03:00
static struct pernet_operations fib_notifier_net_ops = {
. init = fib_notifier_net_init ,
2017-11-12 22:29:33 +03:00
. exit = fib_notifier_net_exit ,
2019-09-05 21:06:56 +03:00
. id = & fib_notifier_net_id ,
. size = sizeof ( struct fib_notifier_net ) ,
2017-08-03 14:28:11 +03:00
} ;
static int __init fib_notifier_init ( void )
{
return register_pernet_subsys ( & fib_notifier_net_ops ) ;
}
subsys_initcall ( fib_notifier_init ) ;