Commit 2cc7d5730957c4a3f3659d17d2ba5e06d5581c1f

Authored by Harald Welte
Committed by David S. Miller
1 parent 4fdb3bb723

[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
... ... @@ -71,5 +71,8 @@
71 71 NF_IP6_PRI_LAST = INT_MAX,
72 72 };
73 73  
  74 +int ipv6_netfilter_init(void);
  75 +void ipv6_netfilter_fini(void);
  76 +
74 77 #endif /*__LINUX_IP6_NETFILTER_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  
... ... @@ -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