2017-03-10 15:56:18 +08:00
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <linux/notifier.h>
|
|
|
|
#include <linux/rcupdate.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <net/net_namespace.h>
|
|
|
|
#include <net/netns/ipv4.h>
|
|
|
|
#include <net/ip_fib.h>
|
|
|
|
|
|
|
|
static ATOMIC_NOTIFIER_HEAD(fib_chain);
|
|
|
|
|
|
|
|
int call_fib_notifier(struct notifier_block *nb, struct net *net,
|
|
|
|
enum fib_event_type event_type,
|
|
|
|
struct fib_notifier_info *info)
|
|
|
|
{
|
|
|
|
info->net = net;
|
|
|
|
return nb->notifier_call(nb, event_type, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
int call_fib_notifiers(struct net *net, enum fib_event_type event_type,
|
|
|
|
struct fib_notifier_info *info)
|
|
|
|
{
|
|
|
|
net->ipv4.fib_seq++;
|
|
|
|
info->net = net;
|
|
|
|
return atomic_notifier_call_chain(&fib_chain, event_type, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int fib_seq_sum(void)
|
|
|
|
{
|
|
|
|
unsigned int fib_seq = 0;
|
|
|
|
struct net *net;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
for_each_net(net)
|
|
|
|
fib_seq += net->ipv4.fib_seq;
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
return fib_seq;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool fib_dump_is_consistent(struct notifier_block *nb,
|
|
|
|
void (*cb)(struct notifier_block *nb),
|
|
|
|
unsigned int fib_seq)
|
|
|
|
{
|
|
|
|
atomic_notifier_chain_register(&fib_chain, nb);
|
|
|
|
if (fib_seq == fib_seq_sum())
|
|
|
|
return true;
|
|
|
|
atomic_notifier_chain_unregister(&fib_chain, nb);
|
|
|
|
if (cb)
|
|
|
|
cb(nb);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define FIB_DUMP_MAX_RETRIES 5
|
|
|
|
int register_fib_notifier(struct notifier_block *nb,
|
|
|
|
void (*cb)(struct notifier_block *nb))
|
|
|
|
{
|
|
|
|
int retries = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
unsigned int fib_seq = fib_seq_sum();
|
|
|
|
struct net *net;
|
|
|
|
|
|
|
|
/* Mutex semantics guarantee that every change done to
|
|
|
|
* FIB tries before we read the change sequence counter
|
|
|
|
* is now visible to us.
|
|
|
|
*/
|
|
|
|
rcu_read_lock();
|
|
|
|
for_each_net_rcu(net) {
|
2017-03-10 15:56:19 +08:00
|
|
|
fib_rules_notify(net, nb);
|
|
|
|
fib_notify(net, nb);
|
2017-03-10 15:56:18 +08:00
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
if (fib_dump_is_consistent(nb, cb, fib_seq))
|
|
|
|
return 0;
|
|
|
|
} while (++retries < FIB_DUMP_MAX_RETRIES);
|
|
|
|
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(register_fib_notifier);
|
|
|
|
|
|
|
|
int unregister_fib_notifier(struct notifier_block *nb)
|
|
|
|
{
|
|
|
|
return atomic_notifier_chain_unregister(&fib_chain, nb);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(unregister_fib_notifier);
|