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