Returns true or false. */
 extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len);
 
+struct nf_queue_rerouter {
+       void (*save)(const struct sk_buff *skb, struct nf_info *info);
+       int (*reroute)(struct sk_buff **skb, const struct nf_info *info);
+       int rer_size;
+};
+
+#define nf_info_reroute(x) ((void *)x + sizeof(struct nf_info))
+
+extern int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer);
+extern int nf_unregister_queue_rerouter(int pf);
+
 #else /* !CONFIG_NETFILTER */
 #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
 static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {}
 
        NF_IP6_PRI_LAST = INT_MAX,
 };
 
+int ipv6_netfilter_init(void);
+void ipv6_netfilter_fini(void);
+
 #endif /*__LINUX_IP6_NETFILTER_H*/
 
        nf_queue_outfn_t outfn;
        void *data;
 } queue_handler[NPROTO];
+
+static struct nf_queue_rerouter *queue_rerouter;
+
 static DEFINE_RWLOCK(queue_handler_lock);
 
 int nf_register_hook(struct nf_hook_ops *reg)
        return 0;
 }
 
+int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer)
+{
+       if (pf >= NPROTO)
+               return -EINVAL;
+
+       write_lock_bh(&queue_handler_lock);
+       memcpy(&queue_rerouter[pf], rer, sizeof(queue_rerouter[pf]));
+       write_unlock_bh(&queue_handler_lock);
+
+       return 0;
+}
+
+int nf_unregister_queue_rerouter(int pf)
+{
+       if (pf >= NPROTO)
+               return -EINVAL;
+
+       write_lock_bh(&queue_handler_lock);
+       memset(&queue_rerouter[pf], 0, sizeof(queue_rerouter[pf]));
+       write_unlock_bh(&queue_handler_lock);
+       return 0;
+}
+
 /* 
  * Any packet that leaves via this function must come back 
  * through nf_reinject().
  */
-static int nf_queue(struct sk_buff *skb, 
+static int nf_queue(struct sk_buff **skb, 
                    struct list_head *elem, 
                    int pf, unsigned int hook,
                    struct net_device *indev,
        read_lock(&queue_handler_lock);
        if (!queue_handler[pf].outfn) {
                read_unlock(&queue_handler_lock);
-               kfree_skb(skb);
+               kfree_skb(*skb);
                return 1;
        }
 
-       info = kmalloc(sizeof(*info), GFP_ATOMIC);
+       info = kmalloc(sizeof(*info)+queue_rerouter[pf].rer_size, GFP_ATOMIC);
        if (!info) {
                if (net_ratelimit())
                        printk(KERN_ERR "OOM queueing packet %p\n",
-                              skb);
+                              *skb);
                read_unlock(&queue_handler_lock);
-               kfree_skb(skb);
+               kfree_skb(*skb);
                return 1;
        }
 
        if (outdev) dev_hold(outdev);
 
 #ifdef CONFIG_BRIDGE_NETFILTER
-       if (skb->nf_bridge) {
-               physindev = skb->nf_bridge->physindev;
+       if ((*skb)->nf_bridge) {
+               physindev = (*skb)->nf_bridge->physindev;
                if (physindev) dev_hold(physindev);
-               physoutdev = skb->nf_bridge->physoutdev;
+               physoutdev = (*skb)->nf_bridge->physoutdev;
                if (physoutdev) dev_hold(physoutdev);
        }
 #endif
+       if (queue_rerouter[pf].save)
+               queue_rerouter[pf].save(*skb, info);
+
+       status = queue_handler[pf].outfn(*skb, info, queue_handler[pf].data);
+
+       if (status >= 0 && queue_rerouter[pf].reroute)
+               status = queue_rerouter[pf].reroute(skb, info);
 
-       status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
        read_unlock(&queue_handler_lock);
 
        if (status < 0) {
 #endif
                module_put(info->elem->owner);
                kfree(info);
-               kfree_skb(skb);
+               kfree_skb(*skb);
+
                return 1;
        }
+
        return 1;
 }
 
                ret = -EPERM;
        } else if (verdict == NF_QUEUE) {
                NFDEBUG("nf_hook: Verdict = QUEUE.\n");
-               if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn))
+               if (!nf_queue(pskb, elem, pf, hook, indev, outdev, okfn))
                        goto next_hook;
        }
 unlock:
                break;
 
        case NF_QUEUE:
-               if (!nf_queue(skb, elem, info->pf, info->hook, 
+               if (!nf_queue(&skb, elem, info->pf, info->hook, 
                              info->indev, info->outdev, info->okfn))
                        goto next_hook;
                break;
 {
        int i, h;
 
+       queue_rerouter = kmalloc(NPROTO * sizeof(struct nf_queue_rerouter),
+                                GFP_KERNEL);
+       if (!queue_rerouter)
+               panic("netfilter: cannot allocate queue rerouter array\n");
+       memset(queue_rerouter, 0, NPROTO * sizeof(struct nf_queue_rerouter));
+
        for (i = 0; i < NPROTO; i++) {
                for (h = 0; h < NF_MAX_HOOKS; h++)
                        INIT_LIST_HEAD(&nf_hooks[i][h]);
 EXPORT_SYMBOL(nf_setsockopt);
 EXPORT_SYMBOL(nf_unregister_hook);
 EXPORT_SYMBOL(nf_unregister_queue_handler);
+EXPORT_SYMBOL_GPL(nf_register_queue_rerouter);
+EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter);
 EXPORT_SYMBOL(nf_unregister_sockopt);
 
-#include <linux/config.h>
+/* IPv4 specific functions of netfilter core */
 
+#include <linux/config.h>
 #ifdef CONFIG_NETFILTER
 
-/* IPv4 specific functions of netfilter core */
 #include <linux/kernel.h>
 #include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
 
 #include <linux/tcp.h>
 #include <linux/udp.h>
        return 0;
 }
 EXPORT_SYMBOL(ip_route_me_harder);
+
+/*
+ * Extra routing may needed on local out, as the QUEUE target never
+ * returns control to the table.
+ */
+
+struct ip_rt_info {
+       u_int32_t daddr;
+       u_int32_t saddr;
+       u_int8_t tos;
+};
+
+static void queue_save(const struct sk_buff *skb, struct nf_info *info)
+{
+       struct ip_rt_info *rt_info = nf_info_reroute(info);
+
+       if (info->hook == NF_IP_LOCAL_OUT) {
+               const struct iphdr *iph = skb->nh.iph;
+
+               rt_info->tos = iph->tos;
+               rt_info->daddr = iph->daddr;
+               rt_info->saddr = iph->saddr;
+       }
+}
+
+static int queue_reroute(struct sk_buff **pskb, const struct nf_info *info)
+{
+       const struct ip_rt_info *rt_info = nf_info_reroute(info);
+
+       if (info->hook == NF_IP_LOCAL_OUT) {
+               struct iphdr *iph = (*pskb)->nh.iph;
+
+               if (!(iph->tos == rt_info->tos
+                     && iph->daddr == rt_info->daddr
+                     && iph->saddr == rt_info->saddr))
+                       return ip_route_me_harder(pskb);
+       }
+       return 0;
+}
+
+static struct nf_queue_rerouter ip_reroute = {
+       .rer_size       = sizeof(struct ip_rt_info),
+       .save           = queue_save,
+       .reroute        = queue_reroute,
+};
+
+static int init(void)
+{
+       return nf_register_queue_rerouter(PF_INET, &ip_reroute);
+}
+
+static void fini(void)
+{
+       nf_unregister_queue_rerouter(PF_INET);
+}
+
+module_init(init);
+module_exit(fini);
+
 #endif /* CONFIG_NETFILTER */
 
 #define NET_IPQ_QMAX 2088
 #define NET_IPQ_QMAX_NAME "ip_queue_maxlen"
 
-struct ipq_rt_info {
-       __u8 tos;
-       __u32 daddr;
-       __u32 saddr;
-};
-
 struct ipq_queue_entry {
        struct list_head list;
        struct nf_info *info;
        struct sk_buff *skb;
-       struct ipq_rt_info rt_info;
 };
 
 typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
        entry->info = info;
        entry->skb = skb;
 
-       if (entry->info->hook == NF_IP_LOCAL_OUT) {
-               struct iphdr *iph = skb->nh.iph;
-
-               entry->rt_info.tos = iph->tos;
-               entry->rt_info.daddr = iph->daddr;
-               entry->rt_info.saddr = iph->saddr;
-       }
-
        nskb = ipq_build_packet_message(entry, &status);
        if (nskb == NULL)
                goto err_out_free;
        memcpy(e->skb->data, v->payload, v->data_len);
        e->skb->ip_summed = CHECKSUM_NONE;
 
-       /*
-        * Extra routing may needed on local out, as the QUEUE target never
-        * returns control to the table.
-        */
-       if (e->info->hook == NF_IP_LOCAL_OUT) {
-               struct iphdr *iph = e->skb->nh.iph;
-
-               if (!(iph->tos == e->rt_info.tos
-                     && iph->daddr == e->rt_info.daddr
-                     && iph->saddr == e->rt_info.saddr))
-                       return ip_route_me_harder(&e->skb);
-       }
        return 0;
 }
 
 
 #include <linux/netdevice.h>
 #include <linux/icmpv6.h>
 #include <linux/smp_lock.h>
+#include <linux/netfilter_ipv6.h>
 
 #include <net/ip.h>
 #include <net/ipv6.h>
        err = igmp6_init(&inet6_family_ops);
        if (err)
                goto igmp_fail;
+       err = ipv6_netfilter_init();
+       if (err)
+               goto netfilter_fail;
        /* Create /proc/foo6 entries. */
 #ifdef CONFIG_PROC_FS
        err = -ENOMEM;
        raw6_proc_exit();
 proc_raw6_fail:
 #endif
+       ipv6_netfilter_fini();
+netfilter_fail:
        igmp6_cleanup();
 igmp_fail:
        ndisc_cleanup();
        ip6_route_cleanup();
        ipv6_packet_cleanup();
        igmp6_cleanup();
+       ipv6_netfilter_fini();
        ndisc_cleanup();
        icmpv6_cleanup();
 #ifdef CONFIG_SYSCTL
 
 
 #include <linux/kernel.h>
 #include <linux/ipv6.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
 #include <net/dst.h>
 #include <net/ipv6.h>
 #include <net/ip6_route.h>
 }
 EXPORT_SYMBOL(ip6_route_me_harder);
 
+/*
+ * Extra routing may needed on local out, as the QUEUE target never
+ * returns control to the table.
+ */
+
+struct ip6_rt_info {
+       struct in6_addr daddr;
+       struct in6_addr saddr;
+};
+
+static void save(const struct sk_buff *skb, struct nf_info *info)
+{
+       struct ip6_rt_info *rt_info = nf_info_reroute(info);
+
+       if (info->hook == NF_IP6_LOCAL_OUT) {
+               struct ipv6hdr *iph = skb->nh.ipv6h;
+
+               rt_info->daddr = iph->daddr;
+               rt_info->saddr = iph->saddr;
+       }
+}
+
+static int reroute(struct sk_buff **pskb, const struct nf_info *info)
+{
+       struct ip6_rt_info *rt_info = nf_info_reroute(info);
+
+       if (info->hook == NF_IP6_LOCAL_OUT) {
+               struct ipv6hdr *iph = (*pskb)->nh.ipv6h;
+               if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
+                   !ipv6_addr_equal(&iph->saddr, &rt_info->saddr))
+                       return ip6_route_me_harder(*pskb);
+       }
+       return 0;
+}
+
+static struct nf_queue_rerouter ip6_reroute = {
+       .rer_size       = sizeof(struct ip6_rt_info),
+       .save           = &save,
+       .reroute        = &reroute,
+};
+
+int __init ipv6_netfilter_init(void)
+{
+       return nf_register_queue_rerouter(PF_INET6, &ip6_reroute);
+}
+
+void ipv6_netfilter_fini(void)
+{
+       nf_unregister_queue_rerouter(PF_INET6);
+}
+
+#else /* CONFIG_NETFILTER */
+int __init ipv6_netfilter_init(void)
+{
+       return 0;
+}
+
+void ipv6_netfilter_fini(void)
+{
+}
 #endif /* CONFIG_NETFILTER */
 
 #define NET_IPQ_QMAX 2088
 #define NET_IPQ_QMAX_NAME "ip6_queue_maxlen"
 
-struct ipq_rt_info {
-       struct in6_addr daddr;
-       struct in6_addr saddr;
-};
-
 struct ipq_queue_entry {
        struct list_head list;
        struct nf_info *info;
        struct sk_buff *skb;
-       struct ipq_rt_info rt_info;
 };
 
 typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
        entry->info = info;
        entry->skb = skb;
 
-       if (entry->info->hook == NF_IP_LOCAL_OUT) {
-               struct ipv6hdr *iph = skb->nh.ipv6h;
-
-               entry->rt_info.daddr = iph->daddr;
-               entry->rt_info.saddr = iph->saddr;
-       }
-
        nskb = ipq_build_packet_message(entry, &status);
        if (nskb == NULL)
                goto err_out_free;
        memcpy(e->skb->data, v->payload, v->data_len);
        e->skb->ip_summed = CHECKSUM_NONE;
 
-       /*
-        * Extra routing may needed on local out, as the QUEUE target never
-        * returns control to the table.
-         * Not a nice way to cmp, but works
-        */
-       if (e->info->hook == NF_IP_LOCAL_OUT) {
-               struct ipv6hdr *iph = e->skb->nh.ipv6h;
-               if (!ipv6_addr_equal(&iph->daddr, &e->rt_info.daddr) ||
-                   !ipv6_addr_equal(&iph->saddr, &e->rt_info.saddr))
-                       return ip6_route_me_harder(e->skb);
-       }
        return 0;
 }