[NETFILTER]: nf_queue: fix rerouting after packet mangling
authorPatrick McHardy <kaber@trash.net>
Mon, 27 Feb 2006 21:03:24 +0000 (13:03 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 27 Feb 2006 21:03:24 +0000 (13:03 -0800)
Packets should be rerouted when they come back from userspace, not before.
Also move the queue_rerouters to RCU to avoid taking the queue_handler_lock
for each reinjected packet.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/netfilter/nf_queue.c

index 1fc7152fba8fc89113db4913dfa2134a09871a6d..c61f7237237f98cae58e66101aca9fedad61acbc 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/skbuff.h>
 #include <linux/netfilter.h>
 #include <linux/seq_file.h>
+#include <linux/rcupdate.h>
 #include <net/protocol.h>
 
 #include "nf_internals.h"
@@ -64,7 +65,7 @@ int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer)
                return -EINVAL;
 
        write_lock_bh(&queue_handler_lock);
-       queue_rerouter[pf] = rer;
+       rcu_assign_pointer(queue_rerouter[pf], rer);
        write_unlock_bh(&queue_handler_lock);
 
        return 0;
@@ -77,8 +78,9 @@ int nf_unregister_queue_rerouter(int pf)
                return -EINVAL;
 
        write_lock_bh(&queue_handler_lock);
-       queue_rerouter[pf] = NULL;
+       rcu_assign_pointer(queue_rerouter[pf], NULL);
        write_unlock_bh(&queue_handler_lock);
+       synchronize_rcu();
        return 0;
 }
 EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter);
@@ -114,6 +116,7 @@ int nf_queue(struct sk_buff **skb,
        struct net_device *physindev = NULL;
        struct net_device *physoutdev = NULL;
 #endif
+       struct nf_queue_rerouter *rerouter;
 
        /* QUEUE == DROP if noone is waiting, to be safe. */
        read_lock(&queue_handler_lock);
@@ -155,15 +158,13 @@ int nf_queue(struct sk_buff **skb,
                if (physoutdev) dev_hold(physoutdev);
        }
 #endif
-       if (queue_rerouter[pf])
-               queue_rerouter[pf]->save(*skb, info);
+       rerouter = rcu_dereference(queue_rerouter[pf]);
+       if (rerouter)
+               rerouter->save(*skb, info);
 
        status = queue_handler[pf]->outfn(*skb, info, queuenum,
                                          queue_handler[pf]->data);
 
-       if (status >= 0 && queue_rerouter[pf])
-               status = queue_rerouter[pf]->reroute(skb, info);
-
        read_unlock(&queue_handler_lock);
 
        if (status < 0) {
@@ -189,6 +190,7 @@ void nf_reinject(struct sk_buff *skb, struct nf_info *info,
 {
        struct list_head *elem = &info->elem->list;
        struct list_head *i;
+       struct nf_queue_rerouter *rerouter;
 
        rcu_read_lock();
 
@@ -225,6 +227,12 @@ void nf_reinject(struct sk_buff *skb, struct nf_info *info,
                verdict = NF_ACCEPT;
        }
 
+       if (verdict == NF_ACCEPT) {
+               rerouter = rcu_dereference(queue_rerouter[info->pf]);
+               if (rerouter && rerouter->reroute(&skb, info) < 0)
+                       verdict = NF_DROP;
+       }
+
        if (verdict == NF_ACCEPT) {
        next_hook:
                verdict = nf_iterate(&nf_hooks[info->pf][info->hook],