]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
complete libiptc rewrite. Time to load 10k rules goes down from 2.20 minutes to...
authorHarald Welte <laforge@gnumonks.org>
Sun, 29 Aug 2004 23:32:14 +0000 (23:32 +0000)
committerHarald Welte <laforge@gnumonks.org>
Sun, 29 Aug 2004 23:32:14 +0000 (23:32 +0000)
Makefile
libiptc/libip4tc.c
libiptc/libip6tc.c
libiptc/libiptc.c
libiptc/linux_list.h [new file with mode: 0644]
libiptc/linux_stddef.h [new file with mode: 0644]

index 82e554674ab19167eb07f63d69e4f494d9eac838..0c8fe50535a79013c4c6b553859f63984ea2b207 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -14,8 +14,8 @@ TOPLEVEL_INCLUDED=YES
 ifndef KERNEL_DIR
 KERNEL_DIR=/usr/src/linux
 endif
-IPTABLES_VERSION:=1.2.11
-OLD_IPTABLES_VERSION:=1.2.10
+IPTABLES_VERSION:=1.3.0
+OLD_IPTABLES_VERSION:=1.2.11
 
 PREFIX:=/usr/local
 LIBDIR:=$(PREFIX)/lib
index d394315fc35c9d4daebb5f5d9ff1773f8dc7648b..399d591030c250b30d213e3fe169a82626e3d377 100644 (file)
@@ -129,8 +129,8 @@ dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
        size_t i;
        STRUCT_ENTRY_TARGET *t;
 
-       printf("Entry %u (%lu):\n", entry2index(handle, e),
-              entry2offset(handle, e));
+       printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+              iptcb_entry2offset(handle, e));
        printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
               IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
        printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
@@ -238,6 +238,7 @@ is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
        return 1;
 }
 
+#if 0
 /***************************** DEBUGGING ********************************/
 static inline int
 unconditional(const struct ipt_ip *ip)
@@ -292,20 +293,20 @@ check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
                assert(t->verdict == -NF_DROP-1
                       || t->verdict == -NF_ACCEPT-1
                       || t->verdict == RETURN
-                      || t->verdict < (int)h->entries.size);
+                      || t->verdict < (int)h->entries->size);
 
                if (t->verdict >= 0) {
                        STRUCT_ENTRY *te = get_entry(h, t->verdict);
                        int idx;
 
-                       idx = entry2index(h, te);
+                       idx = iptcb_entry2index(h, te);
                        assert(strcmp(GET_TARGET(te)->u.user.name,
                                      IPT_ERROR_TARGET)
                               != 0);
                        assert(te != e);
 
                        /* Prior node must be error node, or this node. */
-                       assert(t->verdict == entry2offset(h, e)+e->next_offset
+                       assert(t->verdict == iptcb_entry2offset(h, e)+e->next_offset
                               || strcmp(GET_TARGET(index2entry(h, idx-1))
                                         ->u.user.name, IPT_ERROR_TARGET)
                               == 0);
@@ -518,3 +519,5 @@ do_check(TC_HANDLE_T h, unsigned int line)
                      ERROR_TARGET) == 0);
 }
 #endif /*IPTC_DEBUG*/
+
+#endif
index 6400ce3084ce57336abad130abbe7c21b0701ad9..c915ccbc06722f27c6e3e6888f381c6140e0b7e6 100644 (file)
@@ -136,8 +136,8 @@ dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
        int len;
        struct ip6t_entry_target *t;
        
-       printf("Entry %u (%lu):\n", entry2index(handle, e),
-              entry2offset(handle, e));
+       printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+              iptcb_entry2offset(handle, e));
        puts("SRC IP: ");
        inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
        puts(buf);
@@ -428,8 +428,8 @@ do_check(TC_HANDLE_T h, unsigned int line)
                assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
 
                /* Hooks and underflows must be valid entries */
-               entry2index(h, get_entry(h, h->info.hook_entry[i]));
-               entry2index(h, get_entry(h, h->info.underflow[i]));
+               iptcb_entry2index(h, get_entry(h, h->info.hook_entry[i]));
+               iptcb_entry2index(h, get_entry(h, h->info.underflow[i]));
        }
 
        assert(h->info.size
index b29111df7be435f6b58bc36a5ffd3bb93162d0fe..bf32617556df0ebf66360b4c1fc737944a9053ca 100644 (file)
@@ -1,4 +1,4 @@
-/* Library which manipulates firewall rules.  Version $Revision: 1.46 $ */
+/* Library which manipulates firewall rules.  Version $Revision: 1.39 $ */
 
 /* Architecture of firewall rules is as follows:
  *
@@ -10,7 +10,7 @@
 
 /* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
  * COPYING for details). 
- * (C) 2000-2003 by the Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2000-2004 by the Netfilter Core Team <coreteam@netfilter.org>
  *
  * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
  *     - Reimplementation of chain cache to use offsets instead of entries
  *     - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
  *       don't rebuild the chain cache after every operation, instead fix it
  *       up after a ruleset change.  
+ * 2004-Aug-18: Harald Welte <laforge@netfilter.org>:
+ *     - futher performance work: total reimplementation of libiptc.
+ *     - libiptc now has a real internal (linked-list) represntation of the
+ *       ruleset and a parser/compiler from/to this internal representation
+ *     - again sponsored by Astaro AG (http://www.astaro.com/)
  */
-
 #include <sys/types.h>
 #include <sys/socket.h>
 
+#include "linux_list.h"
+
+//#define IPTC_DEBUG2 1
+
+#ifdef IPTC_DEBUG2
+#include <fcntl.h>
+#define DEBUGP(x, args...)     fprintf(stderr, "%s: " x, __FUNCTION__, ## args)
+#define DEBUGP_C(x, args...)   fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#define DEBUGP_C(x, args...)
+#endif
+
 #ifndef IPT_LIB_DIR
 #define IPT_LIB_DIR "/usr/local/lib/iptables"
 #endif
@@ -49,6 +66,16 @@ static const char *hooknames[]
 #endif
 };
 
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct chain_head;
+struct rule_head;
+
 struct counter_map
 {
        enum {
@@ -60,49 +87,92 @@ struct counter_map
        unsigned int mappos;
 };
 
-/* Convenience structures */
-struct ipt_error_target
+enum iptcc_rule_type {
+       IPTCC_R_STANDARD,               /* standard target (ACCEPT, ...) */
+       IPTCC_R_MODULE,                 /* extension module (SNAT, ...) */
+       IPTCC_R_FALLTHROUGH,            /* fallthrough rule */
+       IPTCC_R_JUMP,                   /* jump to other chain */
+};
+
+struct rule_head
 {
-       STRUCT_ENTRY_TARGET t;
-       char error[TABLE_MAXNAMELEN];
+       struct list_head list;
+       struct chain_head *chain;
+       struct counter_map counter_map;
+
+       unsigned int index;             /* index (needed for counter_map) */
+       unsigned int offset;            /* offset in rule blob */
+
+       enum iptcc_rule_type type;
+       struct chain_head *jump;        /* jump target, if IPTCC_R_JUMP */
+
+       unsigned int size;              /* size of entry data */
+       STRUCT_ENTRY entry[0];
 };
 
-struct chain_cache
+struct chain_head
 {
+       struct list_head list;
        char name[TABLE_MAXNAMELEN];
-       /* This is the first rule in chain. */
-       unsigned int start_off;
-       /* Last rule in chain */
-       unsigned int end_off;
+       unsigned int hooknum;           /* hook number+1 if builtin */
+       unsigned int references;        /* how many jumps reference us */
+       int verdict;                    /* verdict if builtin */
+
+       STRUCT_COUNTERS counters;       /* per-chain counters */
+       struct counter_map counter_map;
+
+       unsigned int num_rules;         /* number of rules in list */
+       struct list_head rules;         /* list of rules */
+
+       unsigned int index;             /* index (needed for jump resolval) */
+       unsigned int head_offset;       /* offset in rule blob */
+       unsigned int foot_index;        /* index (needed for counter_map) */
+       unsigned int foot_offset;       /* offset in rule blob */
 };
 
 STRUCT_TC_HANDLE
 {
-       /* Have changes been made? */
-       int changed;
-       /* Size in here reflects original state. */
+       int changed;                     /* Have changes been made? */
+
+       struct list_head chains;
+       
+       struct chain_head *chain_iterator_cur;
+       struct rule_head *rule_iterator_cur;
+
        STRUCT_GETINFO info;
+       STRUCT_GET_ENTRIES *entries;
+};
 
-       struct counter_map *counter_map;
-       /* Array of hook names */
-       const char **hooknames;
+/* allocate a new chain head for the cache */
+static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
+{
+       struct chain_head *c = malloc(sizeof(*c));
+       if (!c)
+               return NULL;
+       memset(c, 0, sizeof(*c));
+
+       strncpy(c->name, name, TABLE_MAXNAMELEN);
+       c->hooknum = hooknum;
+       INIT_LIST_HEAD(&c->rules);
 
-       /* Cached position of chain heads (NULL = no cache). */
-       unsigned int cache_num_chains;
-       unsigned int cache_num_builtins;
-       struct chain_cache *cache_chain_heads;
+       return c;
+}
 
-       /* Chain iterator: current chain cache entry. */
-       struct chain_cache *cache_chain_iteration;
+/* allocate and initialize a new rule for the cache */
+static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size)
+{
+       struct rule_head *r = malloc(sizeof(*r)+size);
+       if (!r)
+               return NULL;
+       memset(r, 0, sizeof(*r));
 
-       /* Rule iterator: terminal rule */
-       STRUCT_ENTRY *cache_rule_end;
+       r->chain = c;
+       r->size = size;
 
-       /* Number in here reflects current state. */
-       unsigned int new_number;
-       STRUCT_GET_ENTRIES entries;
-};
+       return r;
+}
 
+/* notify us that the ruleset has been modified by the user */
 static void
 set_changed(TC_HANDLE_T h)
 {
@@ -116,8 +186,13 @@ static void do_check(TC_HANDLE_T h, unsigned int line);
 #define CHECK(h)
 #endif
 
+
+/**********************************************************************
+ * iptc blob utility functions (iptcb_*)
+ **********************************************************************/
+
 static inline int
-get_number(const STRUCT_ENTRY *i,
+iptcb_get_number(const STRUCT_ENTRY *i,
           const STRUCT_ENTRY *seek,
           unsigned int *pos)
 {
@@ -127,92 +202,536 @@ get_number(const STRUCT_ENTRY *i,
        return 0;
 }
 
+static inline int
+iptcb_get_entry_n(STRUCT_ENTRY *i,
+           unsigned int number,
+           unsigned int *pos,
+           STRUCT_ENTRY **pe)
+{
+       if (*pos == number) {
+               *pe = i;
+               return 1;
+       }
+       (*pos)++;
+       return 0;
+}
+
+static inline STRUCT_ENTRY *
+iptcb_get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset);
+}
+
 static unsigned int
-entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
+iptcb_entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
 {
        unsigned int pos = 0;
 
-       if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
-                         get_number, seek, &pos) == 0) {
+       if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+                         iptcb_get_number, seek, &pos) == 0) {
                fprintf(stderr, "ERROR: offset %u not an entry!\n",
-                       (unsigned int)((char *)seek - (char *)h->entries.entrytable));
+                       (unsigned int)((char *)seek - (char *)h->entries->entrytable));
                abort();
        }
        return pos;
 }
 
-static inline int
-get_entry_n(STRUCT_ENTRY *i,
-           unsigned int number,
-           unsigned int *pos,
-           STRUCT_ENTRY **pe)
+static inline STRUCT_ENTRY *
+iptcb_offset2entry(TC_HANDLE_T h, unsigned int offset)
 {
-       if (*pos == number) {
-               *pe = i;
+       return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset);
+}
+
+
+static inline unsigned long
+iptcb_entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries->entrytable;
+}
+
+static inline unsigned int
+iptcb_offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+       return iptcb_entry2index(h, iptcb_offset2entry(h, offset));
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((h->info.valid_hooks & (1 << i))
+                   && iptcb_get_entry(h, h->info.hook_entry[i]) == e)
+                       return i+1;
+       }
+       return 0;
+}
+
+
+/**********************************************************************
+ * iptc cache utility functions (iptcc_*)
+ **********************************************************************/
+
+/* Is the given chain builtin (1) or user-defined (0) */
+static unsigned int iptcc_is_builtin(struct chain_head *c)
+{
+       return (c->hooknum ? 1 : 0);
+}
+
+/* Get a specific rule within a chain */
+static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
+                                           unsigned int rulenum)
+{
+       struct rule_head *r;
+       unsigned int num = 0;
+
+       list_for_each_entry(r, &c->rules, list) {
+               num++;
+               if (num == rulenum)
+                       return r;
+       }
+       return NULL;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset)
+{
+       struct list_head *pos;
+
+       if (list_empty(&handle->chains))
+               return NULL;
+
+       list_for_each(pos, &handle->chains) {
+               struct chain_head *c = list_entry(pos, struct chain_head, list);
+               if (offset >= c->head_offset && offset <= c->foot_offset)
+                       return c;
+       }
+
+       return NULL;
+}
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_label(const char *name, TC_HANDLE_T handle)
+{
+       struct list_head *pos;
+
+       if (list_empty(&handle->chains))
+               return NULL;
+
+       list_for_each(pos, &handle->chains) {
+               struct chain_head *c = list_entry(pos, struct chain_head, list);
+               if (!strcmp(c->name, name))
+                       return c;
+       }
+
+       return NULL;
+}
+
+/* called when rule is to be removed from cache */
+static void iptcc_delete_rule(struct rule_head *r)
+{
+       DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
+       /* clean up reference count of called chain */
+       if (r->type == IPTCC_R_JUMP
+           && r->jump)
+               r->jump->references--;
+
+       list_del(&r->list);
+       free(r);
+}
+
+
+/**********************************************************************
+ * RULESET PARSER (blob -> cache)
+ **********************************************************************/
+
+static int alphasort(const void *a, const void *b)
+{
+       return strcmp(((struct chain_head *)a)->name,
+                     ((struct chain_head *)b)->name);
+}
+
+/* Delete policy rule of previous chain, since cache doesn't contain
+ * chain policy rules.
+ * WARNING: This function has ugly design and relies on a lot of context, only
+ * to be called from specific places within the parser */
+static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
+{
+       if (h->chain_iterator_cur) {
+               /* policy rule is last rule */
+               struct rule_head *pr = (struct rule_head *)
+                       h->chain_iterator_cur->rules.prev;
+
+               /* save verdict */
+               h->chain_iterator_cur->verdict = 
+                       *(int *)GET_TARGET(pr->entry)->data;
+
+               /* save counter and counter_map information */
+               h->chain_iterator_cur->counter_map.maptype = 
+                                               COUNTER_MAP_NORMAL_MAP;
+               h->chain_iterator_cur->counter_map.mappos = num-1;
+               memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters, 
+                       sizeof(h->chain_iterator_cur->counters));
+
+               /* foot_offset points to verdict rule */
+               h->chain_iterator_cur->foot_index = num;
+               h->chain_iterator_cur->foot_offset = pr->offset;
+
+               /* delete rule from cache */
+               iptcc_delete_rule(pr);
+
                return 1;
        }
-       (*pos)++;
        return 0;
 }
 
-static STRUCT_ENTRY *
-index2entry(TC_HANDLE_T h, unsigned int index)
+/* Another ugly helper function split out of cache_add_entry to make it less
+ * spaghetti code */
+static void __iptcc_p_add_chain(TC_HANDLE_T h, struct chain_head *c,
+                               unsigned int offset, unsigned int *num)
 {
-       unsigned int pos = 0;
-       STRUCT_ENTRY *ret = NULL;
+       __iptcc_p_del_policy(h, *num);
 
-       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
-                     get_entry_n, index, &pos, &ret);
+       c->head_offset = offset;
+       c->index = *num;
 
-       return ret;
+       list_add_tail(&c->list, &h->chains);
+       h->chain_iterator_cur = c;
 }
 
-static inline STRUCT_ENTRY *
-get_entry(TC_HANDLE_T h, unsigned int offset)
+/* main parser function: add an entry from the blob to the cache */
+static int cache_add_entry(STRUCT_ENTRY *e, 
+                          TC_HANDLE_T h, 
+                          STRUCT_ENTRY **prev,
+                          unsigned int *num)
 {
-       return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+       unsigned int builtin;
+       unsigned int offset = (char *)e - (char *)h->entries->entrytable;
+
+       DEBUGP("entering...");
+
+       /* Last entry ("policy rule"). End it.*/
+       if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
+               /* This is the ERROR node at the end of the chain */
+               DEBUGP_C("%u:%u: end of table:\n", *num, offset);
+
+               __iptcc_p_del_policy(h, *num);
+
+               h->chain_iterator_cur = NULL;
+               goto out_inc;
+       }
+
+       /* We know this is the start of a new chain if it's an ERROR
+        * target, or a hook entry point */
+
+       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+               struct chain_head *c = 
+                       iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
+               DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset, 
+                       (char *)c->name, c);
+               if (!c) {
+                       errno = -ENOMEM;
+                       return -1;
+               }
+
+               __iptcc_p_add_chain(h, c, offset, num);
+
+       } else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
+               struct chain_head *c =
+                       iptcc_alloc_chain_head((char *)hooknames[builtin-1], 
+                                               builtin);
+               DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n", 
+                       *num, offset, c, &c->rules);
+               if (!c) {
+                       errno = -ENOMEM;
+                       return -1;
+               }
+
+               c->hooknum = builtin;
+
+               __iptcc_p_add_chain(h, c, offset, num);
+
+               /* FIXME: this is ugly. */
+               goto new_rule;
+       } else {
+               /* has to be normal rule */
+               struct rule_head *r;
+new_rule:
+
+               if (!(r = iptcc_alloc_rule(h->chain_iterator_cur, 
+                                          e->next_offset))) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+               DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
+
+               r->index = *num;
+               r->offset = offset;
+               memcpy(r->entry, e, e->next_offset);
+               r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
+               r->counter_map.mappos = r->index;
+
+               /* handling of jumps, etc. */
+               if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
+                       STRUCT_STANDARD_TARGET *t;
+
+                       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+                       if (t->target.u.target_size
+                           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+
+                       if (t->verdict < 0) {
+                               DEBUGP_C("standard, verdict=%d\n", t->verdict);
+                               r->type = IPTCC_R_STANDARD;
+                       } else if (t->verdict == r->offset+e->next_offset) {
+                               DEBUGP_C("fallthrough\n");
+                               r->type = IPTCC_R_FALLTHROUGH;
+                       } else {
+                               DEBUGP_C("jump, target=%u\n", t->verdict);
+                               r->type = IPTCC_R_JUMP;
+                               /* Jump target fixup has to be deferred
+                                * until second pass, since we migh not
+                                * yet have parsed the target */
+                       }
+               }
+
+               list_add_tail(&r->list, &h->chain_iterator_cur->rules);
+       }
+out_inc:
+       (*num)++;
+       return 0;
 }
 
-static inline unsigned long
-entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+
+/* parse an iptables blob into it's pieces */
+static int parse_table(TC_HANDLE_T h)
 {
-       return (void *)e - (void *)h->entries.entrytable;
+       STRUCT_ENTRY *prev;
+       unsigned int num = 0;
+       struct chain_head *c;
+
+       /* First pass: over ruleset blob */
+       ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+                       cache_add_entry, h, &prev, &num);
+
+       /* Second pass: fixup parsed data from first pass */
+       list_for_each_entry(c, &h->chains, list) {
+               struct rule_head *r;
+               list_for_each_entry(r, &c->rules, list) {
+                       struct chain_head *c;
+                       STRUCT_STANDARD_TARGET *t;
+
+                       if (r->type != IPTCC_R_JUMP)
+                               continue;
+
+                       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+                       c = iptcc_find_chain_by_offset(h, t->verdict);
+                       if (!c)
+                               return -1;
+                       r->jump = c;
+                       c->references++;
+               }
+       }
+
+       /* FIXME: sort chains */
+
+       return 1;
 }
 
-static inline unsigned long
-index2offset(TC_HANDLE_T h, unsigned int index)
+
+/**********************************************************************
+ * RULESET COMPILATION (cache -> blob)
+ **********************************************************************/
+
+/* Convenience structures */
+struct iptcb_chain_start{
+       STRUCT_ENTRY e;
+       struct ipt_error_target name;
+};
+#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) +                 \
+                                ALIGN(sizeof(struct ipt_error_target)))
+
+struct iptcb_chain_foot {
+       STRUCT_ENTRY e;
+       STRUCT_STANDARD_TARGET target;
+};
+#define IPTCB_CHAIN_FOOT_SIZE  (sizeof(STRUCT_ENTRY) +                 \
+                                ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
+
+struct iptcb_chain_error {
+       STRUCT_ENTRY entry;
+       struct ipt_error_target target;
+};
+#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) +                 \
+                                ALIGN(sizeof(struct ipt_error_target)))
+
+
+
+/* compile rule from cache into blob */
+static inline int iptcc_compile_rule (TC_HANDLE_T h, STRUCT_REPLACE *repl, struct rule_head *r)
 {
-       return entry2offset(h, index2entry(h, index));
+       /* handle jumps */
+       if (r->type == IPTCC_R_JUMP) {
+               STRUCT_STANDARD_TARGET *t;
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+               /* memset for memcmp convenience on delete/replace */
+               memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+               strcpy(t->target.u.user.name, STANDARD_TARGET);
+               /* Jumps can only happen to builtin chains, so we
+                * can safely assume that they always have a header */
+               t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
+       } else if (r->type == IPTCC_R_FALLTHROUGH) {
+               STRUCT_STANDARD_TARGET *t;
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+               t->verdict = r->offset + r->size;
+       }
+       
+       /* copy entry from cache to blob */
+       memcpy((char *)repl->entries+r->offset, r->entry, r->size);
+
+       return 1;
 }
 
-static inline STRUCT_ENTRY *
-offset2entry(TC_HANDLE_T h, unsigned int offset)
+/* compile chain from cache into blob */
+static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c)
 {
-       return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
+       int ret;
+       struct rule_head *r;
+       struct iptcb_chain_start *head;
+       struct iptcb_chain_foot *foot;
+
+       /* only user-defined chains have heaer */
+       if (!iptcc_is_builtin(c)) {
+               /* put chain header in place */
+               head = (void *)repl->entries + c->head_offset;
+               head->e.target_offset = sizeof(STRUCT_ENTRY);
+               head->e.next_offset = IPTCB_CHAIN_START_SIZE;
+               strcpy(head->name.t.u.user.name, ERROR_TARGET);
+               head->name.t.u.target_size = 
+                               ALIGN(sizeof(struct ipt_error_target));
+               strcpy(head->name.error, c->name);
+       } else {
+               repl->hook_entry[c->hooknum-1] = c->head_offset;        
+               repl->underflow[c->hooknum-1] = c->foot_offset;
+       }
+
+       /* iterate over rules */
+       list_for_each_entry(r, &c->rules, list) {
+               ret = iptcc_compile_rule(h, repl, r);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* put chain footer in place */
+       foot = (void *)repl->entries + c->foot_offset;
+       foot->e.target_offset = sizeof(STRUCT_ENTRY);
+       foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
+       strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
+       foot->target.target.u.target_size =
+                               ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       /* builtin targets have verdict, others return */
+       if (iptcc_is_builtin(c))
+               foot->target.verdict = c->verdict;
+       else
+               foot->target.verdict = RETURN;
+       /* set policy-counters */
+       memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
+
+       return 0;
 }
 
-static inline unsigned int
-offset2index(const TC_HANDLE_T h, unsigned int offset)
+/* calculate offset and number for every rule in the cache */
+static int iptcc_compile_chain_offsets(TC_HANDLE_T h, struct chain_head *c,
+                                      int *offset, int *num)
 {
-       return entry2index(h, offset2entry(h, offset));
+       struct rule_head *r;
+
+       c->head_offset = *offset;
+       DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
+
+       if (!iptcc_is_builtin(c))  {
+               /* Chain has header */
+               *offset += sizeof(STRUCT_ENTRY) 
+                            + ALIGN(sizeof(struct ipt_error_target));
+               (*num)++;
+       }
+
+       list_for_each_entry(r, &c->rules, list) {
+               DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
+               r->offset = *offset;
+               r->index = *num;
+               *offset += r->size;
+               (*num)++;
+       }
+
+       DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num, 
+               *offset, *num);
+       c->foot_offset = *offset;
+       c->foot_index = *num;
+       *offset += sizeof(STRUCT_ENTRY)
+                  + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       (*num)++;
+
+       return 1;
 }
 
+/* put the pieces back together again */
+static int iptcc_compile_table_prep(TC_HANDLE_T h, unsigned int *size)
+{
+       struct chain_head *c;
+       unsigned int offset = 0, num = 0;
+       int ret = 0;
 
-static const char *
-get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+       /* First pass: calculate offset for every rule */
+       list_for_each_entry(c, &h->chains, list) {
+               ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Append one error rule at end of chain */
+       num++;
+       offset += sizeof(STRUCT_ENTRY)
+                 + ALIGN(sizeof(struct ipt_error_target));
+
+       /* ruleset size is now in offset */
+       *size = offset;
+       return num;
+}
+
+static int iptcc_compile_table(TC_HANDLE_T h, STRUCT_REPLACE *repl)
 {
-       STRUCT_ENTRY *e;
+       struct chain_head *c;
+       struct iptcb_chain_error *error;
 
-       e = get_entry(h, offset);
-       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
-               fprintf(stderr, "ERROR: offset %u not an error node!\n",
-                       offset);
-               abort();
+       /* Second pass: copy from cache to offsets, fill in jumps */
+       list_for_each_entry(c, &h->chains, list) {
+               int ret = iptcc_compile_chain(h, repl, c);
+               if (ret < 0)
+                       return ret;
        }
 
-       return (const char *)GET_TARGET(e)->data;
+       /* Append error rule at end of chain */
+       error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
+       error->entry.target_offset = sizeof(STRUCT_ENTRY);
+       error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
+       error->target.t.u.user.target_size = 
+               ALIGN(sizeof(struct ipt_error_target));
+       strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET);
+       strcpy((char *)&error->target.error, "ERROR");
+
+       return 1;
 }
 
+/**********************************************************************
+ * EXTERNAL API (operates on cache only)
+ **********************************************************************/
+
 /* Allocate handle of given size */
 static TC_HANDLE_T
 alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
@@ -220,33 +739,37 @@ alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
        size_t len;
        TC_HANDLE_T h;
 
-       len = sizeof(STRUCT_TC_HANDLE)
-               + size
-               + num_rules * sizeof(struct counter_map);
+       len = sizeof(STRUCT_TC_HANDLE) + size;
 
-       if ((h = malloc(len)) == NULL) {
+       h = malloc(sizeof(STRUCT_TC_HANDLE));
+       if (!h) {
                errno = ENOMEM;
                return NULL;
        }
-
-       h->changed = 0;
-       h->cache_num_chains = 0;
-       h->cache_chain_heads = NULL;
-       h->counter_map = (void *)h
-               + sizeof(STRUCT_TC_HANDLE)
-               + size;
+       memset(h, 0, sizeof(*h));
+       INIT_LIST_HEAD(&h->chains);
        strcpy(h->info.name, tablename);
-       strcpy(h->entries.name, tablename);
+
+       h->entries = malloc(size);
+       if (!h->entries)
+               goto out_free_handle;
+
+       strcpy(h->entries->name, tablename);
 
        return h;
+
+out_free_handle:
+       free(h);
+
+       return NULL;
 }
 
+
 TC_HANDLE_T
 TC_INIT(const char *tablename)
 {
        TC_HANDLE_T h;
        STRUCT_GETINFO info;
-       unsigned int i;
        int tmp;
        socklen_t s;
 
@@ -272,6 +795,9 @@ TC_INIT(const char *tablename)
        if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
                return NULL;
 
+       DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
+               info.valid_hooks, info.num_entries, info.size);
+
        if ((h = alloc_handle(info.name, info.size, info.num_entries))
            == NULL) {
                close(sockfd);
@@ -279,54 +805,59 @@ TC_INIT(const char *tablename)
                return NULL;
        }
 
-/* Too hard --RR */
-#if 0
-       sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
-       dynlib = dlopen(pathname, RTLD_NOW);
-       if (!dynlib) {
-               errno = ENOENT;
-               return NULL;
-       }
-       h->hooknames = dlsym(dynlib, "hooknames");
-       if (!h->hooknames) {
-               errno = ENOENT;
-               return NULL;
-       }
-#else
-       h->hooknames = hooknames;
-#endif
-
        /* Initialize current state */
        h->info = info;
-       h->new_number = h->info.num_entries;
-       for (i = 0; i < h->info.num_entries; i++)
-               h->counter_map[i]
-                       = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
 
-       h->entries.size = h->info.size;
+       h->entries->size = h->info.size;
 
        tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
 
-       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
-                      &tmp) < 0) {
-               close(sockfd);
-               sockfd = -1;
-               free(h);
-               return NULL;
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
+                      &tmp) < 0)
+               goto error;
+
+#ifdef IPTC_DEBUG2
+       {
+               int fd = open("/tmp/libiptc-so_get_entries.blob", 
+                               O_CREAT|O_WRONLY);
+               if (fd >= 0) {
+                       write(fd, h->entries, tmp);
+                       close(fd);
+               }
        }
+#endif
+
+       if (parse_table(h) < 0)
+               goto error;
 
        CHECK(h);
        return h;
+error:
+       TC_FREE(&h);
+       return NULL;
 }
 
 void
 TC_FREE(TC_HANDLE_T *h)
 {
+       struct chain_head *c, *tmp;
+
        close(sockfd);
        sockfd = -1;
-       if ((*h)->cache_chain_heads)
-               free((*h)->cache_chain_heads);
+
+       list_for_each_entry_safe(c, tmp, &(*h)->chains, list) {
+               struct rule_head *r, *rtmp;
+
+               list_for_each_entry_safe(r, rtmp, &c->rules, list) {
+                       free(r);
+               }
+
+               free(c);
+       }
+
+       free((*h)->entries);
        free(*h);
+
        *h = NULL;
 }
 
@@ -343,10 +874,10 @@ void
 TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
 {
        CHECK(handle);
-
+#if 0
        printf("libiptc v%s.  %u entries, %u bytes.\n",
               IPTABLES_VERSION,
-              handle->new_number, handle->entries.size);
+              handle->new_number, handle->entries->size);
        printf("Table `%s'\n", handle->info.name);
        printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
               handle->info.hook_entry[HOOK_PRE_ROUTING],
@@ -361,206 +892,18 @@ TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
               handle->info.underflow[HOOK_LOCAL_OUT],
               handle->info.underflow[HOOK_POST_ROUTING]);
 
-       ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
+       ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
                      dump_entry, handle);
-}
-
-/* Returns 0 if not hook entry, else hooknumber + 1 */
-static inline unsigned int
-is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
-{
-       unsigned int i;
-
-       for (i = 0; i < NUMHOOKS; i++) {
-               if ((h->info.valid_hooks & (1 << i))
-                   && get_entry(h, h->info.hook_entry[i]) == e)
-                       return i+1;
-       }
-       return 0;
-}
-
-static inline int
-add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
-{
-       unsigned int builtin;
-
-       /* Last entry.  End it. */
-       if (entry2offset(h, e) + e->next_offset == h->entries.size) {
-               /* This is the ERROR node at end of the table */
-               h->cache_chain_heads[h->cache_num_chains-1].end_off = 
-                       entry2offset(h, *prev);
-               return 0;
-       }
-
-       /* We know this is the start of a new chain if it's an ERROR
-          target, or a hook entry point */
-       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
-               /* prev was last entry in previous chain */
-               h->cache_chain_heads[h->cache_num_chains-1].end_off
-                       = entry2offset(h, *prev);
-
-               strcpy(h->cache_chain_heads[h->cache_num_chains].name,
-                      (const char *)GET_TARGET(e)->data);
-               h->cache_chain_heads[h->cache_num_chains].start_off
-                       = entry2offset(h, (void *)e + e->next_offset);
-               h->cache_num_chains++;
-       } else if ((builtin = is_hook_entry(e, h)) != 0) {
-               if (h->cache_num_chains > 0)
-                       /* prev was last entry in previous chain */
-                       h->cache_chain_heads[h->cache_num_chains-1].end_off
-                               = entry2offset(h, *prev);
-
-               strcpy(h->cache_chain_heads[h->cache_num_chains].name,
-                      h->hooknames[builtin-1]);
-               h->cache_chain_heads[h->cache_num_chains].start_off
-                       = entry2offset(h, (void *)e);
-               h->cache_num_chains++;
-       }
-
-       *prev = e;
-       return 0;
-}
-
-static int alphasort(const void *a, const void *b)
-{
-       return strcmp(((struct chain_cache *)a)->name,
-                     ((struct chain_cache *)b)->name);
-}
-
-static int populate_cache(TC_HANDLE_T h)
-{
-       unsigned int i;
-       STRUCT_ENTRY *prev;
-
-       /* # chains < # rules / 2 + num builtins - 1 */
-       h->cache_chain_heads = malloc((h->new_number / 2 + 4)
-                                     * sizeof(struct chain_cache));
-       if (!h->cache_chain_heads) {
-               errno = ENOMEM;
-               return 0;
-       }
-
-       h->cache_num_chains = 0;
-       h->cache_num_builtins = 0;
-
-       /* Count builtins */
-       for (i = 0; i < NUMHOOKS; i++) {
-               if (h->info.valid_hooks & (1 << i))
-                       h->cache_num_builtins++;
-       }
-
-       prev = NULL;
-       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
-                     add_chain, h, &prev);
-
-       qsort(h->cache_chain_heads + h->cache_num_builtins,
-             h->cache_num_chains - h->cache_num_builtins,
-             sizeof(struct chain_cache), alphasort);
-
-       return 1;
-}
-
-static int 
-correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
-{
-       int i;          /* needs to be signed because deleting first
-                          chain can make it drop to -1 */
-
-       if (!delta)
-               return 1;
-
-       for (i = 0; i < h->cache_num_chains; i++) {
-               struct chain_cache *cc = &h->cache_chain_heads[i];
-
-               if (delta < 0) {
-                       /* take care about deleted chains */
-                       if (cc->start_off > offset+delta
-                           && cc->end_off < offset) {
-                               /* this chain is within the deleted range,
-                                * let's remove it from the cache */
-                               void *start;
-                               unsigned int size;
-
-                               h->cache_num_chains--;
-
-                               /* no need for memmove since we are 
-                                * removing the last entry */
-                               if (i >= h->cache_num_chains)
-                                       continue;
-
-                               start = &h->cache_chain_heads[i+1];
-                               size = (h->cache_num_chains-i)
-                                       * sizeof(struct chain_cache);
-                               memmove(cc, start, size);
-
-                               /* iterate over same index again, since
-                                * it is now a different chain */
-                               i--;
-                               continue;
-                       }
-               }
-
-               if (cc->start_off > offset)
-                       cc->start_off += delta;
-
-               if (cc->end_off >= offset)
-                       cc->end_off += delta;
-       }
-       /* HW_FIXME: sorting might be needed, but just in case a new chain was
-        * added */
-
-       return 1;
-}
-
-static int
-add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
-               unsigned int end_off)
-{
-       struct chain_cache *ccs = realloc(h->cache_chain_heads, 
-                                         (h->new_number / 2 + 4 + 1)
-                                          * sizeof(struct chain_cache));
-       struct chain_cache *newcc;
-       
-       if (!ccs)
-               return 0;
-
-       h->cache_chain_heads = ccs;
-       newcc = &h->cache_chain_heads[h->cache_num_chains];
-       h->cache_num_chains++;
-
-       strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
-       newcc->name[TABLE_MAXNAMELEN-1] = '\0';
-       newcc->start_off = start_off;
-       newcc->end_off = end_off;
-
-       return 1;
-}
-
-/* Returns cache ptr if found, otherwise NULL. */
-static struct chain_cache *
-find_label(const char *name, TC_HANDLE_T handle)
-{
-       unsigned int i;
-
-       if (handle->cache_chain_heads == NULL
-           && !populate_cache(handle))
-               return NULL;
-
-       /* FIXME: Linear search through builtins, then binary --RR */
-       for (i = 0; i < handle->cache_num_chains; i++) {
-               if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
-                       return &handle->cache_chain_heads[i];
-       }
-
-       return NULL;
+#endif
 }
 
 /* Does this chain exist? */
 int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
 {
-       return find_label(chain, handle) != NULL;
+       return iptcc_find_label(chain, handle) != NULL;
 }
 
+#if 0
 /* Returns the position of the final (ie. unconditional) element. */
 static unsigned int
 get_chain_end(const TC_HANDLE_T handle, unsigned int start)
@@ -597,369 +940,266 @@ get_chain_end(const TC_HANDLE_T handle, unsigned int start)
                handle->entries.size, off);
        abort();
 }
+#endif
+
+static void iptcc_chain_iterator_advance(TC_HANDLE_T handle)
+{
+       struct chain_head *c = handle->chain_iterator_cur;
+
+       if (c->list.next == &handle->chains)
+               handle->chain_iterator_cur = NULL;
+       else
+               handle->chain_iterator_cur = 
+                       list_entry(c->list.next, struct chain_head, list);
+}
 
 /* Iterator functions to run through the chains. */
 const char *
 TC_FIRST_CHAIN(TC_HANDLE_T *handle)
 {
-       if ((*handle)->cache_chain_heads == NULL
-           && !populate_cache(*handle))
+       struct chain_head *c = list_entry((*handle)->chains.next,
+                                         struct chain_head, list);
+
+       iptc_fn = TC_FIRST_CHAIN;
+
+
+       if (list_empty(&(*handle)->chains)) {
+               DEBUGP(": no chains\n");
                return NULL;
+       }
 
-       (*handle)->cache_chain_iteration
-               = &(*handle)->cache_chain_heads[0];
+       (*handle)->chain_iterator_cur = c;
+       iptcc_chain_iterator_advance(*handle);
 
-       return (*handle)->cache_chain_iteration->name;
+       DEBUGP(": returning `%s'\n", c->name);
+       return c->name;
 }
 
 /* Iterator functions to run through the chains.  Returns NULL at end. */
 const char *
 TC_NEXT_CHAIN(TC_HANDLE_T *handle)
 {
-       (*handle)->cache_chain_iteration++;
+       struct chain_head *c = (*handle)->chain_iterator_cur;
+
+       iptc_fn = TC_NEXT_CHAIN;
 
-       if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
-           == (*handle)->cache_num_chains)
+       if (!c) {
+               DEBUGP(": no more chains\n");
                return NULL;
+       }
 
-       return (*handle)->cache_chain_iteration->name;
+       iptcc_chain_iterator_advance(*handle);
+       
+       DEBUGP(": returning `%s'\n", c->name);
+       return c->name;
 }
 
 /* Get first rule in the given chain: NULL for empty chain. */
 const STRUCT_ENTRY *
 TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
 {
-       struct chain_cache *c;
+       struct chain_head *c;
+       struct rule_head *r;
+
+       iptc_fn = TC_FIRST_RULE;
 
-       c = find_label(chain, *handle);
+       DEBUGP("first rule(%s): ", chain);
+
+       c = iptcc_find_label(chain, *handle);
        if (!c) {
                errno = ENOENT;
                return NULL;
        }
 
        /* Empty chain: single return/policy rule */
-       if (c->start_off == c->end_off)
+       if (list_empty(&c->rules)) {
+               DEBUGP_C("no rules, returning NULL\n");
                return NULL;
+       }
+
+       r = list_entry(c->rules.next, struct rule_head, list);
+       (*handle)->rule_iterator_cur = r;
+       DEBUGP_C("%p\n", r);
 
-       (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
-       return offset2entry(*handle, c->start_off);
+       return r->entry;
 }
 
 /* Returns NULL when rules run out. */
 const STRUCT_ENTRY *
 TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
 {
-       if ((void *)prev + prev->next_offset
-           == (void *)(*handle)->cache_rule_end)
+       struct rule_head *r;
+
+       DEBUGP("rule_iterator_cur=%p...", (*handle)->rule_iterator_cur);
+
+       if (!(*handle)->rule_iterator_cur) {
+               DEBUGP_C("returning NULL\n");
                return NULL;
+       }
+       
+       r = list_entry((*handle)->rule_iterator_cur->list.next, 
+                       struct rule_head, list);
 
-       return (void *)prev + prev->next_offset;
+       iptc_fn = TC_NEXT_RULE;
+
+       DEBUGP_C("next=%p, head=%p...", &r->list, 
+               &(*handle)->rule_iterator_cur->chain->rules);
+
+       if (&r->list == &(*handle)->rule_iterator_cur->chain->rules) {
+               (*handle)->rule_iterator_cur = NULL;
+               DEBUGP_C("finished, returning NULL\n");
+               return NULL;
+       }
+
+       (*handle)->rule_iterator_cur = r;
+
+       /* NOTE: prev is without any influence ! */
+       DEBUGP_C("returning rule %p\n", r);
+       return r->entry;
 }
 
-#if 0
 /* How many rules in this chain? */
 unsigned int
 TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
 {
-       unsigned int off = 0;
-       STRUCT_ENTRY *start, *end;
-
+       struct chain_head *c;
+       iptc_fn = TC_NUM_RULES;
        CHECK(*handle);
-       if (!find_label(&off, chain, *handle)) {
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
                errno = ENOENT;
                return (unsigned int)-1;
        }
-
-       start = get_entry(*handle, off);
-       end = get_entry(*handle, get_chain_end(*handle, off));
-
-       return entry2index(*handle, end) - entry2index(*handle, start);
+       
+       return c->num_rules;
 }
 
-/* Get n'th rule in this chain. */
 const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
                                unsigned int n,
                                TC_HANDLE_T *handle)
 {
-       unsigned int pos = 0, chainindex;
+       struct chain_head *c;
+       struct rule_head *r;
+       
+       iptc_fn = TC_GET_RULE;
 
        CHECK(*handle);
-       if (!find_label(&pos, chain, *handle)) {
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
                errno = ENOENT;
                return NULL;
        }
 
-       chainindex = entry2index(*handle, get_entry(*handle, pos));
-
-       return index2entry(*handle, chainindex + n);
+       r = iptcc_get_rule_num(c, n);
+       if (!r)
+               return NULL;
+       return r->entry;
 }
-#endif
 
-static const char *
-target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+/* Returns a pointer to the target name of this position. */
+const char *standard_target_map(int verdict)
 {
-       int spos;
-       unsigned int labelidx;
-       STRUCT_ENTRY *jumpto;
-
-       /* To avoid const warnings */
-       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
-
-       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
-               return GET_TARGET(e)->u.user.name;
-
-       /* Standard target: evaluate */
-       spos = *(int *)GET_TARGET(e)->data;
-       if (spos < 0) {
-               if (spos == RETURN)
+       switch (verdict) {
+               case RETURN:
                        return LABEL_RETURN;
-               else if (spos == -NF_ACCEPT-1)
+                       break;
+               case -NF_ACCEPT-1:
                        return LABEL_ACCEPT;
-               else if (spos == -NF_DROP-1)
-                       return LABEL_DROP;
-               else if (spos == -NF_QUEUE-1)
-                       return LABEL_QUEUE;
-
-               fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
-                       entry2offset(handle, e), handle->entries.size,
-                       spos);
-               abort();
-       }
-
-       jumpto = get_entry(handle, spos);
-
-       /* Fall through rule */
-       if (jumpto == (void *)e + e->next_offset)
-               return "";
-
-       /* Must point to head of a chain: ie. after error rule */
-       labelidx = entry2index(handle, jumpto) - 1;
-       return get_errorlabel(handle, index2offset(handle, labelidx));
-}
-
-/* Returns a pointer to the target name of this position. */
-const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
-                         TC_HANDLE_T *handle)
-{
-       return target_name(*handle, e);
-}
-
-/* Is this a built-in chain?  Actually returns hook + 1. */
-int
-TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
-{
-       unsigned int i;
-
-       for (i = 0; i < NUMHOOKS; i++) {
-               if ((handle->info.valid_hooks & (1 << i))
-                   && handle->hooknames[i]
-                   && strcmp(handle->hooknames[i], chain) == 0)
-                       return i+1;
-       }
-       return 0;
-}
-
-/* Get the policy of a given built-in chain */
-const char *
-TC_GET_POLICY(const char *chain,
-             STRUCT_COUNTERS *counters,
-             TC_HANDLE_T *handle)
-{
-       unsigned int start;
-       STRUCT_ENTRY *e;
-       int hook;
-
-       hook = TC_BUILTIN(chain, *handle);
-       if (hook != 0)
-               start = (*handle)->info.hook_entry[hook-1];
-       else
-               return NULL;
-
-       e = get_entry(*handle, get_chain_end(*handle, start));
-       *counters = e->counters;
-
-       return target_name(*handle, e);
-}
-
-static inline int
-correct_verdict(STRUCT_ENTRY *e,
-               char *base,
-               unsigned int offset, int delta_offset)
-{
-       STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
-       unsigned int curr = (char *)e - base;
-
-       /* Trap: insert of fall-through rule.  Don't change fall-through
-          verdict to jump-over-next-rule. */
-       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
-           && t->verdict > (int)offset
-           && !(curr == offset &&
-                t->verdict == curr + e->next_offset)) {
-               t->verdict += delta_offset;
+                       break;
+               case -NF_DROP-1:
+                       return LABEL_DROP;
+                       break;
+               case -NF_QUEUE-1:
+                       return LABEL_QUEUE;
+                       break;
+               default:
+                       fprintf(stderr, "ERROR: %d not a valid target)\n",
+                               verdict);
+                       abort();
+                       break;
        }
-
-       return 0;
+       /* not reached */
+       return NULL;
 }
 
-/* Adjusts standard verdict jump positions after an insertion/deletion. */
-static int
-set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
+                         TC_HANDLE_T *handle)
 {
-       ENTRY_ITERATE((*handle)->entries.entrytable,
-                     (*handle)->entries.size,
-                     correct_verdict, (char *)(*handle)->entries.entrytable,
-                     offset, delta_offset);
-
-       set_changed(*handle);
-       return 1;
-}
+       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+       struct rule_head *r = container_of(e, struct rule_head, entry);
 
-/* If prepend is set, then we are prepending to a chain: if the
- * insertion position is an entry point, keep the entry point. */
-static int
-insert_rules(unsigned int num_rules, unsigned int rules_size,
-            const STRUCT_ENTRY *insert,
-            unsigned int offset, unsigned int num_rules_offset,
-            int prepend,
-            TC_HANDLE_T *handle)
-{
-       TC_HANDLE_T newh;
-       STRUCT_GETINFO newinfo;
-       unsigned int i;
+       iptc_fn = TC_GET_TARGET;
 
-       if (offset >= (*handle)->entries.size) {
-               errno = EINVAL;
-               return 0;
+       switch(r->type) {
+               int spos;
+               case IPTCC_R_FALLTHROUGH:
+                       return "";
+                       break;
+               case IPTCC_R_JUMP:
+                       DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
+                       return r->jump->name;
+                       break;
+               case IPTCC_R_STANDARD:
+                       spos = *(int *)GET_TARGET(e)->data;
+                       DEBUGP("r=%p, spos=%d'\n", r, spos);
+                       return standard_target_map(spos);
+                       break;
+               case IPTCC_R_MODULE:
+                       return GET_TARGET(e)->u.user.name;
+                       break;
        }
+       return NULL;
+}
+/* Is this a built-in chain?  Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
+{
+       struct chain_head *c;
+       
+       iptc_fn = TC_BUILTIN;
 
-       newinfo = (*handle)->info;
-
-       /* Fix up entry points. */
-       for (i = 0; i < NUMHOOKS; i++) {
-               /* Entry points to START of chain, so keep same if
-                   inserting on at that point. */
-               if ((*handle)->info.hook_entry[i] > offset)
-                       newinfo.hook_entry[i] += rules_size;
-
-               /* Underflow always points to END of chain (policy),
-                  so if something is inserted at same point, it
-                  should be advanced. */
-               if ((*handle)->info.underflow[i] >= offset)
-                       newinfo.underflow[i] += rules_size;
-       }
-
-       newh = alloc_handle((*handle)->info.name,
-                           (*handle)->entries.size + rules_size,
-                           (*handle)->new_number + num_rules);
-       if (!newh)
-               return 0;
-       newh->info = newinfo;
-
-       /* Copy pre... */
-       memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
-       /* ... Insert new ... */
-       memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
-       /* ... copy post */
-       memcpy((char *)newh->entries.entrytable + offset + rules_size,
-              (char *)(*handle)->entries.entrytable + offset,
-              (*handle)->entries.size - offset);
-
-       /* Move counter map. */
-       /* Copy pre... */
-       memcpy(newh->counter_map, (*handle)->counter_map,
-              sizeof(struct counter_map) * num_rules_offset);
-       /* ... copy post */
-       memcpy(newh->counter_map + num_rules_offset + num_rules,
-              (*handle)->counter_map + num_rules_offset,
-              sizeof(struct counter_map) * ((*handle)->new_number
-                                            - num_rules_offset));
-       /* Set intermediates to no counter copy */
-       for (i = 0; i < num_rules; i++)
-               newh->counter_map[num_rules_offset+i]
-                       = ((struct counter_map){ COUNTER_MAP_SET, 0 });
-
-       newh->new_number = (*handle)->new_number + num_rules;
-       newh->entries.size = (*handle)->entries.size + rules_size;
-       newh->hooknames = (*handle)->hooknames;
-
-       newh->cache_chain_heads = (*handle)->cache_chain_heads;
-       newh->cache_num_builtins = (*handle)->cache_num_builtins;
-       newh->cache_num_chains = (*handle)->cache_num_chains;
-       newh->cache_rule_end = (*handle)->cache_rule_end;
-       newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
-       if (!correct_cache(newh, offset, rules_size)) {
-               free(newh);
-               return 0;
+       c = iptcc_find_label(chain, handle);
+       if (!c) {
+               errno = ENOENT;
+               return -1;
        }
 
-       free(*handle);
-       *handle = newh;
-
-       return set_verdict(offset, rules_size, handle);
+       return iptcc_is_builtin(c);
 }
 
-static int
-delete_rules(unsigned int num_rules, unsigned int rules_size,
-            unsigned int offset, unsigned int num_rules_offset,
-            TC_HANDLE_T *handle)
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
 {
-       unsigned int i;
+       struct chain_head *c;
 
-       if (offset + rules_size > (*handle)->entries.size) {
-               errno = EINVAL;
-               return 0;
-       }
+       iptc_fn = TC_GET_POLICY;
 
-       /* Fix up entry points. */
-       for (i = 0; i < NUMHOOKS; i++) {
-               /* In practice, we never delete up to a hook entry,
-                  since the built-in chains are always first,
-                  so these two are never equal */
-               if ((*handle)->info.hook_entry[i] >= offset + rules_size)
-                       (*handle)->info.hook_entry[i] -= rules_size;
-               else if ((*handle)->info.hook_entry[i] > offset) {
-                       fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
-                               i, (*handle)->info.hook_entry[i], offset);
-                       abort();
-               }
+       DEBUGP("called for chain %s\n", chain);
 
-               /* Underflow points to policy (terminal) rule in
-                   built-in, so sequality is valid here (when deleting
-                   the last rule). */
-               if ((*handle)->info.underflow[i] >= offset + rules_size)
-                       (*handle)->info.underflow[i] -= rules_size;
-               else if ((*handle)->info.underflow[i] > offset) {
-                       fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
-                               i, (*handle)->info.underflow[i], offset);
-                       abort();
-               }
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
        }
 
-       /* Move the rules down. */
-       memmove((char *)(*handle)->entries.entrytable + offset,
-               (char *)(*handle)->entries.entrytable + offset + rules_size,
-               (*handle)->entries.size - (offset + rules_size));
-
-       /* Move the counter map down. */
-       memmove(&(*handle)->counter_map[num_rules_offset],
-               &(*handle)->counter_map[num_rules_offset + num_rules],
-               sizeof(struct counter_map)
-               * ((*handle)->new_number - (num_rules + num_rules_offset)));
-
-       /* Fix numbers */
-       (*handle)->new_number -= num_rules;
-       (*handle)->entries.size -= rules_size;
+       if (!iptcc_is_builtin(c))
+               return NULL;
 
-       /* Fix the chain cache */
-       if (!correct_cache(*handle, offset+rules_size, -(int)rules_size))
-               return 0;
+       *counters = c->counters;
 
-       return set_verdict(offset, -(int)rules_size, handle);
+       return standard_target_map(c->verdict);
 }
 
 static int
-standard_map(STRUCT_ENTRY *e, int verdict)
+iptcc_standard_map(struct rule_head *r, int verdict)
 {
+       STRUCT_ENTRY *e = r->entry;
        STRUCT_STANDARD_TARGET *t;
 
        t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
@@ -974,44 +1214,50 @@ standard_map(STRUCT_ENTRY *e, int verdict)
        strcpy(t->target.u.user.name, STANDARD_TARGET);
        t->verdict = verdict;
 
+       r->type = IPTCC_R_STANDARD;
+
        return 1;
 }
 
 static int
-map_target(const TC_HANDLE_T handle,
-          STRUCT_ENTRY *e,
-          unsigned int offset,
-          STRUCT_ENTRY_TARGET *old)
+iptcc_map_target(const TC_HANDLE_T handle,
+          struct rule_head *r)
 {
+       STRUCT_ENTRY *e = r->entry;
        STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
 
-       /* Save old target (except data, which we don't change, except for
-          standard case, where we don't care). */
-       *old = *t;
-
        /* Maybe it's empty (=> fall through) */
-       if (strcmp(t->u.user.name, "") == 0)
-               return standard_map(e, offset + e->next_offset);
+       if (strcmp(t->u.user.name, "") == 0) {
+               r->type = IPTCC_R_FALLTHROUGH;
+               return 1;
+       }
        /* Maybe it's a standard target name... */
        else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
-               return standard_map(e, -NF_ACCEPT - 1);
+               return iptcc_standard_map(r, -NF_ACCEPT - 1);
        else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
-               return standard_map(e, -NF_DROP - 1);
+               return iptcc_standard_map(r, -NF_DROP - 1);
        else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
-               return standard_map(e, -NF_QUEUE - 1);
+               return iptcc_standard_map(r, -NF_QUEUE - 1);
        else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
-               return standard_map(e, RETURN);
+               return iptcc_standard_map(r, RETURN);
        else if (TC_BUILTIN(t->u.user.name, handle)) {
                /* Can't jump to builtins. */
                errno = EINVAL;
                return 0;
        } else {
                /* Maybe it's an existing chain name. */
-               struct chain_cache *c;
-
-               c = find_label(t->u.user.name, handle);
-               if (c)
-                       return standard_map(e, c->start_off);
+               struct chain_head *c;
+               DEBUGP("trying to find chain `%s': ", t->u.user.name);
+
+               c = iptcc_find_label(t->u.user.name, handle);
+               if (c) {
+                       DEBUGP_C("found!\n");
+                       r->type = IPTCC_R_JUMP;
+                       r->jump = c;
+                       c->references++;
+                       return 1;
+               }
+               DEBUGP_C("not found :(\n");
        }
 
        /* Must be a module?  If not, kernel will reject... */
@@ -1019,17 +1265,9 @@ map_target(const TC_HANDLE_T handle,
        memset(t->u.user.name + strlen(t->u.user.name),
               0,
               FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
-       return 1;
-}
-
-static void
-unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
-{
-       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
 
-       /* Save old target (except data, which we don't change, except for
-          standard case, where we don't care). */
-       *t = *old;
+       set_changed(handle);
+       return 1;
 }
 
 /* Insert the entry `fw' in chain `chain' into position `rulenum'. */
@@ -1039,36 +1277,41 @@ TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
                unsigned int rulenum,
                TC_HANDLE_T *handle)
 {
-       unsigned int chainindex, offset;
-       STRUCT_ENTRY_TARGET old;
-       struct chain_cache *c;
-       STRUCT_ENTRY *tmp;
-       int ret;
+       struct chain_head *c;
+       struct rule_head *r, *prev;
 
        iptc_fn = TC_INSERT_ENTRY;
-       if (!(c = find_label(chain, *handle))) {
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-
-       tmp = index2entry(*handle, chainindex + rulenum);
-       if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
+       prev = iptcc_get_rule_num(c, rulenum);
+       if (!prev) {
                errno = E2BIG;
                return 0;
        }
-       offset = index2offset(*handle, chainindex + rulenum);
 
-       /* Mapping target actually alters entry, but that's
-           transparent to the caller. */
-       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       memcpy(r->entry, e, e->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_SET;
+
+       if (!iptcc_map_target(*handle, r)) {
+               free(r);
                return 0;
+       }
+
+       list_add_tail(&r->list, &prev->list);
+       c->num_rules++;
+
+       set_changed(*handle);
 
-       ret = insert_rules(1, e->next_offset, e, offset,
-                          chainindex + rulenum, rulenum == 0, handle);
-       unmap_target((STRUCT_ENTRY *)e, &old);
-       return ret;
+       return 1;
 }
 
 /* Atomically replace rule `rulenum' in `chain' with `fw'. */
@@ -1078,40 +1321,40 @@ TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
                 unsigned int rulenum,
                 TC_HANDLE_T *handle)
 {
-       unsigned int chainindex, offset;
-       STRUCT_ENTRY_TARGET old;
-       struct chain_cache *c;
-       STRUCT_ENTRY *tmp;
-       int ret;
+       struct chain_head *c;
+       struct rule_head *r, *old;
 
        iptc_fn = TC_REPLACE_ENTRY;
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-
-       tmp = index2entry(*handle, chainindex + rulenum);
-       if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
+       if (!(old = iptcc_get_rule_num(c, rulenum))) {
                errno = E2BIG;
                return 0;
        }
 
-       offset = index2offset(*handle, chainindex + rulenum);
-       /* Replace = delete and insert. */
-       if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
-                         offset, chainindex + rulenum, handle))
+       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+               errno = ENOMEM;
                return 0;
+       }
+
+       memcpy(r->entry, e, e->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_SET;
 
-       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+       if (!iptcc_map_target(*handle, r)) {
+               free(r);
                return 0;
+       }
+
+       list_add(&r->list, &old->list);
+       iptcc_delete_rule(old);
 
-       ret = insert_rules(1, e->next_offset, e, offset,
-                          chainindex + rulenum, 1, handle);
-       unmap_target((STRUCT_ENTRY *)e, &old);
-       return ret;
+       set_changed(*handle);
+
+       return 1;
 }
 
 /* Append entry `fw' to chain `chain'.  Equivalent to insert with
@@ -1121,24 +1364,37 @@ TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
                const STRUCT_ENTRY *e,
                TC_HANDLE_T *handle)
 {
-       struct chain_cache *c;
-       STRUCT_ENTRY_TARGET old;
-       int ret;
+       struct chain_head *c;
+       struct rule_head *r;
 
        iptc_fn = TC_APPEND_ENTRY;
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               DEBUGP("unable to find chain `%s'\n", chain);
                errno = ENOENT;
                return 0;
        }
 
-       if (!map_target(*handle, (STRUCT_ENTRY *)e,
-                       c->end_off, &old))
+       if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+               DEBUGP("unable to allocate rule for chain `%s'\n", chain);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       memcpy(r->entry, e, e->next_offset);
+       r->counter_map.maptype = COUNTER_MAP_SET;
+
+       if (!iptcc_map_target(*handle, r)) {
+               DEBUGP("unable to ma target of rule for chain `%s'\n", chain);
+               free(r);
                return 0;
+       }
+
+       list_add_tail(&r->list, &c->rules);
+       c->num_rules++;
+
+       set_changed(*handle);
 
-       ret = insert_rules(1, e->next_offset, e, c->end_off, 
-                          offset2index(*handle, c->end_off), 0, handle);
-       unmap_target((STRUCT_ENTRY *)e, &old);
-       return ret;
+       return 1;
 }
 
 static inline int
@@ -1181,6 +1437,7 @@ target_different(const unsigned char *a_targdata,
 
        return 0;
 }
+#if 0
 
 static int
 is_same(const STRUCT_ENTRY *a,
@@ -1195,11 +1452,11 @@ TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
                TC_HANDLE_T *handle)
 {
        unsigned int offset;
-       struct chain_cache *c;
+       struct chain_head *c;
        STRUCT_ENTRY *e, *fw;
 
        iptc_fn = TC_DELETE_ENTRY;
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
@@ -1241,6 +1498,18 @@ TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
        errno = ENOENT;
        return 0;
 }
+#endif
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *origfw,
+               unsigned char *matchmask,
+               TC_HANDLE_T *handle)
+{
+       errno = ENOSYS;
+       return 0;
+}
 
 /* Delete the rule in position `rulenum' in `chain'. */
 int
@@ -1248,33 +1517,34 @@ TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
                    unsigned int rulenum,
                    TC_HANDLE_T *handle)
 {
-       unsigned int index;
-       int ret;
-       STRUCT_ENTRY *e;
-       struct chain_cache *c;
+       struct chain_head *c;
+       struct rule_head *r;
 
        iptc_fn = TC_DELETE_NUM_ENTRY;
-       if (!(c = find_label(chain, *handle))) {
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       index = offset2index(*handle, c->start_off) + rulenum;
-
-       if (index >= offset2index(*handle, c->end_off)) {
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
                errno = E2BIG;
                return 0;
        }
 
-       e = index2entry(*handle, index);
-       if (e == NULL) {
-               errno = EINVAL;
-               return 0;
+       /* If we are about to delete the rule that is the current
+        * iterator, move rule iterator back.  next pointer will then
+        * point to real next node */
+       if (r == (*handle)->rule_iterator_cur) {
+               (*handle)->rule_iterator_cur = 
+                       list_entry((*handle)->rule_iterator_cur->list.prev,
+                                  struct rule_head, list);
        }
 
-       ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
-                          index, handle);
-       return ret;
+       c->num_rules--;
+       iptcc_delete_rule(r);
+
+       return 1;
 }
 
 /* Check the packet `fw' on chain `chain'.  Returns the verdict, or
@@ -1292,47 +1562,43 @@ TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
 int
 TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
-       unsigned int startindex, endindex;
-       STRUCT_ENTRY *startentry, *endentry;
-       struct chain_cache *c;
-       int ret;
+       struct chain_head *c;
+       struct rule_head *r, *tmp;
 
        iptc_fn = TC_FLUSH_ENTRIES;
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
-       startindex = offset2index(*handle, c->start_off);
-       endindex = offset2index(*handle, c->end_off);
-       startentry = offset2entry(*handle, c->start_off);
-       endentry = offset2entry(*handle, c->end_off);
 
-       ret = delete_rules(endindex - startindex,
-                          (char *)endentry - (char *)startentry,
-                          c->start_off, startindex,
-                          handle);
-       return ret;
+       list_for_each_entry_safe(r, tmp, &c->rules, list) {
+               iptcc_delete_rule(r);
+       }
+
+       c->num_rules = 0;
+
+       set_changed(*handle);
+
+       return 1;
 }
 
 /* Zeroes the counters in a chain. */
 int
 TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
-       unsigned int i, end;
-       struct chain_cache *c;
+       struct chain_head *c;
+       struct rule_head *r;
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       i = offset2index(*handle, c->start_off);
-       end = offset2index(*handle, c->end_off);
-
-       for (; i <= end; i++) {
-               if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
-                       (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
+       list_for_each_entry(r, &c->rules, list) {
+               if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+                       r->counter_map.maptype = COUNTER_MAP_ZEROED;
        }
+
        set_changed(*handle);
 
        return 1;
@@ -1343,29 +1609,23 @@ TC_READ_COUNTER(const IPT_CHAINLABEL chain,
                unsigned int rulenum,
                TC_HANDLE_T *handle)
 {
-       STRUCT_ENTRY *e;
-       struct chain_cache *c;
-       unsigned int chainindex, end;
+       struct chain_head *c;
+       struct rule_head *r;
 
        iptc_fn = TC_READ_COUNTER;
        CHECK(*handle);
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return NULL;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-       end = offset2index(*handle, c->end_off);
-
-       if (chainindex + rulenum > end) {
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
                errno = E2BIG;
                return NULL;
        }
 
-       e = index2entry(*handle, chainindex + rulenum);
-
-       return &e->counters;
+       return &r->entry[0].counters;
 }
 
 int
@@ -1373,33 +1633,24 @@ TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
                unsigned int rulenum,
                TC_HANDLE_T *handle)
 {
-       STRUCT_ENTRY *e;
-       struct chain_cache *c;
-       unsigned int chainindex, end;
+       struct chain_head *c;
+       struct rule_head *r;
        
        iptc_fn = TC_ZERO_COUNTER;
        CHECK(*handle);
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-       end = offset2index(*handle, c->end_off);
-
-       if (chainindex + rulenum > end) {
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
                errno = E2BIG;
                return 0;
        }
 
-       e = index2entry(*handle, chainindex + rulenum);
-
-       if ((*handle)->counter_map[chainindex + rulenum].maptype
-                       == COUNTER_MAP_NORMAL_MAP) {
-               (*handle)->counter_map[chainindex + rulenum].maptype
-                        = COUNTER_MAP_ZEROED;
-       }
+       if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+               r->counter_map.maptype = COUNTER_MAP_ZEROED;
 
        set_changed(*handle);
 
@@ -1412,30 +1663,25 @@ TC_SET_COUNTER(const IPT_CHAINLABEL chain,
               STRUCT_COUNTERS *counters,
               TC_HANDLE_T *handle)
 {
+       struct chain_head *c;
+       struct rule_head *r;
        STRUCT_ENTRY *e;
-       struct chain_cache *c;
-       unsigned int chainindex, end;
 
        iptc_fn = TC_SET_COUNTER;
        CHECK(*handle);
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       chainindex = offset2index(*handle, c->start_off);
-       end = offset2index(*handle, c->end_off);
-
-       if (chainindex + rulenum > end) {
+       if (!(r = iptcc_get_rule_num(c, rulenum))) {
                errno = E2BIG;
                return 0;
        }
 
-       e = index2entry(*handle, chainindex + rulenum);
-
-       (*handle)->counter_map[chainindex + rulenum].maptype
-               = COUNTER_MAP_SET;
+       e = r->entry;
+       r->counter_map.maptype = COUNTER_MAP_SET;
 
        memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
 
@@ -1450,82 +1696,42 @@ TC_SET_COUNTER(const IPT_CHAINLABEL chain,
 int
 TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
-       int ret;
-       struct {
-               STRUCT_ENTRY head;
-               struct ipt_error_target name;
-               STRUCT_ENTRY ret;
-               STRUCT_STANDARD_TARGET target;
-       } newc;
-       unsigned int destination;
+       static struct chain_head *c;
 
        iptc_fn = TC_CREATE_CHAIN;
 
        /* find_label doesn't cover built-in targets: DROP, ACCEPT,
            QUEUE, RETURN. */
-       if (find_label(chain, *handle)
+       if (iptcc_find_label(chain, *handle)
            || strcmp(chain, LABEL_DROP) == 0
            || strcmp(chain, LABEL_ACCEPT) == 0
            || strcmp(chain, LABEL_QUEUE) == 0
            || strcmp(chain, LABEL_RETURN) == 0) {
+               DEBUGP("Chain `%s' already exists\n", chain);
                errno = EEXIST;
                return 0;
        }
 
        if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               DEBUGP("Chain name `%s' too long\n", chain);
                errno = EINVAL;
                return 0;
        }
 
-       memset(&newc, 0, sizeof(newc));
-       newc.head.target_offset = sizeof(STRUCT_ENTRY);
-       newc.head.next_offset
-               = sizeof(STRUCT_ENTRY)
-               + ALIGN(sizeof(struct ipt_error_target));
-       strcpy(newc.name.t.u.user.name, ERROR_TARGET);
-       newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
-       strcpy(newc.name.error, chain);
-
-       newc.ret.target_offset = sizeof(STRUCT_ENTRY);
-       newc.ret.next_offset
-               = sizeof(STRUCT_ENTRY)
-               + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
-       strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
-       newc.target.target.u.target_size
-               = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
-       newc.target.verdict = RETURN;
-
-       destination = index2offset(*handle, (*handle)->new_number -1);
-
-       /* Add just before terminal entry */
-       ret = insert_rules(2, sizeof(newc), &newc.head,
-                          destination,
-                          (*handle)->new_number - 1,
-                          0, handle);
-
-       set_changed(*handle);
-
-       /* add chain cache info for this chain */
-       add_chain_cache(*handle, chain, 
-                       destination+newc.head.next_offset, 
-                       destination+newc.head.next_offset);
-
-       return ret;
-}
+       c = iptcc_alloc_chain_head(chain, 0);
+       if (!c) {
+               DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
+               errno = ENOMEM;
+               return 0;
 
-static int
-count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
-{
-       STRUCT_STANDARD_TARGET *t;
+       }
 
-       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
-               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+       DEBUGP("Creating chain `%s'\n", chain);
+       list_add_tail(&c->list, &(*handle)->chains);
 
-               if (t->verdict == offset)
-                       (*ref)++;
-       }
+       set_changed(*handle);
 
-       return 0;
+       return 1;
 }
 
 /* Get the number of references to this chain. */
@@ -1533,17 +1739,15 @@ int
 TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
                  TC_HANDLE_T *handle)
 {
-       struct chain_cache *c;
+       struct chain_head *c;
 
-       if (!(c = find_label(chain, *handle))) {
+       if (!(c = iptcc_find_label(chain, *handle))) {
                errno = ENOENT;
                return 0;
        }
 
-       *ref = 0;
-       ENTRY_ITERATE((*handle)->entries.entrytable,
-                     (*handle)->entries.size,
-                     count_ref, c->start_off, ref);
+       *ref = c->references;
+
        return 1;
 }
 
@@ -1551,48 +1755,53 @@ TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
 int
 TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
-       unsigned int labelidx, labeloff;
        unsigned int references;
-       struct chain_cache *c;
-       int ret;
-       STRUCT_ENTRY *start;
-
-       if (!TC_GET_REFERENCES(&references, chain, handle))
-               return 0;
+       struct chain_head *c;
 
        iptc_fn = TC_DELETE_CHAIN;
 
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               DEBUGP("cannot find chain `%s'\n", chain);
+               errno = ENOENT;
+               return 0;
+       }
+
        if (TC_BUILTIN(chain, *handle)) {
+               DEBUGP("cannot remove builtin chain `%s'\n", chain);
                errno = EINVAL;
                return 0;
        }
 
-       if (references > 0) {
-               errno = EMLINK;
+       if (!TC_GET_REFERENCES(&references, chain, handle)) {
+               DEBUGP("cannot get references on chain `%s'\n", chain);
                return 0;
        }
 
-       if (!(c = find_label(chain, *handle))) {
-               errno = ENOENT;
+       if (references > 0) {
+               DEBUGP("chain `%s' still has references\n", chain);
+               errno = EMLINK;
                return 0;
        }
 
-       if (c->start_off != c->end_off) {
+       if (c->num_rules) {
+               DEBUGP("chain `%s' is not empty\n", chain);
                errno = ENOTEMPTY;
                return 0;
        }
 
-       /* Need label index: preceeds chain start */
-       labelidx = offset2index(*handle, c->start_off) - 1;
-       labeloff = index2offset(*handle, labelidx);
+       /* If we are about to delete the chain that is the current
+        * iterator, move chain iterator firward. */
+       if (c == (*handle)->chain_iterator_cur)
+               iptcc_chain_iterator_advance(*handle);
+
+       list_del(&c->list);
+       free(c);
 
-       start = offset2entry(*handle, c->start_off);
+       DEBUGP("chain `%s' deleted\n", chain);
 
-       ret = delete_rules(2,
-                          get_entry(*handle, labeloff)->next_offset
-                          + start->next_offset,
-                          labeloff, labelidx, handle);
-       return ret;
+       set_changed(*handle);
+
+       return 1;
 }
 
 /* Renames a chain. */
@@ -1600,15 +1809,12 @@ int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
                    const IPT_CHAINLABEL newname,
                    TC_HANDLE_T *handle)
 {
-       unsigned int labeloff, labelidx;
-       struct chain_cache *c;
-       struct ipt_error_target *t;
-
+       struct chain_head *c;
        iptc_fn = TC_RENAME_CHAIN;
 
        /* find_label doesn't cover built-in targets: DROP, ACCEPT,
            QUEUE, RETURN. */
-       if (find_label(newname, *handle)
+       if (iptcc_find_label(newname, *handle)
            || strcmp(newname, LABEL_DROP) == 0
            || strcmp(newname, LABEL_ACCEPT) == 0
            || strcmp(newname, LABEL_QUEUE) == 0
@@ -1617,7 +1823,7 @@ int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
                return 0;
        }
 
-       if (!(c = find_label(oldname, *handle))
+       if (!(c = iptcc_find_label(oldname, *handle))
            || TC_BUILTIN(oldname, *handle)) {
                errno = ENOENT;
                return 0;
@@ -1628,20 +1834,8 @@ int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
                return 0;
        }
 
-       /* Need label index: preceeds chain start */
-       labelidx = offset2index(*handle, c->start_off) - 1;
-       labeloff = index2offset(*handle, labelidx);
-
-       t = (struct ipt_error_target *)
-               GET_TARGET(get_entry(*handle, labeloff));
-
-       memset(t->error, 0, sizeof(t->error));
-       strcpy(t->error, newname);
-
-       /* update chain cache */
-       memset(c->name, 0, sizeof(c->name));
-       strcpy(c->name, newname);
-
+       strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
+       
        set_changed(*handle);
 
        return 1;
@@ -1654,51 +1848,37 @@ TC_SET_POLICY(const IPT_CHAINLABEL chain,
              STRUCT_COUNTERS *counters,
              TC_HANDLE_T *handle)
 {
-       unsigned int hook;
-       unsigned int policyoff, ctrindex;
-       STRUCT_ENTRY *e;
-       STRUCT_STANDARD_TARGET *t;
+       struct chain_head *c;
 
        iptc_fn = TC_SET_POLICY;
-       /* Figure out which chain. */
-       hook = TC_BUILTIN(chain, *handle);
-       if (hook == 0) {
+
+       if (!(c = iptcc_find_label(chain, *handle))) {
+               DEBUGP("cannot find chain `%s'\n", chain);
                errno = ENOENT;
                return 0;
-       } else
-               hook--;
+       }
 
-       policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
-       if (policyoff != (*handle)->info.underflow[hook]) {
-               printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
-                      chain, policyoff, (*handle)->info.underflow[hook]);
+       if (!iptcc_is_builtin(c)) {
+               DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
+               errno = ENOENT;
                return 0;
        }
 
-       e = get_entry(*handle, policyoff);
-       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
-
        if (strcmp(policy, LABEL_ACCEPT) == 0)
-               t->verdict = -NF_ACCEPT - 1;
+               c->verdict = -NF_ACCEPT - 1;
        else if (strcmp(policy, LABEL_DROP) == 0)
-               t->verdict = -NF_DROP - 1;
+               c->verdict = -NF_DROP - 1;
        else {
                errno = EINVAL;
                return 0;
        }
 
-       ctrindex = entry2index(*handle, e);
-
        if (counters) {
                /* set byte and packet counters */
-               memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
-
-               (*handle)->counter_map[ctrindex].maptype
-                       = COUNTER_MAP_SET;
-
+               memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
+               c->counter_map.maptype = COUNTER_MAP_SET;
        } else {
-               (*handle)->counter_map[ctrindex]
-                       = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+               c->counter_map.maptype = COUNTER_MAP_NOMAP;
        }
 
        set_changed(*handle);
@@ -1721,20 +1901,76 @@ subtract_counters(STRUCT_COUNTERS *answer,
        answer->bcnt = a->bcnt - b->bcnt;
 }
 
+
+static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters,
+                          unsigned int index)
+{
+       newcounters->counters[index] = ((STRUCT_COUNTERS) { 0, 0});
+       DEBUGP_C("NOMAP => zero\n");
+}
+
+static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
+                               STRUCT_REPLACE *repl,
+                               unsigned int index,
+                               unsigned int mappos)
+{
+       /* Original read: X.
+        * Atomic read on replacement: X + Y.
+        * Currently in kernel: Z.
+        * Want in kernel: X + Y + Z.
+        * => Add in X + Y
+        * => Add in replacement read.
+        */
+       newcounters->counters[index] = repl->counters[mappos];
+       DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
+}
+
+static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
+                               STRUCT_REPLACE *repl,
+                               unsigned int index,
+                               unsigned int mappos,
+                               STRUCT_COUNTERS *counters)
+{
+       /* Original read: X.
+        * Atomic read on replacement: X + Y.
+        * Currently in kernel: Z.
+        * Want in kernel: Y + Z.
+        * => Add in Y.
+        * => Add in (replacement read - original read).
+        */
+       subtract_counters(&newcounters->counters[index],
+                         &repl->counters[mappos],
+                         counters);
+       DEBUGP_C("ZEROED => mappos %u\n", mappos);
+}
+
+static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
+                            unsigned int index,
+                            STRUCT_COUNTERS *counters)
+{
+       /* Want to set counter (iptables-restore) */
+
+       memcpy(&newcounters->counters[index], counters,
+               sizeof(STRUCT_COUNTERS));
+
+       DEBUGP_C("SET\n");
+}
+
+
 int
 TC_COMMIT(TC_HANDLE_T *handle)
 {
        /* Replace, then map back the counters. */
        STRUCT_REPLACE *repl;
        STRUCT_COUNTERS_INFO *newcounters;
-       unsigned int i;
+       struct chain_head *c;
+       int ret;
        size_t counterlen;
+       int new_number;
+       unsigned int new_size;
 
        CHECK(*handle);
 
-       counterlen = sizeof(STRUCT_COUNTERS_INFO)
-                       + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
-
 #if 0
        TC_DUMP_ENTRIES(*handle);
 #endif
@@ -1743,11 +1979,21 @@ TC_COMMIT(TC_HANDLE_T *handle)
        if (!(*handle)->changed)
                goto finished;
 
-       repl = malloc(sizeof(*repl) + (*handle)->entries.size);
+       new_number = iptcc_compile_table_prep(*handle, &new_size);
+       if (new_number < 0) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       repl = malloc(sizeof(*repl) + new_size);
        if (!repl) {
                errno = ENOMEM;
                return 0;
        }
+       memset(repl, 0, sizeof(*repl));
+
+       counterlen = sizeof(STRUCT_COUNTERS_INFO)
+                       + sizeof(STRUCT_COUNTERS) * new_number;
 
        /* These are the old counters we will get from kernel */
        repl->counters = malloc(sizeof(STRUCT_COUNTERS)
@@ -1757,7 +2003,6 @@ TC_COMMIT(TC_HANDLE_T *handle)
                errno = ENOMEM;
                return 0;
        }
-
        /* These are the counters we're going to put back, later. */
        newcounters = malloc(counterlen);
        if (!newcounters) {
@@ -1766,21 +2011,40 @@ TC_COMMIT(TC_HANDLE_T *handle)
                errno = ENOMEM;
                return 0;
        }
+       memset(newcounters, 0, counterlen);
 
        strcpy(repl->name, (*handle)->info.name);
-       repl->num_entries = (*handle)->new_number;
-       repl->size = (*handle)->entries.size;
-       memcpy(repl->hook_entry, (*handle)->info.hook_entry,
-              sizeof(repl->hook_entry));
-       memcpy(repl->underflow, (*handle)->info.underflow,
-              sizeof(repl->underflow));
+       repl->num_entries = new_number;
+       repl->size = new_size;
+
        repl->num_counters = (*handle)->info.num_entries;
        repl->valid_hooks = (*handle)->info.valid_hooks;
-       memcpy(repl->entries, (*handle)->entries.entrytable,
-              (*handle)->entries.size);
+
+       DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
+               repl->num_entries, repl->size, repl->num_counters);
+
+       ret = iptcc_compile_table(*handle, repl);
+       if (ret < 0) {
+               errno = ret;
+               free(repl->counters);
+               free(repl);
+               return 0;
+       }
+
+
+#ifdef IPTC_DEBUG2
+       {
+               int fd = open("/tmp/libiptc-so_set_replace.blob", 
+                               O_CREAT|O_WRONLY);
+               if (fd >= 0) {
+                       write(fd, repl, sizeof(*repl) + repl->size);
+                       close(fd);
+               }
+       }
+#endif
 
        if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
-                      sizeof(*repl) + (*handle)->entries.size) < 0) {
+                      sizeof(*repl) + repl->size) < 0) {
                free(repl->counters);
                free(repl);
                free(newcounters);
@@ -1789,50 +2053,65 @@ TC_COMMIT(TC_HANDLE_T *handle)
 
        /* Put counters back. */
        strcpy(newcounters->name, (*handle)->info.name);
-       newcounters->num_counters = (*handle)->new_number;
-       for (i = 0; i < (*handle)->new_number; i++) {
-               unsigned int mappos = (*handle)->counter_map[i].mappos;
-               switch ((*handle)->counter_map[i].maptype) {
-               case COUNTER_MAP_NOMAP:
-                       newcounters->counters[i]
-                               = ((STRUCT_COUNTERS){ 0, 0 });
-                       break;
-
-               case COUNTER_MAP_NORMAL_MAP:
-                       /* Original read: X.
-                        * Atomic read on replacement: X + Y.
-                        * Currently in kernel: Z.
-                        * Want in kernel: X + Y + Z.
-                        * => Add in X + Y
-                        * => Add in replacement read.
-                        */
-                       newcounters->counters[i] = repl->counters[mappos];
-                       break;
-
-               case COUNTER_MAP_ZEROED:
-                       /* Original read: X.
-                        * Atomic read on replacement: X + Y.
-                        * Currently in kernel: Z.
-                        * Want in kernel: Y + Z.
-                        * => Add in Y.
-                        * => Add in (replacement read - original read).
-                        */
-                       subtract_counters(&newcounters->counters[i],
-                                         &repl->counters[mappos],
-                                         &index2entry(*handle, i)->counters);
-                       break;
-
-               case COUNTER_MAP_SET:
-                       /* Want to set counter (iptables-restore) */
-
-                       memcpy(&newcounters->counters[i],
-                              &index2entry(*handle, i)->counters,
-                              sizeof(STRUCT_COUNTERS));
+       newcounters->num_counters = new_number;
+
+       list_for_each_entry(c, &(*handle)->chains, list) {
+               struct rule_head *r;
+
+               /* Builtin chains have their own counters */
+               if (iptcc_is_builtin(c)) {
+                       DEBUGP("counter for chain-index %u: ", c->foot_index);
+                       switch(c->counter_map.maptype) {
+                       case COUNTER_MAP_NOMAP:
+                               counters_nomap(newcounters, c->foot_index);
+                               break;
+                       case COUNTER_MAP_NORMAL_MAP:
+                               counters_normal_map(newcounters, repl,
+                                                   c->foot_index, 
+                                                   c->counter_map.mappos);
+                               break;
+                       case COUNTER_MAP_ZEROED:
+                               counters_map_zeroed(newcounters, repl,
+                                                   c->foot_index, 
+                                                   c->counter_map.mappos,
+                                                   &c->counters);
+                               break;
+                       case COUNTER_MAP_SET:
+                               counters_map_set(newcounters, c->foot_index,
+                                                &c->counters);
+                               break;
+                       }
+               }
 
-                       break;
+               list_for_each_entry(r, &c->rules, list) {
+                       DEBUGP("counter for index %u: ", r->index);
+                       switch (r->counter_map.maptype) {
+                       case COUNTER_MAP_NOMAP:
+                               counters_nomap(newcounters, r->index);
+                               break;
+
+                       case COUNTER_MAP_NORMAL_MAP:
+                               counters_normal_map(newcounters, repl,
+                                                   r->index, 
+                                                   r->counter_map.mappos);
+                               break;
+
+                       case COUNTER_MAP_ZEROED:
+                               counters_map_zeroed(newcounters, repl,
+                                                   r->index,
+                                                   r->counter_map.mappos,
+                                                   &r->entry->counters);
+                               break;
+
+                       case COUNTER_MAP_SET:
+                               counters_map_set(newcounters, r->index,
+                                                &r->entry->counters);
+                               break;
+                       }
                }
        }
 
+
 #ifdef KERNEL_64_USERSPACE_32
        {
                /* Kernel will think that pointer should be 64-bits, and get
@@ -1848,6 +2127,17 @@ TC_COMMIT(TC_HANDLE_T *handle)
        }
 #endif /* KERNEL_64_USERSPACE_32 */
 
+#ifdef IPTC_DEBUG2
+       {
+               int fd = open("/tmp/libiptc-so_set_add_counters.blob", 
+                               O_CREAT|O_WRONLY);
+               if (fd >= 0) {
+                       write(fd, newcounters, counterlen);
+                       close(fd);
+               }
+       }
+#endif
+
        if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
                       newcounters, counterlen) < 0) {
                free(repl->counters);
diff --git a/libiptc/linux_list.h b/libiptc/linux_list.h
new file mode 100644 (file)
index 0000000..abdcf88
--- /dev/null
@@ -0,0 +1,723 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({     type __dummy; \
+       typeof(x) __dummy2; \
+       (void)(&__dummy == &__dummy2); \
+       1; \
+})
+
+#define prefetch(x)            1
+
+/* empty define to make this work in userspace -HW */
+#define smp_wmb()
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head * new,
+               struct list_head * prev, struct list_head * next)
+{
+       new->next = next;
+       new->prev = prev;
+       smp_wmb();
+       next->prev = new;
+       prev->next = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+       __list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+                                       struct list_head *head)
+{
+       __list_add_rcu(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry.  Instead, either synchronize_kernel()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is
+ * empty _and_ checks that no other CPU might be
+ * in the process of still modifying either member
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ *
+ * @head: the list to test.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+       struct list_head *next = head->next;
+       return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+                                struct list_head *head)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+       struct list_head *at = head->next;
+
+       first->prev = head;
+       head->next = first;
+
+       last->next = at;
+       at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+                                   struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head);
+               INIT_LIST_HEAD(list);
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+               pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each     -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev  -       iterate over a list backwards
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+       for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+               pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * list_for_each_safe  -       iterate over a list safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
+                    prefetch(pos->member.prev);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.prev, typeof(*pos), member),  \
+                    prefetch(pos->member.prev))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ *                     list_for_each_entry_continue
+ * @pos:       the type * to use as a start point
+ * @head:      the head of the list
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+       ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue -      iterate over list of given type
+ *                     continuing after existing point
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member)                \
+       for (pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop counter.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_rcu   -       iterate over an rcu-protected list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+       for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+               pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __list_for_each_rcu(pos, head) \
+       for (pos = (head)->next; pos != (head); \
+               pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * list_for_each_safe_rcu      -       iterate over an rcu-protected list safe
+ *                                     against removal of list entry
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_safe_rcu(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * list_for_each_entry_rcu     -       iterate over rcu list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member)                     \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+                    prefetch(pos->member.next);                        \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member),  \
+                    ({ smp_read_barrier_depends(); 0;}),               \
+                    prefetch(pos->member.next))
+
+
+/**
+ * list_for_each_continue_rcu  -       iterate over an rcu-protected list
+ *                     continuing after existing point.
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+       for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+               (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+       struct hlist_node *first;
+};
+
+struct hlist_node {
+       struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+       return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+       return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+       struct hlist_node *next = n->next;
+       struct hlist_node **pprev = n->pprev;
+       *pprev = next;
+       if (next)
+               next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+       __hlist_del(n);
+       n->next = LIST_POISON1;
+       n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+       __hlist_del(n);
+       n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+       if (n->pprev)  {
+               __hlist_del(n);
+               INIT_HLIST_NODE(n);
+       }
+}
+
+#define hlist_del_rcu_init hlist_del_init
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+       struct hlist_node *first = h->first;
+       n->next = first;
+       if (first)
+               first->pprev = &n->next;
+       h->first = n;
+       n->pprev = &h->first;
+}
+
+
+/**
+ * hlist_add_head_rcu - adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry(), but only if smp_read_barrier_depends()
+ * is used to prevent memory-consistency problems on Alpha CPUs.
+ * Regardless of the type of CPU, the list-traversal primitive
+ * must be guarded by rcu_read_lock().
+ *
+ * OK, so why don't we have an hlist_for_each_entry_rcu()???
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+                                       struct hlist_head *h)
+{
+       struct hlist_node *first = h->first;
+       n->next = first;
+       n->pprev = &h->first;
+       smp_wmb();
+       if (first)
+               first->pprev = &n->next;
+       h->first = n;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       n->pprev = next->pprev;
+       n->next = next;
+       next->pprev = &n->next;
+       *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       next->next = n->next;
+       n->next = next;
+       next->pprev = &n->next;
+
+       if(next->next)
+               next->next->pprev  = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+       for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+            pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+       for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+            pos = n)
+
+/**
+ * hlist_for_each_entry        - iterate over list of given type
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member)                   \
+       for (pos = (head)->first;                                        \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member)                \
+       for (pos = (pos)->next;                                          \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member)                    \
+       for (; pos && ({ prefetch(pos->next); 1;}) &&                    \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:      the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @n:         another &struct hlist_node to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member)           \
+       for (pos = (head)->first;                                        \
+            pos && ({ n = pos->next; 1; }) &&                           \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @pos:       the &struct hlist_node to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member)               \
+       for (pos = (head)->first;                                        \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
+
+#endif
diff --git a/libiptc/linux_stddef.h b/libiptc/linux_stddef.h
new file mode 100644 (file)
index 0000000..56416f1
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _LINUX_STDDEF_H
+#define _LINUX_STDDEF_H
+
+#undef NULL
+#if defined(__cplusplus)
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                     \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({     type __dummy; \
+       typeof(x) __dummy2; \
+       (void)(&__dummy == &__dummy2); \
+       1; \
+})
+
+
+#endif