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; |
2a95183a | 284 | #endif |
b0f38338 | 285 | case NFPROTO_IPV4: |
62a0fe46 | 286 | if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= hooknum)) |
ef57170b | 287 | return NULL; |
62a0fe46 | 288 | return net->nf.hooks_ipv4 + hooknum; |
b0f38338 | 289 | case NFPROTO_IPV6: |
62a0fe46 | 290 | if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv6) <= hooknum)) |
ef57170b | 291 | return NULL; |
62a0fe46 | 292 | return net->nf.hooks_ipv6 + hooknum; |
bb4badf3 | 293 | #if IS_ENABLED(CONFIG_DECNET) |
b0f38338 | 294 | case NFPROTO_DECNET: |
62a0fe46 | 295 | if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_decnet) <= hooknum)) |
ef57170b | 296 | return NULL; |
62a0fe46 | 297 | return net->nf.hooks_decnet + hooknum; |
bb4badf3 | 298 | #endif |
b0f38338 FW |
299 | default: |
300 | WARN_ON_ONCE(1); | |
301 | return NULL; | |
302 | } | |
bd3769bf | 303 | |
e687ad60 | 304 | #ifdef CONFIG_NETFILTER_INGRESS |
62a0fe46 PNA |
305 | if (hooknum == NF_NETDEV_INGRESS) { |
306 | if (dev && dev_net(dev) == net) | |
307 | return &dev->nf_hooks_ingress; | |
e687ad60 | 308 | } |
7816ec56 | 309 | #endif |
960632ec | 310 | WARN_ON_ONCE(1); |
bd3769bf | 311 | return NULL; |
e3b37f11 | 312 | } |
7181ebaf | 313 | |
cb7ccd83 PNA |
314 | static int __nf_register_net_hook(struct net *net, int pf, |
315 | const struct nf_hook_ops *reg) | |
0edcf282 | 316 | { |
960632ec AC |
317 | struct nf_hook_entries *p, *new_hooks; |
318 | struct nf_hook_entries __rcu **pp; | |
085db2c0 | 319 | |
cb7ccd83 | 320 | if (pf == NFPROTO_NETDEV) { |
7816ec56 AC |
321 | #ifndef CONFIG_NETFILTER_INGRESS |
322 | if (reg->hooknum == NF_NETDEV_INGRESS) | |
323 | return -EOPNOTSUPP; | |
324 | #endif | |
325 | if (reg->hooknum != NF_NETDEV_INGRESS || | |
326 | !reg->dev || dev_net(reg->dev) != net) | |
327 | return -EINVAL; | |
328 | } | |
d4bb5caa | 329 | |
cb7ccd83 | 330 | pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev); |
bd3769bf LT |
331 | if (!pp) |
332 | return -EINVAL; | |
333 | ||
e3b37f11 | 334 | mutex_lock(&nf_hook_mutex); |
085db2c0 | 335 | |
960632ec AC |
336 | p = nf_entry_dereference(*pp); |
337 | new_hooks = nf_hook_entries_grow(p, reg); | |
338 | ||
339 | if (!IS_ERR(new_hooks)) | |
340 | rcu_assign_pointer(*pp, new_hooks); | |
e3b37f11 | 341 | |
fd706d69 | 342 | mutex_unlock(&nf_hook_mutex); |
960632ec AC |
343 | if (IS_ERR(new_hooks)) |
344 | return PTR_ERR(new_hooks); | |
345 | ||
2420b79f | 346 | hooks_validate(new_hooks); |
4c091156 | 347 | #ifdef CONFIG_NETFILTER_INGRESS |
cb7ccd83 | 348 | if (pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) |
4c091156 EB |
349 | net_inc_ingress_queue(); |
350 | #endif | |
e9666d10 | 351 | #ifdef CONFIG_JUMP_LABEL |
cb7ccd83 | 352 | static_key_slow_inc(&nf_hooks_needed[pf][reg->hooknum]); |
a2d7ec58 | 353 | #endif |
960632ec | 354 | BUG_ON(p == new_hooks); |
8c873e21 | 355 | nf_hook_entries_free(p); |
f6ebe77f HW |
356 | return 0; |
357 | } | |
f6ebe77f | 358 | |
960632ec | 359 | /* |
3d3cdc38 | 360 | * nf_remove_net_hook - remove a hook from blob |
960632ec AC |
361 | * |
362 | * @oldp: current address of hook blob | |
363 | * @unreg: hook to unregister | |
364 | * | |
365 | * This cannot fail, hook unregistration must always succeed. | |
366 | * Therefore replace the to-be-removed hook with a dummy hook. | |
367 | */ | |
06cad3ac FW |
368 | static bool nf_remove_net_hook(struct nf_hook_entries *old, |
369 | const struct nf_hook_ops *unreg) | |
f6ebe77f | 370 | { |
960632ec | 371 | struct nf_hook_ops **orig_ops; |
960632ec | 372 | unsigned int i; |
085db2c0 | 373 | |
960632ec AC |
374 | orig_ops = nf_hook_entries_get_hook_ops(old); |
375 | for (i = 0; i < old->num_hook_entries; i++) { | |
376 | if (orig_ops[i] != unreg) | |
377 | continue; | |
378 | WRITE_ONCE(old->hooks[i].hook, accept_all); | |
379 | WRITE_ONCE(orig_ops[i], &dummy_ops); | |
06cad3ac | 380 | return true; |
085db2c0 | 381 | } |
960632ec | 382 | |
06cad3ac | 383 | return false; |
933bd83e FW |
384 | } |
385 | ||
0ded1785 WY |
386 | static void __nf_unregister_net_hook(struct net *net, int pf, |
387 | const struct nf_hook_ops *reg) | |
933bd83e | 388 | { |
960632ec AC |
389 | struct nf_hook_entries __rcu **pp; |
390 | struct nf_hook_entries *p; | |
933bd83e | 391 | |
cb7ccd83 | 392 | pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev); |
960632ec AC |
393 | if (!pp) |
394 | return; | |
395 | ||
396 | mutex_lock(&nf_hook_mutex); | |
397 | ||
398 | p = nf_entry_dereference(*pp); | |
399 | if (WARN_ON_ONCE(!p)) { | |
400 | mutex_unlock(&nf_hook_mutex); | |
401 | return; | |
402 | } | |
403 | ||
06cad3ac FW |
404 | if (nf_remove_net_hook(p, reg)) { |
405 | #ifdef CONFIG_NETFILTER_INGRESS | |
406 | if (pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) | |
407 | net_dec_ingress_queue(); | |
408 | #endif | |
e9666d10 | 409 | #ifdef CONFIG_JUMP_LABEL |
06cad3ac FW |
410 | static_key_slow_dec(&nf_hooks_needed[pf][reg->hooknum]); |
411 | #endif | |
412 | } else { | |
413 | WARN_ONCE(1, "hook not found, pf %d num %d", pf, reg->hooknum); | |
414 | } | |
960632ec | 415 | |
06cad3ac | 416 | p = __nf_hook_entries_try_shrink(p, pp); |
960632ec | 417 | mutex_unlock(&nf_hook_mutex); |
933bd83e FW |
418 | if (!p) |
419 | return; | |
420 | ||
26888dfd | 421 | nf_queue_nf_hook_drop(net); |
8c873e21 | 422 | nf_hook_entries_free(p); |
085db2c0 | 423 | } |
cb7ccd83 PNA |
424 | |
425 | void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) | |
426 | { | |
427 | if (reg->pf == NFPROTO_INET) { | |
428 | __nf_unregister_net_hook(net, NFPROTO_IPV4, reg); | |
429 | __nf_unregister_net_hook(net, NFPROTO_IPV6, reg); | |
430 | } else { | |
431 | __nf_unregister_net_hook(net, reg->pf, reg); | |
432 | } | |
433 | } | |
085db2c0 EB |
434 | EXPORT_SYMBOL(nf_unregister_net_hook); |
435 | ||
06cad3ac FW |
436 | void nf_hook_entries_delete_raw(struct nf_hook_entries __rcu **pp, |
437 | const struct nf_hook_ops *reg) | |
438 | { | |
439 | struct nf_hook_entries *p; | |
440 | ||
441 | p = rcu_dereference_raw(*pp); | |
442 | if (nf_remove_net_hook(p, reg)) { | |
443 | p = __nf_hook_entries_try_shrink(p, pp); | |
444 | nf_hook_entries_free(p); | |
445 | } | |
446 | } | |
447 | EXPORT_SYMBOL_GPL(nf_hook_entries_delete_raw); | |
448 | ||
cb7ccd83 PNA |
449 | int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) |
450 | { | |
451 | int err; | |
452 | ||
453 | if (reg->pf == NFPROTO_INET) { | |
454 | err = __nf_register_net_hook(net, NFPROTO_IPV4, reg); | |
455 | if (err < 0) | |
456 | return err; | |
457 | ||
458 | err = __nf_register_net_hook(net, NFPROTO_IPV6, reg); | |
459 | if (err < 0) { | |
460 | __nf_unregister_net_hook(net, NFPROTO_IPV4, reg); | |
461 | return err; | |
462 | } | |
463 | } else { | |
464 | err = __nf_register_net_hook(net, reg->pf, reg); | |
465 | if (err < 0) | |
466 | return err; | |
467 | } | |
468 | ||
469 | return 0; | |
470 | } | |
471 | EXPORT_SYMBOL(nf_register_net_hook); | |
472 | ||
085db2c0 EB |
473 | int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg, |
474 | unsigned int n) | |
475 | { | |
476 | unsigned int i; | |
477 | int err = 0; | |
478 | ||
479 | for (i = 0; i < n; i++) { | |
480 | err = nf_register_net_hook(net, ®[i]); | |
481 | if (err) | |
482 | goto err; | |
483 | } | |
484 | return err; | |
485 | ||
486 | err: | |
487 | if (i > 0) | |
488 | nf_unregister_net_hooks(net, reg, i); | |
489 | return err; | |
490 | } | |
491 | EXPORT_SYMBOL(nf_register_net_hooks); | |
492 | ||
493 | void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, | |
933bd83e | 494 | unsigned int hookcount) |
085db2c0 | 495 | { |
4e645b47 | 496 | unsigned int i; |
933bd83e | 497 | |
4e645b47 FW |
498 | for (i = 0; i < hookcount; i++) |
499 | nf_unregister_net_hook(net, ®[i]); | |
085db2c0 EB |
500 | } |
501 | EXPORT_SYMBOL(nf_unregister_net_hooks); | |
502 | ||
f6ebe77f | 503 | /* Returns 1 if okfn() needs to be executed by the caller, |
e2361cb9 | 504 | * -EPERM for NF_DROP, 0 otherwise. Caller must hold rcu_read_lock. */ |
01886bd9 | 505 | int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, |
960632ec | 506 | const struct nf_hook_entries *e, unsigned int s) |
f6ebe77f | 507 | { |
f6ebe77f | 508 | unsigned int verdict; |
c63cbc46 | 509 | int ret; |
f6ebe77f | 510 | |
960632ec AC |
511 | for (; s < e->num_hook_entries; s++) { |
512 | verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state); | |
26dfab72 PNA |
513 | switch (verdict & NF_VERDICT_MASK) { |
514 | case NF_ACCEPT: | |
26dfab72 PNA |
515 | break; |
516 | case NF_DROP: | |
517 | kfree_skb(skb); | |
518 | ret = NF_DROP_GETERR(verdict); | |
519 | if (ret == 0) | |
520 | ret = -EPERM; | |
521 | return ret; | |
26dfab72 | 522 | case NF_QUEUE: |
0d9cb300 | 523 | ret = nf_queue(skb, state, s, verdict); |
960632ec | 524 | if (ret == 1) |
26dfab72 PNA |
525 | continue; |
526 | return ret; | |
527 | default: | |
528 | /* Implicit handling for NF_STOLEN, as well as any other | |
529 | * non conventional verdicts. | |
530 | */ | |
531 | return 0; | |
532 | } | |
960632ec | 533 | } |
26dfab72 PNA |
534 | |
535 | return 1; | |
f6ebe77f HW |
536 | } |
537 | EXPORT_SYMBOL(nf_hook_slow); | |
538 | ||
b7bd1809 PNA |
539 | /* This needs to be compiled in any case to avoid dependencies between the |
540 | * nfnetlink_queue code and nf_conntrack. | |
541 | */ | |
a4b4766c KM |
542 | struct nfnl_ct_hook __rcu *nfnl_ct_hook __read_mostly; |
543 | EXPORT_SYMBOL_GPL(nfnl_ct_hook); | |
b7bd1809 | 544 | |
1f4b2439 PNA |
545 | struct nf_ct_hook __rcu *nf_ct_hook __read_mostly; |
546 | EXPORT_SYMBOL_GPL(nf_ct_hook); | |
547 | ||
c0cd1156 | 548 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
f6ebe77f HW |
549 | /* This does not belong here, but locally generated errors need it if connection |
550 | tracking in use: without this, connection may not be in hash table, and hence | |
551 | manufactured ICMP or RST packets will not be associated with it. */ | |
312a0c16 PM |
552 | void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) |
553 | __rcu __read_mostly; | |
f6ebe77f HW |
554 | EXPORT_SYMBOL(ip_ct_attach); |
555 | ||
2c205dd3 PNA |
556 | struct nf_nat_hook __rcu *nf_nat_hook __read_mostly; |
557 | EXPORT_SYMBOL_GPL(nf_nat_hook); | |
558 | ||
312a0c16 | 559 | void nf_ct_attach(struct sk_buff *new, const struct sk_buff *skb) |
f6ebe77f | 560 | { |
312a0c16 | 561 | void (*attach)(struct sk_buff *, const struct sk_buff *); |
f6ebe77f | 562 | |
a9e419dc | 563 | if (skb->_nfct) { |
c3a47ab3 PM |
564 | rcu_read_lock(); |
565 | attach = rcu_dereference(ip_ct_attach); | |
566 | if (attach) | |
567 | attach(new, skb); | |
568 | rcu_read_unlock(); | |
f6ebe77f HW |
569 | } |
570 | } | |
571 | EXPORT_SYMBOL(nf_ct_attach); | |
de6e05c4 | 572 | |
de6e05c4 YK |
573 | void nf_conntrack_destroy(struct nf_conntrack *nfct) |
574 | { | |
1f4b2439 | 575 | struct nf_ct_hook *ct_hook; |
de6e05c4 YK |
576 | |
577 | rcu_read_lock(); | |
1f4b2439 PNA |
578 | ct_hook = rcu_dereference(nf_ct_hook); |
579 | BUG_ON(ct_hook == NULL); | |
580 | ct_hook->destroy(nfct); | |
de6e05c4 YK |
581 | rcu_read_unlock(); |
582 | } | |
583 | EXPORT_SYMBOL(nf_conntrack_destroy); | |
9cb01766 | 584 | |
b60a6040 THJ |
585 | bool nf_ct_get_tuple_skb(struct nf_conntrack_tuple *dst_tuple, |
586 | const struct sk_buff *skb) | |
587 | { | |
588 | struct nf_ct_hook *ct_hook; | |
589 | bool ret = false; | |
590 | ||
591 | rcu_read_lock(); | |
592 | ct_hook = rcu_dereference(nf_ct_hook); | |
593 | if (ct_hook) | |
594 | ret = ct_hook->get_tuple_skb(dst_tuple, skb); | |
595 | rcu_read_unlock(); | |
596 | return ret; | |
597 | } | |
598 | EXPORT_SYMBOL(nf_ct_get_tuple_skb); | |
599 | ||
62da9865 DB |
600 | /* Built-in default zone used e.g. by modules. */ |
601 | const struct nf_conntrack_zone nf_ct_zone_dflt = { | |
602 | .id = NF_CT_DEFAULT_ZONE_ID, | |
603 | .dir = NF_CT_DEFAULT_ZONE_DIR, | |
604 | }; | |
605 | EXPORT_SYMBOL_GPL(nf_ct_zone_dflt); | |
de6e05c4 | 606 | #endif /* CONFIG_NF_CONNTRACK */ |
f6ebe77f | 607 | |
25fd386e FW |
608 | static void __net_init |
609 | __netfilter_net_init(struct nf_hook_entries __rcu **e, int max) | |
f3c1a44a | 610 | { |
b0f38338 | 611 | int h; |
085db2c0 | 612 | |
ef57170b | 613 | for (h = 0; h < max; h++) |
b0f38338 FW |
614 | RCU_INIT_POINTER(e[h], NULL); |
615 | } | |
616 | ||
617 | static int __net_init netfilter_net_init(struct net *net) | |
618 | { | |
ef57170b FW |
619 | __netfilter_net_init(net->nf.hooks_ipv4, ARRAY_SIZE(net->nf.hooks_ipv4)); |
620 | __netfilter_net_init(net->nf.hooks_ipv6, ARRAY_SIZE(net->nf.hooks_ipv6)); | |
2a95183a | 621 | #ifdef CONFIG_NETFILTER_FAMILY_ARP |
ef57170b | 622 | __netfilter_net_init(net->nf.hooks_arp, ARRAY_SIZE(net->nf.hooks_arp)); |
2a95183a FW |
623 | #endif |
624 | #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE | |
ef57170b | 625 | __netfilter_net_init(net->nf.hooks_bridge, ARRAY_SIZE(net->nf.hooks_bridge)); |
2a95183a | 626 | #endif |
bb4badf3 | 627 | #if IS_ENABLED(CONFIG_DECNET) |
ef57170b | 628 | __netfilter_net_init(net->nf.hooks_decnet, ARRAY_SIZE(net->nf.hooks_decnet)); |
bb4badf3 | 629 | #endif |
085db2c0 | 630 | |
f3c1a44a G |
631 | #ifdef CONFIG_PROC_FS |
632 | net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter", | |
633 | net->proc_net); | |
12202fa7 PNA |
634 | if (!net->nf.proc_netfilter) { |
635 | if (!net_eq(net, &init_net)) | |
636 | pr_err("cannot create netfilter proc entry"); | |
637 | ||
f3c1a44a G |
638 | return -ENOMEM; |
639 | } | |
640 | #endif | |
085db2c0 | 641 | |
073dd5ad | 642 | return 0; |
f3c1a44a G |
643 | } |
644 | ||
645 | static void __net_exit netfilter_net_exit(struct net *net) | |
646 | { | |
647 | remove_proc_entry("netfilter", net->proc_net); | |
648 | } | |
649 | ||
650 | static struct pernet_operations netfilter_net_ops = { | |
651 | .init = netfilter_net_init, | |
652 | .exit = netfilter_net_exit, | |
653 | }; | |
654 | ||
6d11cfdb | 655 | int __init netfilter_init(void) |
f6ebe77f | 656 | { |
085db2c0 | 657 | int ret; |
f6ebe77f | 658 | |
6d11cfdb PNA |
659 | ret = register_pernet_subsys(&netfilter_net_ops); |
660 | if (ret < 0) | |
661 | goto err; | |
662 | ||
663 | ret = netfilter_log_init(); | |
664 | if (ret < 0) | |
665 | goto err_pernet; | |
f6ebe77f | 666 | |
6d11cfdb PNA |
667 | return 0; |
668 | err_pernet: | |
669 | unregister_pernet_subsys(&netfilter_net_ops); | |
670 | err: | |
671 | return ret; | |
f6ebe77f | 672 | } |