198 lines · 4.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <linux/rtnetlink.h>#include <linux/notifier.h>#include <linux/rcupdate.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <net/net_namespace.h>#include <net/netns/generic.h>#include <net/fib_notifier.h> static unsigned int fib_notifier_net_id; struct fib_notifier_net {	struct list_head fib_notifier_ops;	struct atomic_notifier_head fib_chain;}; int call_fib_notifier(struct notifier_block *nb,		      enum fib_event_type event_type,		      struct fib_notifier_info *info){	int err; 	err = nb->notifier_call(nb, event_type, info);	return notifier_to_errno(err);}EXPORT_SYMBOL(call_fib_notifier); int call_fib_notifiers(struct net *net, enum fib_event_type event_type,		       struct fib_notifier_info *info){	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);	int err; 	err = atomic_notifier_call_chain(&fn_net->fib_chain, event_type, info);	return notifier_to_errno(err);}EXPORT_SYMBOL(call_fib_notifiers); static unsigned int fib_seq_sum(struct net *net){	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);	struct fib_notifier_ops *ops;	unsigned int fib_seq = 0; 	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);	}	rcu_read_unlock(); 	return fib_seq;} static int fib_net_dump(struct net *net, struct notifier_block *nb,			struct netlink_ext_ack *extack){	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);	struct fib_notifier_ops *ops;	int err = 0; 	rcu_read_lock();	list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) {		if (!try_module_get(ops->owner))			continue;		err = ops->fib_dump(net, nb, extack);		module_put(ops->owner);		if (err)			goto unlock;	} unlock:	rcu_read_unlock(); 	return err;} static bool fib_dump_is_consistent(struct net *net, struct notifier_block *nb,				   void (*cb)(struct notifier_block *nb),				   unsigned int fib_seq){	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))		return true;	atomic_notifier_chain_unregister(&fn_net->fib_chain, nb);	if (cb)		cb(nb);	return false;} #define FIB_DUMP_MAX_RETRIES 5int register_fib_notifier(struct net *net, struct notifier_block *nb,			  void (*cb)(struct notifier_block *nb),			  struct netlink_ext_ack *extack){	int retries = 0;	int err; 	do {		unsigned int fib_seq = fib_seq_sum(net); 		err = fib_net_dump(net, nb, extack);		if (err)			return err; 		if (fib_dump_is_consistent(net, nb, cb, fib_seq))			return 0;	} while (++retries < FIB_DUMP_MAX_RETRIES); 	return -EBUSY;}EXPORT_SYMBOL(register_fib_notifier); int unregister_fib_notifier(struct net *net, struct notifier_block *nb){	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 	return atomic_notifier_chain_unregister(&fn_net->fib_chain, nb);}EXPORT_SYMBOL(unregister_fib_notifier); static int __fib_notifier_ops_register(struct fib_notifier_ops *ops,				       struct net *net){	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);	struct fib_notifier_ops *o; 	list_for_each_entry(o, &fn_net->fib_notifier_ops, list)		if (ops->family == o->family)			return -EEXIST;	list_add_tail_rcu(&ops->list, &fn_net->fib_notifier_ops);	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){	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 	INIT_LIST_HEAD(&fn_net->fib_notifier_ops);	ATOMIC_INIT_NOTIFIER_HEAD(&fn_net->fib_chain);	return 0;} static void __net_exit fib_notifier_net_exit(struct net *net){	struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id); 	WARN_ON_ONCE(!list_empty(&fn_net->fib_notifier_ops));} static struct pernet_operations fib_notifier_net_ops = {	.init = fib_notifier_net_init,	.exit = fib_notifier_net_exit,	.id = &fib_notifier_net_id,	.size = sizeof(struct fib_notifier_net),}; static int __init fib_notifier_init(void){	return register_pernet_subsys(&fib_notifier_net_ops);} subsys_initcall(fib_notifier_init);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t