GNU Linux-libre 5.4.207-gnu1
[releases.git] / net / core / fib_notifier.c
1 #include <linux/rtnetlink.h>
2 #include <linux/notifier.h>
3 #include <linux/rcupdate.h>
4 #include <linux/kernel.h>
5 #include <linux/module.h>
6 #include <linux/init.h>
7 #include <net/net_namespace.h>
8 #include <net/netns/generic.h>
9 #include <net/fib_notifier.h>
10
11 static unsigned int fib_notifier_net_id;
12
13 struct fib_notifier_net {
14         struct list_head fib_notifier_ops;
15 };
16
17 static ATOMIC_NOTIFIER_HEAD(fib_chain);
18
19 int call_fib_notifier(struct notifier_block *nb, struct net *net,
20                       enum fib_event_type event_type,
21                       struct fib_notifier_info *info)
22 {
23         int err;
24
25         info->net = net;
26         err = nb->notifier_call(nb, event_type, info);
27         return notifier_to_errno(err);
28 }
29 EXPORT_SYMBOL(call_fib_notifier);
30
31 int call_fib_notifiers(struct net *net, enum fib_event_type event_type,
32                        struct fib_notifier_info *info)
33 {
34         int err;
35
36         info->net = net;
37         err = atomic_notifier_call_chain(&fib_chain, event_type, info);
38         return notifier_to_errno(err);
39 }
40 EXPORT_SYMBOL(call_fib_notifiers);
41
42 static unsigned int fib_seq_sum(void)
43 {
44         struct fib_notifier_net *fn_net;
45         struct fib_notifier_ops *ops;
46         unsigned int fib_seq = 0;
47         struct net *net;
48
49         rtnl_lock();
50         down_read(&net_rwsem);
51         for_each_net(net) {
52                 fn_net = net_generic(net, fib_notifier_net_id);
53                 rcu_read_lock();
54                 list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) {
55                         if (!try_module_get(ops->owner))
56                                 continue;
57                         fib_seq += ops->fib_seq_read(net);
58                         module_put(ops->owner);
59                 }
60                 rcu_read_unlock();
61         }
62         up_read(&net_rwsem);
63         rtnl_unlock();
64
65         return fib_seq;
66 }
67
68 static int fib_net_dump(struct net *net, struct notifier_block *nb)
69 {
70         struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
71         struct fib_notifier_ops *ops;
72
73         list_for_each_entry_rcu(ops, &fn_net->fib_notifier_ops, list) {
74                 int err;
75
76                 if (!try_module_get(ops->owner))
77                         continue;
78                 err = ops->fib_dump(net, nb);
79                 module_put(ops->owner);
80                 if (err)
81                         return err;
82         }
83
84         return 0;
85 }
86
87 static bool fib_dump_is_consistent(struct notifier_block *nb,
88                                    void (*cb)(struct notifier_block *nb),
89                                    unsigned int fib_seq)
90 {
91         atomic_notifier_chain_register(&fib_chain, nb);
92         if (fib_seq == fib_seq_sum())
93                 return true;
94         atomic_notifier_chain_unregister(&fib_chain, nb);
95         if (cb)
96                 cb(nb);
97         return false;
98 }
99
100 #define FIB_DUMP_MAX_RETRIES 5
101 int register_fib_notifier(struct notifier_block *nb,
102                           void (*cb)(struct notifier_block *nb))
103 {
104         int retries = 0;
105         int err;
106
107         do {
108                 unsigned int fib_seq = fib_seq_sum();
109                 struct net *net;
110
111                 rcu_read_lock();
112                 for_each_net_rcu(net) {
113                         err = fib_net_dump(net, nb);
114                         if (err)
115                                 goto err_fib_net_dump;
116                 }
117                 rcu_read_unlock();
118
119                 if (fib_dump_is_consistent(nb, cb, fib_seq))
120                         return 0;
121         } while (++retries < FIB_DUMP_MAX_RETRIES);
122
123         return -EBUSY;
124
125 err_fib_net_dump:
126         rcu_read_unlock();
127         return err;
128 }
129 EXPORT_SYMBOL(register_fib_notifier);
130
131 int unregister_fib_notifier(struct notifier_block *nb)
132 {
133         return atomic_notifier_chain_unregister(&fib_chain, nb);
134 }
135 EXPORT_SYMBOL(unregister_fib_notifier);
136
137 static int __fib_notifier_ops_register(struct fib_notifier_ops *ops,
138                                        struct net *net)
139 {
140         struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
141         struct fib_notifier_ops *o;
142
143         list_for_each_entry(o, &fn_net->fib_notifier_ops, list)
144                 if (ops->family == o->family)
145                         return -EEXIST;
146         list_add_tail_rcu(&ops->list, &fn_net->fib_notifier_ops);
147         return 0;
148 }
149
150 struct fib_notifier_ops *
151 fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net)
152 {
153         struct fib_notifier_ops *ops;
154         int err;
155
156         ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL);
157         if (!ops)
158                 return ERR_PTR(-ENOMEM);
159
160         err = __fib_notifier_ops_register(ops, net);
161         if (err)
162                 goto err_register;
163
164         return ops;
165
166 err_register:
167         kfree(ops);
168         return ERR_PTR(err);
169 }
170 EXPORT_SYMBOL(fib_notifier_ops_register);
171
172 void fib_notifier_ops_unregister(struct fib_notifier_ops *ops)
173 {
174         list_del_rcu(&ops->list);
175         kfree_rcu(ops, rcu);
176 }
177 EXPORT_SYMBOL(fib_notifier_ops_unregister);
178
179 static int __net_init fib_notifier_net_init(struct net *net)
180 {
181         struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
182
183         INIT_LIST_HEAD(&fn_net->fib_notifier_ops);
184         return 0;
185 }
186
187 static void __net_exit fib_notifier_net_exit(struct net *net)
188 {
189         struct fib_notifier_net *fn_net = net_generic(net, fib_notifier_net_id);
190
191         WARN_ON_ONCE(!list_empty(&fn_net->fib_notifier_ops));
192 }
193
194 static struct pernet_operations fib_notifier_net_ops = {
195         .init = fib_notifier_net_init,
196         .exit = fib_notifier_net_exit,
197         .id = &fib_notifier_net_id,
198         .size = sizeof(struct fib_notifier_net),
199 };
200
201 static int __init fib_notifier_init(void)
202 {
203         return register_pernet_subsys(&fib_notifier_net_ops);
204 }
205
206 subsys_initcall(fib_notifier_init);