fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / net / ipv4 / netfilter / ip_tables.c
index 8da90bd..fc1f153 100644 (file)
@@ -2,7 +2,7 @@
  * Packet matching code.
  *
  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
  * Packet matching code.
  *
  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
- * Copyright (C) 2000-2004 Netfilter Core Team <coreteam@netfilter.org>
+ * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
  *     - increase module usage count as soon as we have rules inside
  *       a table
  * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
  *     - increase module usage count as soon as we have rules inside
  *       a table
+ * 08 Oct 2005 Harald Welte <lafore@netfilter.org>
+ *     - Generalize into "x_tables" layer and "{ip,ip6,arp}_tables"
  */
  */
-#include <linux/config.h>
 #include <linux/cache.h>
 #include <linux/cache.h>
+#include <linux/capability.h>
 #include <linux/skbuff.h>
 #include <linux/kmod.h>
 #include <linux/vmalloc.h>
 #include <linux/netdevice.h>
 #include <linux/module.h>
 #include <linux/skbuff.h>
 #include <linux/kmod.h>
 #include <linux/vmalloc.h>
 #include <linux/netdevice.h>
 #include <linux/module.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
 #include <linux/icmp.h>
 #include <net/ip.h>
 #include <linux/icmp.h>
 #include <net/ip.h>
+#include <net/compat.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
 #include <linux/proc_fs.h>
 #include <linux/proc_fs.h>
+#include <linux/err.h>
+#include <linux/cpumask.h>
 
 
+#include <linux/netfilter/x_tables.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
 
 MODULE_LICENSE("GPL");
 #include <linux/netfilter_ipv4/ip_tables.h>
 
 MODULE_LICENSE("GPL");
@@ -59,15 +63,6 @@ do {                                                         \
 #else
 #define IP_NF_ASSERT(x)
 #endif
 #else
 #define IP_NF_ASSERT(x)
 #endif
-#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
-
-static DECLARE_MUTEX(ipt_mutex);
-
-/* Must have mutex */
-#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
-#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
-#include <linux/netfilter_ipv4/lockhelp.h>
-#include <linux/netfilter_ipv4/listhelp.h>
 
 #if 0
 /* All the better to debug you with... */
 
 #if 0
 /* All the better to debug you with... */
@@ -82,48 +77,8 @@ static DECLARE_MUTEX(ipt_mutex);
    context stops packets coming through and allows user context to read
    the counters or update the rules.
 
    context stops packets coming through and allows user context to read
    the counters or update the rules.
 
-   To be cache friendly on SMP, we arrange them like so:
-   [ n-entries ]
-   ... cache-align padding ...
-   [ n-entries ]
-
    Hence the start of any table is given by get_table() below.  */
 
    Hence the start of any table is given by get_table() below.  */
 
-/* The table itself */
-struct ipt_table_info
-{
-       /* Size per table */
-       unsigned int size;
-       /* Number of entries: FIXME. --RR */
-       unsigned int number;
-       /* Initial number of entries. Needed for module usage count */
-       unsigned int initial_entries;
-
-       /* Entry points and underflows */
-       unsigned int hook_entry[NF_IP_NUMHOOKS];
-       unsigned int underflow[NF_IP_NUMHOOKS];
-
-       /* ipt_entry tables: one per CPU */
-       char entries[0] ____cacheline_aligned;
-};
-
-static LIST_HEAD(ipt_target);
-static LIST_HEAD(ipt_match);
-static LIST_HEAD(ipt_tables);
-#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
-
-#ifdef CONFIG_SMP
-#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
-#else
-#define TABLE_OFFSET(t,p) 0
-#endif
-
-#if 0
-#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
-#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
-#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
-#endif
-
 /* Returns whether matches rule or not. */
 static inline int
 ip_packet_match(const struct iphdr *ip,
 /* Returns whether matches rule or not. */
 static inline int
 ip_packet_match(const struct iphdr *ip,
@@ -224,8 +179,8 @@ ipt_error(struct sk_buff **pskb,
          const struct net_device *in,
          const struct net_device *out,
          unsigned int hooknum,
          const struct net_device *in,
          const struct net_device *out,
          unsigned int hooknum,
-         const void *targinfo,
-         void *userinfo)
+         const struct xt_target *target,
+         const void *targinfo)
 {
        if (net_ratelimit())
                printk("ip_tables: error: `%s'\n", (char *)targinfo);
 {
        if (net_ratelimit())
                printk("ip_tables: error: `%s'\n", (char *)targinfo);
@@ -242,7 +197,8 @@ int do_match(struct ipt_entry_match *m,
             int *hotdrop)
 {
        /* Stop iteration if it doesn't match */
             int *hotdrop)
 {
        /* Stop iteration if it doesn't match */
-       if (!m->u.kernel.match->match(skb, in, out, m->data, offset, hotdrop))
+       if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
+                                     offset, skb->nh.iph->ihl*4, hotdrop))
                return 1;
        else
                return 0;
                return 1;
        else
                return 0;
@@ -260,8 +216,7 @@ ipt_do_table(struct sk_buff **pskb,
             unsigned int hook,
             const struct net_device *in,
             const struct net_device *out,
             unsigned int hook,
             const struct net_device *in,
             const struct net_device *out,
-            struct ipt_table *table,
-            void *userdata)
+            struct ipt_table *table)
 {
        static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
        u_int16_t offset;
 {
        static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
        u_int16_t offset;
@@ -273,6 +228,7 @@ ipt_do_table(struct sk_buff **pskb,
        const char *indev, *outdev;
        void *table_base;
        struct ipt_entry *e, *back;
        const char *indev, *outdev;
        void *table_base;
        struct ipt_entry *e, *back;
+       struct xt_table_info *private;
 
        /* Initialization */
        ip = (*pskb)->nh.iph;
 
        /* Initialization */
        ip = (*pskb)->nh.iph;
@@ -289,30 +245,16 @@ ipt_do_table(struct sk_buff **pskb,
 
        read_lock_bh(&table->lock);
        IP_NF_ASSERT(table->valid_hooks & (1 << hook));
 
        read_lock_bh(&table->lock);
        IP_NF_ASSERT(table->valid_hooks & (1 << hook));
-       table_base = (void *)table->private->entries
-               + TABLE_OFFSET(table->private, smp_processor_id());
-       e = get_entry(table_base, table->private->hook_entry[hook]);
-
-#ifdef CONFIG_NETFILTER_DEBUG
-       /* Check noone else using our table */
-       if (((struct ipt_entry *)table_base)->comefrom != 0xdead57ac
-           && ((struct ipt_entry *)table_base)->comefrom != 0xeeeeeeec) {
-               printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
-                      smp_processor_id(),
-                      table->name,
-                      &((struct ipt_entry *)table_base)->comefrom,
-                      ((struct ipt_entry *)table_base)->comefrom);
-       }
-       ((struct ipt_entry *)table_base)->comefrom = 0x57acc001;
-#endif
+       private = table->private;
+       table_base = (void *)private->entries[smp_processor_id()];
+       e = get_entry(table_base, private->hook_entry[hook]);
 
        /* For return from builtin chain */
 
        /* For return from builtin chain */
-       back = get_entry(table_base, table->private->underflow[hook]);
+       back = get_entry(table_base, private->underflow[hook]);
 
        do {
                IP_NF_ASSERT(e);
                IP_NF_ASSERT(back);
 
        do {
                IP_NF_ASSERT(e);
                IP_NF_ASSERT(back);
-               (*pskb)->nfcache |= e->nfcache;
                if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
                        struct ipt_entry_target *t;
 
                if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
                        struct ipt_entry_target *t;
 
@@ -341,8 +283,8 @@ ipt_do_table(struct sk_buff **pskb,
                                                         back->comefrom);
                                        continue;
                                }
                                                         back->comefrom);
                                        continue;
                                }
-                               if (table_base + v
-                                   != (void *)e + e->next_offset) {
+                               if (table_base + v != (void *)e + e->next_offset
+                                   && !(e->ip.flags & IPT_F_GOTO)) {
                                        /* Save old back ptr in next entry */
                                        struct ipt_entry *next
                                                = (void *)e + e->next_offset;
                                        /* Save old back ptr in next entry */
                                        struct ipt_entry *next
                                                = (void *)e + e->next_offset;
@@ -363,8 +305,8 @@ ipt_do_table(struct sk_buff **pskb,
                                verdict = t->u.kernel.target->target(pskb,
                                                                     in, out,
                                                                     hook,
                                verdict = t->u.kernel.target->target(pskb,
                                                                     in, out,
                                                                     hook,
-                                                                    t->data,
-                                                                    userdata);
+                                                                    t->u.kernel.target,
+                                                                    t->data);
 
 #ifdef CONFIG_NETFILTER_DEBUG
                                if (((struct ipt_entry *)table_base)->comefrom
 
 #ifdef CONFIG_NETFILTER_DEBUG
                                if (((struct ipt_entry *)table_base)->comefrom
@@ -394,9 +336,6 @@ ipt_do_table(struct sk_buff **pskb,
                }
        } while (!hotdrop);
 
                }
        } while (!hotdrop);
 
-#ifdef CONFIG_NETFILTER_DEBUG
-       ((struct ipt_entry *)table_base)->comefrom = 0xdead57ac;
-#endif
        read_unlock_bh(&table->lock);
 
 #ifdef DEBUG_ALLOW_ALL
        read_unlock_bh(&table->lock);
 
 #ifdef DEBUG_ALLOW_ALL
@@ -408,75 +347,6 @@ ipt_do_table(struct sk_buff **pskb,
 #endif
 }
 
 #endif
 }
 
-/* If it succeeds, returns element and locks mutex */
-static inline void *
-find_inlist_lock_noload(struct list_head *head,
-                       const char *name,
-                       int *error,
-                       struct semaphore *mutex)
-{
-       void *ret;
-
-#if 0 
-       duprintf("find_inlist: searching for `%s' in %s.\n",
-                name, head == &ipt_target ? "ipt_target"
-                : head == &ipt_match ? "ipt_match"
-                : head == &ipt_tables ? "ipt_tables" : "UNKNOWN");
-#endif
-
-       *error = down_interruptible(mutex);
-       if (*error != 0)
-               return NULL;
-
-       ret = list_named_find(head, name);
-       if (!ret) {
-               *error = -ENOENT;
-               up(mutex);
-       }
-       return ret;
-}
-
-#ifndef CONFIG_KMOD
-#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
-#else
-static void *
-find_inlist_lock(struct list_head *head,
-                const char *name,
-                const char *prefix,
-                int *error,
-                struct semaphore *mutex)
-{
-       void *ret;
-
-       ret = find_inlist_lock_noload(head, name, error, mutex);
-       if (!ret) {
-               duprintf("find_inlist: loading `%s%s'.\n", prefix, name);
-               request_module("%s%s", prefix, name);
-               ret = find_inlist_lock_noload(head, name, error, mutex);
-       }
-
-       return ret;
-}
-#endif
-
-static inline struct ipt_table *
-ipt_find_table_lock(const char *name, int *error, struct semaphore *mutex)
-{
-       return find_inlist_lock(&ipt_tables, name, "iptable_", error, mutex);
-}
-
-static inline struct ipt_match *
-find_match_lock(const char *name, int *error, struct semaphore *mutex)
-{
-       return find_inlist_lock(&ipt_match, name, "ipt_", error, mutex);
-}
-
-struct ipt_target *
-ipt_find_target_lock(const char *name, int *error, struct semaphore *mutex)
-{
-       return find_inlist_lock(&ipt_target, name, "ipt_", error, mutex);
-}
-
 /* All zeroes == unconditional rule. */
 static inline int
 unconditional(const struct ipt_ip *ip)
 /* All zeroes == unconditional rule. */
 static inline int
 unconditional(const struct ipt_ip *ip)
@@ -493,7 +363,8 @@ unconditional(const struct ipt_ip *ip)
 /* Figures out from what hook each rule can be called: returns 0 if
    there are loops.  Puts hook bitmask in comefrom. */
 static int
 /* Figures out from what hook each rule can be called: returns 0 if
    there are loops.  Puts hook bitmask in comefrom. */
 static int
-mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
+mark_source_chains(struct xt_table_info *newinfo,
+                  unsigned int valid_hooks, void *entry0)
 {
        unsigned int hook;
 
 {
        unsigned int hook;
 
@@ -502,7 +373,7 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
        for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
                unsigned int pos = newinfo->hook_entry[hook];
                struct ipt_entry *e
        for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
                unsigned int pos = newinfo->hook_entry[hook];
                struct ipt_entry *e
-                       = (struct ipt_entry *)(newinfo->entries + pos);
+                       = (struct ipt_entry *)(entry0 + pos);
 
                if (!(valid_hooks & (1 << hook)))
                        continue;
 
                if (!(valid_hooks & (1 << hook)))
                        continue;
@@ -513,6 +384,7 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
                for (;;) {
                        struct ipt_standard_target *t
                                = (void *)ipt_get_target(e);
                for (;;) {
                        struct ipt_standard_target *t
                                = (void *)ipt_get_target(e);
+                       int visited = e->comefrom & (1 << hook);
 
                        if (e->comefrom & (1 << NF_IP_NUMHOOKS)) {
                                printk("iptables: loop hook %u pos %u %08X.\n",
 
                        if (e->comefrom & (1 << NF_IP_NUMHOOKS)) {
                                printk("iptables: loop hook %u pos %u %08X.\n",
@@ -523,13 +395,20 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
                                |= ((1 << hook) | (1 << NF_IP_NUMHOOKS));
 
                        /* Unconditional return/END. */
                                |= ((1 << hook) | (1 << NF_IP_NUMHOOKS));
 
                        /* Unconditional return/END. */
-                       if (e->target_offset == sizeof(struct ipt_entry)
+                       if ((e->target_offset == sizeof(struct ipt_entry)
                            && (strcmp(t->target.u.user.name,
                                       IPT_STANDARD_TARGET) == 0)
                            && t->verdict < 0
                            && (strcmp(t->target.u.user.name,
                                       IPT_STANDARD_TARGET) == 0)
                            && t->verdict < 0
-                           && unconditional(&e->ip)) {
+                           && unconditional(&e->ip)) || visited) {
                                unsigned int oldpos, size;
 
                                unsigned int oldpos, size;
 
+                               if (t->verdict < -NF_MAX_VERDICT - 1) {
+                                       duprintf("mark_source_chains: bad "
+                                               "negative verdict (%i)\n",
+                                                               t->verdict);
+                                       return 0;
+                               }
+
                                /* Return: backtrack through the last
                                   big jump. */
                                do {
                                /* Return: backtrack through the last
                                   big jump. */
                                do {
@@ -552,13 +431,13 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
                                                goto next;
 
                                        e = (struct ipt_entry *)
                                                goto next;
 
                                        e = (struct ipt_entry *)
-                                               (newinfo->entries + pos);
+                                               (entry0 + pos);
                                } while (oldpos == pos + e->next_offset);
 
                                /* Move along one */
                                size = e->next_offset;
                                e = (struct ipt_entry *)
                                } while (oldpos == pos + e->next_offset);
 
                                /* Move along one */
                                size = e->next_offset;
                                e = (struct ipt_entry *)
-                                       (newinfo->entries + pos + size);
+                                       (entry0 + pos + size);
                                e->counters.pcnt = pos;
                                pos += size;
                        } else {
                                e->counters.pcnt = pos;
                                pos += size;
                        } else {
@@ -567,6 +446,13 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
                                if (strcmp(t->target.u.user.name,
                                           IPT_STANDARD_TARGET) == 0
                                    && newpos >= 0) {
                                if (strcmp(t->target.u.user.name,
                                           IPT_STANDARD_TARGET) == 0
                                    && newpos >= 0) {
+                                       if (newpos > newinfo->size -
+                                               sizeof(struct ipt_entry)) {
+                                               duprintf("mark_source_chains: "
+                                                       "bad verdict (%i)\n",
+                                                               newpos);
+                                               return 0;
+                                       }
                                        /* This a jump; chase it. */
                                        duprintf("Jump rule %u -> %u\n",
                                                 pos, newpos);
                                        /* This a jump; chase it. */
                                        duprintf("Jump rule %u -> %u\n",
                                                 pos, newpos);
@@ -575,7 +461,7 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
                                        newpos = pos + e->next_offset;
                                }
                                e = (struct ipt_entry *)
                                        newpos = pos + e->next_offset;
                                }
                                e = (struct ipt_entry *)
-                                       (newinfo->entries + newpos);
+                                       (entry0 + newpos);
                                e->counters.pcnt = pos;
                                pos = newpos;
                        }
                                e->counters.pcnt = pos;
                                pos = newpos;
                        }
@@ -593,82 +479,104 @@ cleanup_match(struct ipt_entry_match *m, unsigned int *i)
                return 1;
 
        if (m->u.kernel.match->destroy)
                return 1;
 
        if (m->u.kernel.match->destroy)
-               m->u.kernel.match->destroy(m->data,
-                                          m->u.match_size - sizeof(*m));
+               m->u.kernel.match->destroy(m->u.kernel.match, m->data);
        module_put(m->u.kernel.match->me);
        return 0;
 }
 
 static inline int
        module_put(m->u.kernel.match->me);
        return 0;
 }
 
 static inline int
-standard_check(const struct ipt_entry_target *t,
-              unsigned int max_offset)
+check_entry(struct ipt_entry *e, const char *name)
 {
 {
-       struct ipt_standard_target *targ = (void *)t;
-
-       /* Check standard info. */
-       if (t->u.target_size
-           != IPT_ALIGN(sizeof(struct ipt_standard_target))) {
-               duprintf("standard_check: target size %u != %u\n",
-                        t->u.target_size,
-                        IPT_ALIGN(sizeof(struct ipt_standard_target)));
-               return 0;
-       }
+       struct ipt_entry_target *t;
 
 
-       if (targ->verdict >= 0
-           && targ->verdict > max_offset - sizeof(struct ipt_entry)) {
-               duprintf("ipt_standard_check: bad verdict (%i)\n",
-                        targ->verdict);
-               return 0;
+       if (!ip_checkentry(&e->ip)) {
+               duprintf("ip_tables: ip check failed %p %s.\n", e, name);
+               return -EINVAL;
        }
 
        }
 
-       if (targ->verdict < -NF_MAX_VERDICT - 1) {
-               duprintf("ipt_standard_check: bad negative verdict (%i)\n",
-                        targ->verdict);
-               return 0;
+       if (e->target_offset + sizeof(struct ipt_entry_target) > e->next_offset)
+               return -EINVAL;
+
+       t = ipt_get_target(e);
+       if (e->target_offset + t->u.target_size > e->next_offset)
+               return -EINVAL;
+
+       return 0;
+}
+
+static inline int check_match(struct ipt_entry_match *m, const char *name,
+                               const struct ipt_ip *ip, unsigned int hookmask)
+{
+       struct ipt_match *match;
+       int ret;
+
+       match = m->u.kernel.match;
+       ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m),
+                            name, hookmask, ip->proto,
+                            ip->invflags & IPT_INV_PROTO);
+       if (!ret && m->u.kernel.match->checkentry
+           && !m->u.kernel.match->checkentry(name, ip, match, m->data,
+                                             hookmask)) {
+               duprintf("ip_tables: check failed for `%s'.\n",
+                        m->u.kernel.match->name);
+               ret = -EINVAL;
        }
        }
-       return 1;
+       return ret;
 }
 
 static inline int
 }
 
 static inline int
-check_match(struct ipt_entry_match *m,
+find_check_match(struct ipt_entry_match *m,
            const char *name,
            const struct ipt_ip *ip,
            unsigned int hookmask,
            unsigned int *i)
 {
            const char *name,
            const struct ipt_ip *ip,
            unsigned int hookmask,
            unsigned int *i)
 {
-       int ret;
        struct ipt_match *match;
        struct ipt_match *match;
+       int ret;
 
 
-       match = find_match_lock(m->u.user.name, &ret, &ipt_mutex);
-       if (!match) {
-               duprintf("check_match: `%s' not found\n", m->u.user.name);
-               return ret;
-       }
-       if (!try_module_get(match->me)) {
-               up(&ipt_mutex);
-               return -ENOENT;
+       match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
+                                                  m->u.user.revision),
+                                       "ipt_%s", m->u.user.name);
+       if (IS_ERR(match) || !match) {
+               duprintf("find_check_match: `%s' not found\n", m->u.user.name);
+               return match ? PTR_ERR(match) : -ENOENT;
        }
        m->u.kernel.match = match;
        }
        m->u.kernel.match = match;
-       up(&ipt_mutex);
 
 
-       if (m->u.kernel.match->checkentry
-           && !m->u.kernel.match->checkentry(name, ip, m->data,
-                                             m->u.match_size - sizeof(*m),
-                                             hookmask)) {
-               module_put(m->u.kernel.match->me);
-               duprintf("ip_tables: check failed for `%s'.\n",
-                        m->u.kernel.match->name);
-               return -EINVAL;
-       }
+       ret = check_match(m, name, ip, hookmask);
+       if (ret)
+               goto err;
 
        (*i)++;
        return 0;
 
        (*i)++;
        return 0;
+err:
+       module_put(m->u.kernel.match->me);
+       return ret;
 }
 
 }
 
-static struct ipt_target ipt_standard_target;
+static inline int check_target(struct ipt_entry *e, const char *name)
+{
+       struct ipt_entry_target *t;
+       struct ipt_target *target;
+       int ret;
+
+       t = ipt_get_target(e);
+       target = t->u.kernel.target;
+       ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t),
+                             name, e->comefrom, e->ip.proto,
+                             e->ip.invflags & IPT_INV_PROTO);
+       if (!ret && t->u.kernel.target->checkentry
+                  && !t->u.kernel.target->checkentry(name, e, target,
+                                                     t->data, e->comefrom)) {
+               duprintf("ip_tables: check failed for `%s'.\n",
+                        t->u.kernel.target->name);
+               ret = -EINVAL;
+       }
+       return ret;
+}
 
 static inline int
 
 static inline int
-check_entry(struct ipt_entry *e, const char *name, unsigned int size,
+find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
            unsigned int *i)
 {
        struct ipt_entry_target *t;
            unsigned int *i)
 {
        struct ipt_entry_target *t;
@@ -676,50 +584,36 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size,
        int ret;
        unsigned int j;
 
        int ret;
        unsigned int j;
 
-       if (!ip_checkentry(&e->ip)) {
-               duprintf("ip_tables: ip check failed %p %s.\n", e, name);
-               return -EINVAL;
-       }
+       ret = check_entry(e, name);
+       if (ret)
+               return ret;
 
        j = 0;
 
        j = 0;
-       ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);
+       ret = IPT_MATCH_ITERATE(e, find_check_match, name, &e->ip,
+                                                       e->comefrom, &j);
        if (ret != 0)
                goto cleanup_matches;
 
        t = ipt_get_target(e);
        if (ret != 0)
                goto cleanup_matches;
 
        t = ipt_get_target(e);
-       target = ipt_find_target_lock(t->u.user.name, &ret, &ipt_mutex);
-       if (!target) {
-               duprintf("check_entry: `%s' not found\n", t->u.user.name);
-               goto cleanup_matches;
-       }
-       if (!try_module_get(target->me)) {
-               up(&ipt_mutex);
-               ret = -ENOENT;
+       target = try_then_request_module(xt_find_target(AF_INET,
+                                                    t->u.user.name,
+                                                    t->u.user.revision),
+                                        "ipt_%s", t->u.user.name);
+       if (IS_ERR(target) || !target) {
+               duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
+               ret = target ? PTR_ERR(target) : -ENOENT;
                goto cleanup_matches;
        }
        t->u.kernel.target = target;
                goto cleanup_matches;
        }
        t->u.kernel.target = target;
-       up(&ipt_mutex);
 
 
-       if (t->u.kernel.target == &ipt_standard_target) {
-               if (!standard_check(t, size)) {
-                       ret = -EINVAL;
-                       goto cleanup_matches;
-               }
-       } else if (t->u.kernel.target->checkentry
-                  && !t->u.kernel.target->checkentry(name, e, t->data,
-                                                     t->u.target_size
-                                                     - sizeof(*t),
-                                                     e->comefrom)) {
-               module_put(t->u.kernel.target->me);
-               duprintf("ip_tables: check failed for `%s'.\n",
-                        t->u.kernel.target->name);
-               ret = -EINVAL;
-               goto cleanup_matches;
-       }
+       ret = check_target(e, name);
+       if (ret)
+               goto err;
 
        (*i)++;
        return 0;
 
        (*i)++;
        return 0;
-
+ err:
+       module_put(t->u.kernel.target->me);
  cleanup_matches:
        IPT_MATCH_ITERATE(e, cleanup_match, &j);
        return ret;
  cleanup_matches:
        IPT_MATCH_ITERATE(e, cleanup_match, &j);
        return ret;
@@ -727,7 +621,7 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size,
 
 static inline int
 check_entry_size_and_hooks(struct ipt_entry *e,
 
 static inline int
 check_entry_size_and_hooks(struct ipt_entry *e,
-                          struct ipt_table_info *newinfo,
+                          struct xt_table_info *newinfo,
                           unsigned char *base,
                           unsigned char *limit,
                           const unsigned int *hook_entries,
                           unsigned char *base,
                           unsigned char *limit,
                           const unsigned int *hook_entries,
@@ -761,7 +655,7 @@ check_entry_size_and_hooks(struct ipt_entry *e,
            < 0 (not IPT_RETURN). --RR */
 
        /* Clear counters and comefrom */
            < 0 (not IPT_RETURN). --RR */
 
        /* Clear counters and comefrom */
-       e->counters = ((struct ipt_counters) { 0, 0 });
+       e->counters = ((struct xt_counters) { 0, 0 });
        e->comefrom = 0;
 
        (*i)++;
        e->comefrom = 0;
 
        (*i)++;
@@ -780,8 +674,7 @@ cleanup_entry(struct ipt_entry *e, unsigned int *i)
        IPT_MATCH_ITERATE(e, cleanup_match, NULL);
        t = ipt_get_target(e);
        if (t->u.kernel.target->destroy)
        IPT_MATCH_ITERATE(e, cleanup_match, NULL);
        t = ipt_get_target(e);
        if (t->u.kernel.target->destroy)
-               t->u.kernel.target->destroy(t->data,
-                                           t->u.target_size - sizeof(*t));
+               t->u.kernel.target->destroy(t->u.kernel.target, t->data);
        module_put(t->u.kernel.target->me);
        return 0;
 }
        module_put(t->u.kernel.target->me);
        return 0;
 }
@@ -791,7 +684,8 @@ cleanup_entry(struct ipt_entry *e, unsigned int *i)
 static int
 translate_table(const char *name,
                unsigned int valid_hooks,
 static int
 translate_table(const char *name,
                unsigned int valid_hooks,
-               struct ipt_table_info *newinfo,
+               struct xt_table_info *newinfo,
+               void *entry0,
                unsigned int size,
                unsigned int number,
                const unsigned int *hook_entries,
                unsigned int size,
                unsigned int number,
                const unsigned int *hook_entries,
@@ -812,11 +706,11 @@ translate_table(const char *name,
        duprintf("translate_table: size %u\n", newinfo->size);
        i = 0;
        /* Walk through entries, checking offsets. */
        duprintf("translate_table: size %u\n", newinfo->size);
        i = 0;
        /* Walk through entries, checking offsets. */
-       ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+       ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
                                check_entry_size_and_hooks,
                                newinfo,
                                check_entry_size_and_hooks,
                                newinfo,
-                               newinfo->entries,
-                               newinfo->entries + size,
+                               entry0,
+                               entry0 + size,
                                hook_entries, underflows, &i);
        if (ret != 0)
                return ret;
                                hook_entries, underflows, &i);
        if (ret != 0)
                return ret;
@@ -844,93 +738,79 @@ translate_table(const char *name,
                }
        }
 
                }
        }
 
-       if (!mark_source_chains(newinfo, valid_hooks))
+       if (!mark_source_chains(newinfo, valid_hooks, entry0))
                return -ELOOP;
 
        /* Finally, each sanity check must pass */
        i = 0;
                return -ELOOP;
 
        /* Finally, each sanity check must pass */
        i = 0;
-       ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
-                               check_entry, name, size, &i);
+       ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
+                               find_check_entry, name, size, &i);
 
        if (ret != 0) {
 
        if (ret != 0) {
-               IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
-                                 cleanup_entry, &i);
+               IPT_ENTRY_ITERATE(entry0, newinfo->size,
+                               cleanup_entry, &i);
                return ret;
        }
 
        /* And one copy for every other CPU */
                return ret;
        }
 
        /* And one copy for every other CPU */
-       for (i = 1; i < NR_CPUS; i++) {
-               memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
-                      newinfo->entries,
-                      SMP_ALIGN(newinfo->size));
+       for_each_possible_cpu(i) {
+               if (newinfo->entries[i] && newinfo->entries[i] != entry0)
+                       memcpy(newinfo->entries[i], entry0, newinfo->size);
        }
 
        return ret;
 }
 
        }
 
        return ret;
 }
 
-static struct ipt_table_info *
-replace_table(struct ipt_table *table,
-             unsigned int num_counters,
-             struct ipt_table_info *newinfo,
-             int *error)
+/* Gets counters. */
+static inline int
+add_entry_to_counter(const struct ipt_entry *e,
+                    struct xt_counters total[],
+                    unsigned int *i)
 {
 {
-       struct ipt_table_info *oldinfo;
-
-#ifdef CONFIG_NETFILTER_DEBUG
-       {
-               struct ipt_entry *table_base;
-               unsigned int i;
-
-               for (i = 0; i < NR_CPUS; i++) {
-                       table_base =
-                               (void *)newinfo->entries
-                               + TABLE_OFFSET(newinfo, i);
-
-                       table_base->comefrom = 0xdead57ac;
-               }
-       }
-#endif
-
-       /* Do the substitution. */
-       write_lock_bh(&table->lock);
-       /* Check inside lock: is the old number correct? */
-       if (num_counters != table->private->number) {
-               duprintf("num_counters != table->private->number (%u/%u)\n",
-                        num_counters, table->private->number);
-               write_unlock_bh(&table->lock);
-               *error = -EAGAIN;
-               return NULL;
-       }
-       oldinfo = table->private;
-       table->private = newinfo;
-       newinfo->initial_entries = oldinfo->initial_entries;
-       write_unlock_bh(&table->lock);
+       ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
 
 
-       return oldinfo;
+       (*i)++;
+       return 0;
 }
 
 }
 
-/* Gets counters. */
 static inline int
 static inline int
-add_entry_to_counter(const struct ipt_entry *e,
+set_entry_to_counter(const struct ipt_entry *e,
                     struct ipt_counters total[],
                     unsigned int *i)
 {
                     struct ipt_counters total[],
                     unsigned int *i)
 {
-       ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
+       SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
 
        (*i)++;
        return 0;
 }
 
 static void
 
        (*i)++;
        return 0;
 }
 
 static void
-get_counters(const struct ipt_table_info *t,
-            struct ipt_counters counters[])
+get_counters(const struct xt_table_info *t,
+            struct xt_counters counters[])
 {
        unsigned int cpu;
        unsigned int i;
 {
        unsigned int cpu;
        unsigned int i;
+       unsigned int curcpu;
 
 
-       for (cpu = 0; cpu < NR_CPUS; cpu++) {
+       /* Instead of clearing (by a previous call to memset())
+        * the counters and using adds, we set the counters
+        * with data used by 'current' CPU
+        * We dont care about preemption here.
+        */
+       curcpu = raw_smp_processor_id();
+
+       i = 0;
+       IPT_ENTRY_ITERATE(t->entries[curcpu],
+                         t->size,
+                         set_entry_to_counter,
+                         counters,
+                         &i);
+
+       for_each_possible_cpu(cpu) {
+               if (cpu == curcpu)
+                       continue;
                i = 0;
                i = 0;
-               IPT_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
+               IPT_ENTRY_ITERATE(t->entries[cpu],
                                  t->size,
                                  add_entry_to_counter,
                                  counters,
                                  t->size,
                                  add_entry_to_counter,
                                  counters,
@@ -938,33 +818,52 @@ get_counters(const struct ipt_table_info *t,
        }
 }
 
        }
 }
 
-static int
-copy_entries_to_user(unsigned int total_size,
-                    struct ipt_table *table,
-                    void __user *userptr)
+static inline struct xt_counters * alloc_counters(struct ipt_table *table)
 {
 {
-       unsigned int off, num, countersize;
-       struct ipt_entry *e;
-       struct ipt_counters *counters;
-       int ret = 0;
+       unsigned int countersize;
+       struct xt_counters *counters;
+       struct xt_table_info *private = table->private;
 
        /* We need atomic snapshot of counters: rest doesn't change
           (other than comefrom, which userspace doesn't care
           about). */
 
        /* We need atomic snapshot of counters: rest doesn't change
           (other than comefrom, which userspace doesn't care
           about). */
-       countersize = sizeof(struct ipt_counters) * table->private->number;
-       counters = vmalloc(countersize);
+       countersize = sizeof(struct xt_counters) * private->number;
+       counters = vmalloc_node(countersize, numa_node_id());
 
        if (counters == NULL)
 
        if (counters == NULL)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        /* First, sum counters... */
 
        /* First, sum counters... */
-       memset(counters, 0, countersize);
        write_lock_bh(&table->lock);
        write_lock_bh(&table->lock);
-       get_counters(table->private, counters);
+       get_counters(private, counters);
        write_unlock_bh(&table->lock);
 
        write_unlock_bh(&table->lock);
 
-       /* ... then copy entire thing from CPU 0... */
-       if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
+       return counters;
+}
+
+static int
+copy_entries_to_user(unsigned int total_size,
+                    struct ipt_table *table,
+                    void __user *userptr)
+{
+       unsigned int off, num;
+       struct ipt_entry *e;
+       struct xt_counters *counters;
+       struct xt_table_info *private = table->private;
+       int ret = 0;
+       void *loc_cpu_entry;
+
+       counters = alloc_counters(table);
+       if (IS_ERR(counters))
+               return PTR_ERR(counters);
+
+       /* choose the copy that is on our node/cpu, ...
+        * This choice is lazy (because current thread is
+        * allowed to migrate to another cpu)
+        */
+       loc_cpu_entry = private->entries[raw_smp_processor_id()];
+       /* ... then copy entire thing ... */
+       if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
                ret = -EFAULT;
                goto free_counters;
        }
                ret = -EFAULT;
                goto free_counters;
        }
@@ -976,7 +875,7 @@ copy_entries_to_user(unsigned int total_size,
                struct ipt_entry_match *m;
                struct ipt_entry_target *t;
 
                struct ipt_entry_match *m;
                struct ipt_entry_target *t;
 
-               e = (struct ipt_entry *)(table->private->entries + off);
+               e = (struct ipt_entry *)(loc_cpu_entry + off);
                if (copy_to_user(userptr + off
                                 + offsetof(struct ipt_entry, counters),
                                 &counters[num],
                if (copy_to_user(userptr + off
                                 + offsetof(struct ipt_entry, counters),
                                 &counters[num],
@@ -1017,135 +916,357 @@ copy_entries_to_user(unsigned int total_size,
        return ret;
 }
 
        return ret;
 }
 
-static int
-get_entries(const struct ipt_get_entries *entries,
-           struct ipt_get_entries __user *uptr)
+#ifdef CONFIG_COMPAT
+struct compat_delta {
+       struct compat_delta *next;
+       unsigned int offset;
+       short delta;
+};
+
+static struct compat_delta *compat_offsets = NULL;
+
+static int compat_add_offset(unsigned int offset, short delta)
 {
 {
-       int ret;
-       struct ipt_table *t;
+       struct compat_delta *tmp;
 
 
-       t = ipt_find_table_lock(entries->name, &ret, &ipt_mutex);
-       if (t) {
-               duprintf("t->private->number = %u\n",
-                        t->private->number);
-               if (entries->size == t->private->size)
-                       ret = copy_entries_to_user(t->private->size,
-                                                  t, uptr->entrytable);
-               else {
-                       duprintf("get_entries: I've got %u not %u!\n",
-                                t->private->size,
-                                entries->size);
-                       ret = -EINVAL;
+       tmp = kmalloc(sizeof(struct compat_delta), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+       tmp->offset = offset;
+       tmp->delta = delta;
+       if (compat_offsets) {
+               tmp->next = compat_offsets->next;
+               compat_offsets->next = tmp;
+       } else {
+               compat_offsets = tmp;
+               tmp->next = NULL;
+       }
+       return 0;
+}
+
+static void compat_flush_offsets(void)
+{
+       struct compat_delta *tmp, *next;
+
+       if (compat_offsets) {
+               for(tmp = compat_offsets; tmp; tmp = next) {
+                       next = tmp->next;
+                       kfree(tmp);
                }
                }
-               up(&ipt_mutex);
-       } else
-               duprintf("get_entries: Can't find %s!\n",
-                        entries->name);
+               compat_offsets = NULL;
+       }
+}
 
 
-       return ret;
+static short compat_calc_jump(unsigned int offset)
+{
+       struct compat_delta *tmp;
+       short delta;
+
+       for(tmp = compat_offsets, delta = 0; tmp; tmp = tmp->next)
+               if (tmp->offset < offset)
+                       delta += tmp->delta;
+       return delta;
 }
 
 }
 
-static int
-do_replace(void __user *user, unsigned int len)
+static void compat_standard_from_user(void *dst, void *src)
 {
 {
-       int ret;
-       struct ipt_replace tmp;
-       struct ipt_table *t;
-       struct ipt_table_info *newinfo, *oldinfo;
-       struct ipt_counters *counters;
+       int v = *(compat_int_t *)src;
 
 
-       if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
-               return -EFAULT;
+       if (v > 0)
+               v += compat_calc_jump(v);
+       memcpy(dst, &v, sizeof(v));
+}
 
 
-       /* Hack: Causes ipchains to give correct error msg --RR */
-       if (len != sizeof(tmp) + tmp.size)
-               return -ENOPROTOOPT;
+static int compat_standard_to_user(void __user *dst, void *src)
+{
+       compat_int_t cv = *(int *)src;
 
 
-       /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
-       if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
-               return -ENOMEM;
+       if (cv > 0)
+               cv -= compat_calc_jump(cv);
+       return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
+}
 
 
-       newinfo = vmalloc(sizeof(struct ipt_table_info)
-                         + SMP_ALIGN(tmp.size) * NR_CPUS);
-       if (!newinfo)
-               return -ENOMEM;
+static inline int
+compat_calc_match(struct ipt_entry_match *m, int * size)
+{
+       *size += xt_compat_match_offset(m->u.kernel.match);
+       return 0;
+}
 
 
-       if (copy_from_user(newinfo->entries, user + sizeof(tmp),
-                          tmp.size) != 0) {
-               ret = -EFAULT;
-               goto free_newinfo;
-       }
+static int compat_calc_entry(struct ipt_entry *e, struct xt_table_info *info,
+               void *base, struct xt_table_info *newinfo)
+{
+       struct ipt_entry_target *t;
+       unsigned int entry_offset;
+       int off, i, ret;
 
 
-       counters = vmalloc(tmp.num_counters * sizeof(struct ipt_counters));
-       if (!counters) {
-               ret = -ENOMEM;
-               goto free_newinfo;
-       }
-       memset(counters, 0, tmp.num_counters * sizeof(struct ipt_counters));
+       off = 0;
+       entry_offset = (void *)e - base;
+       IPT_MATCH_ITERATE(e, compat_calc_match, &off);
+       t = ipt_get_target(e);
+       off += xt_compat_target_offset(t->u.kernel.target);
+       newinfo->size -= off;
+       ret = compat_add_offset(entry_offset, off);
+       if (ret)
+               return ret;
 
 
-       ret = translate_table(tmp.name, tmp.valid_hooks,
-                             newinfo, tmp.size, tmp.num_entries,
-                             tmp.hook_entry, tmp.underflow);
-       if (ret != 0)
-               goto free_newinfo_counters;
+       for (i = 0; i< NF_IP_NUMHOOKS; i++) {
+               if (info->hook_entry[i] && (e < (struct ipt_entry *)
+                               (base + info->hook_entry[i])))
+                       newinfo->hook_entry[i] -= off;
+               if (info->underflow[i] && (e < (struct ipt_entry *)
+                               (base + info->underflow[i])))
+                       newinfo->underflow[i] -= off;
+       }
+       return 0;
+}
 
 
-       duprintf("ip_tables: Translated table\n");
+static int compat_table_info(struct xt_table_info *info,
+               struct xt_table_info *newinfo)
+{
+       void *loc_cpu_entry;
+       int i;
 
 
-       t = ipt_find_table_lock(tmp.name, &ret, &ipt_mutex);
-       if (!t)
-               goto free_newinfo_counters_untrans;
+       if (!newinfo || !info)
+               return -EINVAL;
 
 
-       /* You lied! */
-       if (tmp.valid_hooks != t->valid_hooks) {
-               duprintf("Valid hook crap: %08X vs %08X\n",
-                        tmp.valid_hooks, t->valid_hooks);
-               ret = -EINVAL;
-               goto free_newinfo_counters_untrans_unlock;
+       memset(newinfo, 0, sizeof(struct xt_table_info));
+       newinfo->size = info->size;
+       newinfo->number = info->number;
+       for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+               newinfo->hook_entry[i] = info->hook_entry[i];
+               newinfo->underflow[i] = info->underflow[i];
        }
        }
+       loc_cpu_entry = info->entries[raw_smp_processor_id()];
+       return IPT_ENTRY_ITERATE(loc_cpu_entry, info->size,
+                       compat_calc_entry, info, loc_cpu_entry, newinfo);
+}
+#endif
+
+static int get_info(void __user *user, int *len, int compat)
+{
+       char name[IPT_TABLE_MAXNAMELEN];
+       struct ipt_table *t;
+       int ret;
 
 
-       /* Get a reference in advance, we're not allowed fail later */
-       if (!try_module_get(t->me)) {
-               ret = -EBUSY;
-               goto free_newinfo_counters_untrans_unlock;
+       if (*len != sizeof(struct ipt_getinfo)) {
+               duprintf("length %u != %u\n", *len,
+                       (unsigned int)sizeof(struct ipt_getinfo));
+               return -EINVAL;
        }
 
        }
 
+       if (copy_from_user(name, user, sizeof(name)) != 0)
+               return -EFAULT;
 
 
-       oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
-       if (!oldinfo)
-               goto put_module;
+       name[IPT_TABLE_MAXNAMELEN-1] = '\0';
+#ifdef CONFIG_COMPAT
+       if (compat)
+               xt_compat_lock(AF_INET);
+#endif
+       t = try_then_request_module(xt_find_table_lock(AF_INET, name),
+                       "iptable_%s", name);
+       if (t && !IS_ERR(t)) {
+               struct ipt_getinfo info;
+               struct xt_table_info *private = t->private;
+
+#ifdef CONFIG_COMPAT
+               if (compat) {
+                       struct xt_table_info tmp;
+                       ret = compat_table_info(private, &tmp);
+                       compat_flush_offsets();
+                       private =  &tmp;
+               }
+#endif
+               info.valid_hooks = t->valid_hooks;
+               memcpy(info.hook_entry, private->hook_entry,
+                               sizeof(info.hook_entry));
+               memcpy(info.underflow, private->underflow,
+                               sizeof(info.underflow));
+               info.num_entries = private->number;
+               info.size = private->size;
+               strcpy(info.name, name);
+
+               if (copy_to_user(user, &info, *len) != 0)
+                       ret = -EFAULT;
+               else
+                       ret = 0;
 
 
-       /* Update module usage count based on number of rules */
-       duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
-               oldinfo->number, oldinfo->initial_entries, newinfo->number);
-       if ((oldinfo->number > oldinfo->initial_entries) || 
-           (newinfo->number <= oldinfo->initial_entries)) 
-               module_put(t->me);
-       if ((oldinfo->number > oldinfo->initial_entries) &&
-           (newinfo->number <= oldinfo->initial_entries))
+               xt_table_unlock(t);
                module_put(t->me);
                module_put(t->me);
+       } else
+               ret = t ? PTR_ERR(t) : -ENOENT;
+#ifdef CONFIG_COMPAT
+       if (compat)
+               xt_compat_unlock(AF_INET);
+#endif
+       return ret;
+}
 
 
-       /* Get the old counters. */
-       get_counters(oldinfo, counters);
-       /* Decrease module usage counts and free resource */
-       IPT_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
-       vfree(oldinfo);
-       /* Silent error: too late now. */
-       copy_to_user(tmp.counters, counters,
-                    sizeof(struct ipt_counters) * tmp.num_counters);
-       vfree(counters);
-       up(&ipt_mutex);
-       return 0;
+static int
+get_entries(struct ipt_get_entries __user *uptr, int *len)
+{
+       int ret;
+       struct ipt_get_entries get;
+       struct ipt_table *t;
+
+       if (*len < sizeof(get)) {
+               duprintf("get_entries: %u < %d\n", *len,
+                               (unsigned int)sizeof(get));
+               return -EINVAL;
+       }
+       if (copy_from_user(&get, uptr, sizeof(get)) != 0)
+               return -EFAULT;
+       if (*len != sizeof(struct ipt_get_entries) + get.size) {
+               duprintf("get_entries: %u != %u\n", *len,
+                               (unsigned int)(sizeof(struct ipt_get_entries) +
+                               get.size));
+               return -EINVAL;
+       }
+
+       t = xt_find_table_lock(AF_INET, get.name);
+       if (t && !IS_ERR(t)) {
+               struct xt_table_info *private = t->private;
+               duprintf("t->private->number = %u\n",
+                        private->number);
+               if (get.size == private->size)
+                       ret = copy_entries_to_user(private->size,
+                                                  t, uptr->entrytable);
+               else {
+                       duprintf("get_entries: I've got %u not %u!\n",
+                                private->size,
+                                get.size);
+                       ret = -EINVAL;
+               }
+               module_put(t->me);
+               xt_table_unlock(t);
+       } else
+               ret = t ? PTR_ERR(t) : -ENOENT;
+
+       return ret;
+}
+
+static int
+__do_replace(const char *name, unsigned int valid_hooks,
+               struct xt_table_info *newinfo, unsigned int num_counters,
+               void __user *counters_ptr)
+{
+       int ret;
+       struct ipt_table *t;
+       struct xt_table_info *oldinfo;
+       struct xt_counters *counters;
+       void *loc_cpu_old_entry;
+
+       ret = 0;
+       counters = vmalloc(num_counters * sizeof(struct xt_counters));
+       if (!counters) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       t = try_then_request_module(xt_find_table_lock(AF_INET, name),
+                                   "iptable_%s", name);
+       if (!t || IS_ERR(t)) {
+               ret = t ? PTR_ERR(t) : -ENOENT;
+               goto free_newinfo_counters_untrans;
+       }
+
+       /* You lied! */
+       if (valid_hooks != t->valid_hooks) {
+               duprintf("Valid hook crap: %08X vs %08X\n",
+                        valid_hooks, t->valid_hooks);
+               ret = -EINVAL;
+               goto put_module;
+       }
+
+       oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
+       if (!oldinfo)
+               goto put_module;
+
+       /* Update module usage count based on number of rules */
+       duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
+               oldinfo->number, oldinfo->initial_entries, newinfo->number);
+       if ((oldinfo->number > oldinfo->initial_entries) ||
+           (newinfo->number <= oldinfo->initial_entries))
+               module_put(t->me);
+       if ((oldinfo->number > oldinfo->initial_entries) &&
+           (newinfo->number <= oldinfo->initial_entries))
+               module_put(t->me);
+
+       /* Get the old counters. */
+       get_counters(oldinfo, counters);
+       /* Decrease module usage counts and free resource */
+       loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
+       IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
+       xt_free_table_info(oldinfo);
+       if (copy_to_user(counters_ptr, counters,
+                        sizeof(struct xt_counters) * num_counters) != 0)
+               ret = -EFAULT;
+       vfree(counters);
+       xt_table_unlock(t);
+       return ret;
 
  put_module:
        module_put(t->me);
 
  put_module:
        module_put(t->me);
- free_newinfo_counters_untrans_unlock:
-       up(&ipt_mutex);
+       xt_table_unlock(t);
  free_newinfo_counters_untrans:
  free_newinfo_counters_untrans:
-       IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
- free_newinfo_counters:
        vfree(counters);
        vfree(counters);
+ out:
+       return ret;
+}
+
+static int
+do_replace(void __user *user, unsigned int len)
+{
+       int ret;
+       struct ipt_replace tmp;
+       struct xt_table_info *newinfo;
+       void *loc_cpu_entry;
+
+       if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+               return -EFAULT;
+
+       /* Hack: Causes ipchains to give correct error msg --RR */
+       if (len != sizeof(tmp) + tmp.size)
+               return -ENOPROTOOPT;
+
+       /* overflow check */
+       if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
+                       SMP_CACHE_BYTES)
+               return -ENOMEM;
+       if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
+               return -ENOMEM;
+
+       newinfo = xt_alloc_table_info(tmp.size);
+       if (!newinfo)
+               return -ENOMEM;
+
+       /* choose the copy that is our node/cpu */
+       loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+       if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
+                          tmp.size) != 0) {
+               ret = -EFAULT;
+               goto free_newinfo;
+       }
+
+       ret = translate_table(tmp.name, tmp.valid_hooks,
+                             newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
+                             tmp.hook_entry, tmp.underflow);
+       if (ret != 0)
+               goto free_newinfo;
+
+       duprintf("ip_tables: Translated table\n");
+
+       ret = __do_replace(tmp.name, tmp.valid_hooks,
+                             newinfo, tmp.num_counters,
+                             tmp.counters);
+       if (ret)
+               goto free_newinfo_untrans;
+       return 0;
+
+ free_newinfo_untrans:
+       IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
  free_newinfo:
  free_newinfo:
-       vfree(newinfo);
+       xt_free_table_info(newinfo);
        return ret;
 }
 
        return ret;
 }
 
@@ -1153,7 +1274,7 @@ do_replace(void __user *user, unsigned int len)
  * and everything is OK. */
 static inline int
 add_counter_to_entry(struct ipt_entry *e,
  * and everything is OK. */
 static inline int
 add_counter_to_entry(struct ipt_entry *e,
-                    const struct ipt_counters addme[],
+                    const struct xt_counters addme[],
                     unsigned int *i)
 {
 #if 0
                     unsigned int *i)
 {
 #if 0
@@ -1172,462 +1293,776 @@ add_counter_to_entry(struct ipt_entry *e,
 }
 
 static int
 }
 
 static int
-do_add_counters(void __user *user, unsigned int len)
+do_add_counters(void __user *user, unsigned int len, int compat)
 {
        unsigned int i;
 {
        unsigned int i;
-       struct ipt_counters_info tmp, *paddc;
+       struct xt_counters_info tmp;
+       struct xt_counters *paddc;
+       unsigned int num_counters;
+       char *name;
+       int size;
+       void *ptmp;
        struct ipt_table *t;
        struct ipt_table *t;
-       int ret;
+       struct xt_table_info *private;
+       int ret = 0;
+       void *loc_cpu_entry;
+#ifdef CONFIG_COMPAT
+       struct compat_xt_counters_info compat_tmp;
 
 
-       if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+       if (compat) {
+               ptmp = &compat_tmp;
+               size = sizeof(struct compat_xt_counters_info);
+       } else
+#endif
+       {
+               ptmp = &tmp;
+               size = sizeof(struct xt_counters_info);
+       }
+
+       if (copy_from_user(ptmp, user, size) != 0)
                return -EFAULT;
 
                return -EFAULT;
 
-       if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ipt_counters))
+#ifdef CONFIG_COMPAT
+       if (compat) {
+               num_counters = compat_tmp.num_counters;
+               name = compat_tmp.name;
+       } else
+#endif
+       {
+               num_counters = tmp.num_counters;
+               name = tmp.name;
+       }
+
+       if (len != size + num_counters * sizeof(struct xt_counters))
                return -EINVAL;
 
                return -EINVAL;
 
-       paddc = vmalloc(len);
+       paddc = vmalloc_node(len - size, numa_node_id());
        if (!paddc)
                return -ENOMEM;
 
        if (!paddc)
                return -ENOMEM;
 
-       if (copy_from_user(paddc, user, len) != 0) {
+       if (copy_from_user(paddc, user + size, len - size) != 0) {
                ret = -EFAULT;
                goto free;
        }
 
                ret = -EFAULT;
                goto free;
        }
 
-       t = ipt_find_table_lock(tmp.name, &ret, &ipt_mutex);
-       if (!t)
+       t = xt_find_table_lock(AF_INET, name);
+       if (!t || IS_ERR(t)) {
+               ret = t ? PTR_ERR(t) : -ENOENT;
                goto free;
                goto free;
+       }
 
        write_lock_bh(&t->lock);
 
        write_lock_bh(&t->lock);
-       if (t->private->number != paddc->num_counters) {
+       private = t->private;
+       if (private->number != num_counters) {
                ret = -EINVAL;
                goto unlock_up_free;
        }
 
        i = 0;
                ret = -EINVAL;
                goto unlock_up_free;
        }
 
        i = 0;
-       IPT_ENTRY_ITERATE(t->private->entries,
-                         t->private->size,
+       /* Choose the copy that is on our node */
+       loc_cpu_entry = private->entries[raw_smp_processor_id()];
+       IPT_ENTRY_ITERATE(loc_cpu_entry,
+                         private->size,
                          add_counter_to_entry,
                          add_counter_to_entry,
-                         paddc->counters,
+                         paddc,
                          &i);
  unlock_up_free:
        write_unlock_bh(&t->lock);
                          &i);
  unlock_up_free:
        write_unlock_bh(&t->lock);
-       up(&ipt_mutex);
+       xt_table_unlock(t);
+       module_put(t->me);
  free:
        vfree(paddc);
 
        return ret;
 }
 
  free:
        vfree(paddc);
 
        return ret;
 }
 
-static int
-do_ipt_set_ctl(struct sock *sk,        int cmd, void __user *user, unsigned int len)
+#ifdef CONFIG_COMPAT
+struct compat_ipt_replace {
+       char                    name[IPT_TABLE_MAXNAMELEN];
+       u32                     valid_hooks;
+       u32                     num_entries;
+       u32                     size;
+       u32                     hook_entry[NF_IP_NUMHOOKS];
+       u32                     underflow[NF_IP_NUMHOOKS];
+       u32                     num_counters;
+       compat_uptr_t           counters;       /* struct ipt_counters * */
+       struct compat_ipt_entry entries[0];
+};
+
+static inline int compat_copy_match_to_user(struct ipt_entry_match *m,
+               void __user **dstptr, compat_uint_t *size)
 {
 {
-       int ret;
+       return xt_compat_match_to_user(m, dstptr, size);
+}
 
 
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
+static int compat_copy_entry_to_user(struct ipt_entry *e,
+               void __user **dstptr, compat_uint_t *size)
+{
+       struct ipt_entry_target *t;
+       struct compat_ipt_entry __user *ce;
+       u_int16_t target_offset, next_offset;
+       compat_uint_t origsize;
+       int ret;
 
 
-       switch (cmd) {
-       case IPT_SO_SET_REPLACE:
-               ret = do_replace(user, len);
-               break;
+       ret = -EFAULT;
+       origsize = *size;
+       ce = (struct compat_ipt_entry __user *)*dstptr;
+       if (copy_to_user(ce, e, sizeof(struct ipt_entry)))
+               goto out;
+
+       *dstptr += sizeof(struct compat_ipt_entry);
+       ret = IPT_MATCH_ITERATE(e, compat_copy_match_to_user, dstptr, size);
+       target_offset = e->target_offset - (origsize - *size);
+       if (ret)
+               goto out;
+       t = ipt_get_target(e);
+       ret = xt_compat_target_to_user(t, dstptr, size);
+       if (ret)
+               goto out;
+       ret = -EFAULT;
+       next_offset = e->next_offset - (origsize - *size);
+       if (put_user(target_offset, &ce->target_offset))
+               goto out;
+       if (put_user(next_offset, &ce->next_offset))
+               goto out;
+       return 0;
+out:
+       return ret;
+}
 
 
-       case IPT_SO_SET_ADD_COUNTERS:
-               ret = do_add_counters(user, len);
-               break;
+static inline int
+compat_check_calc_match(struct ipt_entry_match *m,
+           const char *name,
+           const struct ipt_ip *ip,
+           unsigned int hookmask,
+           int *size, int *i)
+{
+       struct ipt_match *match;
 
 
-       default:
-               duprintf("do_ipt_set_ctl:  unknown request %i\n", cmd);
-               ret = -EINVAL;
+       match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
+                                                  m->u.user.revision),
+                                       "ipt_%s", m->u.user.name);
+       if (IS_ERR(match) || !match) {
+               duprintf("compat_check_calc_match: `%s' not found\n",
+                               m->u.user.name);
+               return match ? PTR_ERR(match) : -ENOENT;
        }
        }
+       m->u.kernel.match = match;
+       *size += xt_compat_match_offset(match);
 
 
-       return ret;
+       (*i)++;
+       return 0;
 }
 
 }
 
-static int
-do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
+static inline int
+check_compat_entry_size_and_hooks(struct ipt_entry *e,
+                          struct xt_table_info *newinfo,
+                          unsigned int *size,
+                          unsigned char *base,
+                          unsigned char *limit,
+                          unsigned int *hook_entries,
+                          unsigned int *underflows,
+                          unsigned int *i,
+                          const char *name)
 {
 {
-       int ret;
+       struct ipt_entry_target *t;
+       struct ipt_target *target;
+       unsigned int entry_offset;
+       int ret, off, h, j;
 
 
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
+       duprintf("check_compat_entry_size_and_hooks %p\n", e);
+       if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0
+           || (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
+               duprintf("Bad offset %p, limit = %p\n", e, limit);
+               return -EINVAL;
+       }
 
 
-       switch (cmd) {
-       case IPT_SO_GET_INFO: {
-               char name[IPT_TABLE_MAXNAMELEN];
-               struct ipt_table *t;
+       if (e->next_offset < sizeof(struct compat_ipt_entry) +
+                       sizeof(struct compat_xt_entry_target)) {
+               duprintf("checking: element %p size %u\n",
+                        e, e->next_offset);
+               return -EINVAL;
+       }
 
 
-               if (*len != sizeof(struct ipt_getinfo)) {
-                       duprintf("length %u != %u\n", *len,
-                                sizeof(struct ipt_getinfo));
-                       ret = -EINVAL;
-                       break;
-               }
+       ret = check_entry(e, name);
+       if (ret)
+               return ret;
 
 
-               if (copy_from_user(name, user, sizeof(name)) != 0) {
-                       ret = -EFAULT;
-                       break;
-               }
-               name[IPT_TABLE_MAXNAMELEN-1] = '\0';
-               t = ipt_find_table_lock(name, &ret, &ipt_mutex);
-               if (t) {
-                       struct ipt_getinfo info;
-
-                       info.valid_hooks = t->valid_hooks;
-                       memcpy(info.hook_entry, t->private->hook_entry,
-                              sizeof(info.hook_entry));
-                       memcpy(info.underflow, t->private->underflow,
-                              sizeof(info.underflow));
-                       info.num_entries = t->private->number;
-                       info.size = t->private->size;
-                       strcpy(info.name, name);
-
-                       if (copy_to_user(user, &info, *len) != 0)
-                               ret = -EFAULT;
-                       else
-                               ret = 0;
+       off = 0;
+       entry_offset = (void *)e - (void *)base;
+       j = 0;
+       ret = IPT_MATCH_ITERATE(e, compat_check_calc_match, name, &e->ip,
+                       e->comefrom, &off, &j);
+       if (ret != 0)
+               goto cleanup_matches;
 
 
-                       up(&ipt_mutex);
-               }
+       t = ipt_get_target(e);
+       target = try_then_request_module(xt_find_target(AF_INET,
+                                                    t->u.user.name,
+                                                    t->u.user.revision),
+                                        "ipt_%s", t->u.user.name);
+       if (IS_ERR(target) || !target) {
+               duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
+                                                       t->u.user.name);
+               ret = target ? PTR_ERR(target) : -ENOENT;
+               goto cleanup_matches;
        }
        }
-       break;
+       t->u.kernel.target = target;
 
 
-       case IPT_SO_GET_ENTRIES: {
-               struct ipt_get_entries get;
+       off += xt_compat_target_offset(target);
+       *size += off;
+       ret = compat_add_offset(entry_offset, off);
+       if (ret)
+               goto out;
 
 
-               if (*len < sizeof(get)) {
-                       duprintf("get_entries: %u < %u\n", *len, sizeof(get));
-                       ret = -EINVAL;
-               } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
-                       ret = -EFAULT;
-               } else if (*len != sizeof(struct ipt_get_entries) + get.size) {
-                       duprintf("get_entries: %u != %u\n", *len,
-                                sizeof(struct ipt_get_entries) + get.size);
-                       ret = -EINVAL;
-               } else
-                       ret = get_entries(&get, user);
-               break;
+       /* Check hooks & underflows */
+       for (h = 0; h < NF_IP_NUMHOOKS; h++) {
+               if ((unsigned char *)e - base == hook_entries[h])
+                       newinfo->hook_entry[h] = hook_entries[h];
+               if ((unsigned char *)e - base == underflows[h])
+                       newinfo->underflow[h] = underflows[h];
        }
 
        }
 
-       default:
-               duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
-               ret = -EINVAL;
-       }
+       /* Clear counters and comefrom */
+       e->counters = ((struct ipt_counters) { 0, 0 });
+       e->comefrom = 0;
+
+       (*i)++;
+       return 0;
 
 
+out:
+       module_put(t->u.kernel.target->me);
+cleanup_matches:
+       IPT_MATCH_ITERATE(e, cleanup_match, &j);
        return ret;
 }
 
        return ret;
 }
 
-/* Registration hooks for targets. */
-int
-ipt_register_target(struct ipt_target *target)
+static inline int compat_copy_match_from_user(struct ipt_entry_match *m,
+       void **dstptr, compat_uint_t *size, const char *name,
+       const struct ipt_ip *ip, unsigned int hookmask)
 {
 {
-       int ret;
+       xt_compat_match_from_user(m, dstptr, size);
+       return 0;
+}
 
 
-       ret = down_interruptible(&ipt_mutex);
-       if (ret != 0)
+static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
+       unsigned int *size, const char *name,
+       struct xt_table_info *newinfo, unsigned char *base)
+{
+       struct ipt_entry_target *t;
+       struct ipt_target *target;
+       struct ipt_entry *de;
+       unsigned int origsize;
+       int ret, h;
+
+       ret = 0;
+       origsize = *size;
+       de = (struct ipt_entry *)*dstptr;
+       memcpy(de, e, sizeof(struct ipt_entry));
+
+       *dstptr += sizeof(struct compat_ipt_entry);
+       ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size,
+                       name, &de->ip, de->comefrom);
+       if (ret)
                return ret;
                return ret;
+       de->target_offset = e->target_offset - (origsize - *size);
+       t = ipt_get_target(e);
+       target = t->u.kernel.target;
+       xt_compat_target_from_user(t, dstptr, size);
 
 
-       if (!list_named_insert(&ipt_target, target)) {
-               duprintf("ipt_register_target: `%s' already in list!\n",
-                        target->name);
-               ret = -EINVAL;
+       de->next_offset = e->next_offset - (origsize - *size);
+       for (h = 0; h < NF_IP_NUMHOOKS; h++) {
+               if ((unsigned char *)de - base < newinfo->hook_entry[h])
+                       newinfo->hook_entry[h] -= origsize - *size;
+               if ((unsigned char *)de - base < newinfo->underflow[h])
+                       newinfo->underflow[h] -= origsize - *size;
        }
        }
-       up(&ipt_mutex);
        return ret;
 }
 
        return ret;
 }
 
-void
-ipt_unregister_target(struct ipt_target *target)
+static inline int compat_check_entry(struct ipt_entry *e, const char *name)
 {
 {
-       down(&ipt_mutex);
-       LIST_DELETE(&ipt_target, target);
-       up(&ipt_mutex);
+       int ret;
+
+       ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom);
+       if (ret)
+               return ret;
+
+       return check_target(e, name);
 }
 
 }
 
-int
-ipt_register_match(struct ipt_match *match)
+static int
+translate_compat_table(const char *name,
+               unsigned int valid_hooks,
+               struct xt_table_info **pinfo,
+               void **pentry0,
+               unsigned int total_size,
+               unsigned int number,
+               unsigned int *hook_entries,
+               unsigned int *underflows)
 {
 {
+       unsigned int i, j;
+       struct xt_table_info *newinfo, *info;
+       void *pos, *entry0, *entry1;
+       unsigned int size;
        int ret;
 
        int ret;
 
-       ret = down_interruptible(&ipt_mutex);
+       info = *pinfo;
+       entry0 = *pentry0;
+       size = total_size;
+       info->number = number;
+
+       /* Init all hooks to impossible value. */
+       for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+               info->hook_entry[i] = 0xFFFFFFFF;
+               info->underflow[i] = 0xFFFFFFFF;
+       }
+
+       duprintf("translate_compat_table: size %u\n", info->size);
+       j = 0;
+       xt_compat_lock(AF_INET);
+       /* Walk through entries, checking offsets. */
+       ret = IPT_ENTRY_ITERATE(entry0, total_size,
+                               check_compat_entry_size_and_hooks,
+                               info, &size, entry0,
+                               entry0 + total_size,
+                               hook_entries, underflows, &j, name);
        if (ret != 0)
        if (ret != 0)
-               return ret;
+               goto out_unlock;
 
 
-       if (!list_named_insert(&ipt_match, match)) {
-               duprintf("ipt_register_match: `%s' already in list!\n",
-                        match->name);
-               ret = -EINVAL;
+       ret = -EINVAL;
+       if (j != number) {
+               duprintf("translate_compat_table: %u not %u entries\n",
+                        j, number);
+               goto out_unlock;
        }
        }
-       up(&ipt_mutex);
 
 
-       return ret;
-}
+       /* Check hooks all assigned */
+       for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+               /* Only hooks which are valid */
+               if (!(valid_hooks & (1 << i)))
+                       continue;
+               if (info->hook_entry[i] == 0xFFFFFFFF) {
+                       duprintf("Invalid hook entry %u %u\n",
+                                i, hook_entries[i]);
+                       goto out_unlock;
+               }
+               if (info->underflow[i] == 0xFFFFFFFF) {
+                       duprintf("Invalid underflow %u %u\n",
+                                i, underflows[i]);
+                       goto out_unlock;
+               }
+       }
 
 
-void
-ipt_unregister_match(struct ipt_match *match)
-{
-       down(&ipt_mutex);
-       LIST_DELETE(&ipt_match, match);
-       up(&ipt_mutex);
+       ret = -ENOMEM;
+       newinfo = xt_alloc_table_info(size);
+       if (!newinfo)
+               goto out_unlock;
+
+       newinfo->number = number;
+       for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+               newinfo->hook_entry[i] = info->hook_entry[i];
+               newinfo->underflow[i] = info->underflow[i];
+       }
+       entry1 = newinfo->entries[raw_smp_processor_id()];
+       pos = entry1;
+       size =  total_size;
+       ret = IPT_ENTRY_ITERATE(entry0, total_size,
+                       compat_copy_entry_from_user, &pos, &size,
+                       name, newinfo, entry1);
+       compat_flush_offsets();
+       xt_compat_unlock(AF_INET);
+       if (ret)
+               goto free_newinfo;
+
+       ret = -ELOOP;
+       if (!mark_source_chains(newinfo, valid_hooks, entry1))
+               goto free_newinfo;
+
+       ret = IPT_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
+                                                                       name);
+       if (ret)
+               goto free_newinfo;
+
+       /* And one copy for every other CPU */
+       for_each_possible_cpu(i)
+               if (newinfo->entries[i] && newinfo->entries[i] != entry1)
+                       memcpy(newinfo->entries[i], entry1, newinfo->size);
+
+       *pinfo = newinfo;
+       *pentry0 = entry1;
+       xt_free_table_info(info);
+       return 0;
+
+free_newinfo:
+       xt_free_table_info(newinfo);
+out:
+       IPT_ENTRY_ITERATE(entry0, total_size, cleanup_entry, &j);
+       return ret;
+out_unlock:
+       compat_flush_offsets();
+       xt_compat_unlock(AF_INET);
+       goto out;
 }
 
 }
 
-int ipt_register_table(struct ipt_table *table)
+static int
+compat_do_replace(void __user *user, unsigned int len)
 {
        int ret;
 {
        int ret;
-       struct ipt_table_info *newinfo;
-       static struct ipt_table_info bootstrap
-               = { 0, 0, 0, { 0 }, { 0 }, { } };
+       struct compat_ipt_replace tmp;
+       struct xt_table_info *newinfo;
+       void *loc_cpu_entry;
 
 
-       newinfo = vmalloc(sizeof(struct ipt_table_info)
-                         + SMP_ALIGN(table->table->size) * NR_CPUS);
-       if (!newinfo)
-               return -ENOMEM;
+       if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+               return -EFAULT;
 
 
-       memcpy(newinfo->entries, table->table->entries, table->table->size);
+       /* Hack: Causes ipchains to give correct error msg --RR */
+       if (len != sizeof(tmp) + tmp.size)
+               return -ENOPROTOOPT;
 
 
-       ret = translate_table(table->name, table->valid_hooks,
-                             newinfo, table->table->size,
-                             table->table->num_entries,
-                             table->table->hook_entry,
-                             table->table->underflow);
-       if (ret != 0) {
-               vfree(newinfo);
-               return ret;
-       }
+       /* overflow check */
+       if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
+                       SMP_CACHE_BYTES)
+               return -ENOMEM;
+       if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
+               return -ENOMEM;
 
 
-       ret = down_interruptible(&ipt_mutex);
-       if (ret != 0) {
-               vfree(newinfo);
-               return ret;
-       }
+       newinfo = xt_alloc_table_info(tmp.size);
+       if (!newinfo)
+               return -ENOMEM;
 
 
-       /* Don't autoload: we'd eat our tail... */
-       if (list_named_find(&ipt_tables, table->name)) {
-               ret = -EEXIST;
-               goto free_unlock;
+       /* choose the copy that is our node/cpu */
+       loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+       if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
+                          tmp.size) != 0) {
+               ret = -EFAULT;
+               goto free_newinfo;
        }
 
        }
 
-       /* Simplifies replace_table code. */
-       table->private = &bootstrap;
-       if (!replace_table(table, 0, newinfo, &ret))
-               goto free_unlock;
+       ret = translate_compat_table(tmp.name, tmp.valid_hooks,
+                             &newinfo, &loc_cpu_entry, tmp.size,
+                             tmp.num_entries, tmp.hook_entry, tmp.underflow);
+       if (ret != 0)
+               goto free_newinfo;
 
 
-       duprintf("table->private->number = %u\n",
-                table->private->number);
-       
-       /* save number of initial entries */
-       table->private->initial_entries = table->private->number;
+       duprintf("compat_do_replace: Translated table\n");
 
 
-       table->lock = RW_LOCK_UNLOCKED;
-       list_prepend(&ipt_tables, table);
+       ret = __do_replace(tmp.name, tmp.valid_hooks,
+                             newinfo, tmp.num_counters,
+                             compat_ptr(tmp.counters));
+       if (ret)
+               goto free_newinfo_untrans;
+       return 0;
 
 
- unlock:
-       up(&ipt_mutex);
+ free_newinfo_untrans:
+       IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
+ free_newinfo:
+       xt_free_table_info(newinfo);
        return ret;
        return ret;
-
- free_unlock:
-       vfree(newinfo);
-       goto unlock;
 }
 
 }
 
-void ipt_unregister_table(struct ipt_table *table)
+static int
+compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
+               unsigned int len)
 {
 {
-       down(&ipt_mutex);
-       LIST_DELETE(&ipt_tables, table);
-       up(&ipt_mutex);
+       int ret;
 
 
-       /* Decrease module usage counts and free resources */
-       IPT_ENTRY_ITERATE(table->private->entries, table->private->size,
-                         cleanup_entry, NULL);
-       vfree(table->private);
-}
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
 
 
-/* Returns 1 if the port is matched by the range, 0 otherwise */
-static inline int
-port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
-{
-       int ret;
+       switch (cmd) {
+       case IPT_SO_SET_REPLACE:
+               ret = compat_do_replace(user, len);
+               break;
+
+       case IPT_SO_SET_ADD_COUNTERS:
+               ret = do_add_counters(user, len, 1);
+               break;
+
+       default:
+               duprintf("do_ipt_set_ctl:  unknown request %i\n", cmd);
+               ret = -EINVAL;
+       }
 
 
-       ret = (port >= min && port <= max) ^ invert;
        return ret;
 }
 
        return ret;
 }
 
-static int
-tcp_find_option(u_int8_t option,
-               const struct sk_buff *skb,
-               unsigned int optlen,
-               int invert,
-               int *hotdrop)
+struct compat_ipt_get_entries
 {
 {
-       /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
-       u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
-       unsigned int i;
+       char name[IPT_TABLE_MAXNAMELEN];
+       compat_uint_t size;
+       struct compat_ipt_entry entrytable[0];
+};
 
 
-       duprintf("tcp_match: finding option\n");
+static int compat_copy_entries_to_user(unsigned int total_size,
+                    struct ipt_table *table, void __user *userptr)
+{
+       unsigned int off, num;
+       struct compat_ipt_entry e;
+       struct xt_counters *counters;
+       struct xt_table_info *private = table->private;
+       void __user *pos;
+       unsigned int size;
+       int ret = 0;
+       void *loc_cpu_entry;
+
+       counters = alloc_counters(table);
+       if (IS_ERR(counters))
+               return PTR_ERR(counters);
+
+       /* choose the copy that is on our node/cpu, ...
+        * This choice is lazy (because current thread is
+        * allowed to migrate to another cpu)
+        */
+       loc_cpu_entry = private->entries[raw_smp_processor_id()];
+       pos = userptr;
+       size = total_size;
+       ret = IPT_ENTRY_ITERATE(loc_cpu_entry, total_size,
+                       compat_copy_entry_to_user, &pos, &size);
+       if (ret)
+               goto free_counters;
 
 
-       if (!optlen)
-               return invert;
+       /* ... then go back and fix counters and names */
+       for (off = 0, num = 0; off < size; off += e.next_offset, num++) {
+               unsigned int i;
+               struct ipt_entry_match m;
+               struct ipt_entry_target t;
 
 
-       /* If we don't have the whole header, drop packet. */
-       op = skb_header_pointer(skb,
-                               skb->nh.iph->ihl*4 + sizeof(struct tcphdr),
-                               optlen, _opt);
-       if (op == NULL) {
-               *hotdrop = 1;
-               return 0;
-       }
+               ret = -EFAULT;
+               if (copy_from_user(&e, userptr + off,
+                                       sizeof(struct compat_ipt_entry)))
+                       goto free_counters;
+               if (copy_to_user(userptr + off +
+                       offsetof(struct compat_ipt_entry, counters),
+                        &counters[num], sizeof(counters[num])))
+                       goto free_counters;
 
 
-       for (i = 0; i < optlen; ) {
-               if (op[i] == option) return !invert;
-               if (op[i] < 2) i++;
-               else i += op[i+1]?:1;
-       }
+               for (i = sizeof(struct compat_ipt_entry);
+                               i < e.target_offset; i += m.u.match_size) {
+                       if (copy_from_user(&m, userptr + off + i,
+                                       sizeof(struct ipt_entry_match)))
+                               goto free_counters;
+                       if (copy_to_user(userptr + off + i +
+                               offsetof(struct ipt_entry_match, u.user.name),
+                               m.u.kernel.match->name,
+                               strlen(m.u.kernel.match->name) + 1))
+                               goto free_counters;
+               }
 
 
-       return invert;
+               if (copy_from_user(&t, userptr + off + e.target_offset,
+                                       sizeof(struct ipt_entry_target)))
+                       goto free_counters;
+               if (copy_to_user(userptr + off + e.target_offset +
+                       offsetof(struct ipt_entry_target, u.user.name),
+                       t.u.kernel.target->name,
+                       strlen(t.u.kernel.target->name) + 1))
+                       goto free_counters;
+       }
+       ret = 0;
+free_counters:
+       vfree(counters);
+       return ret;
 }
 
 static int
 }
 
 static int
-tcp_match(const struct sk_buff *skb,
-         const struct net_device *in,
-         const struct net_device *out,
-         const void *matchinfo,
-         int offset,
-         int *hotdrop)
+compat_get_entries(struct compat_ipt_get_entries __user *uptr, int *len)
 {
 {
-       struct tcphdr _tcph, *th;
-       const struct ipt_tcp *tcpinfo = matchinfo;
-
-       if (offset) {
-               /* To quote Alan:
-
-                  Don't allow a fragment of TCP 8 bytes in. Nobody normal
-                  causes this. Its a cracker trying to break in by doing a
-                  flag overwrite to pass the direction checks.
-               */
-               if (offset == 1) {
-                       duprintf("Dropping evil TCP offset=1 frag.\n");
-                       *hotdrop = 1;
-               }
-               /* Must not be a fragment. */
-               return 0;
+       int ret;
+       struct compat_ipt_get_entries get;
+       struct ipt_table *t;
+
+
+       if (*len < sizeof(get)) {
+               duprintf("compat_get_entries: %u < %u\n",
+                               *len, (unsigned int)sizeof(get));
+               return -EINVAL;
        }
 
        }
 
-#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
+       if (copy_from_user(&get, uptr, sizeof(get)) != 0)
+               return -EFAULT;
 
 
-       th = skb_header_pointer(skb, skb->nh.iph->ihl*4,
-                               sizeof(_tcph), &_tcph);
-       if (th == NULL) {
-               /* We've been asked to examine this packet, and we
-                  can't.  Hence, no choice but to drop. */
-               duprintf("Dropping evil TCP offset=0 tinygram.\n");
-               *hotdrop = 1;
-               return 0;
+       if (*len != sizeof(struct compat_ipt_get_entries) + get.size) {
+               duprintf("compat_get_entries: %u != %u\n", *len,
+                       (unsigned int)(sizeof(struct compat_ipt_get_entries) +
+                       get.size));
+               return -EINVAL;
        }
 
        }
 
-       if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
-                       ntohs(th->source),
-                       !!(tcpinfo->invflags & IPT_TCP_INV_SRCPT)))
-               return 0;
-       if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
-                       ntohs(th->dest),
-                       !!(tcpinfo->invflags & IPT_TCP_INV_DSTPT)))
-               return 0;
-       if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
-                     == tcpinfo->flg_cmp,
-                     IPT_TCP_INV_FLAGS))
-               return 0;
-       if (tcpinfo->option) {
-               if (th->doff * 4 < sizeof(_tcph)) {
-                       *hotdrop = 1;
-                       return 0;
+       xt_compat_lock(AF_INET);
+       t = xt_find_table_lock(AF_INET, get.name);
+       if (t && !IS_ERR(t)) {
+               struct xt_table_info *private = t->private;
+               struct xt_table_info info;
+               duprintf("t->private->number = %u\n",
+                        private->number);
+               ret = compat_table_info(private, &info);
+               if (!ret && get.size == info.size) {
+                       ret = compat_copy_entries_to_user(private->size,
+                                                  t, uptr->entrytable);
+               } else if (!ret) {
+                       duprintf("compat_get_entries: I've got %u not %u!\n",
+                                private->size,
+                                get.size);
+                       ret = -EINVAL;
                }
                }
-               if (!tcp_find_option(tcpinfo->option, skb,
-                                    th->doff*4 - sizeof(_tcph),
-                                    tcpinfo->invflags & IPT_TCP_INV_OPTION,
-                                    hotdrop))
-                       return 0;
-       }
-       return 1;
+               compat_flush_offsets();
+               module_put(t->me);
+               xt_table_unlock(t);
+       } else
+               ret = t ? PTR_ERR(t) : -ENOENT;
+
+       xt_compat_unlock(AF_INET);
+       return ret;
 }
 
 }
 
-/* Called when user tries to insert an entry of this type. */
+static int do_ipt_get_ctl(struct sock *, int, void __user *, int *);
+
 static int
 static int
-tcp_checkentry(const char *tablename,
-              const struct ipt_ip *ip,
-              void *matchinfo,
-              unsigned int matchsize,
-              unsigned int hook_mask)
+compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
 {
 {
-       const struct ipt_tcp *tcpinfo = matchinfo;
+       int ret;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
 
 
-       /* Must specify proto == TCP, and no unknown invflags */
-       return ip->proto == IPPROTO_TCP
-               && !(ip->invflags & IPT_INV_PROTO)
-               && matchsize == IPT_ALIGN(sizeof(struct ipt_tcp))
-               && !(tcpinfo->invflags & ~IPT_TCP_INV_MASK);
+       switch (cmd) {
+       case IPT_SO_GET_INFO:
+               ret = get_info(user, len, 1);
+               break;
+       case IPT_SO_GET_ENTRIES:
+               ret = compat_get_entries(user, len);
+               break;
+       default:
+               ret = do_ipt_get_ctl(sk, cmd, user, len);
+       }
+       return ret;
 }
 }
+#endif
 
 static int
 
 static int
-udp_match(const struct sk_buff *skb,
-         const struct net_device *in,
-         const struct net_device *out,
-         const void *matchinfo,
-         int offset,
-         int *hotdrop)
+do_ipt_set_ctl(struct sock *sk,        int cmd, void __user *user, unsigned int len)
 {
 {
-       struct udphdr _udph, *uh;
-       const struct ipt_udp *udpinfo = matchinfo;
+       int ret;
 
 
-       /* Must not be a fragment. */
-       if (offset)
-               return 0;
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
 
 
-       uh = skb_header_pointer(skb, skb->nh.iph->ihl*4,
-                               sizeof(_udph), &_udph);
-       if (uh == NULL) {
-               /* We've been asked to examine this packet, and we
-                  can't.  Hence, no choice but to drop. */
-               duprintf("Dropping evil UDP tinygram.\n");
-               *hotdrop = 1;
-               return 0;
+       switch (cmd) {
+       case IPT_SO_SET_REPLACE:
+               ret = do_replace(user, len);
+               break;
+
+       case IPT_SO_SET_ADD_COUNTERS:
+               ret = do_add_counters(user, len, 0);
+               break;
+
+       default:
+               duprintf("do_ipt_set_ctl:  unknown request %i\n", cmd);
+               ret = -EINVAL;
        }
 
        }
 
-       return port_match(udpinfo->spts[0], udpinfo->spts[1],
-                         ntohs(uh->source),
-                         !!(udpinfo->invflags & IPT_UDP_INV_SRCPT))
-               && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
-                             ntohs(uh->dest),
-                             !!(udpinfo->invflags & IPT_UDP_INV_DSTPT));
+       return ret;
 }
 
 }
 
-/* Called when user tries to insert an entry of this type. */
 static int
 static int
-udp_checkentry(const char *tablename,
-              const struct ipt_ip *ip,
-              void *matchinfo,
-              unsigned int matchinfosize,
-              unsigned int hook_mask)
+do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
 {
 {
-       const struct ipt_udp *udpinfo = matchinfo;
+       int ret;
 
 
-       /* Must specify proto == UDP, and no unknown invflags */
-       if (ip->proto != IPPROTO_UDP || (ip->invflags & IPT_INV_PROTO)) {
-               duprintf("ipt_udp: Protocol %u != %u\n", ip->proto,
-                        IPPROTO_UDP);
-               return 0;
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       switch (cmd) {
+       case IPT_SO_GET_INFO:
+               ret = get_info(user, len, 0);
+               break;
+
+       case IPT_SO_GET_ENTRIES:
+               ret = get_entries(user, len);
+               break;
+
+       case IPT_SO_GET_REVISION_MATCH:
+       case IPT_SO_GET_REVISION_TARGET: {
+               struct ipt_get_revision rev;
+               int target;
+
+               if (*len != sizeof(rev)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               if (cmd == IPT_SO_GET_REVISION_TARGET)
+                       target = 1;
+               else
+                       target = 0;
+
+               try_then_request_module(xt_find_revision(AF_INET, rev.name,
+                                                        rev.revision,
+                                                        target, &ret),
+                                       "ipt_%s", rev.name);
+               break;
        }
        }
-       if (matchinfosize != IPT_ALIGN(sizeof(struct ipt_udp))) {
-               duprintf("ipt_udp: matchsize %u != %u\n",
-                        matchinfosize, IPT_ALIGN(sizeof(struct ipt_udp)));
-               return 0;
+
+       default:
+               duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
+               ret = -EINVAL;
        }
        }
-       if (udpinfo->invflags & ~IPT_UDP_INV_MASK) {
-               duprintf("ipt_udp: unknown flags %X\n",
-                        udpinfo->invflags);
-               return 0;
+
+       return ret;
+}
+
+int ipt_register_table(struct xt_table *table, const struct ipt_replace *repl)
+{
+       int ret;
+       struct xt_table_info *newinfo;
+       static struct xt_table_info bootstrap
+               = { 0, 0, 0, { 0 }, { 0 }, { } };
+       void *loc_cpu_entry;
+
+       newinfo = xt_alloc_table_info(repl->size);
+       if (!newinfo)
+               return -ENOMEM;
+
+       /* choose the copy on our node/cpu
+        * but dont care of preemption
+        */
+       loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
+       memcpy(loc_cpu_entry, repl->entries, repl->size);
+
+       ret = translate_table(table->name, table->valid_hooks,
+                             newinfo, loc_cpu_entry, repl->size,
+                             repl->num_entries,
+                             repl->hook_entry,
+                             repl->underflow);
+       if (ret != 0) {
+               xt_free_table_info(newinfo);
+               return ret;
        }
 
        }
 
-       return 1;
+       ret = xt_register_table(table, &bootstrap, newinfo);
+       if (ret != 0) {
+               xt_free_table_info(newinfo);
+               return ret;
+       }
+
+       return 0;
+}
+
+void ipt_unregister_table(struct ipt_table *table)
+{
+       struct xt_table_info *private;
+       void *loc_cpu_entry;
+
+       private = xt_unregister_table(table);
+
+       /* Decrease module usage counts and free resources */
+       loc_cpu_entry = private->entries[raw_smp_processor_id()];
+       IPT_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
+       xt_free_table_info(private);
 }
 
 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
 }
 
 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
@@ -1644,8 +2079,10 @@ static int
 icmp_match(const struct sk_buff *skb,
           const struct net_device *in,
           const struct net_device *out,
 icmp_match(const struct sk_buff *skb,
           const struct net_device *in,
           const struct net_device *out,
+          const struct xt_match *match,
           const void *matchinfo,
           int offset,
           const void *matchinfo,
           int offset,
+          unsigned int protoff,
           int *hotdrop)
 {
        struct icmphdr _icmph, *ic;
           int *hotdrop)
 {
        struct icmphdr _icmph, *ic;
@@ -1655,8 +2092,7 @@ icmp_match(const struct sk_buff *skb,
        if (offset)
                return 0;
 
        if (offset)
                return 0;
 
-       ic = skb_header_pointer(skb, skb->nh.iph->ihl*4,
-                               sizeof(_icmph), &_icmph);
+       ic = skb_header_pointer(skb, protoff, sizeof(_icmph), &_icmph);
        if (ic == NULL) {
                /* We've been asked to examine this packet, and we
                 * can't.  Hence, no choice but to drop.
        if (ic == NULL) {
                /* We've been asked to examine this packet, and we
                 * can't.  Hence, no choice but to drop.
@@ -1676,28 +2112,34 @@ icmp_match(const struct sk_buff *skb,
 /* Called when user tries to insert an entry of this type. */
 static int
 icmp_checkentry(const char *tablename,
 /* Called when user tries to insert an entry of this type. */
 static int
 icmp_checkentry(const char *tablename,
-          const struct ipt_ip *ip,
+          const void *info,
+          const struct xt_match *match,
           void *matchinfo,
           void *matchinfo,
-          unsigned int matchsize,
           unsigned int hook_mask)
 {
        const struct ipt_icmp *icmpinfo = matchinfo;
 
           unsigned int hook_mask)
 {
        const struct ipt_icmp *icmpinfo = matchinfo;
 
-       /* Must specify proto == ICMP, and no unknown invflags */
-       return ip->proto == IPPROTO_ICMP
-               && !(ip->invflags & IPT_INV_PROTO)
-               && matchsize == IPT_ALIGN(sizeof(struct ipt_icmp))
-               && !(icmpinfo->invflags & ~IPT_ICMP_INV);
+       /* Must specify no unknown invflags */
+       return !(icmpinfo->invflags & ~IPT_ICMP_INV);
 }
 
 /* The built-in targets: standard (NULL) and error. */
 static struct ipt_target ipt_standard_target = {
        .name           = IPT_STANDARD_TARGET,
 }
 
 /* The built-in targets: standard (NULL) and error. */
 static struct ipt_target ipt_standard_target = {
        .name           = IPT_STANDARD_TARGET,
+       .targetsize     = sizeof(int),
+       .family         = AF_INET,
+#ifdef CONFIG_COMPAT
+       .compatsize     = sizeof(compat_int_t),
+       .compat_from_user = compat_standard_from_user,
+       .compat_to_user = compat_standard_to_user,
+#endif
 };
 
 static struct ipt_target ipt_error_target = {
        .name           = IPT_ERROR_TARGET,
        .target         = ipt_error,
 };
 
 static struct ipt_target ipt_error_target = {
        .name           = IPT_ERROR_TARGET,
        .target         = ipt_error,
+       .targetsize     = IPT_FUNCTION_MAXNAMELEN,
+       .family         = AF_INET,
 };
 
 static struct nf_sockopt_ops ipt_sockopts = {
 };
 
 static struct nf_sockopt_ops ipt_sockopts = {
@@ -1705,179 +2147,78 @@ static struct nf_sockopt_ops ipt_sockopts = {
        .set_optmin     = IPT_BASE_CTL,
        .set_optmax     = IPT_SO_SET_MAX+1,
        .set            = do_ipt_set_ctl,
        .set_optmin     = IPT_BASE_CTL,
        .set_optmax     = IPT_SO_SET_MAX+1,
        .set            = do_ipt_set_ctl,
+#ifdef CONFIG_COMPAT
+       .compat_set     = compat_do_ipt_set_ctl,
+#endif
        .get_optmin     = IPT_BASE_CTL,
        .get_optmax     = IPT_SO_GET_MAX+1,
        .get            = do_ipt_get_ctl,
        .get_optmin     = IPT_BASE_CTL,
        .get_optmax     = IPT_SO_GET_MAX+1,
        .get            = do_ipt_get_ctl,
-};
-
-static struct ipt_match tcp_matchstruct = {
-       .name           = "tcp",
-       .match          = &tcp_match,
-       .checkentry     = &tcp_checkentry,
-};
-
-static struct ipt_match udp_matchstruct = {
-       .name           = "udp",
-       .match          = &udp_match,
-       .checkentry     = &udp_checkentry,
+#ifdef CONFIG_COMPAT
+       .compat_get     = compat_do_ipt_get_ctl,
+#endif
 };
 
 static struct ipt_match icmp_matchstruct = {
        .name           = "icmp",
 };
 
 static struct ipt_match icmp_matchstruct = {
        .name           = "icmp",
-       .match          = &icmp_match,
-       .checkentry     = &icmp_checkentry,
+       .match          = icmp_match,
+       .matchsize      = sizeof(struct ipt_icmp),
+       .proto          = IPPROTO_ICMP,
+       .family         = AF_INET,
+       .checkentry     = icmp_checkentry,
 };
 
 };
 
-#ifdef CONFIG_PROC_FS
-static inline int print_name(const char *i,
-                            off_t start_offset, char *buffer, int length,
-                            off_t *pos, unsigned int *count)
-{
-       if ((*count)++ >= start_offset) {
-               unsigned int namelen;
-
-               namelen = sprintf(buffer + *pos, "%s\n",
-                                 i + sizeof(struct list_head));
-               if (*pos + namelen > length) {
-                       /* Stop iterating */
-                       return 1;
-               }
-               *pos += namelen;
-       }
-       return 0;
-}
-
-static inline int print_target(const struct ipt_target *t,
-                               off_t start_offset, char *buffer, int length,
-                               off_t *pos, unsigned int *count)
-{
-       if (t == &ipt_standard_target || t == &ipt_error_target)
-               return 0;
-       return print_name((char *)t, start_offset, buffer, length, pos, count);
-}
-
-static int ipt_get_tables(char *buffer, char **start, off_t offset, int length)
-{
-       off_t pos = 0;
-       unsigned int count = 0;
-
-       if (down_interruptible(&ipt_mutex) != 0)
-               return 0;
-
-       LIST_FIND(&ipt_tables, print_name, void *,
-                 offset, buffer, length, &pos, &count);
-
-       up(&ipt_mutex);
-
-       /* `start' hack - see fs/proc/generic.c line ~105 */
-       *start=(char *)((unsigned long)count-offset);
-       return pos;
-}
-
-static int ipt_get_targets(char *buffer, char **start, off_t offset, int length)
-{
-       off_t pos = 0;
-       unsigned int count = 0;
-
-       if (down_interruptible(&ipt_mutex) != 0)
-               return 0;
-
-       LIST_FIND(&ipt_target, print_target, struct ipt_target *,
-                 offset, buffer, length, &pos, &count);
-       
-       up(&ipt_mutex);
-
-       *start = (char *)((unsigned long)count - offset);
-       return pos;
-}
-
-static int ipt_get_matches(char *buffer, char **start, off_t offset, int length)
-{
-       off_t pos = 0;
-       unsigned int count = 0;
-
-       if (down_interruptible(&ipt_mutex) != 0)
-               return 0;
-       
-       LIST_FIND(&ipt_match, print_name, void *,
-                 offset, buffer, length, &pos, &count);
-
-       up(&ipt_mutex);
-
-       *start = (char *)((unsigned long)count - offset);
-       return pos;
-}
-
-static struct { char *name; get_info_t *get_info; } ipt_proc_entry[] =
-{ { "ip_tables_names", ipt_get_tables },
-  { "ip_tables_targets", ipt_get_targets },
-  { "ip_tables_matches", ipt_get_matches },
-  { NULL, NULL} };
-#endif /*CONFIG_PROC_FS*/
-
-static int __init init(void)
+static int __init ip_tables_init(void)
 {
        int ret;
 
 {
        int ret;
 
+       ret = xt_proto_init(AF_INET);
+       if (ret < 0)
+               goto err1;
+
        /* Noone else will be downing sem now, so we won't sleep */
        /* Noone else will be downing sem now, so we won't sleep */
-       down(&ipt_mutex);
-       list_append(&ipt_target, &ipt_standard_target);
-       list_append(&ipt_target, &ipt_error_target);
-       list_append(&ipt_match, &tcp_matchstruct);
-       list_append(&ipt_match, &udp_matchstruct);
-       list_append(&ipt_match, &icmp_matchstruct);
-       up(&ipt_mutex);
+       ret = xt_register_target(&ipt_standard_target);
+       if (ret < 0)
+               goto err2;
+       ret = xt_register_target(&ipt_error_target);
+       if (ret < 0)
+               goto err3;
+       ret = xt_register_match(&icmp_matchstruct);
+       if (ret < 0)
+               goto err4;
 
        /* Register setsockopt */
        ret = nf_register_sockopt(&ipt_sockopts);
 
        /* Register setsockopt */
        ret = nf_register_sockopt(&ipt_sockopts);
-       if (ret < 0) {
-               duprintf("Unable to register sockopts.\n");
-               return ret;
-       }
+       if (ret < 0)
+               goto err5;
 
 
-#ifdef CONFIG_PROC_FS
-       {
-       struct proc_dir_entry *proc;
-       int i;
-
-       for (i = 0; ipt_proc_entry[i].name; i++) {
-               proc = proc_net_create(ipt_proc_entry[i].name, 0,
-                                      ipt_proc_entry[i].get_info);
-               if (!proc) {
-                       while (--i >= 0)
-                               proc_net_remove(ipt_proc_entry[i].name);
-                       nf_unregister_sockopt(&ipt_sockopts);
-                       return -ENOMEM;
-               }
-               proc->owner = THIS_MODULE;
-       }
-       }
-#endif
-
-       printk("ip_tables: (C) 2000-2002 Netfilter core team\n");
+       printk("ip_tables: (C) 2000-2006 Netfilter Core Team\n");
        return 0;
        return 0;
+
+err5:
+       xt_unregister_match(&icmp_matchstruct);
+err4:
+       xt_unregister_target(&ipt_error_target);
+err3:
+       xt_unregister_target(&ipt_standard_target);
+err2:
+       xt_proto_fini(AF_INET);
+err1:
+       return ret;
 }
 
 }
 
-static void __exit fini(void)
+static void __exit ip_tables_fini(void)
 {
        nf_unregister_sockopt(&ipt_sockopts);
 {
        nf_unregister_sockopt(&ipt_sockopts);
-#ifdef CONFIG_PROC_FS
-       {
-       int i;
-       for (i = 0; ipt_proc_entry[i].name; i++)
-               proc_net_remove(ipt_proc_entry[i].name);
-       }
-#endif
+
+       xt_unregister_match(&icmp_matchstruct);
+       xt_unregister_target(&ipt_error_target);
+       xt_unregister_target(&ipt_standard_target);
+
+       xt_proto_fini(AF_INET);
 }
 
 EXPORT_SYMBOL(ipt_register_table);
 EXPORT_SYMBOL(ipt_unregister_table);
 }
 
 EXPORT_SYMBOL(ipt_register_table);
 EXPORT_SYMBOL(ipt_unregister_table);
-EXPORT_SYMBOL(ipt_register_match);
-EXPORT_SYMBOL(ipt_unregister_match);
 EXPORT_SYMBOL(ipt_do_table);
 EXPORT_SYMBOL(ipt_do_table);
-EXPORT_SYMBOL(ipt_register_target);
-EXPORT_SYMBOL(ipt_unregister_target);
-EXPORT_SYMBOL(ipt_find_target_lock);
-
-module_init(init);
-module_exit(fini);
+module_init(ip_tables_init);
+module_exit(ip_tables_fini);