xref: /openbmc/linux/net/core/fib_notifier.c (revision b7a59557)
104b1d4e5SIdo Schimmel #include <linux/rtnetlink.h>
204b1d4e5SIdo Schimmel #include <linux/notifier.h>
304b1d4e5SIdo Schimmel #include <linux/rcupdate.h>
404b1d4e5SIdo Schimmel #include <linux/kernel.h>
5864150dfSIdo Schimmel #include <linux/module.h>
604b1d4e5SIdo Schimmel #include <linux/init.h>
704b1d4e5SIdo Schimmel #include <net/net_namespace.h>
83dd97a08SJiri Pirko #include <net/netns/generic.h>
904b1d4e5SIdo Schimmel #include <net/fib_notifier.h>
1004b1d4e5SIdo Schimmel 
113dd97a08SJiri Pirko static unsigned int fib_notifier_net_id;
123dd97a08SJiri Pirko 
133dd97a08SJiri Pirko struct fib_notifier_net {
143dd97a08SJiri Pirko 	struct list_head fib_notifier_ops;
157c550dafSJiri Pirko 	struct atomic_notifier_head fib_chain;
163dd97a08SJiri Pirko };
173dd97a08SJiri Pirko 
call_fib_notifier(struct notifier_block * nb,enum fib_event_type event_type,struct fib_notifier_info * info)187c550dafSJiri Pirko int call_fib_notifier(struct notifier_block *nb,
1904b1d4e5SIdo Schimmel 		      enum fib_event_type event_type,
2004b1d4e5SIdo Schimmel 		      struct fib_notifier_info *info)
2104b1d4e5SIdo Schimmel {
22c30d9356SDavid Ahern 	int err;
23c30d9356SDavid Ahern 
24c30d9356SDavid Ahern 	err = nb->notifier_call(nb, event_type, info);
25c30d9356SDavid Ahern 	return notifier_to_errno(err);
2604b1d4e5SIdo Schimmel }
2704b1d4e5SIdo Schimmel EXPORT_SYMBOL(call_fib_notifier);
2804b1d4e5SIdo Schimmel 
call_fib_notifiers(struct net * net,enum fib_event_type event_type,struct fib_notifier_info * info)2904b1d4e5SIdo Schimmel int call_fib_notifiers(struct net *net, enum fib_event_type event_type,
3004b1d4e5SIdo Schimmel 		       struct fib_notifier_info *info)
3104b1d4e5SIdo Schimmel {
327c550dafSJiri Pirko 	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
33c30d9356SDavid Ahern 	int err;
34c30d9356SDavid Ahern 
357c550dafSJiri Pirko 	err = atomic_notifier_call_chain(&fn_net->fib_chain, event_type, info);
36c30d9356SDavid Ahern 	return notifier_to_errno(err);
3704b1d4e5SIdo Schimmel }
3804b1d4e5SIdo Schimmel EXPORT_SYMBOL(call_fib_notifiers);
3904b1d4e5SIdo Schimmel 
fib_seq_sum(struct net * net)407c550dafSJiri Pirko static unsigned int fib_seq_sum(struct net *net)
4104b1d4e5SIdo Schimmel {
427c550dafSJiri Pirko 	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
4304b1d4e5SIdo Schimmel 	struct fib_notifier_ops *ops;
4404b1d4e5SIdo Schimmel 	unsigned int fib_seq = 0;
4504b1d4e5SIdo Schimmel 
4604b1d4e5SIdo Schimmel 	rtnl_lock();
4711bf284fSKirill Tkhai 	rcu_read_lock();
483dd97a08SJiri Pirko 	list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) {
49864150dfSIdo Schimmel 		if (!try_module_get(ops->owner))
50864150dfSIdo Schimmel 			continue;
5104b1d4e5SIdo Schimmel 		fib_seq += ops->fib_seq_read(net);
52864150dfSIdo Schimmel 		module_put(ops->owner);
53864150dfSIdo Schimmel 	}
5411bf284fSKirill Tkhai 	rcu_read_unlock();
5504b1d4e5SIdo Schimmel 	rtnl_unlock();
5604b1d4e5SIdo Schimmel 
5704b1d4e5SIdo Schimmel 	return fib_seq;
5804b1d4e5SIdo Schimmel }
5904b1d4e5SIdo Schimmel 
fib_net_dump(struct net * net,struct notifier_block * nb,struct netlink_ext_ack * extack)60b7a59557SJiri Pirko static int fib_net_dump(struct net *net, struct notifier_block *nb,
61b7a59557SJiri Pirko 			struct netlink_ext_ack *extack)
6204b1d4e5SIdo Schimmel {
633dd97a08SJiri Pirko 	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
6404b1d4e5SIdo Schimmel 	struct fib_notifier_ops *ops;
657c550dafSJiri Pirko 	int err = 0;
6604b1d4e5SIdo Schimmel 
677c550dafSJiri Pirko 	rcu_read_lock();
683dd97a08SJiri Pirko 	list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) {
69864150dfSIdo Schimmel 		if (!try_module_get(ops->owner))
70864150dfSIdo Schimmel 			continue;
71b7a59557SJiri Pirko 		err = ops->fib_dump(net, nb, extack);
72864150dfSIdo Schimmel 		module_put(ops->owner);
7304b1d4e5SIdo Schimmel 		if (err)
747c550dafSJiri Pirko 			goto unlock;
757c550dafSJiri Pirko 	}
767c550dafSJiri Pirko 
777c550dafSJiri Pirko unlock:
787c550dafSJiri Pirko 	rcu_read_unlock();
797c550dafSJiri Pirko 
8004b1d4e5SIdo Schimmel 	return err;
8104b1d4e5SIdo Schimmel }
8204b1d4e5SIdo Schimmel 
fib_dump_is_consistent(struct net * net,struct notifier_block * nb,void (* cb)(struct notifier_block * nb),unsigned int fib_seq)837c550dafSJiri Pirko static bool fib_dump_is_consistent(struct net *net, struct notifier_block *nb,
8404b1d4e5SIdo Schimmel 				   void (*cb)(struct notifier_block *nb),
8504b1d4e5SIdo Schimmel 				   unsigned int fib_seq)
8604b1d4e5SIdo Schimmel {
877c550dafSJiri Pirko 	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
887c550dafSJiri Pirko 
897c550dafSJiri Pirko 	atomic_notifier_chain_register(&fn_net->fib_chain, nb);
907c550dafSJiri Pirko 	if (fib_seq == fib_seq_sum(net))
9104b1d4e5SIdo Schimmel 		return true;
927c550dafSJiri Pirko 	atomic_notifier_chain_unregister(&fn_net->fib_chain, nb);
9304b1d4e5SIdo Schimmel 	if (cb)
9404b1d4e5SIdo Schimmel 		cb(nb);
9504b1d4e5SIdo Schimmel 	return false;
9604b1d4e5SIdo Schimmel }
9704b1d4e5SIdo Schimmel 
9804b1d4e5SIdo Schimmel #define FIB_DUMP_MAX_RETRIES 5
register_fib_notifier(struct net * net,struct notifier_block * nb,void (* cb)(struct notifier_block * nb),struct netlink_ext_ack * extack)997c550dafSJiri Pirko int register_fib_notifier(struct net *net, struct notifier_block *nb,
100b7a59557SJiri Pirko 			  void (*cb)(struct notifier_block *nb),
101b7a59557SJiri Pirko 			  struct netlink_ext_ack *extack)
10204b1d4e5SIdo Schimmel {
10304b1d4e5SIdo Schimmel 	int retries = 0;
10404b1d4e5SIdo Schimmel 	int err;
10504b1d4e5SIdo Schimmel 
10604b1d4e5SIdo Schimmel 	do {
1077c550dafSJiri Pirko 		unsigned int fib_seq = fib_seq_sum(net);
10804b1d4e5SIdo Schimmel 
109b7a59557SJiri Pirko 		err = fib_net_dump(net, nb, extack);
11004b1d4e5SIdo Schimmel 		if (err)
1117c550dafSJiri Pirko 			return err;
11204b1d4e5SIdo Schimmel 
1137c550dafSJiri Pirko 		if (fib_dump_is_consistent(net, nb, cb, fib_seq))
11404b1d4e5SIdo Schimmel 			return 0;
11504b1d4e5SIdo Schimmel 	} while (++retries < FIB_DUMP_MAX_RETRIES);
11604b1d4e5SIdo Schimmel 
11704b1d4e5SIdo Schimmel 	return -EBUSY;
11804b1d4e5SIdo Schimmel }
11904b1d4e5SIdo Schimmel EXPORT_SYMBOL(register_fib_notifier);
12004b1d4e5SIdo Schimmel 
unregister_fib_notifier(struct net * net,struct notifier_block * nb)1217c550dafSJiri Pirko int unregister_fib_notifier(struct net *net, struct notifier_block *nb)
12204b1d4e5SIdo Schimmel {
1237c550dafSJiri Pirko 	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
1247c550dafSJiri Pirko 
1257c550dafSJiri Pirko 	return atomic_notifier_chain_unregister(&fn_net->fib_chain, nb);
12604b1d4e5SIdo Schimmel }
12704b1d4e5SIdo Schimmel EXPORT_SYMBOL(unregister_fib_notifier);
12804b1d4e5SIdo Schimmel 
__fib_notifier_ops_register(struct fib_notifier_ops * ops,struct net * net)12904b1d4e5SIdo Schimmel static int __fib_notifier_ops_register(struct fib_notifier_ops *ops,
13004b1d4e5SIdo Schimmel 				       struct net *net)
13104b1d4e5SIdo Schimmel {
1323dd97a08SJiri Pirko 	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
13304b1d4e5SIdo Schimmel 	struct fib_notifier_ops *o;
13404b1d4e5SIdo Schimmel 
1353dd97a08SJiri Pirko 	list_for_each_entry(o, &fn_net->fib_notifier_ops, list)
13604b1d4e5SIdo Schimmel 		if (ops->family == o->family)
13704b1d4e5SIdo Schimmel 			return -EEXIST;
1383dd97a08SJiri Pirko 	list_add_tail_rcu(&ops->list, &fn_net->fib_notifier_ops);
13904b1d4e5SIdo Schimmel 	return 0;
14004b1d4e5SIdo Schimmel }
14104b1d4e5SIdo Schimmel 
14204b1d4e5SIdo Schimmel struct fib_notifier_ops *
fib_notifier_ops_register(const struct fib_notifier_ops * tmpl,struct net * net)14304b1d4e5SIdo Schimmel fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net)
14404b1d4e5SIdo Schimmel {
14504b1d4e5SIdo Schimmel 	struct fib_notifier_ops *ops;
14604b1d4e5SIdo Schimmel 	int err;
14704b1d4e5SIdo Schimmel 
14804b1d4e5SIdo Schimmel 	ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL);
14904b1d4e5SIdo Schimmel 	if (!ops)
15004b1d4e5SIdo Schimmel 		return ERR_PTR(-ENOMEM);
15104b1d4e5SIdo Schimmel 
15204b1d4e5SIdo Schimmel 	err = __fib_notifier_ops_register(ops, net);
15304b1d4e5SIdo Schimmel 	if (err)
15404b1d4e5SIdo Schimmel 		goto err_register;
15504b1d4e5SIdo Schimmel 
15604b1d4e5SIdo Schimmel 	return ops;
15704b1d4e5SIdo Schimmel 
15804b1d4e5SIdo Schimmel err_register:
15904b1d4e5SIdo Schimmel 	kfree(ops);
16004b1d4e5SIdo Schimmel 	return ERR_PTR(err);
16104b1d4e5SIdo Schimmel }
16204b1d4e5SIdo Schimmel EXPORT_SYMBOL(fib_notifier_ops_register);
16304b1d4e5SIdo Schimmel 
fib_notifier_ops_unregister(struct fib_notifier_ops * ops)16404b1d4e5SIdo Schimmel void fib_notifier_ops_unregister(struct fib_notifier_ops *ops)
16504b1d4e5SIdo Schimmel {
16604b1d4e5SIdo Schimmel 	list_del_rcu(&ops->list);
16704b1d4e5SIdo Schimmel 	kfree_rcu(ops, rcu);
16804b1d4e5SIdo Schimmel }
16904b1d4e5SIdo Schimmel EXPORT_SYMBOL(fib_notifier_ops_unregister);
17004b1d4e5SIdo Schimmel 
fib_notifier_net_init(struct net * net)17104b1d4e5SIdo Schimmel static int __net_init fib_notifier_net_init(struct net *net)
17204b1d4e5SIdo Schimmel {
1733dd97a08SJiri Pirko 	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
1743dd97a08SJiri Pirko 
1753dd97a08SJiri Pirko 	INIT_LIST_HEAD(&fn_net->fib_notifier_ops);
1767c550dafSJiri Pirko 	ATOMIC_INIT_NOTIFIER_HEAD(&fn_net->fib_chain);
17704b1d4e5SIdo Schimmel 	return 0;
17804b1d4e5SIdo Schimmel }
17904b1d4e5SIdo Schimmel 
fib_notifier_net_exit(struct net * net)1800b6f5955SVasily Averin static void __net_exit fib_notifier_net_exit(struct net *net)
1810b6f5955SVasily Averin {
1823dd97a08SJiri Pirko 	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
1833dd97a08SJiri Pirko 
1843dd97a08SJiri Pirko 	WARN_ON_ONCE(!list_empty(&fn_net->fib_notifier_ops));
1850b6f5955SVasily Averin }
1860b6f5955SVasily Averin 
18704b1d4e5SIdo Schimmel static struct pernet_operations fib_notifier_net_ops = {
18804b1d4e5SIdo Schimmel 	.init = fib_notifier_net_init,
1890b6f5955SVasily Averin 	.exit = fib_notifier_net_exit,
1903dd97a08SJiri Pirko 	.id = &fib_notifier_net_id,
1913dd97a08SJiri Pirko 	.size = sizeof(struct fib_notifier_net),
19204b1d4e5SIdo Schimmel };
19304b1d4e5SIdo Schimmel 
fib_notifier_init(void)19404b1d4e5SIdo Schimmel static int __init fib_notifier_init(void)
19504b1d4e5SIdo Schimmel {
19604b1d4e5SIdo Schimmel 	return register_pernet_subsys(&fib_notifier_net_ops);
19704b1d4e5SIdo Schimmel }
19804b1d4e5SIdo Schimmel 
19904b1d4e5SIdo Schimmel subsys_initcall(fib_notifier_init);
200