commit all current changes
diff --git a/libiptc/libiptc.c b/libiptc/libiptc.c
index 8636c8b..ad20251 100644
--- a/libiptc/libiptc.c
+++ b/libiptc/libiptc.c
@@ -1,4 +1,4 @@
-/* Library which manipulates firewall rules.  Version $Revision: 1.40 $ */
+/* Library which manipulates firewall rules.  Version $Revision: 1.41 $ */
 
 /* Architecture of firewall rules is as follows:
  *
@@ -15,23 +15,19 @@
  * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
  *	- Reimplementation of chain cache to use offsets instead of entries
  * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
- * 	- performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
+ * 	- speed 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.  
+ * 2003-Jun-30: Harald Welte <laforge@netfilter.org>:
+ * 	- reimplementation from scratch. *sigh*.  I hope nobody has to touch 
+ * 	  this code ever again.
  */
+#include "linux_listhelp.h"
 
 #ifndef IPT_LIB_DIR
 #define IPT_LIB_DIR "/usr/local/lib/iptables"
 #endif
 
-#ifndef __OPTIMIZE__
-STRUCT_ENTRY_TARGET *
-GET_TARGET(STRUCT_ENTRY *e)
-{
-	return (void *)e + e->target_offset;
-}
-#endif
-
 static int sockfd = -1;
 static void *iptc_fn = NULL;
 
@@ -64,25 +60,55 @@
 	char error[TABLE_MAXNAMELEN];
 };
 
-struct chain_cache
+struct rule_head
 {
+	struct list_head list;		/* list of rules in chain */
+	
+	struct chain_head *chain;	/* we're part of this chain */
+
+	struct chain_head *jumpto;	/* target of this rule, in case
+					   it is a jump rule */
+
+	struct counter_map counter_map;
+
+	unsigned int size;		/* size of rule */
+	STRUCT_ENTRY *entry_blob;	/* pointer to entry in blob */
+	STRUCT_ENTRY entry[0];
+};
+
+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;
+	struct list_head rules;
+	struct rule_head *firstrule; 	/* first (ERROR) rule */
+	struct rule_head *lastrule;	/* last (RETURN) rule */
 };
 
 STRUCT_TC_HANDLE
 {
 	/* Have changes been made? */
 	int changed;
-	/* Size in here reflects original state. */
+
+	/* linked list of chains in this table */
+	struct list_head chains;
+	
+	/* current position of first_chain() / next_chain() */
+	struct chain_head *chain_iterator_cur;
+
+	/* current position of first_rule() / next_rule() */
+	struct rule_head *rule_iterator_cur;
+
+	/* the structure we receive from getsockopt() */
 	STRUCT_GETINFO info;
 
-	struct counter_map *counter_map;
 	/* Array of hook names */
 	const char **hooknames;
+#if 0
+	/* Size in here reflects original state. */
+
 
 	/* Cached position of chain heads (NULL = no cache). */
 	unsigned int cache_num_chains;
@@ -97,6 +123,7 @@
 
 	/* Number in here reflects current state. */
 	unsigned int new_number;
+#endif
 	STRUCT_GET_ENTRIES entries;
 };
 
@@ -113,6 +140,97 @@
 #define CHECK(h)
 #endif
 
+static struct rule_head *ruleh_alloc(unsigned int size)
+{
+	struct rule_head *ruleh = malloc(sizeof(*ruleh)+size);
+	if (!ruleh)
+		return NULL;
+	
+	memset(ruleh, 0, sizeof(*ruleh)+size);
+	ruleh->size = size;
+
+	return ruleh;
+}
+
+static void ruleh_free(struct rule_head *ruleh)
+{
+	list_del(&ruleh->list);
+	free(ruleh);
+}
+
+static struct chain_head *chainh_alloc(TC_HANDLE_T h, const char *name)
+{
+	struct chain_head *chainh = malloc(sizeof(*chainh));
+	if (!chainh)
+		return NULL;
+
+	memset(chainh, 0, sizeof(*chainh));
+	strncpy(chainh->name, name, sizeof(&chainh->name));
+	list_append(&chainh->list, &h->chains);
+
+	return chainh;
+}
+
+static void
+chainh_clean(struct chain_head *chainh)
+{
+	/* FIXME */
+	struct list_head *cur_item, *item2;
+
+	list_for_each_safe(cur_item, item2, &chainh->rules) {
+		struct rule_head *ruleh = list_entry(cur_item, 
+						     struct rule_head,
+						    list);
+		ruleh_free(ruleh);
+	}
+}
+
+static void 
+chainh_free(struct chain_head *chainh)
+{
+	chainh_clean(chainh);
+	list_del(&chainh->list);
+}
+
+static struct chain_head *
+chainh_find(TC_HANDLE_T h, const IPT_CHAINLABEL name)
+{
+	struct list_head *cur;
+
+	list_for_each(cur, &h->chains) {
+		struct chain_head *ch = list_entry(cur, struct chain_head, 
+						   list);
+		if (!strcmp(name, ch->name))
+			return ch;
+	}
+	return NULL;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+	return chainh_find(handle, name);
+}
+
+
+/* 
+ * functions that directly operate on the blob 
+ */
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+	return (void *)e - (void *)h->entries.entrytable;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+	return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+/* needed by entry2index */
 static inline int
 get_number(const STRUCT_ENTRY *i,
 	   const STRUCT_ENTRY *seek,
@@ -164,24 +282,28 @@
 	return ret;
 }
 
-static inline STRUCT_ENTRY *
-get_entry(TC_HANDLE_T h, unsigned int offset)
-{
-	return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
-}
-
-static inline unsigned long
-entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
-{
-	return (void *)e - (void *)h->entries.entrytable;
-}
-
 static inline unsigned long
 index2offset(TC_HANDLE_T h, unsigned int index)
 {
 	return entry2offset(h, index2entry(h, index));
 }
 
+static char *
+get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+{
+	STRUCT_ENTRY *e;
+
+	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();
+	}
+
+	return (char *)GET_TARGET(e)->data;
+}
+
+#if 0
 static inline STRUCT_ENTRY *
 offset2entry(TC_HANDLE_T h, unsigned int offset)
 {
@@ -195,24 +317,12 @@
 }
 
 
-static const char *
-get_errorlabel(TC_HANDLE_T h, unsigned int offset)
-{
-	STRUCT_ENTRY *e;
-
-	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();
-	}
-
-	return (const char *)GET_TARGET(e)->data;
-}
+#endif
 
 /* Allocate handle of given size */
 static TC_HANDLE_T
-alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
+alloc_tc_handle(const char *tablename, unsigned int size, 
+		unsigned int num_rules)
 {
 	size_t len;
 	TC_HANDLE_T h;
@@ -227,23 +337,162 @@
 	}
 
 	h->changed = 0;
-	h->cache_num_chains = 0;
-	h->cache_chain_heads = NULL;
-	h->counter_map = (void *)h
-		+ sizeof(STRUCT_TC_HANDLE)
-		+ size;
+
 	strcpy(h->info.name, tablename);
 	strcpy(h->entries.name, tablename);
+	INIT_LIST_HEAD(&h->chains);
 
 	return h;
 }
 
+/* get the name of the chain that we jump to */
+static char *
+parse_jumptarget(const STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+	STRUCT_ENTRY *jumpto;
+	int spos, labelidx;
+
+	if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0) {
+		/* called for non-standard target */
+		return "__FIXME";
+	}
+	/* Standard target: evaluate */
+	spos = *(int *)GET_TARGET(e)->data;
+	if (spos < 0) {
+		return "__FIXME";
+	}
+
+	jumpto = get_entry(h, spos);
+
+	/* Fall through rule */
+	if (jumpto == (void *)e + e->next_offset)
+		return "";
+
+	/* Must point to head of a chain: ie. after error rule */
+	/* FIXME: this needs to deal with internal jump targets */
+	labelidx = entry2index(h, jumpto) - 1;
+	return get_errorlabel(h, index2offset(h, labelidx));
+}
+
+/* parser functions */
+
+struct rule_head *
+append_entrycopy(const STRUCT_ENTRY *e, struct rule_head *prev)
+{
+	struct rule_head *ruleh = ruleh_alloc(e->next_offset);
+	if (!ruleh)
+		return NULL;
+	
+	memcpy(&ruleh->entry, e, e->next_offset);
+	ruleh->chain = prev->chain;
+	ruleh->entry_blob = e;
+	list_append(&ruleh->list, &prev->list);
+
+	return ruleh;
+}
+
+/* have to return 0 on success, bcf ENTRY_ITERATE */
+static inline int 
+parse_entry(const STRUCT_ENTRY *e, TC_HANDLE_T h, struct chain_head **curchain)
+{
+	int i;
+	union tgt_u {
+		STRUCT_ENTRY_TARGET ent;
+		STRUCT_STANDARD_TARGET std;
+		struct ipt_error_target err;
+	} *tgt;
+
+	struct rule_head *lastrule = list_entry((*curchain)->rules.prev,
+						 struct rule_head, list);
+	struct rule_head *newrule;
+
+	tgt = (union tgt_u *) GET_TARGET(e);
+
+	if (e->target_offset == sizeof(STRUCT_ENTRY)
+	    && (strcmp(tgt->ent.u.user.name, IPT_STANDARD_TARGET) == 0)) {
+		/* jump to somewhere else */
+		char *targname;
+		struct chain_head *chainh;
+
+		newrule = append_entrycopy(e, lastrule);
+
+		targname = parse_jumptarget(e, h);
+		if (!(chainh = find_label(targname, h))) {
+			chainh = chainh_alloc(h, targname);
+		}
+		if (!chainh) {
+			errno = ENOMEM;
+			return 1;
+		}
+		newrule->jumpto = chainh;
+
+	} else if (e->target_offset == sizeof(STRUCT_ENTRY)
+		   && e->next_offset == sizeof(STRUCT_ENTRY)
+		   			+ ALIGN(sizeof(struct ipt_error_target))
+		   && !strcmp(tgt->ent.u.user.name, ERROR_TARGET)) {
+		/* chain head */
+		*curchain = chainh_find(h, tgt->err.error);
+		if (!(*curchain)) {
+			*curchain = chainh_alloc(h, tgt->err.error);
+			/* FIXME: error handling */
+		}
+		newrule = append_entrycopy(e, lastrule);
+		(*curchain)->firstrule = newrule;
+
+	} else if (e->target_offset == sizeof(STRUCT_ENTRY)
+		   && e->next_offset == sizeof(STRUCT_ENTRY)
+		   			+ ALIGN(sizeof(STRUCT_STANDARD_TARGET))
+		   && tgt->std.verdict == RETURN) {
+		/* chain end */
+		newrule = append_entrycopy(e, lastrule);
+		(*curchain)->lastrule = newrule;
+		*curchain = NULL;
+	} else {
+		/* normal rule */
+		newrule = append_entrycopy(e, lastrule);
+	}
+
+	/* create counter map entry */
+	newrule->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
+	newrule->counter_map.mappos = entry2index(h, e);
+
+	/* iterate over hook_entries, needed to connect builtin
+	 * chains with hook numbers */
+	for (i = 0; i < NUMHOOKS; i++) {
+		if (!(h->info.valid_hooks & (1 << i)))
+			continue;
+		if (h->info.hook_entry[i] == entry2offset(h, e)) {
+			/* found hook entry point */
+			if (*curchain)
+				(*curchain)->hooknum = i;
+		}
+		if (h->info.underflow[i] == entry2offset(h, e)) {
+			/* found underflow point */
+		}
+	}
+
+	return 0;
+}
+
+static int parse_ruleset(TC_HANDLE_T h)
+{
+	struct chain_head *curchain;
+	
+	/* iterate over ruleset; create linked list of rule_head/chain_head */
+	if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size, 
+		      parse_entry, h, &curchain)) {
+		/* some error happened while iterating */
+		return 0;
+	}
+
+	return 1;
+}
+
 TC_HANDLE_T
 TC_INIT(const char *tablename)
 {
 	TC_HANDLE_T h;
 	STRUCT_GETINFO info;
-	unsigned int i;
 	int tmp;
 	socklen_t s;
 
@@ -269,7 +518,7 @@
 	if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
 		return NULL;
 
-	if ((h = alloc_handle(info.name, info.size, info.num_entries))
+	if ((h = alloc_tc_handle(info.name, info.size, info.num_entries))
 	    == NULL) {
 		close(sockfd);
 		sockfd = -1;
@@ -295,11 +544,8 @@
 
 	/* 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->new_number = h->info.num_entries;
+	//
 	h->entries.size = h->info.size;
 
 	tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
@@ -313,16 +559,29 @@
 	}
 
 	CHECK(h);
+	parse_ruleset(h);
+
 	return h;
 }
 
 void
 TC_FREE(TC_HANDLE_T *h)
 {
+	struct list_head *cur_item, *item2;
+
 	close(sockfd);
 	sockfd = -1;
-	if ((*h)->cache_chain_heads)
-		free((*h)->cache_chain_heads);
+
+	/* free all chains */
+	list_for_each_safe(cur_item, item2, &(*h)->chains) {
+		struct chain_head *chead = list_entry(cur_item,
+						      struct chain_head,
+						      list);
+		chainh_free(chead);
+	}
+
+	/* FIXME: free all other ressources we might be using */
+
 	free(*h);
 	*h = NULL;
 }
@@ -336,6 +595,7 @@
 
 static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
  
+#if 0
 void
 TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
 {
@@ -376,180 +636,13 @@
 	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->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)
@@ -557,6 +650,7 @@
 	return 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)
@@ -593,39 +687,38 @@
 		handle->entries.size, off);
 	abort();
 }
+#endif
 
 /* 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))
-		return NULL;
+	struct chain_head *firsthead = list_entry((*handle)->chains.next,
+						   struct chain_head, list);
+	(*handle)->chain_iterator_cur = firsthead;
 
-	(*handle)->cache_chain_iteration
-		= &(*handle)->cache_chain_heads[0];
-
-	return (*handle)->cache_chain_iteration->name;
+	return firsthead->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 *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list);
+	(*handle)->chain_iterator_cur = next;
 
-	if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
-	    == (*handle)->cache_num_chains)
+	if (&next->list == &(*handle)->chains)
 		return NULL;
 
-	return (*handle)->cache_chain_iteration->name;
+	return next->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;
 
 	c = find_label(chain, *handle);
 	if (!c) {
@@ -634,22 +727,26 @@
 	}
 
 	/* Empty chain: single return/policy rule */
-	if (c->start_off == c->end_off)
+	if (list_empty(&c->rules))
 		return NULL;
 
-	(*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
-	return offset2entry(*handle, c->start_off);
+	r = list_entry(c->rules.next, struct rule_head, list);
+	(*handle)->rule_iterator_cur = r;
+
+	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 = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list);
+
+	if (&r->list == &r->chain->rules)
 		return NULL;
 
-	return (void *)prev + prev->next_offset;
+	/* NOTE: prev is without any influence ! */
+	return r->entry;
 }
 
 #if 0
@@ -695,8 +792,6 @@
 target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
 {
 	int spos;
-	unsigned int labelidx;
-	STRUCT_ENTRY *jumpto;
 
 	/* To avoid const warnings */
 	STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
@@ -716,21 +811,24 @@
 		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);
+		fprintf(stderr, "ERROR: entry %p not a valid target (%d)\n",
+			e, spos);
 		abort();
 	}
 
-	jumpto = get_entry(handle, spos);
+#if 0
+//	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 */
+	/* FIXME: this needs to deal with internal jump targets */
 	labelidx = entry2index(handle, jumpto) - 1;
 	return get_errorlabel(handle, index2offset(handle, labelidx));
+#endif
+	return "";
 }
 
 /* Returns a pointer to the target name of this position. */
@@ -761,23 +859,31 @@
 	      STRUCT_COUNTERS *counters,
 	      TC_HANDLE_T *handle)
 {
-	unsigned int start;
 	STRUCT_ENTRY *e;
+	struct chain_head *chainh;
+	struct rule_head *ruleh;
 	int hook;
 
 	hook = TC_BUILTIN(chain, *handle);
-	if (hook != 0)
-		start = (*handle)->info.hook_entry[hook-1];
-	else
+	if (hook == 0)
 		return NULL;
 
-	e = get_entry(*handle, get_chain_end(*handle, start));
+	chainh = find_label(chain, *handle);
+	if (!chainh) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	ruleh = chainh->lastrule;
+
+	e = ruleh->entry;
 	*counters = e->counters;
 
 	return target_name(*handle, e);
 }
 
-static inline int
+#if 0
+static int
 correct_verdict(STRUCT_ENTRY *e,
 		char *base,
 		unsigned int offset, int delta_offset)
@@ -809,149 +915,9 @@
 	set_changed(*handle);
 	return 1;
 }
+#endif
 
-/* 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;
 
-	if (offset >= (*handle)->entries.size) {
-		errno = EINVAL;
-		return 0;
-	}
-
-	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;
-	}
-
-	free(*handle);
-	*handle = newh;
-
-	return set_verdict(offset, rules_size, handle);
-}
-
-static int
-delete_rules(unsigned int num_rules, unsigned int rules_size,
-	     unsigned int offset, unsigned int num_rules_offset,
-	     TC_HANDLE_T *handle)
-{
-	unsigned int i;
-
-	if (offset + rules_size > (*handle)->entries.size) {
-		errno = EINVAL;
-		return 0;
-	}
-
-	/* 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();
-		}
-
-		/* 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();
-		}
-	}
-
-	/* 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;
-
-	/* Fix the chain cache */
-	if (!correct_cache(*handle, offset+rules_size, -(int)rules_size))
-		return 0;
-
-	return set_verdict(offset, -(int)rules_size, handle);
-}
 
 static int
 standard_map(STRUCT_ENTRY *e, int verdict)
@@ -979,7 +945,7 @@
 	   unsigned int offset,
 	   STRUCT_ENTRY_TARGET *old)
 {
-	STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+	STRUCT_ENTRY_TARGET *t = (STRUCT_ENTRY_TARGET *)GET_TARGET(e);
 
 	/* Save old target (except data, which we don't change, except for
 	   standard case, where we don't care). */
@@ -1003,11 +969,14 @@
 		return 0;
 	} else {
 		/* Maybe it's an existing chain name. */
-		struct chain_cache *c;
+		struct chain_head *c;
 
+#if 0
+		/* FIXME */
 		c = find_label(t->u.user.name, handle);
 		if (c)
 			return standard_map(e, c->start_off);
+#endif
 	}
 
 	/* Must be a module?  If not, kernel will reject... */
@@ -1028,18 +997,32 @@
 	*t = *old;
 }
 
-/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+static struct rule_head *
+ruleh_get_n(struct chain_head *chead, int rulenum) 
+{
+	int i = 0;
+	struct list_head *list;
+
+	
+	list_for_each(list, &chead->rules) {
+		struct rule_head *rhead = list_entry(list, struct rule_head, 
+							list);
+		i++;
+		if (i == rulenum)
+			return rhead;
+	}
+	return NULL;
+}
+
+/* Insert the entry `e' in chain `chain' into position `rulenum'. */
 int
 TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
 		const STRUCT_ENTRY *e,
 		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 *prev;
 
 	iptc_fn = TC_INSERT_ENTRY;
 	if (!(c = find_label(chain, *handle))) {
@@ -1047,24 +1030,16 @@
 		return 0;
 	}
 
-	chainindex = offset2index(*handle, c->start_off);
-
-	tmp = index2entry(*handle, chainindex + rulenum);
-	if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
+	prev = ruleh_get_n(c, rulenum-1);
+	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))
-		return 0;
+	if (append_entrycopy(e, prev))
+		return 1;
 
-	ret = insert_rules(1, e->next_offset, e, offset,
-			   chainindex + rulenum, rulenum == 0, handle);
-	unmap_target((STRUCT_ENTRY *)e, &old);
-	return ret;
+	return 0;
 }
 
 /* Atomically replace rule `rulenum' in `chain' with `fw'. */
@@ -1074,11 +1049,8 @@
 		 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 *repl;
 
 	iptc_fn = TC_REPLACE_ENTRY;
 
@@ -1087,54 +1059,43 @@
 		return 0;
 	}
 
-	chainindex = offset2index(*handle, c->start_off);
-
-	tmp = index2entry(*handle, chainindex + rulenum);
-	if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
+	repl = ruleh_get_n(c, rulenum);
+	if (!repl) {
 		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 (!append_entrycopy(e, repl)) {
+		errno = ENOMEM;
 		return 0;
+	}
 
-	if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
-		return 0;
-
-	ret = insert_rules(1, e->next_offset, e, offset,
-			   chainindex + rulenum, 1, handle);
-	unmap_target((STRUCT_ENTRY *)e, &old);
-	return ret;
+	ruleh_free(repl);
+	return 1;
 }
 
-/* Append entry `fw' to chain `chain'.  Equivalent to insert with
+/* Append entry `e' to chain `chain'.  Equivalent to insert with
    rulenum = length of chain. */
 int
 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 *rhead;
 
 	iptc_fn = TC_APPEND_ENTRY;
+
 	if (!(c = find_label(chain, *handle))) {
 		errno = ENOENT;
 		return 0;
 	}
 
-	if (!map_target(*handle, (STRUCT_ENTRY *)e,
-			c->end_off, &old))
-		return 0;
-
-	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;
+	rhead = list_entry(c->rules.prev, struct rule_head, list);
+	if(append_entrycopy(e, rhead))
+		return 1;
+	
+	return 0;
 }
 
 static inline int
@@ -1183,16 +1144,15 @@
 	const STRUCT_ENTRY *b,
 	unsigned char *matchmask);
 
-/* Delete the first rule in `chain' which matches `fw'. */
+/* Delete the first rule in `chain' which matches `origfw'. */
 int
 TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
 		const STRUCT_ENTRY *origfw,
 		unsigned char *matchmask,
 		TC_HANDLE_T *handle)
 {
-	unsigned int offset;
-	struct chain_cache *c;
-	STRUCT_ENTRY *e, *fw;
+	struct chain_head *c;
+	struct list_head *cur, *cur2;
 
 	iptc_fn = TC_DELETE_ENTRY;
 	if (!(c = find_label(chain, *handle))) {
@@ -1200,40 +1160,15 @@
 		return 0;
 	}
 
-	fw = malloc(origfw->next_offset);
-	if (fw == NULL) {
-		errno = ENOMEM;
-		return 0;
-	}
-
-	for (offset = c->start_off; offset < c->end_off;
-	     offset += e->next_offset) {
-		STRUCT_ENTRY_TARGET discard;
-
-		memcpy(fw, origfw, origfw->next_offset);
-
-		/* FIXME: handle this in is_same --RR */
-		if (!map_target(*handle, fw, offset, &discard)) {
-			free(fw);
-			return 0;
-		}
-		e = get_entry(*handle, offset);
-
-#if 0
-		printf("Deleting:\n");
-		dump_entry(newe);
-#endif
-		if (is_same(e, fw, matchmask)) {
-			int ret;
-			ret = delete_rules(1, e->next_offset,
-					   offset, entry2index(*handle, e),
-					   handle);
-			free(fw);
-			return ret;
+	list_for_each_safe(cur, cur2, &c->rules) {
+		struct rule_head *rhead = list_entry(cur, struct rule_head, 
+							list);
+		if (is_same(rhead->entry, origfw, matchmask)) {
+			ruleh_free(rhead);
+			return 1;
 		}
 	}
 
-	free(fw);
 	errno = ENOENT;
 	return 0;
 }
@@ -1244,33 +1179,25 @@
 		    unsigned int rulenum,
 		    TC_HANDLE_T *handle)
 {
-	unsigned int index;
-	int ret;
-	STRUCT_ENTRY *e;
-	struct chain_cache *c;
+	struct chain_head *chainh;
+	struct rule_head *rhead;
 
 	iptc_fn = TC_DELETE_NUM_ENTRY;
-	if (!(c = find_label(chain, *handle))) {
+
+	if (!(chainh = find_label(chain, *handle))) {
 		errno = ENOENT;
 		return 0;
 	}
 
-	index = offset2index(*handle, c->start_off) + rulenum;
-
-	if (index >= offset2index(*handle, c->end_off)) {
+	rhead = ruleh_get_n(chainh, rulenum);
+	if (!rhead) {
 		errno = E2BIG;
 		return 0;
 	}
 
-	e = index2entry(*handle, index);
-	if (e == NULL) {
-		errno = EINVAL;
-		return 0;
-	}
+	ruleh_free(rhead);
 
-	ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
-			   index, handle);
-	return ret;
+	return 1;
 }
 
 /* Check the packet `fw' on chain `chain'.  Returns the verdict, or
@@ -1288,46 +1215,40 @@
 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 list_head *cur, *cur2;
+	struct chain_head *chainh;
 
-	iptc_fn = TC_FLUSH_ENTRIES;
-	if (!(c = find_label(chain, *handle))) {
+	if (!(chainh = 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_safe(cur, cur2, &chainh->rules) {
+		struct rule_head *ruleh = list_entry(cur, struct rule_head, 
+							list);
+		/* don't free the entry and policy/return entries */
+		if (ruleh != chainh->firstrule && ruleh != chainh->lastrule)
+			ruleh_free(ruleh);
+	}
+	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 list_head *cur;
 
 	if (!(c = 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(cur, c->rules.next) {
+		struct rule_head *r = list_entry(cur, struct rule_head, list);
+		if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+			r->counter_map.maptype = COUNTER_MAP_ZEROED;
 	}
 	set_changed(*handle);
 
@@ -1340,28 +1261,19 @@
 		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 = find_label(chain, *handle) )
+	      || !(r = ruleh_get_n(c, rulenum))) {
 		errno = ENOENT;
 		return NULL;
 	}
 
-	chainindex = offset2index(*handle, c->start_off);
-	end = offset2index(*handle, c->end_off);
-
-	if (chainindex + rulenum > end) {
-		errno = E2BIG;
-		return NULL;
-	}
-
-	e = index2entry(*handle, chainindex + rulenum);
-
-	return &e->counters;
+	return &r->entry->counters;
 }
 
 int
@@ -1370,32 +1282,20 @@
 		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 = find_label(chain, *handle))
+	      || !(r = ruleh_get_n(c, rulenum))) {
 		errno = ENOENT;
 		return 0;
 	}
 
-	chainindex = offset2index(*handle, c->start_off);
-	end = offset2index(*handle, c->end_off);
-
-	if (chainindex + rulenum > end) {
-		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);
 
@@ -1409,31 +1309,20 @@
 	       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_SET_COUNTER;
 	CHECK(*handle);
 
-	if (!(c = find_label(chain, *handle))) {
+	if (!(c = find_label(chain, *handle))
+	      || !(r = ruleh_get_n(c, rulenum))) {
 		errno = ENOENT;
 		return 0;
 	}
-
-	chainindex = offset2index(*handle, c->start_off);
-	end = offset2index(*handle, c->end_off);
-
-	if (chainindex + rulenum > end) {
-		errno = E2BIG;
-		return 0;
-	}
-
-	e = index2entry(*handle, chainindex + rulenum);
-
-	(*handle)->counter_map[chainindex + rulenum].maptype
-		= COUNTER_MAP_SET;
-
-	memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+	
+	r->counter_map.maptype = COUNTER_MAP_SET;
+	memcpy(&r->entry->counters, counters, sizeof(STRUCT_COUNTERS));
 
 	set_changed(*handle);
 
@@ -1447,13 +1336,16 @@
 TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
 {
 	int ret;
-	struct {
+	struct chainstart {
 		STRUCT_ENTRY head;
 		struct ipt_error_target name;
+	} *newc1;
+	struct chainend {
 		STRUCT_ENTRY ret;
 		STRUCT_STANDARD_TARGET target;
-	} newc;
-	unsigned int destination;
+	} *newc2;
+	struct rule_head *newr1, *newr2;
+	struct chain_head *chead;
 
 	iptc_fn = TC_CREATE_CHAIN;
 
@@ -1473,42 +1365,53 @@
 		return 0;
 	}
 
-	memset(&newc, 0, sizeof(newc));
-	newc.head.target_offset = sizeof(STRUCT_ENTRY);
-	newc.head.next_offset
+	chead = chainh_alloc(*handle, chain);
+	if (!chead) {
+		errno = ENOMEM;
+		return 0;
+	}
+	
+	newr1 = ruleh_alloc(sizeof(*newc1));
+	if (!newr1) {
+		chainh_free(chead);
+		return 0;
+	}
+	newc1 = (struct chainstart *) newr1->entry;
+
+	newr2 = ruleh_alloc(sizeof(*newc2));
+	if (!newr2) {
+		chainh_free(chead);
+		ruleh_free(newr1);
+		return 0;
+	}
+	newc2 = (struct chainend *) newr2->entry;
+
+	newc1->head.target_offset = sizeof(STRUCT_ENTRY);
+	newc1->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);
+	strcpy(newc1->name.t.u.user.name, ERROR_TARGET);
+	newc1->name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
+	strcpy(newc1->name.error, chain);
 
-	newc.ret.target_offset = sizeof(STRUCT_ENTRY);
-	newc.ret.next_offset
+	newc2->ret.target_offset = sizeof(STRUCT_ENTRY);
+	newc2->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
+	strcpy(newc2->target.target.u.user.name, STANDARD_TARGET);
+	newc2->target.target.u.target_size
 		= ALIGN(sizeof(STRUCT_STANDARD_TARGET));
-	newc.target.verdict = RETURN;
+	newc2->target.verdict = RETURN;
 
-	destination = index2offset(*handle, (*handle)->new_number -1);
+	list_prepend(&newr1->list, &chead->rules);
+	chead->firstrule = newr1;
+	list_append(&newr2->list, &chead->rules);
+	chead->lastrule = newr2;
 
-	/* 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;
+	return 1;
 }
 
+#if 0
 static int
 count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
 {
@@ -1542,17 +1445,32 @@
 		      count_ref, c->start_off, ref);
 	return 1;
 }
+#endif
+
+static unsigned int
+count_rules(struct chain_head *chainh)
+{
+	unsigned int numrules = 0;
+	struct list_head *cur;
+
+	list_for_each(cur, &chainh->rules) {
+		numrules++;
+	}
+
+	if (numrules <=2)
+		return 0;
+	else
+		return numrules-2;
+}
 
 /* Deletes a 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;
+	struct chain_head *chainh;
 
+#if 0
 	if (!TC_GET_REFERENCES(&references, chain, handle))
 		return 0;
 
@@ -1567,28 +1485,20 @@
 		errno = EMLINK;
 		return 0;
 	}
+#endif 
 
-	if (!(c = find_label(chain, *handle))) {
+	if (!(chainh = find_label(chain, *handle))) {
 		errno = ENOENT;
 		return 0;
 	}
 
-	if (c->start_off != c->end_off) {
+	if (!(count_rules(chainh) == 0)) {
 		errno = ENOTEMPTY;
 		return 0;
 	}
 
-	/* Need label index: preceeds chain start */
-	labelidx = offset2index(*handle, c->start_off) - 1;
-	labeloff = index2offset(*handle, labelidx);
-
-	start = offset2entry(*handle, c->start_off);
-
-	ret = delete_rules(2,
-			   get_entry(*handle, labeloff)->next_offset
-			   + start->next_offset,
-			   labeloff, labelidx, handle);
-	return ret;
+	chainh_free(chainh);
+	return 1;
 }
 
 /* Renames a chain. */
@@ -1596,8 +1506,8 @@
 		    const IPT_CHAINLABEL newname,
 		    TC_HANDLE_T *handle)
 {
-	unsigned int labeloff, labelidx;
-	struct chain_cache *c;
+	struct chain_head *c;
+	struct rule_head *ruleh;
 	struct ipt_error_target *t;
 
 	iptc_fn = TC_RENAME_CHAIN;
@@ -1624,22 +1534,14 @@
 		return 0;
 	}
 
-	/* Need label index: preceeds chain start */
-	labelidx = offset2index(*handle, c->start_off) - 1;
-	labeloff = index2offset(*handle, labelidx);
+	ruleh = list_entry(&c->rules.next, struct rule_head, list);
 
 	t = (struct ipt_error_target *)
-		GET_TARGET(get_entry(*handle, labeloff));
+		GET_TARGET(ruleh->entry);
 
 	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);
-
-	set_changed(*handle);
-
 	return 1;
 }
 
@@ -1650,8 +1552,10 @@
 	      STRUCT_COUNTERS *counters,
 	      TC_HANDLE_T *handle)
 {
+	int ctrindex;
 	unsigned int hook;
-	unsigned int policyoff, ctrindex;
+	struct chain_head *chainh;
+	struct rule_head *policyrh;
 	STRUCT_ENTRY *e;
 	STRUCT_STANDARD_TARGET *t;
 
@@ -1664,15 +1568,18 @@
 	} 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 (!(chainh = find_label(chain, *handle))) {
+		errno = ENOENT;
 		return 0;
 	}
 
-	e = get_entry(*handle, policyoff);
-	t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+	policyrh = chainh->lastrule;
+	if (policyrh) {
+		printf("ERROR: Policy for `%s' non-existant", chain);
+		return 0;
+	}
+
+	t = (STRUCT_STANDARD_TARGET *)GET_TARGET(policyrh->entry);
 
 	if (strcmp(policy, LABEL_ACCEPT) == 0)
 		t->verdict = -NF_ACCEPT - 1;
@@ -1689,12 +1596,11 @@
 		/* set byte and packet counters */
 		memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
 
-		(*handle)->counter_map[ctrindex].maptype
-			= COUNTER_MAP_SET;
+		policyrh->counter_map.maptype = COUNTER_MAP_SET;
 
 	} else {
-		(*handle)->counter_map[ctrindex]
-			= ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+		policyrh->counter_map.maptype = COUNTER_MAP_NOMAP;
+		policyrh->counter_map.mappos = 0;
 	}
 
 	set_changed(*handle);