Commit | Line | Data |
---|---|---|
04b1d4e5 IS |
1 | #include <linux/rtnetlink.h> |
2 | #include <linux/notifier.h> | |
3 | #include <linux/rcupdate.h> | |
4 | #include <linux/kernel.h> | |
5 | #include <linux/init.h> | |
6 | #include <net/net_namespace.h> | |
7 | #include <net/fib_notifier.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 | EXPORT_SYMBOL(call_fib_notifier); | |
19 | ||
20 | int call_fib_notifiers(struct net *net, enum fib_event_type event_type, | |
21 | struct fib_notifier_info *info) | |
22 | { | |
23 | info->net = net; | |
24 | return atomic_notifier_call_chain(&fib_chain, event_type, info); | |
25 | } | |
26 | EXPORT_SYMBOL(call_fib_notifiers); | |
27 | ||
28 | static unsigned int fib_seq_sum(void) | |
29 | { | |
30 | struct fib_notifier_ops *ops; | |
31 | unsigned int fib_seq = 0; | |
32 | struct net *net; | |
33 | ||
34 | rtnl_lock(); | |
35 | for_each_net(net) { | |
36 | list_for_each_entry(ops, &net->fib_notifier_ops, list) | |
37 | fib_seq += ops->fib_seq_read(net); | |
38 | } | |
39 | rtnl_unlock(); | |
40 | ||
41 | return fib_seq; | |
42 | } | |
43 | ||
44 | static int fib_net_dump(struct net *net, struct notifier_block *nb) | |
45 | { | |
46 | struct fib_notifier_ops *ops; | |
47 | ||
48 | list_for_each_entry_rcu(ops, &net->fib_notifier_ops, list) { | |
49 | int err = ops->fib_dump(net, nb); | |
50 | ||
51 | if (err) | |
52 | return err; | |
53 | } | |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
58 | static bool fib_dump_is_consistent(struct notifier_block *nb, | |
59 | void (*cb)(struct notifier_block *nb), | |
60 | unsigned int fib_seq) | |
61 | { | |
62 | atomic_notifier_chain_register(&fib_chain, nb); | |
63 | if (fib_seq == fib_seq_sum()) | |
64 | return true; | |
65 | atomic_notifier_chain_unregister(&fib_chain, nb); | |
66 | if (cb) | |
67 | cb(nb); | |
68 | return false; | |
69 | } | |
70 | ||
71 | #define FIB_DUMP_MAX_RETRIES 5 | |
72 | int register_fib_notifier(struct notifier_block *nb, | |
73 | void (*cb)(struct notifier_block *nb)) | |
74 | { | |
75 | int retries = 0; | |
76 | int err; | |
77 | ||
78 | do { | |
79 | unsigned int fib_seq = fib_seq_sum(); | |
80 | struct net *net; | |
81 | ||
82 | rcu_read_lock(); | |
83 | for_each_net_rcu(net) { | |
84 | err = fib_net_dump(net, nb); | |
85 | if (err) | |
86 | goto err_fib_net_dump; | |
87 | } | |
88 | rcu_read_unlock(); | |
89 | ||
90 | if (fib_dump_is_consistent(nb, cb, fib_seq)) | |
91 | return 0; | |
92 | } while (++retries < FIB_DUMP_MAX_RETRIES); | |
93 | ||
94 | return -EBUSY; | |
95 | ||
96 | err_fib_net_dump: | |
97 | rcu_read_unlock(); | |
98 | return err; | |
99 | } | |
100 | EXPORT_SYMBOL(register_fib_notifier); | |
101 | ||
102 | int unregister_fib_notifier(struct notifier_block *nb) | |
103 | { | |
104 | return atomic_notifier_chain_unregister(&fib_chain, nb); | |
105 | } | |
106 | EXPORT_SYMBOL(unregister_fib_notifier); | |
107 | ||
108 | static int __fib_notifier_ops_register(struct fib_notifier_ops *ops, | |
109 | struct net *net) | |
110 | { | |
111 | struct fib_notifier_ops *o; | |
112 | ||
113 | list_for_each_entry(o, &net->fib_notifier_ops, list) | |
114 | if (ops->family == o->family) | |
115 | return -EEXIST; | |
116 | list_add_tail_rcu(&ops->list, &net->fib_notifier_ops); | |
117 | return 0; | |
118 | } | |
119 | ||
120 | struct fib_notifier_ops * | |
121 | fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net) | |
122 | { | |
123 | struct fib_notifier_ops *ops; | |
124 | int err; | |
125 | ||
126 | ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL); | |
127 | if (!ops) | |
128 | return ERR_PTR(-ENOMEM); | |
129 | ||
130 | err = __fib_notifier_ops_register(ops, net); | |
131 | if (err) | |
132 | goto err_register; | |
133 | ||
134 | return ops; | |
135 | ||
136 | err_register: | |
137 | kfree(ops); | |
138 | return ERR_PTR(err); | |
139 | } | |
140 | EXPORT_SYMBOL(fib_notifier_ops_register); | |
141 | ||
142 | void fib_notifier_ops_unregister(struct fib_notifier_ops *ops) | |
143 | { | |
144 | list_del_rcu(&ops->list); | |
145 | kfree_rcu(ops, rcu); | |
146 | } | |
147 | EXPORT_SYMBOL(fib_notifier_ops_unregister); | |
148 | ||
149 | static int __net_init fib_notifier_net_init(struct net *net) | |
150 | { | |
151 | INIT_LIST_HEAD(&net->fib_notifier_ops); | |
152 | return 0; | |
153 | } | |
154 | ||
155 | static struct pernet_operations fib_notifier_net_ops = { | |
156 | .init = fib_notifier_net_init, | |
157 | }; | |
158 | ||
159 | static int __init fib_notifier_init(void) | |
160 | { | |
161 | return register_pernet_subsys(&fib_notifier_net_ops); | |
162 | } | |
163 | ||
164 | subsys_initcall(fib_notifier_init); |