162306a36Sopenharmony_ci#include <linux/rtnetlink.h> 262306a36Sopenharmony_ci#include <linux/notifier.h> 362306a36Sopenharmony_ci#include <linux/rcupdate.h> 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/module.h> 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <net/net_namespace.h> 862306a36Sopenharmony_ci#include <net/netns/generic.h> 962306a36Sopenharmony_ci#include <net/fib_notifier.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic unsigned int fib_notifier_net_id; 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistruct fib_notifier_net { 1462306a36Sopenharmony_ci struct list_head fib_notifier_ops; 1562306a36Sopenharmony_ci struct atomic_notifier_head fib_chain; 1662306a36Sopenharmony_ci}; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciint call_fib_notifier(struct notifier_block *nb, 1962306a36Sopenharmony_ci enum fib_event_type event_type, 2062306a36Sopenharmony_ci struct fib_notifier_info *info) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci int err; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci err = nb->notifier_call(nb, event_type, info); 2562306a36Sopenharmony_ci return notifier_to_errno(err); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ciEXPORT_SYMBOL(call_fib_notifier); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciint call_fib_notifiers(struct net *net, enum fib_event_type event_type, 3062306a36Sopenharmony_ci struct fib_notifier_info *info) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 3362306a36Sopenharmony_ci int err; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci err = atomic_notifier_call_chain(&fn_net->fib_chain, event_type, info); 3662306a36Sopenharmony_ci return notifier_to_errno(err); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ciEXPORT_SYMBOL(call_fib_notifiers); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic unsigned int fib_seq_sum(struct net *net) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 4362306a36Sopenharmony_ci struct fib_notifier_ops *ops; 4462306a36Sopenharmony_ci unsigned int fib_seq = 0; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci rtnl_lock(); 4762306a36Sopenharmony_ci rcu_read_lock(); 4862306a36Sopenharmony_ci list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) { 4962306a36Sopenharmony_ci if (!try_module_get(ops->owner)) 5062306a36Sopenharmony_ci continue; 5162306a36Sopenharmony_ci fib_seq += ops->fib_seq_read(net); 5262306a36Sopenharmony_ci module_put(ops->owner); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci rcu_read_unlock(); 5562306a36Sopenharmony_ci rtnl_unlock(); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return fib_seq; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int fib_net_dump(struct net *net, struct notifier_block *nb, 6162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 6462306a36Sopenharmony_ci struct fib_notifier_ops *ops; 6562306a36Sopenharmony_ci int err = 0; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci rcu_read_lock(); 6862306a36Sopenharmony_ci list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) { 6962306a36Sopenharmony_ci if (!try_module_get(ops->owner)) 7062306a36Sopenharmony_ci continue; 7162306a36Sopenharmony_ci err = ops->fib_dump(net, nb, extack); 7262306a36Sopenharmony_ci module_put(ops->owner); 7362306a36Sopenharmony_ci if (err) 7462306a36Sopenharmony_ci goto unlock; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciunlock: 7862306a36Sopenharmony_ci rcu_read_unlock(); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return err; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic bool fib_dump_is_consistent(struct net *net, struct notifier_block *nb, 8462306a36Sopenharmony_ci void (*cb)(struct notifier_block *nb), 8562306a36Sopenharmony_ci unsigned int fib_seq) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci atomic_notifier_chain_register(&fn_net->fib_chain, nb); 9062306a36Sopenharmony_ci if (fib_seq == fib_seq_sum(net)) 9162306a36Sopenharmony_ci return true; 9262306a36Sopenharmony_ci atomic_notifier_chain_unregister(&fn_net->fib_chain, nb); 9362306a36Sopenharmony_ci if (cb) 9462306a36Sopenharmony_ci cb(nb); 9562306a36Sopenharmony_ci return false; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define FIB_DUMP_MAX_RETRIES 5 9962306a36Sopenharmony_ciint register_fib_notifier(struct net *net, struct notifier_block *nb, 10062306a36Sopenharmony_ci void (*cb)(struct notifier_block *nb), 10162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int retries = 0; 10462306a36Sopenharmony_ci int err; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci do { 10762306a36Sopenharmony_ci unsigned int fib_seq = fib_seq_sum(net); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci err = fib_net_dump(net, nb, extack); 11062306a36Sopenharmony_ci if (err) 11162306a36Sopenharmony_ci return err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (fib_dump_is_consistent(net, nb, cb, fib_seq)) 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci } while (++retries < FIB_DUMP_MAX_RETRIES); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return -EBUSY; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ciEXPORT_SYMBOL(register_fib_notifier); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ciint unregister_fib_notifier(struct net *net, struct notifier_block *nb) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return atomic_notifier_chain_unregister(&fn_net->fib_chain, nb); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_fib_notifier); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int __fib_notifier_ops_register(struct fib_notifier_ops *ops, 13062306a36Sopenharmony_ci struct net *net) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 13362306a36Sopenharmony_ci struct fib_notifier_ops *o; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci list_for_each_entry(o, &fn_net->fib_notifier_ops, list) 13662306a36Sopenharmony_ci if (ops->family == o->family) 13762306a36Sopenharmony_ci return -EEXIST; 13862306a36Sopenharmony_ci list_add_tail_rcu(&ops->list, &fn_net->fib_notifier_ops); 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistruct fib_notifier_ops * 14362306a36Sopenharmony_cifib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct fib_notifier_ops *ops; 14662306a36Sopenharmony_ci int err; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL); 14962306a36Sopenharmony_ci if (!ops) 15062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci err = __fib_notifier_ops_register(ops, net); 15362306a36Sopenharmony_ci if (err) 15462306a36Sopenharmony_ci goto err_register; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return ops; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cierr_register: 15962306a36Sopenharmony_ci kfree(ops); 16062306a36Sopenharmony_ci return ERR_PTR(err); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ciEXPORT_SYMBOL(fib_notifier_ops_register); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_civoid fib_notifier_ops_unregister(struct fib_notifier_ops *ops) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci list_del_rcu(&ops->list); 16762306a36Sopenharmony_ci kfree_rcu(ops, rcu); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ciEXPORT_SYMBOL(fib_notifier_ops_unregister); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int __net_init fib_notifier_net_init(struct net *net) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci INIT_LIST_HEAD(&fn_net->fib_notifier_ops); 17662306a36Sopenharmony_ci ATOMIC_INIT_NOTIFIER_HEAD(&fn_net->fib_chain); 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void __net_exit fib_notifier_net_exit(struct net *net) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci WARN_ON_ONCE(!list_empty(&fn_net->fib_notifier_ops)); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic struct pernet_operations fib_notifier_net_ops = { 18862306a36Sopenharmony_ci .init = fib_notifier_net_init, 18962306a36Sopenharmony_ci .exit = fib_notifier_net_exit, 19062306a36Sopenharmony_ci .id = &fib_notifier_net_id, 19162306a36Sopenharmony_ci .size = sizeof(struct fib_notifier_net), 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int __init fib_notifier_init(void) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci return register_pernet_subsys(&fib_notifier_net_ops); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cisubsys_initcall(fib_notifier_init); 200