18c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 28c2ecf20Sopenharmony_ci#include <linux/notifier.h> 38c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/module.h> 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 88c2ecf20Sopenharmony_ci#include <net/netns/generic.h> 98c2ecf20Sopenharmony_ci#include <net/fib_notifier.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic unsigned int fib_notifier_net_id; 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistruct fib_notifier_net { 148c2ecf20Sopenharmony_ci struct list_head fib_notifier_ops; 158c2ecf20Sopenharmony_ci struct atomic_notifier_head fib_chain; 168c2ecf20Sopenharmony_ci}; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciint call_fib_notifier(struct notifier_block *nb, 198c2ecf20Sopenharmony_ci enum fib_event_type event_type, 208c2ecf20Sopenharmony_ci struct fib_notifier_info *info) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci int err; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci err = nb->notifier_call(nb, event_type, info); 258c2ecf20Sopenharmony_ci return notifier_to_errno(err); 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(call_fib_notifier); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciint call_fib_notifiers(struct net *net, enum fib_event_type event_type, 308c2ecf20Sopenharmony_ci struct fib_notifier_info *info) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 338c2ecf20Sopenharmony_ci int err; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci err = atomic_notifier_call_chain(&fn_net->fib_chain, event_type, info); 368c2ecf20Sopenharmony_ci return notifier_to_errno(err); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(call_fib_notifiers); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic unsigned int fib_seq_sum(struct net *net) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 438c2ecf20Sopenharmony_ci struct fib_notifier_ops *ops; 448c2ecf20Sopenharmony_ci unsigned int fib_seq = 0; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci rtnl_lock(); 478c2ecf20Sopenharmony_ci rcu_read_lock(); 488c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) { 498c2ecf20Sopenharmony_ci if (!try_module_get(ops->owner)) 508c2ecf20Sopenharmony_ci continue; 518c2ecf20Sopenharmony_ci fib_seq += ops->fib_seq_read(net); 528c2ecf20Sopenharmony_ci module_put(ops->owner); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci rcu_read_unlock(); 558c2ecf20Sopenharmony_ci rtnl_unlock(); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return fib_seq; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int fib_net_dump(struct net *net, struct notifier_block *nb, 618c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 648c2ecf20Sopenharmony_ci struct fib_notifier_ops *ops; 658c2ecf20Sopenharmony_ci int err = 0; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci rcu_read_lock(); 688c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) { 698c2ecf20Sopenharmony_ci if (!try_module_get(ops->owner)) 708c2ecf20Sopenharmony_ci continue; 718c2ecf20Sopenharmony_ci err = ops->fib_dump(net, nb, extack); 728c2ecf20Sopenharmony_ci module_put(ops->owner); 738c2ecf20Sopenharmony_ci if (err) 748c2ecf20Sopenharmony_ci goto unlock; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciunlock: 788c2ecf20Sopenharmony_ci rcu_read_unlock(); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return err; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic bool fib_dump_is_consistent(struct net *net, struct notifier_block *nb, 848c2ecf20Sopenharmony_ci void (*cb)(struct notifier_block *nb), 858c2ecf20Sopenharmony_ci unsigned int fib_seq) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci atomic_notifier_chain_register(&fn_net->fib_chain, nb); 908c2ecf20Sopenharmony_ci if (fib_seq == fib_seq_sum(net)) 918c2ecf20Sopenharmony_ci return true; 928c2ecf20Sopenharmony_ci atomic_notifier_chain_unregister(&fn_net->fib_chain, nb); 938c2ecf20Sopenharmony_ci if (cb) 948c2ecf20Sopenharmony_ci cb(nb); 958c2ecf20Sopenharmony_ci return false; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define FIB_DUMP_MAX_RETRIES 5 998c2ecf20Sopenharmony_ciint register_fib_notifier(struct net *net, struct notifier_block *nb, 1008c2ecf20Sopenharmony_ci void (*cb)(struct notifier_block *nb), 1018c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int retries = 0; 1048c2ecf20Sopenharmony_ci int err; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci do { 1078c2ecf20Sopenharmony_ci unsigned int fib_seq = fib_seq_sum(net); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci err = fib_net_dump(net, nb, extack); 1108c2ecf20Sopenharmony_ci if (err) 1118c2ecf20Sopenharmony_ci return err; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (fib_dump_is_consistent(net, nb, cb, fib_seq)) 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci } while (++retries < FIB_DUMP_MAX_RETRIES); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return -EBUSY; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_fib_notifier); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ciint unregister_fib_notifier(struct net *net, struct notifier_block *nb) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return atomic_notifier_chain_unregister(&fn_net->fib_chain, nb); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_fib_notifier); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int __fib_notifier_ops_register(struct fib_notifier_ops *ops, 1308c2ecf20Sopenharmony_ci struct net *net) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 1338c2ecf20Sopenharmony_ci struct fib_notifier_ops *o; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci list_for_each_entry(o, &fn_net->fib_notifier_ops, list) 1368c2ecf20Sopenharmony_ci if (ops->family == o->family) 1378c2ecf20Sopenharmony_ci return -EEXIST; 1388c2ecf20Sopenharmony_ci list_add_tail_rcu(&ops->list, &fn_net->fib_notifier_ops); 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistruct fib_notifier_ops * 1438c2ecf20Sopenharmony_cifib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct fib_notifier_ops *ops; 1468c2ecf20Sopenharmony_ci int err; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL); 1498c2ecf20Sopenharmony_ci if (!ops) 1508c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci err = __fib_notifier_ops_register(ops, net); 1538c2ecf20Sopenharmony_ci if (err) 1548c2ecf20Sopenharmony_ci goto err_register; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return ops; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cierr_register: 1598c2ecf20Sopenharmony_ci kfree(ops); 1608c2ecf20Sopenharmony_ci return ERR_PTR(err); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fib_notifier_ops_register); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_civoid fib_notifier_ops_unregister(struct fib_notifier_ops *ops) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci list_del_rcu(&ops->list); 1678c2ecf20Sopenharmony_ci kfree_rcu(ops, rcu); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fib_notifier_ops_unregister); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int __net_init fib_notifier_net_init(struct net *net) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&fn_net->fib_notifier_ops); 1768c2ecf20Sopenharmony_ci ATOMIC_INIT_NOTIFIER_HEAD(&fn_net->fib_chain); 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void __net_exit fib_notifier_net_exit(struct net *net) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci WARN_ON_ONCE(!list_empty(&fn_net->fib_notifier_ops)); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic struct pernet_operations fib_notifier_net_ops = { 1888c2ecf20Sopenharmony_ci .init = fib_notifier_net_init, 1898c2ecf20Sopenharmony_ci .exit = fib_notifier_net_exit, 1908c2ecf20Sopenharmony_ci .id = &fib_notifier_net_id, 1918c2ecf20Sopenharmony_ci .size = sizeof(struct fib_notifier_net), 1928c2ecf20Sopenharmony_ci}; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int __init fib_notifier_init(void) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci return register_pernet_subsys(&fib_notifier_net_ops); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cisubsys_initcall(fib_notifier_init); 200