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