]>
Commit | Line | Data |
---|---|---|
601e68e1 | 1 | /* netfilter.c: look after the filters for various protocols. |
f6ebe77f HW |
2 | * Heavily influenced by the old firewall.c by David Bonn and Alan Cox. |
3 | * | |
4 | * Thanks to Rob `CmdrTaco' Malda for not influencing this code in any | |
5 | * way. | |
6 | * | |
b3a61254 | 7 | * This code is GPL. |
f6ebe77f | 8 | */ |
f6ebe77f HW |
9 | #include <linux/kernel.h> |
10 | #include <linux/netfilter.h> | |
11 | #include <net/protocol.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/skbuff.h> | |
14 | #include <linux/wait.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/if.h> | |
18 | #include <linux/netdevice.h> | |
56768644 | 19 | #include <linux/netfilter_ipv6.h> |
f6ebe77f HW |
20 | #include <linux/inetdevice.h> |
21 | #include <linux/proc_fs.h> | |
d486dd1f | 22 | #include <linux/mutex.h> |
960632ec | 23 | #include <linux/mm.h> |
e3b37f11 | 24 | #include <linux/rcupdate.h> |
457c4cbc | 25 | #include <net/net_namespace.h> |
971502d7 | 26 | #include <net/netfilter/nf_queue.h> |
f6ebe77f HW |
27 | #include <net/sock.h> |
28 | ||
29 | #include "nf_internals.h" | |
30 | ||
2a7851bf FW |
31 | const struct nf_ipv6_ops __rcu *nf_ipv6_ops __read_mostly; |
32 | EXPORT_SYMBOL_GPL(nf_ipv6_ops); | |
bce8032e | 33 | |
e7c8899f FW |
34 | DEFINE_PER_CPU(bool, nf_skb_duplicated); |
35 | EXPORT_SYMBOL_GPL(nf_skb_duplicated); | |
36 | ||
e9666d10 | 37 | #ifdef CONFIG_JUMP_LABEL |
c5905afb | 38 | struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; |
a2d7ec58 ED |
39 | EXPORT_SYMBOL(nf_hooks_needed); |
40 | #endif | |
41 | ||
fd706d69 | 42 | static DEFINE_MUTEX(nf_hook_mutex); |
960632ec AC |
43 | |
44 | /* max hooks per family/hooknum */ | |
45 | #define MAX_HOOK_COUNT 1024 | |
46 | ||
e3b37f11 AC |
47 | #define nf_entry_dereference(e) \ |
48 | rcu_dereference_protected(e, lockdep_is_held(&nf_hook_mutex)) | |
f6ebe77f | 49 | |
960632ec AC |
50 | static struct nf_hook_entries *allocate_hook_entries_size(u16 num) |
51 | { | |
52 | struct nf_hook_entries *e; | |
53 | size_t alloc = sizeof(*e) + | |
54 | sizeof(struct nf_hook_entry) * num + | |
8c873e21 FW |
55 | sizeof(struct nf_hook_ops *) * num + |
56 | sizeof(struct nf_hook_entries_rcu_head); | |
960632ec AC |
57 | |
58 | if (num == 0) | |
59 | return NULL; | |
60 | ||
61 | e = kvzalloc(alloc, GFP_KERNEL); | |
62 | if (e) | |
63 | e->num_hook_entries = num; | |
64 | return e; | |
65 | } | |
66 | ||
8c873e21 FW |
67 | static void __nf_hook_entries_free(struct rcu_head *h) |
68 | { | |
69 | struct nf_hook_entries_rcu_head *head; | |
70 | ||
71 | head = container_of(h, struct nf_hook_entries_rcu_head, head); | |
72 | kvfree(head->allocation); | |
73 | } | |
74 | ||
75 | static void nf_hook_entries_free(struct nf_hook_entries *e) | |
76 | { | |
77 | struct nf_hook_entries_rcu_head *head; | |
78 | struct nf_hook_ops **ops; | |
79 | unsigned int num; | |
80 | ||
81 | if (!e) | |
82 | return; | |
83 | ||
84 | num = e->num_hook_entries; | |
85 | ops = nf_hook_entries_get_hook_ops(e); | |
86 | head = (void *)&ops[num]; | |
87 | head->allocation = e; | |
88 | call_rcu(&head->head, __nf_hook_entries_free); | |
89 | } | |
90 | ||
960632ec AC |
91 | static unsigned int accept_all(void *priv, |
92 | struct sk_buff *skb, | |
93 | const struct nf_hook_state *state) | |
94 | { | |
95 | return NF_ACCEPT; /* ACCEPT makes nf_hook_slow call next hook */ | |
96 | } | |
97 | ||
98 | static const struct nf_hook_ops dummy_ops = { | |
99 | .hook = accept_all, | |
100 | .priority = INT_MIN, | |
101 | }; | |
102 | ||
103 | static struct nf_hook_entries * | |
104 | nf_hook_entries_grow(const struct nf_hook_entries *old, | |
105 | const struct nf_hook_ops *reg) | |
106 | { | |
107 | unsigned int i, alloc_entries, nhooks, old_entries; | |
108 | struct nf_hook_ops **orig_ops = NULL; | |
109 | struct nf_hook_ops **new_ops; | |
110 | struct nf_hook_entries *new; | |
111 | bool inserted = false; | |
112 | ||
113 | alloc_entries = 1; | |
114 | old_entries = old ? old->num_hook_entries : 0; | |
115 | ||
116 | if (old) { | |
117 | orig_ops = nf_hook_entries_get_hook_ops(old); | |
118 | ||
119 | for (i = 0; i < old_entries; i++) { | |
120 | if (orig_ops[i] != &dummy_ops) | |
121 | alloc_entries++; | |
122 | } | |
123 | } | |
124 | ||
125 | if (alloc_entries > MAX_HOOK_COUNT) | |
126 | return ERR_PTR(-E2BIG); | |
127 | ||
128 | new = allocate_hook_entries_size(alloc_entries); | |
129 | if (!new) | |
130 | return ERR_PTR(-ENOMEM); | |
131 | ||
132 | new_ops = nf_hook_entries_get_hook_ops(new); | |
133 | ||
134 | i = 0; | |
135 | nhooks = 0; | |
136 | while (i < old_entries) { | |
137 | if (orig_ops[i] == &dummy_ops) { | |
138 | ++i; | |
139 | continue; | |
140 | } | |
f92b40a8 | 141 | |
960632ec AC |
142 | if (inserted || reg->priority > orig_ops[i]->priority) { |
143 | new_ops[nhooks] = (void *)orig_ops[i]; | |
144 | new->hooks[nhooks] = old->hooks[i]; | |
145 | i++; | |
146 | } else { | |
147 | new_ops[nhooks] = (void *)reg; | |
148 | new->hooks[nhooks].hook = reg->hook; | |
149 | new->hooks[nhooks].priv = reg->priv; | |
150 | inserted = true; | |
151 | } | |
152 | nhooks++; | |
153 | } | |
154 | ||
155 | if (!inserted) { | |
156 | new_ops[nhooks] = (void *)reg; | |
157 | new->hooks[nhooks].hook = reg->hook; | |
158 | new->hooks[nhooks].priv = reg->priv; | |
159 | } | |
160 | ||
161 | return new; | |
162 | } | |
163 | ||
2420b79f FW |
164 | static void hooks_validate(const struct nf_hook_entries *hooks) |
165 | { | |
432d8220 | 166 | #ifdef CONFIG_DEBUG_MISC |
2420b79f FW |
167 | struct nf_hook_ops **orig_ops; |
168 | int prio = INT_MIN; | |
169 | size_t i = 0; | |
170 | ||
171 | orig_ops = nf_hook_entries_get_hook_ops(hooks); | |
172 | ||
173 | for (i = 0; i < hooks->num_hook_entries; i++) { | |
174 | if (orig_ops[i] == &dummy_ops) | |
175 | continue; | |
176 | ||
177 | WARN_ON(orig_ops[i]->priority < prio); | |
178 | ||
179 | if (orig_ops[i]->priority > prio) | |
180 | prio = orig_ops[i]->priority; | |
181 | } | |
182 | #endif | |
183 | } | |
184 | ||
06cad3ac FW |
185 | int nf_hook_entries_insert_raw(struct nf_hook_entries __rcu **pp, |
186 | const struct nf_hook_ops *reg) | |
187 | { | |
188 | struct nf_hook_entries *new_hooks; | |
189 | struct nf_hook_entries *p; | |
190 | ||
191 | p = rcu_dereference_raw(*pp); | |
192 | new_hooks = nf_hook_entries_grow(p, reg); | |
193 | if (IS_ERR(new_hooks)) | |
194 | return PTR_ERR(new_hooks); | |
195 | ||
196 | hooks_validate(new_hooks); | |
197 | ||
198 | rcu_assign_pointer(*pp, new_hooks); | |
199 | ||
200 | BUG_ON(p == new_hooks); | |
201 | nf_hook_entries_free(p); | |
202 | return 0; | |
203 | } | |
204 | EXPORT_SYMBOL_GPL(nf_hook_entries_insert_raw); | |
205 | ||
960632ec AC |
206 | /* |
207 | * __nf_hook_entries_try_shrink - try to shrink hook array | |
208 | * | |
06cad3ac | 209 | * @old -- current hook blob at @pp |
960632ec AC |
210 | * @pp -- location of hook blob |
211 | * | |
212 | * Hook unregistration must always succeed, so to-be-removed hooks | |
213 | * are replaced by a dummy one that will just move to next hook. | |
214 | * | |
215 | * This counts the current dummy hooks, attempts to allocate new blob, | |
216 | * copies the live hooks, then replaces and discards old one. | |
217 | * | |
218 | * return values: | |
219 | * | |
220 | * Returns address to free, or NULL. | |
221 | */ | |
06cad3ac FW |
222 | static void *__nf_hook_entries_try_shrink(struct nf_hook_entries *old, |
223 | struct nf_hook_entries __rcu **pp) | |
960632ec | 224 | { |
960632ec | 225 | unsigned int i, j, skip = 0, hook_entries; |
06cad3ac | 226 | struct nf_hook_entries *new = NULL; |
960632ec AC |
227 | struct nf_hook_ops **orig_ops; |
228 | struct nf_hook_ops **new_ops; | |
229 | ||
960632ec AC |
230 | if (WARN_ON_ONCE(!old)) |
231 | return NULL; | |
232 | ||
233 | orig_ops = nf_hook_entries_get_hook_ops(old); | |
234 | for (i = 0; i < old->num_hook_entries; i++) { | |
235 | if (orig_ops[i] == &dummy_ops) | |
236 | skip++; | |
237 | } | |
238 | ||
239 | /* if skip == hook_entries all hooks have been removed */ | |
240 | hook_entries = old->num_hook_entries; | |
241 | if (skip == hook_entries) | |
242 | goto out_assign; | |
243 | ||
74585d4f | 244 | if (skip == 0) |
960632ec AC |
245 | return NULL; |
246 | ||
247 | hook_entries -= skip; | |
248 | new = allocate_hook_entries_size(hook_entries); | |
249 | if (!new) | |
250 | return NULL; | |
251 | ||
252 | new_ops = nf_hook_entries_get_hook_ops(new); | |
253 | for (i = 0, j = 0; i < old->num_hook_entries; i++) { | |
254 | if (orig_ops[i] == &dummy_ops) | |
255 | continue; | |
256 | new->hooks[j] = old->hooks[i]; | |
257 | new_ops[j] = (void *)orig_ops[i]; | |
258 | j++; | |
259 | } | |
2420b79f | 260 | hooks_validate(new); |
960632ec AC |
261 | out_assign: |
262 | rcu_assign_pointer(*pp, new); | |
263 | return old; | |
264 | } | |
265 | ||
62a0fe46 PNA |
266 | static struct nf_hook_entries __rcu ** |
267 | nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum, | |
268 | struct net_device *dev) | |
f6ebe77f | 269 | { |
62a0fe46 | 270 | switch (pf) { |
b0f38338 FW |
271 | case NFPROTO_NETDEV: |
272 | break; | |
2a95183a | 273 | #ifdef CONFIG_NETFILTER_FAMILY_ARP |
b0f38338 | 274 | case NFPROTO_ARP: |
62a0fe46 | 275 | if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_arp) <= hooknum)) |
ef57170b | 276 | return NULL; |
62a0fe46 | 277 | return net->nf.hooks_arp + hooknum; |
2a95183a FW |
278 | #endif |
279 | #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE | |
b0f38338 | 280 | case NFPROTO_BRIDGE: |
62a0fe46 | 281 | if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= hooknum)) |
ef57170b | 282 | return NULL; |
62a0fe46 | 283 | return net->nf.hooks_bridge + hooknum; |
60a3815d PNA |
284 | #endif |
285 | #ifdef CONFIG_NETFILTER_INGRESS | |
286 | case NFPROTO_INET: | |
287 | if (WARN_ON_ONCE(hooknum != NF_INET_INGRESS)) | |
288 | return NULL; | |
289 | if (!dev || dev_net(dev) != net) { | |
290 | WARN_ON_ONCE(1); | |
291 | return NULL; | |
292 | } | |
293 | return &dev->nf_hooks_ingress; | |
2a95183a | 294 | #endif |
b0f38338 | 295 | case NFPROTO_IPV4: |
62a0fe46 | 296 | if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= hooknum)) |
ef57170b | 297 | return NULL; |
62a0fe46 | 298 | return net->nf.hooks_ipv4 + hooknum; |
b0f38338 | 299 | case NFPROTO_IPV6: |
62a0fe46 | 300 | if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv6) <= hooknum)) |
ef57170b | 301 | return NULL; |
62a0fe46 | 302 | return net->nf.hooks_ipv6 + hooknum; |
bb4badf3 | 303 | #if IS_ENABLED(CONFIG_DECNET) |
b0f38338 | 304 | case NFPROTO_DECNET: |
62a0fe46 | 305 | if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_decnet) <= hooknum)) |
ef57170b | 306 | return NULL; |
62a0fe46 | 307 | return net->nf.hooks_decnet + hooknum; |
bb4badf3 | 308 | #endif |
b0f38338 FW |
309 | default: |
310 | WARN_ON_ONCE(1); | |
311 | return NULL; | |
312 | } | |
bd3769bf | 313 | |
e687ad60 | 314 | #ifdef CONFIG_NETFILTER_INGRESS |
62a0fe46 PNA |
315 | if (hooknum == NF_NETDEV_INGRESS) { |
316 | if (dev && dev_net(dev) == net) | |
317 | return &dev->nf_hooks_ingress; | |
e687ad60 | 318 | } |
7816ec56 | 319 | #endif |
960632ec | 320 | WARN_ON_ONCE(1); |
bd3769bf | 321 | return NULL; |
e3b37f11 | 322 | } |
7181ebaf | 323 | |
60a3815d PNA |
324 | static int nf_ingress_check(struct net *net, const struct nf_hook_ops *reg, |
325 | int hooknum) | |
326 | { | |
327 | #ifndef CONFIG_NETFILTER_INGRESS | |
328 | if (reg->hooknum == hooknum) | |
329 | return -EOPNOTSUPP; | |
330 | #endif | |
331 | if (reg->hooknum != hooknum || | |
332 | !reg->dev || dev_net(reg->dev) != net) | |
333 | return -EINVAL; | |
334 | ||
335 | return 0; | |
336 | } | |
337 | ||
ddcfa710 PNA |
338 | static inline bool nf_ingress_hook(const struct nf_hook_ops *reg, int pf) |
339 | { | |
60a3815d PNA |
340 | if ((pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) || |
341 | (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS)) | |
342 | return true; | |
343 | ||
344 | return false; | |
ddcfa710 PNA |
345 | } |
346 | ||
afd9024c PNA |
347 | static void nf_static_key_inc(const struct nf_hook_ops *reg, int pf) |
348 | { | |
349 | #ifdef CONFIG_JUMP_LABEL | |
60a3815d PNA |
350 | int hooknum; |
351 | ||
352 | if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) { | |
353 | pf = NFPROTO_NETDEV; | |
354 | hooknum = NF_NETDEV_INGRESS; | |
355 | } else { | |
356 | hooknum = reg->hooknum; | |
357 | } | |
358 | static_key_slow_inc(&nf_hooks_needed[pf][hooknum]); | |
afd9024c PNA |
359 | #endif |
360 | } | |
361 | ||
362 | static void nf_static_key_dec(const struct nf_hook_ops *reg, int pf) | |
363 | { | |
364 | #ifdef CONFIG_JUMP_LABEL | |
60a3815d PNA |
365 | int hooknum; |
366 | ||
367 | if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) { | |
368 | pf = NFPROTO_NETDEV; | |
369 | hooknum = NF_NETDEV_INGRESS; | |
370 | } else { | |
371 | hooknum = reg->hooknum; | |
372 | } | |
373 | static_key_slow_dec(&nf_hooks_needed[pf][hooknum]); | |
afd9024c PNA |
374 | #endif |
375 | } | |
376 | ||
cb7ccd83 PNA |
377 | static int __nf_register_net_hook(struct net *net, int pf, |
378 | const struct nf_hook_ops *reg) | |
0edcf282 | 379 | { |
960632ec AC |
380 | struct nf_hook_entries *p, *new_hooks; |
381 | struct nf_hook_entries __rcu **pp; | |
60a3815d | 382 | int err; |
085db2c0 | 383 | |
60a3815d PNA |
384 | switch (pf) { |
385 | case NFPROTO_NETDEV: | |
386 | err = nf_ingress_check(net, reg, NF_NETDEV_INGRESS); | |
387 | if (err < 0) | |
388 | return err; | |
389 | break; | |
390 | case NFPROTO_INET: | |
391 | if (reg->hooknum != NF_INET_INGRESS) | |
392 | break; | |
393 | ||
394 | err = nf_ingress_check(net, reg, NF_INET_INGRESS); | |
395 | if (err < 0) | |
396 | return err; | |
397 | break; | |
7816ec56 | 398 | } |
d4bb5caa | 399 | |
cb7ccd83 | 400 | pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev); |
bd3769bf LT |
401 | if (!pp) |
402 | return -EINVAL; | |
403 | ||
e3b37f11 | 404 | mutex_lock(&nf_hook_mutex); |
085db2c0 | 405 | |
960632ec AC |
406 | p = nf_entry_dereference(*pp); |
407 | new_hooks = nf_hook_entries_grow(p, reg); | |
408 | ||
409 | if (!IS_ERR(new_hooks)) | |
410 | rcu_assign_pointer(*pp, new_hooks); | |
e3b37f11 | 411 | |
fd706d69 | 412 | mutex_unlock(&nf_hook_mutex); |
960632ec AC |
413 | if (IS_ERR(new_hooks)) |
414 | return PTR_ERR(new_hooks); | |
415 | ||
2420b79f | 416 | hooks_validate(new_hooks); |
4c091156 | 417 | #ifdef CONFIG_NETFILTER_INGRESS |
ddcfa710 | 418 | if (nf_ingress_hook(reg, pf)) |
4c091156 EB |
419 | net_inc_ingress_queue(); |
420 | #endif | |
afd9024c PNA |
421 | nf_static_key_inc(reg, pf); |
422 | ||
960632ec | 423 | BUG_ON(p == new_hooks); |
8c873e21 | 424 | nf_hook_entries_free(p); |
f6ebe77f HW |
425 | return 0; |
426 | } | |
f6ebe77f | 427 | |
960632ec | 428 | /* |
3d3cdc38 | 429 | * nf_remove_net_hook - remove a hook from blob |
960632ec AC |
430 | * |
431 | * @oldp: current address of hook blob | |
432 | * @unreg: hook to unregister | |
433 | * | |
434 | * This cannot fail, hook unregistration must always succeed. | |
435 | * Therefore replace the to-be-removed hook with a dummy hook. | |
436 | */ | |
06cad3ac FW |
437 | static bool nf_remove_net_hook(struct nf_hook_entries *old, |
438 | const struct nf_hook_ops *unreg) | |
f6ebe77f | 439 | { |
960632ec | 440 | struct nf_hook_ops **orig_ops; |
960632ec | 441 | unsigned int i; |
085db2c0 | 442 | |
960632ec AC |
443 | orig_ops = nf_hook_entries_get_hook_ops(old); |
444 | for (i = 0; i < old->num_hook_entries; i++) { | |
445 | if (orig_ops[i] != unreg) | |
446 | continue; | |
447 | WRITE_ONCE(old->hooks[i].hook, accept_all); | |
514cc55b | 448 | WRITE_ONCE(orig_ops[i], (void *)&dummy_ops); |
06cad3ac | 449 | return true; |
085db2c0 | 450 | } |
960632ec | 451 | |
06cad3ac | 452 | return false; |
933bd83e FW |
453 | } |
454 | ||
0ded1785 WY |
455 | static void __nf_unregister_net_hook(struct net *net, int pf, |
456 | const struct nf_hook_ops *reg) | |
933bd83e | 457 | { |
960632ec AC |
458 | struct nf_hook_entries __rcu **pp; |
459 | struct nf_hook_entries *p; | |
933bd83e | 460 | |
cb7ccd83 | 461 | pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev); |
960632ec AC |
462 | if (!pp) |
463 | return; | |
464 | ||
465 | mutex_lock(&nf_hook_mutex); | |
466 | ||
467 | p = nf_entry_dereference(*pp); | |
468 | if (WARN_ON_ONCE(!p)) { | |
469 | mutex_unlock(&nf_hook_mutex); | |
470 | return; | |
471 | } | |
472 | ||
06cad3ac FW |
473 | if (nf_remove_net_hook(p, reg)) { |
474 | #ifdef CONFIG_NETFILTER_INGRESS | |
ddcfa710 | 475 | if (nf_ingress_hook(reg, pf)) |
06cad3ac FW |
476 | net_dec_ingress_queue(); |
477 | #endif | |
afd9024c | 478 | nf_static_key_dec(reg, pf); |
06cad3ac FW |
479 | } else { |
480 | WARN_ONCE(1, "hook not found, pf %d num %d", pf, reg->hooknum); | |
481 | } | |
960632ec | 482 | |
06cad3ac | 483 | p = __nf_hook_entries_try_shrink(p, pp); |
960632ec | 484 | mutex_unlock(&nf_hook_mutex); |
933bd83e FW |
485 | if (!p) |
486 | return; | |
487 | ||
26888dfd | 488 | nf_queue_nf_hook_drop(net); |
8c873e21 | 489 | nf_hook_entries_free(p); |
085db2c0 | 490 | } |
cb7ccd83 PNA |
491 | |
492 | void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) | |
493 | { | |
494 | if (reg->pf == NFPROTO_INET) { | |
60a3815d PNA |
495 | if (reg->hooknum == NF_INET_INGRESS) { |
496 | __nf_unregister_net_hook(net, NFPROTO_INET, reg); | |
497 | } else { | |
498 | __nf_unregister_net_hook(net, NFPROTO_IPV4, reg); | |
499 | __nf_unregister_net_hook(net, NFPROTO_IPV6, reg); | |
500 | } | |
cb7ccd83 PNA |
501 | } else { |
502 | __nf_unregister_net_hook(net, reg->pf, reg); | |
503 | } | |
504 | } | |
085db2c0 EB |
505 | EXPORT_SYMBOL(nf_unregister_net_hook); |
506 | ||
06cad3ac FW |
507 | void nf_hook_entries_delete_raw(struct nf_hook_entries __rcu **pp, |
508 | const struct nf_hook_ops *reg) | |
509 | { | |
510 | struct nf_hook_entries *p; | |
511 | ||
512 | p = rcu_dereference_raw(*pp); | |
513 | if (nf_remove_net_hook(p, reg)) { | |
514 | p = __nf_hook_entries_try_shrink(p, pp); | |
515 | nf_hook_entries_free(p); | |
516 | } | |
517 | } | |
518 | EXPORT_SYMBOL_GPL(nf_hook_entries_delete_raw); | |
519 | ||
cb7ccd83 PNA |
520 | int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) |
521 | { | |
522 | int err; | |
523 | ||
524 | if (reg->pf == NFPROTO_INET) { | |
60a3815d PNA |
525 | if (reg->hooknum == NF_INET_INGRESS) { |
526 | err = __nf_register_net_hook(net, NFPROTO_INET, reg); | |
527 | if (err < 0) | |
528 | return err; | |
529 | } else { | |
530 | err = __nf_register_net_hook(net, NFPROTO_IPV4, reg); | |
531 | if (err < 0) | |
532 | return err; | |
533 | ||
534 | err = __nf_register_net_hook(net, NFPROTO_IPV6, reg); | |
535 | if (err < 0) { | |
536 | __nf_unregister_net_hook(net, NFPROTO_IPV4, reg); | |
537 | return err; | |
538 | } | |
cb7ccd83 PNA |
539 | } |
540 | } else { | |
541 | err = __nf_register_net_hook(net, reg->pf, reg); | |
542 | if (err < 0) | |
543 | return err; | |
544 | } | |
545 | ||
546 | return 0; | |
547 | } | |
548 | EXPORT_SYMBOL(nf_register_net_hook); | |
549 | ||
085db2c0 EB |
550 | int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg, |
551 | unsigned int n) | |
552 | { | |
553 | unsigned int i; | |
554 | int err = 0; | |
555 | ||
556 | for (i = 0; i < n; i++) { | |
557 | err = nf_register_net_hook(net, ®[i]); | |
558 | if (err) | |
559 | goto err; | |
560 | } | |
561 | return err; | |
562 | ||
563 | err: | |
564 | if (i > 0) | |
565 | nf_unregister_net_hooks(net, reg, i); | |
566 | return err; | |
567 | } | |
568 | EXPORT_SYMBOL(nf_register_net_hooks); | |
569 | ||
570 | void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, | |
933bd83e | 571 | unsigned int hookcount) |
085db2c0 | 572 | { |
4e645b47 | 573 | unsigned int i; |
933bd83e | 574 | |
4e645b47 FW |
575 | for (i = 0; i < hookcount; i++) |
576 | nf_unregister_net_hook(net, ®[i]); | |
085db2c0 EB |
577 | } |
578 | EXPORT_SYMBOL(nf_unregister_net_hooks); | |
579 | ||
f6ebe77f | 580 | /* Returns 1 if okfn() needs to be executed by the caller, |
e2361cb9 | 581 | * -EPERM for NF_DROP, 0 otherwise. Caller must hold rcu_read_lock. */ |
01886bd9 | 582 | int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, |
960632ec | 583 | const struct nf_hook_entries *e, unsigned int s) |
f6ebe77f | 584 | { |
f6ebe77f | 585 | unsigned int verdict; |
c63cbc46 | 586 | int ret; |
f6ebe77f | 587 | |
960632ec AC |
588 | for (; s < e->num_hook_entries; s++) { |
589 | verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state); | |
26dfab72 PNA |
590 | switch (verdict & NF_VERDICT_MASK) { |
591 | case NF_ACCEPT: | |
26dfab72 PNA |
592 | break; |
593 | case NF_DROP: | |
594 | kfree_skb(skb); | |
595 | ret = NF_DROP_GETERR(verdict); | |
596 | if (ret == 0) | |
597 | ret = -EPERM; | |
598 | return ret; | |
26dfab72 | 599 | case NF_QUEUE: |
0d9cb300 | 600 | ret = nf_queue(skb, state, s, verdict); |
960632ec | 601 | if (ret == 1) |
26dfab72 PNA |
602 | continue; |
603 | return ret; | |
604 | default: | |
605 | /* Implicit handling for NF_STOLEN, as well as any other | |
606 | * non conventional verdicts. | |
607 | */ | |
608 | return 0; | |
609 | } | |
960632ec | 610 | } |
26dfab72 PNA |
611 | |
612 | return 1; | |
f6ebe77f HW |
613 | } |
614 | EXPORT_SYMBOL(nf_hook_slow); | |
615 | ||
ca58fbe0 FW |
616 | void nf_hook_slow_list(struct list_head *head, struct nf_hook_state *state, |
617 | const struct nf_hook_entries *e) | |
618 | { | |
619 | struct sk_buff *skb, *next; | |
620 | struct list_head sublist; | |
621 | int ret; | |
622 | ||
623 | INIT_LIST_HEAD(&sublist); | |
624 | ||
625 | list_for_each_entry_safe(skb, next, head, list) { | |
626 | skb_list_del_init(skb); | |
627 | ret = nf_hook_slow(skb, state, e, 0); | |
628 | if (ret == 1) | |
629 | list_add_tail(&skb->list, &sublist); | |
630 | } | |
631 | /* Put passed packets back on main list */ | |
632 | list_splice(&sublist, head); | |
633 | } | |
634 | EXPORT_SYMBOL(nf_hook_slow_list); | |
635 | ||
b7bd1809 PNA |
636 | /* This needs to be compiled in any case to avoid dependencies between the |
637 | * nfnetlink_queue code and nf_conntrack. | |
638 | */ | |
a4b4766c KM |
639 | struct nfnl_ct_hook __rcu *nfnl_ct_hook __read_mostly; |
640 | EXPORT_SYMBOL_GPL(nfnl_ct_hook); | |
b7bd1809 | 641 | |
1f4b2439 PNA |
642 | struct nf_ct_hook __rcu *nf_ct_hook __read_mostly; |
643 | EXPORT_SYMBOL_GPL(nf_ct_hook); | |
644 | ||
c0cd1156 | 645 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
f6ebe77f HW |
646 | /* This does not belong here, but locally generated errors need it if connection |
647 | tracking in use: without this, connection may not be in hash table, and hence | |
648 | manufactured ICMP or RST packets will not be associated with it. */ | |
312a0c16 PM |
649 | void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) |
650 | __rcu __read_mostly; | |
f6ebe77f HW |
651 | EXPORT_SYMBOL(ip_ct_attach); |
652 | ||
2c205dd3 PNA |
653 | struct nf_nat_hook __rcu *nf_nat_hook __read_mostly; |
654 | EXPORT_SYMBOL_GPL(nf_nat_hook); | |
655 | ||
312a0c16 | 656 | void nf_ct_attach(struct sk_buff *new, const struct sk_buff *skb) |
f6ebe77f | 657 | { |
312a0c16 | 658 | void (*attach)(struct sk_buff *, const struct sk_buff *); |
f6ebe77f | 659 | |
a9e419dc | 660 | if (skb->_nfct) { |
c3a47ab3 PM |
661 | rcu_read_lock(); |
662 | attach = rcu_dereference(ip_ct_attach); | |
663 | if (attach) | |
664 | attach(new, skb); | |
665 | rcu_read_unlock(); | |
f6ebe77f HW |
666 | } |
667 | } | |
668 | EXPORT_SYMBOL(nf_ct_attach); | |
de6e05c4 | 669 | |
de6e05c4 YK |
670 | void nf_conntrack_destroy(struct nf_conntrack *nfct) |
671 | { | |
1f4b2439 | 672 | struct nf_ct_hook *ct_hook; |
de6e05c4 YK |
673 | |
674 | rcu_read_lock(); | |
1f4b2439 PNA |
675 | ct_hook = rcu_dereference(nf_ct_hook); |
676 | BUG_ON(ct_hook == NULL); | |
677 | ct_hook->destroy(nfct); | |
de6e05c4 YK |
678 | rcu_read_unlock(); |
679 | } | |
680 | EXPORT_SYMBOL(nf_conntrack_destroy); | |
9cb01766 | 681 | |
b60a6040 THJ |
682 | bool nf_ct_get_tuple_skb(struct nf_conntrack_tuple *dst_tuple, |
683 | const struct sk_buff *skb) | |
684 | { | |
685 | struct nf_ct_hook *ct_hook; | |
686 | bool ret = false; | |
687 | ||
688 | rcu_read_lock(); | |
689 | ct_hook = rcu_dereference(nf_ct_hook); | |
690 | if (ct_hook) | |
691 | ret = ct_hook->get_tuple_skb(dst_tuple, skb); | |
692 | rcu_read_unlock(); | |
693 | return ret; | |
694 | } | |
695 | EXPORT_SYMBOL(nf_ct_get_tuple_skb); | |
696 | ||
62da9865 DB |
697 | /* Built-in default zone used e.g. by modules. */ |
698 | const struct nf_conntrack_zone nf_ct_zone_dflt = { | |
699 | .id = NF_CT_DEFAULT_ZONE_ID, | |
700 | .dir = NF_CT_DEFAULT_ZONE_DIR, | |
701 | }; | |
702 | EXPORT_SYMBOL_GPL(nf_ct_zone_dflt); | |
de6e05c4 | 703 | #endif /* CONFIG_NF_CONNTRACK */ |
f6ebe77f | 704 | |
25fd386e FW |
705 | static void __net_init |
706 | __netfilter_net_init(struct nf_hook_entries __rcu **e, int max) | |
f3c1a44a | 707 | { |
b0f38338 | 708 | int h; |
085db2c0 | 709 | |
ef57170b | 710 | for (h = 0; h < max; h++) |
b0f38338 FW |
711 | RCU_INIT_POINTER(e[h], NULL); |
712 | } | |
713 | ||
714 | static int __net_init netfilter_net_init(struct net *net) | |
715 | { | |
ef57170b FW |
716 | __netfilter_net_init(net->nf.hooks_ipv4, ARRAY_SIZE(net->nf.hooks_ipv4)); |
717 | __netfilter_net_init(net->nf.hooks_ipv6, ARRAY_SIZE(net->nf.hooks_ipv6)); | |
2a95183a | 718 | #ifdef CONFIG_NETFILTER_FAMILY_ARP |
ef57170b | 719 | __netfilter_net_init(net->nf.hooks_arp, ARRAY_SIZE(net->nf.hooks_arp)); |
2a95183a FW |
720 | #endif |
721 | #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE | |
ef57170b | 722 | __netfilter_net_init(net->nf.hooks_bridge, ARRAY_SIZE(net->nf.hooks_bridge)); |
2a95183a | 723 | #endif |
bb4badf3 | 724 | #if IS_ENABLED(CONFIG_DECNET) |
ef57170b | 725 | __netfilter_net_init(net->nf.hooks_decnet, ARRAY_SIZE(net->nf.hooks_decnet)); |
bb4badf3 | 726 | #endif |
085db2c0 | 727 | |
f3c1a44a G |
728 | #ifdef CONFIG_PROC_FS |
729 | net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter", | |
730 | net->proc_net); | |
12202fa7 PNA |
731 | if (!net->nf.proc_netfilter) { |
732 | if (!net_eq(net, &init_net)) | |
733 | pr_err("cannot create netfilter proc entry"); | |
734 | ||
f3c1a44a G |
735 | return -ENOMEM; |
736 | } | |
737 | #endif | |
085db2c0 | 738 | |
073dd5ad | 739 | return 0; |
f3c1a44a G |
740 | } |
741 | ||
742 | static void __net_exit netfilter_net_exit(struct net *net) | |
743 | { | |
744 | remove_proc_entry("netfilter", net->proc_net); | |
745 | } | |
746 | ||
747 | static struct pernet_operations netfilter_net_ops = { | |
748 | .init = netfilter_net_init, | |
749 | .exit = netfilter_net_exit, | |
750 | }; | |
751 | ||
6d11cfdb | 752 | int __init netfilter_init(void) |
f6ebe77f | 753 | { |
085db2c0 | 754 | int ret; |
f6ebe77f | 755 | |
6d11cfdb PNA |
756 | ret = register_pernet_subsys(&netfilter_net_ops); |
757 | if (ret < 0) | |
758 | goto err; | |
759 | ||
760 | ret = netfilter_log_init(); | |
761 | if (ret < 0) | |
762 | goto err_pernet; | |
f6ebe77f | 763 | |
6d11cfdb PNA |
764 | return 0; |
765 | err_pernet: | |
766 | unregister_pernet_subsys(&netfilter_net_ops); | |
767 | err: | |
768 | return ret; | |
f6ebe77f | 769 | } |