aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netfilter.h11
-rw-r--r--include/linux/netfilter_ipv6.h3
-rw-r--r--net/core/netfilter.c66
-rw-r--r--net/ipv4/netfilter.c64
-rw-r--r--net/ipv4/netfilter/ip_queue.c27
-rw-r--r--net/ipv6/af_inet6.c7
-rw-r--r--net/ipv6/netfilter.c62
-rw-r--r--net/ipv6/netfilter/ip6_queue.c24
8 files changed, 199 insertions, 65 deletions
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 54b97a1baba5..d163e20ca8d9 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -198,6 +198,17 @@ extern void nf_invalidate_cache(int pf);
198 Returns true or false. */ 198 Returns true or false. */
199extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len); 199extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len);
200 200
201struct 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
209extern int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer);
210extern int nf_unregister_queue_rerouter(int pf);
211
201#else /* !CONFIG_NETFILTER */ 212#else /* !CONFIG_NETFILTER */
202#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) 213#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
203static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} 214static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h
index 20c069a5e4ac..5d204ee7a312 100644
--- a/include/linux/netfilter_ipv6.h
+++ b/include/linux/netfilter_ipv6.h
@@ -71,4 +71,7 @@ enum nf_ip6_hook_priorities {
71 NF_IP6_PRI_LAST = INT_MAX, 71 NF_IP6_PRI_LAST = INT_MAX,
72}; 72};
73 73
74int ipv6_netfilter_init(void);
75void ipv6_netfilter_fini(void);
76
74#endif /*__LINUX_IP6_NETFILTER_H*/ 77#endif /*__LINUX_IP6_NETFILTER_H*/
diff --git a/net/core/netfilter.c b/net/core/netfilter.c
index 9849357f6129..1ed4f3110421 100644
--- a/net/core/netfilter.c
+++ b/net/core/netfilter.c
@@ -53,6 +53,9 @@ static struct nf_queue_handler_t {
53 nf_queue_outfn_t outfn; 53 nf_queue_outfn_t outfn;
54 void *data; 54 void *data;
55} queue_handler[NPROTO]; 55} queue_handler[NPROTO];
56
57static struct nf_queue_rerouter *queue_rerouter;
58
56static DEFINE_RWLOCK(queue_handler_lock); 59static DEFINE_RWLOCK(queue_handler_lock);
57 60
58int nf_register_hook(struct nf_hook_ops *reg) 61int nf_register_hook(struct nf_hook_ops *reg)
@@ -260,11 +263,34 @@ int nf_unregister_queue_handler(int pf)
260 return 0; 263 return 0;
261} 264}
262 265
266int 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
278int 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 * Any packet that leaves via this function must come back 290 * Any packet that leaves via this function must come back
265 * through nf_reinject(). 291 * through nf_reinject().
266 */ 292 */
267static int nf_queue(struct sk_buff *skb, 293static int nf_queue(struct sk_buff **skb,
268 struct list_head *elem, 294 struct list_head *elem,
269 int pf, unsigned int hook, 295 int pf, unsigned int hook,
270 struct net_device *indev, 296 struct net_device *indev,
@@ -282,17 +308,17 @@ static int nf_queue(struct sk_buff *skb,
282 read_lock(&queue_handler_lock); 308 read_lock(&queue_handler_lock);
283 if (!queue_handler[pf].outfn) { 309 if (!queue_handler[pf].outfn) {
284 read_unlock(&queue_handler_lock); 310 read_unlock(&queue_handler_lock);
285 kfree_skb(skb); 311 kfree_skb(*skb);
286 return 1; 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 if (!info) { 316 if (!info) {
291 if (net_ratelimit()) 317 if (net_ratelimit())
292 printk(KERN_ERR "OOM queueing packet %p\n", 318 printk(KERN_ERR "OOM queueing packet %p\n",
293 skb); 319 *skb);
294 read_unlock(&queue_handler_lock); 320 read_unlock(&queue_handler_lock);
295 kfree_skb(skb); 321 kfree_skb(*skb);
296 return 1; 322 return 1;
297 } 323 }
298 324
@@ -311,15 +337,21 @@ static int nf_queue(struct sk_buff *skb,
311 if (outdev) dev_hold(outdev); 337 if (outdev) dev_hold(outdev);
312 338
313#ifdef CONFIG_BRIDGE_NETFILTER 339#ifdef CONFIG_BRIDGE_NETFILTER
314 if (skb->nf_bridge) { 340 if ((*skb)->nf_bridge) {
315 physindev = skb->nf_bridge->physindev; 341 physindev = (*skb)->nf_bridge->physindev;
316 if (physindev) dev_hold(physindev); 342 if (physindev) dev_hold(physindev);
317 physoutdev = skb->nf_bridge->physoutdev; 343 physoutdev = (*skb)->nf_bridge->physoutdev;
318 if (physoutdev) dev_hold(physoutdev); 344 if (physoutdev) dev_hold(physoutdev);
319 } 345 }
320#endif 346#endif
347 if (queue_rerouter[pf].save)
348 queue_rerouter[pf].save(*skb, info);
349
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);
321 354
322 status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
323 read_unlock(&queue_handler_lock); 355 read_unlock(&queue_handler_lock);
324 356
325 if (status < 0) { 357 if (status < 0) {
@@ -332,9 +364,11 @@ static int nf_queue(struct sk_buff *skb,
332#endif 364#endif
333 module_put(info->elem->owner); 365 module_put(info->elem->owner);
334 kfree(info); 366 kfree(info);
335 kfree_skb(skb); 367 kfree_skb(*skb);
368
336 return 1; 369 return 1;
337 } 370 }
371
338 return 1; 372 return 1;
339} 373}
340 374
@@ -365,7 +399,7 @@ next_hook:
365 ret = -EPERM; 399 ret = -EPERM;
366 } else if (verdict == NF_QUEUE) { 400 } else if (verdict == NF_QUEUE) {
367 NFDEBUG("nf_hook: Verdict = QUEUE.\n"); 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 goto next_hook; 403 goto next_hook;
370 } 404 }
371unlock: 405unlock:
@@ -428,7 +462,7 @@ void nf_reinject(struct sk_buff *skb, struct nf_info *info,
428 break; 462 break;
429 463
430 case NF_QUEUE: 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 info->indev, info->outdev, info->okfn)) 466 info->indev, info->outdev, info->okfn))
433 goto next_hook; 467 goto next_hook;
434 break; 468 break;
@@ -555,6 +589,12 @@ void __init netfilter_init(void)
555{ 589{
556 int i, h; 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 for (i = 0; i < NPROTO; i++) { 598 for (i = 0; i < NPROTO; i++) {
559 for (h = 0; h < NF_MAX_HOOKS; h++) 599 for (h = 0; h < NF_MAX_HOOKS; h++)
560 INIT_LIST_HEAD(&nf_hooks[i][h]); 600 INIT_LIST_HEAD(&nf_hooks[i][h]);
@@ -573,4 +613,6 @@ EXPORT_SYMBOL(nf_reinject);
573EXPORT_SYMBOL(nf_setsockopt); 613EXPORT_SYMBOL(nf_setsockopt);
574EXPORT_SYMBOL(nf_unregister_hook); 614EXPORT_SYMBOL(nf_unregister_hook);
575EXPORT_SYMBOL(nf_unregister_queue_handler); 615EXPORT_SYMBOL(nf_unregister_queue_handler);
616EXPORT_SYMBOL_GPL(nf_register_queue_rerouter);
617EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter);
576EXPORT_SYMBOL(nf_unregister_sockopt); 618EXPORT_SYMBOL(nf_unregister_sockopt);
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
index 6594d1c9697e..ae0779d82c5d 100644
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -1,10 +1,11 @@
1#include <linux/config.h> 1/* IPv4 specific functions of netfilter core */
2 2
3#include <linux/config.h>
3#ifdef CONFIG_NETFILTER 4#ifdef CONFIG_NETFILTER
4 5
5/* IPv4 specific functions of netfilter core */
6#include <linux/kernel.h> 6#include <linux/kernel.h>
7#include <linux/netfilter.h> 7#include <linux/netfilter.h>
8#include <linux/netfilter_ipv4.h>
8 9
9#include <linux/tcp.h> 10#include <linux/tcp.h>
10#include <linux/udp.h> 11#include <linux/udp.h>
@@ -76,4 +77,63 @@ int ip_route_me_harder(struct sk_buff **pskb)
76 return 0; 77 return 0;
77} 78}
78EXPORT_SYMBOL(ip_route_me_harder); 79EXPORT_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
86struct ip_rt_info {
87 u_int32_t daddr;
88 u_int32_t saddr;
89 u_int8_t tos;
90};
91
92static 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
105static 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
120static struct nf_queue_rerouter ip_reroute = {
121 .rer_size = sizeof(struct ip_rt_info),
122 .save = queue_save,
123 .reroute = queue_reroute,
124};
125
126static int init(void)
127{
128 return nf_register_queue_rerouter(PF_INET, &ip_reroute);
129}
130
131static void fini(void)
132{
133 nf_unregister_queue_rerouter(PF_INET);
134}
135
136module_init(init);
137module_exit(fini);
138
79#endif /* CONFIG_NETFILTER */ 139#endif /* CONFIG_NETFILTER */
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
index b237f7fcad92..78892980f42c 100644
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -43,17 +43,10 @@
43#define NET_IPQ_QMAX 2088 43#define NET_IPQ_QMAX 2088
44#define NET_IPQ_QMAX_NAME "ip_queue_maxlen" 44#define NET_IPQ_QMAX_NAME "ip_queue_maxlen"
45 45
46struct ipq_rt_info {
47 __u8 tos;
48 __u32 daddr;
49 __u32 saddr;
50};
51
52struct ipq_queue_entry { 46struct ipq_queue_entry {
53 struct list_head list; 47 struct list_head list;
54 struct nf_info *info; 48 struct nf_info *info;
55 struct sk_buff *skb; 49 struct sk_buff *skb;
56 struct ipq_rt_info rt_info;
57}; 50};
58 51
59typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); 52typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
@@ -305,14 +298,6 @@ ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, void *data)
305 entry->info = info; 298 entry->info = info;
306 entry->skb = skb; 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 nskb = ipq_build_packet_message(entry, &status); 301 nskb = ipq_build_packet_message(entry, &status);
317 if (nskb == NULL) 302 if (nskb == NULL)
318 goto err_out_free; 303 goto err_out_free;
@@ -393,18 +378,6 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
393 memcpy(e->skb->data, v->payload, v->data_len); 378 memcpy(e->skb->data, v->payload, v->data_len);
394 e->skb->ip_summed = CHECKSUM_NONE; 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 return 0; 381 return 0;
409} 382}
410 383
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 28d9bcab0970..574047353628 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -44,6 +44,7 @@
44#include <linux/netdevice.h> 44#include <linux/netdevice.h>
45#include <linux/icmpv6.h> 45#include <linux/icmpv6.h>
46#include <linux/smp_lock.h> 46#include <linux/smp_lock.h>
47#include <linux/netfilter_ipv6.h>
47 48
48#include <net/ip.h> 49#include <net/ip.h>
49#include <net/ipv6.h> 50#include <net/ipv6.h>
@@ -757,6 +758,9 @@ static int __init inet6_init(void)
757 err = igmp6_init(&inet6_family_ops); 758 err = igmp6_init(&inet6_family_ops);
758 if (err) 759 if (err)
759 goto igmp_fail; 760 goto igmp_fail;
761 err = ipv6_netfilter_init();
762 if (err)
763 goto netfilter_fail;
760 /* Create /proc/foo6 entries. */ 764 /* Create /proc/foo6 entries. */
761#ifdef CONFIG_PROC_FS 765#ifdef CONFIG_PROC_FS
762 err = -ENOMEM; 766 err = -ENOMEM;
@@ -813,6 +817,8 @@ proc_tcp6_fail:
813 raw6_proc_exit(); 817 raw6_proc_exit();
814proc_raw6_fail: 818proc_raw6_fail:
815#endif 819#endif
820 ipv6_netfilter_fini();
821netfilter_fail:
816 igmp6_cleanup(); 822 igmp6_cleanup();
817igmp_fail: 823igmp_fail:
818 ndisc_cleanup(); 824 ndisc_cleanup();
@@ -852,6 +858,7 @@ static void __exit inet6_exit(void)
852 ip6_route_cleanup(); 858 ip6_route_cleanup();
853 ipv6_packet_cleanup(); 859 ipv6_packet_cleanup();
854 igmp6_cleanup(); 860 igmp6_cleanup();
861 ipv6_netfilter_fini();
855 ndisc_cleanup(); 862 ndisc_cleanup();
856 icmpv6_cleanup(); 863 icmpv6_cleanup();
857#ifdef CONFIG_SYSCTL 864#ifdef CONFIG_SYSCTL
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index 5656d0959aba..c8daef97cf56 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -5,6 +5,8 @@
5 5
6#include <linux/kernel.h> 6#include <linux/kernel.h>
7#include <linux/ipv6.h> 7#include <linux/ipv6.h>
8#include <linux/netfilter.h>
9#include <linux/netfilter_ipv6.h>
8#include <net/dst.h> 10#include <net/dst.h>
9#include <net/ipv6.h> 11#include <net/ipv6.h>
10#include <net/ip6_route.h> 12#include <net/ip6_route.h>
@@ -40,4 +42,64 @@ int ip6_route_me_harder(struct sk_buff *skb)
40} 42}
41EXPORT_SYMBOL(ip6_route_me_harder); 43EXPORT_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
50struct ip6_rt_info {
51 struct in6_addr daddr;
52 struct in6_addr saddr;
53};
54
55static 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
67static 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
80static struct nf_queue_rerouter ip6_reroute = {
81 .rer_size = sizeof(struct ip6_rt_info),
82 .save = &save,
83 .reroute = &reroute,
84};
85
86int __init ipv6_netfilter_init(void)
87{
88 return nf_register_queue_rerouter(PF_INET6, &ip6_reroute);
89}
90
91void ipv6_netfilter_fini(void)
92{
93 nf_unregister_queue_rerouter(PF_INET6);
94}
95
96#else /* CONFIG_NETFILTER */
97int __init ipv6_netfilter_init(void)
98{
99 return 0;
100}
101
102void ipv6_netfilter_fini(void)
103{
104}
43#endif /* CONFIG_NETFILTER */ 105#endif /* CONFIG_NETFILTER */
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
index 1c3d247a22cc..c45d8f8815de 100644
--- a/net/ipv6/netfilter/ip6_queue.c
+++ b/net/ipv6/netfilter/ip6_queue.c
@@ -47,16 +47,10 @@
47#define NET_IPQ_QMAX 2088 47#define NET_IPQ_QMAX 2088
48#define NET_IPQ_QMAX_NAME "ip6_queue_maxlen" 48#define NET_IPQ_QMAX_NAME "ip6_queue_maxlen"
49 49
50struct ipq_rt_info {
51 struct in6_addr daddr;
52 struct in6_addr saddr;
53};
54
55struct ipq_queue_entry { 50struct ipq_queue_entry {
56 struct list_head list; 51 struct list_head list;
57 struct nf_info *info; 52 struct nf_info *info;
58 struct sk_buff *skb; 53 struct sk_buff *skb;
59 struct ipq_rt_info rt_info;
60}; 54};
61 55
62typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long); 56typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
@@ -302,13 +296,6 @@ ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info, void *data)
302 entry->info = info; 296 entry->info = info;
303 entry->skb = skb; 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 nskb = ipq_build_packet_message(entry, &status); 299 nskb = ipq_build_packet_message(entry, &status);
313 if (nskb == NULL) 300 if (nskb == NULL)
314 goto err_out_free; 301 goto err_out_free;
@@ -389,17 +376,6 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
389 memcpy(e->skb->data, v->payload, v->data_len); 376 memcpy(e->skb->data, v->payload, v->data_len);
390 e->skb->ip_summed = CHECKSUM_NONE; 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 return 0; 379 return 0;
404} 380}
405 381