Ido Schimmel | c024389 | 2017-03-10 08:56:18 +0100 | [diff] [blame] | 1 | #include <linux/rtnetlink.h> |
| 2 | #include <linux/notifier.h> |
| 3 | #include <linux/rcupdate.h> |
| 4 | #include <linux/kernel.h> |
| 5 | #include <net/net_namespace.h> |
| 6 | #include <net/netns/ipv4.h> |
| 7 | #include <net/ip_fib.h> |
| 8 | |
| 9 | static ATOMIC_NOTIFIER_HEAD(fib_chain); |
| 10 | |
| 11 | int call_fib_notifier(struct notifier_block *nb, struct net *net, |
| 12 | enum fib_event_type event_type, |
| 13 | struct fib_notifier_info *info) |
| 14 | { |
| 15 | info->net = net; |
| 16 | return nb->notifier_call(nb, event_type, info); |
| 17 | } |
| 18 | |
| 19 | int call_fib_notifiers(struct net *net, enum fib_event_type event_type, |
| 20 | struct fib_notifier_info *info) |
| 21 | { |
| 22 | net->ipv4.fib_seq++; |
| 23 | info->net = net; |
| 24 | return atomic_notifier_call_chain(&fib_chain, event_type, info); |
| 25 | } |
| 26 | |
| 27 | static unsigned int fib_seq_sum(void) |
| 28 | { |
| 29 | unsigned int fib_seq = 0; |
| 30 | struct net *net; |
| 31 | |
| 32 | rtnl_lock(); |
| 33 | for_each_net(net) |
| 34 | fib_seq += net->ipv4.fib_seq; |
| 35 | rtnl_unlock(); |
| 36 | |
| 37 | return fib_seq; |
| 38 | } |
| 39 | |
| 40 | static bool fib_dump_is_consistent(struct notifier_block *nb, |
| 41 | void (*cb)(struct notifier_block *nb), |
| 42 | unsigned int fib_seq) |
| 43 | { |
| 44 | atomic_notifier_chain_register(&fib_chain, nb); |
| 45 | if (fib_seq == fib_seq_sum()) |
| 46 | return true; |
| 47 | atomic_notifier_chain_unregister(&fib_chain, nb); |
| 48 | if (cb) |
| 49 | cb(nb); |
| 50 | return false; |
| 51 | } |
| 52 | |
| 53 | #define FIB_DUMP_MAX_RETRIES 5 |
| 54 | int register_fib_notifier(struct notifier_block *nb, |
| 55 | void (*cb)(struct notifier_block *nb)) |
| 56 | { |
| 57 | int retries = 0; |
| 58 | |
| 59 | do { |
| 60 | unsigned int fib_seq = fib_seq_sum(); |
| 61 | struct net *net; |
| 62 | |
| 63 | /* Mutex semantics guarantee that every change done to |
| 64 | * FIB tries before we read the change sequence counter |
| 65 | * is now visible to us. |
| 66 | */ |
| 67 | rcu_read_lock(); |
| 68 | for_each_net_rcu(net) { |
Ido Schimmel | d05f7a7 | 2017-03-10 08:56:19 +0100 | [diff] [blame] | 69 | fib_rules_notify(net, nb); |
| 70 | fib_notify(net, nb); |
Ido Schimmel | c024389 | 2017-03-10 08:56:18 +0100 | [diff] [blame] | 71 | } |
| 72 | rcu_read_unlock(); |
| 73 | |
| 74 | if (fib_dump_is_consistent(nb, cb, fib_seq)) |
| 75 | return 0; |
| 76 | } while (++retries < FIB_DUMP_MAX_RETRIES); |
| 77 | |
| 78 | return -EBUSY; |
| 79 | } |
| 80 | EXPORT_SYMBOL(register_fib_notifier); |
| 81 | |
| 82 | int unregister_fib_notifier(struct notifier_block *nb) |
| 83 | { |
| 84 | return atomic_notifier_chain_unregister(&fib_chain, nb); |
| 85 | } |
| 86 | EXPORT_SYMBOL(unregister_fib_notifier); |