 include/linux/netfilter_ipv4/ip_tables.h      |    2 
 net/ipv4/netfilter/Kconfig                    |   35 
 net/ipv4/netfilter/Makefile                   |    3 
 net/ipv4/netfilter/ip_tables.c                |   12 
 net/ipv4/netfilter/nf-hipac/Makefile          |   33 
 net/ipv4/netfilter/nf-hipac/dimtree.c         | 4307 ++++++++++++++++++++++++++
 net/ipv4/netfilter/nf-hipac/dimtree.h         |  280 +
 net/ipv4/netfilter/nf-hipac/global.c          |  964 +++++
 net/ipv4/netfilter/nf-hipac/global.h          |  387 ++
 net/ipv4/netfilter/nf-hipac/hipac.c           | 3750 ++++++++++++++++++++++
 net/ipv4/netfilter/nf-hipac/hipac.h           |  623 +++
 net/ipv4/netfilter/nf-hipac/ihash.c           |  463 ++
 net/ipv4/netfilter/nf-hipac/ihash.h           |  285 +
 net/ipv4/netfilter/nf-hipac/mode.h            |  240 +
 net/ipv4/netfilter/nf-hipac/nf_hipac_cthelp.c |   49 
 net/ipv4/netfilter/nf-hipac/nfhp_com.h        |  330 +
 net/ipv4/netfilter/nf-hipac/nfhp_dev.c        |  308 +
 net/ipv4/netfilter/nf-hipac/nfhp_dev.h        |   66 
 net/ipv4/netfilter/nf-hipac/nfhp_mod.c        | 1734 ++++++++++
 net/ipv4/netfilter/nf-hipac/nfhp_mod.h        |   47 
 net/ipv4/netfilter/nf-hipac/nfhp_proc.c       |  888 +++++
 net/ipv4/netfilter/nf-hipac/nfhp_proc.h       |   24 
 net/ipv4/netfilter/nf-hipac/rlp.c             |  537 +++
 net/ipv4/netfilter/nf-hipac/rlp.h             |  142 
 24 files changed, 15509 insertions(+)

diff -purN --exclude=.svn 2.6.13/include/linux/netfilter_ipv4/ip_tables.h 2.6.13-patched/include/linux/netfilter_ipv4/ip_tables.h
--- 2.6.13/include/linux/netfilter_ipv4/ip_tables.h	2005-09-10 14:50:33.000000000 +0200
+++ 2.6.13-patched/include/linux/netfilter_ipv4/ip_tables.h	2005-09-10 14:50:33.000000000 +0200
@@ -455,6 +455,8 @@ struct ipt_table
 
 /* net/sched/ipt.c: Gimme access to your targets!  Gets target->me. */
 extern struct ipt_target *ipt_find_target(const char *name, u8 revision);
+/* Gimme access to your matches too! */
+extern struct ipt_match *ipt_find_match(const char *name, u8 revision);
 
 /* Standard entry. */
 struct ipt_standard
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/Kconfig 2.6.13-patched/net/ipv4/netfilter/Kconfig
--- 2.6.13/net/ipv4/netfilter/Kconfig	2005-09-10 14:50:33.000000000 +0200
+++ 2.6.13-patched/net/ipv4/netfilter/Kconfig	2005-09-10 14:50:34.000000000 +0200
@@ -663,7 +663,42 @@ config IP_NF_TARGET_NOTRACK
 	  If you want to compile it as a module, say M here and read
 	  <file:Documentation/modules.txt>.  If unsure, say `N'.
 
+config IP_NF_HIPAC
+	tristate  'nf-HiPAC support (High Performance Packet Classification)'
+	depends on IP_NF_IPTABLES
+	help
+	  nf-HiPAC is a high performance packet classification framework on
+	  top of netfilter. It is based on a novel classification algorithm
+	  that is very much superior to the linear classication algorithm
+	  implemented by iptables. It provides highly efficient packet
+	  matching which is especially useful when large rulesets and/or high
+	  bandwidth networks are involved. Ruleset updates are submitted
+	  dynamically to the kernel via netlink on a per rule basis.
+	  In addition to its native matches (e.g. ip, proto, port match)
+	  nf-HiPAC allows the usage of iptables matches and targets and thus
+	  provides the same flexibility as iptables. Furthermore the semantics
+	  and construction of a ruleset is identical to the iptables way so
+	  that the internal representation is completely transparent to the
+	  user.
+	  Basically, you can think of nf-HiPAC as an alternative, optimized
+	  iptables filter table. Note that it cannot be used for packet
+	  mangling or NAT but you can still adopt iptables' mangle or nat
+	  table for that purpose since nf-HiPAC and iptables can be used
+	  together at the same time.
+
+config IP_NF_HIPAC_SINGLE_PATH
+	bool 'Single path optimization'
+	depends on IP_NF_HIPAC
+	help
+	   This optimization will significantly improve the performance of
+	   nf-HiPAC but may lead to an unacceptable memory usage. Whether memory
+	   consumption becomes a problem depends on your rule set. You can
+	   monitor the memory used by NF-HIPAC via /proc/net/nf-hipac/info.
+	   In almost all scenarios the performance will already be more than good
+	   enough without this optimization. 
 
+	   If unsure, say N.
+				    
 # ARP tables
 config IP_NF_ARPTABLES
 	tristate "ARP tables support"
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/Makefile 2.6.13-patched/net/ipv4/netfilter/Makefile
--- 2.6.13/net/ipv4/netfilter/Makefile	2005-09-10 14:50:33.000000000 +0200
+++ 2.6.13-patched/net/ipv4/netfilter/Makefile	2005-09-10 14:50:34.000000000 +0200
@@ -79,6 +79,9 @@ obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt
 obj-$(CONFIG_IP_NF_TARGET_NOTRACK) += ipt_NOTRACK.o
 obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
 
+# nf-hipac/Makefile
+obj-$(CONFIG_IP_NF_HIPAC) += nf-hipac/
+
 # generic ARP tables
 obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o
 obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/ip_tables.c 2.6.13-patched/net/ipv4/netfilter/ip_tables.c
--- 2.6.13/net/ipv4/netfilter/ip_tables.c	2005-09-10 14:50:33.000000000 +0200
+++ 2.6.13-patched/net/ipv4/netfilter/ip_tables.c	2005-09-10 14:50:34.000000000 +0200
@@ -453,6 +453,17 @@ static inline struct ipt_match *find_mat
 	return ERR_PTR(err);
 }
 
+struct ipt_match *ipt_find_match(const char *name, u8 revision)
+{
+	struct ipt_match *match;
+
+	match = try_then_request_module(find_match(name, revision),
+					"ipt_%s", name);
+	if (IS_ERR(match) || !match)
+		return NULL;
+	return match;
+}
+
 /* Find target, grabs ref.  Returns ERR_PTR() on error. */
 static inline struct ipt_target *find_target(const char *name, u8 revision)
 {
@@ -1954,6 +1965,7 @@ EXPORT_SYMBOL(ipt_register_table);
 EXPORT_SYMBOL(ipt_unregister_table);
 EXPORT_SYMBOL(ipt_register_match);
 EXPORT_SYMBOL(ipt_unregister_match);
+EXPORT_SYMBOL(ipt_find_match);
 EXPORT_SYMBOL(ipt_do_table);
 EXPORT_SYMBOL(ipt_register_target);
 EXPORT_SYMBOL(ipt_unregister_target);
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/Makefile 2.6.13-patched/net/ipv4/netfilter/nf-hipac/Makefile
--- 2.6.13/net/ipv4/netfilter/nf-hipac/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/Makefile	2005-09-10 14:50:34.000000000 +0200
@@ -0,0 +1,33 @@
+#
+# Makefile for nf-hipac on top of IPv4.
+#
+
+obj-$(CONFIG_IP_NF_HIPAC) += nf_hipac.o
+nf_hipac-objs := ihash.o global.o rlp.o dimtree.o hipac.o nfhp_dev.o nfhp_proc.o nfhp_mod.o
+
+ifdef CONFIG_IP_NF_HIPAC
+ifeq ($(CONFIG_IP_NF_CONNTRACK),m)
+obj-m += nf_hipac_cthelp.o
+EXTRA_CFLAGS += -D CONNTRACK_MODULE
+endif
+endif
+
+ifdef CONFIG_IP_NF_HIPAC_SINGLE_PATH
+EXTRA_CFLAGS += -D SINGLE_PATH
+endif
+
+ifneq ($(ARCH), alpha)
+ifneq ($(ARCH), ia64)
+ifneq ($(ARCH), ppc64)
+ifneq ($(ARCH), s390x)
+ifneq ($(ARCH), sh64)
+ifneq ($(ARCH), sparc64)
+ifneq ($(ARCH), x86_64)
+EXTRA_CFLAGS += -D BIT32_ARCH
+endif
+endif
+endif
+endif
+endif
+endif
+endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/dimtree.c 2.6.13-patched/net/ipv4/netfilter/nf-hipac/dimtree.c
--- 2.6.13/net/ipv4/netfilter/nf-hipac/dimtree.c	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/dimtree.c	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,4307 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#include "global.h"
+#include "ihash.h"
+#include "rlp.h"
+#include "dimtree.h"
+
+#define HAS_DT_MATCH(rule)      ((rule)->dt_match_len > 0)
+#define ITH_DT_MATCH(rule, i)   ((rule)->first_dt_match + (i))
+#define LAST_DT_MATCH(rule)     ITH_DT_MATCH(rule, (rule)->dt_match_len - 1)
+#define LEN(array)              (sizeof(array) / sizeof(*(array)))
+
+/*
+ * newspec keeps track of the rlps and elementary intervals that have been
+ * newly allocated during a series of dimtree operations;
+ * orgspec keeps track of the rlps and elementary intervals that can be
+ * freed after the series of dimtree operations has been successfully finished
+ */
+static struct ptrlist orgspec = {LIST_HEAD_INIT(orgspec.head), 0};
+static struct ihash *newspec  = NULL;
+
+
+
+static inline void
+elem_free(struct dt_elem *e)
+{
+	if (unlikely(e == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	hp_free(e);
+}
+
+
+/* free s which can be an elemtary interval or a rlp */
+static inline void
+rlp_elem_free(struct gen_spec *s)
+{
+	if (unlikely(s == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	if (IS_RLP(s)) {
+		rlp_free((struct rlp_spec *) s);
+	} else {
+		/* s must be elemtary interval */
+		assert(IS_ELEM(s));
+		elem_free((struct dt_elem *) s);
+	}
+}
+
+/* set newspec bit of s which can be an elementary interval or a rlp to 0 */
+static inline void
+rlp_elem_newspec_set(struct gen_spec *s, int newspec_set)
+{
+	if (unlikely(s == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	if (IS_RLP(s)) {
+		((struct rlp_spec *) s)->newspec = !!newspec_set;
+	} else {
+		/* s must be elemtary interval */
+		assert(IS_ELEM(s));
+		((struct dt_elem_spec *) s)->newspec = !!newspec_set;
+	}
+}
+
+/* call rlp_elem_free for each member of orgspec and empty orgspec */
+static inline void
+orgspec_dofree(void)
+{
+	struct list_head *lh;
+	struct ptrlist_entry* e;
+
+	for (lh = orgspec.head.next; lh != &orgspec.head;) {
+		e = list_entry(lh, struct ptrlist_entry, head);
+		lh = lh->next;
+		assert((IS_RLP(e->p) &&
+			!((struct rlp_spec *) e->p)->newspec) ||
+		       (IS_ELEM(e->p) &&
+			!((struct dt_elem_spec *) e->p)->newspec));
+		rlp_elem_free(e->p);
+		mini_free(e);
+	}
+	INIT_LIST_HEAD(&orgspec.head);
+	orgspec.len = 0;
+}
+
+/* call rlp_elem_free for each member of newspec and empty newspec */
+static inline void
+newspec_dofree(void)
+{
+	if (unlikely(newspec == NULL)) {
+		return;
+	}
+	IHASH_KEY_ITERATE(newspec, struct gen_spec *, rlp_elem_free);
+	ihash_free(newspec);
+	newspec = NULL;
+}
+
+/* add s to orgspec;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+static inline hipac_error
+orgspec_add(struct gen_spec *s)
+{
+	if (unlikely(s == NULL)) {
+		ARG_ERR;
+	}
+	assert((IS_RLP(s) && !((struct rlp_spec *) s)->newspec) ||
+	       (IS_ELEM(s) && !((struct dt_elem_spec *) s)->newspec));
+#ifdef DEBUG
+	return ptrlist_add(&orgspec, s, 1);
+#else
+	return ptrlist_add(&orgspec, s, 0);
+#endif
+}
+
+/* empty orgspec */
+static inline void
+orgspec_flush(void)
+{
+	ptrlist_flush(&orgspec);
+}
+
+/* empty newspec; if newspec_reset is not 0 the newspec bit is set
+   to 0 for each element of newspec */
+static inline void
+newspec_flush(int newspec_reset)
+{
+	if (unlikely(newspec == NULL)) {
+		return;
+	}
+	if (newspec_reset) {
+		IHASH_KEY_ITERATE(newspec, struct gen_spec *,
+				  rlp_elem_newspec_set, 0);
+	}
+	ihash_free(newspec);
+	newspec = NULL;
+}
+
+
+/*
+ * history operations
+ */
+
+static void
+history_undo(void)
+{
+	newspec_dofree();
+	orgspec_flush();
+}
+
+static void
+history_commit(int newspec_set)
+{
+	orgspec_dofree();
+	newspec_flush(newspec_set);
+}
+
+#ifdef DEBUG
+/* return 1 if orgspec and newspec are empty and 0 otherwise */
+static int
+history_is_empty(void)
+{
+	return newspec == NULL && list_empty(&orgspec.head);
+}
+#endif
+
+/* s is a new rlp or elementary interval layer which does __not__
+   replace another */
+static hipac_error
+history_new(struct gen_spec *s, int newspec_set)
+{
+	int stat;
+
+	if (unlikely(s == NULL)) {
+		ARG_ERR;
+	}
+
+	assert((IS_RLP(s) || IS_ELEM(s)));
+	if (unlikely(newspec == NULL)) {
+		newspec = ihash_new(INITIAL_NEWSPEC_LEN, 0,
+				    NEWSPEC_AVRG_ELEM_PER_BUCKET,
+				    ihash_func_val, eq_val);
+		if (newspec == NULL) {
+			return HE_LOW_MEMORY;
+		}
+	}
+	stat = ihash_insert(&newspec, s, NULL);
+	if (stat < 0) {
+		return stat;
+	}
+	if (newspec_set) {
+		rlp_elem_newspec_set(s, 1);
+	}
+	return stat;
+}
+
+static hipac_error
+history_replace(struct gen_spec *old, struct gen_spec *new, int newspec_set)
+{
+	int stat;
+
+	if (unlikely(old == NULL || new == NULL)) {
+		ARG_ERR;
+	}
+
+	assert((IS_RLP(old) && IS_RLP(new)) ||
+	       (IS_ELEM(old) && IS_ELEM(new)));
+	assert(newspec_set ||
+	       (IS_RLP(old) && !((struct rlp_spec *) old)->newspec) ||
+	       (IS_ELEM(old) && !((struct dt_elem_spec *) old)->newspec));
+	assert(newspec_set ||
+	       (IS_RLP(new) && !((struct rlp_spec *) new)->newspec) ||
+	       (IS_ELEM(new) && !((struct dt_elem_spec *) new)->newspec));
+	if (unlikely(newspec == NULL)) {
+		if (newspec_set &&
+		    ((IS_RLP(old) &&
+		      ((struct rlp_spec *) old)->newspec) ||
+		     (IS_ELEM(old) &&
+		      ((struct dt_elem_spec *) old)->newspec))) {
+			IMPOSSIBLE_CONDITION("old must be contained in new"
+					     "spec but newspec is empty");
+		}
+		newspec = ihash_new(INITIAL_NEWSPEC_LEN, 0,
+				    NEWSPEC_AVRG_ELEM_PER_BUCKET,
+				    ihash_func_val, eq_val);
+		if (newspec == NULL) {
+			return HE_LOW_MEMORY;
+		}
+	}
+	if (newspec_set &&
+	    ((IS_RLP(old) && ((struct rlp_spec *) old)->newspec) ||
+	     (IS_ELEM(old) && ((struct dt_elem_spec *) old)->newspec))) {
+		
+		stat = ihash_replace(&newspec, old, NULL, new, NULL);
+		if (stat == HE_OK) {
+			rlp_elem_newspec_set(new, 1);
+			rlp_elem_free(old);
+		}
+	} else {
+		stat = orgspec_add(old);
+		if (stat < 0) {
+			return stat;
+		}
+		stat = ihash_insert(&newspec, new, NULL);
+		if (stat < 0) {
+			return stat;
+		}
+		if (newspec_set) {
+			rlp_elem_newspec_set(new, 1);
+		}
+	}
+	return stat;
+}
+
+/* s is an obsolete rlp or elementary interval layer */
+static hipac_error
+history_obsolete(struct gen_spec *s, int newspec_set)
+{
+	if (unlikely(s == NULL)) {
+		ARG_ERR;
+	}
+
+	assert((IS_RLP(s) || IS_ELEM(s)));
+	assert(newspec_set ||
+	       (IS_RLP(s) && !((struct rlp_spec *) s)->newspec) ||
+	       (IS_ELEM(s) && !((struct dt_elem_spec *) s)->newspec));
+	if (unlikely(newspec == NULL && newspec_set &&
+		     ((IS_RLP(s) && ((struct rlp_spec *) s)->newspec) ||
+		      (IS_ELEM(s) && ((struct dt_elem_spec *) s)->newspec)))) {
+		IMPOSSIBLE_CONDITION("s is obsolete, newspec_set is not 0 and"
+				     " the newspec bit of s is set __but__ s "
+				     "is not contained in newspec");
+	}
+	if (newspec_set &&
+	    ((IS_RLP(s) && ((struct rlp_spec *) s)->newspec) ||
+	     (IS_ELEM(s) && ((struct dt_elem_spec *) s)->newspec))) {
+		if (ihash_delete(newspec, s, NULL) < 0) {
+			IMPOSSIBLE_CONDITION("unable to remove s from "
+					     "newspec");
+		}
+		rlp_elem_free(s);
+		return HE_OK;
+	}
+	return orgspec_add(s);
+}
+
+/* hp_realloc can result in a pointer becoming invalid; this function is used
+   to apply this fact to the history */
+static void
+history_del_invalid(struct gen_spec *s)
+{
+	if (unlikely(s == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	if (ihash_delete(newspec, s, NULL) < 0) {
+		ERR("unable to remove invalid pointer from newspec");
+	}
+}
+
+
+
+/*
+ * termrule operations
+ */
+
+/* insert 'rule' in 'term' in sorted order (sorted after pointer addresses);
+   'term' must be sorted before */
+static inline hipac_error
+termrule_insert(struct ptrblock **term, struct dt_rule *rule)
+{
+	__u32 i;
+
+	if (unlikely(term == NULL || rule == NULL)) {
+		ARG_ERR;
+	}
+
+	if (*term == NULL) {
+		*term = ptrblock_new(rule, 1);
+		if (*term == NULL) {
+			return HE_LOW_MEMORY;
+		}
+		return HE_OK;
+	}
+
+#ifdef BIT32_ARCH
+	for (i = 0; i < (*term)->len && 
+		     (__u32) (*term)->p[i] < (__u32) rule; i++);
+#else
+	for (i = 0; i < (*term)->len && 
+		     (__u64) (*term)->p[i] < (__u64) rule; i++);
+#endif
+	if (i < (*term)->len && (*term)->p[i] == rule) {
+		IMPOSSIBLE_CONDITION("rule is already contained in term");
+	}
+	return ptrblock_insert(term, rule, i);
+}
+
+/* delete 'rule' from 'term' which must be in sorted order (sorted after
+   pointer addresses) */
+static inline hipac_error
+termrule_delete(struct ptrblock **term, const struct dt_rule *rule)
+{
+	__u32 i;
+
+	if (unlikely(term == NULL || rule == NULL)) {
+		ARG_ERR;
+	}
+	if (*term == NULL) {
+		/* rule is not in term */
+		return HE_OK;
+	}
+
+#ifdef BIT32_ARCH
+	for (i = 0; i < (*term)->len && 
+		     (__u32) (*term)->p[i] < (__u32) rule; i++);
+#else
+	for (i = 0; i < (*term)->len && 
+		     (__u64) (*term)->p[i] < (__u64) rule; i++);
+#endif
+	
+	if (i >= (*term)->len || (*term)->p[i] != rule) {
+		/* rule is not in term */
+		return HE_OK;
+	}
+	return ptrblock_delete_pos(term, i);
+}
+
+/* delete those rules from 'term' whose match boundaries in dimension 'dimid'
+   lie completely within ['left', 'right'] */
+static inline hipac_error
+termrule_delete_ovl(struct ptrblock **term, __u32 left, __u32 right,
+		    __u8 dimid)
+{
+	__u32 i, curleft, curight;
+	struct dt_match *match;
+	int stat;
+
+	if (unlikely(term == NULL || left > right ||
+		     left > MAXKEY(dim2btype[dimid]) ||
+		     right > MAXKEY(dim2btype[dimid]))) {
+		ARG_ERR;
+	}
+	if (*term == NULL) {
+		return HE_OK;
+	}
+
+       	for (i = 0; i < (*term)->len;) {
+		match = HAS_DT_MATCH((struct dt_rule *) (*term)->p[i]) ?
+			LAST_DT_MATCH((struct dt_rule *) (*term)->p[i]) : NULL;
+		if (match != NULL && match->dimid == dimid) {
+			assert(match->left > 0 ||
+			       match->right < MAXKEY(dim2btype[dimid]));
+			curleft = match->left;
+			curight = match->right;
+		} else {
+			curleft = 0;
+			curight = MAXKEY(dim2btype[dimid]);
+		}
+		if (curleft >= left && curight <= right) {
+			stat = ptrblock_delete_pos(term, i);
+			if (stat < 0) {
+				return stat;
+			}
+			if (*term == NULL) {
+				return HE_OK;
+			}
+		} else {
+			i++;
+		}
+	}
+	return HE_OK;
+}
+
+/* returns 1 if there is a rule in 'term' whose last match m produces the
+   interval represented by 'right' and dimid(m) == 'dimid' */
+static inline int
+termrule_exists(const struct ptrblock *term, __u8 dimid, __u32 right)
+{
+	struct dt_match *match;
+	struct dt_rule **rule;
+	__u32 i;
+	
+	if (unlikely(right > MAXKEY(dim2btype[dimid]))) {
+		ARG_MSG;
+		return 0;
+	}
+	if (term == NULL) {
+		return 0;
+	}
+
+	rule = (struct dt_rule **) term->p;
+	for (i = 0; i < term->len; i++) {
+		match = HAS_DT_MATCH(*rule) ? LAST_DT_MATCH(*rule) : NULL;
+		if (match != NULL && match->dimid == dimid &&
+		    (match->right == right ||
+		     (match->left > 0 && match->left - 1 == right))) {
+			return 1;
+		}
+		rule++;
+	}
+	return 0;
+}
+
+/* return 1 if 'rule' terminates in the elementary interval described by 
+   'right' resp. 'wildcard' and 'dimid'; otherwise 0 is returned */
+static inline int
+rule_term(const struct dt_rule *rule, __u32 right, __u8 wildcard, __u8 dimid)
+{
+	__u32 lbound, ubound;
+	const struct dt_match *match;
+	__u8 match_wc, match_nwc1, match_nwc2;
+
+	if (unlikely(rule == NULL || (wildcard && !HAS_WILDCARD_DIM(dimid)))) {
+		ARG_MSG;
+		return 0;
+	}
+
+	match = HAS_DT_MATCH(rule) ? LAST_DT_MATCH(rule) : NULL;
+	if (match != NULL && match->dimid == dimid) {
+		assert(match->left > 0 ||
+		       match->right < MAXKEY(dim2btype[dimid]));
+		lbound = match->left;
+		ubound = match->right;
+	} else if (match == NULL || match->dimid < dimid) {
+		lbound = 0;
+		ubound = MAXKEY(dim2btype[dimid]);
+	} else {
+		return 0;
+	}
+	
+	match_wc   = wildcard && (match == NULL || match->dimid < dimid);
+	
+	match_nwc1 = !wildcard && HAS_WILDCARD_DIM(dimid) &&
+		match != NULL && match->dimid == dimid && ubound >= right &&
+		lbound <= right;
+	
+	match_nwc2 = !wildcard && !HAS_WILDCARD_DIM(dimid) &&
+		ubound >= right && lbound <= right;
+	
+	return match_wc || match_nwc1 || match_nwc2;
+}
+
+/* store the subset of rules from 'term' that terminate in the elemtary
+   interval represented by 'right' resp. 'wildcard' in dimension 'dimid'
+   in 'subterm' */
+static inline hipac_error
+termrule_subset(const struct ptrblock *term, struct ptrblock **subterm,
+		__u32 right, __u8 wildcard, __u8 dimid)
+{
+	struct dt_rule **rule;
+	int stat;
+	__u32 i;
+
+	if (unlikely(subterm == NULL)) {
+		ARG_ERR;
+	}
+
+	*subterm = NULL;
+	if (term == NULL) {
+		return HE_OK;
+	}
+
+	rule = (struct dt_rule **) term->p;
+	for (i = 0; i < term->len; i++, rule++) {
+		if (rule_term(*rule, right, wildcard, dimid)) {
+			stat = ptrblock_insert(
+				subterm, *rule, *subterm == NULL ? 0 :
+				(*subterm)->len);
+			if (stat < 0) {
+				if (*subterm != NULL) {
+					ptrblock_free(*subterm);
+				}
+				*subterm = NULL;
+				return stat;
+			}
+		}
+	}
+	return HE_OK;
+}
+
+/* merge 'tmpterm' into 'term' so that there are no duplicates;
+   'tmpterm' is freed even if termrule_merge fails */
+static inline hipac_error
+termrule_merge(struct ptrblock **term, struct ptrlist *tmpterm)
+{
+	struct ptrlist_entry *e;
+	struct list_head *lh;
+	int stat;
+	__u32 i;
+	
+	if (unlikely(term == NULL || tmpterm == NULL)) {
+		ARG_ERR;
+	}
+
+	if (ptrlist_is_empty(tmpterm)) {
+		ptrlist_free(tmpterm);
+		return HE_OK;
+	}
+
+	for (lh = tmpterm->head.next, i = 0; lh != &tmpterm->head;) {
+		e = list_entry(lh, struct ptrlist_entry, head);
+#ifdef BIT32_ARCH
+		for (; *term != NULL && i < (*term)->len &&
+			     (__u32) (*term)->p[i] < (__u32) e->p; i++);
+#else
+		for (; *term != NULL && i < (*term)->len &&
+			     (__u64) (*term)->p[i] < (__u64) e->p; i++);
+#endif
+		if (*term == NULL || i == (*term)->len) {
+			/* append rest of tmpterm to term */
+			do {
+				stat = ptrblock_insert(
+					term, e->p, *term == NULL ? 0 :
+					(*term)->len);
+				if (stat < 0) {
+					goto error;
+				}
+				lh = lh->next;
+				ptrlist_free_entry(e);
+				e = list_entry(lh, struct ptrlist_entry, head);
+			} while (lh != &tmpterm->head);
+			break;
+		}
+		if (e->p != (*term)->p[i]) {
+			stat = ptrblock_insert(term, e->p, i++);
+			if (stat < 0) {
+				goto error;
+			}
+		}
+		lh = lh->next;
+		ptrlist_free_entry(e);
+	}
+	ptrlist_free(tmpterm);
+	return HE_OK;
+
+ error:
+	ptrlist_free(tmpterm);
+	return stat;
+}
+
+/* remove all elements of 'delterm' from 'term'; 'delterm' must be completely
+   contained in 'term' */
+static inline hipac_error
+termrule_cut(struct ptrblock **term, struct ptrblock *delterm)
+{
+	__u32 i, j;
+	int stat;
+	
+	if (unlikely(term == NULL)) {
+		ARG_ERR;
+	}
+
+	if (delterm == NULL) {
+		return HE_OK;
+	}
+	if (unlikely(*term == NULL)) {
+		IMPOSSIBLE_CONDITION("unable to cut elements from empty "
+				     "termrule block");
+	}
+
+	for (i = 0, j = 0; *term != NULL && i < (*term)->len &&
+		     j < delterm->len; j++) {
+#ifdef BIT32_ARCH
+		for (; i < (*term)->len &&
+			     (__u32) (*term)->p[i] < (__u32) delterm->p[j];
+		     i++);
+#else
+		for (; i < (*term)->len &&
+			     (__u64) (*term)->p[i] < (__u64) delterm->p[j];
+		     i++);
+#endif
+		if (i >= (*term)->len || (*term)->p[i] != delterm->p[j]) {
+			goto error;
+		}
+		stat = ptrblock_delete_pos(term, i);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	if (j >= delterm->len) {
+		return HE_OK;
+	}
+
+ error:
+	IMPOSSIBLE_CONDITION("delterm contains elements which are not "
+			     "contained in term");
+}
+
+/* return the terminal rule (terminal target + no function based matches)
+   from 'term' which dominates the elementary interval represented by 'right'
+   resp. 'wildcard' in the dimension specified by 'dimid' and which does not
+   equal 'rule' */
+static inline struct dt_rule *
+termrule_find_best_term(const struct ptrblock *term,
+			const struct dt_rule *rule,
+			__u32 right, __u8 wildcard, __u8 dimid)
+{
+	struct dt_rule *best = NULL;
+	__u32 nextpos = (__u32) ULONG_MAX;
+	struct dt_rule *tr;
+	__u32 i;
+
+	if (unlikely(term == NULL || rule == NULL ||
+		     right > MAXKEY(dim2btype[dimid]) ||
+		     (wildcard && !HAS_WILDCARD_DIM(dimid)))) {
+		ARG_MSG;
+		return NULL;
+	}
+	
+	for (i = 0; i < term->len; i++) {
+		tr = term->p[i];
+		if (!IS_RULE_TERM(tr) || tr == rule) {
+			continue;
+		}
+		if (rule_term(tr, right, wildcard, dimid) &&
+		    tr->spec.pos < nextpos) {
+			nextpos = tr->spec.pos;
+			best = tr;
+		}
+	}
+	return best;
+}
+
+/* return the number(*) of non-terminal rules (non-terminal target or function
+   based matches) in 'term' not equal to 'rule' which terminate in the
+   elementary interval represented by 'right' resp. 'wildcard' in the
+   dimension specified by 'dimid' and whose position is < term_rule->spec.pos
+   if term_rule != NULL; if there is exactly one such non-terminal rule it is
+   stored in 'ntm_rule';
+   (*) the return value ret is 0, 1 or 2; ret == 0 || ret == 1 means there are
+       exactly ret non-terminal rules; ret == 2 means there are >= 2
+       non-terminal rules */
+static inline __u32
+termrule_num_ntm(struct dt_rule **ntm_rule, const struct ptrblock *term,
+		 const struct dt_rule *term_rule, const struct dt_rule *rule,
+		 __u32 right, __u8 wildcard, __u8 dimid)
+{
+	__u32 num = 0;
+	struct dt_rule *tr;
+	__u32 i;
+
+	if (unlikely(ntm_rule == NULL || term == NULL || rule == NULL ||
+		     right > MAXKEY(dim2btype[dimid]) ||
+		     (wildcard && !HAS_WILDCARD_DIM(dimid)))) {
+		ARG_MSG;
+		return 0;
+	}
+	
+	*ntm_rule = NULL;
+	for (i = 0; i < term->len; i++) {
+		tr = term->p[i];
+		if (IS_RULE_TERM(tr) || tr == rule ||
+		    (term_rule != NULL &&
+		     tr->spec.pos >= term_rule->spec.pos)) {
+			continue;
+		}
+		if (rule_term(tr, right, wildcard, dimid)) {
+			*ntm_rule = tr;
+			if (++num == 2) {
+				/* there are at least 2 non-terminal rules
+				   => stop searching */
+				*ntm_rule = NULL;
+				return num;
+			}
+		}
+	}
+	if (num > 1) {
+		*ntm_rule = NULL;
+	}
+	return num;
+}
+
+/* store all non-terminating rules (non-terminal target or function based
+   matches) from 'term' not equal to rule in 'e' which terminate in the
+   elementary interval represented by 'right' resp. 'wildcard' in the
+   dimension specified by 'dimid' and whose position is < max_rule->spec.pos
+   if max_rule != NULL and > min_rule->spec.pos if min_rule != NULL;
+   the rules are stored in e->ntm_rules in sorted order (sorted after their
+   positions) */
+static inline hipac_error
+termrule_insert_ntm(struct dt_elem **e, const struct ptrblock *term,
+		    const struct dt_rule *min_rule,
+		    const struct dt_rule *max_rule,
+		    const struct dt_rule *rule,
+		    __u32 right, __u8 wildcard, __u8 dimid)
+{
+	struct dt_rule *tr;
+	__u32 i, j, stat;
+
+	if (unlikely(e == NULL || *e == NULL || term == NULL ||
+		     right > MAXKEY(dim2btype[dimid]) ||
+		     (wildcard && !HAS_WILDCARD_DIM(dimid)))) {
+		ARG_ERR;
+	}
+	
+	for (i = 0; i < term->len; i++) {
+		tr = term->p[i];
+		if (IS_RULE_TERM(tr) || tr == rule ||
+		    (min_rule != NULL &&
+		     (tr->spec.pos <= min_rule->spec.pos)) ||
+		    (max_rule != NULL &&
+		     (tr->spec.pos >= max_rule->spec.pos))) {
+			continue;
+		}
+		if (rule_term(tr, right, wildcard, dimid)) {
+			for (j = 0; j < (*e)->ntm_rules.len &&
+				     ((struct dt_rule *)
+				      (*e)->ntm_rules.p[j])->spec.pos <
+				     tr->spec.pos; j++);
+			stat = ptrblock_insert_embed((void **) e,
+						     offsetof(struct dt_elem,
+							      ntm_rules),
+						     tr, j);
+			if (stat < 0) {
+				return stat;
+			}
+		}
+	}
+	return HE_OK;
+}
+
+
+
+/*
+ * tmp_termrule operations
+ */
+
+static inline struct ptrlist *
+tmp_termrule_new(void)
+{
+	return ptrlist_new();
+}
+
+static inline void
+tmp_termrule_free(struct ptrlist *tmpterm)
+{
+	return ptrlist_free(tmpterm);
+}
+
+/* merge 'term' into 'tmpterm' so that there are no duplicates */
+static inline hipac_error
+tmp_termrule_merge(struct ptrlist *tmpterm, struct ptrblock *term)
+{
+	struct ptrlist_entry *e;
+	struct list_head *lh;
+	int stat;
+	__u32 i;
+	
+	if (unlikely(tmpterm == NULL)) {
+		ARG_ERR;
+	}
+
+	if (term == NULL) {
+		return HE_OK;
+	}
+
+	for (i = 0, lh = tmpterm->head.next; i < term->len; i++) {
+#ifdef BIT32_ARCH
+		for (; lh != &tmpterm->head &&
+			     (__u32) list_entry(lh, struct ptrlist_entry,
+						head)->p <
+			     (__u32) term->p[i]; lh = lh->next);
+#else
+		for (; lh != &tmpterm->head &&
+			     (__u64) list_entry(lh, struct ptrlist_entry,
+						head)->p <
+			     (__u64) term->p[i]; lh = lh->next);
+#endif
+		if (lh == &tmpterm->head) {
+			/* append rest of term to tmpterm */
+			for (; i < term->len; i++) {
+				stat = ptrlist_add(tmpterm, term->p[i], 0);
+				if (stat < 0) {
+					return stat;
+				}
+			}
+			break;
+		}
+		e = list_entry(lh, struct ptrlist_entry, head);
+		if (e->p != term->p[i]) {
+			e = ptrlist_new_entry(term->p[i]);
+			if (e == NULL) {
+				return HE_LOW_MEMORY;
+			}
+			list_add_tail(&e->head, lh);
+			tmpterm->len++;
+		}
+	}
+	return HE_OK;
+}
+
+
+
+/*
+ * elementary interval operations
+ */
+
+/* create new elementary interval layer with ntm_len non-terminal rules
+   which are stored in ntm_rules sorted after their positions */
+static inline struct dt_elem *
+elem_new(struct dt_rule *term_rule, struct dt_rule *ntm_rules[], __u32 ntm_len)
+{
+	struct dt_elem *e;
+	__u32 i;
+
+	if (unlikely(ntm_len == 0 || ntm_rules == NULL || *ntm_rules == NULL ||
+		     (termrule == NULL && ntm_len <= 1))) {
+		ARG_MSG;
+		return NULL;
+	}
+
+	e = hp_alloc(sizeof(*e) + ntm_len * sizeof(*e->ntm_rules.p), 1);
+	if (e == NULL) {
+		return NULL;
+	}
+	e->spec.rlp = 0;
+	e->spec.rtype = RT_ELEM;
+	e->spec.newspec = 0;
+	e->term_rule = term_rule;
+	e->ntm_rules.len = ntm_len;
+	for (i = 0; i < ntm_len; i++) {
+		e->ntm_rules.p[i] = ntm_rules[i];
+	}
+	return e;
+}
+
+/* create new elementary interval layer with 0 non-terminal rules; notice that
+   the resulting elementary interval is not valid because it __must__ contain
+   at least one non-terminal rule */
+static inline struct dt_elem *
+elem_new_empty(struct dt_rule *term_rule)
+{
+	struct dt_elem *e;
+	
+	e = hp_alloc(sizeof(*e), 1);
+	if (e == NULL) {
+		return NULL;
+	}
+	e->spec.rlp = 0;
+	e->spec.rtype = RT_ELEM;
+	e->spec.newspec = 0;
+	e->term_rule = term_rule;
+	e->ntm_rules.len = 0;
+	return e;
+}
+
+static inline int
+elem_eq(const struct dt_elem *e1, const struct dt_elem *e2)
+{
+	if (e1 == NULL || e2 == NULL || !IS_ELEM(e1) || !IS_ELEM(e2)) {
+		ARG_MSG;
+		return 0;
+	}
+	if (e1->term_rule != e2->term_rule ||
+	    !ptrblock_eq(&e1->ntm_rules, &e2->ntm_rules)) {
+		return 0;
+	}
+	return 1;
+}
+
+static inline hipac_error
+elem_clone(struct dt_elem *e, struct dt_elem **clone)
+{
+	if (e == NULL || clone == NULL) {
+		ARG_ERR;
+	}
+
+	*clone = hp_alloc(sizeof(*e) + e->ntm_rules.len *
+			  sizeof(*e->ntm_rules.p), 1);
+	if (*clone == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	memcpy(*clone, e, sizeof(*e) + e->ntm_rules.len *
+	       sizeof(*e->ntm_rules.p));
+	return HE_OK;
+}
+
+/* forward declaration */
+static int 
+rlp_eq_rec(const struct rlp_spec *spec1, const struct rlp_spec *spec2);
+
+/* return 1 if g1 and g2 are equal and rules;
+   return 2 if g1 and g2 are equal and elementary intervals;
+   return 3 if g1 and g2 are equal and rlps;
+   return 0 otherwise */
+static inline int
+rlp_rule_elem_eq(const struct gen_spec *g1, const struct gen_spec *g2)
+{
+	if (g1 == NULL || g2 == NULL ||
+	    (IS_RULE(g1) && IS_RULE(g2))) {
+		return g1 == g2;
+	} else if (IS_ELEM(g1) && IS_ELEM(g2)) {
+		struct dt_elem *e1 = (struct dt_elem *) g1;
+		struct dt_elem *e2 = (struct dt_elem *) g2;
+
+		if (e1->ntm_rules.len != e2->ntm_rules.len) {
+			return 0;
+		}
+		return elem_eq(e1, e2) ? 2 : 0;
+	} else if (IS_RLP(g1) && IS_RLP(g2)) {
+		struct rlp_spec *b1 = (struct rlp_spec *) g1;
+		struct rlp_spec *b2 = (struct rlp_spec *) g2;
+
+		return (rlp_spec_eq(b1, b2) && rlp_eq_rec(b1, b2)) ? 3 : 0;
+	}
+	return 0;
+}
+
+/* insert rule into rule_elem which can be a rule or an elementary interval
+   layer; the result which can be a rule or an elementary interval layer
+   is directly written to rule_elem */
+static inline hipac_error
+rule_elem_insert(struct dt_rule_elem_spec **rule_elem, struct dt_rule *rule,
+		 int newspec_set)
+{
+	int stat;
+
+	if (unlikely(rule_elem == NULL || rule == NULL)) {
+		ARG_ERR;
+	}
+
+	if (*rule_elem == NULL) {
+		*rule_elem = (struct dt_rule_elem_spec *) rule;
+		return HE_OK;
+	}
+
+	assert(IS_RULE(*rule_elem) || IS_ELEM(*rule_elem));
+	assert(!IS_ELEM(*rule_elem) ||
+	       ((struct dt_elem *) *rule_elem)->ntm_rules.len > 0);
+	assert(!IS_ELEM(*rule_elem) ||
+	       ((struct dt_elem *) *rule_elem)->term_rule != NULL ||
+	       ((struct dt_elem *) *rule_elem)->ntm_rules.len > 1);
+
+	if (IS_RULE(*rule_elem)) {
+		struct dt_rule *r = (struct dt_rule *) *rule_elem;
+		
+		if (IS_RULE_TERM(rule) && IS_RULE_TERM(r)) {
+			if (rule->spec.pos < r->spec.pos) {
+				*rule_elem = (struct dt_rule_elem_spec *) rule;
+			}
+			return HE_OK;
+		
+		} else if (!IS_RULE_TERM(rule) && !IS_RULE_TERM(r)) {
+			struct dt_rule *ntm[2];
+			struct dt_elem *e;
+			if (r->spec.pos < rule->spec.pos) {
+				ntm[0] = r;
+				ntm[1] = rule;
+			} else {
+				ntm[0] = rule;
+				ntm[1] = r;
+			}
+			e = elem_new(NULL, ntm, sizeof(ntm) / sizeof(*ntm));
+			if (e == NULL) {
+				return HE_LOW_MEMORY;
+			}
+			stat = history_new((struct gen_spec *) e, newspec_set);
+			if (stat < 0) {
+				elem_free(e);
+				return stat;
+			}
+			*rule_elem = (struct dt_rule_elem_spec *) e;
+			return HE_OK;
+			
+		} else {
+			struct dt_rule *term_rule, *ntm_rule;
+			struct dt_elem *e;
+			if (IS_RULE_TERM(rule)) {
+				term_rule = rule;
+				ntm_rule = r;
+			} else {
+				term_rule = r;
+				ntm_rule = rule;
+			}
+			if (term_rule->spec.pos < ntm_rule->spec.pos) {
+				*rule_elem = (struct dt_rule_elem_spec *)
+					term_rule;
+				return HE_OK;
+			}
+			e = elem_new(term_rule, &ntm_rule, 1);
+			if (e == NULL) {
+				return HE_LOW_MEMORY;
+			}
+			stat = history_new((struct gen_spec *) e, newspec_set);
+			if (stat < 0) {
+				elem_free(e);
+				return stat;
+			}
+			*rule_elem = (struct dt_rule_elem_spec *) e;
+			return HE_OK;
+		}
+	} else {
+		/* IS_ELEM(*rule_elem) */
+		struct dt_elem *e = (struct dt_elem *) *rule_elem;
+		__u32 i;
+		
+		if (e->term_rule != NULL && 
+		    rule->spec.pos > e->term_rule->spec.pos) {
+			/* rule is never matched */
+			return HE_OK;
+		}
+		if (IS_RULE_TERM(rule)) {
+			/* find still matching rules if any */
+			if (((struct dt_rule *) e->ntm_rules.p[0])->spec.pos >
+			    rule->spec.pos) {
+				stat = history_obsolete((struct gen_spec *) e,
+							newspec_set);
+				if (stat < 0) {
+					return stat;
+				}
+				*rule_elem = (struct dt_rule_elem_spec *) rule;
+				return HE_OK;
+			}
+			e->term_rule = rule;
+			i = e->ntm_rules.len;
+			do {
+				i--;
+				if (((struct dt_rule *)
+				     e->ntm_rules.p[i])->spec.pos <
+				    rule->spec.pos) {
+					break;
+				}
+			} while (i > 0);
+			assert(((struct dt_rule *)
+				e->ntm_rules.p[i])->spec.pos < rule->spec.pos);
+			if (i < e->ntm_rules.len - 1) {
+				struct dt_elem *e2;
+				e2 = hp_realloc(e, sizeof(*e) + (i + 1) *
+						sizeof(*e->ntm_rules.p));
+				if (e2 == NULL) {
+					/* this should never happen as we
+					   shrink e */
+					return HE_LOW_MEMORY;
+				}
+				if (e != e2) {
+					history_del_invalid(
+						(struct gen_spec *) e);
+					stat = history_new(
+						(struct gen_spec *) e2,
+						newspec_set);
+					if (stat < 0) {
+						elem_free(e2);
+						return stat;
+					}
+				}
+				e2->ntm_rules.len = i + 1;
+				*rule_elem = (struct dt_rule_elem_spec *) e2;
+			}
+			return HE_OK;
+
+		} else {
+			/* !IS_RULE_TERM(rule) */
+			for (i = 0; i < e->ntm_rules.len &&
+				     ((struct dt_rule *)
+				      e->ntm_rules.p[i])->spec.pos <
+				     rule->spec.pos; i++);
+			stat = ptrblock_insert_embed((void **) rule_elem,
+						     offsetof(struct dt_elem,
+							      ntm_rules),
+						     rule, i);
+			if (stat < 0) {
+				return stat;
+			}
+			if (e != (struct dt_elem *) *rule_elem) {
+				history_del_invalid((struct gen_spec *) e);
+				stat = history_new((struct gen_spec *)
+						   *rule_elem, newspec_set);
+				if (stat < 0) {
+					elem_free((struct dt_elem *)
+						  *rule_elem);
+					return stat;
+				}
+			}
+			return HE_OK;
+		}
+	}
+}
+
+/* delete rule from rule_elem which can be a rule or an elementary interval
+   layer; if rule is not contained in rule_elem nothing happens;
+   the result which can be a rule or an elementary interval layer is directly
+   written to rule_elem; term, right, wildcard and dimid must be given to
+   find the next best rule(s) if necessary */
+static inline hipac_error
+rule_elem_delete(struct dt_rule_elem_spec **rule_elem,
+		 const struct dt_rule *rule, const struct ptrblock *term,
+		 __u32 right, __u8 wildcard, __u8 dimid, int newspec_set)
+{
+	int stat;
+
+	if (unlikely(rule_elem == NULL || rule == NULL || term == NULL ||
+		     right > MAXKEY(dim2btype[dimid]) ||
+		     (wildcard && !HAS_WILDCARD_DIM(dimid)))) {
+		ARG_ERR;
+	}
+
+	if (*rule_elem == NULL) {
+		/* rule is not contained in rule_elem */
+		return HE_OK;
+	}
+
+	assert(IS_RULE(*rule_elem) || IS_ELEM(*rule_elem));
+	assert(!IS_ELEM(*rule_elem) ||
+	       ((struct dt_elem *) *rule_elem)->ntm_rules.len > 0);
+	assert(!IS_ELEM(*rule_elem) ||
+	       ((struct dt_elem *) *rule_elem)->term_rule != NULL ||
+	       ((struct dt_elem *) *rule_elem)->ntm_rules.len > 1);
+
+	if (IS_RULE(*rule_elem)) {
+		struct dt_rule *r = (struct dt_rule *) *rule_elem;
+		struct dt_rule *term_rule, *ntm_rule = NULL;
+		__u32 ntm_num;
+
+		if (r != rule) {
+			/* rule is not contained in rule_elem */
+			return HE_OK;
+		}
+
+		/* in fact it would suffice to call termrule_find_best_term
+		   only if IS_RULE_TERM(r) */
+		term_rule = termrule_find_best_term(term, rule, right,
+						    wildcard, dimid);
+		ntm_num = termrule_num_ntm(&ntm_rule, term, term_rule, rule,
+					   right, wildcard, dimid);
+		if (term_rule == NULL && ntm_num <= 1) {
+			*rule_elem = (struct dt_rule_elem_spec *) ntm_rule;
+			return HE_OK;
+		} else if (term_rule != NULL && ntm_num == 0) {
+			*rule_elem = (struct dt_rule_elem_spec *) term_rule;
+			return HE_OK;
+		} else {
+			struct dt_elem *e = elem_new_empty(term_rule);
+			if (e == NULL) {
+				return HE_LOW_MEMORY;
+			}
+			stat = termrule_insert_ntm(&e, term, NULL, term_rule,
+						   rule, right, wildcard,
+						   dimid);
+			if (stat < 0) {
+				hp_free(e);
+				return stat;
+			}
+			assert(e->ntm_rules.len > 0);
+			stat = history_new((struct gen_spec *) e, newspec_set);
+			if (stat < 0) {
+				elem_free(e);
+				return stat;
+			}
+			*rule_elem = (struct dt_rule_elem_spec *) e;
+			return HE_OK;
+		}
+	} else {
+		/* IS_ELEM(*rule_elem) */
+		struct dt_elem *e = (struct dt_elem *) *rule_elem;
+		__u32 i;
+		
+		if (e->term_rule == rule) {
+			struct dt_rule *term_rule;
+			term_rule = termrule_find_best_term(
+				term, rule, right, wildcard, dimid);
+			stat = termrule_insert_ntm(
+				(struct dt_elem **) rule_elem, term,
+				e->ntm_rules.p[e->ntm_rules.len - 1],
+				term_rule, rule, right, wildcard, dimid);
+			if (stat < 0) {
+				/* we only care about rule_elem if its address
+				   has changed; otherwise rule_elem is 
+				   handled by the history */
+				if (e != (struct dt_elem *) *rule_elem) {
+					history_del_invalid((struct gen_spec *)
+							    e);
+					elem_free((struct dt_elem *)
+						  *rule_elem);
+				}
+				return stat;
+			}
+			if (e != (struct dt_elem *) *rule_elem) {
+				history_del_invalid((struct gen_spec *) e);
+				stat = history_new((struct gen_spec *)
+						   *rule_elem, newspec_set);
+				if (stat < 0) {
+					elem_free((struct dt_elem *)
+						  *rule_elem);
+					return stat;
+				}
+			}
+			e = (struct dt_elem *) *rule_elem;
+			if (term_rule == NULL && e->ntm_rules.len == 1) {
+				struct dt_rule_elem_spec *ntm =
+					e->ntm_rules.p[0];
+				stat = history_obsolete((struct gen_spec *) e,
+							newspec_set);
+				if (stat < 0) {
+					return stat;
+				}
+				*rule_elem = ntm;
+				return HE_OK;
+			}
+			e->term_rule = term_rule;
+			return HE_OK;
+		} else {
+			for (i = 0; i < e->ntm_rules.len &&
+				     ((struct dt_rule *)
+				      e->ntm_rules.p[i])->spec.pos <
+				     rule->spec.pos; i++);
+			if (i >= e->ntm_rules.len ||
+			    e->ntm_rules.p[i] != rule) {
+				/* rule is not contained in rule_elem */
+				return HE_OK;
+			}
+			if (e->ntm_rules.len == 1) {
+				struct dt_rule_elem_spec *tm =
+					(struct dt_rule_elem_spec *)
+					e->term_rule;
+				stat = history_obsolete((struct gen_spec *) e,
+							newspec_set);
+				if (stat < 0) {
+					return stat;
+				}
+				*rule_elem = tm;
+				return HE_OK;
+			} else if (e->term_rule == NULL &&
+				   e->ntm_rules.len == 2) {
+				struct dt_rule_elem_spec *ntm =
+					(struct dt_rule_elem_spec *)
+					e->ntm_rules.p[(i + 1) % 2];
+				stat = history_obsolete((struct gen_spec *) e,
+							newspec_set);
+				if (stat < 0) {
+					return stat;
+				}
+				*rule_elem = ntm;
+				return HE_OK;
+			} else {
+				stat = ptrblock_delete_pos_embed(
+					(void **) rule_elem,
+					offsetof(struct dt_elem, ntm_rules),
+					i);
+				if (stat < 0) {
+					return stat;
+				}
+				if (e != (struct dt_elem *) *rule_elem) {
+					history_del_invalid(
+						(struct gen_spec *) e);
+					stat = history_new((struct gen_spec *)
+							   *rule_elem,
+							   newspec_set);
+					if (stat < 0) {
+						elem_free((struct dt_elem *)
+							  *rule_elem);
+						return stat;
+					}
+				}
+				return HE_OK;
+			}
+		}
+	}
+}
+
+
+
+/*
+ * recursive rlp operations
+ */
+
+/* necessary forward declaration */
+static hipac_error
+rlp_clone_rec(const struct rlp_spec *spec, struct rlp_spec **clone,
+	      int newspec_set);
+
+static inline hipac_error
+rlp_clone_help(struct gen_spec **g, int newspec_set)
+{
+	int stat = HE_OK;
+
+	if (*g == NULL) {
+		return HE_OK;
+	}
+	if (IS_RLP(*g)) {
+		stat = rlp_clone_rec((struct rlp_spec *) *g,
+				     (struct rlp_spec **) g,
+				     newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	} else if (IS_ELEM(*g)) {
+		struct dt_elem *clone;
+		stat = elem_clone((struct dt_elem *) *g, &clone);
+		if (stat < 0) {
+			return stat;
+		}
+		stat = history_new((struct gen_spec *) clone,
+				   newspec_set);
+		if (stat < 0) {
+			elem_free(clone);
+			return stat;
+		}
+		*g = (struct gen_spec *) clone;
+	}
+	return HE_OK;
+}
+
+/* clone spec including the elementary interval layers recursively and call
+   history_new for each clone;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+static hipac_error
+rlp_clone_rec(const struct rlp_spec *spec, struct rlp_spec **clone,
+	      int newspec_set)
+{
+	struct gen_spec **nextspec = NULL;
+	__u32 size;
+	int stat;
+	__u16 n;
+       
+	if (unlikely(spec == NULL || clone == NULL)) {
+		ARG_ERR;
+	}
+	
+	size = rlp_size(spec);
+	*clone = hp_alloc(size, 1);
+	if (*clone == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	
+	memcpy(*clone, spec, size);
+	stat = ptrblock_clone(*termrule(spec), termrule(*clone));
+	if (stat < 0) {
+		hp_free(*clone);
+		return stat;
+	}
+	
+	stat = history_new((struct gen_spec *) *clone, newspec_set);
+	if (stat < 0) {
+		hp_free(*termrule(*clone));
+		hp_free(*clone);
+		return stat;
+	}
+	
+	nextspec = rlp_nextspec(*clone);
+	assert(nextspec != NULL);
+	
+	for (n = 0; n < (*clone)->num; n++) {
+		stat = rlp_clone_help(nextspec + n, newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	
+	if (HAS_WILDCARD_SPEC(*clone)) {
+		stat = rlp_clone_help(WILDCARD(*clone), newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	return HE_OK;
+}
+
+/* necessary forward declaration */
+static hipac_error
+rlp_free_rec(struct rlp_spec *spec, int newspec_set, int direct_free);
+
+static inline hipac_error
+rlp_free_help(struct gen_spec *g, int newspec_set, int direct_free)
+{
+	int stat;
+
+	if (g == NULL) {
+		return HE_OK;
+	}
+	if (IS_RLP(g)) {
+		stat = rlp_free_rec((struct rlp_spec *) g, newspec_set,
+				    direct_free);
+		if (stat < 0) {
+			return stat;
+		}
+	} else if (IS_ELEM(g)) {
+		if (direct_free) {
+			rlp_elem_free(g);
+		} else {
+			stat = history_obsolete(g, newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+		}
+	}
+	return HE_OK;
+}
+
+/* 'free' spec including the elementary interval layers recursively;
+   if direct_free is 0 'free' means to call history_obsolete for each element;
+   otherwise the elements are directly freed by rlp_elem_free;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+static hipac_error
+rlp_free_rec(struct rlp_spec *spec, int newspec_set, int direct_free)
+{
+	struct gen_spec **nextspec = NULL;
+	int stat;
+	__u16 n;
+	
+	if (unlikely(spec == NULL)) {
+		ARG_ERR;
+	}
+	
+	nextspec = rlp_nextspec(spec);
+	assert(nextspec != NULL);
+	
+	for (n = 0; n < spec->num; n++) {
+		stat = rlp_free_help(*(nextspec + n), newspec_set,
+				     direct_free);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+
+	if (HAS_WILDCARD_SPEC(spec)) {
+		stat = rlp_free_help(*WILDCARD(spec), newspec_set,
+				     direct_free);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+
+	if (direct_free) {
+		rlp_elem_free((struct gen_spec *) spec);
+		return HE_OK;
+	}
+	return history_obsolete((struct gen_spec *) spec, newspec_set);
+}
+
+/* return 1 if spec1 and spec2 are recursively equal; the headers spec1 and
+   spec2 are assumed to be equal */
+static int
+rlp_eq_rec(const struct rlp_spec *spec1, const struct rlp_spec *spec2)
+{
+	struct gen_spec **nextspec1 = NULL, **nextspec2 = NULL;
+	__u16 n;
+	
+	if (unlikely(spec1 == NULL || spec2 == NULL)) {
+		ARG_ERR;
+	}
+
+	assert(IS_RLP(spec1));
+	assert(IS_RLP(spec2));
+	assert(rlp_spec_eq(spec1, spec2));
+
+	if (!ptrblock_eq(*termrule(spec1), *termrule(spec2))) {
+		return 0;
+	}
+	nextspec1 = rlp_nextspec(spec1);
+	assert(nextspec1 != NULL);
+	nextspec2 = rlp_nextspec(spec2);
+	assert(nextspec2 != NULL);
+
+	/* we don't need to compare the keys of spec1 and spec2 because for
+	   each corresponding rlp pair the termrule blocks are compared
+	   which means that if rlp_eq_rec finally returns 1 the same rules
+	   terminate in the subtree rooted by the top level rlp spec1 and in
+	   the subtree rooted by the top level rlp spec2; since all leaves
+	   of the subtrees are terminal (NULL, rule or elementary interval
+	   layer) we can conclude that there is no other rule except those in
+	   the termrule blocks that have created keys in the rlps */
+	for (n = 0; n < spec1->num; n++) {
+		if (!rlp_rule_elem_eq(*(nextspec1 + n), *(nextspec2 + n))) {
+			return 0;
+		}
+	}
+
+	if (HAS_WILDCARD_SPEC(spec1) &&
+	    !rlp_rule_elem_eq(*WILDCARD(spec1), *WILDCARD(spec2))) {
+		return 0;
+	}
+	return 1;
+}
+
+
+
+/*
+ * internal dimtree operations
+ */
+
+static inline hipac_error
+rlp_clone_ifneeded(struct rlp_spec *b, struct rlp_spec **newb,
+		     int newspec_set)
+{
+	int stat;
+
+	if (unlikely(b == NULL || newb == NULL)) {
+		ARG_ERR;
+	}
+
+	if (b->newspec == 0) {
+		/* we must clone because b is visible for packet matching */
+		stat = rlp_clone(b, newb);
+		if (stat < 0) {
+			return stat;
+		}
+		stat = history_replace((struct gen_spec *) b,
+				       (struct gen_spec *) *newb, newspec_set);
+		if (stat < 0) {
+			rlp_free(*newb);
+			return stat;
+		}
+	} else {
+		/* b can be modified directly */
+		*newb = b;
+	}
+	return HE_OK;
+}
+
+static inline hipac_error
+elem_clone_ifneeded(struct dt_elem *e, struct dt_elem **newe,
+		    int newspec_set)
+{
+	int stat;
+
+	if (unlikely(e == NULL || newe == NULL)) {
+		ARG_ERR;
+	}
+
+	if (e->spec.newspec == 0) {
+		/* we must clone because e is visible for packet matching */
+		stat = elem_clone(e, newe);
+		if (stat < 0) {
+			return stat;
+		}
+		stat = history_replace((struct gen_spec *) e,
+				       (struct gen_spec *) *newe, newspec_set);
+		if (stat < 0) {
+			elem_free(*newe);
+			return stat;
+		}
+	} else {
+		/* e can be modified directly */
+		*newe = e;
+	}
+	return HE_OK;
+}
+
+#ifdef DEBUG
+static void
+print_elem(struct dt_elem *e)
+{
+	int i;
+
+	DPRINT(DEBUG_DIMTREE, "term_rule: %p, ntm_rules:", e->term_rule);
+	if (e->ntm_rules.len == 0) {
+		DPRINT(DEBUG_DIMTREE, " <none> => BUG");
+		return;
+	}
+	for (i = 0; i < e->ntm_rules.len; i++) {
+		DPRINT(DEBUG_DIMTREE, " %p", e->ntm_rules.p[i]);
+	}
+}
+
+static void
+print_rlp(struct rlp_spec *rlp)
+{
+	__u32 key = 0;
+	struct locate_inf inf;
+	int i;
+
+	if (rlp == NULL) {
+		DPRINT(DEBUG_DIMTREE, "rlp: %p (this might not be what you "
+		       "expected)\n", rlp);
+		return;
+	}
+	if (!IS_RLP(rlp)) {
+		DPRINT(DEBUG_DIMTREE, "rlp: %p is __NOT__ a rlp => FATAL "
+		       "ERROR\n", rlp);
+		return;
+	}
+	DPRINT(DEBUG_DIMTREE, "rlp: %p  -  bittype: %d, dimid: %d, "
+	       "newspec: %d, num: %d\n", rlp, rlp->bittype, rlp->dimid,
+	       rlp->newspec, rlp->num);
+	DPRINT(DEBUG_DIMTREE, "   content:");
+	if (HAS_WILDCARD_DIM(rlp->dimid)) {
+		if (*WILDCARD(rlp) != NULL && IS_RULE(*WILDCARD(rlp))) {
+			DPRINT(DEBUG_DIMTREE, " (wc, %p: rule)",
+			       *WILDCARD(rlp));
+		} else if (*WILDCARD(rlp) != NULL && IS_ELEM(*WILDCARD(rlp))) {
+			DPRINT(DEBUG_DIMTREE, " (wc, %p: ", *WILDCARD(rlp));
+			print_elem((struct dt_elem *) *WILDCARD(rlp));
+			DPRINT(DEBUG_DIMTREE, ")");
+		} else {
+			DPRINT(DEBUG_DIMTREE, " (wc, %p)", *WILDCARD(rlp));
+		}
+	}
+	do {
+		if (rlp_locate(rlp, &inf, key) < 0) {
+			DPRINT(DEBUG_DIMTREE, "\n%s: no memory for locate "
+			       "info\n", __FUNCTION__);
+			return;
+		}
+		if (*inf.nextspec != NULL && IS_RULE(*inf.nextspec)) {
+			DPRINT(DEBUG_DIMTREE, " (%u, %p: rule)", inf.key,
+			       *inf.nextspec);
+		} else if (*inf.nextspec != NULL && 
+			   IS_ELEM(*inf.nextspec)) {
+			DPRINT(DEBUG_DIMTREE, " (%u, %p: ", inf.key,
+			       *inf.nextspec);
+			print_elem((struct dt_elem *) *inf.nextspec);
+			DPRINT(DEBUG_DIMTREE, ")");
+		} else {
+			DPRINT(DEBUG_DIMTREE, " (%u, %p)", inf.key,
+			       *inf.nextspec);
+		}
+		key = inf.key + 1;
+	} while (inf.key < MAXKEY(dim2btype[rlp->dimid]));
+	DPRINT(DEBUG_DIMTREE, "\n   term:");
+	if (*termrule(rlp) == NULL) {
+		DPRINT(DEBUG_DIMTREE, " <empty>\n");
+	} else {
+		for (i = 0; i < (*termrule(rlp))->len; i++) {
+			DPRINT(DEBUG_DIMTREE, " %p", (*termrule(rlp))->p[i]);
+		}
+		DPRINT(DEBUG_DIMTREE, "\n");
+	}
+}
+#endif
+
+static inline hipac_error
+segment_insert_help(struct locate_inf *inf, __u8 *ins_num,
+		    struct gen_spec* new_nextspec[], int newspec_set)
+{
+	int stat;
+
+	if (*inf->nextspec == NULL || IS_RULE(*inf->nextspec)) {
+		new_nextspec[*ins_num] = *inf->nextspec;
+	} else if (IS_ELEM(*inf->nextspec)) {
+		struct dt_elem *e;
+		stat = elem_clone((struct dt_elem *) *inf->nextspec, &e);
+		if (stat < 0) {
+			return stat;
+		}
+		stat = history_new((struct gen_spec *) e, newspec_set);
+		if (stat < 0) {
+			elem_free(e);
+			return stat;
+		}
+		new_nextspec[*ins_num] = (struct gen_spec *) e;
+	} else {
+		assert(IS_RLP(*inf->nextspec));
+		stat = rlp_clone_rec(
+			(struct rlp_spec *) *inf->nextspec,
+			(struct rlp_spec **) &new_nextspec[*ins_num],
+			newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	(*ins_num)++;
+	return HE_OK;
+}
+
+/* segment [left, right] is inserted into spec which causes at most two new
+   elementary intervals being created; for every new elementary interval
+   the neighbour interval is cloned recursively */
+static inline hipac_error
+segment_insert(struct rlp_spec **spec, __u32 left, __u32 right,
+	       int newspec_set)
+{  	
+	__u8 ins_num = 0;
+	struct gen_spec* new_nextspec[2];
+	struct locate_inf inf;
+	__u32 new_key[2];
+	int stat;
+
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: left: %u, right: %u, newspec_set: %d\n",
+	       __FUNCTION__, left, right, newspec_set);
+#ifdef DEBUG
+	print_rlp(*spec);
+#endif
+	if (left > 0) {
+		stat = rlp_locate(*spec, &inf, left - 1);
+		if (stat < 0) {
+			return stat;
+		}
+		if (inf.key != left - 1) {
+			new_key[ins_num] = left - 1;
+			stat = segment_insert_help(&inf, &ins_num,
+						   new_nextspec, newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+		}
+	}
+	
+	stat = rlp_locate(*spec, &inf, right);
+	if (stat < 0) {
+		return stat;
+	}
+	if (inf.key != right) {
+		new_key[ins_num] = right;
+		stat = segment_insert_help(&inf, &ins_num, new_nextspec,
+					   newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+
+	if (ins_num > 0) {
+		struct rlp_spec *b;
+		assert(ins_num == 1 || new_key[0] != new_key[1]);
+		if (ins_num == 1) {
+			DPRINT(DEBUG_DIMTREE, "new key: %u\n", new_key[0]);
+		} else {
+			DPRINT(DEBUG_DIMTREE, "new keys: %u, %u\n", new_key[0],
+			       new_key[1]);
+		}
+		stat = rlp_insert(*spec, ins_num, new_key, new_nextspec, &b);
+		if (stat < 0) {
+			return stat;
+		}
+		stat = history_replace((struct gen_spec *) *spec,
+				       (struct gen_spec *) b, newspec_set);
+		if (stat < 0) {
+			rlp_free(b);
+			return stat;
+		}
+		*spec = b;
+#ifdef DEBUG
+		print_rlp(*spec);
+#endif
+	} else {
+		/* we clone the rlp anyway if necessary */
+		struct rlp_spec *b;
+		stat = rlp_clone_ifneeded(*spec, &b, newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+		*spec = b;
+	}
+	return HE_OK;
+}
+
+/* forward declaration */
+static hipac_error
+dimtree_insrec(struct rlp_spec **spec, struct dt_rule *rule,
+	       __u8 match_num, int newspec_set);
+
+static hipac_error
+dimtree_insrec_null(struct rlp_spec **spec, struct dt_rule *rule,
+		    __u8 match_num, int newspec_set)
+{
+	const struct dt_match *match = ITH_DT_MATCH(rule, match_num);
+	__u8 bittype = dim2btype[match->dimid];
+	struct gen_spec *nextspec[] = {NULL};
+	__u32 key = MAXKEY(bittype);
+	struct locate_inf inf;
+	int stat;
+
+	/* create new rlp that defaults to policy and insert match
+	   recursively */
+	assert(spec != NULL && *spec == NULL);
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: match_num: %d, newspec_set: %d, match: "
+	       "(dimid: %d, left: %u, right: %u)\n", __FUNCTION__, match_num,
+	       newspec_set, match->dimid, match->left, match->right);
+	DPRINT(DEBUG_DIMTREE, "%s: new rlp: bittype: %d, dimid: %d, key: "
+	       "%u, nextspec: %p\n", __FUNCTION__, bittype, match->dimid, key,
+	       *nextspec);
+	*spec = rlp_new(bittype, match->dimid, 1, &key, nextspec);
+	if (*spec == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	stat = history_new((struct gen_spec *) *spec, newspec_set);
+	if (stat < 0) {
+		rlp_free(*spec);
+		return stat;
+	}
+
+	/* match must be non-wildcard */
+	assert(match->left > 0 || match->right < MAXKEY((*spec)->bittype));
+	stat = segment_insert(spec, match->left, match->right, newspec_set);
+	if (stat < 0) {
+		return stat;
+	}
+	stat = rlp_locate(*spec, &inf, match->right);
+	if (stat < 0) {
+		return stat;
+	}
+	if (match_num == rule->dt_match_len - 1) {
+		/* final match of rule -> insert rule into termrule block */
+		struct ptrblock **term = termrule(*spec);
+		stat = termrule_insert(term, rule);
+		if (stat < 0) {
+			return stat;
+		}
+		*inf.nextspec = (struct gen_spec *) rule;
+	} else {
+		/* before final match -> insert next match by recursion */
+		stat = dimtree_insrec_null((struct rlp_spec **)
+					   inf.nextspec, rule, match_num + 1,
+					   newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	return HE_OK;
+}
+
+static hipac_error
+dimtree_insrec_rule_elem(struct dt_rule_elem_spec **spec, struct dt_rule *rule,
+			 __u8 match_num, struct ptrblock *term_prop,
+			 int newspec_set)
+{
+	struct dt_match *match = ITH_DT_MATCH(rule, match_num);
+	__u8 bittype = dim2btype[match->dimid];
+	__u32 key = MAXKEY(bittype);
+	struct gen_spec *nextspec[1];
+	struct rlp_spec *newspec;
+	struct ptrblock **term;
+	struct locate_inf inf;
+	int stat;
+
+	assert(spec != NULL);
+	assert(*spec != NULL);
+	assert(IS_RULE(*spec) || IS_ELEM(*spec));
+	assert(match->left > 0 || match->right < MAXKEY(bittype));
+
+	/* create new rlp and insert match recursively; term_prop propagates
+	   through all dimension while remaining in each dimension as
+	   termrule block; if anything goes wrong before term_prop is
+	   attached to newspec term_prop will be freed; later it is treated
+	   by the history */
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: match_num: %d, newspec_set: %d, match: "
+	       "(dimid: %d, left: %u, right: %u)\n", __FUNCTION__, match_num,
+	       newspec_set, match->dimid, match->left, match->right);
+	if (HAS_WILDCARD_DIM(match->dimid)) {
+		nextspec[0] = NULL;
+		DPRINT(DEBUG_DIMTREE, "%s: new rlp: bittype: %d, dimid: %d,"
+		       " key: %u, nextspec: %p\n", __FUNCTION__, bittype,
+		       match->dimid, key, *nextspec);
+		newspec = rlp_new(bittype, match->dimid, 1, &key, nextspec);
+		if (newspec == NULL) {
+			if (term_prop != NULL) {
+				ptrblock_free(term_prop);
+			}
+			return HE_LOW_MEMORY;
+		}
+		*WILDCARD(newspec) = (struct gen_spec *) *spec;
+	} else {
+		nextspec[0] = (struct gen_spec *) *spec;
+		DPRINT(DEBUG_DIMTREE, "%s: new rlp: bittype: %d, dimid: %d,"
+		       " key: %u, nextspec: %p\n", __FUNCTION__, bittype,
+		       match->dimid, key, *nextspec);
+		newspec = rlp_new(bittype, match->dimid, 1, &key, nextspec);
+		if (newspec == NULL) {
+			if (term_prop != NULL) {
+				ptrblock_free(term_prop);
+			}
+			return HE_LOW_MEMORY;
+		}
+	}
+	stat = history_new((struct gen_spec *) newspec, newspec_set);
+	if (stat < 0) {
+		rlp_free(newspec);
+		if (term_prop != NULL) {
+			ptrblock_free(term_prop);
+		}
+		return stat;
+	}
+	stat = segment_insert(&newspec, match->left, match->right,
+			      newspec_set);
+	if (stat < 0) {
+		if (term_prop != NULL) {
+			ptrblock_free(term_prop);
+		}
+		return stat;
+	}
+	/* attach term_prop to newspec -> if anything goes wrong from now on
+	   term_prop must not be freed here */
+	term = termrule(newspec);
+	*term = term_prop;
+	stat = rlp_locate(newspec, &inf, match->right);
+	if (stat < 0) {
+		return stat;
+	}
+       
+	if (match_num == rule->dt_match_len - 1) {
+		/* final match of rule -> insert rule into termrule block */
+		stat = termrule_insert(term, rule);
+		if (stat < 0) {
+			return stat;
+		}
+		if (HAS_WILDCARD_DIM(match->dimid)) {
+			assert(*inf.nextspec == NULL);
+			*inf.nextspec = (struct gen_spec *) rule;
+		} else {
+			stat = rule_elem_insert((struct dt_rule_elem_spec **)
+						inf.nextspec, rule,
+						newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+		}
+	} else {
+		/* before final match -> insert next match by recursion */
+		if (*inf.nextspec == NULL) {
+                        stat = dimtree_insrec_null((struct rlp_spec **)
+						   inf.nextspec, rule,
+						   match_num + 1, newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+                } else {
+			struct ptrblock *term_prop_clone = NULL;
+			if (term_prop != NULL) {
+				stat = ptrblock_clone(term_prop,
+						      &term_prop_clone);
+				if (stat < 0) {
+					return stat;
+				}
+			}
+			stat = dimtree_insrec_rule_elem(
+				(struct dt_rule_elem_spec **) inf.nextspec,
+				rule, match_num + 1, term_prop_clone,
+				newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+		}
+	}
+	/* newspec is a rlp (not struct dt_rule_elem_spec *); the cast is
+	   anyway necessary because of spec */
+	*spec = (struct dt_rule_elem_spec *) newspec;
+	return HE_OK;
+}
+
+static inline hipac_error
+dimtree_insrec_curdimid_sm_help(struct rlp_spec *spec, struct gen_spec **g,
+				struct dt_rule *rule, __u8 match_num,
+				__u32 right, __u8 wildcard, int newspec_set,
+				int do_cut)
+{
+	int stat;
+
+	if (*g == NULL) {
+		/* insert rule into policy interval */
+		stat = dimtree_insrec_null((struct rlp_spec **) g, rule,
+					   match_num, newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	} else if (IS_RLP(*g)) {
+		/* non-terminal case */
+		struct rlp_spec *b = (struct rlp_spec *) *g;
+
+		/* we don't have to clone if dimtree_insrec_curdimid_eq is
+		   called by dimtree_insrec because segment_insert clones
+		   the rlp anyway if necessary */
+		if ((b->dimid != ITH_DT_MATCH(rule, match_num)->dimid)) {
+			stat = rlp_clone_ifneeded(b, &b, newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+		}
+		stat = dimtree_insrec(&b, rule, match_num, newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+		*g = (struct gen_spec *) b;
+	} else {
+		/* the rules that terminate in g will propagate to termrule
+		   blocks in below dimensions */
+		struct dt_rule_elem_spec **re;
+		struct ptrblock *term_prop;
+		assert(IS_ELEM(*g) || IS_RULE(*g));
+		stat = termrule_subset(*termrule(spec), &term_prop, right,
+				       wildcard, spec->dimid);
+		if (stat < 0) {
+			return stat;
+		}
+		if (do_cut && *termrule(spec) != NULL && term_prop != NULL) {
+			/* remove all rules in term_prop from current
+			   termrule block */
+			stat = termrule_cut(termrule(spec), term_prop);
+			if (stat < 0) {
+				ptrblock_free(term_prop);
+				return stat;
+			}
+		}
+
+		re = (struct dt_rule_elem_spec **) g;
+		if (IS_ELEM(*re)) {
+			struct dt_elem *e;
+			stat = elem_clone_ifneeded((struct dt_elem *) *re, &e,
+						   newspec_set);
+			if (stat < 0) {
+				if (term_prop != NULL) {
+					ptrblock_free(term_prop);
+				}
+				return stat;
+			}
+			*re = (struct dt_rule_elem_spec *) e;
+		}
+		stat = dimtree_insrec_rule_elem(re, rule, match_num,
+						term_prop, newspec_set);
+		if (stat < 0) {
+			/* term_prop was freed by
+			   dimtree_insrec_rule_elem */
+			return stat;
+		}
+	}
+	return HE_OK;
+}
+
+static hipac_error
+dimtree_insrec_curdimid_sm(struct rlp_spec **spec, struct dt_rule *rule,
+			   __u8 match_num, int newspec_set)
+{
+	__u32 key = 0;
+	__u32 maxkey = MAXKEY((*spec)->bittype);
+	struct locate_inf inf;
+	int stat;
+
+	assert(spec != NULL);
+	assert(*spec != NULL);
+	assert(IS_RLP(*spec));
+	assert(match_num < rule->dt_match_len);
+	/* insert it into every elementary interval respectively the wildcard
+	   pointer */
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: match_num: %d, newspec_set: %d\n",
+	       __FUNCTION__, match_num, newspec_set);
+#ifdef DEBUG
+	print_rlp(*spec);
+#endif
+	if (HAS_WILDCARD_SPEC(*spec)) {
+		return dimtree_insrec_curdimid_sm_help(
+			*spec, WILDCARD(*spec), rule, match_num, 0, 1,
+			newspec_set, 1);
+	}
+
+	do {
+		stat = rlp_locate(*spec, &inf, key);
+		if (stat < 0) {
+			return stat;
+		}
+		key  = inf.key + 1;
+		stat = dimtree_insrec_curdimid_sm_help(
+			*spec, inf.nextspec, rule, match_num, inf.key, 0,
+			newspec_set, 0);
+		if (stat < 0) {
+			return stat;
+		}
+	} while (inf.key < maxkey);
+	
+	if (*termrule(*spec) != NULL) {
+		/* by inserting rule into every elementary interval the
+		   dimension becomes completely nonterminating */
+		ptrblock_free(*termrule(*spec));
+		*termrule(*spec) = NULL;
+	}
+	return HE_OK;
+}
+
+/* necessary forward declaration */
+static hipac_error
+dimtree_insrec_curdimid_eq_tm(struct rlp_spec **spec, struct dt_rule *rule,
+			      __u32 left, __u32 right, int newspec_set);
+
+static inline hipac_error
+dimtree_insrec_curdimid_eq_tm_help(struct gen_spec **g, struct dt_rule *rule,
+				   struct ptrblock **term, __u8 *ins_termrule,
+				   int newspec_set)
+{
+	int stat;
+
+	if (*g != NULL && IS_RLP(*g)) {
+		/* non-terminal case */
+		struct rlp_spec *b;
+
+		stat = rlp_clone_ifneeded((struct rlp_spec *) *g, &b,
+					  newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+		stat = dimtree_insrec_curdimid_eq_tm(
+			&b, rule, 0, MAXKEY(b->bittype), newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+		*g = (struct gen_spec *) b;
+	} else {
+		/* beyond final match of rule -> insert rule into
+		   termrule block if not already inserted */
+		struct dt_rule_elem_spec **re;
+		if (*ins_termrule) {
+			stat = termrule_insert(term, rule);
+			if (stat < 0) {
+				return stat;
+			}
+			*ins_termrule = 0;
+		}
+		
+		re = (struct dt_rule_elem_spec **) g;
+		if (*re != NULL && IS_ELEM(*re)) {
+			struct dt_elem *e;
+			stat = elem_clone_ifneeded((struct dt_elem *) *re, &e,
+						   newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+			*re = (struct dt_rule_elem_spec *) e;
+		}
+		stat = rule_elem_insert(re, rule, newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	return HE_OK;
+}
+
+static hipac_error
+dimtree_insrec_curdimid_eq_tm(struct rlp_spec **spec, struct dt_rule *rule,
+			      __u32 left, __u32 right, int newspec_set)
+{
+	__u8 ins_termrule = 1;
+	struct ptrblock **term = termrule(*spec);
+	struct locate_inf inf;
+	__u32 key = left;
+	int stat;
+
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: left: %u, right: %u, newspec_set: %d\n",
+	       __FUNCTION__, left, right, newspec_set);
+#ifdef DEBUG
+	print_rlp(*spec);
+#endif
+	if (HAS_WILDCARD_SPEC(*spec) && left == 0 &&
+	    right == MAXKEY((*spec)->bittype)) {
+		/* insert wildcard match into wildcard dimension */
+		return dimtree_insrec_curdimid_eq_tm_help(
+			WILDCARD(*spec), rule, term, &ins_termrule,
+			newspec_set);
+	}
+
+	/* iterate over every elementary interval between left and right
+	   and check if rule is better than the current or recurse if
+	   elementary interval is non-terminating */
+	do {
+		stat = rlp_locate(*spec, &inf, key);
+		if (stat < 0) {
+			return stat;
+		}
+		key  = inf.key + 1;
+		stat = dimtree_insrec_curdimid_eq_tm_help(
+			inf.nextspec, rule, term, &ins_termrule,
+			newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	} while (inf.key < right);
+	return HE_OK;
+}
+
+static hipac_error
+dimtree_insrec_curdimid_eq(struct rlp_spec **spec, struct dt_rule *rule,
+			   const struct dt_match *match, __u8 match_num,
+			   int newspec_set)
+{
+	__u32 key = match->left;
+	struct locate_inf inf;
+	int stat;
+
+	/* match must be non-wildcard */
+	assert(match->left > 0 || match->right < MAXKEY((*spec)->bittype));
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: match_num: %d, newspec_set: %d, match: "
+	       "(dimid: %d, left: %u, right: %u)\n", __FUNCTION__, match_num,
+	       newspec_set, match->dimid, match->left, match->right);
+#ifdef DEBUG
+	print_rlp(*spec);
+#endif
+	stat = segment_insert(spec, match->left, match->right, newspec_set);
+	if (stat < 0) {
+		return stat;
+	}
+
+	/* insert match and iterate over every overlapped interval */
+	if (match_num == rule->dt_match_len - 1) {
+		/* final match of rule */
+		stat = dimtree_insrec_curdimid_eq_tm(
+			spec, rule, match->left, match->right, newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	} else {
+		/* before final match of rule */
+		do {
+			stat = rlp_locate(*spec, &inf, key);
+			if (stat < 0) {
+				return stat;
+			}
+			key = inf.key + 1;
+			
+			if (*inf.nextspec == NULL) {
+				/* insert rule into policy interval */
+				stat = dimtree_insrec_null(
+					(struct rlp_spec **) inf.nextspec,
+					rule, match_num + 1, newspec_set);
+				if (stat < 0) {
+					return stat;
+				}
+			} else if (IS_RLP(*inf.nextspec)) {
+				/* non-terminal case */
+				struct rlp_spec *b = (struct rlp_spec *)
+					*inf.nextspec;
+				
+				/* we don't have to clone if
+				   dimtree_insrec_curdimid_eq is called by
+				   dimtree_insrec because segment_insert
+				   clones the rlp anyway if necessary */
+				if (b->dimid !=
+				    ITH_DT_MATCH(rule, match_num + 1)->dimid) {
+					stat = rlp_clone_ifneeded(
+						(struct rlp_spec *)
+						*inf.nextspec, &b,
+						newspec_set);
+					if (stat < 0) {
+						return stat;
+					}
+				}
+				stat = dimtree_insrec(
+					&b, rule, match_num + 1, newspec_set);
+				if (stat < 0) {
+					return stat;
+				}
+				*inf.nextspec = (struct gen_spec *) b;
+			} else {
+				/* the rules that terminate in the current
+				   elementary interval will propagate to
+				   termrule blocks in below dimensions */
+				struct dt_rule_elem_spec **re;
+				struct ptrblock *term_prop;
+				stat = termrule_subset(
+					*termrule(*spec), &term_prop, inf.key,
+					0, (*spec)->dimid);
+				if (stat < 0) {
+					if (term_prop != NULL) {
+						ptrblock_free(term_prop);
+					}
+					return stat;
+				}
+				re = (struct dt_rule_elem_spec **)
+					inf.nextspec;
+				if (IS_ELEM(*re)) {
+					struct dt_elem *e;
+					stat = elem_clone_ifneeded(
+						(struct dt_elem *) *re, &e,
+						newspec_set);
+					if (stat < 0) {
+						if (term_prop != NULL) {
+							ptrblock_free(
+							      term_prop);
+						}
+						return stat;
+					}
+					*re = (struct dt_rule_elem_spec *) e;
+				}
+				stat = dimtree_insrec_rule_elem(
+					re, rule, match_num + 1, term_prop,
+					newspec_set);
+				if (stat < 0) {
+					/* term_prop was freed by
+					   dimtree_insrec_rule_elem */
+					return stat;
+				}
+			}
+		} while (inf.key < match->right);
+
+		/* as the rule continues we can be sure that every terminating
+		   rule whose match in the current dimension is completely
+		   overlapped by match can be removed from the termrule block;
+		   we possibly forget to remove rules with partially overlapped
+		   matches but this does NOT cause any harm and the case should
+		   be very rare */
+		stat = termrule_delete_ovl(termrule(*spec), match->left,
+					   match->right, (*spec)->dimid);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	return HE_OK;
+}
+
+static hipac_error
+dimtree_insrec_curdimid_gr(struct rlp_spec **spec, struct dt_rule *rule,
+			   const struct dt_match *match, __u8 match_num,
+			   int newspec_set)
+{
+	__u8 bittype = dim2btype[match->dimid];
+	__u32 key = MAXKEY(bittype);	
+	struct gen_spec *nextspec[1];
+	struct rlp_spec *newspec;
+	int stat;
+
+	/* create missing dimension and insert current match by recursion */
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: match_num: %d, newspec_set: %d, match: "
+	       "(dimid: %d, left: %u, right: %u)\n", __FUNCTION__, match_num,
+	       newspec_set, match->dimid, match->left, match->right);
+#ifdef DEBUG
+	print_rlp(*spec);
+#endif
+	if (HAS_WILDCARD_DIM(match->dimid)) {
+		nextspec[0] = NULL;
+		DPRINT(DEBUG_DIMTREE, "%s: new rlp: bittype: %d, dimid: %d,"
+		       " key: %u, nextspec: %p\n", __FUNCTION__, bittype,
+		       match->dimid, key, *nextspec);
+		newspec = rlp_new(bittype, match->dimid, 1, &key, nextspec);
+		if (newspec == NULL) {
+			return HE_LOW_MEMORY;
+		}
+		*WILDCARD(newspec) = (struct gen_spec *) *spec;
+	} else {
+		nextspec[0] = (struct gen_spec *) *spec;
+		DPRINT(DEBUG_DIMTREE, "%s: new rlp: bittype: %d, dimid: %d,"
+		       " key: %u, nextspec: %p\n", __FUNCTION__, bittype,
+		       match->dimid, key, *nextspec);
+		newspec = rlp_new(bittype, match->dimid, 1, &key, nextspec);
+		if (newspec == NULL) {
+			return HE_LOW_MEMORY;
+		}
+	}
+	stat = history_new((struct gen_spec *) newspec, newspec_set);
+	if (stat < 0) {
+		rlp_free(newspec);
+		return stat;
+	}
+	*spec = newspec;
+	return dimtree_insrec(spec, rule, match_num, newspec_set);
+}
+
+static hipac_error
+dimtree_insrec(struct rlp_spec **spec, struct dt_rule *rule,
+	       __u8 match_num, int newspec_set)
+{
+	struct dt_match *match;
+
+	/* spec non-terminating	*/
+	assert(spec != NULL);
+	assert(*spec != NULL);
+	assert(IS_RLP(*spec));
+
+	/* rule is not finished yet */
+	assert(match_num < rule->dt_match_len);
+
+	match = ITH_DT_MATCH(rule, match_num);
+
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: match_num: %d, newspec_set: %d, match: "
+	       "(dimid: %d,  left: %u, right: %u)\n", __FUNCTION__, match_num,
+	       newspec_set, match->dimid, match->left, match->right);
+#ifdef DEBUG
+	print_rlp(*spec);
+#endif
+	if ((*spec)->dimid < match->dimid) {
+		/* match in current dimension treated as wildcard because there
+		   is no match for the current dimension */
+		return dimtree_insrec_curdimid_sm(spec, rule, match_num,
+						  newspec_set);
+	} else if ((*spec)->dimid == match->dimid) {
+		/* there is a match in the current dimension which is per
+		   default no wildcard */
+		return dimtree_insrec_curdimid_eq(spec, rule, match,
+						  match_num, newspec_set);
+		
+	} else {
+		/* the dimension of the current match has not yet been
+		   created */
+		return dimtree_insrec_curdimid_gr(spec, rule, match,
+						  match_num, newspec_set);
+	}
+}
+
+static inline hipac_error
+segment_delete_help(struct rlp_spec *spec, struct locate_inf *bound1,
+		    __u32 lkey, __u32 dkey, __u32 del_key[], __u8 *del_num,
+		    int newspec_set)
+{
+	struct gen_spec *current1, *current2;
+	struct locate_inf bound2;
+	int stat;
+
+	stat = rlp_locate(spec, &bound2, lkey);
+	if (stat < 0) {
+		return stat;
+	}
+	current1 = *bound1->nextspec;
+	current2 = *bound2.nextspec;
+	switch (rlp_rule_elem_eq(current1, current2)) {
+	    case 1:
+		    if (current1 == NULL ||
+			!termrule_exists(*termrule(spec), spec->dimid, dkey)) {
+			    del_key[(*del_num)++] = dkey;
+		    }
+		    break;
+	    case 2:
+		    if (!termrule_exists(*termrule(spec), spec->dimid, dkey)) {
+			    history_obsolete(current1, newspec_set);
+			    del_key[(*del_num)++] = dkey;
+		    }
+		    break;
+	    case 3:
+		    del_key[(*del_num)++] = dkey;
+		    stat = rlp_free_rec((struct rlp_spec *) current1,
+					newspec_set, 0);
+		    if (stat < 0) {
+			    return stat;
+		    }
+		    break;
+	    default:
+		    break;
+	}
+	return HE_OK;
+}
+
+/* segment [left, right] is deleted from spec if the neighbours of left and
+   right point to the same spec; at most two elementary intervals can be
+   deleted */
+static inline hipac_error
+segment_delete(struct rlp_spec **spec, __u32 left, __u32 right,
+	       int newspec_set)
+{
+	__u8 del_num = 0;
+	__u32 maxkey = MAXKEY((*spec)->bittype);
+	__u32 del_key[2] = {0, 0};
+	struct locate_inf bound1;
+	int stat;
+	
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: left: %u, right: %u, newspec_set: %d\n",
+	       __FUNCTION__, left, right, newspec_set);
+#ifdef DEBUG
+	print_rlp(*spec);
+#endif
+	if (left > 0) {
+		stat = rlp_locate(*spec, &bound1, left - 1);
+		if (stat < 0) {
+			return stat;
+		}
+		assert(bound1.key == left - 1);
+		stat = segment_delete_help(*spec, &bound1, left, left - 1,
+					   del_key, &del_num, newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+
+	if (right < maxkey) {
+		stat = rlp_locate(*spec, &bound1, right);
+		if (stat < 0) {
+			return stat;
+		}
+		assert(bound1.key == right);
+		stat = segment_delete_help(*spec, &bound1, right + 1, right,
+					   del_key, &del_num, newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	
+	if (del_num > 0) {
+		struct rlp_spec *b;
+		assert(del_num == 1 || del_key[0] < del_key[1]);
+		if (del_num == 1) {
+			DPRINT(DEBUG_DIMTREE, "del key: %u\n", del_key[0]);
+		} else {
+			DPRINT(DEBUG_DIMTREE, "del keys: %u, %u\n",
+			       del_key[0], del_key[1]);
+		}
+		stat = rlp_delete(*spec, del_num, del_key, &b);
+		if (stat < 0) {
+			return stat;
+		}
+		stat = history_replace((struct gen_spec *) *spec,
+				       (struct gen_spec *) b, newspec_set);
+		if (stat < 0) {
+			rlp_free(b);
+			return stat;
+		}
+		*spec = b;
+	}
+	return HE_OK;
+}
+
+/* forward declaration needed for dimtree_delrec_interval */
+static hipac_error
+dimtree_delrec(struct rlp_spec **spec, const struct dt_rule *rule,
+	       __u8 match_num, struct ptrlist *term_prop, int newspec_set);
+
+static inline hipac_error
+dimtree_delrec_interval(struct gen_spec **spec, const struct dt_rule *rule,
+			__u8 match_num, struct ptrlist *tmpterm,
+			struct ptrblock **term, __u32 right, __u8 wildcard,
+			__u8 dimid, int newspec_set)
+{
+	int stat;
+
+	assert(*spec != NULL);
+	if (IS_RLP(*spec)) {
+		/* non-terminal case */
+		struct rlp_spec *b;
+
+		stat = rlp_clone_ifneeded((struct rlp_spec *) *spec, &b,
+					  newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+		stat = dimtree_delrec(&b, rule, match_num, tmpterm,
+				      newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+		*spec = (struct gen_spec *) b;
+	} else {
+		struct dt_rule_elem_spec **re =
+			(struct dt_rule_elem_spec **) spec;
+
+		if (IS_ELEM(*re)) {
+			struct dt_elem *e;
+			stat = elem_clone_ifneeded((struct dt_elem *) *re, &e,
+						   newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+			*re = (struct dt_rule_elem_spec *) e;
+		}
+		stat = rule_elem_delete(re, rule, *term, right, wildcard,
+					dimid, newspec_set);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	return HE_OK;
+}
+
+static hipac_error
+dimtree_delrec(struct rlp_spec **spec, const struct dt_rule *rule,
+	       __u8 match_num, struct ptrlist *term_prop, int newspec_set)
+{	
+	/* current match is initialized as wildcard */
+	__u32 left   = 0;	
+	__u32 key    = 0;
+	__u32 maxkey = MAXKEY((*spec)->bittype);
+	__u8 match_is_wildcard = 1;
+
+	/* collects all terminating specs from the below dimension */
+	struct ptrlist *tmpterm;
+	struct ptrblock **term;
+	struct locate_inf inf;
+	int stat;
+	
+#ifdef DEBUG
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	if (match_num < rule->dt_match_len) {
+		const struct dt_match *match = ITH_DT_MATCH(rule, match_num);
+		DPRINT(DEBUG_DIMTREE, "%s: match_num: %d, newspec_set: %d, "
+		       "le: %p, match: (dimid: %d, left: %u, right: %u)\n",
+		       __FUNCTION__, match_num, newspec_set, rule,
+		       match->dimid, match->left, match->right);
+	} else {
+		DPRINT(DEBUG_DIMTREE, "%s: match_num: %d, newspec_set: %d, "
+		       "rule: %p, match: <none>\n", __FUNCTION__, match_num,
+		       newspec_set, rule);
+	}
+	print_rlp(*spec);
+#endif
+	tmpterm = tmp_termrule_new();
+	if (tmpterm == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	term = termrule(*spec);
+
+	/* dimtree_delrec is never called for terminal cases */
+	assert(*spec != NULL);
+	assert(IS_RLP(*spec));
+
+	if (match_num < rule->dt_match_len) {
+		/* rule is not finished yet */
+		const struct dt_match *match = ITH_DT_MATCH(rule, match_num);
+		
+		if ((*spec)->dimid == match->dimid) {
+			/* match must be non-wildcard */
+			assert(match->left > 0 ||
+			       match->right < MAXKEY((*spec)->bittype));
+			key = left = match->left;
+			maxkey = match->right;
+			match_is_wildcard = 0;
+			match_num++;
+		}
+	}
+	
+	if (HAS_WILDCARD_SPEC(*spec) && match_is_wildcard) {
+		assert(*WILDCARD(*spec) != NULL);
+		stat = dimtree_delrec_interval(
+			WILDCARD(*spec), rule, match_num, tmpterm, term,
+			0, 1, (*spec)->dimid, newspec_set);
+		if (stat < 0) {
+			goto error;
+		}
+	} else {
+		do {
+			stat = rlp_locate(*spec, &inf, key);
+			if (stat < 0) {
+				goto error;
+			}
+			key = inf.key + 1;
+			assert(*inf.nextspec != NULL);
+			stat = dimtree_delrec_interval(
+				inf.nextspec, rule, match_num, tmpterm, term,
+				inf.key, 0, (*spec)->dimid, newspec_set);
+			if (stat < 0) {
+				goto error;
+			}
+		} while (inf.key < maxkey);
+	}
+		
+	/* delete rule from termrule block if it is there */
+	stat = termrule_delete(term, rule);
+	if (stat < 0) {
+		goto error;
+	}
+
+	/* merge temporary termrule list with termrule block */
+	stat = termrule_merge(term, tmpterm);
+	if (stat < 0) {
+		return stat;
+	}
+
+	if (!match_is_wildcard) {
+		/* remove surrounding elementary intervals represented by left
+		   and maxkey if necessary */
+		stat = segment_delete(spec, left, maxkey, newspec_set);
+		if (stat < 0) {
+			/* tmpterm is already freed */
+			return stat;
+		}
+		term = termrule(*spec);
+	}
+	
+	if ((*spec)->num == 1) {
+		/* spec is empty => drop it */
+		struct gen_spec *nextspec;
+
+		if (HAS_WILDCARD_SPEC(*spec)) {
+			assert((stat = rlp_locate(*spec, &inf, 0),
+				stat < 0 ? 1 : *inf.nextspec == NULL));
+			nextspec = *WILDCARD(*spec);
+		} else {
+			stat = rlp_locate(*spec, &inf, 0);
+			if (stat < 0) {
+				/* tmpterm is already freed */
+				return stat;
+			}
+			nextspec = *inf.nextspec;
+		}
+		
+		if (*term != NULL && term_prop != NULL) {
+			stat = tmp_termrule_merge(term_prop, *term);
+			if (stat < 0) {
+				/* tmpterm is already freed */
+				return stat;
+			}
+		}
+		stat = history_obsolete((struct gen_spec *) *spec,
+					newspec_set);
+		if (stat < 0) {
+			/* tmpterm is already freed */
+			return stat;
+		}
+
+		if (nextspec == NULL || IS_RULE(nextspec)) {
+			*spec = (struct rlp_spec *) nextspec;
+		} else if (IS_RLP(nextspec)) {
+			struct rlp_spec *b;
+
+			stat = rlp_clone_ifneeded((struct rlp_spec *)
+						  nextspec, &b, newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+			*spec = (struct rlp_spec *) b;
+		} else {
+			struct dt_elem *e;
+			assert(IS_ELEM(nextspec));
+			stat = elem_clone_ifneeded((struct dt_elem *)
+						   nextspec, &e, newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+			*spec = (struct rlp_spec *) e;
+		}
+	}
+	return HE_OK;
+
+ error:
+	tmp_termrule_free(tmpterm);
+	return stat;
+}
+
+
+
+/*
+ * public dimtree operations
+ */
+
+hipac_error
+dimtree_new(struct dimtree **newdt, __u32 origin, const char *chain_name,
+	    struct dt_rule *dummy, struct dt_rule *policy)
+{
+	struct dt_chain *chain;
+
+	if (unlikely(newdt == NULL || chain_name == NULL || dummy == NULL ||
+		     policy == NULL || dummy->spec.action != TARGET_DUMMY ||
+		     !IS_RULE_TERM(policy) || policy->dt_match_len != 0)) {
+		ARG_ERR;
+	}
+	*newdt = hp_alloc(sizeof(**newdt), 1);
+	if (*newdt == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	chain = hp_alloc(sizeof(*chain), 1);
+	if (chain == NULL) {
+		hp_free(*newdt);
+		*newdt = NULL;
+		return HE_LOW_MEMORY;
+	}
+	INIT_LIST_HEAD(&chain->head);
+	strncpy(chain->name, chain_name, sizeof(chain->name));
+	chain->name[sizeof(chain->name) - 1] = '\0';
+	chain->first = policy;
+	chain->len = 2;
+	list_add(&policy->head, &chain->head);
+	list_add(&dummy->head, &chain->head);
+
+	(*newdt)->origin = origin;
+        (*newdt)->top = (struct gen_spec *) policy;
+	(*newdt)->top_new = NULL;
+        (*newdt)->need_commit = 0;
+	(*newdt)->chain = chain;
+	return HE_OK;
+}
+
+void
+dimtree_free(struct dimtree *dt)
+{
+	struct list_head *lh;
+	struct dt_rule *rule;
+
+	if (unlikely(dt == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	if (dt->top != NULL) {
+		if (IS_RLP(dt->top)) {
+			rlp_free_rec((struct rlp_spec *) dt->top, 0, 1);
+		} else if (IS_ELEM(dt->top)) {
+			elem_free((struct dt_elem *) dt->top);
+		}
+	}
+	for (lh = dt->chain->head.next; lh != &dt->chain->head;) {
+		rule = list_entry(lh, struct dt_rule, head);
+		lh = lh->next;
+		if (rule->exec_match != NULL) {
+			ptrblock_free(rule->exec_match);
+		}
+		hp_free(rule);
+	}
+	hp_free(dt->chain);
+	hp_free(dt);
+}
+
+void
+dimtree_flush(struct dimtree *dt)
+{
+	struct gen_spec *top;
+	struct list_head *lh;
+	struct dt_rule *rule;
+
+	if (unlikely(dt == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	assert(dt->chain->len >= 2);
+	assert(list_entry(dt->chain->head.next,
+			  struct dt_rule, head)->spec.action == TARGET_DUMMY);
+	top = dt->top;
+	dt->top = (struct gen_spec *) list_entry(dt->chain->head.prev,
+						 struct dt_rule, head);
+	((struct dt_rule *) dt->top)->spec.pos = 1;
+	dt->need_commit = 0;
+	synchronize_rcu();
+	if (top != NULL) {
+		if (IS_RLP(top)) {
+			rlp_free_rec((struct rlp_spec *) top, 0, 1);
+		} else  if (IS_ELEM(top)) {
+			elem_free((struct dt_elem *) top);
+		}
+	}
+	for (lh = dt->chain->head.next->next; lh != dt->chain->head.prev;) {
+		rule = list_entry(lh, struct dt_rule, head);
+		lh = lh->next;
+		list_del(lh->prev);
+		if (rule->exec_match != NULL) {
+			ptrblock_free(rule->exec_match);
+		}
+		hp_free(rule);
+	}
+	dt->chain->first = list_entry(dt->chain->head.prev, struct dt_rule,
+				      head);
+	dt->chain->len = 2;
+}
+
+const char *
+dimtree_get_chain_name(const struct dimtree *dt)
+{
+	if (unlikely(dt == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+	return dt->chain->name;
+}
+
+static hipac_error
+dimtree_insert_intern(struct dimtree *dt, struct dt_rule *rule, __u32 origin,
+		      int inc, int insert_chain, int commit)
+{
+	struct gen_spec *top;
+	struct list_head *lh;
+	struct dt_rule *r;
+	int stat;
+	
+	if (unlikely(dt == NULL || rule == NULL ||
+		     rule->spec.pos <= 0 ||
+		     rule->spec.pos >
+		     list_entry(dt->chain->head.prev,
+				struct dt_rule, head)->spec.pos ||
+		     (IS_TARGET_DUMMY(rule) && !insert_chain))) {
+		ARG_ERR;
+	}
+	
+	/* insert rule into dt_chain */
+	assert(!rule->deleted);
+	if (insert_chain) {
+		if (likely(inc)) {
+			for (lh = dt->chain->head.prev; lh != &dt->chain->head;
+			     lh = lh->prev) {
+				r = list_entry(lh, struct dt_rule, head);
+				if (r->spec.pos < rule->spec.pos) {
+					break;
+				}
+				r->spec.pos++;
+			}
+			list_add(&rule->head, lh);
+		} else {
+			__u32 maxpos = list_entry(dt->chain->head.prev,
+						  struct dt_rule,
+						  head)->spec.pos;
+			if (((maxpos + 1) * rule->spec.pos) / dt->chain->len <
+			    dt->chain->len >> 1) {
+				list_for_each (lh, &dt->chain->head) {
+					r = list_entry(lh, struct dt_rule,
+						       head);
+					if (r->spec.pos > rule->spec.pos) {
+						break;
+					}
+				}
+				list_add_tail(&rule->head, lh);
+			} else {
+				for (lh = dt->chain->head.prev;
+				     lh != &dt->chain->head; lh = lh->prev) {
+					r = list_entry(lh, struct dt_rule,
+						       head);
+					if (r->spec.pos <= rule->spec.pos) {
+						break;
+					}
+				}
+				list_add(&rule->head, lh);
+			}
+		}
+		dt->chain->len++;
+		if (IS_TARGET_DUMMY(rule)) {
+			return HE_OK;
+		}
+	}
+
+	/* origin check */
+	if (!(dt->origin & origin)) {
+		return HE_RULE_ORIGIN_MISMATCH;
+	}
+
+	if (!dt->need_commit) {
+		/* first operation in a series => clone top level structure
+		   if necessary */
+		if (dt->top == NULL) {
+			top = NULL;
+		} else if (IS_RLP(dt->top)) {
+			stat = rlp_clone((struct rlp_spec *) dt->top,
+					 (struct rlp_spec **) &top);
+			if (stat < 0) {
+				return stat;
+			}
+			stat = history_replace(dt->top, top, !commit);
+			if (stat < 0) {
+				rlp_free((struct rlp_spec *) top);
+				history_undo();
+				return stat;
+			}
+		} else if (IS_ELEM(dt->top)) {
+			stat = elem_clone((struct dt_elem *) dt->top,
+					  (struct dt_elem **) &top);
+			if (stat < 0) {
+				return stat;
+			}
+			stat = history_replace(dt->top, top, !commit);
+			if (stat < 0) {
+				elem_free((struct dt_elem *) top);
+				history_undo();
+				return stat;
+			}
+		} else {
+			assert(IS_RULE(dt->top));
+			top = dt->top;
+		}
+	} else {
+		top = dt->top_new;
+	}
+
+	/* insert rule into rlp */
+	if (rule->dt_match_len == 0) {
+		/* rule has no native matches at all */
+		if (top != NULL && IS_RLP(top)) {
+			stat = dimtree_insrec_curdimid_eq_tm(
+				(struct rlp_spec **) &top, rule, 0,
+				MAXKEY(dim2btype[((struct rlp_spec *)
+						  top)->dimid]), !commit);
+		} else {
+			stat = rule_elem_insert((struct dt_rule_elem_spec **)
+						&top, rule, !commit);
+		}
+	} else {
+		/* rule has at least one native match */
+		if (top == NULL) {
+			stat = dimtree_insrec_null((struct rlp_spec **) &top,
+						   rule, 0, !commit);
+		} else if (IS_RLP(top)) {
+			stat = dimtree_insrec((struct rlp_spec **) &top,
+					      rule, 0, !commit);
+		} else {
+			/* construct termrule block containing all
+			   non TARGET_DUMMY rules except the inserted rule
+			   from dt->chain */
+			struct ptrblock *term_prop = NULL;
+			struct list_head *lh;
+			struct dt_rule *r;
+			
+			stat = HE_OK;
+			list_for_each (lh, &dt->chain->head) {
+				r = list_entry(lh, struct dt_rule, head);
+				if (r->spec.action == TARGET_DUMMY ||
+				    r == rule || r->deleted) {
+					continue;
+				}
+				assert(r->dt_match_len == 0);
+				stat = termrule_insert(&term_prop, r);
+				if (stat < 0) {
+					if (term_prop != NULL) {
+						ptrblock_free(term_prop);
+					}
+					break;
+				}
+			}
+			if (stat == HE_OK) {
+				stat = dimtree_insrec_rule_elem(
+					(struct dt_rule_elem_spec **) &top,
+					rule, 0, term_prop, !commit);
+			}
+		}
+	}
+	if (stat < 0) {
+		history_undo();
+		dt->top_new = NULL;
+		return stat;
+	}
+	if (commit) {
+#ifdef DEBUG
+		if (rule_occur(dt->top, rule, 1)) {
+			DPRINT(DEBUG_DIMTREE, "rule present in original"
+			       "structure\n");
+			return HE_IMPOSSIBLE_CONDITION;
+		}
+#endif
+		dt->top = top;
+		dt->top_new = NULL;
+		synchronize_rcu();
+		history_commit(0);
+		assert(history_is_empty());
+	} else {
+		assert((IS_RULE(top) && IS_RULE(dt->top)) ||
+		       !history_is_empty());
+		dt->need_commit = 1;
+		dt->top_new = top;
+	}
+	return HE_OK;
+}
+
+#ifdef DEBUG
+void
+dt_rule_print(const struct dt_rule *rule);
+#endif
+
+hipac_error
+dimtree_insert(struct dimtree *dt, struct dt_rule *rule, __u32 origin,
+	       int inc, int commit)
+{
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: origin: %X, inc: %d, commit: %d\n",
+	       __FUNCTION__, origin, inc, commit);
+	DPRINT(DEBUG_DIMTREE, "dt: origin: %X, need_commit: %u,"
+	       " chain: %s (len: %u)\n", dt->origin, dt->need_commit,
+	       dt->chain->name, dt->chain->len);
+#ifdef DEBUG
+	if (dt->top_new == NULL) {
+		if (dt->top != NULL) {
+			if (IS_RLP(dt->top)) {
+				print_rlp((struct rlp_spec *) dt->top);
+			} else if (IS_ELEM(dt->top)) {
+				print_elem((struct dt_elem *) dt->top);
+				DPRINT(DEBUG_DIMTREE, "\n");
+			} else {
+				DPRINT(DEBUG_DIMTREE, "top level rule: %p\n",
+				       dt->top);
+			}
+		}
+	} else {
+		if (IS_RLP(dt->top_new)) {
+			print_rlp((struct rlp_spec *) dt->top_new);
+		} else if (IS_ELEM(dt->top_new)) {
+				print_elem((struct dt_elem *) dt->top_new);
+				DPRINT(DEBUG_DIMTREE, "\n");
+		} else {
+			DPRINT(DEBUG_DIMTREE, "top level rule: %p\n",
+			       dt->top_new);
+		}
+	}
+	if (hipac_debug & DEBUG_DIMTREE) {
+		dt_rule_print(rule);
+	}
+#endif
+	return dimtree_insert_intern(dt, rule, origin, inc, 1, commit);
+}
+
+static struct dt_rule *
+dimtree_delete_find_best_term(struct dimtree *dt,
+			      const struct dt_rule *term_rule, __u32 *ntm_num)
+{
+	struct list_head *lh;
+	struct dt_rule *cr;
+	
+	if (unlikely(dt == NULL || term_rule == NULL || ntm_num == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+
+	*ntm_num = 0;
+	for (lh = term_rule->head.next; lh != &dt->chain->head;
+	     lh = lh->next) {
+		cr = list_entry(lh, struct dt_rule, head);
+		if (cr->deleted) {
+			continue;
+		}
+		if (IS_RULE_TERM(cr)) {
+			return cr;
+		} else if (cr->spec.action != TARGET_DUMMY) {
+			(*ntm_num)++;
+		}
+	}
+	return NULL;
+}
+
+/* from and to are exclusive */
+static hipac_error
+dimtree_delete_insert_ntm(struct dimtree *dt, struct dt_elem **e,
+			  const struct dt_rule *from, const struct dt_rule *to)
+{
+	struct list_head *lh;
+	struct dt_rule *cr;
+	int stat;
+	
+	if (unlikely(dt == NULL || e == NULL || *e == NULL || to == NULL)) {
+		ARG_ERR;
+	}
+
+	for (lh = (from == NULL ? dt->chain->head.next : from->head.next);
+	     lh != &to->head; lh = lh->next) {
+		cr = list_entry(lh, struct dt_rule, head);
+		if (cr->deleted || cr->spec.action == TARGET_DUMMY) {
+			continue;
+		}
+		assert(cr->spec.pos < to->spec.pos);
+		assert(!IS_RULE_TERM(cr));
+		stat = ptrblock_insert_embed((void **) e,
+					     offsetof(struct dt_elem,
+						      ntm_rules), cr,
+					     (*e)->ntm_rules.len);
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	return HE_OK;
+}
+
+static hipac_error
+dimtree_delete_rule_elem(struct dt_rule_elem_spec **rule_elem,
+			 const struct dt_rule *rule, struct dimtree *dt,
+			 int newspec_set)
+{
+	struct dt_elem *e;
+	int stat;
+	__u32 i;
+	
+	if (IS_RULE(*rule_elem)) {
+		struct dt_rule *r = (struct dt_rule *) *rule_elem;
+		struct dt_rule *term_rule;
+		__u32 ntm_num;
+		
+		if (r != rule) {
+			/* deleted rule must have a higher position than r */
+			return HE_OK;
+		}
+		term_rule = dimtree_delete_find_best_term(dt, rule, &ntm_num);
+		if (term_rule == NULL) {
+			IMPOSSIBLE_CONDITION("attempt to delete the only "
+					     "terminal rule");
+		}
+		if (ntm_num == 0) {
+			*rule_elem = (struct dt_rule_elem_spec *) term_rule;
+			return HE_OK;
+		} else {
+			struct dt_elem *e = elem_new_empty(term_rule);
+			if (e == NULL) {
+				return HE_LOW_MEMORY;
+			}
+			stat = dimtree_delete_insert_ntm(dt, &e, rule,
+							 term_rule);
+			if (stat < 0) {
+				elem_free(e);
+				return stat;
+			}
+			assert(e->ntm_rules.len > 0);
+			stat = history_new((struct gen_spec *) e, newspec_set);
+			if (stat < 0) {
+				elem_free(e);
+				return stat;
+			}
+			*rule_elem = (struct dt_rule_elem_spec *) e;
+			return HE_OK;
+		}
+	}
+
+	assert(IS_ELEM(*rule_elem));
+	e = (struct dt_elem *) *rule_elem;
+	assert(e->term_rule != NULL);
+	if (IS_RULE_TERM(rule)) {
+		struct dt_rule *term_rule;
+		__u32 ntm_num;
+
+		if (e->term_rule != rule) {
+			/* deleted rule must have a higher position than
+			   e->term_rule */
+			assert(rule->spec.pos > e->term_rule->spec.pos);
+			return HE_OK;
+		}
+		term_rule = dimtree_delete_find_best_term(dt, rule, &ntm_num);
+		if (term_rule == NULL) {
+			IMPOSSIBLE_CONDITION("attempt to delete the only "
+					     "terminal rule");
+		}
+		stat = dimtree_delete_insert_ntm(
+			 dt, (struct dt_elem **) rule_elem, rule, term_rule);
+		if (stat < 0) {
+			/* we only care about rule_elem if its address has
+			   changed; otherwise rule_elem is handled by the
+			   history */
+			if (e != (struct dt_elem *) *rule_elem) {
+				history_del_invalid((struct gen_spec *) e);
+				elem_free((struct dt_elem *) *rule_elem);
+			}
+			return stat;
+		}
+		if (e != (struct dt_elem *) *rule_elem) {
+			history_del_invalid((struct gen_spec *) e);
+			stat = history_new((struct gen_spec *)
+					   *rule_elem, newspec_set);
+			if (stat < 0) {
+				elem_free((struct dt_elem *) *rule_elem);
+				return stat;
+			}
+		}
+		(*(struct dt_elem **) rule_elem)->term_rule = term_rule;
+		return HE_OK;
+	} else {
+		for (i = 0; i < e->ntm_rules.len &&
+			     ((struct dt_rule *)
+			      e->ntm_rules.p[i])->spec.pos <
+			     rule->spec.pos; i++);
+		if (i >= e->ntm_rules.len || e->ntm_rules.p[i] != rule) {
+			/* deleted rule must have a higher position than
+			   e->ntm_rules.p[e->ntm_rules.len - 1] */
+			return HE_OK;
+		}
+		if (e->ntm_rules.len == 1) {
+			struct dt_rule_elem_spec *tm =
+				(struct dt_rule_elem_spec *)
+				e->term_rule;
+			stat = history_obsolete((struct gen_spec *) e,
+						newspec_set);
+			if (stat < 0) {
+				return stat;
+			}
+			*rule_elem = tm;
+			return HE_OK;
+		} else {
+			stat = ptrblock_delete_pos_embed(
+				 (void **) rule_elem,
+				 offsetof(struct dt_elem, ntm_rules),
+				 i);
+			if (stat < 0) {
+				/* we only care about rule_elem if its address
+				   has changed; otherwise rule_elem is 
+				   handled by the history */
+				if (e != (struct dt_elem *) *rule_elem) {
+					history_del_invalid((struct gen_spec *)
+							    e);
+					elem_free((struct dt_elem *)
+						  *rule_elem);
+				}
+				return stat;
+			}
+			if (e != (struct dt_elem *) *rule_elem) {
+				history_del_invalid((struct gen_spec *) e);
+				stat = history_new((struct gen_spec *)
+						   *rule_elem, newspec_set);
+				if (stat < 0) {
+					elem_free((struct dt_elem *)
+						  *rule_elem);
+					return stat;
+				}
+			}
+			return HE_OK;
+		}
+	}
+}
+
+hipac_error
+dimtree_delete(struct dimtree *dt, struct dt_rule *rule, int commit)
+{
+	struct gen_spec *top;
+	int stat;
+
+	if (unlikely(dt == NULL || rule == NULL || rule->deleted ||
+		     rule == list_entry(dt->chain->head.next,
+					struct dt_rule, head) ||
+		     rule == list_entry(dt->chain->head.prev,
+					struct dt_rule, head))) {
+		ARG_ERR;
+	}
+
+	assert(dt->top != NULL);
+	DPRINT(DEBUG_DIMTREE,
+	       "----------------------------------------------------------\n");
+	DPRINT(DEBUG_DIMTREE, "%s: commit: %d\n", __FUNCTION__, commit);
+	DPRINT(DEBUG_DIMTREE, "dt: origin: %X, need_commit: %u,"
+	       " chain: %s (len: %u)\n", dt->origin, dt->need_commit,
+	       dt->chain->name, dt->chain->len);
+#ifdef DEBUG
+	if (dt->top_new == NULL) {
+		if (dt->top != NULL) {
+			if (IS_RLP(dt->top)) {
+				print_rlp((struct rlp_spec *) dt->top);
+			} else if (IS_ELEM(dt->top)) {
+				print_elem((struct dt_elem *) dt->top);
+				DPRINT(DEBUG_DIMTREE, "\n");
+			} else {
+				DPRINT(DEBUG_DIMTREE, "top level rule: %p\n",
+				       dt->top);
+			}
+		}
+	} else {
+		if (IS_RLP(dt->top_new)) {
+			print_rlp((struct rlp_spec *) dt->top_new);
+		} else if (IS_ELEM(dt->top_new)) {
+				print_elem((struct dt_elem *) dt->top_new);
+				DPRINT(DEBUG_DIMTREE, "\n");
+		} else {
+			DPRINT(DEBUG_DIMTREE, "top level rule: %p\n",
+			       dt->top_new);
+		}
+	}
+	if (hipac_debug & DEBUG_DIMTREE) {
+		dt_rule_print(rule);
+	}
+#endif
+
+	if (!dt->need_commit) {
+		/* first operation in a series => clone top level structure
+		   if necessary */
+		if (IS_RLP(dt->top)) {
+			stat = rlp_clone((struct rlp_spec *) dt->top,
+					 (struct rlp_spec **) &top);
+			if (stat < 0) {
+				return stat;
+			}
+			stat = history_replace(dt->top, top, !commit);
+			if (stat < 0) {
+				rlp_free((struct rlp_spec *) top);
+				history_undo();
+				return stat;
+			}
+		} else if (IS_ELEM(dt->top)) {
+			stat = elem_clone((struct dt_elem *) dt->top,
+					  (struct dt_elem **) &top);
+			if (stat < 0) {
+				return stat;
+			}
+			stat = history_replace(dt->top, top, !commit);
+			if (stat < 0) {
+				elem_free((struct dt_elem *) top);
+				history_undo();
+				return stat;
+			}
+		} else {
+			assert(IS_RULE(dt->top));
+			top = dt->top;
+		}
+	} else {
+		top = dt->top_new;
+	}
+
+	/* delete rule from rlp / elementary interval */
+	if (IS_RLP(top)) {
+		stat = dimtree_delrec((struct rlp_spec **) &top, rule,
+				      0, NULL, !commit);
+	} else {
+		stat = dimtree_delete_rule_elem((struct dt_rule_elem_spec **)
+						&top, rule, dt, !commit);
+	}
+	if (stat < 0) {
+		history_undo();
+		return stat;
+	}
+	
+	if (commit) {
+#ifdef DEBUG
+		if (dt->top != NULL && IS_RLP(dt->top) &&
+		    !rule_occur(dt->top, rule, 0)) {
+			/* this check only works if the top level structure is
+			   a rlp */
+			DPRINT(DEBUG_DIMTREE, "rule %p not present in "
+			       "original rlp\n", rule);
+			return HE_IMPOSSIBLE_CONDITION;
+		}
+#endif
+		dt->top = top;
+		dt->top_new = NULL;
+		synchronize_rcu();
+		history_commit(0);
+		assert(history_is_empty());
+	} else {
+		assert((IS_RULE(top) && IS_RULE(dt->top)) ||
+		       !history_is_empty());
+		dt->need_commit = 1;
+		dt->top_new = top;
+		rule->deleted = 1;
+	}
+	return HE_OK;
+}
+
+void
+dimtree_commit(struct ptrblock *dt_block)
+{
+	struct dimtree *dt;
+	__u32 i;
+	
+	if (unlikely(dt_block == NULL)) {
+		ARG_MSG;
+		return;
+	}
+
+	for (i = 0; i < dt_block->len; i++) {
+		dt = (struct dimtree *) dt_block->p[i];
+		if (dt->need_commit) {
+			dt->top = dt->top_new;
+			dt->top_new = NULL;
+			dt->need_commit = 0;
+		}
+	}
+	synchronize_rcu();
+	history_commit(1);
+	assert(history_is_empty());
+}
+
+void
+dimtree_failed(struct ptrblock *dt_block)
+{
+	struct list_head *lh;
+	struct dimtree *dt;
+	__u32 i;
+	
+	if (unlikely(dt_block == NULL)) {
+		ARG_MSG;
+		return;
+	}
+
+	for (i = 0; i < dt_block->len; i++) {
+		dt = (struct dimtree *) dt_block->p[i];
+		if (dt->need_commit) {
+			dt->need_commit = 0;
+			dt->top_new = NULL;
+			list_for_each (lh, &dt->chain->head) {
+				list_entry(lh, struct dt_rule,
+					   head)->deleted = 0;
+			}
+		}
+		assert(dt->need_commit || dt->top_new == NULL);
+	}
+	history_undo();
+}
+
+void
+dimtree_chain_fix(struct ptrblock *dt_block)
+{
+	struct list_head *lh;
+	struct dt_rule *rule;
+	__u32 i, prevpos_new, prevpos_org;
+	struct dimtree *dt;
+	
+	if (unlikely(dt_block == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	
+	for (i = 0; i < dt_block->len; i++) {
+		dt = (struct dimtree *) dt_block->p[i];
+		assert(!list_empty(&dt->chain->head));
+		if (dt->chain->first == NULL) {
+			lh = dt->chain->head.next;
+			prevpos_org = list_entry(lh, struct dt_rule,
+						 head)->spec.pos;
+			prevpos_new = list_entry(lh, struct dt_rule,
+						 head)->spec.pos = 0;
+			lh = lh->next;
+		} else {
+			lh = dt->chain->first->head.next;
+			prevpos_org = prevpos_new = dt->chain->first->spec.pos;
+		}
+		dt->chain->first = list_entry(dt->chain->head.prev,
+					      struct dt_rule, head);
+		for (; lh != &dt->chain->head; lh = lh->next) {
+			rule = list_entry(lh, struct dt_rule, head);
+			if (unlikely(rule->spec.pos == prevpos_org)) {
+				rule->spec.pos = prevpos_new;
+			} else {
+				prevpos_org = rule->spec.pos;
+				rule->spec.pos = ++prevpos_new;
+			}
+		}
+	}
+}
+
+static hipac_error
+hipac_get_rlp_stat_rec(struct gen_spec *g, struct hipac_rlp_stat *stat,
+		       __u8 depth, __u8 parent_dimid)
+{
+	struct gen_spec **nextspec = NULL;
+	struct rlp_spec *rlp;
+	int ret;
+	__u16 n;
+
+	if (g == NULL) {
+		return HE_OK;
+	}
+	if (IS_RULE(g) || IS_ELEM(g)) {
+		if (depth < 1) {
+			return HE_OK;
+		}
+		stat->termptr_num++;
+		if (parent_dimid >= LEN(stat->termptr_dimid_num)) {
+			IMPOSSIBLE_CONDITION("termptr_dimid_num too small");
+		}
+		stat->termptr_dimid_num[parent_dimid]++;
+		if (depth - 1 >= LEN(stat->termptr_depth_num)) {
+			IMPOSSIBLE_CONDITION("termptr_depth_num too small");
+		}
+		stat->termptr_depth_num[depth - 1]++;
+		if (IS_ELEM(g)) {
+			struct dt_elem *e = (struct dt_elem *) g;
+			__u32 ptr_num;
+			stat->dt_elem_num++;
+			ptr_num = e->ntm_rules.len +
+				(e->term_rule == NULL ? 0 : 1);
+			stat->dt_elem_ptr_num += ptr_num;
+			stat_distribution_add(stat->dt_elem_stat,
+					      LEN(stat->dt_elem_stat),
+					      ptr_num);
+		}
+		return HE_OK;
+	}
+	
+	/* rlp statistics */
+	rlp = (struct rlp_spec *) g;
+	if (hp_size(rlp, &stat->rlp_mem_real, &stat->rlp_mem_tight) < 0) {
+		return HE_IMPOSSIBLE_CONDITION;
+	}
+	if (hp_size(*termrule(rlp), &stat->termrule_mem_real,
+		    &stat->termrule_mem_tight) < 0) {
+		return HE_IMPOSSIBLE_CONDITION;
+	}
+	stat->rlp_num++;
+	if (rlp->dimid >= LEN(stat->rlp_dimid_num)) {
+		IMPOSSIBLE_CONDITION("rlp_dimid_num too small");
+	}
+	stat->rlp_dimid_num[rlp->dimid]++;
+	if (depth >= LEN(stat->rlp_depth_num)) {
+		IMPOSSIBLE_CONDITION("rlp_depth_num too small");
+	}
+	stat->rlp_depth_num[depth]++;
+	if (*termrule(rlp) != NULL) {
+		stat->termrule_num++;
+		stat->termrule_ptr_num += (*termrule(rlp))->len;
+	}
+	stat->keys_num += rlp->num;
+       	if (rlp->dimid >= LEN(stat->rlp_dimid_keys_stat)) {
+		IMPOSSIBLE_CONDITION("rlp_dimid_keys_stat too small");
+	}
+	stat_distribution_add(stat->rlp_dimid_keys_stat[rlp->dimid],
+			      LEN(*stat->rlp_dimid_keys_stat), rlp->num);
+	if (depth > 0) {
+		stat->nontermptr_num++;
+		if (parent_dimid >= LEN(stat->nontermptr_dimid_num)) {
+			IMPOSSIBLE_CONDITION("nontermptr_dimid_num too small");
+		}
+		stat->nontermptr_dimid_num[parent_dimid]++;
+		if (depth - 1 >= LEN(stat->nontermptr_depth_num)) {
+			IMPOSSIBLE_CONDITION("nontermptr_depth_num too small");
+		}
+		stat->nontermptr_depth_num[depth - 1]++;
+	}
+
+	/* recursion */
+	nextspec = rlp_nextspec(rlp);
+	assert(nextspec != NULL);
+	
+	for (n = 0; n < rlp->num; n++) {
+		ret = hipac_get_rlp_stat_rec(*(nextspec + n), stat,
+					     depth + 1, rlp->dimid);
+		if (ret < 0) {
+			return ret;
+		}
+	}
+	if (HAS_WILDCARD_SPEC(rlp)) {
+		ret = hipac_get_rlp_stat_rec(*WILDCARD(rlp), stat,
+					     depth + 1, rlp->dimid);
+		if (ret < 0) {
+			return ret;
+		}
+	}
+	return HE_OK;
+}
+
+hipac_error
+hipac_get_rlp_stat(void *hipac, struct hipac_rlp_stat *stat)
+{
+	struct dimtree *dt = hipac;
+
+	if (dt == NULL || stat == NULL) {
+		ARG_ERR;
+	}
+
+	memset(stat, 0, sizeof(*stat));
+	stat->total_mem_tight = mem_current_tight;
+	stat->total_mem_real = mem_current_real;
+	if (dt->top == NULL) {
+		IMPOSSIBLE_CONDITION("top level rlp NULL");
+	}
+	return hipac_get_rlp_stat_rec(dt->top, stat, 0, 0);
+}
+
+hipac_error
+hipac_get_dimtree_stat(void *hipac, struct hipac_dimtree_stat *stat)
+{
+	struct dimtree *dt = hipac;
+	struct list_head *lh;
+	struct dt_rule *r;
+	__u32 pos, num;
+
+	if (dt == NULL || stat == NULL) {
+		ARG_ERR;
+	}
+
+	memset(stat, 0, sizeof(*stat));
+	if (hp_size(dt->chain, &stat->chain_mem_real,
+		    &stat->chain_mem_tight) < 0) {
+		return HE_IMPOSSIBLE_CONDITION;
+	}
+	stat->rule_num = dt->chain->len;
+	pos = num = 0;
+	list_for_each (lh, &dt->chain->head) {
+		r = list_entry(lh, struct dt_rule, head);
+		if (r->spec.pos == pos) {
+			num++;
+		} else {
+			if (num > 1) {
+				stat_distribution_add(
+				      stat->rules_same_pos_stat,
+				      LEN(stat->rules_same_pos_stat), num);
+			}
+			num = 1;
+			pos = r->spec.pos;
+		}
+		if (hp_size(r, &stat->chain_mem_real,
+			    &stat->chain_mem_tight) < 0) {
+			return HE_IMPOSSIBLE_CONDITION;
+		}
+		if (HAS_EXEC_MATCH(r)) {
+			stat->rules_with_exec_matches++;
+		}
+		if (IS_TARGET_EXEC(r)) {
+			stat->rules_with_exec_target++;
+		}
+		if (r->dt_match_len >= LEN(stat->dt_match_stat)) {
+			IMPOSSIBLE_CONDITION("dt_match_stat too small");
+		}
+		stat->dt_match_stat[r->dt_match_len]++;
+	}
+	if (num > 1) {
+		stat_distribution_add(stat->rules_same_pos_stat,
+				      LEN(stat->rules_same_pos_stat), num);
+	}
+	return HE_OK;
+}
+
+
+
+/*
+ * hipac matching algorithm
+ */
+
+
+#ifdef SINGLE_PATH
+
+/* match packet against the rlp in dt and return the terminal action
+   (TARGET_ACCEPT or TARGET_DROP) of the highest priority terminal rule or
+   the policy if there is no such rule */
+hipac_target_t
+hipac_match(void *hipac, const void *packet)
+{
+	struct dt_rule *rule;
+	struct gen_spec *t;
+	__u8 action, i, j;
+	int hotdrop = 0;
+
+	t = ((struct dimtree *) hipac)->top;
+	assert(t != NULL);
+	assert(packet != NULL);
+
+ 	while (!hotdrop && IS_RLP(t)) {
+		t = ((struct rlp_spec *) t)->locate((struct rlp_spec *) t,
+						    packet, &hotdrop);
+	}
+	if (hotdrop)
+		return TARGET_DROP;
+
+	if (likely(IS_RULE(t))) {
+		assert(IS_RULE_TERM((struct dt_rule *) t));
+		return ((struct dt_rule *) t)->spec.action;
+	}
+	
+	/* initialization required to prevent compiler warning */
+	action = 0;
+
+	assert(IS_ELEM(t));
+	assert(((struct dt_elem *) t)->term_rule != NULL);
+	assert(IS_RULE_TERM(((struct dt_elem *) t)->term_rule));
+	assert(((struct dt_elem *) t)->ntm_rules.p != NULL);
+	for (i = 0; i < ((struct dt_elem *) t)->ntm_rules.len; i++) {
+		rule = ((struct dt_elem *) t)->ntm_rules.p[i];
+		if (HAS_EXEC_MATCH(rule)) {
+			assert(!(rule->exec_match->len & 1));
+			assert(rule->exec_match->len >= 2);
+			for (j = 0; j < rule->exec_match->len; j += 2) {
+				action = match_fn(packet, 
+						  rule->exec_match->p[j],
+						  rule->exec_match->p[j + 1]);
+				if (action != MATCH_YES) {
+					break;
+				}
+			}
+			if (action == MATCH_NO) {
+				continue;
+			}
+			if (action == MATCH_HOTDROP) {
+				return TARGET_DROP;
+			}
+		}
+		action = IS_TARGET_EXEC(rule) ?
+			target_fn(packet, rule->exec_target) 
+			: rule->spec.action;
+		if (action != TARGET_NONE) {
+			assert(action == TARGET_ACCEPT ||
+			       action == TARGET_DROP);
+			return action;
+		}
+	}
+
+	/* terminal rule or policy matches */
+	return ((struct dt_elem *) t)->term_rule->spec.action;
+}
+
+#  ifdef DEBUG
+
+/*
+ * debugging version of hipac_match (single path)
+ */
+
+/* return the matched rules in order - for verification purposes only */
+struct ptrblock *
+hipac_match_debug(struct dimtree *hipac, const void *packet)
+{
+	struct ptrblock *b = NULL;
+	struct dt_rule *rule;
+	struct gen_spec *t;
+	__u8 action, i, j;
+	int hotdrop = 0;
+
+	t = ((struct dimtree *) hipac)->top;
+	assert(t != NULL);
+	assert(packet != NULL);
+
+	while (!hotdrop && IS_RLP(t)) {
+		t = ((struct rlp_spec *) t)->locate((struct rlp_spec *) t,
+						    packet, &hotdrop);
+	}
+	if (hotdrop)
+		return b;
+
+	if (likely(IS_RULE(t))) {
+		assert(IS_RULE_TERM((struct dt_rule *) t));
+		if (ptrblock_append(&b, t) < 0) {
+			ERR("ptrblock_append failed");
+		}
+		return b;
+	}
+	
+	/* initialization required to prevent compiler warning */
+	action = 0;
+
+	assert(IS_ELEM(t));
+	assert(((struct dt_elem *) t)->term_rule != NULL);
+	assert(IS_RULE_TERM(((struct dt_elem *) t)->term_rule));
+	assert(((struct dt_elem *) t)->ntm_rules.p != NULL);
+	for (i = 0; i < ((struct dt_elem *) t)->ntm_rules.len; i++) {
+		rule = ((struct dt_elem *) t)->ntm_rules.p[i];
+		if (HAS_EXEC_MATCH(rule)) {
+			assert(!(rule->exec_match->len & 1));
+			assert(rule->exec_match->len >= 2);
+			for (j = 0; j < rule->exec_match->len; j += 2) {
+				action = match_fn(packet, 
+						  rule->exec_match->p[j],
+						  rule->exec_match->p[j + 1]);
+				if (action != MATCH_YES) {
+					break;
+				}
+			}
+			if (action == MATCH_NO) {
+				continue;
+			}
+			if (action == MATCH_HOTDROP) {
+				return b;
+			}
+		}
+		if (ptrblock_append(&b, rule) < 0) {
+			ERR("ptrblock_append failed");
+			return b;
+		}
+		action = IS_TARGET_EXEC(rule) ?
+			target_fn(packet, rule->exec_target) 
+			: rule->spec.action;
+		if (action != TARGET_NONE){
+			assert(action == TARGET_ACCEPT ||
+			       action == TARGET_DROP);
+			return b;
+		}
+	}
+
+	/* terminal rule or policy matches */
+	if (ptrblock_append(&b, ((struct dt_elem *) t)->term_rule) < 0) {
+		ERR("ptrblock_append failed");
+	}
+	return b;
+}
+
+#  endif   // DEBUG
+
+#else      // SINGLE_PATH
+
+static inline hipac_target_t
+match_packet(const struct dimtree *dt, const void *packet,
+	     struct dt_rule *rule)
+{
+	__u32 i;
+
+	if (HAS_EXEC_MATCH(rule)) {
+		assert(!(rule->exec_match->len & 1));
+		assert(rule->exec_match->len >= 2);
+		for (i = 0; i < rule->exec_match->len; i += 2) {
+			switch (match_fn(packet, rule->exec_match->p[i],
+					 rule->exec_match->p[i + 1])) {
+			    case MATCH_YES:
+				    break;
+
+			    case MATCH_NO:
+				    return TARGET_NONE;
+
+			    case MATCH_HOTDROP: 
+				    return TARGET_DROP;
+			}
+		}
+	}
+	return IS_TARGET_EXEC(rule) ?
+		target_fn(packet, rule->exec_target) : rule->spec.action;
+}
+
+
+/* match packet against the rlp in dt and return the terminal action
+   (TARGET_ACCEPT or TARGET_DROP) of the highest priority terminal rule or
+   the policy if there is no such rule */
+hipac_target_t
+hipac_match(void *hipac, const void *packet)
+{
+#       define NUM_LEAVES 4
+	/* UINT_MAX - 1 is required because of
+	   if (likely(term_pos < nonterm_pos)) {...} optimization */
+	__u32 term_pos = UINT_MAX - 1;
+	__u32 nonterm_pos = UINT_MAX;
+	struct dt_rule *term_rule = NULL;
+	struct dt_rule_elem_spec *rule_elem[NUM_LEAVES];
+	struct dt_rule **ntm_rule[NUM_LEAVES];
+	struct dt_rule **ntm_end[NUM_LEAVES];
+	struct gen_spec *t;
+	__u32 ntm_next_pos, new_next;
+	__u8 ntm_rule_sz, ntm_cur_ind;
+	__u8 action, i, len, max;
+	int hotdrop = 0;
+	
+	max = 1;
+	i = len = 0;
+	rule_elem[0] = (struct dt_rule_elem_spec *) 
+		((struct dimtree *) hipac)->top;
+	assert(packet != NULL);
+	assert(rule_elem[0] != NULL);
+	assert(!IS_RULE(rule_elem[0]) ||
+	       IS_RULE_TERM(((struct dt_rule *) rule_elem[0])));
+	assert(!IS_ELEM(rule_elem[0]) ||
+	       (IS_RULE_TERM(((struct dt_elem *) rule_elem[0])->term_rule) &&
+		((struct dt_elem *) rule_elem[0])->ntm_rules.len > 0));
+	
+	do {
+		t = (struct gen_spec *) rule_elem[i++];
+		while (!hotdrop && t && IS_RLP(t)) {
+			t = ((struct rlp_spec *) t)->locate(
+				(struct rlp_spec *) t, packet, &hotdrop,
+				(struct gen_spec **) rule_elem, &max);
+		};
+		if (hotdrop)
+			return TARGET_DROP;
+		assert(max <= NUM_LEAVES);
+		if (unlikely(t == NULL)) {
+			continue;
+		}
+		rule_elem[len++] = (struct dt_rule_elem_spec *) t;
+		if (likely(IS_RULE(t))) {
+			if (likely(IS_RULE_TERM((struct dt_rule *) t))) {
+				if (((struct dt_rule *) t)->spec.pos <
+				    term_pos) {
+					term_rule = (struct dt_rule *) t;
+					term_pos = term_rule->spec.pos;
+				}
+			} else if (((struct dt_rule *) t)->spec.pos <
+				   nonterm_pos) {
+				nonterm_pos = ((struct dt_rule *)
+					       t)->spec.pos;
+			}
+		} else {
+			if (((struct dt_elem *) t)->term_rule != NULL &&
+			    ((struct dt_elem *) t)->term_rule->spec.pos <
+			    term_pos) {
+				term_rule = ((struct dt_elem *)
+					     t)->term_rule;
+				term_pos = term_rule->spec.pos;
+				assert(IS_RULE_TERM(term_rule));
+			}
+			assert(((struct dt_elem *) t)->ntm_rules.len > 0);
+			if (((struct dt_rule *)
+			     ((struct dt_elem *) t)->ntm_rules.p[0])->spec.pos
+			    < nonterm_pos) {
+				nonterm_pos = ((struct dt_rule *)
+					       ((struct dt_elem *)
+						t)->ntm_rules.p[0])->spec.pos;
+			}
+		}
+	} while (i < max);
+		
+	/* optimization for the ideal case that no non-terminal rules
+	   (function based matches or no terminal target) exist */
+	if (likely(term_pos < nonterm_pos)) {
+		assert(term_rule != NULL);
+		action = term_rule->spec.action;
+		return action;
+	}
+
+	/* initialize ntm_rule, ntm_end, ntm_rule_sz, ntm_cur_ind and
+	   ntm_next_pos now that term_pos is given */
+	ntm_rule_sz = ntm_cur_ind = 0;
+	ntm_next_pos = UINT_MAX;
+	for (i = 0; i < len; i++) {
+		assert(rule_elem[i] != NULL);
+		if (likely(IS_RULE(rule_elem[i]))) {
+			struct dt_rule **r = (struct dt_rule **) &rule_elem[i];
+			__u32 pos = (*r)->spec.pos;
+			if (!IS_RULE_TERM(*r) && pos < term_pos) {
+				if (pos == nonterm_pos) {
+					ntm_cur_ind = ntm_rule_sz;
+				} else if (pos < ntm_next_pos) {
+					ntm_next_pos = pos;
+				}
+				ntm_rule[ntm_rule_sz] = r;
+				ntm_end[ntm_rule_sz++] = r;
+			}
+		} else {
+			struct dt_elem *e = (struct dt_elem *) rule_elem[i];
+			__u32 pos = ((struct dt_rule *)
+				     *e->ntm_rules.p)->spec.pos;
+			if (pos < term_pos) {
+				if (pos == nonterm_pos) {
+					ntm_cur_ind = ntm_rule_sz;
+				} else if (pos < ntm_next_pos) {
+					ntm_next_pos = pos;
+				}
+				ntm_rule[ntm_rule_sz] =
+					(struct dt_rule **) e->ntm_rules.p;
+				ntm_end[ntm_rule_sz++] = (struct dt_rule **)
+					&e->ntm_rules.p[e->ntm_rules.len - 1];
+			}
+		}
+	}
+	assert(ntm_rule_sz > 0);
+	
+	/* process non-terminal rules in order up to term_pos */
+	ntm_next_pos = ntm_next_pos < term_pos ? ntm_next_pos : term_pos;
+	while (ntm_rule_sz > 0 &&
+	       (*ntm_rule[ntm_cur_ind])->spec.pos < ntm_next_pos) {
+		
+		/* match packet against current block of rules */
+		for (; (ntm_rule[ntm_cur_ind] <= ntm_end[ntm_cur_ind] &&
+			(*ntm_rule[ntm_cur_ind])->spec.pos < ntm_next_pos);
+		     ntm_rule[ntm_cur_ind]++) {
+
+			switch (action =
+				match_packet((struct dimtree *) hipac, packet,
+					     *ntm_rule[ntm_cur_ind])) {
+
+			    case TARGET_NONE:
+				    break;
+			    default:
+				    assert(action == TARGET_ACCEPT ||
+					   action == TARGET_DROP);
+				    return action;
+			}
+		}
+
+		/* remove current block of rules if no rule is left that may
+		   be matched */
+		if (ntm_rule[ntm_cur_ind] > ntm_end[ntm_cur_ind] ||
+		    (*ntm_rule[ntm_cur_ind])->spec.pos >= term_pos) {
+			ntm_rule_sz--;
+			assert(ntm_cur_ind <= ntm_rule_sz);
+			ntm_rule[ntm_cur_ind] = ntm_rule[ntm_rule_sz];
+			ntm_end[ntm_cur_ind] = ntm_end[ntm_rule_sz];
+		}
+
+		/* set ntm_cur_ind and ntm_next_pos for next run */
+		new_next = term_pos;
+		for (i = 0; i < ntm_rule_sz; i++) {
+			if ((*ntm_rule[i])->spec.pos == ntm_next_pos) {
+				ntm_cur_ind = i;
+			} else if ((*ntm_rule[i])->spec.pos < new_next) {
+				new_next = (*ntm_rule[i])->spec.pos;
+			}
+		}
+		ntm_next_pos = new_next;
+	}
+	
+	/* terminal rule or policy matches */
+	assert(term_rule != NULL);
+	action = term_rule->spec.action;
+	return action;
+}
+
+#  ifdef DEBUG
+
+/*
+ * debugging version of hipac_match (multi path)
+ */
+
+/* for verification purposes only */
+static inline hipac_target_t
+match_packet_debug(struct ptrblock **b, const struct dimtree *dt,
+		   const void *packet, struct dt_rule *rule)
+{
+	__u32 i;
+
+	if (HAS_EXEC_MATCH(rule)) {
+		assert(!(rule->exec_match->len & 1));
+		assert(rule->exec_match->len >= 2);
+		for (i = 0; i < rule->exec_match->len; i += 2) {
+			switch (match_fn(packet, rule->exec_match->p[i],
+					 rule->exec_match->p[i + 1])) {
+			    case MATCH_YES:
+				    break;
+
+			    case MATCH_NO:
+				    return TARGET_NONE;
+
+			    case MATCH_HOTDROP: 
+				    return TARGET_DROP;
+			}
+		}
+	}
+	if (ptrblock_append(b, rule) < 0) {
+		ERR("ptrblock_append failed");
+	}
+	return IS_TARGET_EXEC(rule) ?
+		target_fn(packet, rule->exec_target) : rule->spec.action;
+}
+
+/* return the matched rules in order - for verification purposes only */
+struct ptrblock *
+hipac_match_debug(struct dimtree *hipac, const void *packet)
+{
+#       define NUM_LEAVES 4
+	struct ptrblock *b = NULL;
+	/* UINT_MAX - 1 is required because of
+	   if (likely(term_pos < nonterm_pos)) {...} optimization */
+	__u32 term_pos = UINT_MAX - 1;
+	__u32 nonterm_pos = UINT_MAX;
+	struct dt_rule *term_rule = NULL;
+	struct dt_rule_elem_spec *rule_elem[NUM_LEAVES];
+	struct dt_rule **ntm_rule[NUM_LEAVES];
+	struct dt_rule **ntm_end[NUM_LEAVES];
+	struct gen_spec *t;
+	__u32 ntm_next_pos, new_next;
+	__u8 ntm_rule_sz, ntm_cur_ind;
+	__u8 action, i, len, max;
+	int hotdrop = 0;
+
+	max = 1;
+	i = len = 0;
+	rule_elem[0] = (struct dt_rule_elem_spec *) 
+		((struct dimtree *) hipac)->top;
+	assert(packet != NULL);
+	assert(rule_elem[0] != NULL);
+	assert(!IS_RULE(rule_elem[0]) ||
+	       IS_RULE_TERM(((struct dt_rule *) rule_elem[0])));
+	assert(!IS_ELEM(rule_elem[0]) ||
+	       (IS_RULE_TERM(((struct dt_elem *) rule_elem[0])->term_rule) &&
+		((struct dt_elem *) rule_elem[0])->ntm_rules.len > 0));
+ 
+       	do {
+		t = (struct gen_spec *) rule_elem[i++];
+		while (!hotdrop && t && IS_RLP(t)) {
+			t = ((struct rlp_spec *) t)->locate(
+				(struct rlp_spec *) t, packet, &hotdrop,
+				(struct gen_spec **) rule_elem, &max);
+		};
+		if (hotdrop)
+			return b;
+		assert(max <= NUM_LEAVES);
+		if (unlikely(t == NULL)) {
+			continue;
+		}
+		rule_elem[len++] = (struct dt_rule_elem_spec *) t;
+		if (likely(IS_RULE(t))) {
+			if (likely(IS_RULE_TERM((struct dt_rule *) t))) {
+				if (((struct dt_rule *) t)->spec.pos <
+				    term_pos) {
+					term_rule = (struct dt_rule *) t;
+					term_pos = term_rule->spec.pos;
+				}
+			} else if (((struct dt_rule *) t)->spec.pos <
+				   nonterm_pos) {
+				nonterm_pos = ((struct dt_rule *)
+					       t)->spec.pos;
+			}
+		} else {
+			if (((struct dt_elem *) t)->term_rule != NULL &&
+			    ((struct dt_elem *) t)->term_rule->spec.pos <
+			    term_pos) {
+				term_rule = ((struct dt_elem *)
+					     t)->term_rule;
+				term_pos = term_rule->spec.pos;
+				assert(IS_RULE_TERM(term_rule));
+			}
+			assert(((struct dt_elem *) t)->ntm_rules.len > 0);
+			if (((struct dt_rule *)
+			     ((struct dt_elem *) t)->ntm_rules.p[0])->spec.pos
+			    < nonterm_pos) {
+				nonterm_pos = ((struct dt_rule *)
+					       ((struct dt_elem *)
+						t)->ntm_rules.p[0])->spec.pos;
+			}
+		}
+	} while (i < max);
+		
+	/* optimization for the ideal case that no non-terminal rules
+	   (function based matches or no terminal target) exist */
+	if (likely(term_pos < nonterm_pos)) {
+		assert(term_rule != NULL);
+		if (ptrblock_append(&b, term_rule) < 0) {
+			ERR("ptrblock_append failed");
+		}
+		return b;
+	}
+
+	/* initialize ntm_rule, ntm_end, ntm_rule_sz, ntm_cur_ind and
+	   ntm_next_pos now that term_pos is given */
+	ntm_rule_sz = ntm_cur_ind = 0;
+	ntm_next_pos = UINT_MAX;
+	for (i = 0; i < len; i++) {
+		assert(rule_elem[i] != NULL);
+		if (likely(IS_RULE(rule_elem[i]))) {
+			struct dt_rule **r = (struct dt_rule **) &rule_elem[i];
+			__u32 pos = (*r)->spec.pos;
+			if (!IS_RULE_TERM(*r) && pos < term_pos) {
+				if (pos == nonterm_pos) {
+					ntm_cur_ind = ntm_rule_sz;
+				} else if (pos < ntm_next_pos) {
+					ntm_next_pos = pos;
+				}
+				ntm_rule[ntm_rule_sz] = r;
+				ntm_end[ntm_rule_sz++] = r;
+			}
+		} else {
+			struct dt_elem *e = (struct dt_elem *) rule_elem[i];
+			__u32 pos = ((struct dt_rule *)
+				     *e->ntm_rules.p)->spec.pos;
+			if (pos < term_pos) {
+				if (pos == nonterm_pos) {
+					ntm_cur_ind = ntm_rule_sz;
+				} else if (pos < ntm_next_pos) {
+					ntm_next_pos = pos;
+				}
+				ntm_rule[ntm_rule_sz] =
+					(struct dt_rule **) e->ntm_rules.p;
+				ntm_end[ntm_rule_sz++] = (struct dt_rule **)
+					&e->ntm_rules.p[e->ntm_rules.len - 1];
+			}
+		}
+	}
+	assert(ntm_rule_sz > 0);
+	
+	/* process non-terminal rules in order up to term_pos */
+	ntm_next_pos = ntm_next_pos < term_pos ? ntm_next_pos : term_pos;
+	while (ntm_rule_sz > 0 &&
+	       (*ntm_rule[ntm_cur_ind])->spec.pos < ntm_next_pos) {
+		
+		/* match packet against current block of rules */
+		for (; (ntm_rule[ntm_cur_ind] <= ntm_end[ntm_cur_ind] &&
+			(*ntm_rule[ntm_cur_ind])->spec.pos < ntm_next_pos);
+		     ntm_rule[ntm_cur_ind]++) {
+
+			switch (action =
+				match_packet_debug(&b,
+						   (struct dimtree *) hipac,
+						   packet, 
+						   *ntm_rule[ntm_cur_ind])) {
+
+			    case TARGET_NONE:
+				    break;
+			    default:
+				    assert(action == TARGET_ACCEPT ||
+					   action == TARGET_DROP);
+				    return b;
+			}
+		}
+
+		/* remove current block of rules if no rule is left that may
+		   be matched */
+		if (ntm_rule[ntm_cur_ind] > ntm_end[ntm_cur_ind] ||
+		    (*ntm_rule[ntm_cur_ind])->spec.pos >= term_pos) {
+			ntm_rule_sz--;
+			assert(ntm_cur_ind <= ntm_rule_sz);
+			ntm_rule[ntm_cur_ind] = ntm_rule[ntm_rule_sz];
+			ntm_end[ntm_cur_ind] = ntm_end[ntm_rule_sz];
+		}
+
+		/* set ntm_cur_ind and ntm_next_pos for next run */
+		new_next = term_pos;
+		for (i = 0; i < ntm_rule_sz; i++) {
+			if ((*ntm_rule[i])->spec.pos == ntm_next_pos) {
+				ntm_cur_ind = i;
+			} else if ((*ntm_rule[i])->spec.pos < new_next) {
+				new_next = (*ntm_rule[i])->spec.pos;
+			}
+		}
+		ntm_next_pos = new_next;
+	}
+
+	/* terminal rule or policy matches */
+	assert(term_rule != NULL);
+	if (ptrblock_append(&b, term_rule) < 0) {
+		ERR("ptrblock_append failed");
+	}
+	return b;
+}
+
+#  endif  // DEBUG
+
+#endif    // SINGLE_PATH
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/dimtree.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/dimtree.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/dimtree.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/dimtree.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,280 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _DIMTREE_H
+#define _DIMTREE_H
+
+#include "global.h"
+#include "rlp.h"
+
+/* upper bound for matches of the given bit type */
+#define MAXKEY(bittype) \
+((bittype) == BIT_U16 ? 0xffff : 0xffffffff)
+
+/* used to distinguish a rule from an elementary interval */
+#define RT_RULE 0
+#define RT_ELEM 1
+
+
+/* header for dimtree rules and elementary intervals */
+struct dt_rule_elem_spec
+{
+	unsigned rlp    : 1; // must be 0	
+	unsigned rtype  : 1; // {RT_RULE, RT_ELEM}
+};
+
+/* header for dimtree rules */
+struct dt_rule_spec
+{
+	unsigned rlp    :  1; // must be 0	
+	unsigned rtype  :  1; // must be RT_RULE
+	unsigned action :  4; // packet action
+	unsigned pos    : 26; // position of the rule in the chain
+};
+
+/* dt_match represents the native interval match [left, right] associated
+   with dimension dimid whereby [left, right] may not be a wildcard match */
+struct dt_match
+{
+        __u8 dimid;
+        __u32 left, right;
+	char next_match[0];
+};
+
+/* dt_rule is an entry in the dt_chain; at the end of the struct we have
+   dt_match_len >= 0 dt_matches
+   if the rule has a function based target then exec_target points to the
+   target's data which is handled by target_fn;
+   the rule's exec_match pointer block references >= 0 blocks each of >= 1
+   function based matches, called fblocks;
+   the (2 * i)-th pointer of exec_match points to the beginning of the i-th
+   fblock;
+   the (2 * i + 1)-th pointer of exec_match points to the end of the i-th
+   fblock;
+   the start and end pointers are handed to match_fn */
+struct dt_rule
+{
+        struct dt_rule_spec spec;
+	struct list_head head;
+	struct ptrblock *exec_match;
+	void *exec_target;
+	__u32 exec_target_size;
+	__u8 deleted;
+	__u8 dt_match_len;
+	struct dt_match first_dt_match[0];
+};
+
+#define IS_RULE(r) (!IS_RLP(r) &&                                         \
+		    ((struct dt_rule_elem_spec *) (r))->rtype == RT_RULE)
+#define HAS_EXEC_MATCH(r)  ((r)->exec_match != NULL)
+#define IS_TARGET_DUMMY(r) ((r)->spec.action == TARGET_DUMMY)
+#define IS_TARGET_NONE(r)  ((r)->spec.action == TARGET_NONE)
+#define IS_TARGET_EXEC(r)  ((r)->spec.action == TARGET_EXEC)
+#define IS_TARGET_TERM(r)  ((r)->spec.action == TARGET_ACCEPT || \
+			    (r)->spec.action == TARGET_DROP)
+#define IS_RULE_TERM(r)    (IS_TARGET_TERM(r) && !HAS_EXEC_MATCH(r))
+
+/* return the size of a dt_rule with dt_match_len dt_matches */
+static inline __u32
+dt_rule_size(__u8 dt_match_len)
+{
+	return (sizeof(struct dt_rule) + 
+		dt_match_len * sizeof(struct dt_match));
+}
+
+/* head of the list of rules */
+struct dt_chain
+{
+	struct list_head head;
+	char name[HIPAC_CHAIN_NAME_MAX_LEN];
+	struct dt_rule *first; // optimization of dimtree_chain_fix
+	__u32 len;
+};
+
+
+
+/* header for elementary intervals */
+struct dt_elem_spec
+{
+	unsigned rlp     : 1; // must be 0
+	unsigned rtype   : 1; // must be RT_ELEM
+	unsigned newspec : 1; // indicates whether the elementary interval is
+	                      // contained in newspec
+};
+
+/* elementary interval */
+struct dt_elem
+{
+	struct dt_elem_spec spec;
+	/* terminating target (TARGET_ACCEPT, TARGET_DROP) without function
+	   based matches */
+	struct dt_rule *term_rule;
+	/* block of non-terminating rules (function based matches or no
+	   terminal target) whose position is < term_rule->spec.pos */
+	struct ptrblock ntm_rules;
+};
+
+#define IS_ELEM(e) (!IS_RLP(e) &&                                         \
+		    ((struct dt_rule_elem_spec *) (e))->rtype == RT_ELEM)
+
+
+
+struct dimtree
+{
+	__u32 origin;
+        struct gen_spec *top;
+	struct gen_spec *top_new;    // new not yet active top level structure
+	int need_commit;             // 1 if top_new is valid
+        struct dt_chain *chain;
+};
+
+
+
+/* create new dimtree and store it in *newdt; chain_name is copied to
+   dt->chain->name; memory for newdt is allocated within dimtree_new;
+   origin is a bit vector where exactly one bit is set; it is used to
+   uniquely define the "origin property" of newdt; dummy and policy
+   define the base ruleset; dummy must have TARGET_DUMMY as target,
+   policy must be a terminal rule without any dt_matches;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+dimtree_new(struct dimtree **newdt, __u32 origin, const char *chain_name,
+	    struct dt_rule *dummy, struct dt_rule *policy);
+
+/* free memory for dt and all embedded structures; make sure that no packet
+   matching occurs on dt any more */
+void
+dimtree_free(struct dimtree *dt);
+
+/* remove all rules except the first and the last one from dt->chain and
+   free them; set dt->top to the last rule in the chain */
+void
+dimtree_flush(struct dimtree *dt);
+
+const char *
+dimtree_get_chain_name(const struct dimtree *dt);
+
+/* insert rule into the dt_chain and the rlps; inc indicates whether all
+   rule positions >= rule->spec.pos should be incremented by 1;
+   if commit is not 0 then the top level structure in dt is replaced by the
+   new one and the old rlps and elementary intervals are freed;
+   in case of a fault all newly created rlps and elementary intervals
+   are freed; origin is a bit vector describing the allowed dimtrees
+   into which rule may be inserted; if rule must not be inserted into dt
+   it is anyway inserted into dt->chain (so take care to remove it from
+   there);
+   NOTICE: if commit is not 0 it is assumed that this operation is the
+           first one (at all or directly after a previously committed
+           operation or series of operations (-> dimtree_commit))
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION,
+                    HE_RULE_ORIGIN_MISMATCH */
+hipac_error
+dimtree_insert(struct dimtree *dt, struct dt_rule *rule, __u32 origin,
+	       int inc, int commit);
+
+/* delete rule from rlp, _NOT_ from the dt_chain; 'rule' must point to a
+   rule in dt->chain; if commit is not 0 then the top level structure in dt
+   is replaced by the new one and the old rlps and elementary intervals
+   are freed; in case of a fault all newly created rlps and elementary
+   intervals are freed;
+   NOTICE: if commit is not 0 it is assumed that this operation is the
+           first one (at all or directly after a previously committed
+           operation or series of operations (-> dimtree_commit))
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+dimtree_delete(struct dimtree *dt, struct dt_rule *rule, int commit);
+
+/* called at the end of a successful series of dimtree_insert and/or
+   dimtree_delete operation(s) to make the result visible, i.e. set dt->top
+   to dt->top_new for each dimtree dt in dt_block and free the old rlps
+   and elementary intervals */
+void
+dimtree_commit(struct ptrblock *dt_block);
+
+/* called at the end of an unsuccessful series of dimtree_insert and/or
+   dimtree_delete operation(s) to undo the changes, i.e. set dt->top_new
+   to NULL and need_commit to 0 for each dimtree dt in dt_block and free the
+   new rlps and elementary intervals */
+void
+dimtree_failed(struct ptrblock *dt_block);
+
+#ifdef DEBUG
+int
+rule_occur(struct gen_spec *g, struct dt_rule *rule, int print);
+#endif
+
+/* remove all rules between start and the rule(s) r with position end_pos inc.
+   start and r themselves; the positions of the rules behind r are not
+   changed */
+static inline void
+dimtree_chain_delete(struct dimtree *dt, struct dt_rule *start, __u32 end_pos)
+{
+	struct dt_rule *rule;
+       	struct list_head *lh;
+
+	if (unlikely(dt == NULL || start == NULL ||
+		     start->spec.pos > end_pos)) {
+		ARG_MSG;
+		return;
+	}
+
+	assert(dt->need_commit == 0);
+	if (start->head.prev == &dt->chain->head) {
+		/* start is the first element => dt->chain->first stays
+		   NULL until dimtree_chain_fix has been called */
+		dt->chain->first = NULL;
+	} else if (dt->chain->first != NULL &&
+		   dt->chain->first->spec.pos >= start->spec.pos) {
+		dt->chain->first = list_entry(start->head.prev,
+					      struct dt_rule, head);
+	}
+	for (lh = &start->head, rule = start; lh != &dt->chain->head &&
+		     rule->spec.pos <= end_pos;) {
+		lh = lh->next;
+		list_del(lh->prev);
+#ifdef DEBUG
+		if (rule_occur(dt->top, rule, 1)) {
+			ERR("rule present in original structure");
+			return;
+		}
+#endif
+		if (rule->exec_match != NULL) {
+			ptrblock_free(rule->exec_match);
+		}
+		hp_free(rule);
+		dt->chain->len--;
+		rule = list_entry(lh, struct dt_rule, head);
+	}
+}
+
+/* iterate over the dt_chain in dt and tighten the position numbers */
+void
+dimtree_chain_fix(struct ptrblock *dt_block);
+
+#ifdef DEBUG
+/* matching algorithm used for correctness checks; the returned ptrblock
+   contains the rules matching the packet ordered after their positions;
+   the last rule should always have TARGET_ACCEPT or TARGET_DROP as action
+   and may not contain exec_matches */
+struct ptrblock *
+hipac_match_debug(struct dimtree *dt, const void *packet);
+#endif
+
+#endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/global.c 2.6.13-patched/net/ipv4/netfilter/nf-hipac/global.c
--- 2.6.13/net/ipv4/netfilter/nf-hipac/global.c	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/global.c	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,964 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#include "global.h"
+#include "ihash.h"
+
+#define LEN(array) (sizeof(array) / sizeof(*(array)))
+
+
+__u64 mem_max = 0;
+__u64 mem_current_tight = 0;
+__u64 mem_current_real = 0;
+static struct ihash *memhash = NULL;
+
+
+void *
+hp_alloc(__u32 size, int do_add)
+{
+	__u32 sz_real;
+	void *p;
+
+	if (unlikely(size == 0 || size >= 0x80000000)) {
+		ARG_MSG;
+		return NULL;
+	}
+	if (unlikely(memhash == NULL)) {
+		memhash = ihash_new(INITIAL_MEMHASH_LEN, 1,
+				    MEMHASH_AVRG_ELEM_PER_BUCKET,
+				    ihash_func_val, eq_val);
+		if (memhash == NULL) {
+			ERR("unable to create memhash");
+			return NULL;
+		}
+	}
+	if (size <= PAGE_SIZE) {
+		sz_real = mini_alloc_size(size);
+		if (unlikely(do_add && mem_current_real + sz_real > mem_max)) {
+			goto mem_max_reached;
+		}
+		p = mini_alloc(size);
+	} else {
+		sz_real = big_alloc_size(size);
+		if (unlikely(do_add && mem_current_real + sz_real > mem_max)) {
+			goto mem_max_reached;
+		}
+		p = big_alloc(size);
+	}
+	if (p == NULL) {
+		return NULL;
+	}
+	if (ihash_insert(&memhash, p,
+			 val_to_ptr(((!!do_add) << 31) | size)) < 0) {
+		if (size <= PAGE_SIZE) {
+			mini_free(p);
+		} else {
+			big_free(p);
+		}
+		return NULL;
+	}
+	if (do_add) {
+		mem_current_tight += size;
+		mem_current_real += sz_real;
+	}
+	return p;
+	
+ mem_max_reached:
+	return NULL;
+}
+
+void
+hp_free(void *p)
+{
+	__u32 size, sz_real, do_add;
+	void *inf;
+
+	if (unlikely(p == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	if (unlikely(memhash == NULL)) {
+		ERR("hp_free called before hp_alloc");
+		return;
+	}
+	inf = ihash_lookup_val(memhash, p);
+	if (unlikely(inf == NULL)) {
+		ERR("pointer %p not in memhash", p);
+		return;
+	}
+	size = ptr_to_val(inf);
+	do_add = size & 0x80000000;
+	size &= 0x7FFFFFFF;
+	if (size <= PAGE_SIZE) {
+		mini_free(p);
+		if (unlikely(ihash_delete(memhash, p, NULL) < 0)) {
+			goto hashdel_failed;
+		}
+		if (!do_add) {
+			return;
+		}
+		sz_real = mini_alloc_size(size);
+	} else {
+		big_free(p);
+		if (unlikely(ihash_delete(memhash, p, NULL) < 0)) {
+			goto hashdel_failed;
+		}
+		if (!do_add) {
+			return;
+		}
+		sz_real = big_alloc_size(size);
+	}
+	mem_current_tight -= size;
+	mem_current_real -= sz_real;
+	return;
+
+ hashdel_failed:
+	ERR("memhash delete failed");
+	return;
+}
+
+void *
+hp_realloc(void *p, __u32 newsize)
+{
+	__u32 sz, sz_real, newsz_real, do_add;
+	void *inf, *newp;
+
+	if (unlikely(newsize == 0 || newsize >= 0x80000000 || p == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+	if (unlikely(memhash == NULL)) {
+		ERR("hp_realloc called before hp_alloc");
+		return NULL;
+	}
+	inf = ihash_lookup_val(memhash, p);
+	if (unlikely(inf == NULL)) {
+		ERR("pointer %p not in memhash\n", p);
+		return NULL;
+	}
+	sz = ptr_to_val(inf);
+	do_add = sz & 0x80000000;
+	sz &= 0x7FFFFFFF;
+	sz_real = sz <= PAGE_SIZE ? mini_alloc_size(sz) : big_alloc_size(sz);
+	if (newsize <= PAGE_SIZE) {
+		newsz_real = mini_alloc_size(newsize);
+		if (sz_real == newsz_real) {
+			goto only_size_change;
+		}
+		if (unlikely(do_add && mem_current_real + newsz_real >
+			     mem_max + sz_real)) {
+			if (newsize <= sz) {
+				goto only_size_change;
+			}
+			goto mem_max_reached;
+		}
+		newp = mini_alloc(newsize);
+	} else {
+		newsz_real = big_alloc_size(newsize);
+		if (sz_real == newsz_real) {
+			goto only_size_change;
+		}
+		if (unlikely(do_add && mem_current_real + newsz_real >
+			     mem_max + sz_real)) {
+			if (newsize <= sz) {
+				goto only_size_change;
+			}
+			goto mem_max_reached;
+		}
+		newp = big_alloc(newsize);
+	}
+	if (newp == NULL) {
+		if (newsize <= sz) {
+			goto only_size_change;
+		}
+		return NULL;
+	}
+	if (unlikely(ihash_replace(&memhash, p, NULL, newp,
+				   val_to_ptr(((!!do_add) << 31) |
+					      newsize)) < 0)) {
+		if (newsize <= PAGE_SIZE) {
+			mini_free(newp);
+		} else {
+			big_free(newp);
+		}
+		if (newsize <= sz) {
+			goto only_size_change;
+		}
+		return NULL;
+	}
+	memcpy(newp, p, sz < newsize ? sz : newsize);
+	if (sz <= PAGE_SIZE) {
+		mini_free(p);
+	} else {
+		big_free(p);
+	}
+	if (do_add) {
+		mem_current_tight += newsize;
+		mem_current_tight -= sz;
+		mem_current_real += newsz_real;
+		mem_current_real -= sz_real;
+	}
+	return newp;
+
+ mem_max_reached:
+	return NULL;
+
+ only_size_change:
+	if (unlikely(ihash_replace(&memhash, p, NULL, p,
+				   val_to_ptr(((!!do_add) << 31) |
+					      newsize)) < 0)) {
+		ERR("unable to replace memhash entry");
+		return NULL;
+	}
+	if (do_add) {
+		mem_current_tight += newsize;
+		mem_current_tight -= sz;
+	} 
+	return p;
+}
+
+hipac_error
+hp_size(void *p, __u64 *size_real, __u64 *size_tight)
+{
+	void *inf;
+	__u32 size;
+	
+	if (unlikely(size_real == NULL || size_tight == NULL)) {
+		ARG_ERR;
+	}
+	if (unlikely(p == NULL)) {
+		return HE_OK;
+	}
+	inf = ihash_lookup_val(memhash, p);
+	if (unlikely(inf == NULL)) {
+		IMPOSSIBLE_CONDITION("size request for unkown pointer");
+	}
+	size = ((__u32) ptr_to_val(inf)) & 0x7FFFFFFF;
+	*size_tight += size;
+	*size_real += size <= PAGE_SIZE ? mini_alloc_size(size) :
+		big_alloc_size(size);
+	return HE_OK;
+}
+
+void
+hp_mem_exit(void)
+{
+	if (unlikely(memhash == NULL)) {
+		return;
+	}
+	if (unlikely(memhash->elem_ct != 0)) {
+		WARN("memhash still contains unfreed pointers");
+	}
+	if (unlikely(mem_current_tight != 0)) {
+		WARN("mem_current_tight is not 0");
+	}
+	if (unlikely(mem_current_real != 0)) {
+		WARN("mem_current_real is not 0");
+	}
+	ihash_free(memhash);
+	memhash = NULL;
+}
+
+hipac_error
+hipac_get_mem_stat(struct hipac_mem_stat *stat)
+{
+	struct ihash_stat istat;
+
+	if (stat == NULL) {
+		ARG_ERR;
+	}
+	if (sizeof(istat.bucket_dist) != sizeof(stat->memhash_bucket_stat)) {
+		IMPOSSIBLE_CONDITION("struct ihash_stat and struct "
+				     "hipac_mem_stat incompatible");
+	}
+	if (ihash_stat(memhash, &istat) < 0) {
+		IMPOSSIBLE_CONDITION("ihash_stat failed");
+	}
+	
+	stat->total_mem_tight = mem_current_tight;
+	stat->total_mem_real = mem_current_real;
+	stat->memhash_elem_num = istat.elem_ct;
+	stat->memhash_len = istat.bucket_len;
+	stat->memhash_smallest_bucket_len = istat.small_bucket_len;
+	stat->memhash_biggest_bucket_len = istat.big_bucket_len;
+	memcpy(stat->memhash_bucket_stat, istat.bucket_dist,
+	       sizeof(istat.bucket_dist));
+	return HE_OK;
+}
+
+
+
+/*
+ * statistical distributions
+ */
+
+void
+stat_distribution_add(__u32 dist[], __u32 len, __u32 val)
+{
+	__u32 i;
+
+	if (unlikely(dist == NULL || len == 0)) {
+		ARG_MSG;
+		return;
+	}
+	
+	for (i = 0; i < len - 1; i++) {
+		if (val <= (1 << i) - 1) {
+			dist[i]++;
+			return;
+		}
+	}
+	dist[i]++;
+}
+
+
+
+/*
+ * pointer block
+ */
+
+struct ptrblock *
+ptrblock_new(void *p, int do_add)
+{
+	struct ptrblock *new;
+	
+	if (unlikely(p == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+	new = hp_alloc(sizeof(*new) + sizeof(*new->p), do_add);
+	if (new == NULL) {
+		return NULL;
+	}
+	new->len = 1;
+	new->p[0] = p;
+	return new;
+}
+
+int
+ptrblock_eq(const struct ptrblock *b1, const struct ptrblock *b2)
+{
+	__u32 i;
+	
+	if (b1 == b2) {
+		return 1;
+	}
+	if (b1 == NULL || b2 == NULL || b1->len != b2->len) {
+		return 0;
+	}
+	/* b1->len == 0 is valid if b1 and b2 are embedded ptrblocks */
+	for (i = 0; i < b1->len; i++) {
+		if (b1->p[i] != b2->p[i]) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+hipac_error
+ptrblock_clone(struct ptrblock *b, struct ptrblock **clone)
+{
+	__u32 sz;
+
+	if (unlikely(clone == NULL)) {
+		ARG_ERR;
+	}
+
+	if (b == NULL) {
+		*clone = NULL;
+		return HE_OK;
+	}
+	sz = ptrblock_size(b);
+	*clone = hp_alloc(sz, 1);
+	if (*clone == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	memcpy(*clone, b, sz);
+	return HE_OK;
+}
+
+hipac_error
+ptrblock_insert(struct ptrblock **b, void *p, __u32 pos)
+{
+	struct ptrblock *new;
+
+	if (unlikely(p == NULL || b == NULL || (*b == NULL && pos > 0) ||
+		     (*b != NULL && pos > (*b)->len))) {
+		ARG_ERR;
+	}
+
+	if (*b == NULL) {
+		new = ptrblock_new(p, 1);
+		if (new == NULL) {
+			return HE_LOW_MEMORY;
+		}
+		*b = new;
+		return HE_OK;
+	}
+	new = hp_realloc(*b, sizeof(**b) + ((*b)->len + 1) * sizeof(*(*b)->p));
+	if (new == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	if (new->len > pos) {
+		memmove(&new->p[pos + 1], &new->p[pos], (new->len - pos) *
+			sizeof(*new->p));
+	}
+	new->len++;
+	new->p[pos] = p;
+	*b = new;
+	return HE_OK;
+}
+
+hipac_error
+ptrblock_insert_embed(void **o, __u32 ptrblock_offset, void *p, __u32 pos)
+{
+	struct ptrblock *b;
+	void *new;
+
+	if (unlikely(o == NULL || *o == NULL || p == NULL ||
+		     pos > ((struct ptrblock *)
+			    ((char *) *o + ptrblock_offset))->len)) {
+		ARG_ERR;
+	}
+	b = (struct ptrblock *) ((char *) *o + ptrblock_offset);
+	new = hp_realloc(*o, ptrblock_offset + sizeof(*b) +
+			 (b->len + 1) * sizeof(*b->p));
+	if (new == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	b = (struct ptrblock *) ((char *) new + ptrblock_offset);
+	if (b->len > pos) {
+		memmove(&b->p[pos + 1], &b->p[pos], (b->len - pos) *
+			sizeof(*b->p));
+	}
+	b->len++;
+	b->p[pos] = p;
+	*o = new;
+	return HE_OK;
+}
+
+hipac_error
+ptrblock_append(struct ptrblock **b, void *p)
+{
+	struct ptrblock *new;
+
+	if (unlikely(p == NULL || b == NULL)) {
+		ARG_ERR;
+	}
+
+	if (*b == NULL) {
+		new = ptrblock_new(p, 1);
+		if (new == NULL) {
+			return HE_LOW_MEMORY;
+		}
+		*b = new;
+		return HE_OK;
+	}
+	new = hp_realloc(*b, sizeof(**b) + ((*b)->len + 1) * sizeof(*(*b)->p));
+	if (new == NULL) {
+		return HE_LOW_MEMORY;
+	}
+#ifdef DEBUG
+	{
+		__u32 i;
+		for (i = 0; i < new->len; i++) {
+			if (new->p[i] == p) {
+				IMPOSSIBLE_CONDITION("ptrblock contains "
+						     "duplicated pointer");
+			}
+		}
+	}
+#endif
+	new->p[new->len++] = p;
+	*b = new;
+	return HE_OK;
+}
+
+hipac_error
+ptrblock_delete_pos(struct ptrblock **b, __u32 pos)
+{
+	struct ptrblock *new;
+
+	if (unlikely(b == NULL || *b == NULL || pos >= (*b)->len)) {
+		ARG_ERR;
+	}
+
+	if ((*b)->len == 1) {
+		ptrblock_free(*b);
+		*b = NULL;
+		return HE_OK;
+	}
+	(*b)->len--;
+	if ((*b)->len > pos) {
+		memmove(&(*b)->p[pos], &(*b)->p[pos + 1],
+			((*b)->len - pos) * sizeof(*(*b)->p));
+	}
+	new = hp_realloc(*b, sizeof(**b) + (*b)->len * sizeof(*(*b)->p));
+	if (new == NULL) {
+		WARN("hp_realloc returns NULL although less memory was "
+		     "requested");
+	} else {
+		*b = new;
+	}
+	return HE_OK;
+}
+
+hipac_error
+ptrblock_delete_pos_embed(void **o, __u32 ptrblock_offset, __u32 pos)
+{
+	struct ptrblock *new;
+	struct ptrblock *b;
+
+	if (unlikely(o == NULL || *o == NULL ||
+		     pos >= ((struct ptrblock *)
+			     ((char *) *o + ptrblock_offset))->len)) {
+		ARG_ERR;
+	}
+	b = (struct ptrblock *) ((char *) *o + ptrblock_offset);
+	b->len--;
+	if (b->len > pos) {
+		memmove(&b->p[pos], &b->p[pos + 1],
+			(b->len - pos) * sizeof(*b->p));
+	}
+	new = hp_realloc(*o, ptrblock_offset + sizeof(*b) +
+			 b->len * sizeof(*b->p));
+	if (new == NULL) {
+		WARN("hp_realloc returns NULL although less memory was "
+		     "requested");
+	} else {
+		*o = new;
+	}
+	return HE_OK;
+}
+
+hipac_error
+ptrblock_delete(struct ptrblock **b, void *p)
+{
+	__u32 i;
+
+	if (unlikely(b == NULL || *b == NULL)) {
+		ARG_ERR;
+	}
+	for (i = 0; i < (*b)->len; i++) {
+		if ((*b)->p[i] == p) {
+			return ptrblock_delete_pos(b, i);
+		}
+	}
+	IMPOSSIBLE_CONDITION("pointer %p not in ptrblock", p);
+}
+
+hipac_error
+ptrblock_delete_tail(struct ptrblock **b)
+{
+	struct ptrblock *new;
+
+	if (unlikely(b == NULL || *b == NULL)) {
+		ARG_ERR;
+	}
+
+	if ((*b)->len == 1) {
+		ptrblock_free(*b);
+		*b = NULL;
+		return HE_OK;
+	}
+	(*b)->len--;
+	new = hp_realloc(*b, sizeof(**b) + (*b)->len * sizeof(*(*b)->p));
+	if (new == NULL) {
+		WARN("hp_realloc returns NULL although less memory was "
+		     "requested");
+	} else {
+		*b = new;
+	}
+	return HE_OK;
+}
+
+hipac_error
+ptrblock_delete_multi(struct ptrblock **b, const struct ptrblock *mark)
+{
+	struct ptrblock *new;
+	__u32 first, last, i;
+
+	if (unlikely(b == NULL || mark == NULL ||
+		     (*b != NULL && mark->len < (*b)->len))) {
+		ARG_ERR;
+	}
+
+	if (*b == NULL) {
+		return HE_OK;
+	}
+	for (first = 0; first < (*b)->len && mark->p[first] != NULL; first++);
+	if (first == (*b)->len) {
+		/* nothing to delete */
+		return HE_OK;
+	}
+	for (last = first + 1, i = 0; last < (*b)->len; last++) {
+		if (mark->p[last] != NULL) {
+			continue;
+		}
+		if (last > first + 1) {
+			memmove(&(*b)->p[first - i], &(*b)->p[first + 1],
+				(last - first - 1) * sizeof(*(*b)->p));
+		}
+		i++;
+		first = last;
+	}
+	if ((*b)->len > first + 1) {
+		memmove(&(*b)->p[first - i], &(*b)->p[first + 1],
+			((*b)->len - first - 1) * sizeof(*(*b)->p));
+	}
+	(*b)->len -= i + 1;
+	if ((*b)->len == 0) {
+		ptrblock_free(*b);
+		*b = NULL;
+		return HE_OK;
+	}
+	new = hp_realloc(*b, sizeof(**b) + (*b)->len * sizeof(*(*b)->p));
+	if (new == NULL) {
+		WARN("hp_realloc returns NULL although less memory was "
+		     "requested");
+	} else {
+		*b = new;
+	}
+	return HE_OK;
+}
+
+hipac_error
+ptrblock_delete_null(struct ptrblock **b)
+{
+	struct ptrblock *new;
+	__u32 first, last, i;
+
+	if (unlikely(b == NULL)) {
+		ARG_ERR;
+	}
+
+	if (*b == NULL) {
+		return HE_OK;
+	}
+	for (first = 0; first < (*b)->len && (*b)->p[first] != NULL; first++);
+	if (first == (*b)->len) {
+		/* nothing to delete */
+		return HE_OK;
+	}
+	for (last = first + 1, i = 0; last < (*b)->len; last++) {
+		if ((*b)->p[last] != NULL) {
+			continue;
+		}
+		if (last > first + 1) {
+			memmove(&(*b)->p[first - i], &(*b)->p[first + 1],
+				(last - first - 1) * sizeof(*(*b)->p));
+		}
+		i++;
+		first = last;
+	}
+	if ((*b)->len > first + 1) {
+		memmove(&(*b)->p[first - i], &(*b)->p[first + 1],
+			((*b)->len - first - 1) * sizeof(*(*b)->p));
+	}
+	(*b)->len -= i + 1;
+	if ((*b)->len == 0) {
+		ptrblock_free(*b);
+		*b = NULL;
+		return HE_OK;
+	}
+	new = hp_realloc(*b, sizeof(**b) + (*b)->len * sizeof(*(*b)->p));
+	if (new == NULL) {
+		WARN("hp_realloc returns NULL although less memory was "
+		     "requested");
+	} else {
+		*b = new;
+	}
+	return HE_OK;
+}
+
+
+
+/*
+ * block of structs
+ */
+struct strblock *
+strblock_new(const void *s, __u32 size, int do_add)
+{
+	struct strblock *new;
+	
+	if (unlikely(s == NULL || size == 0)) {
+		ARG_MSG;
+		return NULL;
+	}
+	new = hp_alloc(sizeof(*new) + size, do_add);
+	if (new == NULL) {
+		return NULL;
+	}
+	new->len = 1;
+	new->size = size;
+	memcpy(new->d, s, size);
+	return new;
+}
+
+int
+strblock_eq(const struct strblock *b1, const struct strblock *b2,
+	    int (* eq) (void *, void *))
+{
+	__u32 i;
+
+	if (b1 == b2) {
+		return 1;
+	}
+	if (b1 == NULL || b2 == NULL || b1->len != b2->len ||
+	    b1->size != b2->size) {
+		return 0;
+	}
+	assert(b1->len > 0);
+	for (i = 0; i < b1->len; i++) {
+		if (!eq(STRBLOCK_ITH(b1, i, void *),
+			STRBLOCK_ITH(b2, i, void *))) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+hipac_error
+strblock_clone(struct strblock *b, struct strblock **clone)
+{
+	__u32 sz;
+
+	if (unlikely(clone == NULL)) {
+		ARG_ERR;
+	}
+
+	if (b == NULL) {
+		*clone = NULL;
+		return HE_OK;
+	}
+	sz = strblock_size(b);
+	*clone = hp_alloc(sz, 1);
+	if (*clone == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	memcpy(*clone, b, sz);
+	return HE_OK;
+}
+
+hipac_error
+strblock_insert(struct strblock **b, const void *s, __u32 size, __u32 pos)
+{
+	struct strblock *new;
+
+	if (unlikely(s == NULL || b == NULL ||
+		     (*b == NULL && (pos > 0 || size == 0)) ||
+		     (*b != NULL && (pos > (*b)->len ||
+				     (*b)->size != size)))) {
+		ARG_ERR;
+	}
+
+	if (*b == NULL) {
+		new = strblock_new(s, size, 1);
+		if (new == NULL) {
+			return HE_LOW_MEMORY;
+		}
+		*b = new;
+		return HE_OK;
+	}
+	new = hp_realloc(*b, sizeof(**b) + ((*b)->len + 1) * size);
+	if (new == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	if (new->len > pos) {
+		memmove(STRBLOCK_ITH(new, pos + 1, void *),
+			STRBLOCK_ITH(new, pos, void *),
+			(new->len - pos) * size);
+	}
+	new->len++;
+	memcpy(STRBLOCK_ITH(new, pos, void *), s, size);
+	*b = new;
+	return HE_OK;
+}
+
+hipac_error
+strblock_append(struct strblock **b, const void *s, __u32 size)
+{
+	struct strblock *new;
+
+	if (unlikely(s == NULL || b == NULL || (*b == NULL && size == 0) ||
+		     (*b != NULL && (*b)->size != size))) {
+		ARG_ERR;
+	}
+
+	if (*b == NULL) {
+		new = strblock_new(s, size, 1);
+		if (new == NULL) {
+			return HE_LOW_MEMORY;
+		}
+		*b = new;
+		return HE_OK;
+	}
+	new = hp_realloc(*b, sizeof(**b) + ((*b)->len + 1) * size);
+	if (new == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	memcpy(STRBLOCK_ITH(new, new->len, void *), s, size);
+	new->len++;
+	*b = new;
+	return HE_OK;
+}
+
+hipac_error
+strblock_delete_pos(struct strblock **b, __u32 pos)
+{
+	struct strblock *new;
+
+	if (unlikely(b == NULL || *b == NULL || pos >= (*b)->len)) {
+		ARG_ERR;
+	}
+
+	if ((*b)->len == 1) {
+		strblock_free(*b);
+		*b = NULL;
+		return HE_OK;
+	}
+	(*b)->len--;
+	if ((*b)->len > pos) {
+		memmove(STRBLOCK_ITH(*b, pos, void *),
+			STRBLOCK_ITH(*b, pos + 1, void *),
+			((*b)->len - pos) * (*b)->size);
+	}
+	new = hp_realloc(*b, sizeof(**b) + (*b)->len * (*b)->size);
+	if (new == NULL) {
+		WARN("hp_realloc returns NULL although less memory was "
+		     "requested");
+	} else {
+		*b = new;
+	}
+	return HE_OK;
+}
+
+hipac_error
+strblock_delete_tail(struct strblock **b)
+{
+	struct strblock *new;
+
+	if (unlikely(b == NULL || *b == NULL)) {
+		ARG_ERR;
+	}
+
+	if ((*b)->len == 1) {
+		strblock_free(*b);
+		*b = NULL;
+		return HE_OK;
+	}
+	(*b)->len--;
+	new = hp_realloc(*b, sizeof(**b) + (*b)->len * (*b)->size);
+	if (new == NULL) {
+		WARN("hp_realloc returns NULL although less memory was "
+		     "requested");
+	} else {
+		*b = new;
+	}
+	return HE_OK;
+}
+
+
+
+/*
+ * pointer list
+ */
+
+struct ptrlist *
+ptrlist_new(void)
+{
+	struct ptrlist *new;
+
+	new = mini_alloc(sizeof(*new));
+	if (new == NULL) {
+		return NULL;
+	}
+	new->len = 0;
+	INIT_LIST_HEAD(&new->head);
+	return new;
+}
+
+struct ptrlist_entry *
+ptrlist_new_entry(void *p)
+{
+	struct ptrlist_entry *new;
+
+	new = mini_alloc(sizeof(*new));
+	if (new == NULL) {
+		return NULL;
+	}
+	new->p = p;
+	return new;
+}
+
+void
+ptrlist_flush(struct ptrlist *l)
+{
+	struct list_head *lh;
+
+	if (unlikely(l == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	for (lh = l->head.next; lh != &l->head;) {
+		lh = lh->next;
+		mini_free(list_entry(lh->prev, struct ptrlist_entry, head));
+	}
+	INIT_LIST_HEAD(&l->head);
+	l->len = 0;
+}
+
+void
+ptrlist_free(struct ptrlist *l)
+{
+	struct list_head *lh;
+
+	if (unlikely(l == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	for (lh = l->head.next; lh != &l->head;) {
+		lh = lh->next;
+		mini_free(list_entry(lh->prev, struct ptrlist_entry, head));
+	}
+	mini_free(l);
+}
+
+hipac_error
+ptrlist_add(struct ptrlist *l, void *p, int check_dup)
+{
+	struct list_head *lh;
+	struct ptrlist_entry* e;
+
+	if (unlikely(l == NULL || p == NULL)) {
+		ARG_ERR;
+	}
+	if (unlikely(check_dup)) {
+		list_for_each(lh, &l->head) {
+			e = list_entry(lh, struct ptrlist_entry, head);
+			if (e->p == p) {
+				IMPOSSIBLE_CONDITION("pointer %p already in "
+						     "ptrlist", p);
+			}
+		}
+	}
+	e = mini_alloc(sizeof(*e));
+	if (e == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	e->p = p;
+	list_add_tail(&e->head, &l->head);
+	l->len++;
+	return HE_OK;
+}
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/global.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/global.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/global.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/global.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,387 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _GLOBAL_H
+#define _GLOBAL_H
+
+#include "mode.h"
+#include "hipac.h"  // hipac_error
+
+#define INITIAL_MEMHASH_LEN          64
+#define MEMHASH_AVRG_ELEM_PER_BUCKET 15
+#define INITIAL_NEWSPEC_LEN          1024
+#define NEWSPEC_AVRG_ELEM_PER_BUCKET 3
+
+#ifdef DEBUG
+#  define DPRINT(type, str, args...) if ((type) & hipac_debug)  \
+                                          printk(str , ## args)
+#else
+#  define DPRINT(type, str, args...) do {} while (0)
+#endif
+
+/* the single space before the last ',' is vital to make this macro work the
+   expected way because of some idiosyncrasy of gcc */
+#ifdef DEBUG
+#  define MSG(type, str, args...)                                           \
+   printk(type "%-15s : %-30s : %6d :   " str "\n", __FILE__, __FUNCTION__, \
+	  __LINE__ , ## args)
+#else
+#  define MSG(type, str, args...)                             \
+   printk(type "%s:%s:%d: " str "\n", __FILE__, __FUNCTION__, \
+	  __LINE__ , ## args)
+#endif
+
+#define ERR(str, args...)    MSG(KERN_ERR, str , ## args)
+#define WARN(str, args...)   MSG(KERN_WARNING, str , ## args)
+#define NOTICE(str, args...) MSG(KERN_NOTICE, str , ## args)
+#define DBG(str, args...)    MSG(KERN_DEBUG, str , ## args)
+#define ARG_MSG              MSG(KERN_ERR, "function arguments invalid")
+
+#define ARG_ERR                                      \
+do {                                                 \
+	MSG(KERN_ERR, "function arguments invalid"); \
+	return HE_IMPOSSIBLE_CONDITION;              \
+} while (0)
+
+#define IMPOSSIBLE_CONDITION(str, args...) \
+do {                                       \
+	MSG(KERN_ERR, str , ## args);      \
+	return HE_IMPOSSIBLE_CONDITION;    \
+} while (0)
+
+
+
+/* generic header for dimtree rules, elementary intervals and rlps */
+struct gen_spec
+{
+	unsigned rlp : 1;
+};
+
+/* dimid to bittype array */
+extern __u8 *dim2btype;
+
+/* match executor function */
+extern hipac_match_exec_t match_fn;
+
+/* target executor function */
+extern hipac_target_exec_t target_fn;
+
+/* dimension extractor function */
+extern hipac_extract_t *extract_fn;
+
+
+
+/*
+ * memory management wrappers
+ */
+
+/* upper bound for memory consumption in bytes */
+extern __u64 mem_max;
+
+/* current memory consumption in bytes in terms of how much
+   has been requested */
+extern __u64 mem_current_tight;
+
+/* current memory consumption in bytes in terms of how much
+   has actually been allocated */
+extern __u64 mem_current_real;
+
+/* do_add indicates whether mem_current_tight and mem_current_real 
+   should be updated or not */
+void *
+hp_alloc(__u32 size, int do_add);
+
+void
+hp_free(void *p);
+
+void *
+hp_realloc(void *p, __u32 newsize);
+
+/* add the number of bytes requested for p to *size_tight and the number
+   of bytes allocated for p to *size_real; if p is NULL, size_tight and
+   size_real are not modified;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+hp_size(void *p, __u64 *size_real, __u64 *size_tight);
+
+/* internal memhash is freed; if it is not empty a warning is printed */
+void
+hp_mem_exit(void);
+
+
+
+/*
+ * statistical distributions
+ */
+
+/* initialize statistical distribution dist of length len, i.e. set it to 0 */
+static inline void
+stat_distribution_init(__u32 dist[], __u32 len)
+{
+	if (unlikely(dist == NULL || len == 0)) {
+		ARG_MSG;
+		return;
+	}
+	memset(dist, 0, len * sizeof(*dist));
+}
+
+/* dist is an array of length len representing a statistical distribution;
+   val is added to dist */
+void
+stat_distribution_add(__u32 dist[], __u32 len, __u32 val);
+
+
+
+/*
+ * pointer block
+ */
+struct ptrblock
+{
+	__u32 len;
+	void *p[0];
+};
+
+/* return new pointer block with p as the only element; do_add indicates
+   whether mem_current_tight and mem_current_real should be updated or not */
+struct ptrblock *
+ptrblock_new(void *p, int do_add);
+
+static inline void
+ptrblock_free(struct ptrblock *b)
+{
+	hp_free(b);
+}
+
+static inline __u32
+ptrblock_size(const struct ptrblock *b)
+{
+	if (unlikely(b == NULL)) {
+		ARG_MSG;
+		return 0;
+	}
+	return sizeof(*b) + b->len * sizeof(*b->p);
+}
+
+/* returns 1 if b1 and b2 are equal and 0 otherwise; b1->len or b2->len might
+   be 0 in order allow equality test on embedded ptrblocks */
+int
+ptrblock_eq(const struct ptrblock *b1, const struct ptrblock *b2);
+
+/* clone b and store the result in clone; the memory for clone is allocated
+   via hp_alloc if necessary and do_add is 1; b might be NULL;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_clone(struct ptrblock *b, struct ptrblock **clone);
+
+/* insert p into b at position pos; if *b is NULL and pos is 0
+   ptrblock_new(p, 1) is called and the result is assigned to b;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_insert(struct ptrblock **b, void *p, __u32 pos);
+
+/* insert p into (struct ptrblock *) ((char *) *o + ptrblock_offset)
+   at position pos; o is assumed to end after the embedded ptrblock;
+   hp_realloc is used to resize o;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_insert_embed(void **o, __u32 ptrblock_offset, void *p, __u32 pos);
+
+/* append p to b; if *b is NULL ptrblock_new(p, 1) is called and the result
+   is assigned to b;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_append(struct ptrblock **b, void *p);
+
+/* delete pointer at position pos in b; if b contains only one element and
+   pos is 0 then *b is freed and NULL is assigned to *b;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_delete_pos(struct ptrblock **b, __u32 pos);
+
+/* delete pointer at position pos in
+   (struct ptrblock *) ((char *) *o + ptrblock_offset); o is assumed to end
+   after the embedded ptrblock; hp_realloc is used to resize o;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_delete_pos_embed(void **o, __u32 ptrblock_offset, __u32 pos);
+
+/* delete p in b; if p is the only element in b then *b is freed and NULL
+   is assigned to *b;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_delete(struct ptrblock **b, void *p);
+
+/* delete trailing pointer in b; if b contains only one element then *b is
+   freed and NULL is assigned to *b;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_delete_tail(struct ptrblock **b);
+
+/* for all mark->p[i] == NULL: delete the pointer at the position i fom b;
+   if b is empty after the delete operation NULL is assigned to *b;
+   note that mark->len must be >= (*b)->len;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_delete_multi(struct ptrblock **b, const struct ptrblock *mark);
+
+/* similar to ptrblock_delete_multi: the pointers in b which are NULL are
+   deleted;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrblock_delete_null(struct ptrblock **b);
+
+
+
+/*
+ * block of structs
+ */
+struct strblock
+{
+	__u32 len, size;
+	char d[0];
+};
+
+#define STRBLOCK_ITH(b, i, cast) ((cast) ((b)->d + (i) * (b)->size))
+
+/* return new struct block with s as the only element; size is the size of 
+   the struct pointed to by s in bytes; do_add indicates whether
+   mem_current_tight and mem_current_real should be updated or not */
+struct strblock *
+strblock_new(const void *s, __u32 size, int do_add);
+
+static inline void
+strblock_free(struct strblock *b)
+{
+	hp_free(b);
+}
+
+static inline __u32
+strblock_size(const struct strblock *b)
+{
+	if (unlikely(b == NULL)) {
+		ARG_MSG;
+		return 0;
+	}
+	return sizeof(*b) + b->len * b->size;
+}
+
+/* returns 1 if b1 and b2 are equal and 0 otherwise; eq is an equality test
+   function for the embedded structs; eq(a, b) returns 1 if a equals to b
+   and 0 otherwise */
+int
+strblock_eq(const struct strblock *b1, const struct strblock *b2,
+	    int (* eq) (void *, void *));
+
+/* clone b and store the result in clone; the memory for clone is allocated
+   via hp_alloc if necessary and do_add is 1; b might be NULL;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+strblock_clone(struct strblock *b, struct strblock **clone);
+
+/* insert struct s into b at position pos; if *b is NULL and pos is 0
+   strblock_new(s, size, 1) is called and the result is assigned to b;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+strblock_insert(struct strblock **b, const void *s, __u32 size, __u32 pos);
+
+/* append struct s to b; if *b is NULL then strblock_new(s, size, 1) is
+   called and the result is assigned to b;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+strblock_append(struct strblock **b, const void *s, __u32 size);
+
+/* delete struct at position pos in b; if b contains only one element and
+   pos is 0 then *b is freed and NULL is assigned to *b;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+strblock_delete_pos(struct strblock **b, __u32 pos);
+
+/* delete trailing struct in b; if b contains only one element then *b is
+   freed and NULL is assigned to *b;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+strblock_delete_tail(struct strblock **b);
+
+
+
+/*
+ * pointer list
+ */
+struct ptrlist
+{
+	struct list_head head;
+	__u32 len;
+};
+
+struct ptrlist_entry
+{
+	struct list_head head;
+	void *p;
+};
+
+/* return new empty pointer list or NULL if allocation fails */
+struct ptrlist *
+ptrlist_new(void);
+
+/* return new pointer list entry containing p or NULL if allocation fails */
+struct ptrlist_entry *
+ptrlist_new_entry(void *p);
+
+/* free all entries from the pointer list l */
+void
+ptrlist_flush(struct ptrlist *l);
+
+/* free all entries from the pointer list l and l itself */
+void
+ptrlist_free(struct ptrlist *l);
+
+/* free ptrlist entry */
+static inline void
+ptrlist_free_entry(struct ptrlist_entry *e)
+{
+	if (unlikely(e == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	list_del(&e->head);
+	mini_free(e);
+}
+
+/* return 1 if l is empty and 0 otherwise */
+static inline int
+ptrlist_is_empty(const struct ptrlist *l)
+{
+	if (unlikely(l == NULL)) {
+		ARG_MSG;
+		return 0;
+	}
+	assert((l->len != 0 || l->head.next == &l->head) &&
+	       (l->head.next != &l->head || l->len == 0));
+	return l->len == 0;
+}
+
+/* add a new pointer list entry containing p to l; if check_dup is not 0 
+   the new entry is only added if p is not already contained in a list
+   entry;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ptrlist_add(struct ptrlist *l, void *p, int check_dup);
+
+#endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/hipac.c 2.6.13-patched/net/ipv4/netfilter/nf-hipac/hipac.c
--- 2.6.13/net/ipv4/netfilter/nf-hipac/hipac.c	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/hipac.c	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,3750 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#include "hipac.h"
+#include "global.h"
+#include "ihash.h"
+#include "dimtree.h"
+
+
+static struct hipac_chain *current_chain = NULL;
+static struct ihash* chain_hash = NULL;
+static struct ptrblock* native_dts = NULL;
+
+__u8 *dim2btype;
+__u8 d2blen;
+hipac_extract_t *extract_fn;
+static hipac_copy_constructor_t copy_fn;
+static hipac_destroy_exec_t destroy_fn;
+hipac_match_exec_t match_fn;
+hipac_target_exec_t target_fn;
+static hipac_eq_exec_t eq_fn;
+
+
+
+/* 
+ * Some helpful defines in order to make code more readable
+ */
+
+#define DONT_COMMIT    0
+#define COMMIT         1
+#define DONT_INC       0
+#define INC            1
+#define DONT_ADD       0
+#define ADD            1
+#define ORIGIN_ALL     0xffff
+
+#define CHAIN_IS_REFERENCED(chain) ((chain)->ref_count != 0)
+#define CHAIN_NOT_CONNECTED(chain) ((chain)->start == NULL)
+#define IS_ROOT_CHAIN(chain)       ((chain)->dimtree != NULL)
+#define IS_NOT_JUMP_RULE(rule)     ((rule)->r.action != TARGET_CHAIN)
+#define IS_JUMP_RULE(rule)         ((rule)->r.action == TARGET_CHAIN)
+
+#define P_ELEM(x, i)         (STRBLOCK_ITH(x, i, struct path *))
+#define P_ELEM_DIMTREE(x, i) (STRBLOCK_ITH(x, i, struct path *)->dimtree)
+#define P_ELEM_PREV(x, i)    (STRBLOCK_ITH(x, i, struct path *)->prev)
+#define P_ELEM_RULE(x, i)    (STRBLOCK_ITH(x, i, struct path *)->rule)
+
+
+#define CHAIN_HASH_LEN         16
+#define CHAIN_HASH_AVR_BUCKET   3
+#define HIPAC_REC_LIMIT        10
+
+
+#ifdef DEBUG
+#       define LOW_MEM(args...) do { NOTICE(args); return HE_LOW_MEMORY; \
+                                   } while (0)
+#       define CHECK_ERROR(func) \
+                if (error == HE_LOW_MEMORY) { \
+	                NOTICE(func " returned LOW_MEMORY error!"); \
+        } else if (error == HE_IMPOSSIBLE_CONDITION) {  \
+	        ERR(func " returned IMPOSSIBLE_CONDITION error!"); \
+	}
+
+        static inline hipac_error
+	strblock_append_check(struct strblock **b, const void *s, __u32 size){
+		__u32 i;
+		if (*b)
+			for (i = 0; i < (*b)->len; i++){
+				if (!(memcmp(STRBLOCK_ITH(*b, i, void *), 
+					     s, size)))
+					IMPOSSIBLE_CONDITION(
+						"already in strblock");
+			}
+		return strblock_append(b, s, size);
+	}
+
+#else
+#       define LOW_MEM(args...) return HE_LOW_MEMORY
+#       define CHECK_ERROR(func) \
+                if (error == HE_IMPOSSIBLE_CONDITION) {  \
+                        ERR(func " returned IMPOSSIBLE_CONDITION error!"); \
+                }
+
+        static inline hipac_error
+	strblock_append_check(struct strblock **b, const void *s, __u32 size){
+		return strblock_append(b, s, size);
+	}
+#endif
+
+
+
+
+/* element in strblock next_chain in struct hipac_chain
+   means that current chain contains 'count' >= 1 rules 
+   that jump to chain 'chain'                                         */
+struct next_chain_elem
+{
+	__u32 count;
+	struct hipac_chain *chain;
+};
+
+
+/* the combined rule of all the chain_rules on the path
+   from a ROOT_CHAIN to the current chain                             */
+struct prefix_rule
+{
+	__u32 origin;
+	struct ptrblock *exec_matches;
+	__u8  native_mct;
+	struct hipac_match first_match[0];
+};
+
+
+/* the path from a ROOT_CHAIN to the current chain;
+   dimtree:   the dimtree corresponding to the ROOT of that path
+   prev:      the previous chain_rule on that path
+   rule:      the combined rule of all the chain_rules on that path   */
+struct path
+{
+	struct dimtree *dimtree;
+	struct chain_rule *prev;
+	struct prefix_rule *rule;
+};
+
+
+/* hipac_chain is the 'head' of the doubly linked list of chain_rules;
+   name:           the name of the chain
+   ref_count:      the number of rules that jump to this chain
+   next_chains:    block of next_chain_elem structs; each chain that is
+                   jumped to from a rule in this chain has its own 
+		   next_chain_elem in this block with its 'count' field set to
+		   the number of rules that jump to that chain
+   paths:          block of all the paths from any ROOT_CHAIN to this chain
+   start:          contains pointers to dt_rules that mark the beginning
+                   of this chain in the internal dt_chain
+   end:            the same for the ending of the chain
+   dimtree:        points to a dimtree if chain is a ROOT_CHAIN,
+                   otherwise it's NULL                                */
+struct hipac_chain
+{
+	struct list_head head;
+	char name[HIPAC_CHAIN_NAME_MAX_LEN];
+	__u32 list_pos;
+	__u32 ref_count;
+	struct strblock *next_chains; 
+	struct strblock *paths;
+	struct ptrblock *start;
+	struct ptrblock *end;   
+	struct dimtree *dimtree;
+};
+
+/* chain_rule is contained in a cyclic doubly linked list of rules where the
+   'head' of the list is of type struct hipac_chain;
+   dtr:  contains pointers to dt_rules in the internal dt_chain that correspond
+         to this chain_rule                                           */
+struct chain_rule
+{
+	struct list_head head;
+	struct ptrblock *dtr;
+	struct hipac_rule r;
+};
+
+
+
+
+
+/* 
+ * Several functions to free certain structs.
+ * The functions recursively free all other data structures that
+ * are pointed to from within the structs.  
+ */
+
+static inline void
+dt_rule_free(struct dt_rule *rule)
+{
+	if (rule->exec_match)
+		ptrblock_free(rule->exec_match);
+	hp_free(rule);
+}
+
+static inline void
+hipac_rule_free(struct hipac_rule *rule)
+{
+	destroy_fn(rule);
+	hp_free(rule);
+}
+
+static inline void
+chain_rule_free(struct chain_rule *rule)
+{
+	if (rule->dtr)
+		ptrblock_free(rule->dtr);
+	hp_free(rule);
+}
+
+static inline void
+chain_rule_destroy(struct chain_rule *rule)
+{
+	if (rule->dtr)
+		ptrblock_free(rule->dtr);
+	destroy_fn(&rule->r);
+	hp_free(rule);
+}
+
+static inline void
+prefix_rule_free(struct prefix_rule *p)
+{
+	if (p->exec_matches)
+		ptrblock_free(p->exec_matches);
+	hp_free(p);
+}
+
+static inline void
+path_free(struct path *p)
+{
+	if (p->rule)
+		prefix_rule_free(p->rule);
+	hp_free(p);
+}
+
+static inline void
+paths_free(struct strblock *paths)
+{
+	__u32 i;
+	for (i = 0; i < paths->len; i++)
+		prefix_rule_free(P_ELEM_RULE(paths, i));
+	strblock_free(paths);
+}
+
+/* End of free functions */
+
+
+
+
+
+
+
+
+/*
+ * chain_hash_* functions
+ */
+
+
+/* insert 'chain' into the global hash of all chains ('chain_hash') 
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+static inline hipac_error
+chain_hash_insert(struct hipac_chain* chain)
+{
+	return ihash_insert(&chain_hash, chain->name, chain);//IS_THIS_CORRECT?
+}
+
+
+
+/* remove 'chain' from the global hash of all chains ('chain_hash')
+   the removed chain is not freed                                     */
+static inline void
+chain_hash_remove(struct hipac_chain* chain)
+{
+	if (current_chain && current_chain == chain)
+		current_chain = NULL;
+	ihash_delete(chain_hash, chain->name, NULL);
+}
+
+
+
+/* replace 'org' with 'new' in global hash of all chains
+   the replaced chain is not freed                                    */
+static inline hipac_error
+chain_hash_replace(struct hipac_chain *org, struct hipac_chain *new)
+{
+	if (current_chain && current_chain == org)
+		current_chain = NULL;
+	return ihash_replace(&chain_hash, org->name, NULL, new->name, new);
+}
+
+
+
+/* lookup 'chain' with name 'name' in global 'chain_hash',
+   the hash of all chains. 
+   possible errors: HE_CHAIN_NOT_EXISTENT, HE_IMPOSSIBLE_CONDITION    */
+static inline hipac_error
+chain_hash_lookup(const char* name, struct hipac_chain **chain)
+{
+	if (unlikely(!name || !chain))
+		ARG_ERR;
+	if ((current_chain) &&
+	    (!strcmp(name, current_chain->name))){
+		*chain = current_chain;
+		return HE_OK;
+	}
+	*chain = (struct hipac_chain*) ihash_lookup(chain_hash, name);
+	if (*chain != NULL) {
+		current_chain = *chain;
+		return HE_OK;
+	}
+	return HE_CHAIN_NOT_EXISTENT;
+}
+
+
+/* End of chain_hash_* functions */
+
+
+
+
+
+/* get previous dt_rules of the internal dt_rule representations of
+   chain_rule 'rule'.
+   if previous chain_rule 'prev' is not a jump rule return pointer to 
+   'prev->dtr' and set 'free_needed' to 0. otherwise a new ptrblock
+   with pointers to the previous dt_rules has to be computed from the
+   'chain->end' block of the chain 'prev' is pointing to and 
+   'free_needed' is set to 1.
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+static inline hipac_error
+get_prev_dt_rules(const struct hipac_chain *chain, 
+		  const struct chain_rule *rule, 
+		  __u8 *free_needed, struct ptrblock **p)
+{			     
+	struct chain_rule *prev;
+	
+	if (unlikely(CHAIN_NOT_CONNECTED(chain)))
+		return HE_IMPOSSIBLE_CONDITION;
+	
+	if (unlikely(rule->head.prev == &chain->head)){
+		*p = chain->start;
+		*free_needed = 0;
+		return HE_OK;
+	}
+	
+	prev = list_entry(rule->head.prev, struct chain_rule, head);
+	*free_needed = IS_JUMP_RULE(prev);
+	if (!(*free_needed)){
+		*p = prev->dtr;
+	} else {
+		struct hipac_chain *c = NULL;
+		hipac_error error;
+		__u32 i;
+		chain_hash_lookup((void *) &prev->r 
+				  + prev->r.target_offset, &c);
+		*p = NULL;
+		for (i = 0; i < c->paths->len; i++){
+			if (prev == P_ELEM_PREV(c->paths, i)){
+				if ((error = 
+				     ptrblock_append(p, c->end->p[i]))){
+					CHECK_ERROR("ptrblock_append");
+					if (*p)
+						ptrblock_free(*p);
+					*p = NULL;
+					return error;
+				}
+			}
+		}
+	}
+	return HE_OK;
+}
+
+
+
+/* get next dt_rules of the internal dt_rule representations of 
+   chain_rule 'rule'.
+   if next chain_rule 'next' is not a jump rule return pointer to
+   'next->dtr' and set 'free_needed' to 0. otherwise a new ptrblock
+   with pointers to the next dt_rules has to be computed from the
+   'chain->start' block of the chain 'next' is pointing to and 
+   'free_needed' is set to 1. 
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+static inline hipac_error
+get_next_dt_rules(const struct hipac_chain *chain, 
+		  const struct chain_rule *rule, 
+		  __u8 *free_needed, struct ptrblock **p)
+{			     
+	struct chain_rule *next;
+	
+	if (unlikely(CHAIN_NOT_CONNECTED(chain)))
+		return HE_IMPOSSIBLE_CONDITION;
+	
+	if (unlikely(rule->head.next == &chain->head)){
+		*p = chain->end;
+		*free_needed = 0;
+		return HE_OK;
+	}
+	
+	next = list_entry(rule->head.next, struct chain_rule, head);
+	*free_needed = IS_JUMP_RULE(next);
+	if (!(*free_needed)){
+		*p = next->dtr;
+	} else {
+		struct hipac_chain *c = NULL;
+		hipac_error error;
+		__u32 i;
+		chain_hash_lookup((void *) &next->r + 
+				  next->r.target_offset, &c);
+		*p = NULL;
+		for (i = 0; i < c->paths->len; i++){
+			if (next == P_ELEM_PREV(c->paths, i)){
+				if ((error = 
+				     ptrblock_append(p, c->start->p[i]))){
+					CHECK_ERROR("ptrblock_append");
+					if (*p)
+						ptrblock_free(*p);
+					*p = NULL;
+					return error;
+				}
+			}
+		}
+	}
+	return HE_OK;
+}
+
+
+
+
+
+/*
+ * chain_* functions
+ */
+
+
+/* create new hipac_chain with name 'name' and initialize all fields 
+   in struct hipac_chain 'result'. 'list_pos' is used to initialize
+   the list_pos member of 'result'
+   hipac_chain 'result' is not inserted into 'chain_hash'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_EXISTS,
+                    HE_IMPOSSIBLE_CONDITION                           */
+static inline hipac_error
+chain_new(const char *name, struct hipac_chain **result, __u32 list_pos)
+{	
+	struct hipac_chain *chain;
+	hipac_error error;
+
+	if (unlikely(!name || !result))
+		ARG_ERR;
+	
+	if (unlikely(!(error = chain_hash_lookup(name, &chain))))
+		return HE_CHAIN_EXISTS;
+		
+	*result = chain = hp_alloc(sizeof(*chain), ADD);
+	if (!chain)
+		LOW_MEM("chain alloc failed!");
+	INIT_LIST_HEAD(&chain->head);
+	strncpy(chain->name, name, HIPAC_CHAIN_NAME_MAX_LEN);
+	chain->name[HIPAC_CHAIN_NAME_MAX_LEN - 1] = '\0';
+	chain->list_pos = list_pos;
+	chain->ref_count = 0;
+	chain->next_chains = NULL;
+	chain->paths = NULL;
+	chain->start = NULL;
+	chain->end = NULL;
+	chain->dimtree = NULL;
+	return HE_OK;
+}
+
+
+
+/* free hipac_chain 'chain' and recursively all other data 
+   structures that are pointed to from within this struct.
+   also free all rules in this chain.
+   attention: make sure 'chain' is NOT in the global 
+              'chain_hash' anymore!                                   */
+static inline void
+chain_free(struct hipac_chain* chain)
+{
+	struct list_head *lh;
+	struct chain_rule *rule;
+	
+	if (unlikely(!chain)){
+		ARG_MSG;
+		return;
+	}
+	
+	lh = chain->head.next;
+	while (lh != &chain->head) {
+		rule = list_entry(lh, struct chain_rule, head);
+		lh = lh->next;
+		list_del(lh->prev);
+		chain_rule_destroy(rule);
+	} 
+	if (chain->next_chains)
+		strblock_free(chain->next_chains);
+	if (chain->paths)
+		paths_free(chain->paths);
+	if (chain->start)
+		ptrblock_free(chain->start);
+	if (chain->end)
+		ptrblock_free(chain->end);
+       	hp_free(chain);
+}
+
+
+
+/* flush hipac_chain 'chain'
+   free all rules in this chain and all other data structures
+   that are pointed to from within this struct.                       */
+static inline void
+chain_flush(struct hipac_chain* chain)
+{
+	struct list_head *lh;
+	struct chain_rule *rule;
+	
+	if (unlikely(!chain)){
+		ARG_MSG;
+		return;
+	}
+	
+	lh = chain->head.next;
+	while (lh != &chain->head) {
+		rule = list_entry(lh, struct chain_rule, head);
+		lh = lh->next;
+		list_del(lh->prev);
+		chain_rule_destroy(rule);
+	}
+	if (chain->next_chains){
+		strblock_free(chain->next_chains);
+		chain->next_chains = NULL;
+	}
+	if (chain->paths){
+		paths_free(chain->paths);
+		chain->paths = NULL;
+	}
+	if (chain->start){
+		ptrblock_free(chain->start);
+		chain->start = NULL;
+	}
+	if (chain->end){
+		ptrblock_free(chain->end);
+		chain->end = NULL;
+	}
+	chain->ref_count = 0;
+	
+}
+
+
+
+/* insert chain_rule 'rule' into 'chain' at position rule->r.pos;
+   if chain is empty, rule->r.pos is set to 1;
+   if rule->r.pos is larger than maxpos, rule->r.pos is set to maxpos;
+   'do_inc': when not 0 the pos field of all rules with 
+             pos >= rule->r.pos is incremented by 1                   */
+static inline void
+chain_insert(struct hipac_chain* chain, struct chain_rule *rule,
+	     const __u8 do_inc)
+{
+	struct list_head *lh;
+	__u32 rulepos;
+	struct chain_rule *curule;
+	
+	if (unlikely(!chain || !rule)){
+		ARG_MSG;
+		return;
+	}
+
+	if (list_empty(&chain->head)) {
+		list_add(&rule->head, &chain->head);
+		rule->r.pos = 1;
+		return;
+	}
+
+	if (rule->r.pos == 0)
+		rule->r.pos = 1;
+
+	lh = chain->head.prev;
+	rulepos = rule->r.pos;
+	curule = list_entry(lh, struct chain_rule, head);
+	
+	if (rulepos > curule->r.pos) {
+		list_add_tail(&rule->head, &chain->head);
+		rule->r.pos = curule->r.pos + 1;
+		return;
+	}
+
+	if (do_inc) {
+		do {
+			curule->r.pos++;
+			lh = lh->prev;
+			curule = list_entry(lh, struct chain_rule, head);
+		} while (lh != &chain->head && curule->r.pos >= rulepos);
+	} else {
+		do {
+			lh = lh->prev;
+			curule = list_entry(lh, struct chain_rule, head);
+		} while (lh != &chain->head && curule->r.pos >= rulepos);
+	}
+
+	if (lh == &chain->head) {
+		assert(rulepos == 1);
+		assert(!do_inc || 
+		       list_entry(chain->head.next,
+				  struct chain_rule, head)->r.pos == 2);
+		assert(do_inc ||
+		       list_entry(chain->head.next,
+				  struct chain_rule, head)->r.pos == 1);
+		
+		list_add(&rule->head, &chain->head);
+	} else {
+		assert(curule->r.pos < rulepos);
+		assert(!do_inc ||
+		       list_entry(curule->head.next,
+				  struct chain_rule,
+				  head)->r.pos == rulepos + 1);
+		assert(do_inc ||
+		       list_entry(curule->head.next,
+				  struct chain_rule,
+				  head)->r.pos == rulepos);
+		
+		list_add(&rule->head, &curule->head);
+	}
+}
+
+
+
+/* delete and all rules in 'chain' with position == 'rulepos';
+   attention: you must NOT call chain_delete with an empty chain!
+              does not free the rules!                                */
+static void
+chain_delete(const struct hipac_chain* chain, const __u32 rulepos)
+{
+       	struct chain_rule *current_rule;
+	
+	if (unlikely(!chain)){
+		ARG_MSG;
+		return;
+	}
+	current_rule = list_entry(chain->head.prev, struct chain_rule, head);
+	
+	while (current_rule->r.pos > rulepos) {
+		current_rule->r.pos--;
+		current_rule = list_entry(current_rule->head.prev, 
+					  struct chain_rule, head);
+	}	
+       	list_del(&current_rule->head);
+}
+
+
+
+/* find rule in hipac_chain 'chain' that equals hipac_rule 'rule'.
+   possible errors: HE_RULE_NOT_EXISTENT, HE_IMPOSSIBLE_CONDITION     */
+static inline hipac_error
+chain_find_rule(const struct hipac_chain *chain, const struct hipac_rule *rule,
+		struct chain_rule **result)
+{
+	struct list_head *lh;
+	struct chain_rule *currule;
+
+	if (!chain || !rule || !result)
+		ARG_ERR;
+			
+	list_for_each(lh, &chain->head) {
+		currule = list_entry(lh, struct chain_rule, head);
+		if (eq_fn(rule, &currule->r)){
+			*result = currule;
+			return HE_OK;
+		}
+	}
+	return HE_RULE_NOT_EXISTENT;
+}
+
+
+
+/* find rule in hipac_chain 'chain' with position 'pos'
+   possible errors: HE_RULE_NOT_EXISTENT, HE_IMPOSSIBLE_CONDITION     */
+static inline hipac_error
+chain_find_rule_with_pos(const struct hipac_chain *chain, const __u32 pos,
+			 struct chain_rule **result)
+{
+	struct list_head *lh;
+	struct chain_rule *currule;
+
+	if (!chain || !result)
+		ARG_ERR;
+			
+	list_for_each(lh, &chain->head) {
+		currule = list_entry(lh, struct chain_rule, head);
+		if (currule->r.pos == pos){
+			*result = currule;
+			return HE_OK;
+		}
+	}
+	return HE_RULE_NOT_EXISTENT;
+}
+
+
+/* End of chain_* functions */
+
+
+
+
+
+
+/* build chain_rule 'result' from hipac_rule 'rule'.        
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+hipac_error
+build_chain_rule_from_hipac_rule(const struct hipac_rule *rule, 
+				 struct chain_rule **result)
+{
+	if (unlikely(!rule || !result))
+		ARG_ERR;
+
+	*result = hp_alloc(sizeof(**result) - sizeof(struct hipac_rule)
+	                   + rule->size, ADD);
+	if (!(*result))
+		LOW_MEM("chain_rule alloc failed!");
+	
+	(*result)->dtr = NULL;
+	copy_fn(rule, &(*result)->r);
+       	return HE_OK;
+}
+
+
+
+/* build hipac_rule 'result' from dt_rule 'dt_rule'.
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+hipac_error
+build_hipac_rule_from_dt_rule(const struct dt_rule *dt_rule,
+			      struct hipac_rule **result)
+{
+	__u32 size, exec_match_size = 0;
+	__u32 i;
+
+	if (unlikely(!dt_rule || !result))
+		ARG_ERR;
+
+	size = sizeof(**result) 
+		+ dt_rule->dt_match_len * sizeof(struct hipac_match)
+		+ dt_rule->exec_target_size;
+	
+	if (dt_rule->exec_match){
+		for (i = 0; i < dt_rule->exec_match->len; i += 2){ 
+			exec_match_size += (void *) 
+				dt_rule->exec_match->p[i + 1]
+				- dt_rule->exec_match->p[i];
+		}
+	}
+	size += exec_match_size;
+
+	*result = hp_alloc(size, ADD);
+	if (!(*result))
+		LOW_MEM("hipac_rule alloc failed!");
+	
+	(*result)->pos = dt_rule->spec.pos;
+	(*result)->size = size;
+	(*result)->origin = 0;
+	(*result)->action = dt_rule->spec.action;
+	(*result)->native_mct = dt_rule->dt_match_len;
+	if (dt_rule->exec_match)
+		(*result)->match_offset = sizeof(**result)
+			+ dt_rule->dt_match_len * sizeof(struct hipac_match);
+	else (*result)->match_offset = 0;
+	(*result)->target_offset = sizeof(**result)
+		+ dt_rule->dt_match_len * sizeof(struct hipac_match)
+		+ exec_match_size;
+	
+	for (i = 0; i < dt_rule->dt_match_len; i++){
+		(*result)->first_match[i].dimid =
+			dt_rule->first_dt_match[i].dimid;
+		(*result)->first_match[i].invert = 0;
+		(*result)->first_match[i].left =
+			dt_rule->first_dt_match[i].left;
+		(*result)->first_match[i].right =
+			dt_rule->first_dt_match[i].right;
+	}
+	if (dt_rule->exec_match){
+		void *pos = (void *) (*result) + (*result)->match_offset;
+		for (i = 0; i < dt_rule->exec_match->len; i += 2){ 
+			size = dt_rule->exec_match->p[i + 1]
+				- dt_rule->exec_match->p[i];
+			memcpy(pos, dt_rule->exec_match->p[i], size);
+			pos += size;
+		}
+	}
+	if (dt_rule->exec_target_size){
+		memcpy((void *) (*result) + (*result)->target_offset, 
+		       dt_rule->exec_target, dt_rule->exec_target_size);
+	}
+	return HE_OK;
+}
+
+
+
+/* if hipac_rule 'r' contains exec_matches, add a pointer to the beginning
+   and a pointer to the end of that exec_matches to the ptrblock '*p'  
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+static inline hipac_error
+add_exec_matches(struct ptrblock **p, const struct hipac_rule *r)
+{
+       	hipac_error error;
+
+	if (unlikely(!p || !r))
+		ARG_ERR;
+
+	if (r->match_offset == 0)
+		return HE_OK;
+	
+	if ((error = ptrblock_append(p, (void *) r + r->match_offset))){
+		CHECK_ERROR("ptrblock_append");
+		return error;
+	}
+	if ((error = ptrblock_append(p, (void *) r + r->target_offset))){
+		CHECK_ERROR("ptrblock_append");
+		ptrblock_delete_tail(p);
+		return error;
+	}
+	return HE_OK;
+}
+
+
+
+/* build new dt_rule from prefix_rule and/or hipac_rule.
+   prefix_rule and/or hipac_rule can be NULL.
+   pos:      the position of the new dt_rule; is written to result->spec.pos
+   action:   the action of the new dt_rule;   is written to result->spec.action
+   the exec_matches from prefix and hipac_rule are merged into
+   result->exec_match.
+   if the hipac_rule contains a exec_target it is written to 
+   result->exec_target.
+   attention: does NOT copy the native matches, this must be done externally!
+              allocs space for prefix->native_mct + rule->native_mct matches!
+              when merging the native matches externally, remember to do a
+              'hipac_realloc' when prefix and rule contain the same dimids!
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+static inline hipac_error
+build_dt_rule(struct prefix_rule *prefix, const struct hipac_rule *rule, 
+	      const __u32 pos, const __u32 action, struct dt_rule **result)
+{
+	hipac_error error;
+	struct dt_rule *new_dt_rule;
+	__u8 mct = 0;
+
+	if (unlikely(!result))
+		ARG_ERR;
+	
+	if (prefix)
+		mct += prefix->native_mct;
+	if (rule) 
+		mct += rule->native_mct;
+	
+	new_dt_rule = hp_alloc(dt_rule_size(mct), ADD);
+	if (!new_dt_rule)
+		LOW_MEM("dt_rule alloc failed!");
+	new_dt_rule->spec.rlp = 0;
+	new_dt_rule->spec.rtype = RT_RULE;
+	new_dt_rule->spec.action = action;
+	new_dt_rule->spec.pos = pos;
+	new_dt_rule->exec_match = NULL;
+	new_dt_rule->exec_target = NULL;
+	new_dt_rule->exec_target_size = 0;
+	new_dt_rule->deleted = 0;
+	
+	if (prefix){
+		if ((error = ptrblock_clone(prefix->exec_matches, 
+					    &new_dt_rule->exec_match))){
+			dt_rule_free(new_dt_rule);
+			CHECK_ERROR("ptrblock_clone");
+			return error;
+		}
+	}
+	if (rule){
+		if ((error = add_exec_matches(&new_dt_rule->exec_match,
+						  rule))){
+			dt_rule_free(new_dt_rule);
+			CHECK_ERROR("add_exec_matches");
+			return error;
+		}
+	}
+	if (action == TARGET_EXEC){
+		new_dt_rule->exec_target = (void *) rule + rule->target_offset;
+		new_dt_rule->exec_target_size = ((void *) rule + rule->size)
+			- ((void *) rule + rule->target_offset);
+	}
+	new_dt_rule->dt_match_len = mct;
+	*result = new_dt_rule;
+	return HE_OK;
+}
+
+
+
+/* Remove last element from strblock 'paths' and also free the data
+   structures that are pointed to from within this element            */
+static inline void
+paths_delete_tail(struct strblock **paths)
+{
+	struct prefix_rule *p = P_ELEM_RULE(*paths, (*paths)->len - 1);
+	if (p)
+		prefix_rule_free(p);
+	strblock_delete_tail(paths);
+}
+
+
+
+/* Remove element with position 'pos' from strblock 'paths' and also free
+   the data structures that are pointed to from within this element.  */
+static inline void
+paths_delete_pos(struct strblock **paths, __u32 pos)
+{
+	struct prefix_rule *p = P_ELEM_RULE(*paths, pos);
+	if (p)
+		prefix_rule_free(p);
+	strblock_delete_pos(paths, pos);
+}
+
+
+/* count number of negations/inverted matches in hipac_match array    */
+static inline __u8
+count_inv_matches(const struct hipac_match *first_match, const __u8 match_cnt)
+{
+	__u8 i, result = 0;
+	for (i = 0; i < match_cnt; i++)
+		if (first_match[i].invert)
+			result++;
+	return result;
+}	 
+
+
+
+/* count number of negations/inverted matches in both rules, but
+   without counting matches in the same dimid twice                   */
+static inline __u8
+count_inv_matches_2(const struct hipac_rule *hipac_rule, 
+		    const struct prefix_rule *prefix_rule)
+{
+	__u8 i, j, result = 0;
+
+	for (i = 0, j = 0; i < prefix_rule->native_mct; i++){
+			while ((j < hipac_rule->native_mct)
+			       && (hipac_rule->first_match[j].dimid 
+				   < prefix_rule->first_match[i].dimid)){
+				if (hipac_rule->first_match[j].invert)
+					result++;
+				j++;
+			}
+			if ((j < hipac_rule->native_mct)
+			    && (hipac_rule->first_match[j].dimid 
+				== prefix_rule->first_match[i].dimid)){
+				if (hipac_rule->first_match[j].invert)
+					result++;
+				j++;
+				continue;
+			}
+			if (prefix_rule->first_match[i].invert)
+				result++;
+	}
+	while (j < hipac_rule->native_mct){
+		if (hipac_rule->first_match[j].invert)
+			result++;	
+		j++;
+	}	
+	return result;
+}	 
+
+
+
+/* merge hipac_match 's' into dt_match 'new' while keeping negation
+   in mind.                                                           */
+static inline void
+merge_dimension(struct hipac_match *s, struct dt_match *new,
+		__u32 inv, __u16 *inv_match, __u8 *not_valid)
+{
+	if (!(s->invert)){
+		new->dimid = s->dimid;
+		new->left = s->left;
+		new->right = s->right;
+		return;
+	}
+	if (inv & (1 << *inv_match)){
+		if (s->right < 
+		    MAXKEY(dim2btype[s->dimid])){
+			new->dimid = s->dimid;
+			new->left = s->right + 1;
+			new->right = MAXKEY(dim2btype[s->dimid]);
+			(*inv_match)++;
+		} else {
+			*not_valid = 1;
+		}
+	} else {
+		if (s->left){	
+			new->dimid = s->dimid;
+			new->left = 0;
+			new->right = s->left - 1;
+			(*inv_match)++;
+		} else {
+			*not_valid = 1;
+		}
+	}
+}
+
+
+
+/* insert new dt_rule(s) at position 'pos' into dimtree 'path->dimtree'.
+   the new dt_rule is created from information found in 'path->rule'
+   and 'rule'. if 'path->rule' or 'rule' contain negation solve this by
+   adding several new dt_rules to the dimtree. append the (first) new 
+   dt_rule to the 'rule->dtr' pointer block.
+   if commit is not 0 commit the changes.
+   in case of an error undo all changes.
+   attention: in case of an error already inserted rules are not removed
+              from the internal dimtree chain. those rules have to be
+	      removed externally.  
+   possible errors: HE_LOW_MEMORY, HE_RULE_ORIGIN_MISMATCH,
+                    HE_RULE_PREFIX_MISMATCH, HE_IMPOSSIBLE_CONDITION  */
+hipac_error
+insert_into_dt(const struct path *path,
+	       struct chain_rule *rule,
+	       const __u32 pos, const __u8 commit)
+{
+	struct dt_rule *new_dt_rule;
+	hipac_error error;
+       	__u32 i, j, inv;
+	__u8 first = 1;
+	__u8 num;
+	struct dt_match *new;
+	__u32 mct = 0;
+	
+	if (unlikely(!path || !path->rule || !rule))
+		ARG_ERR;
+
+	num = count_inv_matches_2(&rule->r, path->rule);
+	
+	mct = rule->r.native_mct + path->rule->native_mct;
+	
+	if (!(num)){
+		__u32 new_mct = 0;
+		struct hipac_match *p = path->rule->first_match;
+		struct hipac_match *r = rule->r.first_match;
+		
+
+		if ((error = build_dt_rule(path->rule, &rule->r, pos, 
+					   rule->r.action, &new_dt_rule))){
+			CHECK_ERROR("build_dt_rule");
+			return error;
+		}
+
+		new = new_dt_rule->first_dt_match;
+
+		for (i = 0, j = 0; i < path->rule->native_mct; i++){
+			while ((j < rule->r.native_mct)
+			       && (r[j].dimid < p[i].dimid)){
+				new[new_mct].dimid = r[j].dimid;
+				new[new_mct].left = r[j].left;
+				new[new_mct].right = r[j].right;
+				j++;
+				new_mct++;
+				
+			}
+			if ((j < rule->r.native_mct)
+			    && (r[j].dimid == p[i].dimid)){
+				if (p[i].invert){
+					if (!(r[j].right < p[i].left
+					      || r[j].left > p[i].right)){
+						dt_rule_free(new_dt_rule);
+						return HE_RULE_PREFIX_MISMATCH;
+					}
+				} else if (r[j].left < p[i].left
+					   || r[j].right > p[i].right){
+					dt_rule_free(new_dt_rule);
+					return HE_RULE_PREFIX_MISMATCH;
+				}
+				new[new_mct].dimid = r[j].dimid;
+				new[new_mct].left = r[j].left;
+				new[new_mct].right = r[j].right;
+				j++;
+				new_mct++;
+				continue;
+			}
+			new[new_mct].dimid = p[i].dimid;
+			new[new_mct].left = p[i].left;
+			new[new_mct].right = p[i].right;
+			new_mct++;
+		}
+		
+		while (j < rule->r.native_mct){
+			new[new_mct].dimid = r[j].dimid;
+			new[new_mct].left = r[j].left;
+			new[new_mct].right = r[j].right;
+			j++;
+			new_mct++;
+		}
+  	
+		if (new_mct < mct){
+			new_dt_rule->dt_match_len = new_mct;
+			new_dt_rule = hp_realloc(new_dt_rule, 
+						 dt_rule_size(new_mct));
+			if (!new_dt_rule){
+				dt_rule_free(new_dt_rule);
+				IMPOSSIBLE_CONDITION("new_dt_rule is NULL");
+			}
+		}
+		
+		if ((error = ptrblock_append(&rule->dtr,
+					     (void *) new_dt_rule))){
+			CHECK_ERROR("ptrblock_append");
+			dt_rule_free(new_dt_rule);
+			return error;
+		}
+		if ((error = dimtree_insert(path->dimtree, new_dt_rule,
+					    rule->r.origin, INC, commit))){
+			CHECK_ERROR("dimtree_insert");
+			return error;
+		}
+		return HE_OK;
+	} 
+	//else we have a rule containing negation
+	
+       	for (inv = 0; inv < (1 << num); inv++){
+		__u16 j;
+		__u8 not_valid = 0;
+		__u16 inv_match = 0;
+		__u32 new_mct = 0;
+		struct hipac_match *p = path->rule->first_match;
+		struct hipac_match *r = rule->r.first_match;
+	
+		if ((error = build_dt_rule(path->rule, &rule->r, pos, 
+					   rule->r.action, &new_dt_rule))){
+			CHECK_ERROR("build_dt_rule");
+			if (!first)
+				dimtree_failed(native_dts);
+			return error;
+		}
+		
+		new = new_dt_rule->first_dt_match;
+
+		for (i = 0, j = 0; i < path->rule->native_mct; i++){
+			while ((j < rule->r.native_mct)
+			       && (r[j].dimid < p[i].dimid)){
+				merge_dimension(&r[j], &new[new_mct], inv, 
+						&inv_match, &not_valid);
+				if (not_valid)
+					break;
+				j++;
+				new_mct++;
+			}
+			if (not_valid)
+				break;
+			if ((j < rule->r.native_mct)
+			    && (r[j].dimid == p[i].dimid)){
+				if (!r[j].invert && !p[i].invert){
+					if (r[j].left < p[i].left
+					    || r[j].right > p[i].right){
+						dt_rule_free(new_dt_rule);
+						if (!first)
+							dimtree_failed(
+								native_dts);
+						return HE_RULE_PREFIX_MISMATCH;
+					}
+				} else if (r[j].invert && !p[i].invert){
+					dt_rule_free(new_dt_rule);
+					if (!first)
+						dimtree_failed(native_dts);
+					return HE_RULE_PREFIX_MISMATCH;
+				} else if (!r[j].invert && p[i].invert){
+					if (!(r[j].right < p[i].left
+					      || r[j].left > p[i].right)){
+						dt_rule_free(new_dt_rule);
+						if (!first)
+							dimtree_failed(
+								native_dts);
+						return HE_RULE_PREFIX_MISMATCH;
+					}
+				} else if(r[j].invert && p[i].invert){
+					if (r[j].left > p[i].left
+					    || r[j].right < p[i].right){
+						dt_rule_free(new_dt_rule);
+						if (!first)
+							dimtree_failed(
+								native_dts);
+						return HE_RULE_PREFIX_MISMATCH;
+					}
+				}
+
+				merge_dimension(&r[j], &new[new_mct], inv, 
+						&inv_match, &not_valid);
+				if (not_valid)
+					break;
+				j++;
+				new_mct++;
+				continue;
+				
+			}
+			merge_dimension(&p[i], &new[new_mct], inv, 
+					&inv_match, &not_valid);
+			if (not_valid)
+				break;
+			new_mct++;
+		}
+		if (not_valid){
+			dt_rule_free(new_dt_rule);
+			continue;
+		}
+		while (j < rule->r.native_mct){
+			merge_dimension(&r[j], &new[new_mct], inv, 
+					&inv_match, &not_valid);
+			if (not_valid)
+				break;
+			j++;
+			new_mct++;
+		}			
+		if (not_valid){
+			dt_rule_free(new_dt_rule);
+			continue;
+		}
+		
+		if (new_mct < mct){
+			new_dt_rule->dt_match_len = new_mct;
+			new_dt_rule = hp_realloc(new_dt_rule, 
+						 dt_rule_size(new_mct));
+			if (!new_dt_rule){
+				dt_rule_free(new_dt_rule);
+				IMPOSSIBLE_CONDITION("new_dt_rule is NULL");
+			}
+		}
+
+		if (first){
+			if ((error = ptrblock_append(&rule->dtr,
+						     (void *) new_dt_rule))){
+				CHECK_ERROR("ptrblock_append");
+				dt_rule_free(new_dt_rule);
+				return error;
+			}
+		}
+		if ((error = dimtree_insert(path->dimtree, new_dt_rule,
+					    rule->r.origin, first, 
+					    DONT_COMMIT))){
+			CHECK_ERROR("dimtree_insert");
+			return error;
+		}
+		if (first)
+			first = 0;
+	}	
+	if (commit)
+		dimtree_commit(native_dts);
+	return HE_OK;
+}	
+
+
+
+/* detect loop in hipac_chains.
+   if any rule in hipac_chain 'chain' (or recursively in any other
+   hipac_chain any rule in 'chain' jumps to) jumps to hipac_chain 'org'
+   a loop is detected.
+   possible errors: HE_LOOP_DETECTED, HE_REC_LIMIT                    */
+hipac_error
+detect_loop(const struct hipac_chain *chain, 
+	    const struct hipac_chain *org, __u32 depth)
+{
+	if (unlikely(!chain || !org))
+		ARG_ERR;
+	
+	if (depth > HIPAC_REC_LIMIT)
+		return HE_REC_LIMIT;
+
+	if (chain->next_chains){
+		__u32 i;
+		hipac_error error;
+		struct hipac_chain *next;
+		for (i = 0; i < chain->next_chains->len; i++){
+			next = STRBLOCK_ITH(chain->next_chains, i,
+					    struct next_chain_elem *)->chain;
+			if (next == org)
+				return HE_LOOP_DETECTED;
+			if ((error = detect_loop(next, org, depth + 1)))
+				return error;
+		}
+	}
+	return HE_OK;
+}
+
+
+
+/* add new path to the paths block of hipac_chain 'chain'.
+   the new path is computed from the path 'path' and the chain_rule 'rule'.
+   possible errors: HE_LOW_MEMORY, HE_RULE_PREFIX_MISMATCH, 
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+add_path(struct hipac_chain *chain, const struct path *path,
+	 struct chain_rule *rule)
+{
+	hipac_error error;
+	struct path *new_path;
+	struct prefix_rule *new_prefix;
+	struct hipac_match *r, *p, *new;
+	__u8 mct, i, j = 0, new_mct = 0;
+
+	if (!chain || !path || !path->rule || !rule)
+		ARG_ERR;
+	
+	mct = rule->r.native_mct + path->rule->native_mct;
+	
+	new_prefix = hp_alloc(sizeof(*new_prefix) 
+			      + mct * sizeof(struct hipac_match), ADD);
+	if (!new_prefix){
+		LOW_MEM("new_prefix alloc failed!");
+	}
+	new_path = hp_alloc(sizeof(*new_path), ADD);
+	if (!new_path){
+		hp_free(new_prefix);
+		LOW_MEM("new_path alloc failed!");
+	}
+
+	new_path->dimtree = path->dimtree;
+	new_path->prev = rule;
+	new_path->rule = new_prefix;
+	
+	new_prefix->origin = path->rule->origin & rule->r.origin;
+	new_prefix->exec_matches = NULL;
+	if ((error = ptrblock_clone(path->rule->exec_matches, 
+				    &new_prefix->exec_matches))){
+		CHECK_ERROR("ptrblock_clone");
+		path_free(new_path);
+		return error;
+	}
+	if ((error = add_exec_matches(&new_prefix->exec_matches, 
+				      &rule->r))){
+		CHECK_ERROR("add_exec_matches");
+		path_free(new_path);
+		return error;
+	}
+	r = rule->r.first_match;
+	p = path->rule->first_match;
+	new = new_prefix->first_match;
+	
+	for (i = 0; i < path->rule->native_mct; i++){
+		while ((j < rule->r.native_mct)
+		       && (r[j].dimid < p[i].dimid)){
+			new[new_mct].dimid = r[j].dimid;
+			new[new_mct].invert = r[j].invert;
+			new[new_mct].left = r[j].left;
+			new[new_mct].right = r[j].right;
+			j++;
+			new_mct++;
+		}
+		if ((j < rule->r.native_mct)
+		    && (r[j].dimid == p[i].dimid)){
+			if (!r[j].invert && !p[i].invert){
+				if (r[j].left < p[i].left
+				    || r[j].right > p[i].right){
+					path_free(new_path);
+					return HE_RULE_PREFIX_MISMATCH;
+				}
+			} else if (r[j].invert && !p[i].invert){
+				path_free(new_path);
+				return HE_RULE_PREFIX_MISMATCH;
+			} else if (!r[j].invert && p[i].invert){
+				if (!(r[j].right < p[i].left
+				      || r[j].left > p[i].right)){
+					path_free(new_path);
+					return HE_RULE_PREFIX_MISMATCH;
+				}
+			} else if(r[j].invert && p[i].invert){
+				if (r[j].left > p[i].left
+				    || r[j].right < p[i].right){
+					path_free(new_path);
+					return HE_RULE_PREFIX_MISMATCH;
+				}
+			}
+			
+			new[new_mct].dimid = r[j].dimid;
+			new[new_mct].invert = r[j].invert;
+			new[new_mct].left = r[j].left;
+			new[new_mct].right = r[j].right;
+			j++;
+			new_mct++;
+			continue;
+		}
+		new[new_mct].dimid = p[i].dimid;
+		new[new_mct].invert = p[i].invert;
+		new[new_mct].left = p[i].left;
+		new[new_mct].right = p[i].right;
+		new_mct++;
+	}
+
+	while (j < rule->r.native_mct){
+		new[new_mct].dimid = r[j].dimid;
+		new[new_mct].invert = r[j].invert;
+		new[new_mct].left = r[j].left;
+		new[new_mct].right = r[j].right;
+		j++;
+		new_mct++;
+	}
+	
+	if (new_mct < mct){
+		new_prefix = hp_realloc(new_prefix, sizeof(*new_prefix)
+					+ new_mct
+					* sizeof(struct hipac_match));
+		if (!new_prefix){
+			path_free(new_path);
+			IMPOSSIBLE_CONDITION("new_prefix is NULL");
+		}
+		new_path->rule = new_prefix;
+	}
+
+	new_prefix->native_mct = new_mct;
+	
+	if ((error = strblock_append_check(&chain->paths, new_path, 
+					   sizeof(*new_path)))){
+		CHECK_ERROR("strblock_append");
+		path_free(new_path);
+		return error;
+	}
+	hp_free(new_path);
+	return HE_OK;
+
+}
+
+
+
+/* add a dt_rule marking the beginning of the hipac_chain 'chain'
+   in the internal dimtree chain to 'path->dimtree' and add a pointer
+   to that new dt_rule to the 'chain->start' ptrblock.
+   the dt_rule is added with TARGET_DUMMY, so that it is not inserted
+   into the internal dimtree only into the internal dimtree chain.
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+static inline hipac_error
+add_chain_start(struct hipac_chain *chain, const struct path *path,
+		const __u32 pos)
+{
+	hipac_error error;
+	struct dt_rule *start;
+
+	if ((error = build_dt_rule(NULL, NULL, pos, 
+				   TARGET_DUMMY, &start))){
+		CHECK_ERROR("build_dt_rule");
+		return error;
+	}
+	if ((error = ptrblock_append(&chain->start, start))){
+		CHECK_ERROR("ptrblock_append");
+		dt_rule_free(start);
+		return error;
+	}
+	if ((error = dimtree_insert(path->dimtree, start, 
+				    ORIGIN_ALL, INC, DONT_COMMIT))){
+		CHECK_ERROR("dimtree_insert");
+		ptrblock_delete_tail(&chain->start);
+		dt_rule_free(start);
+		return error;
+	}
+	return HE_OK;
+}
+
+
+
+/* add a dt_rule marking the end of the hipac_chain 'chain'
+   in the internal dimtree chain to 'path->dimtree' and add a pointer
+   to that new dt_rule to the 'chain->end' ptrblock.
+   the dt_rule added to the internal dimtree corresponds to 'path->rule'.
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+hipac_error
+add_chain_end(struct hipac_chain *chain, struct path *path,
+	      const __u32 pos)
+{
+	struct dt_rule *new_dt_rule;
+	hipac_error error;
+       	__u32 i;
+	__u8 first = 1;
+	__u8 num;
+     	struct hipac_match *old;
+	struct dt_match *new;
+		
+		
+	num = count_inv_matches((struct hipac_match *) path->rule->first_match,
+				path->rule->native_mct);
+
+	if (!(num)){
+		if ((error = build_dt_rule(path->rule, NULL, pos, 
+					   TARGET_NONE, &new_dt_rule))){
+			CHECK_ERROR("build_dt_rule");
+			return error;
+		}
+		for (i = 0; i < path->rule->native_mct; i++){
+			new_dt_rule->first_dt_match[i].dimid = 
+				path->rule->first_match[i].dimid;
+			new_dt_rule->first_dt_match[i].left = 
+				path->rule->first_match[i].left;
+			new_dt_rule->first_dt_match[i].right = 
+				path->rule->first_match[i].right;
+		}
+		if ((error = ptrblock_append(&chain->end,
+					     (void *) new_dt_rule))){
+			CHECK_ERROR("ptrblock_append");
+			dt_rule_free(new_dt_rule);
+			return error;
+		}
+		if ((error = dimtree_insert(path->dimtree, new_dt_rule, 
+					    ORIGIN_ALL, INC, DONT_COMMIT))){
+			CHECK_ERROR("dimtree_insert");
+			return error;
+		}
+		return HE_OK;
+	} 
+	//else we have a rule containing negation
+	
+       	for (i = 0; i < (1 << num); i++){
+		__u16 j;
+		__u8 not_valid = 0;
+		__u16 inv_match = 0;
+	
+	
+		if ((error = build_dt_rule(path->rule, NULL, pos, 
+					   TARGET_NONE, &new_dt_rule))){
+			CHECK_ERROR("build_dt_rule");
+			if (!first)
+				dimtree_failed(native_dts);
+			return error;
+		}
+		old = path->rule->first_match;
+		new = new_dt_rule->first_dt_match;
+		for (j = 0; j < path->rule->native_mct; j++){
+			if (!(old[j].invert)){
+				new[j].dimid = old[j].dimid;
+				new[j].left = old[j].left;
+				new[j].right = old[j].right;
+				continue;
+			}
+			if (i & (1 << inv_match)){
+				if (old[j].right < 
+				    MAXKEY(dim2btype[old[j].dimid])){
+					new[j].dimid = old[j].dimid;
+					new[j].left = old[j].right + 1;
+					new[j].right = 
+						MAXKEY(dim2btype[new[j].dimid]);
+				} else {
+					not_valid = 1;
+					break;
+				}
+			} else {
+				if (old[j].left){
+					new[j].dimid = old[j].dimid;
+					new[j].left = 0;
+					new[j].right = old[j].left - 1;
+				} else {
+					not_valid = 1;
+					break;
+				}
+			}
+			inv_match++;
+		}
+		if (not_valid){
+			dt_rule_free(new_dt_rule);
+			continue;
+		}	
+		if (first){
+			if ((error = ptrblock_append(&chain->end,
+						     (void *) new_dt_rule))){
+				CHECK_ERROR("ptrblock_append");
+				dt_rule_free(new_dt_rule);
+				return error;
+			}
+		}
+		if ((error = dimtree_insert(path->dimtree, new_dt_rule,
+					    ORIGIN_ALL, first, DONT_COMMIT))){
+			CHECK_ERROR("dimtree_insert");
+			return error;
+		}
+		if (first)
+			first = 0;
+	}
+	return HE_OK;
+}
+
+
+
+/* add hipac_chain 'to' to the next_chain block of hipac_chain 'from'.
+   if 'from' already contains a reference to hipac_chain 'to' then the
+   corresponding count field is incremented by 1, otherwise a new
+   next_chain_elem with its count field set to 1 is added to the
+   next_chain block.
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+static inline hipac_error
+add_next_chain(struct hipac_chain *from, struct hipac_chain *to)
+{
+	hipac_error error;
+	struct next_chain_elem *nc;
+
+	if (from->next_chains){
+		__u32 i;
+		for (i = 0; i < from->next_chains->len; i++){
+			nc = STRBLOCK_ITH(from->next_chains, i,
+					  struct next_chain_elem *);
+			if (nc->chain == to){
+				nc->count++;
+				return HE_OK;
+			}
+		}
+	}
+
+	nc = hp_alloc(sizeof(*nc), ADD);
+	if (!nc)
+		LOW_MEM("next_chain alloc failed!");
+	nc->count = 1;
+	nc->chain = to;
+	error = strblock_append_check(&from->next_chains, nc, sizeof(*nc));
+	hp_free(nc);
+	CHECK_ERROR("strblock_append");
+     	return error;
+}
+
+
+
+/* remove one reference to hipac_chain 'to' from the next_chain block
+   of hipac_chain 'from'.                                             */
+static inline void
+delete_next_chain(struct hipac_chain *from, const struct hipac_chain *to)
+{
+     	struct next_chain_elem *nc;
+	
+	if (from->next_chains){
+		__u32 i;
+		for (i = 0; i < from->next_chains->len; i++){
+			nc = STRBLOCK_ITH(from->next_chains, i,
+					  struct next_chain_elem *);
+			if (nc->chain == to){
+				if (nc->count > 1){
+					nc->count--;
+				} else {
+					strblock_delete_pos(&from->next_chains,
+							    i);
+				}
+				break;
+			}
+		}
+	}
+}
+
+
+
+/* recursively insert jump rule 'rule' into hipac data structures
+   and dimtrees. in case of an error changes must be undone 
+   externally via delete_jump_from_hipac_layer(),
+   delete_dt_rules_from_dt_chains() and dimtree_chain_fix().
+   attention: in case of an success does NOT commit the changes.
+              don't forget to eventually commit the modifications
+	      externally via dimtree_commit().
+   possible errors: HE_LOW_MEMORY, HE_LOOP_DETECTED, HE_REC_LIMIT,
+                    HE_RULE_ORIGIN_MISMATCH, HE_RULE_RREFIX_MISMATCH,
+		    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+insert_jump_rec(const struct hipac_chain *org, const struct ptrblock *next,
+		const struct path *path, const __u32 ins_num,
+		struct chain_rule *rule, __u32 depth)
+{
+	hipac_error error;
+	struct list_head *lh;
+	struct chain_rule *currule;
+	struct path *new_path;
+	struct hipac_chain *chain = NULL;
+	__u32 i;
+       
+	if (depth > HIPAC_REC_LIMIT)
+		return HE_REC_LIMIT;
+
+	chain_hash_lookup((void *) &rule->r + rule->r.target_offset, &chain);
+		
+	if (org == chain)
+		return HE_LOOP_DETECTED;
+	
+	for (i = 0; i < ins_num; i++){
+		if ((error = add_path(chain, path + i, rule))){
+			CHECK_ERROR("add_path");
+			for (; i > 0; i--){
+				paths_delete_tail(&chain->paths);
+				ptrblock_delete_tail(&chain->start);
+			}
+			return error;
+		}
+		if ((error = add_chain_start(chain, 
+					     P_ELEM(chain->paths,
+						    chain->paths->len - 1),
+					     ((struct dt_rule *)
+					      next->p[i])->spec.pos))){
+			CHECK_ERROR("add_chain_start");
+			paths_delete_tail(&chain->paths);
+			for (; i > 0; i--){
+				paths_delete_tail(&chain->paths);
+				ptrblock_delete_tail(&chain->start);
+			}
+			return error;
+		}
+	}
+
+	new_path = P_ELEM(chain->paths, chain->paths->len - ins_num);
+		
+	list_for_each(lh, &chain->head){
+		currule = list_entry(lh, struct chain_rule, head);
+		if (IS_JUMP_RULE(currule)){
+			if ((error = insert_jump_rec(org, next,
+						     new_path, ins_num,
+						     currule, depth + 1))){
+				CHECK_ERROR("insert_jump_rec");
+				return error;
+			}
+		} else for (i = 0; i < ins_num; i++){
+				if ((error = insert_into_dt(new_path + i, currule,
+							    ((struct dt_rule *)
+							     next->p[i])->spec.pos, 
+							    DONT_COMMIT))){
+					CHECK_ERROR("insert_into_dt");
+					return error;
+				}
+		}
+	}   
+	for (i = 0; i < ins_num; i++){
+		if ((error = add_chain_end(chain, new_path + i, 
+					   ((struct dt_rule *) 
+					    next->p[i])->spec.pos))){
+			CHECK_ERROR("add_chain_end");
+			return error;
+		}
+	}
+		
+	return HE_OK;
+}	
+
+
+
+/* delete all entries in the hipac layer data structures corresponding to
+   jump rule 'rule'. all entries in hipac_chain path, start and end blocks
+   pointing to dt_rules with positions > prev and < next are deleted.
+   attention: be sure that those rules have been deleted from the dimtrees
+              before and that those changes have been commited. there must NOT
+	      be any intervall in any dimtree anymore pointing to one of those
+	      rules! BUT the corresponding dt_rules must NOT yet have been
+	      deleted from the internal dimtree chains!               */
+static void
+delete_jump_from_hipac_layer(const struct hipac_chain *org,
+			     const struct ptrblock *prev, 
+			     const struct ptrblock *next,
+			     const struct chain_rule *rule)
+{
+	struct list_head *lh;
+	struct hipac_chain *chain = NULL;
+	struct chain_rule *currule;
+	__u32 i, j , finished = 0, del_num = 0;
+	
+	chain_hash_lookup((void *) &rule->r + rule->r.target_offset, 
+			  &chain);
+	
+	if (!chain->start)
+		return;
+	
+	for (i = chain->start->len; i > 0; i--){
+		for (j = 0; j < prev->len; j++){
+			if (!chain->paths){
+				finished = 1;
+				break;
+			}
+			if ((P_ELEM_DIMTREE(chain->paths, i - 1)
+			     == P_ELEM_DIMTREE(org->paths, j))
+			    && (((struct dt_rule *)
+				 chain->start->p[i - 1])->spec.pos
+				> ((struct dt_rule *) prev->p[j])->spec.pos)
+			    && (((struct dt_rule *) 
+				 chain->start->p[i - 1])->spec.pos
+				< ((struct dt_rule *) next->p[j])->spec.pos)){
+				
+				chain->start->p[i - 1] = NULL;
+				paths_delete_pos(&chain->paths, i - 1);
+				del_num++;
+				break;
+			}
+		}
+		if (finished)
+			break;
+	}
+
+	if (!del_num)
+		return;
+	
+	ptrblock_delete_multi(&chain->end, chain->start);
+		
+	list_for_each(lh, &chain->head){
+		currule = list_entry(lh, 
+				     struct chain_rule, head);
+		if (IS_JUMP_RULE(currule)){
+			delete_jump_from_hipac_layer(org, prev, next, currule);
+		} else {
+			if (!currule->dtr)
+				break;
+			if (chain->end
+			    && chain->end->len == currule->dtr->len)
+				break;
+			ptrblock_delete_multi(&currule->dtr, chain->start);
+		}
+	}		
+	
+	for (i = chain->start->len; i > 0; i--){
+		if (!chain->start->p[i - 1])
+			ptrblock_delete_pos(&chain->start, i - 1);
+	}
+}							   
+							   
+
+      
+/* delete all dt_rules between prev and next from the internal dimtrees.
+   all rules with positions > prev and < next are deleted.
+   in case of an error undo all made changes.
+   attention: does NOT commit the changes. don't forget to eventually commit
+              the modifications externally via dimtree_commit().
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+static inline hipac_error
+delete_dt_rules_from_dimtrees(const struct hipac_chain *chain,
+			      const struct ptrblock *prev,
+			      const struct ptrblock *next)
+{
+	hipac_error error;
+	__u32 i;
+	struct dt_rule *rule;
+	
+	if (!chain || !prev || !next)
+		ARG_ERR;
+	
+	for (i = 0; i < prev->len; i++){
+		rule = list_entry(((struct dt_rule *) prev->p[i])->head.next, 
+				  struct dt_rule, head);
+		
+		while (rule->spec.pos == 
+		       ((struct dt_rule *) prev->p[i])->spec.pos){
+			rule = list_entry(rule->head.next, 
+					  struct dt_rule, head);
+		}
+		while (rule != ((struct dt_rule *) next->p[i])){
+			if ((error = dimtree_delete(P_ELEM_DIMTREE(chain->paths,
+								   i),
+						    rule, DONT_COMMIT))){
+				CHECK_ERROR("dimtree_delete");
+				return error;
+			}
+			rule = list_entry(rule->head.next,
+					  struct dt_rule, head);
+		}
+	}
+	return HE_OK;
+}
+
+
+
+/* delete all dt_rules between prev and next from the internal dimtree chains.
+   all rules with positions > prev and < next are deleted.
+   attention: be sure that those rules have been deleted from the dimtrees
+              before and that those changes have been commited. there must NOT
+	      be any intervall in any dimtree anymore pointing to one of those
+	      rules!                                                  */
+static inline void
+delete_dt_rules_from_dt_chains(const struct hipac_chain *chain,
+			       const struct ptrblock *prev,
+			       const struct ptrblock *next)
+{
+	__u32 i, end_pos;
+	struct dt_rule *start;
+	
+	if (!chain || !prev || !next)
+		ARG_MSG;
+	
+	for (i = 0; i < prev->len; i++){
+		end_pos = ((struct dt_rule *) next->p[i])->spec.pos - 1;
+		if (((struct dt_rule *) prev->p[i])->spec.pos == end_pos){
+			continue;
+		}
+		start = list_entry(((struct dt_rule *) prev->p[i])->head.next, 
+				   struct dt_rule, head);
+		while (start->spec.pos == 
+		       ((struct dt_rule *) prev->p[i])->spec.pos){
+			start = list_entry(start->head.next, 
+					   struct dt_rule, head);
+		}
+		dimtree_chain_delete(P_ELEM_DIMTREE(chain->paths, i), start, 
+				     end_pos);
+	}
+}
+
+
+
+/* insert chain_rule 'rule' into hipac_chain 'chain' and 
+   commit the changes. in case of an error undo all made changes.
+   possible errors: HE_LOW_MEMORY, HE_LOOP_DETECTED, HE_REC_LIMIT,
+                    HE_RULE_ORIGIN_MISMATCH, HE_RULE_PREFIX_MISMATCH,
+		    HE_TARGET_CHAIN_IS_NATIVE, 
+		    HE_TARGET_CHAIN_NOT_EXISTENT,
+		    HE_IMPOSSIBLE_CONDITION                           */
+static inline hipac_error
+insert(struct hipac_chain *chain, struct chain_rule *rule)
+{
+     	hipac_error error;
+	struct ptrblock *prev, *next;
+	__u8 prev_free, next_free;
+	
+	if (CHAIN_NOT_CONNECTED(chain)){
+		if (IS_JUMP_RULE(rule)){
+			struct hipac_chain *target_chain;
+			if ((error = chain_hash_lookup((void *) &rule->r 
+						       + rule->r.target_offset,
+						       &target_chain))){
+				chain_rule_free(rule);
+				return HE_TARGET_CHAIN_NOT_EXISTENT;
+			}
+			if (target_chain == chain){
+				chain_rule_free(rule);
+				return HE_LOOP_DETECTED;
+			}
+			if (IS_ROOT_CHAIN(target_chain)){
+				chain_rule_free(rule);
+				return HE_TARGET_CHAIN_IS_NATIVE;
+			}
+			if ((error = detect_loop(target_chain, chain, 1))){
+				chain_rule_free(rule);
+				return error;
+			}
+			if ((error = add_next_chain(chain, target_chain))){
+				chain_rule_free(rule);
+				return error;
+			}
+			target_chain->ref_count++;
+		}
+		chain_insert(chain, rule, INC);
+		return HE_OK;
+	}
+
+	chain_insert(chain, rule, INC);
+	if ((error = get_prev_dt_rules(chain, rule, &prev_free, &prev))){
+		CHECK_ERROR("get_prev_dt_rules");
+		chain_delete(chain, rule->r.pos);
+		chain_rule_free(rule);
+		return error;
+	}
+	if ((error = get_next_dt_rules(chain, rule, &next_free, &next))){
+		CHECK_ERROR("get_next_dt_rules");
+		chain_delete(chain, rule->r.pos);
+		chain_rule_free(rule);
+		if (prev_free)
+			ptrblock_free(prev);
+		return error;
+	}
+
+
+	if (likely(IS_NOT_JUMP_RULE(rule))){
+		__u32 i;
+		__u8 commit = DONT_COMMIT;
+		if (next->len == 1)
+			commit = COMMIT;
+		for (i = 0; i < next->len; i++){
+			if ((error = 
+			     insert_into_dt(P_ELEM(chain->paths, i), rule,
+					    ((struct dt_rule *) 
+					     next->p[i])->spec.pos, commit))){
+				CHECK_ERROR("insert_into_dt");
+				dimtree_failed(native_dts);
+				delete_dt_rules_from_dt_chains(chain, 
+							       prev, next);
+				dimtree_chain_fix(native_dts);
+				chain_delete(chain, rule->r.pos);
+				chain_rule_free(rule);
+				if (prev_free)
+					ptrblock_free(prev);
+				if (next_free)
+					ptrblock_free(next);
+				return error;
+			}
+		}
+		if (!commit)
+			dimtree_commit(native_dts);
+	} else {
+		struct hipac_chain *target_chain;
+		if ((error = chain_hash_lookup((void *) &rule->r 
+					       + rule->r.target_offset,
+					       &target_chain))){
+			CHECK_ERROR("chain_hash_lookup");
+			chain_delete(chain, rule->r.pos);
+			chain_rule_free(rule);
+			if (prev_free)
+				ptrblock_free(prev);
+			if (next_free)
+				ptrblock_free(next);
+			return HE_TARGET_CHAIN_NOT_EXISTENT;
+		}
+		if (target_chain == chain){
+			chain_delete(chain, rule->r.pos);
+			chain_rule_free(rule);
+			if (prev_free)
+				ptrblock_free(prev);
+			if (next_free)
+				ptrblock_free(next);
+			return HE_LOOP_DETECTED;
+		}
+		if (IS_ROOT_CHAIN(target_chain)){
+			chain_delete(chain, rule->r.pos);
+			chain_rule_free(rule);
+			if (prev_free)
+				ptrblock_free(prev);
+			if (next_free)
+				ptrblock_free(next);
+			return HE_TARGET_CHAIN_IS_NATIVE;
+		}
+		if ((error = add_next_chain(chain, target_chain))){
+			CHECK_ERROR("add_next_chain");
+			chain_delete(chain, rule->r.pos);
+			chain_rule_free(rule);
+			if (prev_free)
+				ptrblock_free(prev);
+			if (next_free)
+				ptrblock_free(next);
+			return error;
+		}
+		if ((error = insert_jump_rec(chain, next, 
+					     P_ELEM(chain->paths, 0),
+					     chain->paths->len, rule, 1))){
+			CHECK_ERROR("insert_jump_rec");
+			dimtree_failed(native_dts);
+			delete_jump_from_hipac_layer(chain, prev, next, rule);
+			delete_dt_rules_from_dt_chains(chain, prev, next);
+			dimtree_chain_fix(native_dts);
+			delete_next_chain(chain, target_chain);
+			chain_delete(chain, rule->r.pos);
+			chain_rule_free(rule);
+			if (prev_free)
+				ptrblock_free(prev);
+			if (next_free)
+				ptrblock_free(next);
+			return error;
+		}
+		dimtree_commit(native_dts);
+		target_chain->ref_count++;
+	}
+      	if (prev_free)
+		ptrblock_free(prev);
+	if (next_free)
+		ptrblock_free(next);
+	return HE_OK;
+}
+
+
+
+/* delete chain_rule 'rule' from hipac_chain 'chain' and commit
+   the changes. all representations of that rule in the internal 
+   dimtrees are removed. 
+   in case of an error undo all made changes.
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */
+static inline hipac_error
+delete(struct hipac_chain* chain, struct chain_rule* rule)
+{
+	hipac_error error;
+	__u8 inv;
+	
+	if (unlikely(CHAIN_NOT_CONNECTED(chain))){
+		if (unlikely(IS_JUMP_RULE(rule))){
+			struct hipac_chain *target_chain = NULL;
+			chain_hash_lookup((void *) &rule->r 
+					  + rule->r.target_offset,
+					  &target_chain);
+			delete_next_chain(chain, target_chain);
+			target_chain->ref_count--;
+		}
+		chain_delete(chain, rule->r.pos);
+		chain_rule_destroy(rule);
+		return HE_OK;
+	}
+	
+	inv = count_inv_matches(rule->r.first_match, 
+				rule->r.native_mct);
+	
+	if (likely(!inv && IS_NOT_JUMP_RULE(rule))){
+      		__u32 i;
+		__u8 commit = 0;
+		if (rule->dtr->len == 1){
+			commit = 1;
+		}
+		for (i = 0; i < rule->dtr->len; i++){
+			if ((error = 
+			     dimtree_delete(P_ELEM_DIMTREE(chain->paths, i),
+					    (struct dt_rule *) rule->dtr->p[i],
+					    commit))){
+				CHECK_ERROR("dimtree_delete");
+				if (!commit)
+					dimtree_failed(native_dts);
+				return error;
+			}
+		}
+		if (!commit)
+			dimtree_commit(native_dts);
+		for (i = 0; i < rule->dtr->len; i++){
+			dimtree_chain_delete(P_ELEM_DIMTREE(chain->paths, i),
+					     (struct dt_rule *) rule->dtr->p[i],
+					     ((struct dt_rule *) 
+					      rule->dtr->p[i])->spec.pos);
+		}
+	} else {
+		struct ptrblock *prev, *next;
+		__u8 prev_free, next_free;
+		
+		if ((error = get_prev_dt_rules(chain, rule, 
+					       &prev_free, &prev))){
+			CHECK_ERROR("get_prev_dt_rules");
+			return error;
+		}
+		if ((error = get_next_dt_rules(chain, rule, 
+					       &next_free, &next))){
+			CHECK_ERROR("get_next_dt_rules");
+			if (prev_free)
+				ptrblock_free(prev);
+			return error;
+		}
+		if ((error = delete_dt_rules_from_dimtrees(chain, 
+							   prev, next))){
+			CHECK_ERROR("delete_dt_rules_from_dimtrees");
+			dimtree_failed(native_dts);
+			if (prev_free)
+				ptrblock_free(prev);
+			if (next_free)
+				ptrblock_free(next);
+			return error;
+		}
+		dimtree_commit(native_dts);
+		if (unlikely(IS_JUMP_RULE(rule))){
+			struct hipac_chain *target_chain = NULL;
+			chain_hash_lookup((void *) &rule->r + rule->r.target_offset,
+					  &target_chain);
+			delete_next_chain(chain, target_chain);
+			target_chain->ref_count--;
+			delete_jump_from_hipac_layer(chain, prev, next, rule);
+		}
+		delete_dt_rules_from_dt_chains(chain, prev, next);
+		if (prev_free)
+			ptrblock_free(prev);
+		if (next_free)
+			ptrblock_free(next);
+	}
+	dimtree_chain_fix(native_dts);
+	chain_delete(chain, rule->r.pos);
+	chain_rule_destroy(rule);
+	return HE_OK;
+}
+
+
+
+/* replace chain_rule 'old_rule' in hipac_chain 'chain' with 
+   chain_rule 'new_rule' and commit the changes.
+   in case of an error undo all made changes.
+   possible errors: HE_LOW_MEMORY, HE_LOOP_DETECTED, HE_REC_LIMIT,
+                    HE_RULE_ORIGIN_MISMATCH, HE_RULE_PREFIX_MISMATCH,
+		    HE_TARGET_CHAIN_IS_NATIVE,
+                    HE_TARGET_CHAIN_NOT_EXISTENT,
+		    HE_IMPOSSIBLE_CONDITION                           */
+static inline hipac_error
+replace(struct hipac_chain *chain, struct chain_rule *old_rule,
+	struct chain_rule *new_rule)
+{
+     	hipac_error error;
+	struct ptrblock *prev_old, *prev_new, *next_old, *next_new;
+	__u8 prev_free_old, prev_free_new, next_free_old, next_free_new;
+	struct hipac_chain *target_chain = NULL;
+	
+	if (CHAIN_NOT_CONNECTED(chain)){
+		if (IS_JUMP_RULE(new_rule)){
+			if ((error = 
+			     chain_hash_lookup((void *) &new_rule->r 
+					       + new_rule->r.target_offset,
+					       &target_chain))){
+				chain_rule_free(new_rule);
+				return HE_TARGET_CHAIN_NOT_EXISTENT;
+			}
+			if (target_chain == chain){
+				chain_rule_free(new_rule);
+				return HE_LOOP_DETECTED;
+			}
+			if (IS_ROOT_CHAIN(target_chain)){
+				chain_rule_free(new_rule);
+				return HE_TARGET_CHAIN_IS_NATIVE;
+			}
+			if ((error = detect_loop(target_chain, chain, 1))){
+				chain_rule_free(new_rule);
+				return error;
+			}
+			if ((error = add_next_chain(chain, target_chain))){
+				chain_rule_free(new_rule);
+				return error;
+			}
+			target_chain->ref_count++;
+		}
+		if (IS_JUMP_RULE(old_rule)){
+			chain_hash_lookup((void *) &old_rule->r 
+					  + old_rule->r.target_offset,
+					  &target_chain);
+			delete_next_chain(chain, target_chain);
+			target_chain->ref_count--;
+		}
+		chain_delete(chain, old_rule->r.pos);
+		chain_rule_destroy(old_rule);
+		chain_insert(chain, new_rule, INC);
+		return HE_OK;
+	}
+
+	if ((error = get_prev_dt_rules(chain, old_rule, 
+				       &prev_free_new, &prev_new))){
+		CHECK_ERROR("get_prev_dt_rules");
+		chain_rule_free(new_rule);
+		return error;
+	}
+	if ((error = get_next_dt_rules(chain, old_rule, 
+				       &next_free_old, &next_old))){
+		CHECK_ERROR("get_next_dt_rules");
+		chain_rule_free(new_rule);
+		if (prev_free_new)
+			ptrblock_free(prev_new);
+		return error;
+	}
+	if ((error = delete_dt_rules_from_dimtrees(chain, 
+						   prev_new, next_old))){
+		CHECK_ERROR("delete_dt_rules_from_dimtrees");
+		dimtree_failed(native_dts);
+		chain_rule_free(new_rule);
+		if (prev_free_new)
+			ptrblock_free(prev_new);
+		if (next_free_old)
+			ptrblock_free(next_old);
+		return error;
+	}
+	
+	chain_insert(chain, new_rule, INC);
+	
+	if ((error = get_next_dt_rules(chain, new_rule, 
+				       &next_free_new, &next_new))){
+		CHECK_ERROR("get_next_dt_rules");
+		chain_delete(chain, new_rule->r.pos);
+		chain_rule_free(new_rule);
+		dimtree_failed(native_dts);
+		if (prev_free_new)
+			ptrblock_free(prev_new);
+		if (next_free_old)
+			ptrblock_free(next_old);
+		return error;
+	}	
+
+	if (likely(IS_NOT_JUMP_RULE(new_rule))){
+		__u32 i;
+		for (i = 0; i < next_new->len; i++){
+			if ((error = insert_into_dt(P_ELEM(chain->paths, i),
+						    new_rule, 
+						    ((struct dt_rule *)
+						     next_new->p[i])->spec.pos, 
+						    DONT_COMMIT))){
+				CHECK_ERROR("insert_into_dt");
+				dimtree_failed(native_dts);
+				delete_dt_rules_from_dt_chains(chain, 
+							       prev_new, 
+							       next_new);
+				dimtree_chain_fix(native_dts);
+				chain_delete(chain, new_rule->r.pos);
+				chain_rule_free(new_rule);
+				if (prev_free_new)
+					ptrblock_free(prev_new);
+				if (next_free_old)
+					ptrblock_free(next_old);
+				if (next_free_new)
+					ptrblock_free(next_new);
+				return error;
+			}
+		}
+		if ((error = get_prev_dt_rules(chain, old_rule, 
+				       &prev_free_old, &prev_old))){
+			CHECK_ERROR("get_prev_dt_rules");
+			dimtree_failed(native_dts);
+			delete_dt_rules_from_dt_chains(chain, prev_new, 
+						       next_new);
+			dimtree_chain_fix(native_dts);
+			chain_delete(chain, new_rule->r.pos);
+			chain_rule_free(new_rule);
+			if (prev_free_new)
+				ptrblock_free(prev_new);
+			if (next_free_old)
+				ptrblock_free(next_old);
+			if (next_free_new)
+				ptrblock_free(next_new);
+			return error;
+		}
+	} else {
+		if ((error = chain_hash_lookup((void *) &new_rule->r 
+					       + new_rule->r.target_offset,
+					       &target_chain))){
+			CHECK_ERROR("chain_hash_lookup");
+			chain_delete(chain, new_rule->r.pos);
+			chain_rule_free(new_rule);
+			dimtree_failed(native_dts);
+			if (prev_free_new)
+				ptrblock_free(prev_new);
+			if (next_free_old)
+				ptrblock_free(next_old);
+			if (next_free_new)
+				ptrblock_free(next_new);
+			return HE_TARGET_CHAIN_NOT_EXISTENT;
+		}
+		if (target_chain == chain){
+			chain_delete(chain, new_rule->r.pos);
+			chain_rule_free(new_rule);
+			dimtree_failed(native_dts);
+			if (prev_free_new)
+				ptrblock_free(prev_new);
+			if (next_free_old)
+				ptrblock_free(next_old);
+			if (next_free_new)
+				ptrblock_free(next_new);
+			return HE_LOOP_DETECTED;
+		}
+		if (IS_ROOT_CHAIN(target_chain)){
+			chain_delete(chain, new_rule->r.pos);
+			chain_rule_free(new_rule);
+			dimtree_failed(native_dts);
+			if (prev_free_new)
+				ptrblock_free(prev_new);
+			if (next_free_old)
+				ptrblock_free(next_old);
+			if (next_free_new)
+				ptrblock_free(next_new);
+			return HE_TARGET_CHAIN_IS_NATIVE;
+		}
+		if ((error = add_next_chain(chain, target_chain))){
+			CHECK_ERROR("add_next_chain");
+			chain_delete(chain, new_rule->r.pos);
+			chain_rule_free(new_rule);
+			dimtree_failed(native_dts);
+			if (prev_free_new)
+				ptrblock_free(prev_new);
+			if (next_free_old)
+				ptrblock_free(next_old);
+			if (next_free_new)
+				ptrblock_free(next_new);
+			return error;
+		}
+		if ((error = insert_jump_rec(chain, next_new, 
+					     P_ELEM(chain->paths, 0),
+					     chain->paths->len, new_rule, 1))){
+			CHECK_ERROR("insert_jump_rec");
+			dimtree_failed(native_dts);
+			delete_jump_from_hipac_layer(chain, prev_new, 
+						     next_new, new_rule);
+			delete_dt_rules_from_dt_chains(chain, prev_new, 
+						       next_new);
+			dimtree_chain_fix(native_dts);
+			delete_next_chain(chain, target_chain);
+			chain_delete(chain, new_rule->r.pos);
+			chain_rule_free(new_rule);
+			if (prev_free_new)
+				ptrblock_free(prev_new);
+			if (next_free_old)
+				ptrblock_free(next_old);
+			if (next_free_new)
+				ptrblock_free(next_new);
+			return error;
+		}
+		if ((error = get_prev_dt_rules(chain, old_rule, 
+					       &prev_free_old, &prev_old))){
+			CHECK_ERROR("get_prev_dt_rules");
+			dimtree_failed(native_dts);
+			delete_jump_from_hipac_layer(chain, prev_new, 
+						     next_new, new_rule);
+			delete_dt_rules_from_dt_chains(chain, prev_new, 
+						       next_new);
+			dimtree_chain_fix(native_dts);
+			delete_next_chain(chain, target_chain);
+			chain_delete(chain, new_rule->r.pos);
+			chain_rule_free(new_rule);
+			if (prev_free_new)
+				ptrblock_free(prev_new);
+			if (next_free_old)
+				ptrblock_free(next_old);
+			if (next_free_new)
+				ptrblock_free(next_new);
+			return error;
+		}
+		target_chain->ref_count++;
+	}
+	dimtree_commit(native_dts);
+	
+	if (likely(IS_JUMP_RULE(old_rule))){
+		chain_hash_lookup((void *) &old_rule->r 
+				  + old_rule->r.target_offset,
+				  &target_chain);
+		delete_next_chain(chain, target_chain);
+		target_chain->ref_count--;
+		delete_jump_from_hipac_layer(chain, prev_old, next_old, 
+					     old_rule);
+	}
+	delete_dt_rules_from_dt_chains(chain, prev_old, next_old);
+	dimtree_chain_fix(native_dts);
+	chain_delete(chain, old_rule->r.pos);
+	chain_rule_destroy(old_rule);
+	if (prev_free_old)
+		ptrblock_free(prev_old);
+	if (prev_free_new)
+		ptrblock_free(prev_new);
+	if (next_free_old)
+		ptrblock_free(next_old);
+	if (next_free_new)
+		ptrblock_free(next_new);
+	return HE_OK;
+}
+
+
+
+
+
+/*
+ * hipac_* functions
+ */
+
+
+/* init hipac data structures;
+   MUST be called once at the beginning in order to let the other
+   operations work properly!
+   dimid_to_bittype: assigns dimids to bit types.
+                     i-th element of the array contains the bit type
+		     of dimension id i
+   extract:          functions to extract certain fields from a packet. 
+                     the function at position i of the array returns
+		     the entry in a packet that corresponds to 
+		     dimension id i (i.e. the source ip of the packet)
+   len:              length of the dim2btype and extract array
+   copycon:          constructor function
+   destroy:          destructor function
+   match:            match executor function
+   target:           target executor function
+   eq:               equality function to compare rules
+   maxmem:           maximum allowed memory consumption  
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */  
+hipac_error
+hipac_init(const __u8 dimid_to_bittype[], const hipac_extract_t extract[],
+	   const __u8 len, hipac_copy_constructor_t copycon,
+	   hipac_destroy_exec_t destroy, hipac_match_exec_t match,
+	   hipac_target_exec_t target, hipac_eq_exec_t eq, 
+	   const __u64 maxmem)
+{
+        
+	if (unlikely(!dimid_to_bittype || !extract || !copycon || !destroy ||
+		     !match || !target || !eq ))
+	ARG_ERR;
+	
+	mem_max = maxmem;
+	d2blen = len;
+	current_chain = NULL;
+	chain_hash = NULL;
+	native_dts = NULL;
+	dim2btype = hp_alloc(len, ADD);
+	if (!dim2btype)
+		LOW_MEM("dim2btype alloc failed!");
+	extract_fn = hp_alloc(len * sizeof(void *), ADD);
+	if (!extract_fn){
+		hp_free(dim2btype);
+		LOW_MEM("extract_fn alloc failed!");
+	}
+	chain_hash = ihash_new(CHAIN_HASH_LEN, ADD, CHAIN_HASH_AVR_BUCKET,
+			       ihash_func_str, eq_str);
+	if (!chain_hash){
+		hp_free(dim2btype);
+		hp_free(extract_fn);
+		LOW_MEM("ihash_new failed!");
+	}
+	memcpy(dim2btype, dimid_to_bittype, len);
+	memcpy(extract_fn, extract, len * sizeof(void *));
+	copy_fn = copycon;
+	destroy_fn = destroy;
+	match_fn = match;
+	target_fn = target;
+	eq_fn = eq;
+	return HE_OK;
+}
+
+
+
+/* free all hipac data structures;
+   MUST be called once in the end
+   attention: make sure there are no external accesses to hipac
+              data structures taking place anymore!                   */
+void
+hipac_exit(void)
+{
+	if (native_dts){
+		__u8 i;
+		for(i = 0; i < native_dts->len; i++){
+			dimtree_free((struct dimtree*) native_dts->p[i]);
+		}
+		ptrblock_free(native_dts);
+	} 
+	hp_free(dim2btype);
+	hp_free(extract_fn);
+	IHASH_VAL_ITERATE(chain_hash, struct hipac_chain *, chain_free);
+	ihash_free(chain_hash);
+	hp_mem_exit();
+}
+
+
+
+/* return new hipac data structure
+   name:        name of the public chain
+   name_intern: name of the internal dimtree chain
+   policy:      initial policy
+   origin:      bitvector uniq to this data structure
+   hipac:       pointer to a pointer to the resulting hipac data
+                structure. use as first argument to hipac_match()
+   possible errors: HE_LOW_MEMORY, HE_NATIVE_CHAIN_EXISTS,
+                    HE_CHAIN_EXISTS, HE_IMPOSSIBLE_CONDITION          */
+hipac_error
+hipac_new(const char *name, const char* name_intern, const __u8 policy, 
+	  const __u32 origin, void **hipac)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	struct dt_rule *start, *end;
+	struct prefix_rule *prefix_rule;
+	struct path *new_path;
+	__u32 i, j, list_pos = 0;
+
+	if (unlikely(!name || !name_intern || !hipac))
+		ARG_ERR;
+
+	for (i = 0; i < chain_hash->len; i++) {
+		if (chain_hash->bucket[i] == NULL) {
+			continue;
+		}
+		for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+			struct hipac_chain *c;
+			c = chain_hash->bucket[i]->kv[j].val;
+			if (c->dimtree && list_pos <= c->list_pos) {
+				list_pos = c->list_pos + 1;
+			}
+		}
+	}
+	
+	if (native_dts){
+		__u32 i = 0;
+		for (i = 0; i < native_dts->len; i++)
+			if (!strcmp(((struct dimtree *)native_dts->p[i])
+				    ->chain->name, name_intern))
+				return HE_NATIVE_CHAIN_EXISTS;
+	}
+
+	if ((error = chain_new(name, &chain, list_pos))){
+		CHECK_ERROR("chain_new");
+		return error;
+	}
+
+	if ((error = build_dt_rule(NULL, NULL, 0, TARGET_DUMMY, &start))){
+		CHECK_ERROR("build_dt_rule");
+		chain_free(chain);
+		return error;
+	}
+
+	if ((error = ptrblock_append(&chain->start, start))){
+		CHECK_ERROR("ptrblock_append");
+		chain_free(chain);
+		dt_rule_free(start);
+		return error;
+	}
+	if ((error = build_dt_rule(NULL, NULL, 1, policy, &end))){
+		CHECK_ERROR("build_dt_rule");
+		chain_free(chain);
+		dt_rule_free(start);
+		return error;
+	}
+	
+        if ((error = ptrblock_append(&chain->end, end))){
+		CHECK_ERROR("ptrblock_append");
+		chain_free(chain);
+		dt_rule_free(start);
+		dt_rule_free(end);
+		return error;
+	}
+	if ((error = dimtree_new((struct dimtree **)hipac, 
+				 origin, name_intern,
+				 start, end))){
+		CHECK_ERROR("dimtree_new");
+		chain_free(chain);
+		dt_rule_free(start);
+		dt_rule_free(end);
+		return error;
+	}
+
+	if ((error = ptrblock_append(&native_dts, 
+				     *(struct dimtree**) hipac))){
+		CHECK_ERROR("ptrblock_append");
+		dimtree_free(*(struct dimtree**) hipac);
+		chain_free(chain);
+		return error;
+	}
+
+	prefix_rule = hp_alloc(sizeof(*prefix_rule), ADD);
+	if (!prefix_rule){
+		dimtree_free(*(struct dimtree**) hipac);
+		chain_free(chain);
+		ptrblock_delete_tail(&native_dts);
+		LOW_MEM("prefix rule alloc failed");
+	}
+	new_path = hp_alloc(sizeof(*new_path), ADD);
+	if (!new_path){
+		hp_free(prefix_rule);
+		dimtree_free(*(struct dimtree**) hipac);
+		chain_free(chain);
+		ptrblock_delete_tail(&native_dts);
+		LOW_MEM("new_path alloc failed");
+	}
+	new_path->dimtree = *(struct dimtree**) hipac;
+	new_path->prev = NULL;
+	new_path->rule = prefix_rule;
+	prefix_rule->origin = ORIGIN_ALL;
+	prefix_rule->exec_matches = NULL;
+	prefix_rule->native_mct = 0;
+	if ((error = strblock_append_check(&chain->paths, new_path, 
+					   sizeof(*new_path)))){
+		CHECK_ERROR("strblock_append");
+		path_free(new_path);
+		dimtree_free(*(struct dimtree**) hipac);
+		chain_free(chain);
+		ptrblock_delete_tail(&native_dts);
+		return error;
+	}
+	hp_free(new_path);
+
+	if ((error = chain_hash_insert(chain))){
+		CHECK_ERROR("chain_hash_insert");
+		chain_free(chain);
+		dimtree_free(*(struct dimtree**) hipac);
+		ptrblock_delete_tail(&native_dts);
+		return error;
+	}
+	chain->dimtree = *(struct dimtree**) hipac;
+	return HE_OK;
+}
+
+
+
+/* set maximum amount of memory the hipac data structures are 
+   allowed to occupy. return LOW_MEMORY if 'mem' is lower than
+   currently allocated memory
+   possible errors: HE_LOW_MEMORY                                     */  
+hipac_error
+hipac_set_maxmem(const __u64 mem)
+{
+	if (mem_current_real > mem){
+		LOW_MEM();
+	}
+	mem_max = mem;
+	return HE_OK;
+}
+
+
+
+/* get maximum amount of memory the hipac data structures are 
+   allowed to occupy.                                                 */  
+__u64
+hipac_get_maxmem(void)
+{
+	return mem_max;
+}
+
+
+
+/* set policy of chain with name 'name' to 'policy'.
+   possible errors: HE_CHAIN_NOT_EXISTENT, HE_CHAIN_IS_USERDEFINED,
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_set_policy(const char *name, const __u8 policy)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	
+        if (unlikely(!name))
+		ARG_ERR;
+	if ((error = chain_hash_lookup(name, &chain))){
+		CHECK_ERROR("chain_hash_lookup");
+		return error;
+	}
+	if (!chain->dimtree)
+		return HE_CHAIN_IS_USERDEFINED;
+	((struct dt_rule *)(chain->end->p[0]))->spec.action = policy;
+	return HE_OK;
+}
+
+
+
+/* get policy of chain with name 'name' and write it to 'result'.
+   possible errors: HE_CHAIN_NOT_EXISTENT, HE_CHAIN_IS_USERDEFINED,
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_policy(const char *name, __u8 *result)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	
+	if (unlikely(!name || !result))
+		ARG_ERR;
+	if ((error = chain_hash_lookup(name, &chain))){
+		CHECK_ERROR("chain_hash_lookup");
+		return error;
+	}
+	if (!chain->dimtree)
+		return HE_CHAIN_IS_USERDEFINED;
+	*result = ((struct dt_rule *)(chain->end->p[0]))->spec.action;
+	return HE_OK;
+}
+
+
+
+/* create new user-defined chain with name 'name'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_EXISTS, 
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_new_chain(const char* name)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	__u32 i, j, list_pos;
+
+	if (unlikely(!name))
+		ARG_ERR;
+	
+	list_pos = chain_hash->elem_ct - (native_dts ? native_dts->len : 0);
+	if ((error = chain_new(name, &chain, list_pos))){
+		CHECK_ERROR("chain_new");
+		return error;
+	}
+	if ((error = chain_hash_insert(chain))){
+		CHECK_ERROR("chain_hash_insert");
+		chain_free(chain);
+		return error;
+	}
+	for (i = 0; i < chain_hash->len; i++) {
+		if (chain_hash->bucket[i] == NULL) {
+			continue;
+		}
+		for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+			struct hipac_chain *c;
+			c = chain_hash->bucket[i]->kv[j].val;
+			if (c->dimtree) {
+				continue;
+			}
+			if (strcmp(c->name, name) > 0) {
+				if (c->list_pos < list_pos) {
+					list_pos = c->list_pos;
+				}
+				c->list_pos++;
+			}
+		}
+	}
+	chain->list_pos = list_pos;
+
+	return HE_OK;
+}
+
+
+
+/* delete all rules in chain with name 'name'.
+   if 'name' is NULL all rules in all chains are deleted.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT,
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error 
+hipac_flush_chain(const char *name)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	struct list_head *lh;
+	struct chain_rule *rule;
+	struct next_chain_elem *n_elem;
+	__u32 i, j;
+	
+	if (!name){
+		//flushing all chains	
+		for (i = 0; i < chain_hash->len; i++) {
+			if (chain_hash->bucket[i] == NULL) {
+				continue;
+			}
+			for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+				chain = chain_hash->bucket[i]->kv[j].val;
+				if (chain->dimtree){
+					dimtree_flush(chain->dimtree);
+					lh = chain->head.next;
+					while (lh != &chain->head) {
+						rule = list_entry(
+							lh, struct chain_rule,
+							head);
+						lh = lh->next;
+						list_del(lh->prev);
+						chain_rule_destroy(rule);
+					}
+					if (chain->next_chains){
+						strblock_free(
+							chain->next_chains);
+						chain->next_chains = NULL;
+					}
+				} else {
+					chain_flush(chain);
+				}
+			}                                                         
+		}
+		return HE_OK;
+	}
+
+	if ((error = chain_hash_lookup(name, &chain)))
+		return error;
+
+
+	if (unlikely(CHAIN_NOT_CONNECTED(chain))){			
+		if (chain->next_chains){
+			for (i = 0; i < chain->next_chains->len; i++){
+				n_elem = STRBLOCK_ITH(chain->next_chains, i,
+						      struct next_chain_elem *);
+				n_elem->chain->ref_count -= n_elem->count;
+			}
+			strblock_free(chain->next_chains);
+			chain->next_chains = NULL;
+		}
+		lh = chain->head.next;
+		while (lh != &chain->head) {
+			rule = list_entry(lh, struct chain_rule, head);
+			lh = lh->next;
+			list_del(lh->prev);
+			chain_rule_destroy(rule);
+		}
+		return HE_OK;
+	}
+
+	
+	if (!chain->dimtree){
+		if ((error = delete_dt_rules_from_dimtrees(chain, 
+							   chain->start,
+							   chain->end))){
+			CHECK_ERROR("delete_dt_rules_from_dimtrees");
+			dimtree_failed(native_dts);
+			return error;
+		}
+		dimtree_commit(native_dts);
+	}
+	
+	if (chain->next_chains){
+		for (i = 0; i < chain->next_chains->len; i++){
+			n_elem = STRBLOCK_ITH(chain->next_chains, i,
+					      struct next_chain_elem *);
+			n_elem->chain->ref_count -= n_elem->count;
+		}
+		strblock_free(chain->next_chains);
+		chain->next_chains = NULL;
+	}
+
+	lh = chain->head.next;
+	while (lh != &chain->head) {
+		rule = list_entry(lh, struct chain_rule, head);
+		lh = lh->next;
+		list_del(lh->prev);
+		if (IS_JUMP_RULE(rule)){
+			delete_jump_from_hipac_layer(chain, chain->start,
+						     chain->end, rule);
+		}
+		chain_rule_destroy(rule);
+	}
+	
+	if (chain->dimtree){
+		dimtree_flush(chain->dimtree);
+	} else {
+		delete_dt_rules_from_dt_chains(chain, 
+					       chain->start, chain->end);
+		dimtree_chain_fix(native_dts);
+	}
+	return HE_OK;
+}
+
+
+
+/* delete user-defined chain with name 'name'.
+   if 'name' is NULL delete all chains that are empty 
+   and not referenced from other chains.
+   possible errors: HE_CHAIN_NOT_EXISTENT, HE_CHAIN_IS_NATIVE,
+                    HE_CHAIN_NOT_EMPTY, HE_CHAIN_IS_REFERENCED        */   
+hipac_error
+hipac_delete_chain(const char *name)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	__u32 i, j;
+	
+	if (!name){
+		//delete all empty and not referenced user-defined chains
+		for (i = 0; i < chain_hash->len; i++) {
+			if (chain_hash->bucket[i] == NULL) {
+				continue;
+			}
+			for (j = 0; j < chain_hash->bucket[i]->len;) {
+				__u32 k, l;
+				chain = chain_hash->bucket[i]->kv[j].val;
+				if (chain->dimtree
+				    || !list_empty(&chain->head)
+				    || CHAIN_IS_REFERENCED(chain)) {
+					j++;
+					continue;
+				}
+				chain_hash_remove(chain);
+				for (k = 0; k < chain_hash->len; k++) {
+					if (!chain_hash->bucket[k]) {
+						continue;
+					}
+					for (l = 0; l < chain_hash->
+						     bucket[k]->len; l++) {
+						struct hipac_chain *c;
+						c = chain_hash->bucket[k]->
+							kv[l].val;
+						if (!c->dimtree &&
+						    c->list_pos >
+						    chain->list_pos) {
+							c->list_pos--;
+						}
+					}
+				}
+				chain_free(chain);
+			}                                                         
+		}
+		return HE_OK;
+	}
+
+	if ((error = chain_hash_lookup(name, &chain)))
+		return HE_CHAIN_NOT_EXISTENT;
+
+	if (chain->dimtree)
+		return HE_CHAIN_IS_NATIVE;
+	
+	if (!list_empty(&chain->head))
+		return HE_CHAIN_NOT_EMPTY;
+
+	if (CHAIN_IS_REFERENCED(chain))
+		return HE_CHAIN_IS_REFERENCED;
+	
+	chain_hash_remove(chain);
+	for (i = 0; i < chain_hash->len; i++) {
+		struct hipac_chain *c;
+		if (chain_hash->bucket[i] == NULL) {
+			continue;
+		}
+		for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+			c = chain_hash->bucket[i]->kv[j].val;
+			if (!c->dimtree && c->list_pos > chain->list_pos) {
+				c->list_pos--;
+			}
+		}                                                         
+	}
+	chain_free(chain);
+	return HE_OK;
+}
+
+
+
+/* rename chain with name 'name' to 'new_name'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_EXISTS, 
+                    HE_CHAIN_NOT_EXISTENT, HE_CHAIN_IS_NATIVE,
+		    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_rename_chain(const char *name, const char *new_name)
+{
+	hipac_error error;
+	struct hipac_chain *old_chain, *new_chain;
+	struct list_head *lh;
+	struct chain_rule *rule;
+	int new_is_larger;
+	char *old;
+	__u32 i, j, k, list_pos;
+
+	if (unlikely(!name || !new_name))
+		ARG_ERR;
+	
+	if ((!(error = chain_hash_lookup(new_name, &old_chain))))
+		return HE_CHAIN_EXISTS;
+
+	if ((error = chain_hash_lookup(name, &old_chain)))
+		return error;
+	
+	if (old_chain->dimtree)
+		return HE_CHAIN_IS_NATIVE;
+
+	new_chain = hp_alloc(sizeof(*new_chain), ADD);
+	if (!new_chain)
+		return HE_LOW_MEMORY;
+
+	memcpy(new_chain, old_chain, sizeof(*new_chain));
+
+	strncpy(new_chain->name, new_name, HIPAC_CHAIN_NAME_MAX_LEN);
+	new_chain->name[HIPAC_CHAIN_NAME_MAX_LEN - 1] = '\0';
+
+	if ((error = chain_hash_replace(old_chain, new_chain))) {
+		CHECK_ERROR("chain_hash_replace");
+		hp_free(new_chain);
+		return error;
+	}
+	current_chain = NULL;
+	
+	if (list_empty(&old_chain->head)) {
+		INIT_LIST_HEAD(&new_chain->head);
+	} else {
+		lh = old_chain->head.next;
+		list_del(&old_chain->head);
+		list_add_tail(&new_chain->head, lh);
+	}
+	
+	new_is_larger = (strcmp(new_name, name) > 0);
+	list_pos = old_chain->list_pos;
+	if (!CHAIN_IS_REFERENCED(old_chain)) {
+		for (i = 0; i < chain_hash->len; i++) {
+			struct hipac_chain *chain;
+			if (chain_hash->bucket[i] == NULL) {
+				continue;
+			}
+			for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+				chain = chain_hash->bucket[i]->kv[j].val;
+				if (chain->dimtree)
+					continue;
+				if (new_is_larger) {
+					if (chain->list_pos >
+					    old_chain->list_pos &&
+					    strcmp(chain->name,
+						   new_name) < 0) {
+						if (list_pos <
+						    chain->list_pos) {
+							list_pos = chain->
+								list_pos;
+						}
+						chain->list_pos--;
+					}
+				} else {
+					if (chain->list_pos <
+					    old_chain->list_pos &&
+					    strcmp(chain->name,
+						   new_name) > 0) {
+						if (list_pos >
+						    chain->list_pos) {
+							list_pos = chain->
+								list_pos;
+						}
+						chain->list_pos++;
+					}
+				}
+			}
+		}
+		new_chain->list_pos = list_pos;
+		hp_free(old_chain);
+		return HE_OK;
+	}
+	
+	for (i = 0; i < chain_hash->len; i++) {
+		struct hipac_chain *chain, **next;
+		if (chain_hash->bucket[i] == NULL) {
+			continue;
+		}
+		for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+			chain = chain_hash->bucket[i]->kv[j].val;
+
+			if (chain->next_chains){
+				for (k = 0; k < chain->next_chains->len; k++){
+					next = &STRBLOCK_ITH(
+						chain->next_chains, k,
+						struct next_chain_elem *)
+						->chain;
+					if (*next == old_chain)
+						*next = new_chain;
+				}
+			}
+
+			list_for_each(lh, &chain->head) {
+				rule = list_entry(lh, struct chain_rule, head);
+				if (IS_JUMP_RULE(rule)){
+					old = (void *) &rule->r 
+						+ rule->r.target_offset;
+					if (!strcmp(old, name)){
+						strncpy(old, new_name,
+						    HIPAC_CHAIN_NAME_MAX_LEN);
+						old[HIPAC_CHAIN_NAME_MAX_LEN
+						    - 1] = '\0';
+					}
+				}
+			}	
+
+			if (chain->dimtree)
+				continue;
+				
+			if (new_is_larger) {
+				if (chain->list_pos > old_chain->list_pos &&
+				    strcmp(chain->name, new_name) < 0) {
+					if (list_pos < chain->list_pos) {
+						list_pos = chain->list_pos;
+					}
+					chain->list_pos--;
+				}
+			} else {
+				if (chain->list_pos < old_chain->list_pos &&
+				    strcmp(chain->name, new_name) > 0) {
+					if (list_pos > chain->list_pos) {
+						list_pos = chain->list_pos;
+					}
+					chain->list_pos++;
+				}
+			}
+		}
+	}
+	new_chain->list_pos = list_pos;
+	hp_free(old_chain);
+	return HE_OK;
+}
+
+
+
+/* get an array of hipac_chain_info structs containing required infos
+   for a rule listing of chain with name 'name'. if 'name' is NULL
+   return infos for all chains. 'len' specifies the length of the
+   returned struct hipac_chain_info array.
+   attention: don't forget to free the struct hipac_chain_info array
+              after the rule listing via hipac_free_chain_infos()!
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT,
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_chain_infos(const char *name, struct hipac_chain_info **inf,
+		      __u32 *len)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+
+	if (unlikely(!inf || !len))
+		ARG_ERR;
+	
+	if (!name){
+		__u32 i, j, e;
+		*len = chain_hash->elem_ct;
+		*inf = hp_alloc(*len * sizeof(**inf), ADD);
+		if (!(*inf)){
+			LOW_MEM("hipac_chain_info alloc failed!");
+		}
+		for (i = 0; i < chain_hash->len; i++) {
+		        if (!chain_hash->bucket[i])
+				continue;
+			for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+				chain = chain_hash->bucket[i]->kv[j].val;
+				if (chain->dimtree) {
+					e = chain->list_pos;
+					(*inf)[e].policy = ((struct dt_rule *)
+							    (chain->end->p[0]))
+						->spec.action;
+				} else {
+					e = chain->list_pos +
+						(native_dts ?
+						 native_dts->len : 0);
+					(*inf)[e].policy = 0;
+				}
+				(*inf)[e].label = chain->name;
+				(*inf)[e].is_internal_chain = 0;
+				if (list_empty(&chain->head)){
+					(*inf)[e].rule_num = 0;
+				} else {
+					(*inf)[e].rule_num =
+						list_entry(chain->head.prev,
+							   struct chain_rule, 
+							   head)->r.pos;
+				}
+				(*inf)[e].chain_head = &chain->head;
+			}                                                       
+		}
+		return HE_OK;
+	}
+		
+
+	if ((error = chain_hash_lookup(name, &chain))){
+		// it's not a user-defined chain
+		// check if it's a internal dimtree chain
+		__u32 i;
+		struct dimtree *dt;
+		if (!native_dts) 
+			return  HE_CHAIN_NOT_EXISTENT;
+		for (i = 0; i < native_dts->len; i++){
+			dt = (struct dimtree *) native_dts->p[i];
+			if (!strcmp(name, dt->chain->name)){
+				*len = 1;
+				*inf = hp_alloc(sizeof(**inf), ADD);
+				if (!(*inf))
+					LOW_MEM();
+				(*inf)[0].label = dt->chain->name;
+				(*inf)[0].policy = 
+					list_entry(dt->chain->head.prev,
+						   struct dt_rule, 
+						   head)->spec.action;
+				(*inf)[0].is_internal_chain = 1;
+				(*inf)[0].rule_num = dt->chain->len;
+				(*inf)[0].chain_head = &dt->chain->head;
+				return HE_OK;
+			}
+		}
+		return HE_CHAIN_NOT_EXISTENT;
+	}
+	
+	*len = 1;
+	*inf = hp_alloc(sizeof(**inf), ADD);
+	if (!(*inf))
+		LOW_MEM("hipac_chain_info alloc failed!");
+	(*inf)[0].label = chain->name;
+	if (chain->dimtree)
+		(*inf)[0].policy = ((struct dt_rule *)
+				    (chain->end->p[0]))->spec.action;
+	else (*inf)[0].policy = 0;
+	(*inf)[0].is_internal_chain = 0;
+	if (list_empty(&chain->head)){
+		(*inf)[0].rule_num = 0;
+	} else {
+		(*inf)[0].rule_num = list_entry(
+			chain->head.prev,
+			struct chain_rule, head)->r.pos;
+	} 
+	(*inf)[0].chain_head = &chain->head;
+	return HE_OK;
+}
+
+
+
+/* free array of hipac_chain_info structs that has been allocated
+   before via hipac_get_chain_infos(). 
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_free_chain_infos(struct hipac_chain_info *inf)
+{
+	if (unlikely(!inf))
+		ARG_ERR;
+	hp_free(inf);
+	return HE_OK;
+}
+
+
+
+/* get next hipac_rule 'next' of previous hipac_rule 'prev'.
+   with this function you can walk over the chain during rule listing.
+   to get the first hipac_rule of a chain, set 'prev_rule' to NULL.
+   when the end of the chain is reached or the chain is empty the
+   hipac_error HE_RULE_NOT_EXISTENT is returned.
+   attention: during rule listing of a chain hipac_get_next_rule() 
+              must always be called until finally HE_RULE_NOT_EXISTENT 
+	      is returned!
+   possible errors: HE_LOW_MEMORY, HE_RULE_NOT_EXISTENT,
+                    IMPOSSIBLE_CONDITION                              */
+hipac_error
+hipac_get_next_rule(const struct hipac_chain_info *inf,
+		    struct hipac_rule *prev, 
+		    struct hipac_rule **next)
+{
+	hipac_error error;
+	static struct dt_rule *dt_rule = NULL;
+
+	if (unlikely(!inf || !next))
+		ARG_ERR;
+
+	if (unlikely(!prev)){
+		if (!inf->is_internal_chain){
+			if (unlikely(list_empty(inf->chain_head))){
+				*next = NULL;
+				return HE_RULE_NOT_EXISTENT;
+			} else {
+				*next = &list_entry(inf->chain_head->next,
+						    struct chain_rule, 
+						    head)->r;
+			}
+		} else {
+			if (dt_rule)
+				IMPOSSIBLE_CONDITION("dt_rule is defined!");
+			dt_rule = list_entry(inf->chain_head->next,
+					     struct dt_rule, head);
+			if ((error = build_hipac_rule_from_dt_rule(dt_rule, 
+								   next))){
+				CHECK_ERROR("build_hipac_rule_from_dt_rule");
+				dt_rule = NULL;
+				*next = NULL;
+				return error;
+			}
+		}
+		return HE_OK;
+	}
+       
+	if (!inf->is_internal_chain){
+		struct chain_rule *prev_chain_rule;
+		prev_chain_rule = list_entry(prev, 
+					     struct chain_rule, r);
+		if (prev_chain_rule->head.next == inf->chain_head){
+			*next = NULL;
+			return HE_RULE_NOT_EXISTENT;
+		}
+		*next = &list_entry(prev_chain_rule->head.next,
+				    struct chain_rule, head)->r;
+	} else {
+		hp_free(prev);
+		if (!dt_rule)
+			IMPOSSIBLE_CONDITION("dt_rule not defined!");
+		if (dt_rule->head.next == inf->chain_head){
+			dt_rule = NULL;
+			*next = NULL;
+			return HE_RULE_NOT_EXISTENT;
+		}
+		dt_rule = list_entry(dt_rule->head.next,
+				     struct dt_rule, head);
+		if ((error = build_hipac_rule_from_dt_rule(dt_rule, 
+							   next))){
+			CHECK_ERROR("build_hipac_rule_from_dt_rule");
+			dt_rule = NULL;
+			*next = NULL;
+			return error;
+		}
+	}
+	return HE_OK;
+}
+
+
+/* append hipac_rule 'rule' to chain with name 'name'.
+   'rule->pos' is set to the position of the last rule
+   in the chain + 1.  
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT, 
+                    HE_LOOP_DETECTED, HE_REC_LIMIT,
+		    HE_RULE_ORIGIN_MISMATCH, HE_RULE_PREFIX_MISMATCH,
+		    HE_TARGET_CHAIN_NOT_EXISTENT,
+		    HE_TARGET_CHAIN_IS_NATIVE, 
+		    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_append(const char *name, const struct hipac_rule *rule)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	struct chain_rule *new_rule;
+	
+	if (unlikely(!name || !rule))
+		ARG_ERR;
+	
+	if ((error = chain_hash_lookup(name, &chain)))
+		return error;
+
+	if (unlikely(error = build_chain_rule_from_hipac_rule(rule, &new_rule)))
+		return error;
+
+	new_rule->r.pos = (list_empty(&chain->head)) ?
+		1 : (list_entry(chain->head.prev,
+				struct chain_rule, head)->r.pos + 1);
+	return insert(chain, new_rule);
+}	
+
+
+
+/* insert hipac_rule 'rule' at position 'rule->pos' into chain
+   with name 'name'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT,
+                    HE_LOOP_DETECTED, HE_REC_LIMIT,
+		    HE_RULE_ORIGIN_MISMATCH, HE_RULE_PREFIX_MISMATCH,
+		    HE_TARGET_CHAIN_NOT_EXISTENT,
+		    HE_TARGET_CHAIN_IS_NATIVE, 
+		    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_insert(const char *name, const struct hipac_rule *rule)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	struct chain_rule *new_rule;
+
+	if (unlikely(!name || !rule))
+		ARG_ERR;
+
+	if ((error = chain_hash_lookup(name, &chain)))
+		return error;
+
+	if (unlikely(error = build_chain_rule_from_hipac_rule(rule, &new_rule)))
+		return error;
+
+	return insert(chain, new_rule);
+}
+
+
+
+/* delete hipac_rule with position 'pos' from chain with name 'name'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT, 
+                    HE_RULE_NOT_EXISTENT, HE_IMPOSSIBLE_CONDITION     */
+hipac_error
+hipac_delete_pos(const char *name, const __u32 pos)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	struct chain_rule *del_rule;
+	
+	if (unlikely(!name))
+		ARG_ERR;
+	
+	if ((error = chain_hash_lookup(name, &chain)))
+		return error;
+	
+	if ((error = chain_find_rule_with_pos(chain, pos, &del_rule)))
+		return error;
+
+	return delete(chain, del_rule);
+}
+
+
+
+/* find the first rule in chain with name 'name' that equals to
+   hipac_rule 'rule' and delete it.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT, 
+                    HE_RULE_NOT_EXISTENT, HE_IMPOSSIBLE_CONDITION     */
+hipac_error
+hipac_delete(const char *name, const struct hipac_rule *rule)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	struct chain_rule *del_rule;
+
+	if (unlikely(!name || !rule))
+		ARG_ERR;
+	
+	if ((error = chain_hash_lookup(name, &chain)))
+		return error;
+	
+	if ((error = chain_find_rule(chain, rule, &del_rule)))
+		return error;
+	
+	return delete(chain, del_rule);
+}
+
+
+
+/* replace rule with position 'rule->pos' in chain with name 'name'
+   with hipac_rule 'rule'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT,
+                    HE_RULE_NOT_EXISTENT, HE_LOOP_DETECTED,
+		    HE_REC_LIMIT, HE_RULE_ORIGIN_MISMATCH,
+		    HE_RULE_PREFIX_MISMATCH,
+		    HE_TARGET_CHAIN_NOT_EXISTENT,
+		    HE_TARGET_CHAIN_IS_NATIVE, 
+		    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_replace(const char *name, const struct hipac_rule *rule)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	struct chain_rule *del_rule, *new_rule;
+		
+	if (unlikely(!name || !rule))
+		ARG_ERR;
+	
+	if ((error = chain_hash_lookup(name, &chain)))
+		return error;
+	
+	if ((error = chain_find_rule_with_pos(chain, rule->pos, 
+						      &del_rule)))
+		return error;
+	
+	if (unlikely(error = build_chain_rule_from_hipac_rule(rule, &new_rule)))
+		return error;
+	
+	return replace(chain, del_rule, new_rule);
+}
+
+
+
+
+
+/*
+ * hipac statistic functions
+ */
+
+
+
+/* get hipac chain statistics
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_chain_stat(struct hipac_chain_stat *stat)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	struct prefix_rule *prefix;
+	struct list_head *lh;
+	struct chain_rule *rule;
+	__u32 i, j, k;
+	
+	if (unlikely(!stat))
+		ARG_ERR;
+	
+	stat->mem_tight = 0;
+	stat->mem_real = 0;
+	stat->chain_num = chain_hash->elem_ct;
+	stat->rule_num = 0;
+	stat_distribution_init(stat->prefix_stat, 16);
+	stat_distribution_init(stat->incoming_stat, 16);
+	stat_distribution_init(stat->outgoing_stat, 16);
+	
+	for (i = 0; i < chain_hash->len; i++) {
+		if (chain_hash->bucket[i] == NULL) {
+			continue;
+		}
+		for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+			chain = chain_hash->bucket[i]->kv[j].val;
+			if ((error = hp_size(chain, 
+					     &stat->mem_real,
+					     &stat->mem_tight)))
+				return error;
+			if ((error = hp_size(chain->next_chains, 
+					     &stat->mem_real,
+					     &stat->mem_tight)))
+				return error;
+			if ((error = hp_size(chain->paths, 
+					     &stat->mem_real,
+					     &stat->mem_tight)))
+				return error;
+			if (chain->paths){
+				for (k = 0; k < chain->paths->len; k++){
+					prefix = P_ELEM_RULE(chain->paths, k);
+					if ((error = 
+					     hp_size(prefix, 
+						     &stat->mem_real,
+						     &stat->mem_tight)))
+					       	return error;
+					if (prefix
+					    && (error = 
+						hp_size(prefix->exec_matches, 
+							&stat->mem_real,
+							&stat->mem_tight)))
+						return error;
+				}
+			}
+			if ((error = hp_size(chain->start, 
+					     &stat->mem_real,
+					     &stat->mem_tight)))
+				return error;
+			if ((error = hp_size(chain->end, 
+					     &stat->mem_real,
+					     &stat->mem_tight)))
+				return error;
+
+			if (!list_empty(&chain->head)){
+				stat->rule_num += 
+					list_entry(chain->head.prev,
+						   struct chain_rule, 
+						   head)->r.pos;
+			}   
+			
+			if (chain->paths)
+				stat_distribution_add(stat->prefix_stat, 16, 
+						      chain->paths->len);
+			else stat_distribution_add(stat->prefix_stat, 16, 0);
+			stat_distribution_add(stat->incoming_stat, 16,
+					      chain->ref_count);
+			if (chain->next_chains)
+				stat_distribution_add(stat->outgoing_stat, 16,
+						      chain->next_chains->len);
+			else stat_distribution_add(stat->outgoing_stat, 16, 0);
+			
+			list_for_each(lh, &chain->head) {
+				rule = list_entry(lh, struct chain_rule, head);
+				if ((error = hp_size(rule, 
+						     &stat->mem_real,
+						     &stat->mem_tight)))
+					return error;
+				if ((error = hp_size(rule->dtr, 
+						     &stat->mem_real,
+						     &stat->mem_tight)))
+					return error;
+			}
+		}
+	}
+	return HE_OK;
+}
+
+
+
+/* get hipac rule statistics
+   returned statistic constains all rules of those chains that are
+   reachable from the root chain represented by the 'hipac' pointer.
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_rule_stat(void *hipac, struct hipac_rule_stat *stat)
+{
+	struct hipac_chain *chain;
+	struct list_head *lh;
+	struct chain_rule *rule;
+	__u32 i, j, k, inv;
+	__u8 found;
+	
+	if (unlikely(!hipac || !stat))
+		ARG_ERR;
+	
+	stat->rule_num = 0;
+	stat->exec_match_num = 0;
+	stat->exec_target_num = 0;
+	stat->jump_target_num = 0;
+	stat->return_target_num = 0;
+	stat_distribution_init(stat->hipac_match_stat, 16);
+	stat_distribution_init(stat->inv_rules_stat, 16);
+	
+	for (i = 0; i < chain_hash->len; i++) {
+		if (chain_hash->bucket[i] == NULL) {
+			continue;
+		}
+		for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+			chain = chain_hash->bucket[i]->kv[j].val;
+			found = 0;
+			if (chain->paths){
+				for (k = 0; k < chain->paths->len; k++){
+					if (hipac ==
+					    P_ELEM_DIMTREE(chain->paths, k)){
+						found = 1;
+						break;
+					}
+				}
+			}
+			if (!found)
+				continue;
+			if (!list_empty(&chain->head)){
+				stat->rule_num += 
+					list_entry(chain->head.prev,
+						   struct chain_rule, 
+						   head)->r.pos;
+			}   
+			
+			list_for_each(lh, &chain->head) {
+				rule = list_entry(lh, struct chain_rule, head);
+				if (rule->r.match_offset)
+					stat->exec_match_num++;
+				if (rule->r.action == TARGET_EXEC)
+					stat->exec_target_num++;
+				if (rule->r.action == TARGET_CHAIN)
+					stat->jump_target_num++;
+				if (rule->r.action == TARGET_RETURN)
+					stat->return_target_num++;
+				stat->hipac_match_stat[rule->r.native_mct]++;
+				inv = count_inv_matches(rule->r.first_match, 
+							rule->r.native_mct);
+				stat->inv_rules_stat[inv]++;
+			}
+		}
+	}
+	return HE_OK;
+}
+	
+
+
+/* get hipac user statistics
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_user_stat(struct hipac_user_stat *stat)
+{
+	struct hipac_chain *chain;
+	__u32 i, j;
+	
+	if (unlikely(!stat))
+		ARG_ERR;
+
+	stat->total_mem_tight = mem_current_tight;
+	stat->total_mem_real = mem_current_real;
+	stat->chain_num = chain_hash->elem_ct;
+	stat->rule_num = 0;
+	
+	for (i = 0; i < chain_hash->len; i++) {
+		if (chain_hash->bucket[i] == NULL) {
+			continue;
+		}
+		for (j = 0; j < chain_hash->bucket[i]->len; j++) {
+			chain = chain_hash->bucket[i]->kv[j].val;
+			if (!list_empty(&chain->head)){
+				stat->rule_num += 
+					list_entry(chain->head.prev,
+						   struct chain_rule, 
+						   head)->r.pos;
+			}                 
+		}
+	}
+	return HE_OK;
+}
+
+
+
+#ifdef DEBUG
+hipac_error
+hipac_get_dt_rule_ptrs(const char *name, const __u32 pos, 
+		       void **res)
+{
+	hipac_error error;
+	struct hipac_chain *chain;
+	struct chain_rule *rule;
+	
+	if (unlikely(!name || !res))
+		ARG_ERR;
+	
+	if ((error = chain_hash_lookup(name, &chain)))
+		return error;
+       
+	if (list_empty(&chain->head)){
+		*res = chain->end;
+		return HE_OK;
+	}
+	rule = list_entry(chain->head.prev, struct chain_rule, head);
+	if (pos > rule->r.pos){
+		if (pos == rule->r.pos + 1){
+			*res = chain->end;
+			return HE_OK;
+		} else {
+			return HE_RULE_NOT_EXISTENT;
+		}
+	}
+
+	if (unlikely(error = chain_find_rule_with_pos(chain, pos, &rule)))
+		return error;
+
+	*res = rule->dtr;
+	return HE_OK;
+}
+
+
+
+__u8
+dt_rules_have_same_position(void *hipac, void *dt_start, void *dt_rule)
+{
+	struct dt_rule *rule = (struct dt_rule *) dt_start;
+	
+	if (!hipac || !dt_start || !dt_rule){
+		 ARG_MSG;
+		 return 0;
+	}
+	if (rule->head.prev != &((struct dimtree *) hipac)->chain->head) {
+	        if (rule->spec.pos ==
+		    list_entry(rule->head.prev, struct dt_rule, head)
+		    ->spec.pos){
+			ERR("previous rule with same position found");
+			return 0;
+		}
+	}
+	while (rule->spec.pos == ((struct dt_rule *) dt_rule)->spec.pos) {
+		if (rule == dt_rule)
+			return 1;
+		if (rule->head.next == 
+		    &((struct dimtree *) hipac)->chain->head)
+			return 0;
+		rule = list_entry(rule->head.next, struct dt_rule, head);
+	}
+	return 0;
+}
+
+
+#endif
+
+
+
+/* End of hipac_* functions */
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/hipac.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/hipac.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/hipac.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/hipac.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,623 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _HIPAC_H
+#define _HIPAC_H
+
+#include "mode.h"
+
+/* values of bittype in specification header */
+#define BIT_U16  0
+#define BIT_U32  1
+
+/* maximum length of a hipac chain name (including terminating '\0') */
+#define HIPAC_CHAIN_NAME_MAX_LEN 32
+
+/* representation of the match [left, right] associated with a dimension id;
+   [left, right] must not be a wildcard match */
+struct hipac_match
+{
+        unsigned dimid  : 5;
+	unsigned invert : 1;
+        __u32 left;
+        __u32 right;
+	char next_match[0];
+};
+
+struct hipac_rule
+{
+	__u32 pos;
+	char  cmp_start[0];
+	__u32 size;
+	__u32 origin;
+	__u8  action;
+	__u8  native_mct;
+	__u16 match_offset;
+	__u32 target_offset;
+	struct hipac_match first_match[0];
+};
+
+struct hipac_chain_info
+{
+	char *label;
+	__u8 policy;
+	__u8 is_internal_chain;
+	__u32 rule_num;
+	struct list_head *chain_head;
+};
+
+
+
+/* return values of function based match executor */
+typedef enum
+{
+	MATCH_YES,
+	MATCH_NO,
+	MATCH_HOTDROP
+} hipac_match_t;
+
+
+/* hipac_rule action value; TARGET_DUMMY is reserved for internal usage only;
+   the function based target exectutor may return TARGET_ACCEPT, TARGET_DROP
+   or TARGET_NONE */
+typedef enum
+{
+	TARGET_DROP = NF_DROP,
+	TARGET_ACCEPT = NF_ACCEPT,
+	TARGET_NONE = (NF_ACCEPT > NF_DROP ? NF_ACCEPT + 1 : NF_DROP + 1),
+	TARGET_RETURN,
+	TARGET_DUMMY,
+	TARGET_EXEC,
+	TARGET_CHAIN
+} hipac_target_t;
+
+
+/* function based match and target executor function types */
+typedef hipac_match_t (* hipac_match_exec_t) (const void *packet,
+					      void *first_match, void *end);
+typedef hipac_target_t (* hipac_target_exec_t) (const void *packet,
+						void *target);
+
+
+/* dimension extractor function type */
+typedef __u32 (* hipac_extract_t) (const void *packet, int *hotdrop);
+
+
+/* equality function type */
+typedef int (* hipac_eq_exec_t) (const struct hipac_rule *r1,
+				 const struct hipac_rule *r2);
+
+
+/* constructor/destructor function type */
+typedef void (* hipac_copy_constructor_t) (const struct hipac_rule *r_org,
+					   struct hipac_rule *r_new);
+typedef void (* hipac_destroy_exec_t) (struct hipac_rule *r);
+
+
+/* hipac error codes */
+typedef enum
+{
+	HE_OK                        =  0,
+	HE_IMPOSSIBLE_CONDITION      = -1,
+	HE_LOW_MEMORY                = -2,
+	HE_CHAIN_EXISTS              = -3,
+    	HE_CHAIN_NOT_EXISTENT        = -4,
+	HE_CHAIN_IS_EMPTY            = -5,
+	HE_CHAIN_NOT_EMPTY           = -6,
+	HE_CHAIN_IS_USERDEFINED      = -7,
+	HE_CHAIN_IS_CONNECTED        = -8,
+	HE_CHAIN_IS_REFERENCED       = -9,
+	HE_CHAIN_NOT_NATIVE          = -10,
+	HE_CHAIN_IS_NATIVE           = -11,
+	HE_RULE_NOT_EXISTENT         = -12,
+	HE_RULE_ORIGIN_MISMATCH      = -13,
+	HE_RULE_PREFIX_MISMATCH      = -14,
+	HE_LOOP_DETECTED             = -15,
+	HE_REC_LIMIT                 = -16,
+	HE_TARGET_CHAIN_NOT_EXISTENT = -17,
+	HE_TARGET_CHAIN_IS_NATIVE    = -18,
+	HE_NATIVE_CHAIN_EXISTS       = -19,
+	HE_NEXT_ERROR                = -100  // shouldn't be changed
+} hipac_error;
+
+
+
+/* return maximum key of a dimension with the given bittype */
+static inline __u32
+hipac_maxkey(__u8 bittype)
+{
+	if (bittype == BIT_U16)
+		return 0xffff;
+	return 0xffffffff;
+}
+
+
+/* init hipac data structures;
+   MUST be called once at the beginning in order to let the other
+   operations work properly!
+   dimid_to_bittype: assigns dimids to bit types. 
+                     i-th element of the array contains the bit type
+		     of dimension id i
+   extract:          functions to extract certain fields from a packet. 
+                     the function at position i of the array returns
+		     the entry in a packet that corresponds to 
+		     dimension id i (i.e. the source ip of the packet)
+   len:              length of the dim2btype and extract array
+   copycon:          constructor function
+   destroy:          destructor function
+   match:            match executor function
+   target:           target executor function
+   eq:               equality function to compare rules
+   maxmem:           maximum allowed memory consumption  
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION            */  
+hipac_error
+hipac_init(const __u8 dimid_to_bittype[], const hipac_extract_t extract[],
+	   const __u8 len, hipac_copy_constructor_t copycon,
+	   hipac_destroy_exec_t destroy, hipac_match_exec_t match,
+	   hipac_target_exec_t target, hipac_eq_exec_t eq, 
+	   const __u64 maxmem);
+
+
+/* free all hipac data structures;
+   MUST be called once in the end
+   attention: make sure there are no external accesses to hipac 
+              data structures taking place anymore!                   */
+void
+hipac_exit(void);
+
+
+/* return new hipac data structure
+   name:        name of the public chain
+   name_intern: name of the internal dimtree chain
+   policy:      initial policy
+   origin:      bitvector uniq to this data structure
+   hipac:       pointer to a pointer to the resulting hipac data
+                structure. use as first argument to hipac_match()
+   possible errors: HE_LOW_MEMORY, HE_NATIVE_CHAIN_EXISTS,
+                    HE_CHAIN_EXISTS, HE_IMPOSSIBLE_CONDITION          */
+hipac_error
+hipac_new(const char *name, const char* name_intern, const __u8 policy, 
+	  const __u32 origin, void **hipac);  
+
+
+/* set maximum amount of memory the hipac data structures are 
+   allowed to occupy. return LOW_MEMORY if 'mem' is lower than
+   currently allocated memory
+   possible errors: HE_LOW_MEMORY                                     */  
+hipac_error
+hipac_set_maxmem(const __u64 mem);
+
+
+/* get maximum amount of memory the hipac data structures are 
+   allowed to occupy.                                                 */  
+__u64
+hipac_get_maxmem(void);
+
+
+/* set policy of chain with name 'name' to 'policy'.
+   possible errors: HE_CHAIN_NOT_EXISTENT, HE_CHAIN_IS_USERDEFINED,
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error 
+hipac_set_policy(const char *name, const __u8 policy);
+
+
+/* get policy of chain with name 'name' and write it to 'result'.
+   possible errors: HE_CHAIN_NOT_EXISTENT, HE_CHAIN_IS_USERDEFINED,
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_policy(const char *name, __u8 *result);
+
+
+/* create new user-defined chain with name 'name'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_EXISTS, 
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_new_chain(const char* name);
+
+
+/* delete all rules in chain with name 'name'.
+   if 'name' is NULL all rules in all chains are deleted
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT,
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_flush_chain(const char *name);
+
+
+/* delete user-defined chain with name 'name'.
+   if 'name' is NULL delete all chains that are empty 
+   and not referenced from other chains.
+   possible errors: HE_CHAIN_NOT_EXISTENT, HE_CHAIN_IS_NATIVE,
+                    HE_CHAIN_NOT_EMPTY, HE_CHAIN_IS_REFERENCED        */   
+hipac_error
+hipac_delete_chain(const char *name);
+
+
+/* rename chain with name 'name' to 'new_name'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_EXISTS, 
+                    HE_CHAIN_NOT_EXISTENT, HE_CHAIN_IS_NATIVE,
+		    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_rename_chain(const char *name, const char *new_name);
+
+
+/* get an array of hipac_chain_info structs containing required infos
+   for a rule listing of chain with name 'name'. if 'name' is NULL
+   return infos for all chains. 'len' specifies the length of the
+   returned struct hipac_chain_info array.
+   attention: don't forget to free the struct hipac_chain_info array
+              after the rule listing via hipac_free_chain_infos()!
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT,
+                    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_chain_infos(const char *name, struct hipac_chain_info **inf,
+		      __u32 *len);
+
+
+/* free array of hipac_chain_info structs that has been allocated
+   before via hipac_get_chain_infos(). 
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_free_chain_infos(struct hipac_chain_info *inf);
+
+
+/* get next hipac_rule 'next' of previous hipac_rule 'prev'.
+   with this function you can walk over the chain during rule listing.
+   to get the first hipac_rule of a chain, set 'prev' to NULL.
+   when the end of the chain is reached or the chain is empty the
+   hipac_error HE_RULE_NOT_EXISTENT is returned.
+   attention: during rule listing of a chain hipac_get_next_rule() 
+              must always be called until finally HE_RULE_NOT_EXISTENT 
+	      is returned!
+   possible errors: HE_LOW_MEMORY, HE_RULE_NOT_EXISTENT,
+                    IMPOSSIBLE_CONDITION                              */
+hipac_error
+hipac_get_next_rule(const struct hipac_chain_info *inf,
+		    struct hipac_rule *prev,
+		    struct hipac_rule **next);
+
+
+/* append hipac_rule 'rule' to chain with name 'name'.
+   'rule->pos' is set to the position of the last rule
+   in the chain + 1.  
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT, 
+                    HE_LOOP_DETECTED, HE_REC_LIMIT,
+		    HE_RULE_ORIGIN_MISMATCH, HE_RULE_PREFIX_MISMATCH,
+		    HE_TARGET_CHAIN_NOT_EXISTENT,
+		    HE_TARGET_CHAIN_IS_NATIVE, 
+		    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_append(const char *name, const struct hipac_rule *rule);
+
+
+/* insert hipac_rule 'rule' at position 'rule->pos' into chain
+   with name 'name'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT,
+                    HE_LOOP_DETECTED, HE_REC_LIMIT,
+		    HE_RULE_ORIGIN_MISMATCH, HE_RULE_PREFIX_MISMATCH,
+		    HE_TARGET_CHAIN_NOT_EXISTENT,
+		    HE_TARGET_CHAIN_IS_NATIVE, 
+		    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_insert(const char *name, const struct hipac_rule *rule);
+
+
+/* delete hipac_rule with position 'pos' from chain with name 'name'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT, 
+                    HE_RULE_NOT_EXISTENT, HE_IMPOSSIBLE_CONDITION     */
+hipac_error
+hipac_delete_pos(const char *name, const __u32 pos);
+
+
+/* find the first rule in chain with name 'name' that equals to
+   hipac_rule 'rule' and delete it.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT, 
+                    HE_RULE_NOT_EXISTENT, HE_IMPOSSIBLE_CONDITION     */
+hipac_error
+hipac_delete(const char *name, const struct hipac_rule *rule);
+
+
+/* replace rule with position 'rule->pos' in chain with name 'name'
+   with hipac_rule 'rule'.
+   possible errors: HE_LOW_MEMORY, HE_CHAIN_NOT_EXISTENT,
+                    HE_RULE_NOT_EXISTENT, HE_LOOP_DETECTED,
+		    HE_REC_LIMIT, HE_RULE_ORIGIN_MISMATCH,
+		    HE_RULE_PREFIX_MISMATCH,
+		    HE_TARGET_CHAIN_NOT_EXISTENT,
+		    HE_TARGET_CHAIN_IS_NATIVE, 
+		    HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_replace(const char *name, const struct hipac_rule *rule);
+
+
+/* match packet and return the terminal packet action which is either
+   TARGET_ACCEPT or TARGET_DROP; note that this is the only function
+   that may be used in parallel with other functions of the hipac API */
+hipac_target_t
+hipac_match(void *hipac, const void *packet);
+
+
+
+/*
+ * hipac statistics: data structures
+ */
+
+/* rlp statistics
+   total_mem_tight:       current overall memory consumption in bytes
+                          in terms of how much has been requested
+   total_mem_real:        current overall memory consumption in bytes
+                          in terms of how much has actually been
+                          allocated
+   rlp_mem_tight:         current memory consumption in bytes of all
+                          rlps (not including termrule blocks) in
+                          terms of how much has been requested
+   rlp_mem_real:          current memory consumption in bytes of all
+                          rlps (not including termrule blocks) in
+                          terms of how much has actually been
+			  allocated
+   termrule_mem_tight:    current memory consumption in bytes of all
+                          termrule blocks in terms of how much has
+			  been requested
+   termrule_mem_real:     current memory consumption in bytes of all
+                          termrule blocks in terms of how much has
+			  actually been allocated
+   rlp_num:               number of rlps
+   rlp_dimid_num:         mapping with [i] containing the number of
+                          rlps in dimension i
+   rlp_depth_num:         mapping with [i] containing the number of
+                          rlps in depth i
+   termrule_num:          number of termrule blocks
+   termrule_ptr_num:      number of entries in all termrule blocks
+   keys_num:              number of keys in all rlps
+   rlp_dimid_keys_stat:   array of distributions with [i][j]
+                          containing the number of rlps in
+			  dimension i with 2^(i - 1) <= keys < 2^i
+   termptr_num:           number of terminal pointers (of all rlps)
+   termptr_dimid_num:     mapping with [i] containing the number of
+                          terminal pointers in dimension i
+   termptr_depth_num:     mapping with [i] containing the number of
+                          terminal pointers in depth i
+   nontermptr_num:        number of non-terminal pointers (of all
+                          rlps)
+   nontermptr_dimid_num:  mapping with [i] containing the number of
+                          non-terminal pointers in dimension i
+   nontermptr_depth_num:  mapping with [i] containing the number of
+                          non-terminal pointers in depth i
+   dt_elem_num:           number of elementary interval structures
+   dt_elem_ptr_num:       number of rules in all elementary interval
+                          structures
+   dt_elem_stat:          distribution with [i] containing the number
+                          of elementary interval structures with
+			  2^(i - 1) <= rules < 2^i                    */
+struct hipac_rlp_stat
+{
+	__u64 total_mem_tight;
+	__u64 total_mem_real;
+	__u64 rlp_mem_tight;
+	__u64 rlp_mem_real;
+	__u64 termrule_mem_tight;
+	__u64 termrule_mem_real;
+	__u32 rlp_num;
+	__u32 rlp_dimid_num[16];
+	__u32 rlp_depth_num[16];
+	__u32 termrule_num;
+	__u32 termrule_ptr_num;
+	__u32 keys_num;
+	__u32 rlp_dimid_keys_stat[16][18];
+	__u32 termptr_num;
+	__u32 termptr_dimid_num[16];
+	__u32 termptr_depth_num[16];
+	__u32 nontermptr_num;
+	__u32 nontermptr_dimid_num[16];
+	__u32 nontermptr_depth_num[16];
+	__u32 dt_elem_num;
+	__u32 dt_elem_ptr_num;
+	__u32 dt_elem_stat[16];
+};
+
+/* dimtree statistics
+   chain_mem_tight:         current memory consumption in bytes of
+                            a dimtree chain including the rules in
+                            terms of how much has been requested
+   chain_mem_real:          current memory consumption in bytes of
+                            a dimtree chain including the rules in
+                            terms of how much has actually been
+                            allocated
+   rule_num:                number of dimtree rules
+   rules_with_exec_matches: number of dimtree rules containing at
+                            least one function based match
+   rules_with_exec_target:  number of dimtree rules containing
+                            a function based target
+   rules_same_pos_stat:     distribution with [i] containing number
+                            of dimtree rule series of length
+                            >= 2^(i - 1) and < 2^i where all rules
+                            share the same position 
+   dt_match_stat:           mapping with [i] containing the number
+                            of dimtree rules having i non-wildcard
+                            matches                                   */
+struct hipac_dimtree_stat
+{
+	__u64 chain_mem_tight;
+	__u64 chain_mem_real;
+	__u32 rule_num;
+	__u32 rules_with_exec_matches;
+	__u32 rules_with_exec_target;
+	__u32 rules_same_pos_stat[16];
+	__u32 dt_match_stat[16];
+};
+
+/* hipac memory statistics
+   total_mem_tight:             current overall memory consumption in
+                                bytes in terms of how much has been
+                                requested
+   total_mem_real:              current overall memory consumption in
+                                bytes in terms of how much has
+                                actually been allocated
+   memhash_elem_num:            number of objects for which memory
+                                has been requested
+   memhash_len:                 number of buckets in the memory hash
+   memhash_smallest_bucket_len: number of objects in the smallest
+                                bucket of the memory hash
+   memhash_biggest_bucket_len:  number of objects in the biggest
+                                bucket of the memory hash
+   memhash_bucket_stat:         distribution with [i] containing the
+                                number of buckets with
+                                2^(i - 1) <= objects < 2^i            */
+struct hipac_mem_stat
+{
+	__u64 total_mem_tight;
+	__u64 total_mem_real;
+	__u32 memhash_elem_num;
+	__u32 memhash_len;
+	__u32 memhash_smallest_bucket_len;
+	__u32 memhash_biggest_bucket_len;
+	__u32 memhash_bucket_stat[16];
+	
+};
+
+
+/* hipac chain statistics
+   mem_tight:     current memory consumption in bytes of all
+                  hipac chains including the rules in terms of 
+		  how much has been requested
+   mem_real:      current memory consumption in bytes of all
+                  hipac chains including the rules in terms of
+		  how much has actually been allocated
+   chain_num:     number of chains
+   rule_num:      number of rules in all chains
+   paths_stat:    distribution with [i] containing the number of 
+                  chains with 2^(i - 1) <= paths < 2^i
+   incoming_stat: distribution with [i] containing the number of
+                  chains with 2^(i - 1) <= incoming edges < 2^i
+   outgoing_stat: distribution with [i] containing the number of
+                  chains with 2^(i - 1) <= outgoing edges < 2^i       */
+struct hipac_chain_stat
+{	
+	__u64 mem_tight;
+	__u64 mem_real;
+	__u32 chain_num;
+	__u32 rule_num;
+	__u32 prefix_stat[16];
+	__u32 incoming_stat[16];
+	__u32 outgoing_stat[16];
+};
+
+
+/* hipac rule statistics
+   rule_num:          number of rules 
+   exec_match_num:    number of rules with exec_matches
+   exec_target_num:   number of rules with exec_target
+   jump_target_num:   number of rules with jump target
+   return_target_num: number of rules with return target
+   hipac_match_stat:  mapping with [i] containing the number
+                      of rules with i hipac_matches
+   inv_rules_stat:    mapping with [i] containing the number
+                      of rules with i inversion flags                 */
+struct hipac_rule_stat
+{
+	__u32 rule_num;
+	__u32 exec_match_num;
+	__u32 exec_target_num;
+	__u32 jump_target_num;
+	__u32 return_target_num;
+	__u32 hipac_match_stat[16];
+	__u32 inv_rules_stat[16];
+};
+
+
+/* hipac user statistics
+   total_mem_tight: current memory consumption in bytes in terms 
+                    of how much has been requested
+   total_mem_real:  current memory consumption in bytes in terms
+                    of how much has actually been allocated
+   chain_num:       number of chains
+   rule_num:        number of rules in all chains                     */
+struct hipac_user_stat
+{
+	__u64 total_mem_tight;
+	__u64 total_mem_real;	
+	__u32 chain_num;
+	__u32 rule_num;
+};
+
+
+
+/*
+ * hipac statistics: functions
+ */
+
+/* get rlp statistics, i.e. the statistics of the internal
+   rlp representation of all rules reachable from the root chain
+   represented by the 'hipac' pointer
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_rlp_stat(void *hipac, struct hipac_rlp_stat *stat);
+
+
+/* get dimtree statistics, i.e. the statistics of the internal
+   chain representation of all rules reachable from the root chain
+   represented by the 'hipac' pointer
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_dimtree_stat(void *hipac, struct hipac_dimtree_stat *stat);
+
+
+/* get hipac memory statistics
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_mem_stat(struct hipac_mem_stat *stat);
+
+
+/* get hipac chain statistics
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_chain_stat(struct hipac_chain_stat *stat);
+
+
+/* get hipac rule statistics
+   returned statistics constains all rules of those chains that are
+   reachable from the root chain represented by the 'hipac' pointer
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_rule_stat(void *hipac, struct hipac_rule_stat *stat);
+
+
+/* get hipac user statistics
+   possible errors: HE_IMPOSSIBLE_CONDITION                           */
+hipac_error
+hipac_get_user_stat(struct hipac_user_stat *stat);
+
+#ifdef DEBUG
+/* per object debugging: selection is done by an externally defined variable
+   hipac_debug which is a bit vector of DEBUG_* */
+#  define DEBUG_HIPAC   0x01
+#  define DEBUG_DIMTREE 0x02
+#  define DEBUG_RLP     0x04
+#  define DEBUG_IHASH   0x08
+#  define DEBUG_GLOBAL  0x10
+   extern unsigned hipac_debug;
+
+hipac_error
+hipac_get_dt_rule_ptrs(const char *name, const __u32 pos, void **res);
+
+__u8
+dt_rules_have_same_position(void *hipac, void *dt_start, void *dt_rule);
+#endif
+
+#endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/ihash.c 2.6.13-patched/net/ipv4/netfilter/nf-hipac/ihash.c
--- 2.6.13/net/ipv4/netfilter/nf-hipac/ihash.c	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/ihash.c	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,463 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#include "global.h"
+#include "ihash.h"
+
+#define MAX_BUCKETS_MINI_ALLOC    (MINI_ALLOC_MAX / sizeof(void *))
+#define INC_POSSIBLE(ihash, len)  (!(ihash)->use_mini_alloc ||     \
+				   (len) <= MAX_BUCKETS_MINI_ALLOC)
+#define BUCKET_SIZE(len)          (sizeof(struct ihash_bucket) + (len) * \
+				   sizeof(struct ihash_keyval))
+#define LEN(array)                (sizeof(array) / sizeof(*(array)))
+
+
+int
+eq_val(const void *key1, const void *key2)
+{
+	return key1 == key2;
+}
+
+__u32
+ihash_func_val(const void *key)
+{
+#ifdef BIT32_ARCH
+	/* 32 bit mix function */
+	__u32 h = (__u32) key;
+	
+	h += ~(h << 15);
+	h ^=  (h >> 10);
+	h +=  (h << 3);
+	h ^=  (h >> 6);
+	h += ~(h << 11);
+	h ^=  (h >> 16);
+#else
+	/* 64 bit mix function */
+	__u64 h = (__u64) key;
+
+	h += ~(h << 32);
+	h ^=  (h >> 22);
+	h += ~(h << 13);
+	h ^=  (h >> 8);
+	h +=  (h << 3);
+	h ^=  (h >> 15);
+	h += ~(h << 27);
+	h ^=  (h >> 31);
+#endif
+	return h;
+}
+
+int
+eq_str(const void *key1, const void *key2)
+{
+	return !strcmp(key1, key2);
+}
+
+__u32
+ihash_func_str(const void *key)
+{
+	__u32 high, h = 0;
+	const char *c = key;
+
+	if (unlikely(key == NULL)) {
+		ERR("key is NULL");
+		return 0;
+	}
+	for (; *c != '\0'; c++) {
+		/* CRC variant */
+		high = h & 0xf8000000;
+		h <<= 5;
+		h ^= high >> 27;
+		h ^= *c;
+	}
+	return h;
+}
+
+static inline __u32
+near_pow2(__u32 n)
+{
+	if (n == 0 || n > 0x80000000) {
+		return 1;
+	}
+	n--;
+	n |= n >> 1;
+	n |= n >> 2;
+	n |= n >> 4;
+	n |= n >> 8;
+	n |= n >> 16;
+	return ++n;
+}
+
+struct ihash *
+ihash_new(__u32 len, int use_mini_alloc, __u32 avrg_elem_per_bucket,
+	  ihash_func_t hash_fn, eq_t eq_fn)
+{
+	struct ihash *h;
+	struct ihash_bucket **b;
+	__u32 i;
+
+	if (unlikely(hash_fn == NULL || eq_fn == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+	h = mini_alloc(sizeof(*h));
+	if (h == NULL) {
+		return NULL;
+	}
+	if (use_mini_alloc && len > MAX_BUCKETS_MINI_ALLOC) {
+		len = MAX_BUCKETS_MINI_ALLOC;
+	} else {
+		len = near_pow2(len);
+	}
+	b = use_mini_alloc ? mini_alloc(len * sizeof(*b)) :
+		hp_alloc(len * sizeof(*b), 1);
+	if (b == NULL) {
+		mini_free(h);
+		return NULL;
+	}
+	h->hash_fn = hash_fn;
+	h->eq_fn = eq_fn;
+	h->use_mini_alloc = use_mini_alloc;
+	h->avrg_elem_per_bucket = avrg_elem_per_bucket;
+	h->len = len;
+	h->elem_ct = 0;
+	h->bucket = b;
+	/* strictly speaking memset(b, 0, len * sizeof(*b)) would be wrong
+	   here because there are architectures where the machine
+	   representation of the NULL pointer is not 0x0 */
+	for (i = 0; i < len; i++) {
+		b[i] = NULL;
+	}
+	return h;
+}
+
+void
+ihash_free(struct ihash *h)
+{
+	__u32 i;
+
+	if (unlikely(h == NULL)) {
+		ARG_MSG;
+		return;
+	}
+	for (i = 0; i < h->len; i++) {
+		if (h->bucket[i] != NULL) {
+			mini_free(h->bucket[i]);
+		}
+	}
+	if (h->use_mini_alloc) {
+		mini_free(h->bucket);
+	} else {
+		hp_free(h->bucket);
+	}
+	mini_free(h);
+}
+
+/* return values:  0 : ok
+                  -1 : low memory
+                  -2 : bucket cannot be enlarged further */
+static inline int
+insert(struct ihash *h, void *key, void *val)
+{
+	struct ihash_bucket *b;
+	__u32 i;
+
+	i = HASH(h->hash_fn, key, h->len);
+	if (h->bucket[i] == NULL) {
+		/* first element in bucket */
+		b = mini_alloc(BUCKET_SIZE(1));
+		if (b == NULL) {
+			return -1;
+		}
+		b->len = 1;
+		b->kv[0].key = key;
+		b->kv[0].val = val;
+		h->elem_ct++;
+		h->bucket[i] = b;
+		return 0;
+	}
+	if (unlikely(BUCKET_SIZE(h->bucket[i]->len + 1) > MINI_ALLOC_MAX)) {
+		/* bucket cannot be enlarged further */
+		return -2;
+	}
+	if (unlikely(mini_alloc_size(BUCKET_SIZE(h->bucket[i]->len)) !=
+		     mini_alloc_size(BUCKET_SIZE(h->bucket[i]->len + 1)))) {
+		/* bucket must be enlarged */
+		b = mini_alloc(BUCKET_SIZE(h->bucket[i]->len + 1));
+		if (b == NULL) {
+			return -1;
+		}
+		b->len = h->bucket[i]->len + 1;
+		b->kv[0].key = key;
+		b->kv[0].val = val;
+		memcpy(&b->kv[1], &h->bucket[i]->kv[0],
+		       h->bucket[i]->len * sizeof(*b->kv));
+		h->elem_ct++;
+		mini_free(h->bucket[i]);
+		h->bucket[i] = b;
+		return 0;
+	}
+
+	h->bucket[i]->kv[h->bucket[i]->len].key = key;
+	h->bucket[i]->kv[h->bucket[i]->len].val = val;
+	h->bucket[i]->len++;
+	h->elem_ct++;
+	return 0;
+}
+
+/* return values like insert */
+static inline int
+rehash(struct ihash *h_old, struct ihash *h_new)
+{
+	__u32 i, j;
+	int stat;
+
+	for (i = 0; i < h_old->len; i++) {
+		if (h_old->bucket[i] == NULL) {
+			continue;
+		}
+		for (j = 0; j < h_old->bucket[i]->len; j++) {
+			stat = insert(
+				h_new, h_old->bucket[i]->kv[j].key,
+				h_old->bucket[i]->kv[j].val);
+			if (stat < 0) {
+				return stat;
+			}
+		}
+	}
+	return 0;
+}
+
+hipac_error
+ihash_insert(struct ihash **h, void *key, void *val)
+{
+	int shift = 1;
+	int do_inc = 0;
+	int stat;
+	__u32 len;
+	
+	if (unlikely(h == NULL || *h == NULL || key == NULL)) {
+		ARG_ERR;
+	}
+	len = (*h)->len;
+	while (1) {
+		if (unlikely((do_inc || (*h)->elem_ct >=
+			      len * (*h)->avrg_elem_per_bucket) &&
+			     INC_POSSIBLE(*h, len << shift))) {
+			/* increase hash table */
+			struct ihash *new;
+			
+			new = ihash_new(len << shift, (*h)->use_mini_alloc,
+					(*h)->avrg_elem_per_bucket,
+					(*h)->hash_fn, (*h)->eq_fn);
+			if (new == NULL) {
+				return HE_LOW_MEMORY;
+			}
+			stat = rehash(*h, new);
+			if (stat < 0) {
+				ihash_free(new);
+				if (stat == -2 &&
+				    INC_POSSIBLE(*h, len << ++shift)) {
+					WARN("ihash bucket full after rehash "
+					     "-> try again with more buckets");
+					continue;
+				}
+				return HE_LOW_MEMORY;
+			}
+			ihash_free(*h);
+			*h = new;
+			do_inc = 0;
+		}
+		stat = insert(*h, key, val);
+		if (stat < 0) {
+			if (stat == -2 &&
+			    (((*h)->elem_ct <
+			      len * (*h)->avrg_elem_per_bucket &&
+			      INC_POSSIBLE(*h, len << shift)) ||
+			     INC_POSSIBLE(*h, len << ++shift))) {
+				WARN("ihash bucket full after rehash -> try "
+				     "again with more buckets");
+				do_inc = 1;
+				continue;
+			}
+			return HE_LOW_MEMORY;
+		}
+		return HE_OK;
+	}
+}
+
+static inline void
+delete(struct ihash *h, int i, int j, void **val)
+{
+	struct ihash_bucket *b;
+	
+	if (unlikely(mini_alloc_size(BUCKET_SIZE(h->bucket[i]->len)) !=
+		     mini_alloc_size(BUCKET_SIZE(h->bucket[i]->len - 1)))) {
+		/* shrink bucket */
+		b = mini_alloc(BUCKET_SIZE(h->bucket[i]->len - 1));
+		if (b != NULL) {
+			b->len = h->bucket[i]->len - 1;
+			if (j > 0) {
+				memcpy(b->kv, h->bucket[i]->kv,
+				       j * sizeof(*b->kv));
+			}
+			if (h->bucket[i]->len > j + 1) {
+				memcpy(&b->kv[j], &h->bucket[i]->kv[j+1],
+				       (h->bucket[i]->len - j - 1) *
+				       sizeof(*b->kv));
+			}
+			if (val != NULL) {
+				*val = h->bucket[i]->kv[j].val;
+			}
+			mini_free(h->bucket[i]);
+			h->bucket[i] = b;
+			h->elem_ct--;
+			return;
+		} else {
+			WARN("unable to shrink ihash bucket");
+		}
+	}
+	
+ 	if (val != NULL) {
+		*val = h->bucket[i]->kv[j].val;
+	}
+	if (h->bucket[i]->len > j + 1) {
+		memmove(&h->bucket[i]->kv[j], &h->bucket[i]->kv[j + 1],
+			(h->bucket[i]->len - j - 1) * sizeof(*b->kv));
+	}
+	h->bucket[i]->len--;
+	h->elem_ct--;
+}
+
+hipac_error
+ihash_delete(struct ihash *h, const void *key, void **val)
+{
+	int i, j;
+
+	if (unlikely(h == NULL || key == NULL)) {
+		ARG_ERR;
+	}
+	i = HASH(h->hash_fn, key, h->len);
+	if (unlikely(h->bucket[i] == NULL)) {
+		goto not_contained;
+	}
+	for (j = h->bucket[i]->len - 1; j >= 0; j--) {
+		if (h->eq_fn(h->bucket[i]->kv[j].key, key)) {
+			delete(h, i, j, val);
+			return HE_OK;
+		}
+	}
+	
+ not_contained:
+	IMPOSSIBLE_CONDITION("key not contained in ihash");
+}
+
+hipac_error
+ihash_replace(struct ihash **h, const void *oldkey, void **oldval,
+	      void *newkey, void *newval)
+{
+	int i, j, stat;
+	
+	if (unlikely(h == NULL || *h == NULL || oldkey == NULL ||
+		     newkey == NULL)) {
+		ARG_ERR;
+	}
+	i = HASH((*h)->hash_fn, oldkey, (*h)->len);
+	if (unlikely((*h)->bucket[i] == NULL)) {
+		goto not_contained;
+	}
+	if (i != HASH((*h)->hash_fn, newkey, (*h)->len)) {
+		stat = ihash_insert(h, newkey, newval);
+		if (unlikely(stat < 0)) {
+			if (stat != HE_LOW_MEMORY) {
+				IMPOSSIBLE_CONDITION("ihash insert failed for"
+						     " another reason than "
+						     "low memory");
+			}
+			return stat;
+		}
+		/* a rehash might have occured so i must be recomputed */
+		i = HASH((*h)->hash_fn, oldkey, (*h)->len);
+		for (j = (*h)->bucket[i]->len - 1; j >= 0; j--) {
+			if ((*h)->eq_fn((*h)->bucket[i]->kv[j].key, oldkey)) {
+				delete(*h, i, j, oldval);
+				return HE_OK;
+			}
+		}
+		/* oldkey is not contained in h */
+		i = HASH((*h)->hash_fn, newkey, (*h)->len);
+		for (j = (*h)->bucket[i]->len - 1; j >= 0; j--) {
+			if ((*h)->eq_fn((*h)->bucket[i]->kv[j].key, newkey)) {
+				delete(*h, i, j, NULL);
+				goto not_contained;
+			}
+		}
+		IMPOSSIBLE_CONDITION("newkey not contained in ihash although "
+				     "it has been inserted");
+	}
+	for (j = (*h)->bucket[i]->len - 1; j >= 0; j--) {
+		if ((*h)->eq_fn((*h)->bucket[i]->kv[j].key, oldkey)) {
+			if (oldval != NULL) {
+				*oldval = (*h)->bucket[i]->kv[j].val;
+			}
+			(*h)->bucket[i]->kv[j].key = newkey;
+			(*h)->bucket[i]->kv[j].val = newval;
+			return HE_OK;
+		}
+	}
+
+ not_contained:
+	IMPOSSIBLE_CONDITION("oldkey not contained in ihash");
+}
+
+hipac_error
+ihash_stat(struct ihash *h, struct ihash_stat *stat)
+{
+	__u32 i;
+
+	if (unlikely(h == NULL || stat == NULL)) {
+		ARG_ERR;
+	}
+
+	stat->elem_ct = h->elem_ct;
+	stat->bucket_len = h->len;
+	stat->small_bucket_len = 0xffffffff;
+	stat->big_bucket_len = 0;
+	stat_distribution_init(stat->bucket_dist, LEN(stat->bucket_dist));
+
+	for (i = 0; i < h->len; i++) {
+		if (h->bucket[i] == NULL) {
+			stat->small_bucket_len = 0;
+			stat_distribution_add(stat->bucket_dist,
+					      LEN(stat->bucket_dist), 0);
+			continue;
+		}
+		if (h->bucket[i]->len < stat->small_bucket_len) {
+			stat->small_bucket_len = h->bucket[i]->len;
+		}
+		if (h->bucket[i]->len > stat->big_bucket_len) {
+			stat->big_bucket_len = h->bucket[i]->len;
+		}
+		stat_distribution_add(stat->bucket_dist,
+				      LEN(stat->bucket_dist),
+				      h->bucket[i]->len);
+	}
+	return HE_OK;
+}
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/ihash.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/ihash.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/ihash.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/ihash.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,285 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _IHASH_H
+#define _IHASH_H
+
+#include "mode.h"
+#include "global.h"  // hipac_error and error message macros
+
+#define HASH(fn, key, len) (fn(key) & ((len) - 1))
+
+typedef __u32 (*ihash_func_t) (const void *key);
+typedef int (*eq_t) (const void *key1, const void *key2);
+
+struct ihash_keyval
+{
+	void *key, *val;
+};
+
+struct ihash_bucket
+{
+	__u32 len;
+	struct ihash_keyval kv[0];
+};
+
+struct ihash
+{
+	ihash_func_t hash_fn;
+	eq_t eq_fn;
+	int use_mini_alloc;
+	__u32 len, elem_ct, avrg_elem_per_bucket;
+	struct ihash_bucket **bucket;
+};
+
+struct ihash_stat
+{
+	__u32 elem_ct;
+	__u32 bucket_len, small_bucket_len, big_bucket_len;
+	/* bucket_dist[i] (0 <= i <= 14) contains the number of buckets
+	   with <= 2^i - 1 (and >= 2^(i-1) if i > 0) elements; 
+	   bucket_dist[15] contains the number of buckets with >= 2^14
+	   elements */
+	__u32 bucket_dist[16];
+};
+
+
+/* equality and hash function for strings as keys */
+int
+eq_str(const void *key1, const void *key2);
+
+__u32
+ihash_func_str(const void *key);
+
+
+/* equality and hash function for values as keys */
+int
+eq_val(const void *key1, const void *key2);
+
+__u32
+ihash_func_val(const void *key);
+
+/* if the value of the (key, val) pair is not a pointer but a value ptr_to_val
+   and val_to_ptr serve as a conversion functions */
+static inline __u64
+ptr_to_val(const void *p)
+{
+#ifdef BIT32_ARCH
+	return (__u32) p;
+#else
+	return (__u64) p;
+#endif
+}
+
+static inline void *
+val_to_ptr(__u64 v)
+{
+#ifdef BIT32_ARCH
+	return (void *) (__u32) v;
+#else
+	return (void *) v;
+#endif
+}
+
+
+/* create new hash table with len' buckets whereby len' is the nearest power
+   of two >= len; if use_mini_alloc is not 0 then mini_alloc is used to
+   allcate the bucket pointer array, otherwise hp_alloc is used;
+   avrg_elem_per_bucket indicates how many elements per bucket are allowed
+   at maximum assuming that they are equally distributed; in the case
+   use_mini_alloc is not 0 and the bucket pointer array cannot be further
+   enlarged the average number of elements per bucket may be larger */
+struct ihash *
+ihash_new(__u32 len, int use_mini_alloc, __u32 avrg_elem_per_bucket,
+	  ihash_func_t hash_fn, eq_t eq_fn);
+
+void
+ihash_free(struct ihash *h);
+
+/* key must not be contained in h (this is not checked);
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ihash_insert(struct ihash **h, void *key, void *val);
+
+/* delete key and the corresponding value v from h; v stored in *val if val
+   is not NULL; key must be contained in h;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ihash_delete(struct ihash *h, const void *key, void **val);
+
+/* replace oldkey and the corresponding value v by newkey and newval; v is
+   stored in *oldval if oldval is not NULL; oldkey must be contained in h;
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ihash_replace(struct ihash **h, const void *oldkey, void **oldval,
+	      void *newkey, void *newval);
+
+/* compute statistical info about h;
+   possible errors: HE_IMPOSSIBLE_CONDITION */
+hipac_error
+ihash_stat(struct ihash *h, struct ihash_stat *stat);
+
+/* generic lookup function */
+static inline void *
+ihash_lookup(const struct ihash *h, const void *key)
+{
+	__u32 i;
+	struct ihash_keyval *kv, *end;
+
+	if (unlikely(h == NULL || key == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+	i = HASH(h->hash_fn, key, h->len);
+	if (h->bucket[i] == NULL) {
+		return NULL;
+	}
+	end = h->bucket[i]->kv + h->bucket[i]->len;
+	for (kv = h->bucket[i]->kv; kv < end; kv++) {
+		if (h->eq_fn(kv->key, key)) {
+			return kv->val;
+		}
+	}
+	return NULL;
+}
+
+/* optimized lookup function if keys are values */
+static inline void *
+ihash_lookup_val(const struct ihash *h, const void *key)
+{
+	__u32 i;
+	struct ihash_keyval *kv, *end;
+
+	if (unlikely(h == NULL || key == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+	i = HASH(ihash_func_val, key, h->len);
+	if (h->bucket[i] == NULL) {
+		return NULL;
+	}
+	end = h->bucket[i]->kv + h->bucket[i]->len;
+	for (kv = h->bucket[i]->kv; kv < end; kv++) {
+		if (kv->key == key) {
+			return kv->val;
+		}
+	}
+	return NULL;
+}
+
+/* optimized lookup function if keys are strings */
+static inline void *
+ihash_lookup_str(const struct ihash *h, const void *key)
+{
+	__u32 i;
+	struct ihash_keyval *kv, *end;
+
+	if (unlikely(h == NULL || key == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+	i = HASH(ihash_func_str, key, h->len);
+	if (i < 0 || i >= h->len || h->bucket[i] == NULL) {
+		return NULL;
+	}
+	end = h->bucket[i]->kv + h->bucket[i]->len;
+	for (kv = h->bucket[i]->kv; kv < end; kv++) {
+		if (!strcmp(kv->key, key)) {
+			return kv->val;
+		}
+	}
+	return NULL;
+}
+
+/* call fn(key) for all keys of h */
+#define IHASH_KEY_ITERATE(h, cast, fn, args...)                           \
+do {                                                                      \
+	__u32 i, j;                                                       \
+                                                                          \
+	if (unlikely((h) == NULL)) {                                      \
+		ARG_MSG;                                                  \
+		break;                                                    \
+	}                                                                 \
+	for (i = 0; i < (h)->len; i++) {                                  \
+                if ((h)->bucket[i] == NULL) {                             \
+                        continue;                                         \
+                }                                                         \
+		for (j = 0; j < (h)->bucket[i]->len; j++) {               \
+			(fn)((cast) (h)->bucket[i]->kv[j].key , ## args); \
+		}                                                         \
+	}                                                                 \
+} while (0)
+
+/* call fn(val) for all values of h */
+#define IHASH_VAL_ITERATE(h, cast, fn, args...)                           \
+do {                                                                      \
+	__u32 i, j;                                                       \
+                                                                          \
+	if (unlikely((h) == NULL)) {                                      \
+		ARG_MSG;                                                  \
+		break;                                                    \
+	}                                                                 \
+	for (i = 0; i < (h)->len; i++) {                                  \
+                if ((h)->bucket[i] == NULL) {                             \
+                        continue;                                         \
+                }                                                         \
+		for (j = 0; j < (h)->bucket[i]->len; j++) {               \
+			(fn)((cast) (h)->bucket[i]->kv[j].val , ## args); \
+		}                                                         \
+	}                                                                 \
+} while (0)
+
+/* use the following macros to iterate over all (key, val) pairs in hash:
+   IHASH_FOR_EACH(hash, key, val) {
+           // do something with key, val
+           IHASH_FOR_EACH_END;
+   }
+   IHASH_FOR_EACH_KEY and IHASH_FOR_EACH_VAL are used similarly */
+#define IHASH_FOR_EACH(h, hkey, hval)                                     \
+{                                                                         \
+	__u32 _ihash_i, _ihash_j;                                         \
+	for (_ihash_i = 0; _ihash_i < (h)->len; _ihash_i++) {             \
+		if ((h)->bucket[_ihash_i] == NULL) {                      \
+			continue;                                         \
+		}                                                         \
+		for (_ihash_j = 0; _ihash_j < (h)->bucket[_ihash_i]->len; \
+		     _ihash_j++) {                                        \
+			(hkey) = (h)->bucket[_ihash_i]->kv[_ihash_j].key; \
+			(hval) = (h)->bucket[_ihash_i]->kv[_ihash_j].val;
+
+#define IHASH_FOR_EACH_KEY(h, hkey)                                       \
+{                                                                         \
+	__u32 _ihash_i, _ihash_j;                                         \
+	for (_ihash_i = 0; _ihash_i < (h)->len; _ihash_i++) {             \
+		if ((h)->bucket[_ihash_i] == NULL) {                      \
+			continue;                                         \
+		}                                                         \
+		for (_ihash_j = 0; _ihash_j < (h)->bucket[_ihash_i]->len; \
+		     _ihash_j++) {                                        \
+			(hkey) = (h)->bucket[_ihash_i]->kv[_ihash_j].key;
+
+#define IHASH_FOR_EACH_VAL(h, hval)                                       \
+{                                                                         \
+	__u32 _ihash_i, _ihash_j;                                         \
+	for (_ihash_i = 0; _ihash_i < (h)->len; _ihash_i++) {             \
+		if ((h)->bucket[_ihash_i] == NULL) {                      \
+			continue;                                         \
+		}                                                         \
+		for (_ihash_j = 0; _ihash_j < (h)->bucket[_ihash_i]->len; \
+		     _ihash_j++) {                                        \
+			(hval) = (h)->bucket[_ihash_i]->kv[_ihash_j].val;
+
+#define IHASH_FOR_EACH_END }}} do {} while (0)
+
+#endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/mode.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/mode.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/mode.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/mode.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,240 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _MODE_H
+#define _MODE_H
+
+#include <linux/stddef.h> // offsetof
+#include <asm/page.h>
+
+/* maximal number of bytes allocatable by mini_alloc */
+#define MINI_ALLOC_MAX 131072
+
+
+/*
+ * NEVER use big_alloc and big_free. Use hp_alloc and hp_free instead.
+ * The only exceptions to this rule is the implementation of hp_alloc,
+ * hp_realloc and hp_free.
+ *
+ * mini_alloc and mini_free can be used for small (<= MINI_ALLOC_MAX bytes)
+ * data structures if one wants to avoid the overhead of hp_alloc and hp_free
+ */
+
+static inline unsigned
+big_alloc_size(unsigned size)
+{
+	return size == 0 ? 0 : (((size - 1) + PAGE_SIZE) & ~(PAGE_SIZE - 1));
+}
+
+
+#ifdef __KERNEL__
+/*
+ * Kernel space
+ */
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>    // ULONG_MAX
+#include <linux/compiler.h>  // likely, unlikely
+#include <linux/smp.h>       // smp_num_cpus, cpu_number_map, smp_processor_id
+#include <linux/rcupdate.h>  // Read Copy Update: sychronize_rcu
+#include <linux/cache.h>     // __cacheline_aligned
+#include <linux/netfilter.h> // NF_ACCEPT, NF_DROP
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/version.h>
+
+#define assert(as)           do {} while (0)
+#define printf(str, args...) printk(str , ## args)
+
+static inline unsigned
+mini_alloc_size(unsigned size)
+{
+	unsigned int s;
+#define CACHE(x) if (size <= x) { s = x; goto found;}
+#include <linux/kmalloc_sizes.h>
+	return 0;
+found:
+	return s;
+}
+
+/* for small amounts of memory only (up to 128 KB) */
+static inline void *
+mini_alloc(unsigned size)
+{
+	if (size > 0 && size <= MINI_ALLOC_MAX) {
+		return kmalloc(size, GFP_KERNEL);
+	}
+	return NULL;
+}
+
+static inline void
+mini_free(void *p)
+{
+	kfree(p);
+}
+
+/* memory is allocated in amounts of multiples of PAGE_SIZE */
+static inline void *
+big_alloc(unsigned size)
+{
+	return vmalloc(size);
+}
+
+static inline void
+big_free(void *p)
+{
+	vfree(p);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+#define synchronize_rcu(x)    synchronize_kernel(x)
+#endif
+
+/* dirty hack to make stuff work with uml (otherwise high_physmem and end_vm
+   are not defined) */
+#ifdef  CONFIG_UML_NET
+#  undef  TOP
+#  ifdef  CONFIG_HOST_2G_2G
+#    define TOP 0x80000000
+#  else
+#    define TOP 0xc0000000
+#  endif
+#  undef  SIZE
+#  define SIZE  ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000)
+#  undef  START
+#  define START (TOP - SIZE)
+#  undef  VMALLOC_OFFSET
+#  define VMALLOC_OFFSET (8 * 1024 * 1024)
+#  undef  VMALLOC_START
+#  define VMALLOC_START (((unsigned long) (START + 32 * 1024 * 1024) + \
+ 	VMALLOC_OFFSET) & ~(VMALLOC_OFFSET - 1))
+static unsigned long high_physmem = START + 32 * 1024 * 1024;
+static unsigned long end_vm       = VMALLOC_START + 32 * 1024 * 1024;
+#endif  /* CONFIG_UML_NET */
+
+
+
+
+#else /* __KERNEL__ */
+/*
+ * User space
+ */
+#include <features.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#  include <asm/types.h>
+#else /* libc5 */
+#  include <linux/types.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>  // ULONG_MAX
+#include <assert.h>
+#include <malloc.h>
+
+/* no assertions if not debugging */
+#ifndef DEBUG
+#  undef  assert
+#  define assert(as) do {} while (0)
+#endif
+
+/* locking unnecessary in user space */
+#define synchronize_rcu(x)    do {} while (0)
+
+/* printk compatibility */
+#define KERN_EMERG    "KERN_EMERG: "
+#define KERN_ALERT    "KERN_ALERT: "
+#define KERN_CRIT     "KERN_CRIT: "
+#define KERN_ERR      "KERN_ERR: "
+#define KERN_WARNING  "KERN_WARNING: "
+#define KERN_NOTICE   "KERN_NOTICE: "
+#define KERN_INFO     "KERN_INFO: "
+#define KERN_DEBUG    "KERN_DEBUG: "
+#define printk(str, args...) printf(str , ## args)
+
+/* netfilter verdict compatibility */
+#define NF_DROP   0
+#define NF_ACCEPT 1
+
+/* macro to annotate likely branch directions which results in the
+   blocks being reordered appropriately */
+#if __GNUC__ == 2 && __GNUC_MINOR__ < 96
+#  define __builtin_expect(x, expected_value) (x)
+#  define likely(x)   __builtin_expect((x), 1)
+#  define unlikely(x) __builtin_expect((x), 0)
+#endif
+
+static inline unsigned
+mini_alloc_size(unsigned size)
+{
+	unsigned int s;
+#define CACHE(x) if (size <= x) { s = x; goto found;}
+	CACHE(32);
+	CACHE(64);
+	CACHE(96);
+	CACHE(128);
+	CACHE(192);
+	CACHE(256);
+	CACHE(512);
+	CACHE(1024);
+	CACHE(2048);
+	CACHE(4096);
+	CACHE(8192);
+	CACHE(16384);
+	CACHE(32768);
+	CACHE(65536);
+	CACHE(131072);
+	return 0;
+found:
+	return s;
+}
+
+/* for small amounts of memory only (up to 128 KB) */
+static inline void *
+mini_alloc(unsigned size)
+{
+	return malloc(mini_alloc_size(size));
+}
+
+static inline void
+mini_free(void *p)
+{
+	free(p);
+}
+
+/* memory is allocated in amounts of multiples of PAGE_SIZE */
+static inline void *
+big_alloc(unsigned size)
+{
+	return malloc(big_alloc_size(size));
+}
+
+static inline void
+big_free(void *p)
+{
+	free(p);
+}
+
+#endif /* __KERNEL__ */
+
+#endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/nf_hipac_cthelp.c 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nf_hipac_cthelp.c
--- 2.6.13/net/ipv4/netfilter/nf-hipac/nf_hipac_cthelp.c	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nf_hipac_cthelp.c	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,49 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include "nfhp_mod.h"
+
+static int
+__init init(void)
+{
+	need_ip_conntrack();
+	if (nfhp_register_cthelp(THIS_MODULE)) {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void
+__exit fini(void)
+{
+	nfhp_unregister_cthelp(THIS_MODULE);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_AUTHOR("Michael Bellion and Thomas Heinz");
+MODULE_DESCRIPTION("nf-HiPAC - connection tracking dependency helper module");
+MODULE_LICENSE("GPL");
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_com.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_com.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_com.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_com.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,330 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _NFHP_COM_H
+#define _NFHP_COM_H
+
+#ifdef __KERNEL__
+#  include <linux/if.h>
+#  include <linux/in.h>
+#  include <linux/types.h>
+#endif
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netlink.h>
+#include "hipac.h"
+
+
+/* a similar line will hopefully make its way into netlink.h */
+#define NETLINK_NFHIPAC	    26
+#define NLHP_PROTO          NETLINK_NFHIPAC
+#define NLHP_TYPE           0xFADE
+
+/* dimension id's */
+enum {
+	DIMID_STATE,
+	DIMID_SRC_IP,
+	DIMID_DEST_IP,
+	DIMID_INIFACE,
+	DIMID_OUTIFACE,
+	DIMID_PROTO,
+	DIMID_FRAGMENT,
+	DIMID_DPORT,
+	DIMID_SPORT,
+	DIMID_SYN,
+	DIMID_ICMP_TYPE,
+	DIMID_TTL,
+	NUMBER_OF_DIM
+};
+
+/* bit types */
+#define BIT_STATE       BIT_U16
+#define BIT_SRC_IP      BIT_U32
+#define BIT_DEST_IP     BIT_U32
+#define BIT_INIFACE     BIT_U16
+#define BIT_OUTIFACE    BIT_U16
+#define BIT_PROTO       BIT_U16
+#define BIT_FRAGMENT    BIT_U16
+#define BIT_DPORT       BIT_U16
+#define BIT_SPORT       BIT_U16
+#define BIT_SYN         BIT_U16
+#define BIT_ICMP_TYPE   BIT_U16
+#define BIT_TTL         BIT_U16
+
+/* origin bits */
+#define NFHP_ORIGIN_INPUT   0x1
+#define NFHP_ORIGIN_FORWARD 0x2
+#define NFHP_ORIGIN_OUTPUT  0x4
+
+/* hipac_error and nfhipac_error together form the netlink error messages */
+typedef enum
+{
+	NFHE_INDEX   = HE_NEXT_ERROR,      // Unable to retrieve unused ifindex
+	NFHE_NOMSG   = HE_NEXT_ERROR - 1,  // Incorrect message format
+	NFHE_CMD     = HE_NEXT_ERROR - 2,  // Invalid command
+	NFHE_LABEL   = HE_NEXT_ERROR - 3,  // Empty chain label
+	NFHE_NLABEL  = HE_NEXT_ERROR - 4,  // Empty new chain label
+	NFHE_POLICY  = HE_NEXT_ERROR - 5,  // Invalid policy
+	NFHE_ACTION  = HE_NEXT_ERROR - 6,  // Invalid action
+	NFHE_NMCT    = HE_NEXT_ERROR - 7,  // Invalid native match count
+	NFHE_IEOFF   = HE_NEXT_ERROR - 8,  // Invalid target_offset/next_offset
+	                                   // in ipt_entry
+	NFHE_SORT    = HE_NEXT_ERROR - 9,  // Native matches not sorted or 
+	                                   // dimid duplicate
+        NFHE_MINT    = HE_NEXT_ERROR - 10, // Invalid interval in native match
+	NFHE_DEVA    = HE_NEXT_ERROR - 11, // Native interface match but no 
+                                           // corresponding interface name
+	NFHE_DEVB    = HE_NEXT_ERROR - 12, // Interface name but no corres-
+	                                   // ponding native interface match
+	NFHE_FRAG    = HE_NEXT_ERROR - 13, // Invalid fragment match
+	NFHE_PROTO   = HE_NEXT_ERROR - 14, // Invalid protocol match
+        NFHE_SYN     = HE_NEXT_ERROR - 15, // Invalid syn match
+	NFHE_STATE   = HE_NEXT_ERROR - 16, // Invalid state match
+	NFHE_TCP     = HE_NEXT_ERROR - 17, // tcp match dependency failure
+	NFHE_TCPUDP  = HE_NEXT_ERROR - 18, // tcp or udp match dependency failure
+	NFHE_ICMP    = HE_NEXT_ERROR - 19, // icmp match dependency failure
+	NFHE_CMPMIS  = HE_NEXT_ERROR - 20, // Missing cmp_len array
+	NFHE_CMPSH   = HE_NEXT_ERROR - 21, // cmp_len array too short
+	NFHE_CMPLA   = HE_NEXT_ERROR - 22, // cmp_len array contains a value
+	                                   // larger than the corresponding
+	                                   // ipt match/target size
+	NFHE_ORIGIN  = HE_NEXT_ERROR - 23, // Illegal combination of matches
+                                           // (no valid origin)
+	NFHE_IPTMSZ  = HE_NEXT_ERROR - 24, // Invalid ipt match size
+	NFHE_IPTMCH  = HE_NEXT_ERROR - 25, // checkentry fails for ipt match
+	NFHE_IPTTMI  = HE_NEXT_ERROR - 26, // Missing ipt target
+	NFHE_IPTTSZ  = HE_NEXT_ERROR - 27, // Invalid ipt target size
+	NFHE_IPTTCH  = HE_NEXT_ERROR - 28, // checkentry fails for ipt target
+	NFHE_TOFF    = HE_NEXT_ERROR - 29, // Invalid target_offset
+	NFHE_CHAINE  = HE_NEXT_ERROR - 30, // Empty chain name
+	NFHE_CHAINL  = HE_NEXT_ERROR - 31, // Chain name too long
+	NFHE_CT      = HE_NEXT_ERROR - 32, // Kernel does not have support for 
+	                                   // connection tracking, please recompile
+	NFHE_CTHELP  = HE_NEXT_ERROR - 33, // Unable to load connection tracking
+	                                   // helper module (nf_hipac_cthelp.o)
+	NFHE_ILL     = HE_NEXT_ERROR - 34, // Illegal condition
+	NFHE_IMPL    = HE_NEXT_ERROR - 35, // Feature not yet implemented
+	NFHE_SYSOFF  = HE_NEXT_ERROR - 36  // - offset for system errno's -
+} nfhipac_error;
+
+/* errno is positive */
+#define ERRNO_TO_NFHE(e) (NFHE_SYSOFF - e)
+#define NFHE_TO_ERRNO(e) (NFHE_SYSOFF - e)
+
+/* connection tracking states */
+typedef enum
+{
+	NFHP_STATE_ESTABLISHED,
+	NFHP_STATE_RELATED,
+	NFHP_STATE_NEW,
+	NFHP_STATE_INVALID,
+	NFHP_STATE_UNTRACKED,
+	NFHP_STATE_NUM_VALID = NFHP_STATE_INVALID
+} nfhp_state;
+
+/* netlink commands */
+#define CMD_NONE           0
+#define CMD_MIN            1
+#define CMD_APPEND         1
+#define CMD_INSERT         2
+#define CMD_DELETE_RULE    3
+#define CMD_DELETE_POS     4 
+#define CMD_REPLACE        5
+#define CMD_FLUSH          6
+#define CMD_NEW_CHAIN      7
+#define CMD_DELETE_CHAIN   8
+#define CMD_RENAME_CHAIN   9
+#define CMD_SET_POLICY    10
+#define CMD_LIST          11
+#define CMD_MAX           11
+
+struct nfhp_rule
+{
+	char indev[IFNAMSIZ];
+	char outdev[IFNAMSIZ];
+	struct hipac_rule r;
+	struct hipac_match m[NUMBER_OF_DIM];  // == r.first_match
+	/* we cannot use aligned(__alignof__(u_int64_t)) instead of
+	   aligned(8) because of incompatibilities in gcc versions */
+	struct ipt_entry e[0] __attribute__((aligned(8)));
+};
+
+struct nfhp_chain
+{
+	char label[HIPAC_CHAIN_NAME_MAX_LEN];
+	char newlabel[HIPAC_CHAIN_NAME_MAX_LEN];
+	u_int8_t policy;
+};
+
+/* universal macros which can be used for USER <-> KERNEL (both directions) */
+#define HAS_IPT_MATCH(r)      ((r)->match_offset > 0)
+#define HAS_IPT_TARGET(r)     ((r)->action == TARGET_EXEC)
+#define HAS_CHAIN_TARGET(r)   ((r)->action == TARGET_CHAIN)
+#define NEXT_IPT_MATCH(m)     ((struct ipt_entry_match *)          \
+			       ((char *) (m) + (m)->u.match_size))
+
+
+/*
+ * netlink communication: USER -> KERNEL
+ */
+
+/* command sent to kernel; only the necessary parts (depending on the command
+   type) must be filled in;
+
+  this is how a nfhp_cmd really looks like:
+  --------------------------------------------
+  |              nfhp_cmd struct             |
+  |------------------------------------------|
+  |                ipt_entry                 |
+  |------------------------------------------|
+  |             ipt_entry_match 1            |
+  |------------------------------------------|
+  |                  . . .                   |
+  |------------------------------------------|
+  |             ipt_entry_match m            |
+  |------------------------------------------|
+  |             ipt_entry_target             |
+  |                    or                    |
+  |                chain label               |
+  |------------------------------------------|
+  |          cmp_len array of size m         |
+  |  or m + 1 if ipt_entry_target available  |
+  --------------------------------------------
+
+  ipt_entry, ipt_entry_matches, ipt_entry_target / chain label and cmp_len are
+  optional; here are the rules defining their presence:
+  1) if the rule action is TARGET_EXEC there is an ipt_entry_target
+  2) if the rule action is TARGET_CHAIN there is a chain label
+  3) if there is an ipt_entry_match or ipt_entry_target or chain label there
+     is an ipt_entry
+  4) if there is an ipt_entry and cmd is CMD_DELETE_RULE there is cmp_len
+
+  => the smallest command simply consists of the nfhp_cmd struct
+
+  struct nfhp_cmd contains an embedded struct hipac_rule; set its member
+  match_offset to a value > 0 if there is at least one ipt_entry_match;
+  otherwise it must be 0; you don't have to specify the following members:
+                           size, origin, target_offset
+
+  if ipt_entry exists you only have to specify the following members:
+                           target_offset, next_offset
+
+  note: - the iptables_matches and the iptables_target must be aligned with
+          the IPT_ALIGN macro
+*/
+struct nfhp_cmd
+{
+	u_int32_t cmd;
+	struct nfhp_chain chain;
+	struct nfhp_rule rule;
+};
+
+/* macros to access nfhp_cmd; hr is a pointer to the embedded hipac_rule */
+#define HAS_IPT_ENTRY(hr)      (HAS_IPT_MATCH(hr) || HAS_IPT_TARGET(hr) || \
+			        HAS_CHAIN_TARGET(hr))
+#define HAS_CMP_LEN(cmd, hr)   ((cmd) == CMD_DELETE_RULE && \
+				(HAS_IPT_MATCH(hr) || HAS_IPT_TARGET(hr)))
+#define NFHP_RULE(hr)          ((struct nfhp_rule *)              \
+			        ((char *) (hr) - (unsigned long)  \
+			 	 (&((struct nfhp_rule *) 0)->r)))
+#define IPT_ENTRY(hr)          (NFHP_RULE(hr)->e)
+#define FIRST_IPT_MATCH_IE(hr) ((struct ipt_entry_match *) \
+				NFHP_RULE(hr)->e->elems)
+#define IPT_ENTRY_END(hr)      FIRST_IPT_MATCH_IE(hr)
+#define IPT_TARGET_IE(hr)      ((struct ipt_entry_target *)        \
+			        ((char *) NFHP_RULE(hr)->e +       \
+				 NFHP_RULE(hr)->e->target_offset))
+#define IPT_MATCH_END_IE(r)    ((struct ipt_entry_match *) IPT_TARGET_IE(r))
+#define CHAIN_TARGET_IE(hr)    ((char *) IPT_TARGET_IE(hr))
+#define CMP_LEN(hr)            ((u16 *) ((char *) IPT_ENTRY(hr) +     \
+					 IPT_ENTRY(hr)->next_offset))
+
+
+
+/*
+ * netlink communication: KERNEL -> USER
+ */
+
+/*
+  in reply to a CMD_LIST command the kernel sends a series of packets to
+  the userspace; each packet is filled as much as possible so that the
+  number of packets being transfered is reduced to a minimum;
+  in case of an error which can happen sometime during the
+  transmission a packet containing the error number is sent (int32_t);
+  the data sent to the userspace is organized in the following way:
+  |struct nfhp_list_chain (chain 1)|rule 1|padding|...|rule n_1|padding|
+  |                   .   .   .   .                                    |
+  |struct nfhp_list_chain (chain k)|rule 1|padding|...|rule n_k|padding|
+  the rules are of the type struct nfhp_rule;
+  
+  this is how a nfhp_list_rule really looks like:
+  --------------------------------------------
+  |           nfhp_list_rule struct          |
+  |------------------------------------------|
+  |               hipac_match 1              |
+  |------------------------------------------|
+  |                  . . .                   |
+  |------------------------------------------|
+  |               hipac_match n              |
+  |------------------------------------------|
+  |             ipt_entry_match 1            |
+  |------------------------------------------|
+  |                  . . .                   |
+  |------------------------------------------|
+  |             ipt_entry_match m            |
+  |------------------------------------------|
+  |             ipt_entry_target             |
+  |                    or                    |
+  |                chain label               |
+  --------------------------------------------
+
+  the number of hipac_matches depends on native_mct (member of hipac_rule);
+  there is neither ipt_entry nor cmp_len;
+
+  IMPORTANT: - there might be a padding between two consecutive rules
+               in order to meet the alignment requirements for rules which
+	       contain 64 bit members; so you have to use the IPT_ALIGN
+               macro to jump to the next rule; note that there is no padding
+               after a chain because it contains 64 bit members which
+	       enforce the strictest alignment on the system
+*/
+struct nfhp_list_chain
+{
+	char label[HIPAC_CHAIN_NAME_MAX_LEN];
+	u_int8_t policy;
+	u_int32_t rule_num;
+};
+
+struct nfhp_list_rule
+{
+        char indev[IFNAMSIZ];
+        char outdev[IFNAMSIZ];
+        struct hipac_rule r;
+};
+
+/* these macros together with the universal macros can be used to access
+   nfhp_list_rule */
+#define FIRST_IPT_MATCH(r) ((struct ipt_entry_match *)          \
+			    ((char *) (r) + (r)->match_offset))
+#define IPT_TARGET(r)      ((struct ipt_entry_target *)          \
+			    ((char *) (r) + (r)->target_offset))
+#define IPT_MATCH_END(r)   ((struct ipt_entry_match *) IPT_TARGET(r))
+#define CHAIN_TARGET(r)    ((char *) IPT_TARGET(r))
+
+#endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_dev.c 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_dev.c
--- 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_dev.c	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_dev.c	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,308 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/notifier.h>
+#include "nfhp_com.h"
+#include "nfhp_dev.h"
+
+
+#define IFNAME_MAP_INIT_LEN 31
+
+struct ifname_map_t
+{
+	u32 len;
+	u32 size;
+	struct
+	{
+		char *ifname;
+		u16 vindex;
+	} map[0];
+};
+
+static struct ifname_map_t *ifname_map;
+static char (*ifnames)[IFNAMSIZ];
+struct nf_hipac_dev_ifindex_map_t nf_hipac_dev_ifindex_map
+__attribute__((aligned(SMP_CACHE_BYTES)));
+
+static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;
+
+static int
+init_data(void)
+{
+	ifname_map = kmalloc(sizeof(ifname_map) + IFNAME_MAP_INIT_LEN *
+			     sizeof(*ifname_map->map), GFP_KERNEL);
+	if (ifname_map == NULL) {
+		return -ENOMEM;
+	}
+
+	ifnames = kmalloc(IFNAME_MAP_INIT_LEN * sizeof(*ifnames), GFP_KERNEL);
+	if (ifnames == NULL) {
+		kfree(ifname_map);
+		return -ENOMEM;
+	}
+	memset(&nf_hipac_dev_ifindex_map, 0, sizeof(nf_hipac_dev_ifindex_map));
+	memset(ifname_map, 0, sizeof(ifname_map) + IFNAME_MAP_INIT_LEN *
+	       sizeof(*ifname_map->map));
+	memset(ifnames, 0, IFNAME_MAP_INIT_LEN * sizeof(*ifnames));
+	ifname_map->size = IFNAME_MAP_INIT_LEN;
+	return 0;
+}
+
+static void
+free_data(void)
+{
+	if (ifname_map != NULL) {
+		kfree(ifname_map);
+		ifname_map = NULL;
+	}
+
+	if (ifnames != NULL) {
+		kfree(ifnames);
+		ifnames = NULL;
+	}
+}
+
+static void
+ifindex_map_add_replace(u16 ifindex, u16 vindex)
+{
+	u16 i;
+	for (i = 0; i < nf_hipac_dev_ifindex_map.len; i++) {
+		if (nf_hipac_dev_ifindex_map.map[i].ifindex == ifindex) {
+			nf_hipac_dev_ifindex_map.map[i].vindex = vindex;
+			return;
+		}
+	}
+	for (i = 0; i < nf_hipac_dev_ifindex_map.len; i++) {
+		if (nf_hipac_dev_ifindex_map.map[i].ifindex == 0) {
+			nf_hipac_dev_ifindex_map.map[i].ifindex = ifindex;
+			nf_hipac_dev_ifindex_map.map[i].vindex = vindex;
+			return;
+		}
+	}
+	if (nf_hipac_dev_ifindex_map.len < NF_HIPAC_MAX_UP_INTERFACES) {
+		nf_hipac_dev_ifindex_map.map[nf_hipac_dev_ifindex_map.len]
+			.ifindex = ifindex;
+		nf_hipac_dev_ifindex_map.map[nf_hipac_dev_ifindex_map.len]
+			.vindex = vindex;
+		nf_hipac_dev_ifindex_map.len++;
+	} else {
+		printk(KERN_ERR "NF_HiPAC: too much interfaces UP at the "
+		       "same time. Please increase NF_HIPAC_MAX_UP_INTERFACES "
+		       "in nf_hipac_dev.h and recompile!");
+	}
+	return;
+}
+
+static void
+ifindex_map_del(u16 ifindex)
+{
+	u16 i;
+	for (i = 0; i < nf_hipac_dev_ifindex_map.len; i++) {
+		if (nf_hipac_dev_ifindex_map.map[i].ifindex == ifindex) {
+			nf_hipac_dev_ifindex_map.map[i].ifindex = 0;
+			nf_hipac_dev_ifindex_map.map[i].vindex = 0;
+			return;
+		}
+	}
+	return;
+}
+
+int
+ifname_map_lookup_vindex(const char *ifname)
+{
+	u16 pos;
+	int cmp;
+	u32 start = 1;
+	u32 stop = ifname_map->len;
+
+	while (stop >= start) {
+		pos = ((start + stop) >> 1) - 1;
+		cmp = strcmp(ifname_map->map[pos].ifname, ifname);
+		if (cmp < 0) {
+			start = pos + 2;
+		} else if (cmp > 0) {
+			stop = pos;
+		} else {
+			return ifname_map->map[pos].vindex;
+		}
+	}
+	return -1;
+}
+
+int
+nf_hipac_dev_lookup_ifname(int vindex, char ifname[])
+{
+	if (vindex < 1 || vindex > ifname_map->len)
+		return -1;
+	strlcpy(ifname, ifnames[vindex - 1], IFNAMSIZ);
+	return 0;
+}
+
+int
+nf_hipac_dev_get_vindex(const char *ifname)
+{
+	u32 max = 0;
+	u32 start = 1;
+	u16 pos;
+	u32 stop;
+	int cmp;
+	struct net_device *dev;
+
+	if (unlikely(ifname_map->len == 0)) {
+		strlcpy(ifnames[0], ifname, sizeof(*ifnames));
+		dev = dev_get_by_name(ifname);
+		spin_lock_bh(&dev_lock);
+		ifname_map->len = 1;
+		ifname_map->map[0].ifname = ifnames[0];
+		ifname_map->map[0].vindex = 1;
+		if (dev) {
+			if (dev->flags & IFF_UP)
+				ifindex_map_add_replace(dev->ifindex, 1);
+			dev_put(dev);
+		}
+		spin_unlock_bh(&dev_lock);
+		return 1;
+	}
+
+	stop = ifname_map->len;
+	while (stop >= start) {
+		pos = ((start + stop) >> 1) - 1;
+		cmp = strcmp(ifname_map->map[pos].ifname, ifname);
+		if (cmp < 0) {
+			start = pos + 2;
+		} else if (cmp > 0) {
+			stop = pos;
+			max = pos + 1;
+		} else {
+			return ifname_map->map[pos].vindex;
+		}
+	}
+	if (max == 0) {
+		/* max has not been touched (otherwise it must be >= 1)
+		   => new ifname is "maximal" */
+		pos = ifname_map->len;
+	} else {
+		pos = max - 1;
+	}
+
+	if (ifname_map->len == 65535) {
+		return NFHE_INDEX;
+	}
+
+	/* new vindex required -> do reallocations first if necessary */
+	if (unlikely(ifname_map->len == ifname_map->size)) {
+		u32 newsize = ((ifname_map->size + 1) << 1) - 1;
+		struct ifname_map_t *new_ifname_map;
+		char (*new_ifnames)[IFNAMSIZ];
+		new_ifname_map = kmalloc(sizeof(new_ifname_map) + newsize *
+					 sizeof(*new_ifname_map->map),
+					 GFP_KERNEL);
+		if (new_ifname_map == NULL) {
+			return HE_LOW_MEMORY;
+		}
+		new_ifnames = kmalloc(newsize * sizeof(*new_ifnames),
+				      GFP_KERNEL);
+		if (new_ifnames == NULL) {
+			kfree(new_ifname_map);
+			return HE_LOW_MEMORY;
+		}
+		memcpy(new_ifname_map, ifname_map, sizeof(new_ifname_map) +
+		       ifname_map->size * sizeof(*new_ifname_map->map));
+		new_ifname_map->size = newsize;
+		memcpy(new_ifnames, ifnames,
+		       ifname_map->size * sizeof(*new_ifnames));
+		strlcpy(new_ifnames[ifname_map->len], ifname,
+			sizeof(*new_ifnames));
+		dev = dev_get_by_name(ifname);
+		spin_lock_bh(&dev_lock);
+		kfree(ifname_map);
+		kfree(ifnames);
+		ifname_map = new_ifname_map;
+		ifnames = new_ifnames;
+	} else {
+		strlcpy(ifnames[ifname_map->len], ifname, sizeof(*ifnames));
+		dev = dev_get_by_name(ifname);
+		spin_lock_bh(&dev_lock);
+	}
+	
+	if (pos < ifname_map->len) {
+		memmove(&ifname_map->map[pos + 1], &ifname_map->map[pos],
+			(ifname_map->len - pos) * sizeof(*ifname_map->map));
+	}
+	ifname_map->map[pos].ifname = ifnames[ifname_map->len];
+	ifname_map->map[pos].vindex = ++ifname_map->len;
+	if (dev) {
+		if (dev->flags & IFF_UP)
+			ifindex_map_add_replace(dev->ifindex, ifname_map->len);
+		dev_put(dev);
+	}
+	spin_unlock_bh(&dev_lock);
+	return ifname_map->len;
+}
+
+static int
+nf_hipac_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	int vindex;
+	struct net_device *dev = ptr;
+	switch (event) {
+	case NETDEV_UP:
+		spin_lock_bh(&dev_lock);
+		vindex = ifname_map_lookup_vindex(dev->name);
+		if (vindex > 0) {
+			// interface is in ruleset => add to ifindex_map
+			ifindex_map_add_replace(dev->ifindex, vindex);
+		}
+		spin_unlock_bh(&dev_lock);
+		break;
+		
+	case NETDEV_DOWN:
+		spin_lock_bh(&dev_lock);
+		ifindex_map_del(dev->ifindex);
+		spin_unlock_bh(&dev_lock);
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block nf_hipac_dev_notifier = {
+        .notifier_call  = nf_hipac_dev_event,
+};
+
+int
+nf_hipac_dev_init(void)
+{
+	int stat;
+
+	stat = init_data();
+	if (stat < 0) {
+		return stat;
+	}
+	stat = register_netdevice_notifier(&nf_hipac_dev_notifier);
+	if (stat < 0) {
+		free_data();
+		return stat;
+	}
+	return 0;
+}
+
+void
+nf_hipac_dev_exit(void)
+{
+	unregister_netdevice_notifier(&nf_hipac_dev_notifier);
+	free_data();
+}
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_dev.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_dev.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_dev.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_dev.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,66 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _NF_HIPAC_DEV_H
+#define _NF_HIPAC_DEV_H
+
+#define NF_HIPAC_MAX_UP_INTERFACES 255
+
+struct nf_hipac_dev_ifindex_map_t
+{
+	u16 len;
+	struct
+	{
+		u16 ifindex;
+		u16 vindex;
+	} map[NF_HIPAC_MAX_UP_INTERFACES];
+};
+
+/* mapping from interface index to virtual interface index */
+extern struct nf_hipac_dev_ifindex_map_t nf_hipac_dev_ifindex_map;
+
+/* call init during module initialization; if something fails a negative 
+   errno is returned, otherwise 0 is returned */
+int
+nf_hipac_dev_init(void);
+
+/* call exit during module finalization */
+void
+nf_hipac_dev_exit(void);
+
+/* copies the device name corresponding to vindex to ifname which should
+   be at least IFNAMSIZ bytes large and return 0;
+   if vindex cannot be found a value < 0 is returned */
+int
+nf_hipac_dev_lookup_ifname(int vindex, char ifname[]);
+
+/* return the corresponding virtual interface index if the interface is
+   already known; otherwise the interface is added to the list of known
+   non-existing interfaces and a new virtual interface index is returned;
+   if something fails a nfhipac_error is returned */
+int
+nf_hipac_dev_get_vindex(const char *ifname);
+
+/* return virtual interface index corresponding to ifindex */
+static inline u16
+nf_hipac_dev_ifindex_to_vindex(u16 ifindex)
+{
+	u16 i;
+	for (i = 0; i < nf_hipac_dev_ifindex_map.len; i++) 
+		if (nf_hipac_dev_ifindex_map.map[i].ifindex == ifindex)
+			return nf_hipac_dev_ifindex_map.map[i].vindex;
+	return 0;
+}
+
+#endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_mod.c 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_mod.c
--- 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_mod.c	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_mod.c	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,1734 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/netlink.h>
+#include <net/sock.h>
+#include <linux/types.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/ip.h>
+#include <linux/spinlock.h>
+#include <linux/netfilter.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/completion.h>
+#include "nfhp_mod.h"
+#include "nfhp_com.h"
+#include "nfhp_dev.h"
+#include "nfhp_proc.h"
+#include "hipac.h"
+
+#define MAX(a, b)  ((a) >= (b) ? (a) : (b))
+#define MIN(a, b)  ((a) >= (b) ? (b) : (a))
+#define SKB_LEN(s) (((s)->end - (s)->tail) - NLMSG_LENGTH(0))
+
+/* hook match functions */
+static nf_hookfn input_match;
+static nf_hookfn forward_match;
+static nf_hookfn output_match;
+
+/* hipac data structures for INPUT, FORWARD and OUPUT hook plus
+   their corresponding netfilter ops */
+void *hipac_input   = NULL;
+void *hipac_forward = NULL;
+void *hipac_output  = NULL;
+struct nf_hook_ops input_op =
+{
+	.hook           = input_match,
+	.owner          = THIS_MODULE,
+	.pf             = PF_INET,
+	.hooknum        = NF_IP_LOCAL_IN,
+	.priority       = NF_IP_PRI_FILTER - 1,
+};
+struct nf_hook_ops forward_op =
+{
+	.hook           = forward_match,
+	.owner          = THIS_MODULE,
+	.pf             = PF_INET,
+	.hooknum        = NF_IP_FORWARD,
+	.priority       = NF_IP_PRI_FILTER - 1,
+};
+struct nf_hook_ops output_op  =
+{
+	.hook           = output_match,
+	.owner          = THIS_MODULE,
+	.pf             = PF_INET,
+	.hooknum        = NF_IP_LOCAL_OUT,
+	.priority       = NF_IP_PRI_FILTER - 1,
+};
+
+/* used to serialize hipac modifications caused by netlink commands and
+   the interface handling module */
+DECLARE_MUTEX(nlhp_lock);
+
+static struct sock *nfhp_sock = NULL;
+
+/* connection tracking dependency helper module */
+static DECLARE_MUTEX(cthelp_lock);
+static struct module *nfhp_cthelp_module = NULL;
+
+DECLARE_COMPLETION(thread_comp);
+static int threadID = 0;
+DECLARE_WAIT_QUEUE_HEAD(thread_wait);
+
+/* latest hipac_get_chain_info snapshot */
+struct list_info
+{
+	struct hipac_chain_info *inf;
+	u32 len;
+};
+static struct list_info linfo = {NULL, 0};
+
+struct packet
+{
+	int hook;
+	struct sk_buff **skbuff;
+	const struct net_device *indev, *outdev;
+};
+
+
+
+/*
+ * dimension extractor functions
+ */
+
+static u32
+get_state(const void *pkt, int *hotdrop)
+{
+#ifdef CONFIG_IP_NF_CONNTRACK
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	unsigned int result;
+        enum ip_conntrack_info ctinfo;
+	if (skb->nfct == &ip_conntrack_untracked.ct_general)
+		result = NFHP_STATE_UNTRACKED;
+	else if (!ip_conntrack_get(skb, &ctinfo))
+			result = NFHP_STATE_INVALID;
+	else	result = ctinfo % NFHP_STATE_NUM_VALID;
+	return result;
+#else
+	return 0;
+#endif
+}
+
+static u32
+get_src_ip(const void *pkt, int *hotdrop)
+{
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	return ntohl(skb->nh.iph->saddr);
+}
+
+static u32
+get_dst_ip(const void *pkt, int *hotdrop)
+{
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	return ntohl(skb->nh.iph->daddr);
+}
+
+static u32
+get_iniface(const void *pkt, int *hotdrop)
+{
+	return nf_hipac_dev_ifindex_to_vindex(((struct packet*) pkt)
+					      ->indev->ifindex);
+}
+
+static u32
+get_outiface(const void *pkt, int *hotdrop)
+{
+	return nf_hipac_dev_ifindex_to_vindex(((struct packet*)	pkt)
+					      ->outdev->ifindex);
+}
+
+static u32
+get_proto(const void *pkt, int *hotdrop)
+{
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	return skb->nh.iph->protocol;
+}
+
+static u32
+get_fragment(const void *pkt, int *hotdrop)
+{
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	int offset = ntohs(skb->nh.iph->frag_off) & IP_OFFSET;
+	if (unlikely(offset)) {
+		if (unlikely(offset == 1 &&
+			     skb->nh.iph->protocol == IPPROTO_TCP)) {
+			printk(KERN_NOTICE "Dropping evil TCP offset=1 "
+			       "fragment.\n");
+			*hotdrop = 1;
+		}
+		return 1;
+	}
+	return 0;
+}
+
+static u32
+get_dport(const void *pkt, int *hotdrop)
+{
+	struct udphdr _udph, *uh;
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	uh = skb_header_pointer(skb, skb->nh.iph->ihl*4, 
+				sizeof(_udph), &_udph);
+	if (unlikely(uh == NULL)) {
+		/* We've been asked to examine this packet, and we
+		   can't.  Hence, no choice but to drop. */
+		*hotdrop = 1;
+		return 0;
+	}
+	return ntohs(uh->dest);
+}
+
+static u32
+get_sport(const void *pkt, int *hotdrop)
+{
+	struct udphdr _udph, *uh;
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	uh = skb_header_pointer(skb, skb->nh.iph->ihl*4, 
+				sizeof(_udph), &_udph);
+	if (unlikely(uh == NULL)) {
+		*hotdrop = 1;
+		return 0;
+	}
+	return ntohs(uh->source);
+}
+
+static u32
+get_syn(const void *pkt, int *hotdrop)
+{
+	struct tcphdr _tcph, *th;
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	th = skb_header_pointer(skb, skb->nh.iph->ihl*4,
+				sizeof(_tcph), &_tcph);
+	if (unlikely(th == NULL)) {
+		*hotdrop = 1;
+		return 0;
+	}
+	return !(th->syn && !th->ack && !th->fin && !th->rst);
+}
+
+static u32
+get_icmptypes(const void *pkt, int *hotdrop)
+{
+	struct icmphdr _icmph, *ic;
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	ic = skb_header_pointer(skb, skb->nh.iph->ihl*4,
+				sizeof(_icmph), &_icmph);
+	if (unlikely(ic == NULL)) {
+		*hotdrop = 1;
+		return 0;
+	}
+	return (ic->type << 8) + ic->code;
+}
+
+static u32
+get_ttl(const void *pkt, int *hotdrop)
+{
+	const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+	return skb->nh.iph->ttl;
+}
+
+
+/*
+ * conntrack dependency management
+ */
+
+#ifdef CONNTRACK_MODULE          /* conntrack built as module */
+
+int
+nfhp_register_cthelp(struct module *cthelp)
+{
+	int ret;
+
+	ret = down_interruptible(&cthelp_lock);
+	if (ret != 0) {
+		return ret;
+	}
+	if (nfhp_cthelp_module != NULL) {
+		printk(KERN_ERR "nfhp_register_cthelp: module already "
+		       "registered\n");
+		ret = -EINVAL;
+	}
+	nfhp_cthelp_module = cthelp;
+	up(&cthelp_lock);
+	return ret;
+}
+
+void
+nfhp_unregister_cthelp(struct module *cthelp)
+{
+	 down(&cthelp_lock);
+	 if (nfhp_cthelp_module != cthelp) {
+		 printk(KERN_ERR "nfhp_unregister_cthelp: unregistered "
+			"module tries to unregister\n");
+		 up(&cthelp_lock);
+		 return;
+	 }
+	 nfhp_cthelp_module = NULL;
+	 up(&cthelp_lock);
+}
+
+static inline int
+cthelp_use(void)
+{
+	int ret;
+
+	/* check whether the conntrack dependency helper is registered */
+	ret = down_interruptible(&cthelp_lock);
+	if (ret < 0) {
+		return ERRNO_TO_NFHE(-ret);
+	}
+	if (nfhp_cthelp_module != NULL) {
+		try_module_get(nfhp_cthelp_module);
+		up(&cthelp_lock);
+		return 0;
+	}
+
+	/* try to load the module */
+	up(&cthelp_lock);
+	request_module("nf_hipac_cthelp");
+	
+	/* did we succeed? */
+	ret = down_interruptible(&cthelp_lock);
+	if (ret < 0) {
+		return ERRNO_TO_NFHE(-ret);
+	}
+	if (nfhp_cthelp_module != NULL) {
+		try_module_get(nfhp_cthelp_module);
+		up(&cthelp_lock);
+		return 0;
+	}
+	up(&cthelp_lock);
+	return NFHE_CTHELP;
+}
+
+static inline void
+cthelp_unuse(void)
+{
+	if (nfhp_cthelp_module == NULL) {
+		printk(KERN_ERR "%s: conntrack dependency helper "
+		       "module not registered\n", __FUNCTION__);
+		return;
+	}
+	module_put(nfhp_cthelp_module);
+}
+
+#else                  /* conntrack built in or not available */
+
+int
+nfhp_register_cthelp(struct module *cthelp)
+{
+}
+
+void
+nfhp_unregister_cthelp(struct module *cthelp)
+{
+}
+
+static inline int
+cthelp_use(void)
+{
+#ifdef CONFIG_IP_NF_CONNTRACK
+	return 0;
+#else
+	return NFHE_CT;
+#endif
+}
+
+static inline void
+cthelp_unuse(void)
+{
+}
+
+#endif
+
+
+/*
+ * functions and data structures necessary for hipac initialization
+ */
+
+/* dimension id to bit type mapping */
+static const u8 dim2btype[] =
+{
+	[DIMID_STATE]      = BIT_STATE,
+	[DIMID_SRC_IP]     = BIT_SRC_IP,
+	[DIMID_DEST_IP]    = BIT_DEST_IP,
+	[DIMID_INIFACE]    = BIT_INIFACE,
+	[DIMID_OUTIFACE]   = BIT_OUTIFACE,
+	[DIMID_PROTO]      = BIT_PROTO,
+	[DIMID_FRAGMENT]   = BIT_FRAGMENT,
+	[DIMID_DPORT]      = BIT_DPORT,
+	[DIMID_SPORT]      = BIT_SPORT,
+	[DIMID_SYN]        = BIT_SYN,
+	[DIMID_ICMP_TYPE]  = BIT_ICMP_TYPE,
+	[DIMID_TTL]        = BIT_TTL
+};
+
+/* dimension extractor functions */
+static const hipac_extract_t extract[] =
+{
+	[DIMID_STATE]      = get_state,
+	[DIMID_SRC_IP]     = get_src_ip,
+	[DIMID_DEST_IP]    = get_dst_ip,
+	[DIMID_INIFACE]    = get_iniface,
+	[DIMID_OUTIFACE]   = get_outiface,
+	[DIMID_PROTO]      = get_proto,
+	[DIMID_FRAGMENT]   = get_fragment,
+	[DIMID_DPORT]      = get_dport,
+	[DIMID_SPORT]      = get_sport,
+	[DIMID_SYN]        = get_syn,
+	[DIMID_ICMP_TYPE]  = get_icmptypes,
+	[DIMID_TTL]        = get_ttl,
+};
+
+/* iptables_match executor */
+static hipac_match_t
+hipac_match_exec(const void *packet, void *first_match, void *end)
+{
+	const struct packet *p = packet;
+	hipac_match_t match = MATCH_YES;
+	struct ipt_entry_match *m = first_match;
+	int hotdrop = 0;
+	struct iphdr *ip = (*p->skbuff)->nh.iph;
+	u16 offset = ntohs(ip->frag_off) & IP_OFFSET;
+
+	for (; m < (struct ipt_entry_match *) end; m = NEXT_IPT_MATCH(m)) {
+		if (!m->u.kernel.match->match(*p->skbuff, p->indev,
+					      p->outdev, m->data,
+					      offset, &hotdrop)) {
+			match = MATCH_NO;
+			break;
+		}
+	}
+	if (hotdrop) {
+		return MATCH_HOTDROP;
+	}
+	return match;
+}
+
+/* iptables_target executor */
+static hipac_target_t
+hipac_target_exec(const void *packet, void *target)
+{
+	const struct packet *p = packet;
+	struct ipt_entry_target *t = target;
+	hipac_target_t ht;
+
+	ht = t->u.kernel.target->target(p->skbuff, p->indev, p->outdev, 
+					p->hook, t->data, NULL);
+	if (ht == IPT_CONTINUE) {
+		return TARGET_NONE;
+	}
+	return ht;
+}
+
+/* equality test - rnl is the hipac_rule in netlink format which implies
+   that it contains ipt_entry and cmp_len if the rule has an ipt_entry_match
+   or ipt_entry_target or chain label; rhp is in hipac format which means
+   that it does not contain ipt_entry and cmp_len */
+static int
+hipac_eq_exec(const struct hipac_rule *rnl, const struct hipac_rule *rhp)
+{
+	u32 cmp_len_ind = 0;
+	u16 *cmp_len;
+
+	if (rnl == rhp) {
+		printk(KERN_ERR "%s: rnl == rhp error\n", __FUNCTION__);
+		return 0;
+	}
+	if (rnl == NULL || rhp == NULL || rnl->size != rhp->size ||
+	    rnl->native_mct != rhp->native_mct ||
+	    memcmp(rnl->cmp_start, rhp->cmp_start,
+		   sizeof(*rnl) - offsetof(struct hipac_rule, cmp_start) +
+		   rnl->native_mct * sizeof(*rnl->first_match))) {
+		return 0;
+	}
+	cmp_len = CMP_LEN(rnl);
+	if (HAS_IPT_MATCH(rnl)) {
+		struct ipt_entry_match *mnl, *mhp, *endhp;
+		mnl = FIRST_IPT_MATCH_IE(rnl);
+		mhp = FIRST_IPT_MATCH(rhp);
+		endhp = IPT_MATCH_END(rhp);
+		for (; mhp < endhp;
+		     mnl = NEXT_IPT_MATCH(mnl), mhp = NEXT_IPT_MATCH(mhp),
+		     cmp_len_ind++) {
+			if (strncmp(mnl->u.user.name,
+				    mhp->u.kernel.match->name,
+				    sizeof(mnl->u.user.name)) ||
+			    mnl->u.match_size != mhp->u.match_size ||
+			    memcmp(&mnl->data, &mhp->data,
+				   cmp_len[cmp_len_ind])) {
+				return 0;
+			}
+		}
+	}
+	if (HAS_IPT_TARGET(rnl)) {
+		struct ipt_entry_target *tnl, *thp;
+		tnl = IPT_TARGET_IE(rnl);
+		thp = IPT_TARGET(rhp);
+		if (strncmp(tnl->u.user.name, thp->u.kernel.target->name,
+			    sizeof(tnl->u.user.name)) ||
+		    tnl->u.target_size != thp->u.target_size ||
+		    memcmp(&tnl->data, &thp->data,
+			   cmp_len[cmp_len_ind])) {
+			return 0;
+		}
+	} else if (HAS_CHAIN_TARGET(rnl)) {
+		char *tnl, *thp;
+		tnl = CHAIN_TARGET_IE(rnl);
+		thp = CHAIN_TARGET(rhp);
+		/* strlen(tnl) < HIPAC_CHAIN_NAME_MAX_LEN */
+		if (strcmp(tnl, thp)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/* r is constructed by copying rnl to the exclusion of ipt_entry and
+   cmp_len (if present); rnl->size already states the size of r _but_
+   rnl may be smaller than rnl->size if it has a chain target */
+static void
+hipac_copy_constructor(const struct hipac_rule *rnl, struct hipac_rule *r)
+{
+	if (HAS_IPT_ENTRY(rnl)) {
+		u32 size = rnl->size;
+		if (HAS_CHAIN_TARGET(rnl)) {
+			size -= HIPAC_CHAIN_NAME_MAX_LEN -
+				strlen(CHAIN_TARGET_IE(rnl)) - 1;
+		}
+		memcpy(r, rnl, sizeof(*rnl) + rnl->native_mct *
+		       sizeof(*rnl->first_match));
+		if (HAS_IPT_MATCH(rnl)) {
+			memcpy(FIRST_IPT_MATCH(r), IPT_ENTRY_END(rnl),
+			       size - rnl->match_offset);
+		} else {
+			memcpy(IPT_TARGET(r), IPT_ENTRY_END(rnl),
+			       size - rnl->target_offset);
+		}
+	} else {
+		memcpy(r, rnl, rnl->size);
+	}
+}
+
+/* destructor for iptables matches/target */
+static void
+hipac_destroy_exec(struct hipac_rule *r)
+{
+	int i;
+
+	if (r == NULL) {
+		return;
+	}
+	for (i = 0; i < r->native_mct &&
+		     r->first_match[i].dimid < DIMID_STATE; i++);
+	if (i < r->native_mct && r->first_match[i].dimid == DIMID_STATE) {
+		cthelp_unuse();
+	}
+	if (HAS_IPT_MATCH(r)) {
+		struct ipt_entry_match *m, *end;
+		m = FIRST_IPT_MATCH(r);
+		end = IPT_MATCH_END(r);
+		for (; m < end; m = NEXT_IPT_MATCH(m)) {
+			if (m->u.kernel.match->destroy) {
+				m->u.kernel.match->destroy(
+					m->data, m->u.match_size - sizeof(*m));
+			}
+			module_put(m->u.kernel.match->me);
+		}
+	}
+	if (HAS_IPT_TARGET(r)) {
+		struct ipt_entry_target *t;
+		t = IPT_TARGET(r);
+		if (t->u.kernel.target->destroy) {
+			t->u.kernel.target->destroy(
+				t->data, t->u.target_size - sizeof(*t));
+		}
+		module_put(t->u.kernel.target->me);
+	}
+}
+
+/* destructor for iptables matches/target (rnl is the hipac_rule in
+   netlink format) */
+static void
+hipac_destroy_exec_nl(struct hipac_rule *rnl)
+{
+	int i;
+
+	if (rnl == NULL) {
+		return;
+	}
+	for (i = 0; i < rnl->native_mct &&
+		     rnl->first_match[i].dimid < DIMID_STATE; i++);
+	if (i < rnl->native_mct && rnl->first_match[i].dimid == DIMID_STATE) {
+		cthelp_unuse();
+	}
+	if (HAS_IPT_MATCH(rnl)) {
+		struct ipt_entry_match *m, *end;
+		m = FIRST_IPT_MATCH_IE(rnl);
+		end = IPT_MATCH_END_IE(rnl);
+		for (; m < end; m = NEXT_IPT_MATCH(m)) {
+			if (m->u.kernel.match->destroy) {
+				m->u.kernel.match->destroy(
+					m->data, m->u.match_size - sizeof(*m));
+			}
+			module_put(m->u.kernel.match->me);
+		}
+	}
+	if (HAS_IPT_TARGET(rnl)) {
+		struct ipt_entry_target *t;
+		t = IPT_TARGET_IE(rnl);
+		if (t->u.kernel.target->destroy) {
+			t->u.kernel.target->destroy(
+				t->data, t->u.target_size - sizeof(*t));
+		}
+		module_put(t->u.kernel.target->me);
+	}
+}
+
+static unsigned int
+input_match(unsigned int hooknum,
+	    struct sk_buff **skb,
+	    const struct net_device *in,
+	    const struct net_device *out,
+	    int (*okfn) (struct sk_buff *))
+{
+	const struct packet pkt = {hooknum, skb, in, out};
+	return hipac_match(hipac_input, &pkt);
+}
+
+static unsigned int
+forward_match(unsigned int hooknum,
+	      struct sk_buff **skb,
+	      const struct net_device *in,
+	      const struct net_device *out,
+	      int (*okfn) (struct sk_buff *))
+{
+	const struct packet pkt = {hooknum, skb, in, out};
+	return hipac_match(hipac_forward, &pkt);
+}
+
+static unsigned int
+output_match(unsigned int hooknum,
+	     struct sk_buff **skb,
+	     const struct net_device *in,
+	     const struct net_device *out,
+	     int (*okfn) (struct sk_buff *))
+{
+	const struct packet pkt = {hooknum, skb, in, out};
+
+	/* root is playing with raw sockets. */
+	if (unlikely((*skb)->len < sizeof(struct iphdr) ||
+		     ((*skb)->nh.iph->ihl << 2) < sizeof(struct iphdr))) {
+		return NF_ACCEPT;
+	}
+	return hipac_match(hipac_output, &pkt);
+}
+
+
+/*
+ * kernel-user netlink communication
+ */
+
+static inline void *
+nlhp_list_rule(struct nfhp_list_rule *r, struct hipac_rule *rule, int *len)
+{
+	int size = IPT_ALIGN(offsetof(struct nfhp_list_rule, r) + rule->size);
+	u32 i;
+
+	if (*len < size) {
+		return NULL;
+	}
+	r->indev[0] = '\0';
+	r->outdev[0] = '\0';
+	memcpy(&r->r, rule, rule->size);
+	
+	/* fill in interface names if necessary */
+	for (i = 0; i < r->r.native_mct; i++) {
+		switch (r->r.first_match[i].dimid) {
+		case DIMID_INIFACE:
+			if (nf_hipac_dev_lookup_ifname(
+				    r->r.first_match[i].left,
+				    r->indev) < 0) {
+				printk(KERN_ERR "%s: interface name look"
+				       "up failed\n", __FUNCTION__);
+			}
+			break;
+		case DIMID_OUTIFACE:
+			if (nf_hipac_dev_lookup_ifname(
+				    r->r.first_match[i].left,
+				    r->outdev) < 0) {
+				printk(KERN_ERR "%s: interface name look"
+				       "up failed\n", __FUNCTION__);
+			}
+			break;
+		}
+	}
+
+	/* prepare iptables matches/target for userspace */
+	if (HAS_IPT_MATCH(&r->r)) {
+		struct ipt_entry_match *m, *end;
+		m = FIRST_IPT_MATCH(&r->r);
+		end = IPT_MATCH_END(&r->r);
+		for (; m < end; m = NEXT_IPT_MATCH(m)) {
+			strncpy(m->u.user.name, m->u.kernel.match->name,
+				sizeof(m->u.user.name));
+		}
+	}
+	if (HAS_IPT_TARGET(&r->r)) {
+		struct ipt_entry_target *t;
+		t = IPT_TARGET(&r->r);
+		strncpy(t->u.user.name, t->u.kernel.target->name,
+			sizeof(t->u.user.name));
+	}
+
+	*len -= size;
+	return (char *) r + size;
+}
+
+static inline void *
+nlhp_list_chain(struct nfhp_list_chain *c, int pos, int *len)
+{
+	if (*len < sizeof(*c)) {
+		return NULL;
+	}
+	strncpy(c->label, linfo.inf[pos].label, sizeof(c->label));
+	c->label[sizeof(c->label) - 1] = '\0';
+	c->policy = linfo.inf[pos].policy;
+	c->rule_num = linfo.inf[pos].rule_num;
+	*len -= sizeof(*c);
+	return c + 1;
+}
+
+static inline int
+nlhp_list_next_rule(struct hipac_rule *prev, struct hipac_rule **rule, int pos)
+{
+	int stat;
+
+	stat = hipac_get_next_rule(&linfo.inf[pos], prev, rule);
+	switch (stat) {
+	case HE_OK:
+		return 0;
+	case HE_RULE_NOT_EXISTENT:
+		*rule = NULL;
+		return 0;
+	default:
+		if (unlikely(stat > 0)) {
+			/* this should never happen */
+			printk(KERN_ERR "%s: hipac_get_next_rule returned "
+			       "status > 0\n", __FUNCTION__);
+			stat = -stat;
+		}
+		return stat;
+	}
+}
+
+/* callback function for CMD_LIST command */
+static int
+nlhp_list(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	static u32 pos;
+	static struct hipac_rule *rule;
+	struct nlmsghdr *nlh;
+	int len, total, stat;
+	void *data;
+
+	total = skb_tailroom(skb) - NLMSG_SPACE(0);
+	switch (cb->args[0]) {
+	    case 0:
+		    /* first callback in the series */
+		    pos = 0;
+		    rule = NULL;
+		    data = NLMSG_DATA(skb->data);
+		    len = total;
+		    cb->args[0] = 1;
+		    break;
+	    case 1:
+		    /* pos, rule represent the current state */
+		    data = NLMSG_DATA(skb->data);
+		    len = total;
+		    break;
+	    default:
+		    return 0;
+	}
+
+	while (1) {
+		if (rule == NULL) {
+			/* send chain info */
+			data = nlhp_list_chain(data, pos, &len);
+			if (data == NULL) {
+				/* skb full - chain sent next time */
+				break;
+			}
+			stat = nlhp_list_next_rule(NULL, &rule, pos);
+			if (stat < 0) {
+				/* rule listing aborted due to error */
+				return stat;
+			}
+		} else {
+			/* send next rule */
+			data = nlhp_list_rule(data, rule, &len);
+			if (data == NULL) {
+				/* skb full - rule sent next time */
+				break;
+			}
+			stat = nlhp_list_next_rule(rule, &rule, pos);
+			if (stat < 0) {
+				/* rule listing aborted due to error */
+				return stat;
+			}
+		}
+		if (rule == NULL) {
+			if (++pos == linfo.len) {
+				/* we are done */
+				cb->args[0] = 2;
+				break;
+			}
+		}
+	}
+	nlh = NLMSG_PUT(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+			NLHP_TYPE, total - len);
+	nlh->nlmsg_flags |= NLM_F_MULTI;
+	return NLMSG_SPACE(total - len);
+
+nlmsg_failure:
+        skb_trim(skb, skb->tail - skb->data);
+        return NFHE_ILL;
+}
+
+static int
+nlhp_done(struct netlink_callback *cb)
+{
+	up(&nlhp_lock);
+	return 0;
+}
+
+static void
+nlhp_send_reply(struct sk_buff *skb, struct nlmsghdr *nlh, int err)
+{
+	struct sk_buff *r_skb;
+	struct nlmsghdr *r_nlh;
+
+	r_skb = alloc_skb(NLMSG_SPACE(sizeof(int32_t)), GFP_KERNEL);
+	if (r_skb == NULL) {
+		return;
+	}
+
+	r_nlh = NLMSG_PUT(r_skb, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 
+			  NLMSG_ERROR, sizeof(int32_t));
+	*(int32_t *) NLMSG_DATA(r_nlh) = err;
+	if (!NLMSG_OK(r_nlh, NLMSG_LENGTH(sizeof(int32_t)))) {
+		printk(KERN_ERR "netlink message not ok\n");
+		return;
+	}
+	if (netlink_unicast(nfhp_sock, r_skb, NETLINK_CB(skb).pid,
+			    MSG_DONTWAIT) <= 0) {
+		printk(KERN_ERR "netlink_unicast failed\n");
+		return;
+	}
+	return;
+nlmsg_failure:
+	printk(KERN_ERR "NLMSG_PUT failed\n");
+	kfree(r_skb);
+}
+
+static int
+do_cmd(struct sk_buff *skb, int msg_len);
+
+static inline int
+nlhp_chk_user_skb(struct sk_buff *skb)
+{
+	struct nlmsghdr *nlh = (struct nlmsghdr *) skb->data;
+
+	if (skb->len < sizeof(struct nlmsghdr) ||
+	    nlh->nlmsg_len < sizeof(struct nlmsghdr) ||
+	    skb->len < nlh->nlmsg_len ||
+	    nlh->nlmsg_pid <= 0 ||
+	    nlh->nlmsg_type != NLHP_TYPE ||
+	    nlh->nlmsg_flags & NLM_F_MULTI ||
+	    !(nlh->nlmsg_flags & NLM_F_REQUEST) ||
+	    !(nlh->nlmsg_flags & NLM_F_ACK)) {
+		nlhp_send_reply(skb, nlh, NFHE_NOMSG);
+		return 1;
+	}
+	if (nlh->nlmsg_flags & MSG_TRUNC) {
+		nlhp_send_reply(skb, nlh, ERRNO_TO_NFHE(ECOMM));
+		return 1;
+	}
+	if (security_netlink_recv(skb)) {
+		nlhp_send_reply(skb, nlh, ERRNO_TO_NFHE(EPERM));
+		return 1;
+	}
+	return do_cmd(skb, skb->len - NLMSG_LENGTH(0));
+}
+
+static void
+nlhp_data_ready(struct sock *sk, int len)
+{
+	wake_up_interruptible(&thread_wait);
+}
+
+static int
+nlhp_thread_func(void *data)
+{
+	struct sk_buff *skb;
+
+	daemonize("nf_hipac");
+	allow_signal(SIGTERM);
+	while (1) {
+ 		if (wait_event_interruptible(
+			    thread_wait,
+			    (skb = skb_dequeue(&nfhp_sock->sk_receive_queue))
+			    != NULL)
+		    || down_interruptible(&nlhp_lock)) {
+			complete_and_exit(&thread_comp, 0);
+		}
+		if (nlhp_chk_user_skb(skb)) {
+			/* in the other case nlhp_done releases
+			   the lock */
+			up(&nlhp_lock);
+		}
+		kfree_skb(skb);
+	}
+	return 0;
+}
+
+
+/*
+ * request handling
+ */
+
+static int
+cmd_check_init_native_matches(struct nfhp_rule *rule, int inc_modct,
+			      int *inc_cthelp)
+{
+	u32 dimbitv = 0;
+	u8 frag_match = 0;
+	u16 proto_match = 0;
+	struct ipt_entry *e = NULL;
+	struct hipac_rule *r = &rule->r;
+	int i, ifindex, stat;
+	u8 dimid, inv, devbf;
+	u32 left, right;
+	
+	devbf = (rule->indev[0] != '\0');
+	devbf |= (rule->outdev[0] != '\0') << 1;
+	if (HAS_IPT_ENTRY(r)) {
+		/* as nf-hipac does not care about ipt_entry we just fill in
+		   the info needed by the existing modules */
+		e = IPT_ENTRY(r);
+		memset(&e->ip, 0, sizeof(e->ip));
+		if (devbf & 1) {
+			strncpy(e->ip.iniface, rule->indev,
+				sizeof(e->ip.iniface));
+			memset(e->ip.iniface_mask, 0xff,
+			       strlen(rule->indev) + 1);
+		}
+		if (devbf & 2) {
+			strncpy(e->ip.outiface, rule->outdev,
+				sizeof(e->ip.outiface));
+			memset(e->ip.outiface_mask, 0xff,
+			       strlen(rule->outdev) + 1);
+		}
+		e->nfcache = 0;
+		e->comefrom = 0;
+		memset(&e->counters, 0, sizeof(e->counters));
+	}
+	for (i = 0; i < r->native_mct; i++) {
+		r->first_match[i].invert = !!r->first_match[i].invert;
+		dimid = r->first_match[i].dimid;
+		inv = r->first_match[i].invert;
+		left = r->first_match[i].left;
+		right = r->first_match[i].right;
+		if (i > 0 && dimid <= r->first_match[i - 1].dimid) {
+			return NFHE_SORT;
+		}
+		if (left > right || right > hipac_maxkey(dim2btype[dimid]) ||
+		    (left == 0 && right == hipac_maxkey(dim2btype[dimid]))) {
+			return NFHE_MINT;
+		}
+		dimbitv |= 1 << dimid;
+		switch (dimid) {
+		case DIMID_INIFACE:
+			if (!(devbf & 1)) {
+				return NFHE_DEVA;
+			}
+			ifindex = nf_hipac_dev_get_vindex(rule->indev);
+			if (ifindex < 0) {
+				return ifindex;
+			}
+			r->first_match[i].left = ifindex;
+			r->first_match[i].right = ifindex;
+			if (e != NULL && inv) {
+				e->ip.invflags |= IPT_INV_VIA_IN;
+			}
+			devbf &= 0xfe;
+			r->origin &= NFHP_ORIGIN_INPUT |
+				NFHP_ORIGIN_FORWARD;
+			break;
+		case DIMID_OUTIFACE:
+			if (!(devbf & 2)) {
+				return NFHE_DEVA;
+			}
+			ifindex = nf_hipac_dev_get_vindex(rule->outdev);
+			if (ifindex < 0) {
+				return ifindex;
+			}
+			r->first_match[i].left = ifindex;
+			r->first_match[i].right = ifindex;
+			if (e != NULL && inv) {
+				e->ip.invflags |= IPT_INV_VIA_OUT;
+			}
+			devbf &= 0xfd;
+			r->origin &= NFHP_ORIGIN_OUTPUT |
+				NFHP_ORIGIN_FORWARD;
+			break;
+		case DIMID_PROTO:
+			if (!inv && left == right) {
+				proto_match = left;
+			}
+			if (e != NULL) {
+				e->ip.proto = r->first_match[i].left;
+				/* iptables does not support protocol
+				   ranges; treating a range match as
+				   inverted point match avoids illegal use
+				   of iptables matches */
+				if (inv || left != right) {
+					e->ip.invflags |= IPT_INV_PROTO;
+				}
+			}
+			break;
+		case DIMID_FRAGMENT:
+			if (inv || (left != right && left == 0)) {
+				return NFHE_FRAG;
+			}
+			if (e != NULL) {
+				e->ip.flags = IPT_F_FRAG;
+			}
+			if (left > 0) {
+				r->first_match[i].left = 1;
+				r->first_match[i].right =
+					hipac_maxkey(dim2btype[dimid]);
+			} else {
+				frag_match = 1;
+				if (e != NULL) {
+					e->ip.invflags |= IPT_INV_FRAG;
+				}
+			}
+			break;
+		case DIMID_SYN:
+			if (inv || (left != right && left == 0)) {
+				return NFHE_SYN;
+			}
+			if (left > 0) {
+				r->first_match[i].left = 1;
+				r->first_match[i].right =
+					hipac_maxkey(dim2btype[dimid]);
+			}
+			break;
+		case DIMID_STATE:
+			if (left > NFHP_STATE_UNTRACKED) {
+				return NFHE_STATE;
+			}
+			if (inc_modct) {
+				stat = cthelp_use();
+				if (stat < 0) {
+					return stat;
+				}
+				(*inc_cthelp)++;
+			}
+			break;
+		}
+	}
+	if (devbf != 0) {
+		return NFHE_DEVB;
+	}
+
+	/* check inter-match dependencies */
+	if (dimbitv & (1 << DIMID_SYN)) {
+		if (proto_match != IPPROTO_TCP || !frag_match ||
+		    dimbitv & (1 << DIMID_ICMP_TYPE)) {
+			return NFHE_TCP;
+		}
+	} else if (dimbitv & (1 << DIMID_DPORT) ||
+		   dimbitv & (1 << DIMID_SPORT)) {
+		if ((proto_match != IPPROTO_UDP && proto_match != IPPROTO_TCP) || !frag_match ||
+		    dimbitv & (1 << DIMID_ICMP_TYPE)) {
+			return NFHE_TCPUDP;
+		}
+	} else if (dimbitv & (1 << DIMID_ICMP_TYPE) &&
+		   (proto_match != IPPROTO_ICMP || !frag_match)) {
+		return NFHE_ICMP;
+	}
+	return 0;
+}
+
+static int
+init_ipt_match(struct ipt_entry_match *m, char *table,
+	       const struct ipt_ip *ip, unsigned int hook)
+{
+	struct ipt_match *match;
+	int ret = 0;
+	
+	match = ipt_find_match(m->u.user.name, m->u.user.revision);
+	if (!match)
+                return ERRNO_TO_NFHE(ENOENT);
+	m->u.kernel.match = match;
+	if (m->u.kernel.match->checkentry
+	    && !m->u.kernel.match->checkentry(table, ip, m->data,
+					      m->u.match_size - sizeof(*m),
+					      hook)) {
+		module_put(m->u.kernel.match->me);
+		ret = NFHE_IPTMCH;
+	}
+	return ret;
+}
+
+static int
+init_ipt_target(struct ipt_entry_target *t, char *table,
+		const struct ipt_entry *e, unsigned int hook)
+{
+	struct ipt_target *target;
+	int ret = 0;
+	
+	target = ipt_find_target(t->u.user.name, t->u.user.revision);
+	if (!target)
+                return ERRNO_TO_NFHE(ENOENT);
+	t->u.kernel.target = target;
+	if (t->u.kernel.target->checkentry
+	    && !t->u.kernel.target->checkentry(table, e, t->data,
+					       t->u.target_size - sizeof(*t),
+					       hook)) {
+		module_put(t->u.kernel.target->me);
+		ret = NFHE_IPTTCH;
+	}
+	return ret;
+}
+
+static inline u32
+origin_to_hookmask(u32 origin)
+{
+	return (origin & NFHP_ORIGIN_INPUT ? 1 << NF_IP_LOCAL_IN : 0) |
+	       (origin & NFHP_ORIGIN_FORWARD ? 1 << NF_IP_FORWARD : 0) |
+	       (origin & NFHP_ORIGIN_OUTPUT ? 1 << NF_IP_LOCAL_OUT : 0);
+}
+
+static int
+cmd_check_init_ipt_matches(struct hipac_rule *r, int len, u16 **cmp,
+			   int *cmp_len, int inc_modct, int *num_done)
+{
+	struct ipt_entry_match *match;
+	int stat;
+	
+	match = FIRST_IPT_MATCH_IE(r);
+	while (len >= sizeof(*match) && len >= match->u.match_size) {
+		if (match->u.match_size != IPT_ALIGN(match->u.match_size)) {
+			return NFHE_IPTMSZ;
+		}
+		if (*cmp != NULL) {
+			if (*cmp_len < sizeof(**cmp)) {
+				return NFHE_CMPSH;
+			}
+			if (**cmp > match->u.match_size - sizeof(*match)) {
+				return NFHE_CMPLA;
+			}
+		}
+
+		/* this is really ugly but we have to hardcode the maximum
+		   allowed origin bit vector since the actual origin of this
+		   rule might change after another rule has been inserted;
+		   in order to dynamically handle this setting a module would
+		   be required to be asked for its maximum allowed origin bit
+		   vector */
+		if (!strcmp(match->u.user.name, "owner")) {
+			r->origin &= NFHP_ORIGIN_OUTPUT;
+		} else if (!strcmp(match->u.user.name, "owner-socketlookup")) {
+			r->origin &= NFHP_ORIGIN_INPUT | NFHP_ORIGIN_OUTPUT;
+		}
+		if (r->origin == 0) {
+			return NFHE_ORIGIN;
+		}
+		
+		if (inc_modct) {
+			if (r->action == TARGET_CHAIN) {
+				/* TEMPORARY FIX: since hipac currently treats
+				     jump rules in a way that leads to problems
+				     with stateful ipt_matches we restrict
+				     ourselves to certain known stateless
+				     ipt_matches; if you absolutely need a jump
+				     rule with another stateless ipt_match you
+				     are free to add its name here after but
+				     make sure that it is __really__
+				     stateless */
+				if (strcmp(match->u.user.name, "ah") &&
+				    strcmp(match->u.user.name, "dscp") &&
+				    strcmp(match->u.user.name, "ecn") &&
+				    strcmp(match->u.user.name, "esp") &&
+				    strcmp(match->u.user.name, "length") &&
+				    strcmp(match->u.user.name, "owner") &&
+				    strcmp(match->u.user.name, "pkttype") &&
+				    strcmp(match->u.user.name, "tcpmss") &&
+				    strcmp(match->u.user.name, "tos") &&
+				    strcmp(match->u.user.name, "unclean")) {
+					return NFHE_IMPL;
+				}
+			}
+			stat = init_ipt_match(match, "filter",
+					      &IPT_ENTRY(r)->ip,
+					      origin_to_hookmask(r->origin));
+			if (stat < 0) {
+				return stat;
+			}
+		}
+		(*num_done)++;
+		len -= match->u.match_size;
+		match = NEXT_IPT_MATCH(match);
+		if (*cmp != NULL) {
+			(*cmp_len) -= sizeof(**cmp);
+			(*cmp)++;
+		}
+	}
+	if (len > 0) {
+		return NFHE_TOFF;
+	}
+	return 0;
+}
+
+static int
+cmd_check_init_ipt_target(struct hipac_rule *r, int len, u16 *cmp,
+			  int cmp_len, int inc_modct, int *done)
+{
+	struct ipt_entry_target *target;
+	int stat;
+	
+	target = IPT_TARGET_IE(r);
+	if (len < sizeof(*target) || len < target->u.target_size ||
+	    target->u.target_size != IPT_ALIGN(target->u.target_size)) {
+		return NFHE_IPTTSZ;
+	}
+	if (cmp != NULL) {
+		if (cmp_len < sizeof(*cmp)) {
+			return NFHE_CMPSH;
+		}
+		if (*cmp > target->u.target_size - sizeof(*target)) {
+			return NFHE_CMPLA;
+		}
+	}
+
+	/* this is really ugly but we have to hardcode the maximum allowed
+	   origin bit vector since the actual origin of this rule might
+	   change after another rule has been inserted;
+	   in order to dynamically handle this setting a module would be 
+	   required to be asked for its maximum allowed origin bit vector */
+	if (!strcmp(target->u.user.name, "MIRROR")) {
+		r->origin &= NFHP_ORIGIN_FORWARD | NFHP_ORIGIN_INPUT;
+	} else if (!strcmp(target->u.user.name, "TCPMSS")) {
+		r->origin &= NFHP_ORIGIN_FORWARD | NFHP_ORIGIN_OUTPUT;
+	} else if (!strcmp(target->u.user.name, "TARPIT")) {
+		r->origin &= NFHP_ORIGIN_FORWARD | NFHP_ORIGIN_INPUT;
+	}
+	if (r->origin == 0) {
+		return NFHE_ORIGIN;
+	}
+
+	if (inc_modct) {
+		stat = init_ipt_target(target, "filter", IPT_ENTRY(r),
+				       origin_to_hookmask(r->origin));
+		if (stat < 0) {
+			return stat;
+		}
+	}
+	(*done)++;
+	len -= target->u.target_size;
+	if (len > 0) {
+		/* netlink message contains unnecessary padding between the
+		   end of the ipt target and the beginning of the cmp_len
+		   array */
+		r->size -= len;
+	}
+	return 0;
+}
+
+static void
+cmd_cleanup(struct hipac_rule *r, int inc_cthelp, int num_matches, int target)
+{
+	struct ipt_entry_match *m;
+	struct ipt_entry_target *t;
+	int i;
+
+	if (inc_cthelp) {
+		cthelp_unuse();
+	}
+	for (m = FIRST_IPT_MATCH_IE(r), i = 0; i < num_matches;
+	     m = NEXT_IPT_MATCH(m), i++) {
+		if (m->u.kernel.match->destroy) {
+			m->u.kernel.match->destroy(m->data, m->u.match_size -
+						   sizeof(*m));
+		}
+		module_put(m->u.kernel.match->me);
+	}
+	if (target) {
+		t = IPT_TARGET_IE(r);
+		if (t->u.kernel.target->destroy) {
+			t->u.kernel.target->destroy(
+				t->data, t->u.target_size - sizeof(*t));
+		}
+		module_put(t->u.kernel.target->me);
+	}
+}
+
+static int
+cmd_check_init(struct nfhp_cmd *cmd, int msg_len)
+{
+	u16 *cmp = NULL;
+	int inc_cthelp = 0, num_matches = 0, target = 0, cmp_len = 0;
+	int stat, len, inc_modct;
+	struct hipac_rule *r;
+	u32 c;
+	
+	if (msg_len < sizeof(*cmd)) {
+		return NFHE_NOMSG;
+	}
+
+	/* basic checks */
+	c = cmd->cmd;
+	inc_modct = (c == CMD_APPEND || c == CMD_INSERT || c == CMD_REPLACE);
+	if (c < CMD_MIN || c > CMD_MAX) {
+		return NFHE_CMD;
+	}
+	cmd->chain.label[HIPAC_CHAIN_NAME_MAX_LEN - 1] = '\0';
+	cmd->chain.newlabel[HIPAC_CHAIN_NAME_MAX_LEN - 1] = '\0';
+	if (cmd->chain.label[0] == '\0' &&
+	    !(c == CMD_FLUSH ||  c == CMD_DELETE_CHAIN || c == CMD_LIST)) {
+		return NFHE_LABEL;
+	}
+	if (c == CMD_RENAME_CHAIN && cmd->chain.newlabel[0] == '\0') {
+		return NFHE_NLABEL;
+	}
+	if (c == CMD_SET_POLICY && cmd->chain.policy != TARGET_ACCEPT &&
+	    cmd->chain.policy != TARGET_DROP) {
+		return NFHE_POLICY;
+	}
+	if (!(c == CMD_APPEND || c == CMD_INSERT || c == CMD_DELETE_RULE ||
+	      c == CMD_REPLACE)) {
+		/* we are finished since cmd->rule is irrelevant;
+		   if c == CMD_DELETE_POS then cmd->rule.r.pos is verified
+		   by hipac */
+		return 0;
+	}
+
+	/* rule checks */
+	r = &cmd->rule.r;
+	cmd->rule.indev[IFNAMSIZ - 1] = '\0';
+	cmd->rule.outdev[IFNAMSIZ - 1] = '\0';
+	r->origin = NFHP_ORIGIN_INPUT | NFHP_ORIGIN_FORWARD |
+		NFHP_ORIGIN_OUTPUT;
+	/* TEMPORARY FIX: TARGET_RETURN is not yet implemented */
+	if (r->action == TARGET_RETURN) {
+		return NFHE_IMPL;
+	}
+	if (!(r->action == TARGET_ACCEPT || r->action == TARGET_DROP ||
+	      r->action == TARGET_NONE || r->action == TARGET_RETURN ||
+	      r->action == TARGET_EXEC || r->action == TARGET_CHAIN)) {
+		return NFHE_ACTION;
+	}
+	if (r->native_mct > NUMBER_OF_DIM) {
+		return NFHE_NMCT;
+	}
+	if (HAS_IPT_ENTRY(r)) {
+		struct ipt_entry *e = IPT_ENTRY(r);
+		if (e->target_offset > e->next_offset ||
+		    e->target_offset < sizeof(*e) ||
+		    offsetof(struct nfhp_cmd, rule.e) + 
+		    e->next_offset > msg_len) {
+			return NFHE_IEOFF;
+		}
+		/* assume target_offset/next_offset are correct; if they prove
+		   to be wrong we reject the packet anyway;
+		   the values are chosen to be correct for the target of 
+		   hipac_copy_constructor */
+		if (HAS_IPT_MATCH(r)) {
+			r->match_offset = IPT_ALIGN(sizeof(*r) +
+						    r->native_mct *
+						    sizeof(*r->first_match));
+			r->target_offset = (e->target_offset - sizeof(*e)) +
+				r->match_offset;
+		} else {
+			if (e->target_offset != sizeof(*e)) {
+				return NFHE_IEOFF;
+			}
+			r->match_offset = 0;
+			if (HAS_CHAIN_TARGET(r)) {
+				r->target_offset = sizeof(*r) + r->native_mct *
+					sizeof(*r->first_match);
+			} else {
+				r->target_offset =
+					IPT_ALIGN(sizeof(*r) +
+						  r->native_mct *
+						  sizeof(*r->first_match));
+			}
+		}
+		r->size = (e->next_offset - sizeof(*e)) + r->target_offset;
+	} else {
+		/* no iptables matches/target, no chain target */
+		r->size = sizeof(*r) + r->native_mct * sizeof(*r->first_match);
+		r->match_offset = r->target_offset = 0;
+	}
+
+       	/* check the native matches */
+	stat = cmd_check_init_native_matches(&cmd->rule, inc_modct,
+					     &inc_cthelp);
+	if (stat < 0) {
+		goto error;
+	}
+
+	/* set maximum size of cmp_len based on r->size */
+	if (HAS_CMP_LEN(c, r)) {
+		cmp = CMP_LEN(r);
+		cmp_len = msg_len - ((char *) cmp - (char *) cmd);
+		if (cmp_len <= 0) {
+			stat = NFHE_CMPMIS;
+			goto error;
+		}
+	}
+
+	/* check and initialize ipt matches */
+	if (HAS_IPT_MATCH(r)) {
+		len = r->target_offset - r->match_offset;
+		stat = cmd_check_init_ipt_matches(r, len, &cmp, &cmp_len,
+						  inc_modct, &num_matches);
+		if (stat < 0) {
+			goto error;
+		}
+	}
+
+	/* check and initialize ipt target / chain target */
+	if (HAS_IPT_TARGET(r)) {
+		len = r->size - r->target_offset;
+		if (len <= 0) {
+			stat = NFHE_IPTTMI;
+			goto error;
+		}
+		stat = cmd_check_init_ipt_target(r, len, cmp, cmp_len,
+						 inc_modct, &target);
+		if (stat < 0) {
+			goto error;
+		}
+	} else if (HAS_CHAIN_TARGET(r)) {
+		char *chain = CHAIN_TARGET_IE(r);
+		u32 real_len;
+		len = r->size - r->target_offset;
+		if (len <= 0 || chain[0] == '\0') {
+			stat = NFHE_CHAINE;
+			goto error;
+		}
+		real_len = strnlen(chain, len);
+		if (len > HIPAC_CHAIN_NAME_MAX_LEN || real_len == len) {
+			stat = NFHE_CHAINL;
+			goto error;
+		}
+		/* we have to reserve HIPAC_CHAIN_NAME_MAX_LEN bytes for
+		   the chain label */
+		r->size += HIPAC_CHAIN_NAME_MAX_LEN - len;
+	}
+
+	/* rule _syntactically_ correct; it might still be invalid because
+	   of a violation of the hipac semantics */
+	return 0;
+
+ error:
+	cmd_cleanup(r, inc_cthelp, num_matches, target);
+	return stat;
+}
+
+static int
+do_cmd(struct sk_buff *skb, int msg_len)
+{
+	struct nlmsghdr *nlh = (struct nlmsghdr *) skb->data;
+	struct nfhp_cmd *cmd = (struct nfhp_cmd *) NLMSG_DATA(nlh);
+	char *chain_label;
+	int stat;
+
+	stat = cmd_check_init(cmd, msg_len);
+	if (stat < 0) {
+		nlhp_send_reply(skb, nlh, stat);
+		return 1;
+	}
+	if (cmd->chain.label[0] == '\0') {
+		chain_label = NULL;
+	} else {
+		chain_label = cmd->chain.label;
+	}
+
+	switch (cmd->cmd) {
+	    case CMD_APPEND:
+		    stat = hipac_append(chain_label, &cmd->rule.r);
+		    if (stat != HE_OK) {
+			    hipac_destroy_exec_nl(&cmd->rule.r);
+		    }
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_INSERT:
+		    stat = hipac_insert(chain_label, &cmd->rule.r);
+		    if (stat != HE_OK) {
+			    hipac_destroy_exec_nl(&cmd->rule.r);
+		    }
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_DELETE_RULE:
+		    stat = hipac_delete(chain_label, &cmd->rule.r);
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_DELETE_POS:
+		    stat = hipac_delete_pos(chain_label, cmd->rule.r.pos);
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_REPLACE:
+		    stat = hipac_replace(chain_label, &cmd->rule.r);
+		    if (stat != HE_OK) {
+			    hipac_destroy_exec_nl(&cmd->rule.r);
+		    }
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_FLUSH:
+		    stat = hipac_flush_chain(chain_label);
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_NEW_CHAIN:
+		    stat = hipac_new_chain(chain_label);
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_DELETE_CHAIN:
+		    stat = hipac_delete_chain(chain_label);
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_RENAME_CHAIN:
+		    stat = hipac_rename_chain(chain_label,
+					      cmd->chain.newlabel);
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_SET_POLICY:
+		    stat = hipac_set_policy(chain_label, cmd->chain.policy);
+		    nlhp_send_reply(skb, nlh, stat);
+		    break;
+	    case CMD_LIST:
+	    {
+		    if (linfo.inf != NULL) {
+			    if (hipac_free_chain_infos(linfo.inf) != HE_OK) {
+				    /* this should never happen */
+				    printk(KERN_ERR "%s: hipac_free_chain_info"
+					   " failed\n", __FUNCTION__);
+			    }
+			    linfo.inf = NULL;
+			    linfo.len = 0;
+		    }
+		    stat = hipac_get_chain_infos(chain_label, &linfo.inf,
+						 &linfo.len);
+		    if (stat < 0) {
+			    linfo.inf = NULL;
+			    linfo.len = 0;
+			    nlhp_send_reply(skb, nlh, stat);
+			    return 1;
+		    }
+		    if (netlink_dump_start(nfhp_sock, skb, nlh, nlhp_list,
+					   nlhp_done) != 0) {
+			    printk(KERN_ERR "netlink_dump_start failed\n");
+			    return 1;
+		    }
+		    /* nlhp_done will or already has released nlhp_lock so
+		       don't release it again */
+		    return 0;
+	    }
+	    default:
+		    printk(KERN_ERR "invalid command type although "
+			   "cmd_check_init reported a valid command\n");
+		    nlhp_send_reply(skb, nlh, NFHE_NOMSG);
+		    break;
+	}
+	return 1;
+}
+
+
+/*
+ * initialization, finalization
+ */
+
+static int
+__init init(void)
+{
+	struct sysinfo sys;
+	u64 total_mem;
+	int ret;
+
+	si_meminfo(&sys);
+	total_mem = (u64) sys.totalram << PAGE_SHIFT;
+
+	/* initialize hipac layer */
+	if (hipac_init(dim2btype, extract,
+		       sizeof(dim2btype) / sizeof(*dim2btype),
+		       hipac_copy_constructor, hipac_destroy_exec,
+		       hipac_match_exec, hipac_target_exec, hipac_eq_exec,
+		       total_mem >> 1) != HE_OK) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "initialize hipac algorithm\n");
+		return -ENOMEM;
+	}
+	if (hipac_new("INPUT", "__/INPUT_INTERN\\__", TARGET_ACCEPT,
+		      NFHP_ORIGIN_INPUT, &hipac_input) != HE_OK) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "create hipac data structure for input hook\n");
+		ret = -ENOMEM;
+		goto cleanup_hipac;
+	}
+	if (hipac_new("FORWARD", "__/FORWARD_INTERN\\__", TARGET_ACCEPT,
+		      NFHP_ORIGIN_FORWARD, &hipac_forward) != HE_OK) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "create hipac data structure for forward hook\n");
+		ret = -ENOMEM;
+		goto cleanup_hipac;
+	}
+	if (hipac_new("OUTPUT", "__/OUTPUT_INTERN\\__", TARGET_ACCEPT,
+		      NFHP_ORIGIN_OUTPUT, &hipac_output) != HE_OK) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "create hipac data structure for output hook\n");
+		ret = -ENOMEM;
+		goto cleanup_hipac;
+	}
+
+	/* register to netfilter */
+	if ((ret = nf_register_hook(&input_op)) < 0) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "register input hook\n");
+		goto cleanup_hipac;
+	}
+	if ((ret = nf_register_hook(&forward_op)) < 0) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "register forward hook\n");
+		goto cleanup_input;
+	}
+	if ((ret = nf_register_hook(&output_op)) < 0) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "register output hook\n");
+		goto cleanup_forward;
+	}
+
+	/* initialize interface manager */
+	if ((ret = nf_hipac_dev_init()) != 0) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "initialize device management\n");
+		goto cleanup_output;
+	}
+
+	/* initialize proc interface */
+	hpproc_init(total_mem);
+
+	/* enable netlink user communication */
+	nfhp_sock = netlink_kernel_create(NLHP_PROTO, 0, nlhp_data_ready, THIS_MODULE);
+	if (nfhp_sock == NULL) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "create kernel netlink socket\n");
+		ret = -ENOMEM;
+		goto cleanup_hpproc;
+	}
+
+	/* start kernel thread */
+	threadID = kernel_thread(nlhp_thread_func, NULL, CLONE_KERNEL);
+	if (threadID == 0) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "start kernel thread\n");
+		ret = -EIO;
+		goto cleanup_netlink;
+	}
+
+	printk(KERN_INFO "nf_hipac: (C) 2002-2003 HIPAC core team "
+	       "(Michael Bellion, Thomas Heinz)\n");
+	printk(KERN_INFO "nf_hipac: (C) 2004-2005 MARA Systems AB "
+	       "(Michael Bellion)\n");
+	return 0;
+
+cleanup_netlink:
+	if (nfhp_sock == NULL ||
+	    nfhp_sock->sk_socket == NULL) {
+		/* this should never happen */
+		printk(KERN_ERR "nfhp_sock is broken\n");
+	} else {
+		sock_release(nfhp_sock->sk_socket);
+	}
+cleanup_hpproc:
+	hpproc_exit();
+	nf_hipac_dev_exit();
+cleanup_output:
+	nf_unregister_hook(&output_op);
+cleanup_forward:
+	nf_unregister_hook(&forward_op);
+cleanup_input:
+	nf_unregister_hook(&input_op);
+cleanup_hipac:
+	hipac_exit();
+	return ret;	
+}
+
+static void
+__exit fini(void)
+{
+	/* wait for ongoing netlink or proc operations to finish */
+	down(&nlhp_lock);
+	kill_proc(threadID, SIGTERM, 1);
+	wait_for_completion(&thread_comp);
+	if (nfhp_sock == NULL ||
+	    nfhp_sock->sk_socket == NULL) {
+		/* this should never happen */
+		printk(KERN_ERR "nfhp_sock is broken\n");
+	} else {
+		sock_release(nfhp_sock->sk_socket);
+	}
+	if (linfo.inf != NULL &&
+	    hipac_free_chain_infos(linfo.inf) != HE_OK) {
+			/* this should never happen */
+			printk(KERN_ERR "%s: hipac_free_chain_info"
+			       " failed\n", __FUNCTION__);
+	}
+	hpproc_exit();
+	nf_hipac_dev_exit();
+	nf_unregister_hook(&input_op);
+	nf_unregister_hook(&forward_op);
+	nf_unregister_hook(&output_op);
+	hipac_exit();
+	up(&nlhp_lock);
+}
+
+
+module_init(init);
+module_exit(fini);
+MODULE_AUTHOR("Michael Bellion and Thomas Heinz");
+MODULE_DESCRIPTION("NF-HIPAC - netfilter high performance "
+		   "packet classification");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(nfhp_register_cthelp);
+EXPORT_SYMBOL(nfhp_unregister_cthelp);
+
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_mod.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_mod.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_mod.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_mod.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,47 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _NFHP_MOD_H
+#define _NFHP_MOD_H
+
+#include <linux/module.h>
+#include <asm/semaphore.h>
+
+/* hipac data structures for INPUT, FORWARD and OUTPUT hook and the
+   corresponding netfilter hook ops */
+extern void *hipac_input;
+extern struct nf_hook_ops input_op;
+
+extern void *hipac_forward;
+extern struct nf_hook_ops forward_op;
+
+extern void *hipac_output;
+extern struct nf_hook_ops output_op;
+
+/* netlink mutex */
+extern struct semaphore nlhp_lock;
+
+int
+nfhp_register_cthelp(struct module *nfhp_cthelp_module);
+
+void
+nfhp_unregister_cthelp(struct module *nfhp_cthelp_module);
+
+#endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_proc.c 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_proc.c
--- 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_proc.c	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_proc.c	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,888 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/spinlock.h>
+#include <linux/netfilter.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include "nfhp_mod.h"
+#include "hipac.h"
+
+#define BT_I "rlp_input"
+#define BT_F "rlp_forward"
+#define BT_O "rlp_output"
+#define DT_I "dimtree_input"
+#define DT_F "dimtree_forward"
+#define DT_O "dimtree_output"
+#define HP_I "hipac_rules_input"
+#define HP_F "hipac_rules_forward"
+#define HP_O "hipac_rules_output"
+#define HP_C "hipac_chains"
+#define MEM  "mem"
+#define INF  "info"
+
+#define INF_M    S_IRUGO | S_IWUSR
+#define OTH_M    S_IRUSR
+
+struct proc_data
+{
+	char *text;
+	void *stat;
+	u32 len, valid_len;
+	rwlock_t lock;
+	void *hipac;
+	char *hipac_name;
+};
+
+struct nfhp_proc_entry
+{
+	const char *name;
+	struct proc_dir_entry *entry;
+	struct proc_dir_entry *parent;
+	mode_t mode;
+	read_proc_t *read_fn;
+	write_proc_t *write_fn;
+	void *hipac;
+	char *hipac_name;
+	u32 text_mem_required;
+	u32 stat_mem_required;
+};
+static struct proc_dir_entry *nfhipac_dir, *stat_dir;
+static const char proc_nfhipac_dir[] = "nf-hipac";
+static const char proc_stat_dir[]    = "statistics";
+
+static write_proc_t info_write;
+static read_proc_t info_read;
+static read_proc_t mem_read;
+static read_proc_t rlp_read;
+static read_proc_t dimtree_read;
+static read_proc_t hipac_r_read;
+static read_proc_t hipac_c_read;
+
+/* the non constant members are initialized by init_nfhp_proc() */
+static struct nfhp_proc_entry nfhp_proc[] =
+{
+	{ INF,   NULL, NULL, INF_M, info_read,    info_write, NULL, NULL,
+	  1000,  sizeof(struct hipac_user_stat)    },
+
+	{ MEM,   NULL, NULL, OTH_M, mem_read,     NULL,       NULL, NULL,
+	  2000,  sizeof(struct hipac_mem_stat)     },
+
+	{ BT_I,  NULL, NULL, OTH_M, rlp_read,     NULL,       NULL, "INPUT",
+	  25000, sizeof(struct hipac_rlp_stat)     },
+
+	{ BT_F,  NULL, NULL, OTH_M, rlp_read,     NULL,       NULL, "FORWARD",
+	  25000, sizeof(struct hipac_rlp_stat)     },
+
+	{ BT_O,  NULL, NULL, OTH_M, rlp_read,     NULL,       NULL, "OUTPUT",
+	  25000, sizeof(struct hipac_rlp_stat)     },
+
+	{ DT_I,  NULL, NULL, OTH_M, dimtree_read, NULL,       NULL, "INPUT",
+	  3000,  sizeof(struct hipac_dimtree_stat) },
+
+	{ DT_F,  NULL, NULL, OTH_M, dimtree_read, NULL,       NULL, "FORWARD",
+	  3000,  sizeof(struct hipac_dimtree_stat) },
+
+	{ DT_O,  NULL, NULL, OTH_M, dimtree_read, NULL,       NULL, "OUTPUT",
+	  3000,  sizeof(struct hipac_dimtree_stat) },
+
+	{ HP_I,  NULL, NULL, OTH_M, hipac_r_read, NULL,       NULL, "INPUT",
+	  3000,  sizeof(struct hipac_rule_stat)    },
+
+	{ HP_F,  NULL, NULL, OTH_M, hipac_r_read, NULL,       NULL, "FORWARD",
+	  3000,  sizeof(struct hipac_rule_stat)    },
+
+	{ HP_O,  NULL, NULL, OTH_M, hipac_r_read, NULL,       NULL, "OUTPUT",
+	  3000,  sizeof(struct hipac_rule_stat)    },
+
+	{ HP_C,  NULL, NULL, OTH_M, hipac_c_read, NULL,       NULL, NULL,
+	  4000,  sizeof(struct hipac_chain_stat)   }
+};
+
+static const char indent_spc[] = "    ";
+static u64 nfhp_total_mem = 0;
+
+
+
+/*
+ * helpers
+ */
+
+static inline void
+init_nfhp_proc(struct proc_dir_entry *nfhipac_dir,
+	       struct proc_dir_entry *stat_dir)
+{
+	int i;
+
+	for (i = 0; i < sizeof(nfhp_proc) / sizeof(*nfhp_proc); i++) {
+		if (nfhp_proc[i].write_fn == info_write) {
+			nfhp_proc[i].parent = nfhipac_dir;
+		} else {
+			nfhp_proc[i].parent = stat_dir;
+		}
+		if (nfhp_proc[i].hipac_name == NULL) {
+			continue;
+		}
+		if (strcmp(nfhp_proc[i].hipac_name, "INPUT") == 0) {
+			nfhp_proc[i].hipac = hipac_input;
+		} else if (strcmp(nfhp_proc[i].hipac_name, "FORWARD") == 0) {
+			nfhp_proc[i].hipac = hipac_forward;
+		} else {
+			nfhp_proc[i].hipac = hipac_output;
+		}
+	}
+}
+
+static inline int
+init_data(struct proc_data *data, const struct nfhp_proc_entry *e)
+{
+	data->text = kmalloc(e->text_mem_required, GFP_KERNEL);
+	if (data->text == NULL) {
+		return -1;
+	}
+	data->stat = kmalloc(e->stat_mem_required, GFP_KERNEL);
+	if (data->stat == NULL) {
+		kfree(data->text);
+		return -1;
+	}
+	data->len = e->text_mem_required;
+	data->valid_len = 0;
+	data->lock = RW_LOCK_UNLOCKED;
+	data->hipac = e->hipac;
+	data->hipac_name = e->hipac_name;
+	return 0;
+}
+
+static inline void
+free_data(struct proc_data *data)
+{
+	if (data == NULL) {
+		return;
+	}
+	if (data->text != NULL) {
+		kfree(data->text);
+	}
+	if (data->stat != NULL) {
+		kfree(data->stat);
+	}
+	kfree(data);
+}
+
+static inline void
+print_inline(struct proc_data *data, int indent)
+{
+	int i;
+
+	for (i = 0; i < indent; i++) {
+		data->valid_len += sprintf(data->text + data->valid_len,
+					   indent_spc);
+	}
+}
+
+static int
+print_desc(struct proc_data *data, int indent, const char *desc)
+{
+	if (data->len < data->valid_len + indent * strlen(indent_spc) +
+	    strlen(desc)) {
+		/* this should never happen */
+		printk(KERN_ERR "%s: too little memory reserved\n",
+		       __FUNCTION__);
+		return -1;
+	}
+	print_inline(data, indent);
+	data->valid_len += sprintf(data->text + data->valid_len, desc);
+	return 0;
+}
+
+static int
+print_scalar(struct proc_data *data, int indent, const char *desc, u64 val)
+{
+	if (data->len < data->valid_len + indent * strlen(indent_spc) +
+	    strlen(desc) + 22) {
+		/* this should never happen */
+		printk(KERN_ERR "%s: too little memory reserved\n",
+		       __FUNCTION__);
+		return -1;
+	}
+	print_inline(data, indent);
+	data->valid_len += sprintf(data->text + data->valid_len, desc);
+	data->valid_len += sprintf(data->text + data->valid_len,
+				   " %9llu\n", val);
+	return 0;
+}
+
+static int
+print_map(struct proc_data *data, int indent, const char *desc,
+	  u32 map[], int len)
+{
+	int i, empty = 1;
+
+	if (data->len < data->valid_len + (1 + len) * indent *
+	    strlen(indent_spc) + strlen(desc) + 1 + len * 25) {
+		/* this should never happen */
+		printk(KERN_ERR "%s: too little memory reserved\n",
+		       __FUNCTION__);
+		return -1;
+	}
+	for (i = 0; i < len; i++) {
+		if (map[i] == 0) {
+			continue;
+		}
+		if (empty) {
+			empty = 0;
+			print_inline(data, indent);
+			data->valid_len += sprintf(data->text +
+						   data->valid_len, desc);
+			data->valid_len += sprintf(data->text +
+						   data->valid_len, "\n");
+		}
+		print_inline(data, indent);
+		data->valid_len += sprintf(data->text + data->valid_len,
+					   "  %2u: %9u\n", i, map[i]);
+	}
+	return 0;
+}
+
+static int
+print_dist(struct proc_data *data, int indent, const char *desc,
+	   u32 dist[], u32 len)
+{
+	int i, empty = 1;
+
+	if (data->len < data->valid_len + (1 + len) * indent *
+	    strlen(indent_spc) + strlen(desc) + 1 + (len - 1) * 39 + 38) {
+		/* this should never happen */
+		printk(KERN_ERR "%s: too little memory reserved\n",
+		       __FUNCTION__);
+		return -1;
+	}
+	if (len == 0) {
+		return 0;
+	}
+	for (i = 0; i < len - 1; i++) {
+		if (dist[i] == 0) {
+			continue;
+		}
+		if (empty) {
+			empty = 0;
+			print_inline(data, indent);
+			data->valid_len += sprintf(data->text +
+						   data->valid_len, desc);
+			data->valid_len += sprintf(data->text +
+						   data->valid_len, "\n");
+		}
+		print_inline(data, indent);
+		data->valid_len +=
+			sprintf(data->text + data->valid_len,
+				"  [%9u, %9u]: %9u\n",
+				i == 0 ? 0 : 1 << (i - 1), (1 << i) - 1,
+				dist[i]);
+	}
+	if (dist[i] == 0) {
+		return 0;
+	}
+	if (empty) {
+		print_inline(data, indent);
+		data->valid_len += sprintf(data->text + data->valid_len, desc);
+		data->valid_len += sprintf(data->text + data->valid_len, "\n");
+	}
+	print_inline(data, indent);
+	data->valid_len += sprintf(data->text + data->valid_len,
+				   "  [%9u,  infinity[: %9u\n", 1 << (i - 1),
+				   dist[i]);
+	return 0;
+}
+
+static int
+write_stat(char *buf, char **start, off_t off, int count, int *eof,
+	   struct proc_data *d)
+{
+	int len = d->valid_len - off;
+
+	if (len <= 0) {
+		*eof = 1;
+		return 0;
+	}
+	if (len <= count) {
+		*eof = 1;
+	} else {
+		len = count;
+	}
+	read_lock(&d->lock);
+	memcpy(buf, d->text + off, len);
+	read_unlock(&d->lock);
+	*start = buf;
+	return len;
+}
+
+
+
+/*
+ * i/o functions
+ */
+
+static int
+info_write(struct file *file, const char *buffer, unsigned long count,
+	   void *data)
+{
+	static const char nfhp_first[] = "nf-hipac-first\n";
+	static const char ipt_first[]  = "iptables-first\n";
+	static char buf[32] = {0};
+	int len = count > sizeof(buf) - 1 ? sizeof(buf) - 1 : count;
+	u64 new_max_mem;
+	int ret;
+	
+	if (copy_from_user(buf, buffer, len)) {
+		return -EFAULT;
+        }
+
+	/* strings don't have to contain \n at the end */
+	if (!(count == sizeof(nfhp_first) - 1 ||
+	      count == sizeof(ipt_first) - 1 ||
+	      count == sizeof(nfhp_first) - 2 ||
+	      count == sizeof(ipt_first) - 2)) {
+		if (count >= 9 && !(count == 10 && buf[9] != '\n')) {
+			/* input definitely too large */
+			return -EINVAL;
+		}
+
+		/* interpret as number */
+		new_max_mem = simple_strtoul(buf, NULL, 10) << 20;
+		if (new_max_mem > nfhp_total_mem) {
+			new_max_mem = nfhp_total_mem;
+		}
+		if (new_max_mem == hipac_get_maxmem()) {
+			return len;
+		}
+		down(&nlhp_lock);
+		switch (hipac_set_maxmem(new_max_mem)) {
+		    case HE_LOW_MEMORY:
+			    up(&nlhp_lock);
+			    printk(KERN_NOTICE "nf_hipac: actual memory "
+				   "consumption larger than memory bound "
+				   "written to " INF "\n");
+			    return -EINVAL;
+		    case HE_OK:
+			    up(&nlhp_lock);
+			    return len;
+		    default:
+			    /* this should never happen */
+			    up(&nlhp_lock);
+			    printk(KERN_ERR "%s: unexpected return value\n",
+				   __FUNCTION__);
+			    return -EINVAL;
+		}
+	}
+
+	/* change order */
+	if (strncmp(buf, nfhp_first, len) == 0) {
+		if (input_op.priority >= NF_IP_PRI_FILTER) {
+			nf_unregister_hook(&input_op);
+			nf_unregister_hook(&forward_op);
+			nf_unregister_hook(&output_op);
+			input_op.priority = forward_op.priority =
+				output_op.priority = NF_IP_PRI_FILTER - 1;
+			goto hook_register;
+		}
+	} else if (strncmp(buf, ipt_first, len) == 0) {
+		if (input_op.priority <= NF_IP_PRI_FILTER) {
+			nf_unregister_hook(&input_op);
+			nf_unregister_hook(&forward_op);
+			nf_unregister_hook(&output_op);
+			input_op.priority = forward_op.priority =
+				output_op.priority = NF_IP_PRI_FILTER + 1;
+			goto hook_register;
+		}
+	}
+	return len;
+
+hook_register:
+	if ((ret = nf_register_hook(&input_op)) < 0) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "register input hook\n");
+		goto cleanup;
+	}
+	if ((ret = nf_register_hook(&forward_op)) < 0) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "register forward hook\n");
+		goto cleanup_input;
+	}
+	if ((ret = nf_register_hook(&output_op)) < 0) {
+		printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+		       "register output hook\n");
+		goto cleanup_forward;
+	}
+	return len;
+cleanup_forward:
+	nf_unregister_hook(&forward_op);
+cleanup_input:
+	nf_unregister_hook(&input_op);
+cleanup:
+	return ret;	
+}
+
+/*
+  the statistics are being rebuilt if the proc entry is read from its
+  beginning; if you modify the ruleset while at the same time reading
+  a proc file with a pager strange things might happen to your pager
+  output ;-)
+  nonetheless this is the best we can do ... at least I think so :-)
+*/
+
+#define NEED_REBUILD (off == 0 || d->valid_len == 0)
+#define LEN(x)       (sizeof(x) / sizeof(*(x)))
+#define EXEC(fn)            \
+do {                        \
+	if (fn < 0) {       \
+                goto error; \
+	}                   \
+} while (0)
+
+static int
+info_read(char *page, char **start, off_t off, int count, int *eof,
+	  void *data)
+{
+	struct proc_data *d = data;
+	struct hipac_user_stat *stat = d->stat;
+
+	if (!NEED_REBUILD) {
+		return write_stat(page, start, off, count, eof, d);
+	}
+
+	/* (re)compute statistics */
+	down(&nlhp_lock);
+	if (hipac_get_user_stat(stat) != HE_OK) {
+		/* this should never happen */
+		up(&nlhp_lock);
+		printk(KERN_ERR "%s: hipac_get_user_stat failed\n",
+		       __FUNCTION__);
+		*eof = 1;
+		return 0;
+	}
+	up(&nlhp_lock);
+
+	/* (re)build text */
+	write_lock(&d->lock);
+	d->valid_len = 0;
+	EXEC(print_scalar(d, 0, "maximum memory bound:    ",
+			  hipac_get_maxmem()));
+	EXEC(print_scalar(d, 0, "total memory (used):     ",
+			  stat->total_mem_tight));
+	EXEC(print_scalar(d, 0, "total memory (allocated):",
+			  stat->total_mem_real));
+	EXEC(print_scalar(d, 0, "total number of chains:  ",
+			  stat->chain_num));
+	EXEC(print_scalar(d, 0, "total number of rules:   ",
+			  stat->rule_num));
+	if (input_op.priority < NF_IP_PRI_FILTER) {
+		EXEC(print_desc(d, 0, "nf-hipac is invoked before "
+				"iptables\n"));
+	} else {
+		EXEC(print_desc(d, 0, "iptables is invoked before "
+				"nf-hipac\n"));
+	}
+#ifdef SINGLE_PATH
+	EXEC(print_desc(d, 0, "compiled with SINGLE_PATH optimization\n"));
+#else
+	EXEC(print_desc(d, 0, "compiled without SINGLE_PATH optimization\n"));
+#endif
+	write_unlock(&d->lock);
+	return write_stat(page, start, off, count, eof, d);
+
+ error:
+	write_unlock(&d->lock);
+	*eof = 1;
+	return 0;
+}
+
+static int
+mem_read(char *page, char **start, off_t off, int count, int *eof,
+	 void *data)
+{
+	struct proc_data *d = data;
+	struct hipac_mem_stat *stat = d->stat;
+
+	if (!NEED_REBUILD) {
+		return write_stat(page, start, off, count, eof, d);
+	}
+
+	/* (re)compute statistics */
+	down(&nlhp_lock);
+	if (hipac_get_mem_stat(stat) != HE_OK) {
+		/* this should never happen */
+		up(&nlhp_lock);
+		printk(KERN_ERR "%s: hipac_get_mem_stat failed\n",
+		       __FUNCTION__);
+		*eof = 1;
+		return 0;
+	}
+	up(&nlhp_lock);
+
+	/* (re)build text */
+	write_lock(&d->lock);
+	d->valid_len = 0;
+	EXEC(print_scalar(d, 0, "total memory (used):     ",
+			  stat->total_mem_tight));
+	EXEC(print_scalar(d, 0, "total memory (allocated):",
+			  stat->total_mem_real));
+	EXEC(print_desc(d, 0, "memhash:\n"));
+	EXEC(print_scalar(d, 1, "number of entries:                    ",
+			  stat->memhash_elem_num));
+	EXEC(print_scalar(d, 1, "number of buckets:                    ",
+			  stat->memhash_len));
+	EXEC(print_scalar(d, 1, "number of entries in smallest bucket: ",
+			  stat->memhash_smallest_bucket_len));
+	EXEC(print_scalar(d, 1, "number of entries in largest bucket:  ",
+			  stat->memhash_biggest_bucket_len));
+	EXEC(print_dist(d, 1, "number of buckets with [x, y] entries:",
+			stat->memhash_bucket_stat,
+			LEN(stat->memhash_bucket_stat)));
+	write_unlock(&d->lock);
+	return write_stat(page, start, off, count, eof, d);
+
+ error:
+	write_unlock(&d->lock);
+	*eof = 1;
+	return 0;
+}
+
+static int
+rlp_read(char *page, char **start, off_t off, int count, int *eof,
+	 void *data)
+{
+	static char buf[100] = {0};
+	struct proc_data *d = data;
+	struct hipac_rlp_stat *stat = d->stat;
+	int i;
+	
+	if (!NEED_REBUILD) {
+		return write_stat(page, start, off, count, eof, d);
+	}
+
+	/* (re)compute statistics */
+	down(&nlhp_lock);
+	if (hipac_get_rlp_stat(d->hipac, stat) != HE_OK) {
+		/* this should never happen */
+		up(&nlhp_lock);
+		printk(KERN_ERR "%s: hipac_get_rlp_stat failed\n",
+		       __FUNCTION__);
+		*eof = 1;
+		return 0;
+	}
+	up(&nlhp_lock);
+
+	/* (re)build text */
+	write_lock(&d->lock);
+	d->valid_len = 0;
+	EXEC(print_desc(d, 0, "root chain: "));
+	EXEC(print_desc(d, 0, d->hipac_name));
+	EXEC(print_desc(d, 0, "\n"));
+	EXEC(print_scalar(d, 0, "total memory (used):             ",
+			  stat->total_mem_tight));
+	EXEC(print_scalar(d, 0, "total memory (allocated):        ",
+			  stat->total_mem_real));
+	EXEC(print_scalar(d, 1, "rlp memory (used):          ",
+			  stat->rlp_mem_tight));
+	EXEC(print_scalar(d, 1, "rlp memory (allocated):     ",
+			  stat->rlp_mem_real));
+	EXEC(print_scalar(d, 1, "termrule memory (used):     ",
+			  stat->termrule_mem_tight));
+	EXEC(print_scalar(d, 1, "termrule memory (allocated):",
+			  stat->termrule_mem_real));
+	EXEC(print_scalar(d, 0, "number of rlps:                  ",
+			  stat->rlp_num));
+	EXEC(print_map(d, 1, "number of rlps in dimid x:",
+		       stat->rlp_dimid_num, LEN(stat->rlp_dimid_num)));
+	EXEC(print_map(d, 1, "number of rlps in depth x:",
+		       stat->rlp_depth_num, LEN(stat->rlp_depth_num)));
+	EXEC(print_scalar(d, 0, "number of termrule blocks:       ",
+			  stat->termrule_num));
+	EXEC(print_scalar(d, 0, "total number of termrule entries:",
+			  stat->termrule_ptr_num));
+	EXEC(print_scalar(d, 0, "number of keys:                  ",
+			  stat->keys_num));
+	for (i = 0; i < LEN(stat->rlp_dimid_keys_stat); i++) {
+		if (snprintf(buf, sizeof(buf) - 1, "number of rlps in dimid"
+			     " %d with [x, y] keys:", i) < 0) {
+			printk(KERN_ERR "%s: static buffer too small\n",
+			       __FUNCTION__);
+			break;
+		}
+		EXEC(print_dist(d, 1, buf, stat->rlp_dimid_keys_stat[i],
+				LEN(*stat->rlp_dimid_keys_stat)));
+	}
+	EXEC(print_scalar(d, 0, "number of terminal pointers:     ",
+			  stat->termptr_num));
+	EXEC(print_map(d, 1, "number of terminal pointers in dimid x:",
+		       stat->termptr_dimid_num,
+		       LEN(stat->termptr_dimid_num)));
+	EXEC(print_map(d, 1, "number of terminal pointers in depth x:",
+		       stat->termptr_depth_num,
+		       LEN(stat->termptr_depth_num)));
+	EXEC(print_scalar(d, 0, "number of non-terminal pointers: ",
+			  stat->nontermptr_num));
+	EXEC(print_map(d, 1, "number of non-terminal pointers in dimid x:",
+		       stat->nontermptr_dimid_num,
+		       LEN(stat->nontermptr_dimid_num)));
+	EXEC(print_map(d, 1, "number of non-terminal pointers in depth x:",
+		       stat->nontermptr_depth_num,
+		       LEN(stat->nontermptr_depth_num)));
+	EXEC(print_scalar(d, 0, "number of dt_elem structs:       ",
+			  stat->dt_elem_num));
+	EXEC(print_scalar(d, 1, "total number of dt_elem entries:"
+			  "      ", stat->dt_elem_ptr_num));
+	EXEC(print_dist(d, 1, "number of dt_elem structs with [x, y] entries:",
+			stat->dt_elem_stat, LEN(stat->dt_elem_stat)));
+	write_unlock(&d->lock);
+	return write_stat(page, start, off, count, eof, d);
+
+ error:
+	write_unlock(&d->lock);
+	*eof = 1;
+	return 0;
+}
+
+static int
+dimtree_read(char *page, char **start, off_t off, int count, int *eof,
+	     void *data)
+{
+	struct proc_data *d = data;
+	struct hipac_dimtree_stat *stat = d->stat;
+
+	if (!NEED_REBUILD) {
+		return write_stat(page, start, off, count, eof, d);
+	}
+
+	/* (re)compute statistics */
+	down(&nlhp_lock);
+	if (hipac_get_dimtree_stat(d->hipac, stat) != HE_OK) {
+		/* this should never happen */
+		up(&nlhp_lock);
+		printk(KERN_ERR "%s: hipac_get_dimtree_stat failed\n",
+		       __FUNCTION__);
+		*eof = 1;
+		return 0;
+	}
+	up(&nlhp_lock);
+
+	/* (re)build text */
+	write_lock(&d->lock);
+	d->valid_len = 0;
+	EXEC(print_desc(d, 0, "root chain: "));
+	EXEC(print_desc(d, 0, d->hipac_name));
+	EXEC(print_desc(d, 0, "\n"));
+	EXEC(print_scalar(d, 0, "chain memory (used):           ",
+			  stat->chain_mem_tight));
+	EXEC(print_scalar(d, 0, "chain memory (allocated):      ",
+			  stat->chain_mem_real));
+	EXEC(print_scalar(d, 0, "number of rules:               ",
+			  stat->rule_num));
+	EXEC(print_scalar(d, 1, "number of rules with ipt matches:      "
+			  "            ", stat->rules_with_exec_matches));
+	EXEC(print_scalar(d, 1, "number of rules with ipt target:       "
+			  "            ", stat->rules_with_exec_target));
+	EXEC(print_dist(d, 1, "number of \"same pos rules\" series of "
+			"length [x, y]:", stat->rules_same_pos_stat,
+			LEN(stat->rules_same_pos_stat)));
+	EXEC(print_map(d, 0, "number of rules with x dt_matches:",
+		       stat->dt_match_stat, LEN(stat->dt_match_stat)));
+	write_unlock(&d->lock);
+	return write_stat(page, start, off, count, eof, d);
+
+ error:
+	write_unlock(&d->lock);
+	*eof = 1;
+	return 0;
+}
+
+static int
+hipac_r_read(char *page, char **start, off_t off, int count, int *eof,
+	     void *data)
+{
+	struct proc_data *d = data;
+	struct hipac_rule_stat *stat = d->stat;
+
+	if (!NEED_REBUILD) {
+		return write_stat(page, start, off, count, eof, d);
+	}
+
+	/* (re)compute statistics */
+	down(&nlhp_lock);
+	if (hipac_get_rule_stat(d->hipac, stat) != HE_OK) {
+		/* this should never happen */
+		up(&nlhp_lock);
+		printk(KERN_ERR "%s: hipac_get_rule_stat failed\n",
+		       __FUNCTION__);
+		*eof = 1;
+		return 0;
+	}
+	up(&nlhp_lock);
+
+	/* (re)build text */
+	write_lock(&d->lock);
+	d->valid_len = 0;
+	EXEC(print_desc(d, 0, "root chain: "));
+	EXEC(print_desc(d, 0, d->hipac_name));
+	EXEC(print_desc(d, 0, "\n"));
+	EXEC(print_scalar(d, 0, "number of rules:                        ",
+			  stat->rule_num));
+	EXEC(print_scalar(d, 1, "number of rules with ipt matches:  ",
+			  stat->exec_match_num));
+	EXEC(print_scalar(d, 1, "number of rules with ipt target:   ",
+			  stat->exec_target_num));
+	EXEC(print_scalar(d, 1, "number of rules with jump target:  ",
+			  stat->jump_target_num));
+	EXEC(print_scalar(d, 1, "number of rules with return target:",
+			  stat->return_target_num));
+	EXEC(print_map(d, 0, "number of rules with x hipac_matches:   ",
+		       stat->hipac_match_stat, LEN(stat->hipac_match_stat)));
+	EXEC(print_map(d, 0, "number of rules with x inverted matches:",
+		       stat->inv_rules_stat, LEN(stat->inv_rules_stat)));
+	write_unlock(&d->lock);
+	return write_stat(page, start, off, count, eof, d);
+
+ error:
+	write_unlock(&d->lock);
+	*eof = 1;
+	return 0;
+}
+
+static int
+hipac_c_read(char *page, char **start, off_t off, int count, int *eof,
+	     void *data)
+{
+	struct proc_data *d = data;
+	struct hipac_chain_stat *stat = d->stat;
+
+	if (!NEED_REBUILD) {
+		return write_stat(page, start, off, count, eof, d);
+	}
+
+	/* (re)compute statistics */
+	down(&nlhp_lock);
+	if (hipac_get_chain_stat(stat) != HE_OK) {
+		/* this should never happen */
+		up(&nlhp_lock);
+		printk(KERN_ERR "%s: hipac_get_chain_stat failed\n",
+		       __FUNCTION__);
+		*eof = 1;
+		return 0;
+	}
+	up(&nlhp_lock);
+
+	/* (re)build text */
+	write_lock(&d->lock);
+	d->valid_len = 0;
+	EXEC(print_scalar(d, 0, "chain memory (used):     ", stat->mem_tight));
+	EXEC(print_scalar(d, 0, "chain memory (allocated):", stat->mem_real));
+	EXEC(print_scalar(d, 0, "number of chains:        ", stat->chain_num));
+	EXEC(print_scalar(d, 0, "number of rules:         ", stat->rule_num));
+	EXEC(print_dist(d, 1, "number of chains with [x, y] prefixes:     ",
+			stat->prefix_stat, LEN(stat->prefix_stat)));
+	EXEC(print_dist(d, 1, "number of chains with [x, y] incoming arcs:",
+			stat->incoming_stat, LEN(stat->incoming_stat)));
+	EXEC(print_dist(d, 1, "number of chains with [x, y] outgoing arcs:",
+			stat->outgoing_stat, LEN(stat->outgoing_stat)));
+	write_unlock(&d->lock);
+	return write_stat(page, start, off, count, eof, d);
+
+ error:
+	write_unlock(&d->lock);
+	*eof = 1;
+	return 0;
+}
+
+void
+hpproc_init(u64 total_mem)
+{
+	struct proc_data *data;
+	int i, j;
+
+	nfhp_total_mem = total_mem;
+
+	/* create proc directories */
+	nfhipac_dir = proc_mkdir(proc_nfhipac_dir, proc_net);
+	if (nfhipac_dir == NULL) {
+		printk(KERN_NOTICE "nf_hipac: unable to create proc "
+		       "directory\n");
+		return;
+	}
+	nfhipac_dir->owner = THIS_MODULE;
+	stat_dir = proc_mkdir(proc_stat_dir, nfhipac_dir);
+	if (stat_dir == NULL) {
+		printk(KERN_NOTICE "nf_hipac: unable to create proc "
+		       "directory\n");
+		goto cleanup_nfhipac_dir;
+	}
+	stat_dir->owner = THIS_MODULE;
+
+	/* create statistics entries */
+	init_nfhp_proc(nfhipac_dir, stat_dir);
+	for (i = 0; i < sizeof(nfhp_proc) / sizeof(*nfhp_proc); i++) {
+		data = kmalloc(sizeof(*data), GFP_KERNEL);
+		if (data == NULL) {
+			printk(KERN_NOTICE "nf_hipac: unable to create "
+			       "proc infrastructure because of low memory\n");
+			goto cleanup;
+		}
+		if (init_data(data, &nfhp_proc[i]) < 0) {
+			printk(KERN_NOTICE "nf_hipac: unable to create "
+			       "proc infrastructure because of low memory\n");
+			goto cleanup;
+		}
+		nfhp_proc[i].entry = create_proc_entry(nfhp_proc[i].name,
+						       nfhp_proc[i].mode,
+						       nfhp_proc[i].parent);
+		if (nfhp_proc[i].entry == NULL) {
+			printk(KERN_NOTICE "nf_hipac: unable to create proc "
+			       "entry\n");
+			goto cleanup;
+		}
+		nfhp_proc[i].entry->owner = THIS_MODULE;
+		nfhp_proc[i].entry->data = data;
+		nfhp_proc[i].entry->read_proc = nfhp_proc[i].read_fn;
+		nfhp_proc[i].entry->write_proc = nfhp_proc[i].write_fn;
+	}
+	return;
+
+ cleanup:
+	for (j = 0; j <= i; j++)
+		remove_proc_entry(nfhp_proc[j].name, nfhp_proc[i].parent);
+	remove_proc_entry(proc_stat_dir, nfhipac_dir);
+ cleanup_nfhipac_dir:
+	remove_proc_entry(proc_nfhipac_dir, proc_net);
+	return;
+}
+
+void
+hpproc_exit(void)
+{
+	int i;
+
+	for (i = 0; i < sizeof(nfhp_proc) / sizeof(*nfhp_proc); i++)
+		remove_proc_entry(nfhp_proc[i].name, nfhp_proc[i].parent);
+	remove_proc_entry(proc_stat_dir, nfhipac_dir);
+	remove_proc_entry(proc_nfhipac_dir, proc_net);
+}
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_proc.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_proc.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/nfhp_proc.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/nfhp_proc.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,24 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2002-2003   hipac core team <nf@hipac.org>:
+ *     +---------------------------+--------------------------+
+ *     |      Michael Bellion      |       Thomas Heinz       |
+ *     |   <mbellion@hipac.org>    |   <creatix@hipac.org>    |
+ *     +---------------------------+--------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _NFHP_PROC_H
+#define _NFHP_PROC_H
+
+void
+hpproc_init(u64 total_mem);
+
+void
+hpproc_exit(void);
+
+#endif
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/rlp.c 2.6.13-patched/net/ipv4/netfilter/nf-hipac/rlp.c
--- 2.6.13/net/ipv4/netfilter/nf-hipac/rlp.c	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/rlp.c	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,537 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#include "global.h"
+#include "rlp.h"
+
+#define KEYSIZE(bittype) (1 << ((bittype) + 1))
+#define PTR_ALIGN(v)         (((v) + (__alignof__(void *) - 1)) & \
+			      (~(__alignof__(void *) - 1)))
+#define FIRST_KEY(spec)     ((void *) (spec) + sizeof(*(spec)) +           \
+			     HAS_WILDCARD_SPEC(spec) * sizeof(void *))
+#define FIRST_NEXTSPEC(spec) (FIRST_KEY(spec) +                              \
+			      PTR_ALIGN((spec)->num * KEYSIZE((spec)->bittype)))
+
+
+/*
+ * optimized locate functions
+ */
+
+static struct gen_spec *
+#ifdef SINGLE_PATH
+u16_locate(const struct rlp_spec *spec, const void *packet, int *hotdrop)
+#else
+u16_locate(const struct rlp_spec *spec, const void *packet, int *hotdrop,
+	   struct gen_spec **nodes, __u8 *nodes_len)
+#endif
+{
+	const __u16 key = extract_fn[spec->dimid](packet, hotdrop);
+	const __u16 *part = (void *) spec + sizeof(*spec);
+	__u16 left = 0;
+	__u16 right = spec->num - 1;
+	__u16 pos;
+
+	while (left <= right) {
+		pos = (left + right) >> 1;
+		if (part[pos] < key) {
+			left = pos + 1;
+		} else if (pos && part[pos - 1] >= key) {
+			right = pos - 1;
+		} else {
+			return *(struct gen_spec **)
+				((void *) part + PTR_ALIGN(spec->num << 1) +
+				 pos * sizeof(void *));
+		}
+	}
+
+	/* should never be reached */
+	assert(1 == 0);
+	return NULL;
+}
+
+#ifdef SINGLE_PATH
+static struct gen_spec *
+u32_locate(const struct rlp_spec *spec, const void *packet, int *hotdrop)
+{
+	const __u32 key = extract_fn[spec->dimid](packet, hotdrop);
+	const __u32 *part = (void *) spec + sizeof(*spec);
+	__u32 left = 0;
+	__u32 right = spec->num - 1;
+	__u32 pos;
+
+	while (left <= right) {
+		pos = (left + right) >> 1;
+		if (part[pos] < key) {
+			left = pos + 1;
+		} else if (pos && part[pos - 1] >= key) {
+			right = pos - 1;
+		} else {
+			return *(struct gen_spec **)
+				((void *) part + PTR_ALIGN(spec->num << 2) +
+				 pos * sizeof(void *));
+		}
+	}
+
+	/* should never be reached */
+	assert(1 == 0);
+	return NULL;
+}
+#else
+static struct gen_spec *
+u32_locate(const struct rlp_spec *spec, const void *packet, int *hotdrop,
+	   struct gen_spec **nodes, __u8 *nodes_len)
+{
+	const __u32 key = extract_fn[spec->dimid](packet, hotdrop);
+	const __u32 *part = (void *) spec + sizeof(*spec) +
+		HAS_WILDCARD_SPEC(spec) * sizeof(void *);
+	__u32 left = 0;
+	__u32 right = spec->num - 1;
+	__u32 pos;
+
+	while (left <= right) {
+		pos = (left + right) >> 1;
+		if (part[pos] < key) {
+			left = pos + 1;
+		} else if (pos && part[pos - 1] >= key) {
+			right = pos - 1;
+		} else {
+			if (HAS_WILDCARD_SPEC(spec) && *((void **) part - 1)
+				&& !(*hotdrop)) {
+				nodes[(*nodes_len)++] = *((void **) part - 1);
+			}
+			return *(struct gen_spec **)
+				((void *) part + PTR_ALIGN(spec->num << 2) +
+				 pos * sizeof(void *));
+		}
+	}
+
+	/* should never be reached */
+	assert(1 == 0);
+	return NULL;
+}
+#endif // SINGLE_PATH
+
+
+
+/*
+ * lookup helper
+ */
+
+static inline int
+u16_key_exists(const struct rlp_spec *spec, __u32 key, struct locate_inf *inf,
+	       __u32 *position)
+{
+	const __u16 *part = FIRST_KEY(spec);
+	__u16 left = 0;
+	__u16 right = spec->num - 1;
+	__u16 pos;
+
+	while (left <= right) {
+		pos = (left + right) >> 1;
+		if (part[pos] < key) {
+			left = pos + 1;
+		} else if (pos && part[pos - 1] >= key) {
+			right = pos - 1;
+		} else {
+			if (inf != NULL) {
+				inf->key = part[pos];
+				inf->nextspec = FIRST_NEXTSPEC(spec) +
+					pos * sizeof(void *);
+			}
+			if (position != NULL) {
+				*position = pos;
+			}
+			return part[pos] == key;
+		}
+	}
+
+	/* should never be reached */
+	assert(1 == 0);
+	return 0;
+}
+
+static inline int
+u32_key_exists(const struct rlp_spec *spec, __u32 key, struct locate_inf *inf,
+	       __u32 *position)
+{
+	const __u32 *part = FIRST_KEY(spec);
+	__u32 left = 0;
+	__u32 right = spec->num - 1;
+	__u32 pos;
+
+	while (left <= right) {
+		pos = (left + right) >> 1;
+		if (part[pos] < key) {
+			left = pos + 1;
+		} else if (pos && part[pos - 1] >= key) {
+			right = pos - 1;
+		} else {
+			if (inf != NULL) {
+				inf->key = part[pos];
+				inf->nextspec = FIRST_NEXTSPEC(spec) +
+					pos * sizeof(void *);
+			}
+			if (position != NULL) {
+				*position = pos;
+			}
+			return part[pos] == key;
+		}
+	}
+
+	/* should never be reached */
+	assert(1 == 0);
+	return 0;
+}
+
+
+
+/*
+ * interface functions
+ */
+
+struct ptrblock **
+termrule(const struct rlp_spec *spec)
+{
+	if (unlikely(spec == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+
+	return (struct ptrblock **)
+		(FIRST_NEXTSPEC(spec) + spec->num * sizeof(void *));
+}
+
+struct rlp_spec *
+rlp_new(__u8 bittype, __u8 dimid, __u8 ins_num, const __u32 key[],
+	struct gen_spec *nextspec[])
+{
+	struct rlp_spec *new_rlp;
+
+	if (unlikely(bittype > BIT_U32 || key == NULL || nextspec == NULL ||
+		     !(ins_num == 1 || ins_num == 2) ||
+		     (ins_num == 1 && key[0] != hipac_maxkey(bittype)) ||
+		     (ins_num == 2 && (key[0] >= key[1] ||
+				       key[1] != hipac_maxkey(bittype))))) {
+		ARG_MSG;
+		return NULL;
+	}
+
+	new_rlp = hp_alloc(sizeof(*new_rlp) +
+			   HAS_WILDCARD_DIM(dimid) * sizeof(void *) +
+			   PTR_ALIGN(ins_num * KEYSIZE(bittype)) +
+			   (ins_num + 1) * sizeof(void *), 1);
+	if (new_rlp == NULL) {
+		return NULL;
+	}
+	new_rlp->rlp = 1;
+	new_rlp->bittype = bittype;
+	new_rlp->dimid = dimid;
+	new_rlp->newspec = 0;
+	new_rlp->num = ins_num;
+	*termrule(new_rlp) = NULL;
+	if (HAS_WILDCARD_DIM(dimid)) {
+		*WILDCARD(new_rlp) = NULL;
+	}
+
+	switch (bittype) {
+	case BIT_U16: {
+		__u16 *k = FIRST_KEY(new_rlp);
+		struct gen_spec **s = FIRST_NEXTSPEC(new_rlp);
+		new_rlp->locate = u16_locate;
+		k[0] = key[0];
+		s[0] = nextspec[0];
+		if (ins_num == 2) {
+			k[1] = key[1];
+			s[1] = nextspec[1];
+		}
+		break;
+	}
+	case BIT_U32: {
+		__u32 *k = FIRST_KEY(new_rlp);
+		struct gen_spec **s = FIRST_NEXTSPEC(new_rlp);
+		new_rlp->locate = u32_locate;
+		k[0] = key[0];
+		s[0] = nextspec[0];
+		if (ins_num == 2) {
+			k[1] = key[1];
+			s[1] = nextspec[1];
+		}
+		break;
+	}
+	}
+	return new_rlp;
+}
+
+__u32
+rlp_size(const struct rlp_spec *spec)
+{
+	if (unlikely(spec == NULL)) {
+		ARG_MSG;
+		return 0;
+	}
+
+	return sizeof(*spec) +
+		HAS_WILDCARD_SPEC(spec) * sizeof(void *) +
+		PTR_ALIGN(spec->num * KEYSIZE(spec->bittype)) +
+		(spec->num + 1) * sizeof(void *);
+}
+
+struct gen_spec **
+rlp_nextspec(const struct rlp_spec *spec)
+{
+	if (unlikely(spec == NULL)) {
+		ARG_MSG;
+		return NULL;
+	}
+
+	return FIRST_NEXTSPEC(spec);
+}
+
+hipac_error
+rlp_clone(const struct rlp_spec *spec, struct rlp_spec **clone)
+{
+	hipac_error stat;
+	__u32 size;
+	
+	if (unlikely(spec == NULL || clone == NULL)) {
+		ARG_ERR;
+	}
+     
+	size = rlp_size(spec);
+	*clone = hp_alloc(size, 1);
+	if (*clone == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	memcpy(*clone, spec, size);
+	(*clone)->newspec = 0;
+	stat = ptrblock_clone(*termrule(spec), termrule(*clone));
+	if (stat < 0) {
+		hp_free(*clone);
+		*clone = NULL;
+		return stat;
+	}
+	return HE_OK;
+}
+
+hipac_error
+rlp_insert(const struct rlp_spec *spec, __u8 ins_num, const __u32 key[],
+	   struct gen_spec *nextspec[], struct rlp_spec **result)
+{
+	void *first_ksrc, *ksrc, *kdst, *nsrc, *ndst;
+	struct gen_spec *lnspec[2];
+	__u32 pos[2], lkey[2];
+	__u32 i, ksize, nsize;
+	hipac_error stat;
+
+	if (unlikely(spec == NULL || key == NULL || nextspec == NULL ||
+		     result == NULL || !(ins_num == 1 || ins_num == 2) ||
+		     (ins_num == 1 &&
+		      key[0] >= hipac_maxkey(spec->bittype)) ||
+		     (ins_num == 2 &&
+		      (key[0] >= key[1] ||
+		       key[1] >= hipac_maxkey(spec->bittype))))) {
+		ARG_ERR;
+	}
+
+	switch (spec->bittype) {
+	case BIT_U16: {
+		__u8 ct = 0;
+		if (!u16_key_exists(spec, key[0], NULL, &pos[0])) {
+			lkey[ct] = key[0];
+			lnspec[ct++] = nextspec[0];
+		}
+		if (ins_num == 2 &&
+		    !u16_key_exists(spec, key[1], NULL, &pos[ct])) {
+			assert(ct == 0 || pos[0] <= pos[1]);
+			lkey[ct] = key[1];
+			lnspec[ct++] = nextspec[1];
+		}
+		ins_num = ct;
+		break;
+	}
+	case BIT_U32: {
+		__u8 ct = 0;
+		if (!u32_key_exists(spec, key[0], NULL, &pos[0])) {
+			lkey[ct] = key[0];
+			lnspec[ct++] = nextspec[0];
+		}
+		if (ins_num == 2 &&
+		    !u32_key_exists(spec, key[1], NULL, &pos[ct])) {
+			assert(ct == 0 || pos[0] <= pos[1]);
+			lkey[ct] = key[1];
+			lnspec[ct++] = nextspec[1];
+		}
+		ins_num = ct;
+		break;
+	}
+	}
+
+	/* ins_num can be 0, 1 or 2 here */
+	*result = hp_alloc(sizeof(**result) +
+			   HAS_WILDCARD_SPEC(spec) * sizeof(void *) +
+			   PTR_ALIGN((spec->num + ins_num) *
+				 KEYSIZE(spec->bittype)) +
+			   (spec->num + ins_num + 1) * sizeof(void *), 1);
+	if (*result == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	memcpy(*result, spec, sizeof(*spec) +
+	       HAS_WILDCARD_SPEC(spec) * sizeof(void *));
+	(*result)->newspec = 0;
+	(*result)->num += ins_num;
+	stat = ptrblock_clone(*termrule(spec), termrule(*result));
+	if (stat < 0) {
+		hp_free(*result);
+		*result = NULL;
+		return stat;
+	}
+
+	first_ksrc = FIRST_KEY(spec);
+	ksrc = first_ksrc;
+	kdst = FIRST_KEY(*result);
+	nsrc = FIRST_NEXTSPEC(spec);
+	ndst = FIRST_NEXTSPEC(*result);
+	for (i = 0; i < ins_num; i++) {
+		ksize = (first_ksrc + pos[i] * KEYSIZE(spec->bittype)) - ksrc;
+		nsize = (ksize / KEYSIZE(spec->bittype)) * sizeof(void *);
+		if (ksize > 0) {
+			memcpy(kdst, ksrc, ksize);
+			memcpy(ndst, nsrc, nsize);
+		}
+		ksrc += ksize;
+		kdst += ksize;
+		nsrc += nsize;
+		ndst += nsize;
+		switch (spec->bittype) {
+		case BIT_U16:
+			*(__u16 *) kdst = lkey[i];
+			break;
+		case BIT_U32:
+			*(__u32 *) kdst = lkey[i];
+			break;
+		}
+		*(struct gen_spec **) ndst = lnspec[i];
+		kdst += KEYSIZE(spec->bittype);
+		ndst += sizeof(void *);
+	}
+	ksize = (spec->num - (ins_num == 0 ? 0 : pos[ins_num - 1])) *
+		KEYSIZE(spec->bittype);
+	assert(ksize > 0);
+	nsize = (ksize / KEYSIZE(spec->bittype)) * sizeof(void *);
+	memcpy(kdst, ksrc, ksize);
+	memcpy(ndst, nsrc, nsize);
+	return HE_OK;
+}
+
+hipac_error
+rlp_delete(const struct rlp_spec *spec, __u8 del_num, const __u32 key[],
+	   struct rlp_spec **result)
+{
+	void *first_ksrc, *ksrc, *kdst, *nsrc, *ndst;
+	__u32 i, ksize, nsize;
+	hipac_error stat;
+	__u32 pos[2];
+
+	if (unlikely(spec == NULL || key == NULL || result == NULL ||
+		     del_num >= spec->num || !(del_num == 1 || del_num == 2) ||
+		     (del_num == 1 &&
+		      key[0] >= hipac_maxkey(spec->bittype)) ||
+		     (del_num == 2 &&
+		      (key[0] >= key[1] ||
+		       key[1] >= hipac_maxkey(spec->bittype))))) {
+		ARG_ERR;
+	}
+
+	switch (spec->bittype) {
+	case BIT_U16:
+		if (!u16_key_exists(spec, key[0], NULL, &pos[0])) {
+			ARG_ERR;
+		}
+		if (del_num == 2 &&
+		    !u16_key_exists(spec, key[1], NULL, &pos[1])) {
+			ARG_ERR;
+		}
+		break;
+	case BIT_U32:
+		if (!u32_key_exists(spec, key[0], NULL, &pos[0])) {
+			ARG_ERR;
+		}
+		if (del_num == 2 &&
+		    !u32_key_exists(spec, key[1], NULL, &pos[1])) {
+			ARG_ERR;
+		}
+		break;
+	}
+
+	*result = hp_alloc(sizeof(**result) +
+			   HAS_WILDCARD_SPEC(spec) * sizeof(void *) +
+			   PTR_ALIGN((spec->num - del_num) *
+				 KEYSIZE(spec->bittype)) +
+			   (spec->num - del_num + 1) * sizeof(void *), 1);
+	if (*result == NULL) {
+		return HE_LOW_MEMORY;
+	}
+	memcpy(*result, spec, sizeof(*spec) +
+	       HAS_WILDCARD_SPEC(spec) * sizeof(void *));
+	(*result)->newspec = 0;
+	(*result)->num -= del_num;
+	stat = ptrblock_clone(*termrule(spec), termrule(*result));
+	if (stat < 0) {
+		hp_free(*result);
+		*result = NULL;
+		return stat;
+	}
+
+	first_ksrc = FIRST_KEY(spec);
+	ksrc = first_ksrc;
+	kdst = FIRST_KEY(*result);
+	nsrc = FIRST_NEXTSPEC(spec);
+	ndst = FIRST_NEXTSPEC(*result);
+	for (i = 0; i < del_num; i++) {
+		ksize = (first_ksrc + pos[i] * KEYSIZE(spec->bittype)) - ksrc;
+		nsize = (ksize / KEYSIZE(spec->bittype)) * sizeof(void *);
+		if (ksize > 0) {
+			memcpy(kdst, ksrc, ksize);
+			memcpy(ndst, nsrc, nsize);
+		}
+		ksrc += ksize + KEYSIZE(spec->bittype);
+		kdst += ksize;
+		nsrc += nsize + sizeof(void *);
+		ndst += nsize;
+	}
+	ksize = (spec->num - pos[del_num - 1] - 1) * KEYSIZE(spec->bittype);
+	assert(ksize > 0);
+	nsize = (ksize / KEYSIZE(spec->bittype)) * sizeof(void *);
+	memcpy(kdst, ksrc, ksize);
+	memcpy(ndst, nsrc, nsize);
+	return HE_OK;
+}
+
+hipac_error
+rlp_locate(const struct rlp_spec *spec, struct locate_inf *inf, __u32 key)
+{
+	if (unlikely(spec == NULL || inf == NULL)) {
+		ARG_ERR;
+	}
+
+	switch (spec->bittype) {
+	case BIT_U16:
+		u16_key_exists(spec, key, inf, NULL);
+		break;
+	case BIT_U32:
+		u32_key_exists(spec, key, inf, NULL);
+		break;
+	}
+	return HE_OK;
+}
diff -purN --exclude=.svn 2.6.13/net/ipv4/netfilter/nf-hipac/rlp.h 2.6.13-patched/net/ipv4/netfilter/nf-hipac/rlp.h
--- 2.6.13/net/ipv4/netfilter/nf-hipac/rlp.h	1970-01-01 01:00:00.000000000 +0100
+++ 2.6.13-patched/net/ipv4/netfilter/nf-hipac/rlp.h	2005-10-11 18:49:59.000000000 +0200
@@ -0,0 +1,142 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ * 
+ * Licenced under the GNU General Public Licence, version 2.
+ */
+
+
+#ifndef _RLP_H
+#define _RLP_H
+
+#include "global.h"
+
+
+/* rlp header */
+struct rlp_spec
+{
+	unsigned rlp       :  1; // rlp identifier (must be 1)
+	unsigned bittype   :  1; // {BIT_U16, BIT_U32}
+	unsigned dimid     :  5; // dimension id
+	unsigned newspec   :  1; // indicates whether the rlp is contained
+                              	 // in newspec
+	unsigned num       : 24; // number of elements in the rlp
+
+#ifdef SINGLE_PATH
+	struct gen_spec * (*locate)(const struct rlp_spec *spec,
+				    const void *packet, int *hotdrop);
+#else
+	struct gen_spec * (*locate)(const struct rlp_spec *spec,
+				    const void *packet, int *hotdrop,
+				    struct gen_spec **nodes, __u8 *nodes_len);
+#endif
+};
+
+/* rlp header test */
+#define IS_RLP(r) (((struct gen_spec *) (r))->rlp)
+
+/* test whether rlp has a wildcard pointer */
+#ifdef SINGLE_PATH
+#  define HAS_WILDCARD_SPEC(spec) 0
+#  define HAS_WILDCARD_DIM(dimid) 0
+#else
+#  define HAS_WILDCARD_SPEC(spec) ((spec)->dimid == 1 || (spec)->dimid == 2)
+#  define HAS_WILDCARD_DIM(dimid) ((dimid) == 1 || (dimid) == 2)
+#endif
+
+/* wildcard pointer to the next rlp spec */
+#define WILDCARD(r) ((struct gen_spec **) ((__u8 *) (r) +            \
+					   sizeof(struct rlp_spec)))
+
+/* key and nextspec pointer found by rlp_locate */
+struct locate_inf
+{
+	__u32 key;
+	struct gen_spec **nextspec;
+};
+
+
+/* return address of termrule pointer */
+struct ptrblock **
+termrule(const struct rlp_spec *spec);
+
+/* return new rlp with ins_num (1 or 2) elements inserted; the elements
+   are (key[i], nextspec[i]) where 0 <= i < ins_num; if ins_num == 2 then
+   key[1] > key[0] */
+struct rlp_spec *
+rlp_new(__u8 bittype, __u8 dimid, __u8 ins_num, const __u32 key[],
+	struct gen_spec *nextspec[]);
+
+/* return the size of the rlp */
+__u32
+rlp_size(const struct rlp_spec *spec);
+
+/* return array of spec->num nextspec pointers;
+   NOTE: this abstraction breaks as soon as the RLP solving data structure does
+         not contain a contiguous array of nextspec pointers */
+struct gen_spec **
+rlp_nextspec(const struct rlp_spec *spec);
+
+static inline void
+rlp_free(struct rlp_spec *spec)
+{
+	struct ptrblock *term;
+	
+	if (spec == NULL) {
+		ARG_MSG;
+		return;
+	}
+	term = *termrule(spec);
+	if (term != NULL) {
+		ptrblock_free(term);
+	}
+	hp_free(spec);
+}
+
+static inline int
+rlp_spec_eq(const struct rlp_spec *spec1, const struct rlp_spec *spec2)
+{
+	if (spec1 == NULL || spec2 == NULL || !IS_RLP(spec1) ||
+	    !IS_RLP(spec2)) {
+		ARG_MSG;
+		return 0;
+	}
+	return spec1->bittype == spec2->bittype &&
+		spec1->dimid == spec2->dimid &&
+		spec1->num == spec2->num;
+}
+
+/* clone rlp (not recursively);
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+rlp_clone(const struct rlp_spec *spec, struct rlp_spec **clone);
+
+/* insert (key[i], nextspec[i]) into the rlp where 0 <= i < ins_num <= 2
+   and store the new rlp in result; if ins_num == 2 then key[1] must
+   be > key[0];
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+rlp_insert(const struct rlp_spec *spec, __u8 ins_num, const __u32 key[],
+	   struct gen_spec *nextspec[], struct rlp_spec **result);
+
+/* delete (key[i], nextspec[i]) from the rlp where 0 <= i < del_num <= 2
+   and nextspec[i] is associated with key[i] and store the new rlp in
+   result; if del_num == 2 then key[1] must be > key[0];
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+rlp_delete(const struct rlp_spec *spec, __u8 del_num, const __u32 key[],
+	   struct rlp_spec **result);
+
+/* return (key', nextspec) where key' = min{k : k >= key} and nextspec
+   is associated with key';
+   possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
+hipac_error
+rlp_locate(const struct rlp_spec *spec, struct locate_inf *inf, __u32 key);
+
+#endif
