]>
Commit | Line | Data |
---|---|---|
c0243892 IS |
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) { | |
d05f7a7d IS |
69 | fib_rules_notify(net, nb); |
70 | fib_notify(net, nb); | |
c0243892 IS |
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); |