Commit 2cc7d5730957c4a3f3659d17d2ba5e06d5581c1f
Committed by
David S. Miller
1 parent
4fdb3bb723
Exists in
master
and in
7 other branches
[NETFILTER]: Move reroute-after-queue code up to the nf_queue layer.
The rerouting functionality is required by the core, therefore it has to be implemented by the core and not in individual queue handlers. Signed-off-by: Harald Welte <laforge@netfilter.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 8 changed files with 199 additions and 65 deletions Side-by-side Diff
include/linux/netfilter.h
... | ... | @@ -198,6 +198,17 @@ |
198 | 198 | Returns true or false. */ |
199 | 199 | extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len); |
200 | 200 | |
201 | +struct nf_queue_rerouter { | |
202 | + void (*save)(const struct sk_buff *skb, struct nf_info *info); | |
203 | + int (*reroute)(struct sk_buff **skb, const struct nf_info *info); | |
204 | + int rer_size; | |
205 | +}; | |
206 | + | |
207 | +#define nf_info_reroute(x) ((void *)x + sizeof(struct nf_info)) | |
208 | + | |
209 | +extern int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer); | |
210 | +extern int nf_unregister_queue_rerouter(int pf); | |
211 | + | |
201 | 212 | #else /* !CONFIG_NETFILTER */ |
202 | 213 | #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) |
203 | 214 | static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} |
include/linux/netfilter_ipv6.h
net/core/netfilter.c
... | ... | @@ -53,6 +53,9 @@ |
53 | 53 | nf_queue_outfn_t outfn; |
54 | 54 | void *data; |
55 | 55 | } queue_handler[NPROTO]; |
56 | + | |
57 | +static struct nf_queue_rerouter *queue_rerouter; | |
58 | + | |
56 | 59 | static DEFINE_RWLOCK(queue_handler_lock); |
57 | 60 | |
58 | 61 | int nf_register_hook(struct nf_hook_ops *reg) |
59 | 62 | |
... | ... | @@ -260,11 +263,34 @@ |
260 | 263 | return 0; |
261 | 264 | } |
262 | 265 | |
266 | +int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer) | |
267 | +{ | |
268 | + if (pf >= NPROTO) | |
269 | + return -EINVAL; | |
270 | + | |
271 | + write_lock_bh(&queue_handler_lock); | |
272 | + memcpy(&queue_rerouter[pf], rer, sizeof(queue_rerouter[pf])); | |
273 | + write_unlock_bh(&queue_handler_lock); | |
274 | + | |
275 | + return 0; | |
276 | +} | |
277 | + | |
278 | +int nf_unregister_queue_rerouter(int pf) | |
279 | +{ | |
280 | + if (pf >= NPROTO) | |
281 | + return -EINVAL; | |
282 | + | |
283 | + write_lock_bh(&queue_handler_lock); | |
284 | + memset(&queue_rerouter[pf], 0, sizeof(queue_rerouter[pf])); | |
285 | + write_unlock_bh(&queue_handler_lock); | |
286 | + return 0; | |
287 | +} | |
288 | + | |
263 | 289 | /* |
264 | 290 | * Any packet that leaves via this function must come back |
265 | 291 | * through nf_reinject(). |
266 | 292 | */ |
267 | -static int nf_queue(struct sk_buff *skb, | |
293 | +static int nf_queue(struct sk_buff **skb, | |
268 | 294 | struct list_head *elem, |
269 | 295 | int pf, unsigned int hook, |
270 | 296 | struct net_device *indev, |
271 | 297 | |
272 | 298 | |
273 | 299 | |
... | ... | @@ -282,17 +308,17 @@ |
282 | 308 | read_lock(&queue_handler_lock); |
283 | 309 | if (!queue_handler[pf].outfn) { |
284 | 310 | read_unlock(&queue_handler_lock); |
285 | - kfree_skb(skb); | |
311 | + kfree_skb(*skb); | |
286 | 312 | return 1; |
287 | 313 | } |
288 | 314 | |
289 | - info = kmalloc(sizeof(*info), GFP_ATOMIC); | |
315 | + info = kmalloc(sizeof(*info)+queue_rerouter[pf].rer_size, GFP_ATOMIC); | |
290 | 316 | if (!info) { |
291 | 317 | if (net_ratelimit()) |
292 | 318 | printk(KERN_ERR "OOM queueing packet %p\n", |
293 | - skb); | |
319 | + *skb); | |
294 | 320 | read_unlock(&queue_handler_lock); |
295 | - kfree_skb(skb); | |
321 | + kfree_skb(*skb); | |
296 | 322 | return 1; |
297 | 323 | } |
298 | 324 | |
299 | 325 | |
300 | 326 | |
301 | 327 | |
... | ... | @@ -311,15 +337,21 @@ |
311 | 337 | if (outdev) dev_hold(outdev); |
312 | 338 | |
313 | 339 | #ifdef CONFIG_BRIDGE_NETFILTER |
314 | - if (skb->nf_bridge) { | |
315 | - physindev = skb->nf_bridge->physindev; | |
340 | + if ((*skb)->nf_bridge) { | |
341 | + physindev = (*skb)->nf_bridge->physindev; | |
316 | 342 | if (physindev) dev_hold(physindev); |
317 | - physoutdev = skb->nf_bridge->physoutdev; | |
343 | + physoutdev = (*skb)->nf_bridge->physoutdev; | |
318 | 344 | if (physoutdev) dev_hold(physoutdev); |
319 | 345 | } |
320 | 346 | #endif |
347 | + if (queue_rerouter[pf].save) | |
348 | + queue_rerouter[pf].save(*skb, info); | |
321 | 349 | |
322 | - status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data); | |
350 | + status = queue_handler[pf].outfn(*skb, info, queue_handler[pf].data); | |
351 | + | |
352 | + if (status >= 0 && queue_rerouter[pf].reroute) | |
353 | + status = queue_rerouter[pf].reroute(skb, info); | |
354 | + | |
323 | 355 | read_unlock(&queue_handler_lock); |
324 | 356 | |
325 | 357 | if (status < 0) { |
326 | 358 | |
... | ... | @@ -332,9 +364,11 @@ |
332 | 364 | #endif |
333 | 365 | module_put(info->elem->owner); |
334 | 366 | kfree(info); |
335 | - kfree_skb(skb); | |
367 | + kfree_skb(*skb); | |
368 | + | |
336 | 369 | return 1; |
337 | 370 | } |
371 | + | |
338 | 372 | return 1; |
339 | 373 | } |
340 | 374 | |
... | ... | @@ -365,7 +399,7 @@ |
365 | 399 | ret = -EPERM; |
366 | 400 | } else if (verdict == NF_QUEUE) { |
367 | 401 | NFDEBUG("nf_hook: Verdict = QUEUE.\n"); |
368 | - if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn)) | |
402 | + if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn)) | |
369 | 403 | goto next_hook; |
370 | 404 | } |
371 | 405 | unlock: |
... | ... | @@ -428,7 +462,7 @@ |
428 | 462 | break; |
429 | 463 | |
430 | 464 | case NF_QUEUE: |
431 | - if (!nf_queue(skb, elem, info->pf, info->hook, | |
465 | + if (!nf_queue(&skb, elem, info->pf, info->hook, | |
432 | 466 | info->indev, info->outdev, info->okfn)) |
433 | 467 | goto next_hook; |
434 | 468 | break; |
... | ... | @@ -555,6 +589,12 @@ |
555 | 589 | { |
556 | 590 | int i, h; |
557 | 591 | |
592 | + queue_rerouter = kmalloc(NPROTO * sizeof(struct nf_queue_rerouter), | |
593 | + GFP_KERNEL); | |
594 | + if (!queue_rerouter) | |
595 | + panic("netfilter: cannot allocate queue rerouter array\n"); | |
596 | + memset(queue_rerouter, 0, NPROTO * sizeof(struct nf_queue_rerouter)); | |
597 | + | |
558 | 598 | for (i = 0; i < NPROTO; i++) { |
559 | 599 | for (h = 0; h < NF_MAX_HOOKS; h++) |
560 | 600 | INIT_LIST_HEAD(&nf_hooks[i][h]); |
... | ... | @@ -573,5 +613,7 @@ |
573 | 613 | EXPORT_SYMBOL(nf_setsockopt); |
574 | 614 | EXPORT_SYMBOL(nf_unregister_hook); |
575 | 615 | EXPORT_SYMBOL(nf_unregister_queue_handler); |
616 | +EXPORT_SYMBOL_GPL(nf_register_queue_rerouter); | |
617 | +EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter); | |
576 | 618 | EXPORT_SYMBOL(nf_unregister_sockopt); |
net/ipv4/netfilter.c
1 | -#include <linux/config.h> | |
1 | +/* IPv4 specific functions of netfilter core */ | |
2 | 2 | |
3 | +#include <linux/config.h> | |
3 | 4 | #ifdef CONFIG_NETFILTER |
4 | 5 | |
5 | -/* IPv4 specific functions of netfilter core */ | |
6 | 6 | #include <linux/kernel.h> |
7 | 7 | #include <linux/netfilter.h> |
8 | +#include <linux/netfilter_ipv4.h> | |
8 | 9 | |
9 | 10 | #include <linux/tcp.h> |
10 | 11 | #include <linux/udp.h> |
... | ... | @@ -76,5 +77,64 @@ |
76 | 77 | return 0; |
77 | 78 | } |
78 | 79 | EXPORT_SYMBOL(ip_route_me_harder); |
80 | + | |
81 | +/* | |
82 | + * Extra routing may needed on local out, as the QUEUE target never | |
83 | + * returns control to the table. | |
84 | + */ | |
85 | + | |
86 | +struct ip_rt_info { | |
87 | + u_int32_t daddr; | |
88 | + u_int32_t saddr; | |
89 | + u_int8_t tos; | |
90 | +}; | |
91 | + | |
92 | +static void queue_save(const struct sk_buff *skb, struct nf_info *info) | |
93 | +{ | |
94 | + struct ip_rt_info *rt_info = nf_info_reroute(info); | |
95 | + | |
96 | + if (info->hook == NF_IP_LOCAL_OUT) { | |
97 | + const struct iphdr *iph = skb->nh.iph; | |
98 | + | |
99 | + rt_info->tos = iph->tos; | |
100 | + rt_info->daddr = iph->daddr; | |
101 | + rt_info->saddr = iph->saddr; | |
102 | + } | |
103 | +} | |
104 | + | |
105 | +static int queue_reroute(struct sk_buff **pskb, const struct nf_info *info) | |
106 | +{ | |
107 | + const struct ip_rt_info *rt_info = nf_info_reroute(info); | |
108 | + | |
109 | + if (info->hook == NF_IP_LOCAL_OUT) { | |
110 | + struct iphdr *iph = (*pskb)->nh.iph; | |
111 | + | |
112 | + if (!(iph->tos == rt_info->tos | |
113 | + && iph->daddr == rt_info->daddr | |
114 | + && iph->saddr == rt_info->saddr)) | |
115 | + return ip_route_me_harder(pskb); | |
116 | + } | |
117 | + return 0; | |
118 | +} | |
119 | + | |
120 | +static struct nf_queue_rerouter ip_reroute = { | |
121 | + .rer_size = sizeof(struct ip_rt_info), | |
122 | + .save = queue_save, | |
123 | + .reroute = queue_reroute, | |
124 | +}; | |
125 | + | |
126 | +static int init(void) | |
127 | +{ | |
128 | + return nf_register_queue_rerouter(PF_INET, &ip_reroute); | |
129 | +} | |
130 | + | |
131 | +static void fini(void) | |
132 | +{ | |
133 | + nf_unregister_queue_rerouter(PF_INET); | |
134 | +} | |
135 | + | |
136 | +module_init(init); | |
137 | +module_exit(fini); | |
138 | + | |
79 | 139 | #endif /* CONFIG_NETFILTER */ |
net/ipv4/netfilter/ip_queue.c
... | ... | @@ -43,17 +43,10 @@ |
43 | 43 | #define NET_IPQ_QMAX 2088 |
44 | 44 | #define NET_IPQ_QMAX_NAME "ip_queue_maxlen" |
45 | 45 | |
46 | -struct ipq_rt_info { | |
47 | - __u8 tos; | |
48 | - __u32 daddr; | |
49 | - __u32 saddr; | |
50 | -}; | |
51 | - | |
52 | 46 | struct ipq_queue_entry { |
53 | 47 | struct list_head list; |
54 | 48 | struct nf_info *info; |
55 | 49 | struct sk_buff *skb; |
56 | - struct ipq_rt_info rt_info; | |
57 | 50 | }; |
58 | 51 | |
59 | 52 | typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); |
... | ... | @@ -305,14 +298,6 @@ |
305 | 298 | entry->info = info; |
306 | 299 | entry->skb = skb; |
307 | 300 | |
308 | - if (entry->info->hook == NF_IP_LOCAL_OUT) { | |
309 | - struct iphdr *iph = skb->nh.iph; | |
310 | - | |
311 | - entry->rt_info.tos = iph->tos; | |
312 | - entry->rt_info.daddr = iph->daddr; | |
313 | - entry->rt_info.saddr = iph->saddr; | |
314 | - } | |
315 | - | |
316 | 301 | nskb = ipq_build_packet_message(entry, &status); |
317 | 302 | if (nskb == NULL) |
318 | 303 | goto err_out_free; |
... | ... | @@ -393,18 +378,6 @@ |
393 | 378 | memcpy(e->skb->data, v->payload, v->data_len); |
394 | 379 | e->skb->ip_summed = CHECKSUM_NONE; |
395 | 380 | |
396 | - /* | |
397 | - * Extra routing may needed on local out, as the QUEUE target never | |
398 | - * returns control to the table. | |
399 | - */ | |
400 | - if (e->info->hook == NF_IP_LOCAL_OUT) { | |
401 | - struct iphdr *iph = e->skb->nh.iph; | |
402 | - | |
403 | - if (!(iph->tos == e->rt_info.tos | |
404 | - && iph->daddr == e->rt_info.daddr | |
405 | - && iph->saddr == e->rt_info.saddr)) | |
406 | - return ip_route_me_harder(&e->skb); | |
407 | - } | |
408 | 381 | return 0; |
409 | 382 | } |
410 | 383 |
net/ipv6/af_inet6.c
... | ... | @@ -44,6 +44,7 @@ |
44 | 44 | #include <linux/netdevice.h> |
45 | 45 | #include <linux/icmpv6.h> |
46 | 46 | #include <linux/smp_lock.h> |
47 | +#include <linux/netfilter_ipv6.h> | |
47 | 48 | |
48 | 49 | #include <net/ip.h> |
49 | 50 | #include <net/ipv6.h> |
... | ... | @@ -757,6 +758,9 @@ |
757 | 758 | err = igmp6_init(&inet6_family_ops); |
758 | 759 | if (err) |
759 | 760 | goto igmp_fail; |
761 | + err = ipv6_netfilter_init(); | |
762 | + if (err) | |
763 | + goto netfilter_fail; | |
760 | 764 | /* Create /proc/foo6 entries. */ |
761 | 765 | #ifdef CONFIG_PROC_FS |
762 | 766 | err = -ENOMEM; |
... | ... | @@ -813,6 +817,8 @@ |
813 | 817 | raw6_proc_exit(); |
814 | 818 | proc_raw6_fail: |
815 | 819 | #endif |
820 | + ipv6_netfilter_fini(); | |
821 | +netfilter_fail: | |
816 | 822 | igmp6_cleanup(); |
817 | 823 | igmp_fail: |
818 | 824 | ndisc_cleanup(); |
... | ... | @@ -852,6 +858,7 @@ |
852 | 858 | ip6_route_cleanup(); |
853 | 859 | ipv6_packet_cleanup(); |
854 | 860 | igmp6_cleanup(); |
861 | + ipv6_netfilter_fini(); | |
855 | 862 | ndisc_cleanup(); |
856 | 863 | icmpv6_cleanup(); |
857 | 864 | #ifdef CONFIG_SYSCTL |
net/ipv6/netfilter.c
... | ... | @@ -5,6 +5,8 @@ |
5 | 5 | |
6 | 6 | #include <linux/kernel.h> |
7 | 7 | #include <linux/ipv6.h> |
8 | +#include <linux/netfilter.h> | |
9 | +#include <linux/netfilter_ipv6.h> | |
8 | 10 | #include <net/dst.h> |
9 | 11 | #include <net/ipv6.h> |
10 | 12 | #include <net/ip6_route.h> |
... | ... | @@ -40,5 +42,65 @@ |
40 | 42 | } |
41 | 43 | EXPORT_SYMBOL(ip6_route_me_harder); |
42 | 44 | |
45 | +/* | |
46 | + * Extra routing may needed on local out, as the QUEUE target never | |
47 | + * returns control to the table. | |
48 | + */ | |
49 | + | |
50 | +struct ip6_rt_info { | |
51 | + struct in6_addr daddr; | |
52 | + struct in6_addr saddr; | |
53 | +}; | |
54 | + | |
55 | +static void save(const struct sk_buff *skb, struct nf_info *info) | |
56 | +{ | |
57 | + struct ip6_rt_info *rt_info = nf_info_reroute(info); | |
58 | + | |
59 | + if (info->hook == NF_IP6_LOCAL_OUT) { | |
60 | + struct ipv6hdr *iph = skb->nh.ipv6h; | |
61 | + | |
62 | + rt_info->daddr = iph->daddr; | |
63 | + rt_info->saddr = iph->saddr; | |
64 | + } | |
65 | +} | |
66 | + | |
67 | +static int reroute(struct sk_buff **pskb, const struct nf_info *info) | |
68 | +{ | |
69 | + struct ip6_rt_info *rt_info = nf_info_reroute(info); | |
70 | + | |
71 | + if (info->hook == NF_IP6_LOCAL_OUT) { | |
72 | + struct ipv6hdr *iph = (*pskb)->nh.ipv6h; | |
73 | + if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || | |
74 | + !ipv6_addr_equal(&iph->saddr, &rt_info->saddr)) | |
75 | + return ip6_route_me_harder(*pskb); | |
76 | + } | |
77 | + return 0; | |
78 | +} | |
79 | + | |
80 | +static struct nf_queue_rerouter ip6_reroute = { | |
81 | + .rer_size = sizeof(struct ip6_rt_info), | |
82 | + .save = &save, | |
83 | + .reroute = &reroute, | |
84 | +}; | |
85 | + | |
86 | +int __init ipv6_netfilter_init(void) | |
87 | +{ | |
88 | + return nf_register_queue_rerouter(PF_INET6, &ip6_reroute); | |
89 | +} | |
90 | + | |
91 | +void ipv6_netfilter_fini(void) | |
92 | +{ | |
93 | + nf_unregister_queue_rerouter(PF_INET6); | |
94 | +} | |
95 | + | |
96 | +#else /* CONFIG_NETFILTER */ | |
97 | +int __init ipv6_netfilter_init(void) | |
98 | +{ | |
99 | + return 0; | |
100 | +} | |
101 | + | |
102 | +void ipv6_netfilter_fini(void) | |
103 | +{ | |
104 | +} | |
43 | 105 | #endif /* CONFIG_NETFILTER */ |
net/ipv6/netfilter/ip6_queue.c
... | ... | @@ -47,16 +47,10 @@ |
47 | 47 | #define NET_IPQ_QMAX 2088 |
48 | 48 | #define NET_IPQ_QMAX_NAME "ip6_queue_maxlen" |
49 | 49 | |
50 | -struct ipq_rt_info { | |
51 | - struct in6_addr daddr; | |
52 | - struct in6_addr saddr; | |
53 | -}; | |
54 | - | |
55 | 50 | struct ipq_queue_entry { |
56 | 51 | struct list_head list; |
57 | 52 | struct nf_info *info; |
58 | 53 | struct sk_buff *skb; |
59 | - struct ipq_rt_info rt_info; | |
60 | 54 | }; |
61 | 55 | |
62 | 56 | typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); |
... | ... | @@ -302,13 +296,6 @@ |
302 | 296 | entry->info = info; |
303 | 297 | entry->skb = skb; |
304 | 298 | |
305 | - if (entry->info->hook == NF_IP_LOCAL_OUT) { | |
306 | - struct ipv6hdr *iph = skb->nh.ipv6h; | |
307 | - | |
308 | - entry->rt_info.daddr = iph->daddr; | |
309 | - entry->rt_info.saddr = iph->saddr; | |
310 | - } | |
311 | - | |
312 | 299 | nskb = ipq_build_packet_message(entry, &status); |
313 | 300 | if (nskb == NULL) |
314 | 301 | goto err_out_free; |
... | ... | @@ -389,17 +376,6 @@ |
389 | 376 | memcpy(e->skb->data, v->payload, v->data_len); |
390 | 377 | e->skb->ip_summed = CHECKSUM_NONE; |
391 | 378 | |
392 | - /* | |
393 | - * Extra routing may needed on local out, as the QUEUE target never | |
394 | - * returns control to the table. | |
395 | - * Not a nice way to cmp, but works | |
396 | - */ | |
397 | - if (e->info->hook == NF_IP_LOCAL_OUT) { | |
398 | - struct ipv6hdr *iph = e->skb->nh.ipv6h; | |
399 | - if (!ipv6_addr_equal(&iph->daddr, &e->rt_info.daddr) || | |
400 | - !ipv6_addr_equal(&iph->saddr, &e->rt_info.saddr)) | |
401 | - return ip6_route_me_harder(e->skb); | |
402 | - } | |
403 | 379 | return 0; |
404 | 380 | } |
405 | 381 |