可编译易用的模块化nf-HiPAC移植成功_hipack rlp

由于早在上周三对这个周末就有了别的安排,要折腾空气压缩机,突然接到通知又要陪老婆去考试,幸亏周四靠晚上的时间完成了nf-HiPAC的移植工作。

       模块化nf-HiPAC的工作只能排在晚上完成,周四晚上折腾到12点终于算是完成了。效果也还不错,最初的tar包放到了下载频道“模块化的nf-HiPAC”,也会有另外的人放在github上,下载频道的主要是为了使用,而github上的主要是为了更新,这主要鉴于nf-hipac在还有很多TODO的情况下就停止了更新,这些TODO在其网页
http://www.hipac.org/status/todo.html是可以查到的。

       主要的TODO列表如下(主要是怕以上这个网页也不在了...):

Planned Features:

    implement better algorithmic core: The new core will dramatically reduce memory usage while at the same time improving the running time of insert and delete operations. The lookup performance will be improved radically too, especially for bigger rulesets. The concepts and the design are already developed, but the implementation is still in its very early stages
    add support for transactions
    add support for ipv6
    add support for MAC matches
    add functionality similar to iptables-restore
    add native support for set of ranges (e.g. ippools). Each native match is not a single range but instead a set of ranges, which makes a single rule more expressive
    consider making HiPAC available for NAT and mangle tables
    consider removing "rule prefix mismatch" limitation
    the next big step in the far future will be to extend the HiPAC algorithm to do classification with several stages. The HiPAC algorithm will then be capable of combining several classification problems in one data structure, e.g. it will be possible to solve routing, firewalling and traffic control with one HiPAC lookup. The idea is to shorten the packet forwarding path by combining e.g. fib_lookup and iptables filter lookup into one HiPAC query

Outstanding Bugfixes:

    fix handling of jump rules containing iptables matches (currently only some known iptables matches are allowed in jump rules)
    in the current version the number of rule updates per second is limited by the number of synchronize_rcu() calls per second. This slows down rule updates dramatically. Future releases will work around this problem and will offer full rule update speed again

Other Stuff:

    man page for userspace tool
    add more documentation
    more in-depth performance tests


模块化后的nf-hipac可以直接编译出一个ko文件以及一个用户态的工具nf-hipac程序。值得注意的是,我在移植过程中,去掉了和iptables模块的联动机制,这有两方面的原因,第一是因为这方面的移植比较复杂,iptables的内核API在内核版本之间变化太大了;另一方面就是还真的不怎么会用到iptables的match/target模块,如果使用了大量的iptables match/target模块,还不如直接就用iptables呢,再者说,iptables模块只是延展了nf-hipac的功能,对其性能是一个打折的过程。

       以下是模块化移植工作遇到的一些问题:

1.iptables模块的问题

需要将match/target操作进行移植,内核API变化太大,砍去。

2.netlink接口问题

这部分比较容易。2.6.13版本的netlink的input回调函数参数是一个socket,创建者需要自己创建内核线程来从该socket的queue中持续获取skb并且维护queue本身,这部分工作在高版本内核中得到了简化,因为input回调函数的参数就是skb本身,队列的维护由netlink系统完成。

3.内存问题

在原始的nf-hipac中,为了节省内存,用了两套内存管理方案,即mini alloc/free以及big alloc/free,在调试这部分的时候遇到了不少问题,加之现在内存没必要那么节省,我就统一采用了big alloc/free,也就是说统一在vmalloc区域进行分配。

4.net namespace问题

说实话,2.6.32内核对net namespace的支持也只是个半吊子,但无论如何接口已经适配了,这就是说我必须按照net namespace的要求来处理proc_net。

5.其它

我只是测试了2.6.32内核和3.9.6内核,对于其它版本的内核并没有测试,这种风格也是拜ipset-6.23所赐,要知道,我也是一个很较真儿的人,有时候也会有点书生气。

...

即便是不下载tgz压缩包,也可以通过打patch的方式构建编译目录。

新建一个nf-hipac空目录并cd到它,然后执行:

patch -p1 < $path/nfhipac-1.0.0.patch

就会将所有的文件和目录置于该目录下,然后make;make install即可。注意,编译的时候需要准备好你的当前内核版本头文件,链接到/lib/modules/`uname -r`/build。 最后给出一个比较长的patch,即nfhipac-1.0.0.patch文件:

diff -urN nf-hipac/INSTALL nfhipac/INSTALL
--- nf-hipac/INSTALL    1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/INSTALL 2014-11-21 13:00:42.000000000 +0800
@@ -0,0 +1,3 @@
+
+
+make install
diff -urN nf-hipac/kernel/dimtree.c nfhipac/kernel/dimtree.c
--- nf-hipac/kernel/dimtree.c   1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/dimtree.c    2014-11-21 12:36:09.000000000 +0800
@@ -0,0 +1,4308 @@
+/*
+ *             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 -urN nf-hipac/kernel/dimtree.h nfhipac/kernel/dimtree.h
--- nf-hipac/kernel/dimtree.h   1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/dimtree.h    2014-11-21 12:36:09.000000000 +0800
@@ -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 -urN nf-hipac/kernel/global.c nfhipac/kernel/global.c
--- nf-hipac/kernel/global.c    1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/global.c 2014-11-21 12:36:09.000000000 +0800
@@ -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 -urN nf-hipac/kernel/global.h nfhipac/kernel/global.h
--- nf-hipac/kernel/global.h    1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/global.h 2014-11-21 12:36:09.000000000 +0800
@@ -0,0 +1,388 @@
+/*
+ *             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
+//#define DEBUG    1
+
+#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("%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 -urN nf-hipac/kernel/hipac.c nfhipac/kernel/hipac.c
--- nf-hipac/kernel/hipac.c 1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/hipac.c  2014-11-21 12:36:09.000000000 +0800
@@ -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(¤t_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, ¬_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, ¬_valid);
+               if (not_valid)
+                   break;
+               j++;
+               new_mct++;
+               continue;
+               
+           }
+           merge_dimension(&p[i], &new[new_mct], inv, 
+                   &inv_match, ¬_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, ¬_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 -urN nf-hipac/kernel/hipac.h nfhipac/kernel/hipac.h
--- nf-hipac/kernel/hipac.h 1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/hipac.h  2014-11-21 12:36:09.000000000 +0800
@@ -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 -urN nf-hipac/kernel/ihash.c nfhipac/kernel/ihash.c
--- nf-hipac/kernel/ihash.c 1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/ihash.c  2014-11-21 12:36:09.000000000 +0800
@@ -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 -urN nf-hipac/kernel/ihash.h nfhipac/kernel/ihash.h
--- nf-hipac/kernel/ihash.h 1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/ihash.h  2014-11-21 12:36:09.000000000 +0800
@@ -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 -urN nf-hipac/kernel/Makefile nfhipac/kernel/Makefile
--- nf-hipac/kernel/Makefile    1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/Makefile 2014-11-21 12:36:09.000000000 +0800
@@ -0,0 +1,24 @@
+#
+# Makefile for nf-hipac on top of IPv4.
+#
+LINUXPATH = /lib/modules/`uname -r`/build
+CURDIR = $(shell pwd)
+
+obj-m += 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
+
+EXTRA_CFLAGS += -D SINGLE_PATH
+
+ifneq ($(ARCH), ia64)
+ifneq ($(ARCH), x86_64)
+EXTRA_CFLAGS += -D BIT32_ARCH
+endif
+endif
+
+all: mod
+
+mod:
+   $(MAKE) -C $(LINUXPATH) M=$(CURDIR) modules
+
+clean:
+   rm -rf *.o *.ko *.mod.c *.symvers *.mod.o .*.cmd .tmp_versions modules.order
diff -urN nf-hipac/kernel/mode.h nfhipac/kernel/mode.h
--- nf-hipac/kernel/mode.h  1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/mode.h   2014-11-21 12:36:09.000000000 +0800
@@ -0,0 +1,236 @@
+/*
+ *             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
+
+/* 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
+ */
+#ifdef __KERNEL__
+#include <asm/page.h>
+#else
+#include <sys/user.h>
+#endif
+
+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/slab.h>
+#include <linux/kernel.h>    // ULONG_MAX
+#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/vmalloc.h>
+#include <linux/list.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
+#define IPT_ALIGN(s) XT_ALIGN(s)
+#endif
+
+#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)
+{
+   return vmalloc(size);
+}
+
+static inline void
+mini_free(void *p)
+{
+   vfree(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);
+}
+
+/* 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 -urN nf-hipac/kernel/nfhp_com.h nfhipac/kernel/nfhp_com.h
--- nf-hipac/kernel/nfhp_com.h  1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/nfhp_com.h   2014-11-21 12:36:09.000000000 +0800
@@ -0,0 +1,294 @@
+/*
+ *             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_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_CHAIN_TARGET(r)   ((r)->action == TARGET_CHAIN)
+
+/*
+ * 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
+
+
+*/
+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 NFHP_RULE(hr)          ((struct nfhp_rule *)              \
+                   ((char *) (hr) - (unsigned long)  \
+                (&((struct nfhp_rule *) 0)->r)))
+
+
+
+/*
+ * 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 */
+#endif
diff -urN nf-hipac/kernel/nfhp_dev.c nfhipac/kernel/nfhp_dev.c
--- nf-hipac/kernel/nfhp_dev.c  1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/nfhp_dev.c   2014-11-21 12:36:09.000000000 +0800
@@ -0,0 +1,313 @@
+/*
+ *             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)));
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED;
+#else
+static spinlock_t dev_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+#endif
+
+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(&init_net, 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(&init_net, 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(&init_net, 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 -urN nf-hipac/kernel/nfhp_dev.h nfhipac/kernel/nfhp_dev.h
--- nf-hipac/kernel/nfhp_dev.h  1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/nfhp_dev.h   2014-11-21 12:36:09.000000000 +0800
@@ -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 -urN nf-hipac/kernel/nfhp_mod.c nfhipac/kernel/nfhp_mod.c
--- nf-hipac/kernel/nfhp_mod.c  1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/nfhp_mod.c   2014-11-21 12:51:25.000000000 +0800
@@ -0,0 +1,1181 @@
+/*
+ *             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/netlink.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <linux/netdevice.h>
+#include "nfhp_mod.h"
+#include "nfhp_com.h"
+#include "nfhp_dev.h"
+#include "nfhp_proc.h"
+#include "hipac.h"
+
+/* 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_INET_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_INET_FORWARD,
+   .priority       = NF_IP_PRI_FILTER - 1,
+};
+struct nf_hook_ops output_op  =
+{
+   .hook           = output_match,
+   .owner          = THIS_MODULE,
+   .pf             = PF_INET,
+   .hooknum        = NF_INET_LOCAL_OUT,
+   .priority       = NF_IP_PRI_FILTER - 1,
+};
+
+/* used to serialize hipac modifications caused by netlink commands and
+   the interface handling module */
+static DEFINE_MUTEX(nfhp_mutex);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+DECLARE_MUTEX(nlhp_lock);
+#else
+DEFINE_SEMAPHORE(nlhp_lock);
+#endif
+
+static struct sock *nfhp_sock = NULL;
+
+/* 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)
+{
+   return 0;
+}
+
+static u32
+get_src_ip(const void *pkt, int *hotdrop)
+{
+   const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+   struct iphdr *iph = ip_hdr(skb);
+   return ntohl(iph->saddr);
+}
+
+static u32
+get_dst_ip(const void *pkt, int *hotdrop)
+{
+   const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+   struct iphdr *iph = ip_hdr(skb);
+   return ntohl(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;
+   struct iphdr *iph = ip_hdr(skb);
+   return iph->protocol;
+}
+
+static u32
+get_fragment(const void *pkt, int *hotdrop)
+{
+   const struct sk_buff *skb = *((struct packet *) pkt)->skbuff;
+   struct iphdr *iph = ip_hdr(skb);
+   int offset = ntohs(iph->frag_off) & IP_OFFSET;
+   if (unlikely(offset)) {
+       if (unlikely(offset == 1 &&
+                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;
+   struct iphdr *iph = ip_hdr(skb);
+   uh = skb_header_pointer(skb, 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;
+   struct iphdr *iph = ip_hdr(skb);
+   uh = skb_header_pointer(skb, 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;
+   struct iphdr *iph = ip_hdr(skb);
+   th = skb_header_pointer(skb, 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;
+   struct iphdr *iph = ip_hdr(skb);
+   ic = skb_header_pointer(skb, 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;
+   struct iphdr *iph = ip_hdr(skb);
+   return iph->ttl;
+}
+
+
+/*
+ * conntrack dependency management
+ */
+
+int
+nfhp_register_cthelp(struct module *cthelp)
+{
+   return 0;
+}
+
+void
+nfhp_unregister_cthelp(struct module *cthelp)
+{
+}
+
+static inline int
+cthelp_use(void)
+{
+   return NFHE_CT;
+}
+
+static inline void
+cthelp_unuse(void)
+{
+}
+
+/*
+ * 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)
+{
+   return MATCH_NO;
+}
+
+/* iptables_target executor */
+static hipac_target_t
+hipac_target_exec(const void *packet, void *target)
+{
+   return TARGET_NONE;
+}
+
+/* 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)
+{
+
+   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;
+   }
+   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)
+{
+   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();
+   }
+}
+
+/* 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();
+   }
+}
+
+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};
+   struct iphdr *iph = ip_hdr(skb);
+
+   /* root is playing with raw sockets. */
+   if (unlikely(skb->len < sizeof(struct iphdr) ||
+            (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;
+       }
+   }
+
+   *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;
+           }
+       }
+   }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+   nlh = NLMSG_PUT(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+           NLHP_TYPE, total - len);
+#else
+   nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+           NLHP_TYPE, total - len, 0);
+#endif
+   nlh->nlmsg_flags |= NLM_F_MULTI;
+   return NLMSG_SPACE(total - len);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+nlmsg_failure:
+        skb_trim(skb, (unsigned int)((unsigned char *)skb->tail - skb->data));
+        return NFHE_ILL;
+#endif
+}
+
+static int
+nlhp_done(struct netlink_callback *cb)
+{
+   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;
+   }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+   r_nlh = NLMSG_PUT(r_skb, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 
+             NLMSG_ERROR, sizeof(int32_t));
+#else
+   r_nlh = nlmsg_put(r_skb, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
+                           NLMSG_ERROR, sizeof(int32_t), 0);
+#endif
+   *(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 LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+   if (netlink_unicast(nfhp_sock, r_skb, NETLINK_CB(skb).pid,
+               MSG_DONTWAIT) <= 0) {
+#else
+   if (netlink_unicast(nfhp_sock, r_skb, NETLINK_CB(skb).portid,
+               MSG_DONTWAIT) <= 0) {
+#endif
+       printk(KERN_ERR "netlink_unicast failed\n");
+       return;
+   }
+   return;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+nlmsg_failure:
+   printk(KERN_ERR "NLMSG_PUT failed\n");
+   kfree(r_skb);
+#endif
+}
+
+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 LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+   if (security_netlink_recv(skb, CAP_SYS_ADMIN)) {
+#else
+   if (0) {
+#endif
+       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 sk_buff *skb)
+{
+   mutex_lock(&nfhp_mutex);
+   nlhp_chk_user_skb(skb);
+   mutex_unlock(&nfhp_mutex);
+}
+
+/*
+ * 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;
+   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 inline u32
+origin_to_hookmask(u32 origin)
+{
+   return (origin & NFHP_ORIGIN_INPUT ? 1 << NF_INET_LOCAL_IN : 0) |
+          (origin & NFHP_ORIGIN_FORWARD ? 1 << NF_INET_FORWARD : 0) |
+          (origin & NFHP_ORIGIN_OUTPUT ? 1 << NF_INET_LOCAL_OUT : 0);
+}
+
+static void
+cmd_cleanup(struct hipac_rule *r, int inc_cthelp, int num_matches, int target)
+{
+   if (inc_cthelp) {
+       cthelp_unuse();
+   }
+}
+
+static int
+cmd_check_init(struct nfhp_cmd *cmd, int msg_len)
+{
+   int inc_cthelp = 0, num_matches = 0, target = 0 ;
+   int stat, 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;
+   }
+   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;
+   }
+
+   /* 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;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
+   struct netlink_dump_control control = {
+       .dump = nlhp_list,
+       .done = nlhp_done,
+       .module = THIS_MODULE,
+   };
+#endif
+
+   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 LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+           if ((stat = netlink_dump_start(nfhp_sock, skb, nlh, nlhp_list,
+                      nlhp_done)) != 0) {
+#else
+           if ((stat = netlink_dump_start(nfhp_sock, skb, nlh, &control)) != 0) {
+
+#endif
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,32)
+               if (stat != -EINTR)
+#endif
+               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;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
+   struct netlink_kernel_cfg cfg = {
+       .groups = 0,
+       .flags = 0,
+       .cb_mutex = NULL,
+       .input = nlhp_data_ready,
+   };
+#endif
+
+   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 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+   nfhp_sock = netlink_kernel_create(&init_net, NLHP_PROTO, 0, nlhp_data_ready, NULL, THIS_MODULE);
+#else
+   nfhp_sock = netlink_kernel_create(&init_net, NLHP_PROTO, &cfg);
+#endif
+   if (nfhp_sock == NULL) {
+       printk(KERN_ERR "nf_hipac: initialization failed: unable to "
+              "create kernel netlink socket\n");
+       ret = -ENOMEM;
+       goto cleanup_hpproc;
+   }
+
+   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_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);
+   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 -urN nf-hipac/kernel/nfhp_mod.h nfhipac/kernel/nfhp_mod.h
--- nf-hipac/kernel/nfhp_mod.h  1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/nfhp_mod.h   2014-11-21 12:36:09.000000000 +0800
@@ -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 <linux/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 -urN nf-hipac/kernel/nfhp_proc.c nfhipac/kernel/nfhp_proc.c
--- nf-hipac/kernel/nfhp_proc.c 1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/nfhp_proc.c  2014-11-21 12:36:09.000000000 +0800
@@ -0,0 +1,884 @@
+/*
+ *             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/netfilter_ipv4.h>
+#include <linux/proc_fs.h>
+#include <net/net_namespace.h>
+#include <linux/version.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;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
+   data->lock = RW_LOCK_UNLOCKED;
+#else
+   rwlock_init(&data->lock);
+#endif
+   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, init_net.proc_net);
+   if (nfhipac_dir == NULL) {
+       printk(KERN_NOTICE "nf_hipac: unable to create proc "
+              "directory\n");
+       return;
+   }
+   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;
+   }
+
+   /* 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->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, init_net.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, init_net.proc_net);
+}
diff -urN nf-hipac/kernel/nfhp_proc.h nfhipac/kernel/nfhp_proc.h
--- nf-hipac/kernel/nfhp_proc.h 1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/nfhp_proc.h  2014-11-21 12:36:09.000000000 +0800
@@ -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 -urN nf-hipac/kernel/rlp.c nfhipac/kernel/rlp.c
--- nf-hipac/kernel/rlp.c   1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/rlp.c    2014-11-21 12:36:09.000000000 +0800
@@ -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 -urN nf-hipac/kernel/rlp.h nfhipac/kernel/rlp.h
--- nf-hipac/kernel/rlp.h   1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/kernel/rlp.h    2014-11-21 12:36:09.000000000 +0800
@@ -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
diff -urN nf-hipac/Makefile nfhipac/Makefile
--- nf-hipac/Makefile   1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/Makefile    2014-11-21 11:38:54.000000000 +0800
@@ -0,0 +1,15 @@
+
+
+all:
+   $(MAKE) -C user
+   $(MAKE) -C kernel
+
+clean:
+   $(MAKE) -C user clean
+   $(MAKE) -C kernel clean
+
+install: all
+   $(MAKE) -C user install
+   @cp kernel/nf_hipac.ko /lib/modules/`uname -r`/kernel/net/ipv4/netfilter/
+   @depmod -a
+
diff -urN nf-hipac/README nfhipac/README
--- nf-hipac/README 1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/README  2014-11-21 12:56:27.000000000 +0800
@@ -0,0 +1,5 @@
+
+此软件包来自http://www.hipac.org/,本人仅仅做了移植工作(API兼容性),特向原作者表示感谢,且表达敬意。
+
+由于相关作者在2005年停止了更新,影响了nf-hipac的使用和推广。鉴于此算法极具价值以及其可用性(简单,易用),实则有模块化的必要。
+目前该代码在内核2.6.32和3.9.6上进行过测试。2.6.13不再支持。需要在低版本内核上支持hipac的,请参照http://www.hipac.org/上的说明。
diff -urN nf-hipac/user/doit.sh nfhipac/user/doit.sh
--- nf-hipac/user/doit.sh   1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/user/doit.sh    2014-11-21 11:09:14.000000000 +0800
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./nf-hipac -A INPUT -s 1.1.1.1 -j DROP
diff -urN nf-hipac/user/hipac.h nfhipac/user/hipac.h
--- nf-hipac/user/hipac.h   1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/user/hipac.h    2014-11-21 11:09:14.000000000 +0800
@@ -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 -urN nf-hipac/user/libnfhipac.c nfhipac/user/libnfhipac.c
--- nf-hipac/user/libnfhipac.c  1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/user/libnfhipac.c   2014-11-21 11:09:14.000000000 +0800
@@ -0,0 +1,399 @@
+/*
+ *             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 <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include "libnfhipac.h"
+#include "hipac.h"
+#include "nfhp_com.h"
+
+#ifndef PROC_SYS_MODPROBE
+#  define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define MIN(a, b)          ((a) <= (b) ? (a) : (b))
+#define IS_LIST_CMD(cmd)   ((cmd) == CMD_LIST)
+#define CMD_SIZE     4096
+#define RECV_SIZE   65536
+
+struct nfhp_cmd *
+nlhp_new_cmd(int cmd, int *size)
+{
+   struct nlmsghdr *msg;
+   struct nfhp_cmd *c;
+
+   if (cmd > CMD_MAX) {
+       return NULL;
+   }
+   
+   msg = calloc(1, CMD_SIZE);
+   if (msg == NULL) {
+       return NULL;
+   }
+   if (size != NULL){
+       *size = CMD_SIZE - NLMSG_LENGTH(0);
+   }
+   c = NLMSG_DATA(msg);
+   c->cmd = cmd;
+   return c;
+}
+
+void
+nlhp_free_cmd(struct nfhp_cmd *cmd)
+{
+   if (cmd == NULL) {
+       return;
+   }
+   free((char *) cmd - NLMSG_LENGTH(0));
+}
+
+struct nfhp_cmd *
+nlhp_copy_cmd(struct nfhp_cmd *cmd, int size)
+{
+   struct nlmsghdr *clone;
+
+   if (cmd == NULL || size < sizeof(*cmd)) {
+       return NULL;
+   }
+   clone = malloc(NLMSG_SPACE(size));
+   if (clone == NULL) {
+       return NULL;
+   }
+   memcpy(clone, (char *) cmd - NLMSG_LENGTH(0), NLMSG_SPACE(size));
+   return NLMSG_DATA(clone);
+}
+
+/* read location of modprobe binary from procfs */
+static char *
+get_modprobe_cmd(void)
+{
+   int procfile, len;
+   char *ret;
+       
+   procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+   if (procfile < 0) {
+       return NULL;
+   }
+   
+   ret = malloc(1024);
+   if (ret == NULL) {
+       goto error;
+   }
+   switch (read(procfile, ret, 1024)) {
+       case -1:   goto error;
+       case 1024: goto error;
+   }
+   len = strlen(ret);
+   if (ret[len - 1] == '\n') {
+       ret[len - 1] = '\0';
+   }
+   close(procfile);
+   return ret;
+ 
+ error:
+   free(ret);
+   close(procfile);
+   return NULL;
+}
+
+/* try to load module specified by modname */
+static int
+modprobe(char *modname)
+{
+   char *modprobe_cmd = NULL;
+   char *argv[3];
+   
+   modprobe_cmd = get_modprobe_cmd();
+   if (modprobe_cmd == NULL) {
+       return -1;
+   }
+   switch (fork()) {
+       case 0:
+           argv[0] = modprobe_cmd;
+           argv[1] = modname;
+           argv[2] = NULL;
+           execv(argv[0], argv);
+           /* usually not reached */
+           exit(0);
+
+       case -1:
+           return -1;
+           
+       default: /* parent */
+           wait(NULL);
+   }
+   free(modprobe_cmd);
+   return 0;
+}
+
+static inline int
+rcv_kern(int sock, void *buf, int len, struct sockaddr_nl *addr,
+    socklen_t *addrlen)
+{
+   fd_set set;
+   int stat;
+   
+   FD_SET(sock, &set);
+   stat = select(sock + 1, &set, NULL, NULL, NULL);
+   FD_ZERO(&set);
+   if (stat == 0) {
+       return -2;
+   } else if (stat == -1) {
+       return -1;
+   }
+   return recvfrom(sock, buf, len, 0, (struct sockaddr *) addr, addrlen);
+}
+
+static inline int
+check_reply(struct nlmsghdr *msg, int size, int min_size)
+{
+   int pl;
+
+   if (size < 0) {
+       return size;
+   }
+   if (size <= NLMSG_LENGTH(0)) {
+       fprintf(stderr, "internal error: reply has empty payload\n");
+       return -5;
+   }
+   pl = NLMSG_PAYLOAD(msg, 0);
+   if (pl < min_size) {
+       fprintf(stderr, "internal error: payload too short\n");
+       return -5;
+   }
+   if (!NLMSG_OK(msg, size)) {
+       fprintf(stderr, "internal error: reply broken\n");
+       return -5;
+   }
+   return pl;
+}
+
+int
+nlhp_send_cmd(struct nfhp_cmd *cmd, int cmd_size,
+         int (*process_list) (const void *data, int len),
+         int *err)
+{
+   int rcvbuf_size;
+   struct sockaddr_nl addr = {0};
+   socklen_t addrlen = sizeof(addr);
+   struct nlmsghdr *msg, *reply;
+   int sock, stat, pl;
+   void *data;
+
+   if (cmd == NULL || err == NULL || cmd_size < sizeof(*cmd) ||
+       (IS_LIST_CMD(cmd->cmd) && process_list == NULL)) {
+       return -5;
+   }
+
+   stat = 0;
+   *err = 0;
+
+   /* open socket */
+   if ((sock = socket(PF_NETLINK, SOCK_RAW, NLHP_PROTO)) == -1) {
+       return -1;
+   }
+   addr.nl_family = AF_NETLINK;
+   if (bind(sock, (struct sockaddr *) &addr, addrlen) == -1) {
+       stat = -1;
+       goto end;
+   }
+   
+   /* fill netlink header */
+   msg = (struct nlmsghdr *) ((char *) cmd - NLMSG_LENGTH(0));
+   msg->nlmsg_len   = NLMSG_LENGTH(cmd_size);
+   msg->nlmsg_type  = NLHP_TYPE;
+   msg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+   msg->nlmsg_pid   = getpid();
+   if (!NLMSG_OK(msg, NLMSG_LENGTH(cmd_size))) {
+       fprintf(stderr, "internal error: message built failed\n");
+       stat = -5;
+       goto end;
+   }
+
+   /* send request */
+   if (sendto(sock, msg, NLMSG_SPACE(cmd_size), 0,
+          (struct sockaddr *) &addr, addrlen) == -1) {
+       modprobe("nf_hipac");
+       if (sendto(sock, msg, NLMSG_SPACE(cmd_size),
+              0, (struct sockaddr *) &addr, addrlen) == -1) {
+           stat = -1;
+           goto end;
+       }
+   }
+
+   if (!IS_LIST_CMD(cmd->cmd))
+       rcvbuf_size = NLMSG_SPACE(sizeof(int32_t));
+   else rcvbuf_size = RECV_SIZE;
+   
+   /* receive reply */
+           reply = malloc(rcvbuf_size);
+   if (reply == NULL) {
+       stat = -3;
+       goto end;
+   }
+
+   /* non-list command */
+   if (!IS_LIST_CMD(cmd->cmd)) {
+       int size;
+       size = rcv_kern(sock, reply, rcvbuf_size, &addr, &addrlen);
+       pl = check_reply(reply, size, sizeof(int32_t));
+       if (pl < 0) {
+           stat = pl;
+           goto end_free;
+       }
+       if (reply->nlmsg_type == NLMSG_ERROR) {
+           *err = *(int32_t *) NLMSG_DATA(reply);
+           goto end_free;
+       } else {
+           fprintf(stderr, "internal error: netlink error\n");
+           stat = -5;
+           goto end_free;
+       }
+   } else {
+       /* list callback */
+       while (1) {
+           int size;
+           size = rcv_kern(sock, reply, rcvbuf_size, 
+                   &addr, &addrlen);
+           data = NLMSG_DATA(reply);
+           pl = check_reply(reply, size, sizeof(int32_t));
+           if (pl < 0) {
+               stat = pl;
+               break;
+           }
+           if (pl == sizeof(int32_t) &&
+               (reply->nlmsg_type == NLMSG_DONE ||
+                reply->nlmsg_type == NLMSG_ERROR)) {
+               if (*(int32_t *) data < 0) {
+                   /* listing ends abnormally */
+                   *err = *(int32_t *) data;
+               } else {
+                   *err = 0;
+               }
+               break;
+           }
+           if (pl < MIN(sizeof(struct nfhp_list_chain),
+                    sizeof(struct nfhp_list_rule))) {
+               fprintf(stderr, "internal error: "
+                   "payload too short\n");
+               stat = -5;
+               break;
+           }
+           if (process_list(data, pl) < 0) {
+               stat = -4;
+               break;
+           }
+           if (reply->nlmsg_type == NLMSG_DONE) {
+               *err = 0;
+               break;
+           }
+       }
+   }
+
+ end_free:
+   free(reply);
+ end:
+   close(sock);
+   return stat;
+}
+
+const char *
+nlhp_error(int err)
+{
+   static const char *errmsg[] =
+   {
+       /* hipac_error */
+       [-HE_OK]                        = "Operation successful",
+       [-HE_IMPOSSIBLE_CONDITION]      = "Impossible condition (e.g. illegal function call)",
+       [-HE_LOW_MEMORY]                = "Not enough memory available",
+       [-HE_CHAIN_EXISTS]              = "Chain exists already",
+       [-HE_CHAIN_NOT_EXISTENT]        = "Chain does not exist",
+       [-HE_CHAIN_IS_EMPTY]            = "Chain is empty",
+       [-HE_CHAIN_NOT_EMPTY]           = "Chain is not empty",
+       [-HE_CHAIN_IS_USERDEFINED]      = "Chain is user defined",
+       [-HE_CHAIN_IS_CONNECTED]        = "Chain is connected",
+       [-HE_CHAIN_IS_REFERENCED]       = "Chain is still referenced",
+       [-HE_CHAIN_NOT_NATIVE]          = "Chain is not native",
+       [-HE_CHAIN_IS_NATIVE]           = "Chain is native",
+       [-HE_RULE_NOT_EXISTENT]         = "Rule does not exist",
+       [-HE_RULE_ORIGIN_MISMATCH]      = "Rule origin mismatch",
+       [-HE_RULE_PREFIX_MISMATCH]      = "Rule prefix mismatch",
+       [-HE_LOOP_DETECTED]             = "Loop detected",
+       [-HE_REC_LIMIT]                 = "Chain depth limit reached",
+       [-HE_TARGET_CHAIN_NOT_EXISTENT] = "Target chain does not exist",
+       [-HE_TARGET_CHAIN_IS_NATIVE]    = "Target chain is native",
+       [-HE_NATIVE_CHAIN_EXISTS]       = "Native chain exists",
+       
+       /* nfhipac_error */
+       [-NFHE_INDEX]   = "Unable to retrieve unused ifindex",
+       [-NFHE_NOMSG]   = "Incorrect message format",
+       [-NFHE_CMD]     = "Invalid command",
+       [-NFHE_LABEL]   = "Empty chain label",
+       [-NFHE_NLABEL]  = "Empty new chain label",
+       [-NFHE_POLICY]  = "Invalid policy",
+       [-NFHE_ACTION]  = "Invalid action",
+       [-NFHE_NMCT]    = "Invalid native match count",
+       [-NFHE_IEOFF]   = "Invalid target_offset/next_offset in ipt_entry",
+       [-NFHE_SORT]    = "Native matches not sorted or dimid duplicate",
+       [-NFHE_MINT]    = "Invalid interval in native match",
+       [-NFHE_DEVA]    = "Native interface match but no corresponding interface name",
+       [-NFHE_DEVB]    = "Interface name but no corresponding native interface match",
+       [-NFHE_FRAG]    = "Invalid fragment match",
+       [-NFHE_PROTO]   = "Invalid protocol match",
+       [-NFHE_SYN]     = "Invalid syn match",
+       [-NFHE_STATE]   = "Invalid state match",
+       [-NFHE_TCP]     = "tcp match dependency failure",
+       [-NFHE_TCPUDP]  = "tcp or udp match dependency failure",
+       [-NFHE_ICMP]    = "icmp match dependency failure",
+       [-NFHE_CMPMIS]  = "Missing cmp_len array",
+       [-NFHE_CMPSH]   = "cmp_len array too short",
+       [-NFHE_CMPLA]   = "cmp_len array contains a value larger than the corresponding ipt match/target size",
+       [-NFHE_ORIGIN]  = "Illegal combination of matches (no valid origin)",
+       [-NFHE_IPTMSZ]  = "Invalid ipt match size",
+       [-NFHE_IPTMCH]  = "checkentry fails for ipt match",
+       [-NFHE_IPTTMI]  = "Missing ipt target",
+       [-NFHE_IPTTSZ]  = "Invalid ipt target size",
+       [-NFHE_IPTTCH]  = "checkentry fails for ipt target",
+       [-NFHE_TOFF]    = "Invalid target_offset",
+       [-NFHE_CHAINE]  = "Empty chain name",
+       [-NFHE_CHAINL]  = "Chain name too long",
+       [-NFHE_CT]      = "Kernel does not have support for connection tracking, please recompile",
+       [-NFHE_CTHELP]  = "Unable to load connection tracking helper module (nf_hipac_cthelp.o)",
+       [-NFHE_ILL]     = "Illegal condition",
+       [-NFHE_IMPL]    = "Feature not yet implemented"
+   };
+   static char buf[30];
+   int32_t n;
+
+   if (err <= NFHE_SYSOFF) {
+       return strerror(NFHE_TO_ERRNO(err));
+   }
+
+   n = -err;
+   if (n < 0 || n >= sizeof(errmsg) / sizeof(*errmsg) ||
+       errmsg[n] == NULL) {
+       snprintf(buf, sizeof(buf), "Unkown error %d\n", err);
+       return buf;
+   }
+   return errmsg[n];
+}
diff -urN nf-hipac/user/libnfhipac.h nfhipac/user/libnfhipac.h
--- nf-hipac/user/libnfhipac.h  1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/user/libnfhipac.h   2014-11-21 11:09:14.000000000 +0800
@@ -0,0 +1,90 @@
+/*
+ *             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 _LIBNFHIPAC_H
+#define _LIBNFHIPAC_H
+
+#include <features.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#  include <net/if.h>
+#  include <netinet/in.h>
+#  include <asm/types.h>
+#else /* libc5 */
+#  include <linux/if.h>
+#  include <linux/in.h>
+#  include <linux/types.h>
+#endif
+#include "nfhp_com.h"
+
+/*
+  return empty command struct s where only the command type is initialized by
+  cmd; the size of s which is at least (127 KB) - (NLMSG_LENGTH(0) bytes) is
+  stored in *size if size is not NULL; on error NULL is returned;
+  IMPORTANT: __never__ call free, realloc or similar functions for s;
+             use nlhp_free_cmd instead to free the memory allocated for s
+             by nlhp_new_cmd;
+             realloc is never needed because the size of s is chosen to
+             be large enough (don't forget that the size of skb->data is
+        limited to 128 KB)
+*/
+struct nfhp_cmd *
+nlhp_new_cmd(int cmd, int *size);
+
+/* free cmd which must have been allocated by nlhp_new_cmd */
+void
+nlhp_free_cmd(struct nfhp_cmd *cmd);
+
+/* returns a copy of cmd; the size of cmd is passed via size */
+struct nfhp_cmd *
+nlhp_copy_cmd(struct nfhp_cmd *cmd, int size);
+
+/*
+  send command request to nf_hipac kernel module (necessary parts in cmd
+  must be filled in appropriately since cmd is not checked or modified by
+  the library); the size of cmd is passed via cmd_size; if cmd->cmd is
+  CMD_LIST then process_list must point to a callback function which
+  is called for each packet received during a list request;
+  otherwise process_list may be NULL; when called for the first time
+  process_list can assume that data points to a struct nfhp_list_chain;
+  process_list should return 0 on success and a value < 0 on error;
+  each packet may contain several struct nfhp_list_chain and struct
+  nfhp_list_rule arranged in the way described in nfhp_com.h;
+  
+  if nlhp_send_cmd returns 0 then see nlhp_error(*err) for an error
+  description; *err == 0 indicates a successful operation;
+    
+  return values:  0: see *err
+                 -1: system error (see perror or errno)
+        -2: read timeout
+        -3: not enough memory available
+        -4: process_list failed
+        -5: other error
+*/
+int
+nlhp_send_cmd(struct nfhp_cmd *cmd, int cmd_size,
+         int (*process_list) (const void *data, int len), int *err);
+
+/*
+ * return string description of err if available; otherwise NULL is returned;
+ */
+const char *
+nlhp_error(int err);
+
+#endif
diff -urN nf-hipac/user/Makefile nfhipac/user/Makefile
--- nf-hipac/user/Makefile  1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/user/Makefile   2014-11-21 12:46:19.000000000 +0800
@@ -0,0 +1,75 @@
+#
+# use make [target] [options]
+#
+# where options
+#       - PREFIX:      prefix for install
+#  - IPT_LIB_DIR:      location of iptables userspace modules
+#       - IPTABLES_1_2:        compile for iptables 1.2.x  [true|false]
+#
+
+PREFIX     := /usr/local
+IPT_LIB_DIR    := /lib/iptables
+IPTABLES_1_2   := false
+
+DEBUG      := false
+
+LIBDIR     := $(PREFIX)/lib
+BINDIR     := $(PREFIX)/sbin
+
+SRC        := nf-hipac.c nf-hipac-core.c libnfhipac.c
+LIB        := libnfhipac.so
+BIN        := nf-hipac
+
+CC     := gcc
+INSTALL        := install
+LDCONFIG   := ldconfig
+
+CFLAGS     := -Wall -Wno-long-long -Wstrict-prototypes -Wno-trigraphs   \
+          -Wunused -fno-strict-aliasing -fno-common -rdynamic \
+          -D IPT_LIB_DIR=\"$(IPT_LIB_DIR)\"
+
+ifeq ($(DEBUG), true)
+CFLAGS += -g
+BINFLAGS := $(CFLAGS) -Wl,-rpath,$(shell pwd)
+else
+CFLAGS += -O2 -fomit-frame-pointer
+BINFLAGS := $(CFLAGS)
+endif
+
+OFLAGS     := $(CFLAGS) -fPIC
+SFLAGS     := $(CFLAGS) -shared -nostartfiles
+
+
+.PHONY:    install clean
+
+all:       $(BIN)
+
+install:   $(BIN)
+       mkdir -p "$(BINDIR)"
+       $(INSTALL) -o root -g root -m 755 $(BIN) $(BINDIR)
+       mkdir -p "$(LIBDIR)"
+       $(INSTALL) -o root -g root -m 755 $(LIB) $(LIBDIR)
+       $(LDCONFIG)
+
+nf-hipac:  %:%.o -ldl $(LIB) nf-hipac-core.o
+       $(CC) $(BINFLAGS) $^ -o $@
+
+clean:
+       -@ rm -f $(SRC:.c=.d) $(SRC:.c=.o) $(BIN) $(LIB)
+
+%.so:           %.o
+       $(CC) $(SFLAGS) $(filter %.o %.so, $^) -o $@
+
+%.o:            %.c
+       $(CC) $(OFLAGS) -c $< -o $@
+
+%.d:       %.c
+       @ set -e; $(CC) -MM $(CFLAGS) $<              \
+         | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \
+         [ -s $@ ] || rm -f $@
+
+ifneq ($(MAKECMDGOALS), clean)
+ifneq ($(MAKECMDGOALS), etags)
+-include $(SRC:.c=.d)
+endif
+endif
diff -urN nf-hipac/user/mode.h nfhipac/user/mode.h
--- nf-hipac/user/mode.h    1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/user/mode.h 2014-11-21 11:17:40.000000000 +0800
@@ -0,0 +1,231 @@
+/*
+ *             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
+
+/* 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
+ */
+#ifdef __KERNEL__
+#include <asm/page.h>
+#else
+#include <sys/user.h>
+#endif
+
+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/slab.h>
+#include <linux/kernel.h>    // ULONG_MAX
+#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/vmalloc.h>
+#include <linux/list.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)
+{
+   return vmalloc(size);
+}
+
+static inline void
+mini_free(void *p)
+{
+   vfree(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);
+}
+
+/* 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 -urN nf-hipac/user/nf-hipac.c nfhipac/user/nf-hipac.c
--- nf-hipac/user/nf-hipac.c    1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/user/nf-hipac.c 2014-11-21 12:48:12.000000000 +0800
@@ -0,0 +1,55 @@
+/*
+ *             High performance packet classification 
+ *                     <http://www.hipac.org>
+ *
+ * (c) 2004-2005   MARA Systems AB <http://www.marasystems.com>
+ *                 +-----------------------------+
+ *                 |       Michael Bellion       |
+ *                 |  <michael@marasystems.com>  |
+ *                 +-----------------------------+
+ *
+ * heavily based on iptables code from the netfilter project
+ * which is (C) by the netfilter coreteam <coreteam@netfilter.org>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+extern char *lib_dir;
+
+extern int
+do_command(int argc, char *argv[]);
+
+int
+main(int argc, char **argv)
+{
+   lib_dir = getenv(/*"IPTABLES_LIB_DIR"*/"XXX");
+   if (!lib_dir)
+       lib_dir = IPT_LIB_DIR;
+
+#ifdef NO_SHARED_LIBS
+        init_extensions();
+#endif
+   return do_command(argc, argv);
+}
diff -urN nf-hipac/user/nf-hipac-core.c nfhipac/user/nf-hipac-core.c
--- nf-hipac/user/nf-hipac-core.c   1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/user/nf-hipac-core.c    2014-11-21 12:47:32.000000000 +0800
@@ -0,0 +1,3281 @@
+/*
+ *             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>    |
+ *     +---------------------------+--------------------------+
+ *
+ * heavily based on iptables code from the netfilter project
+ * which is (C) by the netfilter coreteam <coreteam@netfilter.org>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <stdio.h>   
+#include <getopt.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <link.h>
+#include <dlfcn.h>
+#include <sys/utsname.h>
+
+
+#include <features.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#  include <net/if.h>
+#  include <netinet/in.h>
+#  include <asm/types.h>
+#else /* libc5 */
+#  include <linux/if.h>
+#  include <linux/in.h>
+#  include <linux/types.h>
+#endif
+
+#include "nfhp_com.h"
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#include "libnfhipac.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+static int listing_start = 0;
+
+#define OPT_NONE   0x00000U
+#define OPT_NUMERIC    0x00001U
+#define OPT_VERBOSE    0x00002U
+#define OPT_LINENUMBERS 0x00004U
+#define OPT_DEBUG       0x00008U
+#define OPT_DEBUGN      0x00010U
+#define OPT_JUMP        0x00020U
+
+#define NUMBER_OF_OPT  6
+
+static const char optflags[NUMBER_OF_OPT + NUMBER_OF_DIM][18]
+= {
+   [0] = "n",
+   [1] = "v",
+   [2] = "-line",
+   [3] = "-debug",
+   [4] = "-debugn",
+   [5] = "j",
+   [NUMBER_OF_OPT + DIMID_STATE] = "-state",
+   [NUMBER_OF_OPT + DIMID_SRC_IP ] = "s",
+   [NUMBER_OF_OPT + DIMID_DEST_IP ] = "d",
+   [NUMBER_OF_OPT + DIMID_INIFACE] = "i",
+   [NUMBER_OF_OPT + DIMID_OUTIFACE] = "o",
+   [NUMBER_OF_OPT + DIMID_PROTO] = "p",
+   [NUMBER_OF_OPT + DIMID_FRAGMENT] = "f",
+   [NUMBER_OF_OPT + DIMID_DPORT] = "-dport",
+   [NUMBER_OF_OPT + DIMID_SPORT] = "-sport",
+   [NUMBER_OF_OPT + DIMID_SYN] = "-syn or --not-syn",
+   [NUMBER_OF_OPT + DIMID_ICMP_TYPE] = "-icmp-type",
+   [NUMBER_OF_OPT + DIMID_TTL] = "-ttl",
+};
+
+
+
+#define HIPAC_NATIVE_MATCHES 5
+static const char native_matches[HIPAC_NATIVE_MATCHES][6]
+= { "tcp", "udp", "icmp", "state", "ttl"};
+
+
+static const char cmdflags[] = { ' ', 'A', 'I', 'D', 'D', 'R', 'F',
+                'N', 'X', 'E', 'P', 'L'};
+
+
+
+static char commands_v_options[CMD_MAX + 1][2] = {
+   //type 0: -n, --line, --exact
+   //type 1: -s, -p, -sport, -dport, ...
+        //      0   1   2  
+
+/*NONE*/         {'x','x'},
+/*APPEND*/       {'x',' '},
+/*INSERT*/       {'x',' '},
+/*DELETE_RULE*/  {'x',' '},
+/*DELETE_POS*/   {'x','x'},
+/*REPLACE*/      {'x',' '},
+/*FLUSH*/        {'x','x'},
+/*NEW_CHAIN*/    {'x','x'},
+/*DELETE_CHAIN*/ {'x','x'},
+/*RENAME_CHAIN*/ {'x','x'},
+/*SET_POLICY*/   {'x','x'},
+/*LIST*/         {' ','x'},
+};
+
+
+static struct option original_opts[] = {
+   { "append", 1, 0, 'A' },
+   { "delete", 1, 0,  'D' },
+   { "insert", 1, 0,  'I' },
+   { "replace", 1, 0,  'R' },
+   { "list", 2, 0,  'L' },
+   { "flush", 2, 0,  'F' },
+   { "new-chain", 1, 0,  'N' },
+   { "delete-chain", 2, 0,  'X' },
+   { "rename-chain", 1, 0,  'E' },
+   { "policy", 1, 0,  'P' },
+   { "source", 1, 0, 's' },
+   { "destination", 1, 0,  'd' },
+   { "src", 1, 0,  's' }, /* synonym */
+   { "dst", 1, 0,  'd' }, /* synonym */
+   { "protocol", 1, 0,  'p' },
+   { "in-interface", 1, 0, 'i' },
+   { "jump", 1, 0, 'j' },
+   { "table", 1, 0, 't' },
+   { "match", 1, 0, 'm' },
+   { "numeric", 0, 0, 'n' },
+   { "out-interface", 1, 0, 'o' },
+   { "verbose", 0, 0, 'v' },
+   { "fragments", 0, 0, 'f' },
+   { "version", 0, 0, 'V' },
+   { "help", 2, 0, 'h' },
+   { "debug", 0, 0, 9 },
+   { "debugn", 0, 0, 10 },
+   { "line-numbers", 0, 0, 11 },
+   { "state", 1, 0, 12 },
+   { "ttl", 1, 0, 14 },
+   { "icmp-type", 1, 0, 15 },  
+   { "source-port", 1, 0, 16 },
+   { "sport", 1, 0, 16 }, /* synonym */
+   { "destination-port", 1, 0, 17 },
+   { "dport", 1, 0, 17 }, /* synonym */
+   { "syn", 0, 0, 18 },
+   { "not-syn", 0, 0, 19 },
+   { 0 }
+};
+
+/* we need this for iptables-restore.  iptables-restore.c sets line to the
+ * current line of the input file, in order  to give a more precise error
+ * message.  iptables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+static unsigned int options = 0;
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+   char *name;
+   u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+static const struct pprot chain_protos[] = {
+   { "tcp", IPPROTO_TCP },
+   { "udp", IPPROTO_UDP },
+   { "icmp", IPPROTO_ICMP },
+   { "esp", IPPROTO_ESP },
+   { "ah", IPPROTO_AH },
+   { "sctp", IPPROTO_SCTP },
+   { "all", 0 },
+};
+
+struct icmp_names {
+   const char *name;
+   u_int8_t type;
+   u_int8_t code_min, code_max;
+};
+
+static const struct icmp_names icmp_codes[] = {
+   { "echo-reply", 0, 0, 0xFF },
+   /* Alias */ { "pong", 0, 0, 0xFF },
+
+   { "destination-unreachable", 3, 0, 0xFF },
+   {   "network-unreachable", 3, 0, 0 },
+   {   "host-unreachable", 3, 1, 1 },
+   {   "protocol-unreachable", 3, 2, 2 },
+   {   "port-unreachable", 3, 3, 3 },
+   {   "fragmentation-needed", 3, 4, 4 },
+   {   "source-route-failed", 3, 5, 5 },
+   {   "network-unknown", 3, 6, 6 },
+   {   "host-unknown", 3, 7, 7 },
+   {   "network-prohibited", 3, 9, 9 },
+   {   "host-prohibited", 3, 10, 10 },
+   {   "TOS-network-unreachable", 3, 11, 11 },
+   {   "TOS-host-unreachable", 3, 12, 12 },
+   {   "communication-prohibited", 3, 13, 13 },
+   {   "host-precedence-violation", 3, 14, 14 },
+   {   "precedence-cutoff", 3, 15, 15 },
+
+   { "source-quench", 4, 0, 0xFF },
+
+   { "redirect", 5, 0, 0xFF },
+   {   "network-redirect", 5, 0, 0 },
+   {   "host-redirect", 5, 1, 1 },
+   {   "TOS-network-redirect", 5, 2, 2 },
+   {   "TOS-host-redirect", 5, 3, 3 },
+
+   { "echo-request", 8, 0, 0xFF },
+   /* Alias */ { "ping", 8, 0, 0xFF },
+
+   { "router-advertisement", 9, 0, 0xFF },
+
+   { "router-solicitation", 10, 0, 0xFF },
+
+   { "time-exceeded", 11, 0, 0xFF },
+   /* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
+   {   "ttl-zero-during-transit", 11, 0, 0 },
+   {   "ttl-zero-during-reassembly", 11, 1, 1 },
+
+   { "parameter-problem", 12, 0, 0xFF },
+   {   "ip-header-bad", 12, 0, 0 },
+   {   "required-option-missing", 12, 1, 1 },
+
+   { "timestamp-request", 13, 0, 0xFF },
+
+   { "timestamp-reply", 14, 0, 0xFF },
+
+   { "address-mask-request", 17, 0, 0xFF },
+
+   { "address-mask-reply", 18, 0, 0xFF }
+};
+
+struct dim_match
+{
+   u_int8_t invert;
+   u_int32_t left;
+        u_int32_t right;
+};
+
+#define OPTION_OFFSET 256
+
+#define FMT_NUMERIC     0x0001
+#define FMT_NOCOUNTS    0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS     0x0008
+#define FMT_NOTABLE     0x0010
+#define FMT_NOTARGET    0x0020
+#define FMT_VIA         0x0040
+#define FMT_NONEWLINE   0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                        | FMT_NUMERIC | FMT_NOTABLE)
+
+
+/*
+ * =============== iptables matches/targets compatibility ===============
+ */
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+
+/*
+ * libiptc.h declarations
+ */
+
+#ifndef IPT_MIN_ALIGN
+/* ipt_entry has pointers and u_int64_t's in it, so if you align to
+   it, you'll also align to any crazy matches and targets someone
+   might write */
+#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry))
+#endif
+
+#define IPT_ALIGN(s) (((s) + ((IPT_MIN_ALIGN)-1)) & ~((IPT_MIN_ALIGN)-1))
+
+typedef char ipt_chainlabel[32];
+
+#define IPTC_LABEL_ACCEPT  "ACCEPT"
+#define IPTC_LABEL_DROP    "DROP"
+#define IPTC_LABEL_QUEUE   "QUEUE"
+#define IPTC_LABEL_RETURN  "RETURN"
+
+/* Transparent handle type. */
+typedef struct iptc_handle *iptc_handle_t;
+
+
+/*
+ * iptables.h declarations
+ */
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/local/lib/iptables"
+#endif
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+
+#ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */
+#define IPT_SO_GET_REVISION_MATCH       (IPT_BASE_CTL + 2)
+#define IPT_SO_GET_REVISION_TARGET      (IPT_BASE_CTL + 3)
+
+struct ipt_get_revision
+{
+   char name[IPT_FUNCTION_MAXNAMELEN-1];
+   
+   u_int8_t revision;
+};
+#endif /* IPT_SO_GET_REVISION_MATCH   Old kernel source */
+    
+struct iptables_rule_match
+{
+   struct iptables_rule_match *next;
+   
+   struct iptables_match *match;
+};
+
+struct iptables_match
+{
+   struct iptables_match *next;
+
+   ipt_chainlabel name;
+   
+   u_int8_t revision;
+
+   const char *version;
+
+   /* Size of match data. */
+   size_t size;
+
+   /* Size of match data relevent for userspace comparison purposes */
+   size_t userspacesize;
+
+   /* Function which prints out usage message. */
+   void (*help)(void);
+
+   /* Initialize the match. */
+   void (*init)(struct ipt_entry_match *m, unsigned int *nfcache);
+
+   /* Function which parses command options; returns true if it
+           ate an option */
+   int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+            const struct ipt_entry *entry,
+            unsigned int *nfcache,
+            struct ipt_entry_match **match);
+
+   /* Final check; exit if not ok. */
+   void (*final_check)(unsigned int flags);
+
+   /* Prints out the match iff non-NULL: put space at end */
+   void (*print)(const struct ipt_ip *ip,
+             const struct ipt_entry_match *match, int numeric);
+
+   /* Saves the match info in parsable form to stdout. */
+   void (*save)(const struct ipt_ip *ip,
+            const struct ipt_entry_match *match);
+
+   /* Pointer to list of extra command-line options */
+   const struct option *extra_opts;
+
+   /* Ignore these men behind the curtain: */
+   unsigned int option_offset;
+   struct ipt_entry_match *m;
+   unsigned int mflags;
+#ifdef NO_SHARED_LIBS
+   unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+struct iptables_target
+{
+   struct iptables_target *next;
+
+   ipt_chainlabel name;
+
+   u_int8_t revision;
+
+   const char *version;
+
+   /* Size of target data. */
+   size_t size;
+
+   /* Size of target data relevent for userspace comparison purposes */
+   size_t userspacesize;
+
+   /* Function which prints out usage message. */
+   void (*help)(void);
+
+   /* Initialize the target. */
+   void (*init)(struct ipt_entry_target *t, unsigned int *nfcache);
+
+   /* Function which parses command options; returns true if it
+           ate an option */
+   int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+            const struct ipt_entry *entry,
+            struct ipt_entry_target **target);
+
+   /* Final check; exit if not ok. */
+   void (*final_check)(unsigned int flags);
+
+   /* Prints out the target iff non-NULL: put space at end */
+   void (*print)(const struct ipt_ip *ip,
+             const struct ipt_entry_target *target, int numeric);
+
+   /* Saves the targinfo in parsable form to stdout. */
+   void (*save)(const struct ipt_ip *ip,
+            const struct ipt_entry_target *target);
+
+   /* Pointer to list of extra command-line options */
+   struct option *extra_opts;
+
+   /* Ignore these men behind the curtain: */
+   unsigned int option_offset;
+   struct ipt_entry_target *t;
+   unsigned int tflags;
+   unsigned int used;
+#ifdef NO_SHARED_LIBS
+   unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+
+/* Your shared library should call one of these. */
+void register_match(struct iptables_match *me);
+void register_target(struct iptables_target *me);
+
+struct in_addr *dotted_to_addr(const char *dotted);
+char *addr_to_dotted(const struct in_addr *addrp);
+char *addr_to_anyname(const struct in_addr *addr);
+char *mask_to_dotted(const struct in_addr *mask);
+
+void parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+              struct in_addr *maskp, unsigned int *naddrs);
+u_int16_t parse_protocol(const char *s);
+void parse_interface(const char *arg, char *vianame, unsigned char *mask);
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct iptables_match *iptables_matches = NULL;
+struct iptables_target *iptables_targets = NULL;
+
+enum ipt_tryload {
+   DONT_LOAD,
+   TRY_LOAD,
+   LOAD_MUST_SUCCEED
+};
+
+struct iptables_target *find_target(const char *name, enum ipt_tryload);
+struct iptables_match *find_match(const char *name, enum ipt_tryload,
+                 struct iptables_rule_match **match);
+
+/* kernel revision handling */
+int kernel_version;
+void get_kernel_version(void);
+#define LINUX_VERSION(x,y,z)    (0x10000*(x) + 0x100*(y) + z)
+#define LINUX_VERSION_MAJOR(x)  (((x)>>16) & 0xFF)
+#define LINUX_VERSION_MINOR(x)  (((x)>> 8) & 0xFF)
+#define LINUX_VERSION_PATCH(x)  ( (x)      & 0xFF)
+
+
+/*
+ * iptables_common.h declarations
+ */
+
+enum exittype {
+   OTHER_PROBLEM = 1,
+   PARAMETER_PROBLEM,
+   VERSION_PROBLEM
+};
+
+/* this is a special 64bit data type that is 8-byte aligned */
+#define aligned_u64 unsigned long long __attribute__((aligned(8)))
+
+void exit_printhelp(struct iptables_rule_match 
+           *matches) __attribute__((noreturn));
+void exit_tryhelp(int) __attribute__((noreturn));
+int check_inverse(const char option[], int *invert, int *optind, int argc);
+int string_to_number(const char *, 
+            unsigned int, 
+            unsigned int,
+            unsigned int *);
+extern int string_to_number_l(const char *, 
+                 unsigned long int, 
+                 unsigned long int,
+                 unsigned long *);
+extern int string_to_number_ll(const char *, 
+                  unsigned long long int, 
+                  unsigned long long int,
+                  unsigned long long *);
+int iptables_insmod(const char *modname, const char *modprobe);
+void exit_error(enum exittype, char *, ...)__attribute__((noreturn,
+                             format(printf,2,3)));
+
+const char *program_name = "nf-hipac"; 
+const char *program_version = "0.9.1";
+char *lib_dir;
+
+#ifdef NO_SHARED_LIBS
+# ifdef _INIT
+#  define _init _INIT
+# endif
+  extern void init_extensions(void);
+#endif
+
+
+
+/*
+ * iptables_common.h implementations (only necessary functions)
+ */
+
+void
+exit_printhelp(struct iptables_rule_match *matches)
+{
+   struct iptables_rule_match *matchp = NULL;
+   struct iptables_target *t = NULL;
+   
+   printf("\n%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LF] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+          program_name, program_version, program_name, program_name,
+          program_name, program_name, program_name, program_name,
+          program_name, program_name);
+
+   printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain      Append to chain\n"
+"  --delete  -D chain      Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"              Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"              Insert in chain as rulenum (default 1 = first)\n"
+"  --replace -R chain rulenum\n"
+"              Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]        List the rules in a chain or all chains\n"
+"  --flush   -F [chain]        Delete all rules in chain or all chains\n"
+"  --new     -N chain      Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]        Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"              Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"              Change chain name, (moving any references)\n"
+"----------\n"
+"Options:\n"
+"  --proto -p [!] proto    protocol: by number or name, eg. `tcp'\n"
+"  --source    -s [!] address[/mask] or\n"
+"                       address[:address]\n"
+"              source(s) specification\n"
+"  --destination -d [!] address[/mask] or\n"
+"                       address[:address]\n"
+"              destination(s) specification\n"
+"  --in-interface -i [!] devname\n"
+"              network interface name\n"
+"  --jump  -j target\n"
+"              target for rule \n"
+"  --numeric   -n      numeric output of addresses and ports\n"
+"  --out-interface -o [!] devname\n"
+"              network interface name\n"
+"  --verbose   -v      verbose mode\n"
+"  --line-numbers      print line numbers when listing\n"
+"  [!] --fragment -f             match second or further fragments only\n"
+"  --version   -V      print package version.\n"
+"----------\n"
+"TCP options:\n"
+" --syn                match when only SYN flag set\n"
+" --not-syn            match when not only SYN flag set\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+"              match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+"              match destination port(s)\n"
+"----------\n"
+"UDP options:\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+"              match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+"              match destination port(s)\n"
+"----------\n"
+"ICMP options:\n"
+" --icmp-type typename         match icmp type\n"
+"              (or numeric type or type/code)\n"
+"              see also: nf-hipac -h icmp\n"   
+"----------\n"
+"STATE options:\n"
+" --state [!] [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|ESTABLISHED,RELATED]\n"
+"              State to match\n"
+"----------\n"
+"TTL options:\n"
+" --ttl   value[:value]        match time to live value(s)\n"
+"----------\n");
+
+
+   /* Print out any special helps. A user might like to be able
+      to add a --help to the commandline, and see expected
+      results. So we call help for all matches & targets */
+   
+   if (iptables_targets) {
+       printf("\n-----------------\n"
+                "Iptables targets:\n"
+                "-----------------\n");
+       for (t = iptables_targets; t; t = t->next) {
+           if (t->used) {
+               printf("\n");
+               t->help();
+           }
+       }
+   }
+
+   if (matches) {
+       printf("\n-----------------\n"
+                "Iptables matches:\n"
+            "-----------------\n");
+       for (matchp = matches; matchp; matchp = matchp->next) {
+           printf("\n");
+           matchp->match->help();
+       }
+   }
+
+   exit(0);
+}
+
+
+int
+check_inverse(const char option[], int *invert, int *optind, int argc)
+{
+   if (option && strcmp(option, "!") == 0) {
+       if (*invert)
+           exit_error(PARAMETER_PROBLEM,
+                  "Multiple `!' flags not allowed");
+       *invert = TRUE;
+       if (optind) {
+           *optind = *optind+1;
+           if (argc && *optind > argc)
+               exit_error(PARAMETER_PROBLEM,
+                      "no argument following `!'");
+       }
+
+       return TRUE;
+   }
+   return FALSE;
+}
+
+
+int
+string_to_number_ll(const char *s, unsigned long long min,
+           unsigned long long max, unsigned long long *ret)
+{
+   unsigned long long number;
+   char *end;
+   
+   /* Handle hex, octal, etc. */
+   errno = 0;
+   number = strtoull(s, &end, 0);
+   if (*end == '\0' && end != s) {
+       /* we parsed a number, let's see if we want this */
+       if (errno != ERANGE && min <= number &&
+           (!max || number <= max)) {
+           *ret = number;
+           return 0;
+       }
+   }
+   return -1;
+}
+
+int
+string_to_number_l(const char *s, unsigned long min, unsigned long max,
+          unsigned long *ret)
+{
+   int result;
+   unsigned long long number;
+
+   result = string_to_number_ll(s, min, max, &number);
+   *ret = (unsigned long)number;
+
+   return result;
+}
+
+int
+string_to_number(const char *s, unsigned int min, unsigned int max,
+        unsigned int *ret)
+{
+   int result;
+   unsigned long number;
+
+   result = string_to_number_l(s, min, max, &number);
+   *ret = (unsigned int)number;
+
+   return result;
+}
+
+static char *get_modprobe(void)
+{
+   int procfile;
+   char *ret;
+
+#define PROCFILE_BUFSIZ 1024
+   procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+   if (procfile < 0)
+       return NULL;
+
+   ret = (char *) malloc(PROCFILE_BUFSIZ);
+   if (ret) {
+       memset(ret, 0, PROCFILE_BUFSIZ);
+       switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
+       case -1: goto fail;
+       case PROCFILE_BUFSIZ: goto fail; /* Partial read.  Wierd */
+       }
+       if (ret[strlen(ret)-1]=='\n') 
+           ret[strlen(ret)-1]=0;
+       close(procfile);
+       return ret;
+   }
+ fail:
+   free(ret);
+   close(procfile);
+   return NULL;
+}
+
+int iptables_insmod(const char *modname, const char *modprobe)
+{
+   char *buf = NULL;
+   char *argv[3];
+   int status;
+
+   /* If they don't explicitly set it, read out of kernel */
+   if (!modprobe) {
+       buf = get_modprobe();
+       if (!buf)
+           return -1;
+       modprobe = buf;
+   }
+
+   switch (fork()) {
+   case 0:
+       argv[0] = (char *)modprobe;
+       argv[1] = (char *)modname;
+       argv[2] = NULL;
+       execv(argv[0], argv);
+
+       /* not usually reached */
+       exit(1);
+   case -1:
+       return -1;
+
+   default: /* parent */
+       wait(&status);
+   }
+
+   free(buf); 
+   if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+       return 0;
+   return -1;
+}
+
+void clear_rule_matches(struct iptables_rule_match **matches)
+{
+   struct iptables_rule_match *matchp, *tmp;
+
+   for (matchp = *matches; matchp;) {
+       tmp = matchp->next;
+       free(matchp);
+       matchp = tmp;
+   }
+
+   *matches = NULL;
+}
+
+
+static void set_revision(char *name, u_int8_t revision)
+{
+   /* Old kernel sources don't have ".revision" field,
+      but we stole a byte from name. */
+   name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0';
+   name[IPT_FUNCTION_MAXNAMELEN - 1] = revision;
+}
+
+
+static void free_opts(int reset_offset)
+{
+   if (opts != original_opts) {
+       free(opts);
+       opts = original_opts;
+       if (reset_offset)
+           global_option_offset = 0;
+   }
+}
+
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+   va_list args;
+   va_start(args, msg);
+   fprintf(stderr, "%s v%s: ", program_name, program_version);
+   vfprintf(stderr, msg, args);
+   va_end(args);
+   fprintf(stderr, "\n");
+   if (status == PARAMETER_PROBLEM)
+       exit_tryhelp(status);
+   if (status == VERSION_PROBLEM)
+       fprintf(stderr,
+           "Perhaps nf-hipac or your "
+           "kernel needs to be upgraded.\n");
+   free_opts(1);
+   exit(status);
+}
+
+
+void
+exit_tryhelp(int status)
+{
+   if (line != -1)
+       fprintf(stderr, "Error occurred at line: %d\n", line);
+   fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+           program_name, program_name );
+   free_opts(1);
+   exit(status);
+}
+
+
+/*
+ * iptables.h implementations (only necessary functions)
+ */
+
+static int compatible_revision(const char *name, u_int8_t revision, int opt)
+{
+   struct ipt_get_revision rev;
+   socklen_t s = sizeof(rev);
+   int max_rev, sockfd;
+
+   sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+   if (sockfd < 0) {
+       fprintf(stderr, "Could not open socket to kernel: %s\n",
+           strerror(errno));
+       exit(1);
+   }
+
+   strcpy(rev.name, name);
+   rev.revision = revision;
+
+   max_rev = getsockopt(sockfd, IPPROTO_IP, opt, &rev, &s);
+   if (max_rev < 0) {
+       /* Definitely don't support this? */
+       if (errno == EPROTONOSUPPORT) {
+           close(sockfd);
+           return 0;
+       } else if (errno == ENOPROTOOPT) {
+           close(sockfd);
+           /* Assume only revision 0 support (old kernel) */
+           return (revision == 0);
+       } else {
+           fprintf(stderr, "getsockopt failed strangely: %s\n",
+               strerror(errno));
+           exit(1);
+       }
+   }
+   close(sockfd);
+   return 1;
+}
+
+static int compatible_match_revision(const char *name, u_int8_t revision)
+{
+   return compatible_revision(name, revision, IPT_SO_GET_REVISION_MATCH);
+}
+
+static int compatible_target_revision(const char *name, u_int8_t revision)
+{
+   return compatible_revision(name, revision, IPT_SO_GET_REVISION_TARGET);
+}
+
+void
+register_match(struct iptables_match *me)
+{
+   struct iptables_match **i;
+
+   /*if (strcmp(me->version, program_version) != 0) {
+    *fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+    *  program_name, me->name, me->version, program_version);
+    *exit(1);
+    *} */
+
+   struct iptables_match *old;
+   
+   /* Revision field stole a char from name. */
+   if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
+       fprintf(stderr, "%s: target `%s' has invalid name\n",
+           program_name, me->name);
+       exit(1);
+   }
+
+   old = find_match(me->name, DONT_LOAD, NULL);
+   if (old) {
+       if (old->revision == me->revision) {
+           fprintf(stderr,
+               "%s: match `%s' already registered.\n",
+               program_name, me->name);
+           exit(1);
+       }
+
+       /* Now we have two (or more) options, check compatibility. */
+       if (compatible_match_revision(old->name, old->revision)
+           && old->revision > me->revision)
+           return;
+
+       /* Replace if compatible. */
+       if (!compatible_match_revision(me->name, me->revision))
+           return;
+
+       /* Delete old one. */
+       for (i = &iptables_matches; *i!=old; i = &(*i)->next);
+       *i = old->next;
+   }
+
+   if (me->size != IPT_ALIGN(me->size)) {
+       fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+           program_name, me->name, (unsigned int) me->size);
+       exit(1);
+   }
+
+   /* Append to list. */
+   for (i = &iptables_matches; *i; i = &(*i)->next);
+   me->next = NULL;
+   *i = me;
+
+   me->m = NULL;
+   me->mflags = 0;
+}
+
+void
+register_target(struct iptables_target *me)
+{
+   /*if (strcmp(me->version, program_version) != 0) {
+    *fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+    *  program_name, me->name, me->version, program_version);
+    *exit(1);
+    *} */
+   
+   struct iptables_target *old;
+   
+        /* Revision field stole a char from name. */
+   if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
+       fprintf(stderr, "%s: target `%s' has invalid name\n",
+           program_name, me->name);
+       exit(1);
+   }
+
+   old = find_target(me->name, DONT_LOAD);
+   if (old) {
+       struct iptables_target **i;
+
+       if (old->revision == me->revision) {
+           fprintf(stderr,
+               "%s: target `%s' already registered.\n",
+               program_name, me->name);
+           exit(1);
+       }
+
+       /* Now we have two (or more) options, check compatibility. */
+       if (compatible_target_revision(old->name, old->revision)
+           && old->revision > me->revision)
+           return;
+
+       /* Replace if compatible. */
+       if (!compatible_target_revision(me->name, me->revision))
+           return;
+
+       /* Delete old one. */
+       for (i = &iptables_targets; *i!=old; i = &(*i)->next);
+       *i = old->next;
+   }
+
+   if (me->size != IPT_ALIGN(me->size)) {
+       fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+           program_name, me->name, (unsigned int) me->size);
+       exit(1);
+   }
+
+   /* Prepend to list. */
+   me->next = iptables_targets;
+   iptables_targets = me;
+   me->t = NULL;
+   me->tflags = 0;
+}
+
+struct in_addr *
+dotted_to_addr(const char *dotted)
+{
+   static struct in_addr addr;
+   unsigned char *addrp;
+   char *p, *q;
+   unsigned int onebyte;
+   int i;
+   char buf[20];
+
+   /* copy dotted string, because we need to modify it */
+   strncpy(buf, dotted, sizeof(buf) - 1);
+   buf[sizeof(buf) - 1] = '\0';
+   addrp = (unsigned char *) &(addr.s_addr);
+   
+   p = buf;
+   for (i = 0; i < 3; i++) {
+       if ((q = strchr(p, '.')) == NULL)
+           return (struct in_addr *) NULL;
+
+       *q = '\0';
+       if (string_to_number(p, 0, 255, &onebyte) == -1)
+           return (struct in_addr *) NULL;
+
+       addrp[i] = (unsigned char) onebyte;
+       p = q + 1;
+   }
+
+   /* we've checked 3 bytes, now we check the last one */
+   if (string_to_number(p, 0, 255, &onebyte) == -1)
+       return (struct in_addr *) NULL;
+
+   addrp[3] = (unsigned char) onebyte;
+
+   return &addr;
+}
+
+char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+   static char buf[20];
+   const unsigned char *bytep;
+
+   bytep = (const unsigned char *) &(addrp->s_addr);
+   sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+   return buf;
+}
+
+static char *
+addr_to_host(const struct in_addr *addr)
+{
+   struct hostent *host;
+
+   if ((host = gethostbyaddr((char *) addr,
+                 sizeof(struct in_addr), AF_INET)) != NULL)
+       return (char *) host->h_name;
+
+   return (char *) NULL;
+}
+
+static char *
+addr_to_network(const struct in_addr *addr)
+{
+   struct netent *net;
+
+   if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+       return (char *) net->n_name;
+
+   return (char *) NULL;
+}
+
+char *
+addr_to_anyname(const struct in_addr *addr)
+{
+   char *name;
+
+   if ((name = addr_to_host(addr)) != NULL ||
+       (name = addr_to_network(addr)) != NULL)
+       return name;
+
+   return addr_to_dotted(addr);
+}
+
+char *
+mask_to_dotted(const struct in_addr *mask)
+{
+   int i;
+   static char buf[20];
+   u_int32_t maskaddr, bits;
+
+   maskaddr = ntohl(mask->s_addr);
+
+   if (maskaddr == 0xFFFFFFFFL)
+       /* we don't want to see "/32" */
+       return "";
+
+   i = 32;
+   bits = 0xFFFFFFFEL;
+   while (--i >= 0 && maskaddr != bits)
+       bits <<= 1;
+   if (i >= 0)
+       sprintf(buf, "/%d", i);
+   else
+       /* mask was not a decent combination of 1's and 0's */
+       sprintf(buf, "/%s", addr_to_dotted(mask));
+
+   return buf;
+}
+
+static struct in_addr *
+network_to_addr(const char *name)
+{
+   struct netent *net;
+   static struct in_addr addr;
+
+   if ((net = getnetbyname(name)) != NULL) {
+       if (net->n_addrtype != AF_INET)
+           return (struct in_addr *) NULL;
+       addr.s_addr = htonl((unsigned long) net->n_net);
+       return &addr;
+   }
+
+   return (struct in_addr *) NULL;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+   void *p;
+
+   if ((p = malloc(size)) == NULL) {
+       perror("nf-hipac: malloc failed");
+       exit(1);
+   }
+   return p;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+   void *p;
+
+   if ((p = calloc(count, size)) == NULL) {
+       perror("nf-hipac: calloc failed");
+       exit(1);
+   }
+   return p;
+}
+
+static void
+inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+   /* memcpy(dst, src, sizeof(struct in_addr)); */
+   dst->s_addr = src->s_addr;
+}
+
+static struct in_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+   struct hostent *host;
+   struct in_addr *addr;
+   unsigned int i;
+
+   *naddr = 0;
+   if ((host = gethostbyname(name)) != NULL) {
+       if (host->h_addrtype != AF_INET ||
+           host->h_length != sizeof(struct in_addr))
+           return (struct in_addr *) NULL;
+
+       while (host->h_addr_list[*naddr] != (char *) NULL)
+           (*naddr)++;
+       addr = fw_calloc(*naddr, sizeof(struct in_addr) * *naddr);
+                       //FIXME: why * *naddr, should be enough without it!
+       for (i = 0; i < *naddr; i++)
+           inaddrcpy(&(addr[i]),
+                 (struct in_addr *) host->h_addr_list[i]);
+       return addr;
+   }
+
+   return (struct in_addr *) NULL;
+}
+
+static struct in_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+   struct in_addr *addrp, *addrptmp;
+
+   if ((addrptmp = dotted_to_addr(name)) != NULL ||
+       (addrptmp = network_to_addr(name)) != NULL) {
+       addrp = fw_malloc(sizeof(struct in_addr));
+       inaddrcpy(addrp, addrptmp);
+       *naddrs = 1;
+       return addrp;
+   }
+   if ((addrp = host_to_addr(name, naddrs)) != NULL)
+       return addrp;
+
+   exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *
+parse_mask(char *mask)
+{
+   static struct in_addr maskaddr;
+   struct in_addr *addrp;
+   unsigned int bits;
+
+   if (mask == NULL) {
+       /* no mask at all defaults to 32 bits */
+       maskaddr.s_addr = 0xFFFFFFFF;
+       return &maskaddr;
+   }
+   if ((addrp = dotted_to_addr(mask)) != NULL)
+       /* dotted_to_addr already returns a network byte order addr */
+       return addrp;
+   if (string_to_number(mask, 0, 32, &bits) == -1)
+       exit_error(PARAMETER_PROBLEM,
+              "invalid mask `%s' specified", mask);
+   if (bits != 0) {
+       maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+       return &maskaddr;
+   }
+
+   maskaddr.s_addr = 0L;
+   return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+             struct in_addr *maskp, unsigned int *naddrs)
+{
+   struct in_addr *addrp;
+   char buf[256];
+   char *p;
+   int i, j, k, n;
+
+   strncpy(buf, name, sizeof(buf) - 1);
+   buf[sizeof(buf) - 1] = '\0';
+   if ((p = strrchr(buf, '/')) != NULL) {
+       *p = '\0';
+       addrp = parse_mask(p + 1);
+   } else
+       addrp = parse_mask(NULL);
+   inaddrcpy(maskp, addrp);
+
+   /* if a null mask is given, the name is ignored, like in "any/0" */
+   if (maskp->s_addr == 0L)
+       strcpy(buf, "0.0.0.0");
+
+   addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+   n = *naddrs;
+   for (i = 0, j = 0; i < n; i++) {
+       addrp[j++].s_addr &= maskp->s_addr;
+       for (k = 0; k < j - 1; k++) {
+           if (addrp[k].s_addr == addrp[j - 1].s_addr) {
+               (*naddrs)--;
+               j--;
+               break;
+           }
+       }
+   }
+}
+
+u_int16_t
+parse_protocol(const char *s)
+{
+   unsigned int proto;
+
+   if (string_to_number(s, 0, 255, &proto) == -1) {
+       struct protoent *pent;
+
+       if ((pent = getprotobyname(s)))
+           proto = pent->p_proto;
+       else {
+           unsigned int i;
+           for (i = 0;
+                i < sizeof(chain_protos)/sizeof(struct pprot);
+                i++) {
+               if (strcmp(s, chain_protos[i].name) == 0) {
+                   proto = chain_protos[i].num;
+                   break;
+               }
+           }
+           if (i == sizeof(chain_protos)/sizeof(struct pprot))
+               exit_error(PARAMETER_PROBLEM,
+                      "unknown protocol `%s' specified",
+                      s);
+       }
+   }
+
+   return (u_int16_t)proto;
+}
+
+void parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+   int vialen = strlen(arg);
+   unsigned int i;
+
+   memset(mask, 0, IFNAMSIZ);
+   memset(vianame, 0, IFNAMSIZ);
+
+   if (vialen + 1 > IFNAMSIZ)
+       exit_error(PARAMETER_PROBLEM,
+              "interface name `%s' must be shorter than IFNAMSIZ"
+              " (%i)", arg, IFNAMSIZ-1);
+
+   strcpy(vianame, arg);
+   if ((vialen == 0) || (vialen == 1 && vianame[0] == '+'))
+       memset(mask, 0, IFNAMSIZ);
+   else if (vianame[vialen - 1] == '+') {
+       memset(mask, 0xFF, vialen - 1);
+       memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+       /* Don't remove `+' here! -HW */
+   } else {
+       /* Include nul-terminator in match */
+       memset(mask, 0xFF, vialen + 1);
+       memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+       for (i = 0; vianame[i]; i++) {
+           if (!isalnum(vianame[i]) 
+               && vianame[i] != '_' 
+               && vianame[i] != '.') {
+               printf("Warning: wierd character in interface"
+                      " `%s' (No aliases, :, ! or *).\n",
+                      vianame);
+               break;
+           }
+       }
+   }
+}
+
+struct iptables_target *
+find_target(const char *name, enum ipt_tryload tryload)
+{
+   struct iptables_target *ptr;
+
+   /* Standard target? */
+   if (strcmp(name, "") == 0
+       || strcmp(name, IPTC_LABEL_ACCEPT) == 0
+       || strcmp(name, IPTC_LABEL_DROP) == 0
+       // not supported by nf-hipac
+       //  || strcmp(name, IPTC_LABEL_QUEUE) == 0 
+       || strcmp(name, IPTC_LABEL_RETURN) == 0)
+       name = "standard";
+
+   for (ptr = iptables_targets; ptr; ptr = ptr->next) {
+       if (strcmp(name, ptr->name) == 0)
+           break;
+   }
+
+#ifndef NO_SHARED_LIBS
+   if (!ptr && tryload != DONT_LOAD) {
+       char path[strlen(lib_dir) + sizeof("/libipt_.so")
+            + strlen(name)];
+       sprintf(path, "%s/libipt_%s.so", lib_dir, name);
+       if (dlopen(path, RTLD_NOW)) {
+           /* Found library.  If it didn't register itself,
+              maybe they specified match as a target. */
+           ptr = find_target(name, DONT_LOAD);
+           if (!ptr)
+               exit_error(PARAMETER_PROBLEM,
+                      "Couldn't load target `%s'\n",
+                      name);
+       } else if (tryload == LOAD_MUST_SUCCEED)
+           exit_error(PARAMETER_PROBLEM,
+                  "Couldn't load target `%s':%s\n",
+                  name, dlerror());
+   }
+#else
+   if (ptr && !ptr->loaded) {
+       if (tryload != DONT_LOAD)
+           ptr->loaded = 1;
+       else
+           ptr = NULL;
+   }
+   if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+       exit_error(PARAMETER_PROBLEM,
+              "Couldn't find target `%s'\n", name);
+   }
+#endif
+
+   if (ptr)
+       ptr->used = 1;
+
+   return ptr;
+}
+
+struct iptables_match *
+find_match(const char *name, enum ipt_tryload tryload,
+      struct iptables_rule_match **matches)
+{
+   struct iptables_match *ptr;
+
+   for (ptr = iptables_matches; ptr; ptr = ptr->next) {
+       if (strcmp(name, ptr->name) == 0)
+           break;
+   }
+
+#ifndef NO_SHARED_LIBS
+   if (!ptr && tryload != DONT_LOAD) {
+       char path[strlen(lib_dir) + sizeof("/libipt_.so")
+            + strlen(name)];
+       sprintf(path, "%s/libipt_%s.so", lib_dir, name);
+       if (dlopen(path, RTLD_NOW)) {
+           /* Found library.  If it didn't register itself,
+              maybe they specified target as match. */
+           ptr = find_match(name, DONT_LOAD, NULL);
+
+           if (!ptr)
+               exit_error(PARAMETER_PROBLEM,
+                      "Couldn't load match `%s'\n",
+                      name);
+       } else if (tryload == LOAD_MUST_SUCCEED)
+           exit_error(PARAMETER_PROBLEM,
+                  "Couldn't load match `%s':%s\n",
+                  name, dlerror());
+   }
+#else
+   if (ptr && !ptr->loaded) {
+       if (tryload != DONT_LOAD)
+           ptr->loaded = 1;
+       else
+           ptr = NULL;
+   }
+   if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+       exit_error(PARAMETER_PROBLEM,
+              "Couldn't find match `%s'\n", name);
+   }
+#endif
+
+   if (ptr && matches) {
+       struct iptables_rule_match **i;
+       struct iptables_rule_match *newentry;
+
+       newentry = fw_malloc(sizeof(struct iptables_rule_match));
+
+       for (i = matches; *i; i = &(*i)->next);
+       newentry->match = ptr;
+       newentry->next = NULL;
+       *i = newentry;
+   }
+
+   return ptr;
+}
+
+void
+get_kernel_version(void)
+{
+   static struct utsname uts;
+   int x = 0, y = 0, z = 0;
+   
+   if (uname(&uts) == -1) {
+       fprintf(stderr, "Unable to retrieve kernel version.\n");
+       free_opts(1);
+       exit(1);
+   }
+   
+   sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
+   kernel_version = LINUX_VERSION(x, y, z);
+}
+
+
+/*
+ * ========== End of iptables matches/targets compatibility code ==========
+ */
+
+
+
+
+static char
+cmd2char(int option)
+{
+   const char *ptr;
+   for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+   return *ptr;
+}
+
+
+static void
+add_command(struct nfhp_cmd *message, int newcmd, int invert)
+{      
+   if (invert)
+       exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
+   if (message->cmd)
+       exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+              cmd2char(newcmd), cmd2char(message->cmd));
+   message->cmd = newcmd;   
+}
+
+static unsigned int
+dimid_to_option(unsigned int dimid)
+{
+   return (1 << (dimid + NUMBER_OF_OPT));
+}
+
+static void
+set_option(unsigned int *options, unsigned int std_opt, unsigned int dimid)
+{
+   unsigned int option;
+   if (std_opt) 
+       option = std_opt;
+   else    option = dimid_to_option(dimid);
+   if (*options & option)
+       exit_error(PARAMETER_PROBLEM,
+              "multiple flags of the same "
+              "type not allowed");
+   *options |= option;
+}
+
+
+
+void
+parse_rulenumber(const char *str, unsigned int *rulenum)
+{
+   if (string_to_number(str, 0, INT_MAX, rulenum) == -1)
+       exit_error(PARAMETER_PROBLEM,
+              "Invalid rule number `%s'", str);
+}
+
+
+
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+         unsigned int *option_offset)
+{
+   unsigned int num_old, num_new, i;
+   struct option *merge;
+
+   for (num_old = 0; oldopts[num_old].name; num_old++);
+   for (num_new = 0; newopts[num_new].name; num_new++);
+   
+   global_option_offset += OPTION_OFFSET;
+   *option_offset = global_option_offset;
+
+   merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+   memcpy(merge, oldopts, num_old * sizeof(struct option));
+   free_opts(0);
+   for (i = 0; i < num_new; i++) {
+       merge[num_old + i] = newopts[i];
+       merge[num_old + i].val += *option_offset;
+   }
+   memset(merge + num_old + num_new, 0, sizeof(struct option));
+   return merge;
+}
+
+static void
+parse_target(const char *arg, char *target)
+{
+   const char *ptr;
+   memset(target, 0, HIPAC_CHAIN_NAME_MAX_LEN);
+
+   if (strlen(arg) < 1)
+       exit_error(PARAMETER_PROBLEM,
+              "Invalid target name (too short)");
+   
+   if (strlen(arg) + 1 > HIPAC_CHAIN_NAME_MAX_LEN)
+       exit_error(PARAMETER_PROBLEM,
+              "target name `%s' must be shorter than "
+              "HIPAC_CHAIN_NAME_MAX_LEN (%i)", 
+              arg, HIPAC_CHAIN_NAME_MAX_LEN);  
+   for (ptr = arg; *ptr; ptr++)
+       if (isspace(*ptr))
+           exit_error(PARAMETER_PROBLEM,
+                  "Invalid target name `%s'", arg);
+   strcpy(target, arg);
+}
+
+void
+parse_policy(const char *targetname, u_int8_t *policy)
+{
+   if (strlen(targetname) < 1)
+       exit_error(PARAMETER_PROBLEM,
+              "Invalid target name (too short)");
+   if (strcasecmp(targetname, IPTC_LABEL_DROP) == 0){
+       *policy = TARGET_DROP;
+       return;
+   } else if (strcasecmp(targetname, IPTC_LABEL_ACCEPT) == 0){
+       *policy = TARGET_ACCEPT;
+       return;
+   } 
+   
+   exit_error(PARAMETER_PROBLEM,
+          "Invalid target name `%s'", targetname);
+}
+
+void
+parse_native_protocol(const char *s, struct dim_match *rule)
+{
+   char buf[256];
+   char *p;
+
+   strncpy(buf, s, sizeof(buf) - 1);
+   buf[sizeof(buf) - 1] = '\0';
+   if ((p = strrchr(buf, ':')) != NULL) {
+       *p = '\0'; 
+       p++;
+       rule[DIMID_PROTO].left = buf[0] ? parse_protocol(buf) : 0;
+       if (strcasecmp(buf, p) == 0){
+           rule[DIMID_PROTO].right = rule[DIMID_PROTO].left;
+       } else {
+           rule[DIMID_PROTO].right = p[0] ? parse_protocol(p) : 255;
+           if (rule[DIMID_PROTO].left > rule[DIMID_PROTO].right)
+               exit_error(PARAMETER_PROBLEM,
+                      "Range Error: start value > "
+                      "end value\n");
+           if ((rule[DIMID_PROTO].left == 0) &&
+               (rule[DIMID_PROTO].right == 255)){
+               return;
+           }
+       }
+   } else {
+       rule[DIMID_PROTO].left =
+           rule[DIMID_PROTO].right = parse_protocol(s);
+   }
+   set_option(&options, 0, DIMID_PROTO);
+}
+
+
+static void
+parse_native_interface(const char *arg, char *vianame)
+{
+   int vialen = strlen(arg);
+   unsigned int i;
+
+   memset(vianame, 0, IFNAMSIZ);
+
+   if (vialen + 1 > IFNAMSIZ)
+       exit_error(PARAMETER_PROBLEM,
+              "interface name `%s' must be shorter than IFNAMSIZ"
+              " (%i)", arg, IFNAMSIZ-1);
+
+   strcpy(vianame, arg);
+   if (vialen == 0)
+       exit_error(PARAMETER_PROBLEM,
+              "no interface name specified");
+   else if (vianame[vialen - 1] == '+') {
+       exit_error(PARAMETER_PROBLEM,
+              "no + matches on interfaces supported");
+   } else {
+       /* Include nul-terminator in match */
+       for (i = 0; vianame[i]; i++) {
+           if (!isalnum(vianame[i]) 
+               && vianame[i] != '_' 
+               && vianame[i] != '.') {
+               printf("Warning: wierd character in interface"
+                      " `%s' (No aliases, :, ! or *).\n",
+                      vianame);
+               break;
+           }
+       }
+   }
+}
+
+static void
+parse_chain(const char *arg, char *vianame)
+{
+   int vialen = strlen(arg);
+
+   memset(vianame, 0, HIPAC_CHAIN_NAME_MAX_LEN);
+
+   if (vialen + 1 > HIPAC_CHAIN_NAME_MAX_LEN)
+       exit_error(PARAMETER_PROBLEM,
+              "chain name `%s' must be shorter than "
+              "HIPAC_CHAIN_NAME_MAX_LEN (%i)", 
+              arg, HIPAC_CHAIN_NAME_MAX_LEN);
+   
+   if (vialen == 0)
+       exit_error(PARAMETER_PROBLEM,
+              "no chain name specified");
+   strcpy(vianame, arg);
+}
+
+static int
+parse_state(const char *state, size_t strlen, unsigned int* sinfo)
+{
+   if (strncasecmp(state, "INVALID", strlen) == 0)
+       *sinfo = NFHP_STATE_INVALID;
+   else if (strncasecmp(state, "UNTRACKED", strlen) == 0)
+       *sinfo = NFHP_STATE_UNTRACKED;
+   else if (strncasecmp(state, "NEW", strlen) == 0)
+       *sinfo = NFHP_STATE_NEW;
+   else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
+       *sinfo = NFHP_STATE_ESTABLISHED;
+   else if (strncasecmp(state, "RELATED", strlen) == 0)
+       *sinfo = NFHP_STATE_RELATED;
+   else
+       return 0;
+   return 1;
+}
+
+static void
+parse_states(const char *arg, struct dim_match *rule)
+
+{
+   unsigned int left, right;
+   const char *comma;
+
+   if ((comma = strchr(arg, ',')) != NULL) {
+       if (comma == arg || !parse_state(arg, comma - arg, &left))
+                        exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+       if (strlen(comma + 1) == 0 || 
+           !parse_state(comma + 1, strlen(comma + 1), &right))
+           exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+       if (left == right) {
+           rule[DIMID_STATE].left = 
+               rule[DIMID_STATE].right = left; 
+       } else if ((left == NFHP_STATE_ESTABLISHED &&
+               right == NFHP_STATE_RELATED) ||
+              (left  == NFHP_STATE_RELATED &&
+               right == NFHP_STATE_ESTABLISHED)) {
+           rule[DIMID_STATE].left = NFHP_STATE_ESTABLISHED;
+           rule[DIMID_STATE].right = NFHP_STATE_RELATED;
+       } else {
+           exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+       }
+   } else {
+       if (strlen(arg) == 0 || 
+           !parse_state(arg, strlen(arg), &left))
+           exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+       rule[DIMID_STATE].left =
+           rule[DIMID_STATE].right = left;
+   }
+}
+
+
+
+static struct in_addr *
+hbo_parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+   struct in_addr *addrp, *addrptmp;
+   int i;
+
+   if ((addrptmp = dotted_to_addr(name)) != NULL ||
+       (addrptmp = network_to_addr(name)) != NULL) {
+       addrp = fw_malloc(sizeof(struct in_addr));
+       addrp->s_addr = ntohl(addrptmp->s_addr);
+       *naddrs = 1;
+       return addrp;
+   }
+   if ((addrp = host_to_addr(name, naddrs)) != NULL){
+       for (i = 0; i < *naddrs; i++)
+           addrp[i].s_addr = ntohl(addrp[i].s_addr);
+       return addrp;
+   }
+
+   exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+   return (struct in_addr *) NULL;
+}
+
+static struct in_addr *
+hbo_parse_mask(char *mask)
+{
+   static struct in_addr maskaddr;
+   struct in_addr *addrp;
+   unsigned int bits;
+   int i;
+   u_int32_t f;
+   
+   if ((addrp = dotted_to_addr(mask)) != NULL){
+       addrp->s_addr = ntohl(addrp->s_addr);
+       if (addrp->s_addr == 0xFFFFFFFFL){
+           return addrp;
+       }
+       i = 32;
+       f = 0xFFFFFFFEL;
+       while (--i >= 0 && addrp->s_addr != f)
+           f <<= 1;
+       if (i >= 0)
+           return addrp;
+       else
+           exit_error(PARAMETER_PROBLEM,
+                  "invalid mask `%s' specified", mask);
+   }
+   if (string_to_number(mask, 0, 32, &bits) == -1)
+       exit_error(PARAMETER_PROBLEM,
+              "invalid mask `%s' specified", mask);
+   if (bits != 0) {
+       maskaddr.s_addr = (0xFFFFFFFF << (32 - bits));
+       return &maskaddr;
+   }
+
+   maskaddr.s_addr = 0L;
+   return &maskaddr;
+}
+
+void
+hbo_parse_hostnetworkmask(const char *name, const int dimid,
+             struct dim_match *rule, struct in_addr **addrpp,
+             unsigned int *naddrs)
+{
+   struct in_addr *addrp;
+   struct in_addr addr, mask;
+   char buf[256];
+   char *p;
+
+   
+   strncpy(buf, name, sizeof(buf) - 1);
+   buf[sizeof(buf) - 1] = '\0';
+   if ((p = strrchr(buf, '/')) != NULL) {
+       *p = '\0';
+       addrp = hbo_parse_mask(p + 1);
+       mask = *addrp;
+       if (mask.s_addr == 0L)
+           strcpy(buf, "0.0.0.0");
+       addrp = hbo_parse_hostnetwork(buf, naddrs);
+       addr = *addrp;
+       free(addrp);
+       *naddrs = 1;
+       rule[dimid].left = addr.s_addr & mask.s_addr;
+       rule[dimid].right = addr.s_addr | (~mask.s_addr);
+       if ((rule[dimid].left == 0) &&
+           (rule[dimid].right == 0xFFFFFFFF)){
+           *naddrs = 0;
+           return;
+       }
+   } else if ((p = strrchr(buf, ':')) != NULL) {
+       *p = '\0';
+       *naddrs = 1;
+       addrp = hbo_parse_hostnetwork(buf, naddrs);
+       addr = *addrp;
+       free(addrp);
+       if (strcasecmp(buf, p + 1) == 0){
+           mask = addr;
+       } else {
+           addrp = hbo_parse_hostnetwork(p + 1, naddrs);
+           mask = *addrp;
+           free(addrp);
+           if (addr.s_addr > mask.s_addr)
+               exit_error(PARAMETER_PROBLEM,
+                      "Range Error: start value > "
+                      "end value\n");
+           if ((addr.s_addr == 0) &&
+               (mask.s_addr == 0xFFFFFFFF)){
+               *naddrs = 0;
+               return;
+           }
+       }
+           rule[dimid].left = addr.s_addr;
+       rule[dimid].right = mask.s_addr;
+   } else {
+       addrp = hbo_parse_hostnetwork(buf, naddrs);
+       rule[dimid].left = addrp->s_addr;
+       rule[dimid].right = addrp->s_addr;
+       if (*naddrs > 1)
+           *addrpp = addrp;
+       else
+           free(addrp);
+   }
+   set_option(&options, 0, dimid);
+}
+
+static int
+service_to_port(const char *name)
+{
+   struct servent *service;
+
+   if ((service = getservbyname(name, NULL)) != NULL)
+       return ntohs((unsigned short) service->s_port);
+
+   return -1;
+}
+
+static int
+parse_port(const char *port)
+{
+   unsigned int portnum;
+
+   if (string_to_number(port, 0, 65535, &portnum) != -1 ||
+       (portnum = service_to_port(port)) != -1)
+       return (int )portnum;
+
+   exit_error(PARAMETER_PROBLEM,
+          "invalid port/service `%s' specified", port);
+   return 0;
+}
+
+static void
+parse_ports(const char *portstring,  const int dimid, struct dim_match *rule)
+{
+   char *buffer;
+   char *cp;
+
+   buffer = strdup(portstring);
+   if ((cp = strchr(buffer, ':')) == NULL)
+        rule[dimid].left =
+            rule[dimid].right = parse_port(buffer);
+   else {
+       *cp = '\0';
+       cp++;
+
+       rule[dimid].left = buffer[0] ? parse_port(buffer) : 0;
+       rule[dimid].right = cp[0] ? parse_port(cp) : 0xFFFF;
+
+       if (rule[dimid].left > rule[dimid].right)
+           exit_error(PARAMETER_PROBLEM,
+                  "invalid portrange (min > max)");
+   }
+   free(buffer);
+   if ((rule[dimid].left == 0) && 
+       (rule[dimid].right == 65535))
+       return;
+   set_option(&options, 0, dimid);
+}
+
+static int
+parse_ttl(const char *ttlstring)
+{
+   unsigned int ttl;
+
+   if (string_to_number(ttlstring, 0, 255, &ttl) != -1)
+       return (int )ttl;
+
+   exit_error(PARAMETER_PROBLEM,
+          "invalid TTL `%s' specified", ttlstring);
+   return 0;
+}
+
+static void
+parse_ttls(const char *ttlstring,  struct dim_match *rule)
+{
+   char *buffer;
+   char *cp;
+
+   buffer = strdup(ttlstring);
+   if ((cp = strchr(buffer, ':')) == NULL)
+       rule[DIMID_TTL].left =
+                        rule[DIMID_TTL].right = parse_ttl(buffer);
+   else {
+       *cp = '\0';
+       cp++;
+
+       rule[DIMID_TTL].left = buffer[0] ? parse_ttl(buffer) : 0;
+       rule[DIMID_TTL].right = cp[0] ? parse_ttl(cp) : 0xFF;
+
+       if (rule[DIMID_TTL].left > rule[DIMID_TTL].right)
+           exit_error(PARAMETER_PROBLEM,
+                  "invalid TTL range (min > max)");
+       if ((rule[DIMID_TTL].left == 0) &&
+           (rule[DIMID_TTL].right == 0xFF)){
+           free(buffer);
+           return;
+       }
+   }
+   free(buffer);
+   set_option(&options, 0, DIMID_TTL);
+}
+
+static void
+parse_syn(const char *flag, struct dim_match *rule)
+{
+   if (strcmp(flag, "SYN") == 0) {
+       rule[DIMID_SYN].left = 0;
+       rule[DIMID_SYN].right = 0;
+       return;
+   } else if (strcmp(flag, "NOT-SYN") == 0){
+       rule[DIMID_SYN].left = 1;
+       rule[DIMID_SYN].right = 65535;
+       return;
+   } else
+       exit_error(PARAMETER_PROBLEM,
+                  "Unknown TCP flag `%s'", flag);
+}
+
+static void
+parse_icmp(const char *icmptype, struct dim_match *rule)
+{
+   unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names);
+   unsigned int match = limit;
+   unsigned int i;
+
+   for (i = 0; i < limit; i++) {
+       if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
+           == 0) {
+           if (match != limit)
+               exit_error(PARAMETER_PROBLEM,
+                      "Ambiguous ICMP type `%s':"
+                      " `%s' or `%s'?",
+                      icmptype,
+                      icmp_codes[match].name,
+                      icmp_codes[i].name);
+           match = i;
+       }
+   }
+
+   if (match != limit) {
+       rule[DIMID_ICMP_TYPE].left = (icmp_codes[match].type << 8) + 
+           icmp_codes[match].code_min;
+       rule[DIMID_ICMP_TYPE].right = (icmp_codes[match].type << 8) +
+           icmp_codes[match].code_max;
+   } else {
+       char *slash;
+       char buffer[strlen(icmptype) + 1];
+       unsigned int type, code;
+
+       strcpy(buffer, icmptype);
+       slash = strchr(buffer, '/');
+
+       if (slash)
+           *slash = '\0';
+
+       if (string_to_number(buffer, 0, 255, &type) == -1)
+           exit_error(PARAMETER_PROBLEM,
+                  "Invalid ICMP type `%s'\n", buffer);
+       if (slash) {
+           if (string_to_number(slash+1, 0, 255, &code) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                      "Invalid ICMP code `%s'\n",
+                      slash+1);
+           rule[DIMID_ICMP_TYPE].left =
+               rule[DIMID_ICMP_TYPE].right =
+               (type << 8) + code;
+       } else {
+           rule[DIMID_ICMP_TYPE].left = type << 8;
+           rule[DIMID_ICMP_TYPE].right = (type << 8) + 0xFF;
+       }
+   }
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+   int i, k;
+
+   for (i = 0; i < NUMBER_OF_OPT; i++) {
+       if (options & (1<<i)) {
+           if ((1<<i) == OPT_VERBOSE
+               || (1<<i) == OPT_DEBUG
+               || (1<<i) == OPT_DEBUGN)
+               continue;
+           if ((1<<i) == OPT_NUMERIC
+               || (1<<i) == OPT_LINENUMBERS)
+               k = 0;
+           else k = 1;
+           
+           if (commands_v_options[command][k] == 'x')
+               exit_error(PARAMETER_PROBLEM,
+                      "Illegal option `-%s'"
+                      " with this command",
+                      optflags[i]);
+       }
+   }
+}
+
+static inline void
+fill_hipac_matches(struct nfhp_cmd *message, struct dim_match *rule)
+{
+   int i;
+   message->rule.r.native_mct = 0;
+   for (i = 0; i < NUMBER_OF_DIM; i++){
+       if (!(options & dimid_to_option(i)))
+           continue;
+       message->rule.m[message->rule.r.native_mct].dimid = i;
+       message->rule.m[message->rule.r.native_mct].invert = 
+           rule[i].invert;
+       message->rule.m[message->rule.r.native_mct].left =
+           rule[i].left;
+       message->rule.m[message->rule.r.native_mct].right =
+           rule[i].right;
+       message->rule.r.native_mct++;
+   }
+}
+
+static char *
+port_to_service(int port)
+{
+   struct servent *service;
+
+   if ((service = getservbyport(htons(port), NULL)))
+       return service->s_name;
+
+   return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+   char *service;
+
+   if (numeric || (service = port_to_service(port)) == NULL)
+       printf("%u", port);
+   else
+       printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int32_t min, u_int32_t max, u_int8_t invert,
+       int numeric)
+{
+   printf("%s", name);
+   if (min == max) {
+       printf(": ");
+       if (invert)
+           printf("!");
+       print_port(min, numeric);
+   } else {
+       
+       printf("s: ");
+       if (invert)
+           printf("!");
+       print_port(min, numeric);
+       printf(":");
+       print_port(max, numeric);
+   }
+   printf("  ");
+}
+
+static void
+print_ttls(u_int32_t min, u_int32_t max)
+{
+   printf("ttl");
+   if (min == max) {
+       printf(": ");
+       printf("%u", min);
+   } else {
+       
+       printf("s: %u:%u", min, max);
+   }
+   printf("  ");
+}
+
+static void 
+print_icmptype(u_int8_t type, u_int8_t code_min, u_int8_t code_max,
+          int numeric)
+{
+   printf("icmp: ");
+   if (!numeric) {
+       unsigned int i;
+
+       for (i = 0;
+            i < sizeof(icmp_codes)/sizeof(struct icmp_names);
+            i++) {
+           if (icmp_codes[i].type == type
+               && icmp_codes[i].code_min == code_min
+               && icmp_codes[i].code_max == code_max)
+               break;
+       }
+
+       if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) {
+           printf("%s ", icmp_codes[i].name);
+           return;
+       }
+   }
+
+   printf("type %u ", type);
+   if (code_min == 0 && code_max == 0xFF)
+       printf(" ");
+   else if (code_min == code_max)
+       printf("code %u ", code_min);
+   else
+       printf("codes %u-%u ", code_min, code_max);
+   printf(" ");
+}
+
+static void
+print_syn(u_int32_t min, u_int32_t max)
+{
+   printf("flags: ");
+   if (!min & !max) {
+       printf("syn");
+   } else if ((min == 1) & (max == 65535)){
+       printf("not-syn");
+   } else
+          printf("unknown");
+   printf("  ");
+}
+
+static void
+print_state(unsigned int left, unsigned int right,
+       u_int8_t invert)
+{
+   printf("state: ");
+   if (invert)
+       printf("!");
+   if (left != right)
+       printf("ESTABLISHED,RELATED");
+   else if (left ==  NFHP_STATE_INVALID)
+       printf("INVALID");
+   else if (left == NFHP_STATE_NEW)
+       printf("NEW");
+   else if (left == NFHP_STATE_RELATED)
+       printf("RELATED");
+   else if (left == NFHP_STATE_ESTABLISHED)
+       printf("ESTABLISHED");
+   else if (left == NFHP_STATE_UNTRACKED)
+       printf("UNTRACKED");
+   printf("  ");
+}
+
+static char *
+print_ip(int dimid, int numeric, struct dim_match *rule)
+{
+   u_int32_t left = htonl(rule[dimid].left);
+   u_int32_t right = htonl(rule[dimid].right);
+   u_int32_t mask;
+   int i = 32;
+   u_int32_t f = 0xFFFFFFFEL;
+   static char buf[BUFSIZ];
+
+   if (left == right){
+       if (numeric)
+           sprintf(buf, "%s", 
+               addr_to_dotted((struct in_addr *) &left));
+       else sprintf(buf, "%s", 
+                addr_to_anyname((struct in_addr*) &left));
+       return buf;
+   }
+       
+   mask = ~(rule[dimid].right - rule[dimid].left);
+   while (--i >= 0 && mask != f)
+       f <<= 1;
+
+   if (i >= 0 && (((rule[dimid].left) & f) == rule[dimid].left)){
+       if (numeric)
+           sprintf(buf, "%s/%d", 
+               addr_to_dotted((struct in_addr *) &left), i);
+       else sprintf(buf, "%s/%d", 
+                addr_to_anyname((struct in_addr*) &left), i);
+       return buf;
+   } else {
+       if (numeric){
+           sprintf(buf, "%s:", 
+               addr_to_dotted((struct in_addr *) &left));
+           strcat(buf, addr_to_dotted((struct in_addr *) &right));
+       }else {
+           sprintf(buf, "%s:", 
+               addr_to_anyname((struct in_addr*) &left));
+           strcat(buf, addr_to_anyname((struct in_addr*) &right));
+       }
+       return buf;
+   }
+}
+
+static void
+print_icmptypes(void)
+{
+   unsigned int i;
+   printf("Valid ICMP Types:");
+
+   for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) {
+       if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
+           if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
+               && (icmp_codes[i].code_max
+               == icmp_codes[i-1].code_max))
+               printf(" (%s)", icmp_codes[i].name);
+           else
+               printf("\n   %s", icmp_codes[i].name);
+       }
+       else
+           printf("\n%s", icmp_codes[i].name);
+   }
+   printf("\n");
+   exit(0);
+}
+
+static char *
+proto_to_name(u_int8_t proto)
+{
+   unsigned int i;
+
+   if (proto) {
+       struct protoent *pent = getprotobynumber(proto);
+       if (pent)
+           return pent->p_name;
+   }
+
+   for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+       if (chain_protos[i].num == proto)
+           return chain_protos[i].name;
+
+   return NULL;
+}
+
+static void
+print_rule(struct nfhp_list_rule* pt_rule, int is_nfhp_rule)
+{
+   int i;
+   unsigned int rule_options = 0;
+   struct dim_match rule[NUMBER_OF_DIM + 1];
+   struct iptables_target *target = NULL;
+   struct ipt_entry_target *t = NULL;
+
+   for (i = 0; i < pt_rule->r.native_mct; i++){
+       set_option(&rule_options, 0, pt_rule->r.first_match[i].dimid);
+       rule[pt_rule->r.first_match[i].dimid].invert =
+           pt_rule->r.first_match[i].invert;
+       rule[pt_rule->r.first_match[i].dimid].left =
+           pt_rule->r.first_match[i].left;
+       rule[pt_rule->r.first_match[i].dimid].right =
+           pt_rule->r.first_match[i].right;
+   }
+
+   if (options & OPT_LINENUMBERS)
+       printf("%-5u ", pt_rule->r.pos);
+
+   if (pt_rule->r.action == TARGET_ACCEPT){
+       printf("%-9s ", IPTC_LABEL_ACCEPT);
+   } else if (pt_rule->r.action == TARGET_DROP){
+       printf("%-9s ", IPTC_LABEL_DROP);
+   } else if (pt_rule->r.action == TARGET_RETURN){
+       printf("%-9s ", IPTC_LABEL_RETURN);
+   } else if (pt_rule->r.action == TARGET_NONE){
+       printf("%-9s ", " ");
+   } else if (pt_rule->r.action == TARGET_DUMMY){
+       printf("%-9s ", "DUMMY");
+   } else if (HAS_CHAIN_TARGET(&pt_rule->r)){
+       if (is_nfhp_rule){
+           printf("%-9s ", 
+                  (char *) ((struct nfhp_rule *) pt_rule)->e
+                  + ((struct nfhp_rule *) 
+                 pt_rule)->e->target_offset);
+       } else {
+           printf("%-9s ", CHAIN_TARGET(&pt_rule->r));
+       }
+   } else if (HAS_IPT_TARGET(&pt_rule->r)){
+   } else {
+       printf("%-9s ", "UNKNOWN");
+   }
+
+   if (rule_options & dimid_to_option(DIMID_PROTO)){
+       char *pname = proto_to_name(rule[DIMID_PROTO].left);
+       printf("%c", rule[DIMID_PROTO].invert ? '!' : ' ');
+       if (rule[DIMID_PROTO].left != rule[DIMID_PROTO].right)
+           printf("%-1hu:%-2hu ", rule[DIMID_PROTO].left,
+                  rule[DIMID_PROTO].right);
+       else if (pname)
+           printf("%-4s ", pname);
+       else
+           printf("%-4hu ", rule[DIMID_PROTO].left);
+   } else printf(" %-4s ", "all");
+
+   if (rule_options & dimid_to_option(DIMID_FRAGMENT)){
+       if (rule[DIMID_FRAGMENT].left == 0)
+           printf("%-3s ", "!f");
+       else printf("%-3s ", "-f");
+   } else printf("%-3s ", "--");
+
+   if (options & OPT_VERBOSE){
+       if (*pt_rule->indev){
+           if (rule_options & dimid_to_option(DIMID_INIFACE))
+               printf("%c", rule[DIMID_INIFACE].invert ? 
+                      '!' : ' ');
+           printf("%-6s ", pt_rule->indev);
+       } else  {
+           if (options & (OPT_NUMERIC | OPT_DEBUGN))
+               printf(" %-6s ", "*");
+           else printf(" %-6s ", "any");
+       }
+       if (*pt_rule->outdev){
+           if (rule_options & dimid_to_option(DIMID_OUTIFACE))
+               printf("%c", rule[DIMID_OUTIFACE].invert ? 
+                      '!' : ' ');
+           printf("%-6s ", pt_rule->outdev);
+       } else {
+           if (options & (OPT_NUMERIC | OPT_DEBUGN))
+               printf(" %-6s ", "*");
+           else printf(" %-6s ", "any");
+       }   
+   }
+   
+   if (rule_options & dimid_to_option(DIMID_SRC_IP)){
+       printf("%c", rule[DIMID_SRC_IP].invert ? '!' : ' ');
+       printf("%-19s ", print_ip(DIMID_SRC_IP, 
+                     options & 
+                     (OPT_NUMERIC | OPT_DEBUGN), rule));
+   } else {
+       if (options & (OPT_NUMERIC | OPT_DEBUGN))
+           printf(" %-19s ", "0.0.0.0/0");
+       else    printf(" %-19s ", "anywhere");
+   }
+
+   if (rule_options & dimid_to_option(DIMID_DEST_IP)){
+       printf("%c", rule[DIMID_DEST_IP].invert ? '!' : ' ');
+       printf("%-19s ", print_ip(DIMID_DEST_IP, 
+                     options & 
+                     (OPT_NUMERIC | OPT_DEBUGN), rule));
+   } else {
+       if (options & (OPT_NUMERIC | OPT_DEBUGN))
+           printf(" %-19s ", "0.0.0.0/0");
+       else    printf(" %-19s ", "anywhere");
+   }   
+   
+   if (rule_options & dimid_to_option(DIMID_SPORT))
+       print_ports("spt", rule[DIMID_SPORT].left,
+               rule[DIMID_SPORT].right,
+               rule[DIMID_SPORT].invert, 
+               options & (OPT_NUMERIC | OPT_DEBUGN));
+
+   if (rule_options & dimid_to_option(DIMID_DPORT))
+       print_ports("dpt", rule[DIMID_DPORT].left,
+               rule[DIMID_DPORT].right, 
+               rule[DIMID_DPORT].invert, 
+               options & (OPT_NUMERIC | OPT_DEBUGN));
+
+   if (rule_options & dimid_to_option(DIMID_SYN))
+       print_syn(rule[DIMID_SYN].left, rule[DIMID_SYN].right);
+
+   if (rule_options & dimid_to_option(DIMID_ICMP_TYPE))
+       print_icmptype(rule[DIMID_ICMP_TYPE].left >> 8,
+                  rule[DIMID_ICMP_TYPE].left & 0xFF,
+                  rule[DIMID_ICMP_TYPE].right & 0xFF,
+                  options & (OPT_NUMERIC | OPT_DEBUGN));
+   
+   if (rule_options & dimid_to_option(DIMID_STATE))
+       print_state(rule[DIMID_STATE].left, rule[DIMID_STATE].right,
+               rule[DIMID_STATE].invert);
+   
+   if (rule_options & dimid_to_option(DIMID_TTL))
+       print_ttls(rule[DIMID_TTL].left, rule[DIMID_TTL].right);
+ 
+   if (target || HAS_IPT_MATCH(&pt_rule->r)){  
+       struct ipt_ip ip;
+       memset(&ip, 0, sizeof(ip));
+       if (rule_options & dimid_to_option(DIMID_PROTO))
+           if (!rule[DIMID_PROTO].invert)
+               ip.proto = rule[DIMID_PROTO].left;
+       if (HAS_IPT_MATCH(&pt_rule->r)){
+       }
+       if (target) {
+           if (target->print)
+               target->print(&ip, t, 
+                         FMT_PRINT_RULE & 
+                         (FMT_NUMERIC && 
+                          (options & 
+                       (OPT_NUMERIC | OPT_DEBUGN))));
+       } else if (t && t->u.target_size != sizeof(*t))
+           printf("[%u bytes of unknown target data] ",
+                  (unsigned int)(t->u.target_size - sizeof(*t)));
+   }
+
+   if (options & OPT_DEBUGN || options & OPT_DEBUG) {
+       printf("||");
+       for (i = 0; i < NUMBER_OF_DIM; i++) {
+           if (rule_options & dimid_to_option(i)) {
+               printf(" Dim%d:%s%u", i, rule[i].invert ? 
+                      "!" : "", rule[i].left);
+               if (rule[i].left != rule[i].right)
+                   printf("-%u", rule[i].right);
+           }
+       }
+   }
+
+   printf("\n");
+   
+   
+}
+
+static void
+print_header(const char *chain, int policy)
+{
+   printf("Chain %s ", chain);
+   if (policy == TARGET_ACCEPT)
+       printf("(policy %s)\n", IPTC_LABEL_ACCEPT);
+   else if (policy == TARGET_DROP)
+       printf("(policy %s)\n", IPTC_LABEL_DROP);
+   else printf("(unknown policy)\n");
+
+   if (options & OPT_LINENUMBERS)
+       printf("%-5s ", "num");
+
+   printf("%-9s ", "target");
+   printf(" %-4s ", "prot");
+   printf("%-3s ", "opt");
+   if (options & OPT_VERBOSE) {
+       printf(" %-6s ", "in");
+       printf(" %-6s ", "out");
+   }
+   printf(" %-19s ", "source");
+   printf(" %-19s ", "destination");
+   printf("\n");
+}
+
+static int 
+process_list(const void *data, int len)
+{
+   static u_int32_t rulenum = 0;
+   static u_int32_t chain_rulenum = 0;
+   const struct nfhp_list_chain *current_chain;
+   const void *p = data;
+
+   if (listing_start) {
+       current_chain = (struct nfhp_list_chain *) p;
+       rulenum = 0;
+       chain_rulenum = current_chain->rule_num;
+       listing_start = 0;
+       p += sizeof(struct nfhp_list_chain);
+       print_header(current_chain->label,
+                current_chain->policy);
+   }
+   while (p < data + len) {
+       if (rulenum < chain_rulenum) {
+           int rule_size =
+               IPT_ALIGN(((struct nfhp_list_rule *) p)->r.size
+                     + offsetof(struct nfhp_list_rule,
+                            r));
+           if (len - (p - data) < rule_size) {
+               fprintf(stderr, "%s: incomplete rule\n",
+                   __FUNCTION__);
+               return -1;
+           }
+           print_rule((struct nfhp_list_rule *) p, 0);
+           p += rule_size;
+           rulenum++;
+       } else {
+           if (len - (p - data) <
+               sizeof(struct nfhp_list_chain)) {
+               fprintf(stderr, "%s: incomplete chain\n",
+                   __FUNCTION__);
+               return -1;
+           }
+           printf("\n");
+           current_chain = (struct nfhp_list_chain *) p;
+           rulenum = 0;
+           chain_rulenum = current_chain->rule_num;
+           p += sizeof(struct nfhp_list_chain);
+           print_header(current_chain->label,
+                    current_chain->policy);
+           
+       }
+   }
+   return 0;
+}
+
+static int
+handle_error(int ret, int err)
+{
+   if (ret == 0 && err == 0) {
+       return 0;
+   }
+
+   switch (ret) {
+   case 0:
+       fprintf(stderr, "%s\n", nlhp_error(err));
+       break;
+   case -1:
+       perror("");
+       break;
+   case -2:
+       fprintf(stderr, "Read timeout occured\n");
+       break;
+   case -3:
+       fprintf(stderr, "Not enough memory available\n");
+       break;
+   case -4:
+       fprintf(stderr, "List handler failed\n");
+       break;
+   }
+   return ret;
+}
+
+int
+do_command(int argc, char *argv[])
+{
+   int c, invert = 0, ipt_match_num = 0, cmd_return, ret = 1, err;
+   struct iptables_target *ipt_target = NULL;
+   u_int32_t ipt_matches_size = 0;
+   char *protocol = NULL;
+   char jumpto[HIPAC_CHAIN_NAME_MAX_LEN] = "";
+   struct nfhp_cmd* message = NULL;
+   struct dim_match rule[NUMBER_OF_DIM];
+   struct in_addr *s_ips = NULL, *d_ips = NULL;
+   unsigned int ns_addrs = 0, nd_addrs = 0;
+   struct iptables_target *t;
+   struct iptables_match *m;
+   struct iptables_rule_match *matches = NULL;
+   struct iptables_rule_match *matchp;
+       
+        /* re-set optind to 0 in case do_command gets called
+    * a second time */
+   optind = 0;
+
+   /* clear mflags in case do_command gets called a second time
+    * (we clear the global list of all matches for security)*/
+   for (m = iptables_matches; m; m = m->next) {
+       m->mflags = 0;
+   }
+   for (t = iptables_targets; t; t = t->next) {
+       t->tflags = 0;
+       t->used = 0;
+   }
+   memset(rule, 0, sizeof(struct dim_match) * NUMBER_OF_DIM);
+   message = nlhp_new_cmd(CMD_NONE, NULL);
+   if (!message)
+       perror("nf-hipac: low memory");
+   
+   message->rule.r.action = TARGET_NONE;
+        
+        /* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+   opterr = 0;
+   while ((c = getopt_long (argc, argv, 
+                "-A:D:I:R:L::F::Z::N:X::E:P:t:m:s:d:p:i:j:c:o:fvnxVh::",
+                opts, NULL)) != -1){
+       switch(c){
+       case 'A':
+           add_command(message, CMD_APPEND, invert);
+           parse_chain(optarg, message->chain.label);
+           break;
+       case 'D':
+           if (optind < argc && argv[optind][0] != '-'
+               && argv[optind][0] != '!') {
+               add_command(message, CMD_DELETE_POS, invert);
+               parse_chain(optarg, message->chain.label);
+               parse_rulenumber(argv[optind++],
+                        &message->rule.r.pos);
+           } else {
+               add_command(message, CMD_DELETE_RULE, invert);
+               parse_chain(optarg, message->chain.label);
+           }
+           break;
+       case 'R':
+           add_command(message, CMD_REPLACE, invert);
+           parse_chain(optarg, message->chain.label);
+           if (optind < argc && argv[optind][0] != '-'
+               && argv[optind][0] != '!')
+               parse_rulenumber(argv[optind++],
+                        &message->rule.r.pos);
+           else
+               exit_error(PARAMETER_PROBLEM,
+                      "-%c requires a rule number",
+                      cmd2char(CMD_REPLACE));
+           break;
+       case 'I':
+           add_command(message, CMD_INSERT, invert);
+           parse_chain(optarg, message->chain.label);
+           if (optind < argc && argv[optind][0] != '-'
+               && argv[optind][0] != '!')
+               parse_rulenumber(argv[optind++],
+                        &message->rule.r.pos);
+           else message->rule.r.pos = 1;
+           break;
+       case 'L':
+           add_command(message, CMD_LIST, invert);
+           if (optarg) parse_chain(optarg, message->chain.label);
+           else if (optind < argc && argv[optind][0] != '-'
+                && argv[optind][0] != '!')
+               parse_chain(argv[optind++], 
+                       message->chain.label);
+           break;
+       case 'F':
+           add_command(message, CMD_FLUSH, invert);
+           if (optarg) parse_chain(optarg, message->chain.label);
+           else if (optind < argc && argv[optind][0] != '-'
+                && argv[optind][0] != '!')
+               parse_chain(argv[optind++],
+                       message->chain.label);
+           break;
+       case 'N':
+           add_command(message, CMD_NEW_CHAIN, invert);
+           if (optarg && (*optarg == '-' || *optarg == '!'))
+               exit_error(PARAMETER_PROBLEM,
+                      "chain name not allowed to start "
+                      "with `%c'\n", *optarg);
+           parse_chain(optarg, message->chain.label);
+           if (strcmp(optarg, IPTC_LABEL_ACCEPT) == 0
+               || strcmp(optarg, IPTC_LABEL_DROP) == 0
+               || strcmp(optarg, IPTC_LABEL_QUEUE) == 0
+               || strcmp(optarg, IPTC_LABEL_RETURN) == 0
+               || find_target(optarg, TRY_LOAD))
+               exit_error(PARAMETER_PROBLEM,
+                      "chain name may not clash "
+                      "with target name\n");
+           break;
+       case 'X':
+           add_command(message, CMD_DELETE_CHAIN, invert);
+           if (optarg) parse_chain(optarg, message->chain.label);
+           else if (optind < argc && argv[optind][0] != '-'
+                && argv[optind][0] != '!')
+               parse_chain(argv[optind++], 
+                       message->chain.label);
+           break;
+       case 'E':
+           add_command(message, CMD_RENAME_CHAIN, invert);
+           parse_chain(optarg, message->chain.label);
+           if (optind < argc && argv[optind][0] != '-'
+               && argv[optind][0] != '!'){
+               parse_chain(argv[optind++], 
+                       message->chain.newlabel);
+               if (strcmp(message->chain.newlabel, 
+                      IPTC_LABEL_ACCEPT) == 0
+                   || strcmp(message->chain.newlabel,
+                         IPTC_LABEL_DROP) == 0
+                   || strcmp(message->chain.newlabel,
+                         IPTC_LABEL_QUEUE) == 0
+                   || strcmp(message->chain.newlabel,
+                         IPTC_LABEL_RETURN) == 0
+                   || find_target(message->chain.newlabel, 
+                          TRY_LOAD))
+                   exit_error(PARAMETER_PROBLEM,
+                          "chain name may not clash "
+                          "with target name\n");
+           } else
+               exit_error(PARAMETER_PROBLEM,
+                          "-%c requires old-chain-name and "
+                      "new-chain-name",
+                       cmd2char(CMD_RENAME_CHAIN));
+       
+           break;
+       case 'P':
+           add_command(message, CMD_SET_POLICY, invert);
+           parse_chain(optarg, message->chain.label);
+           if (optind < argc && argv[optind][0] != '-'
+               && argv[optind][0] != '!')
+               parse_policy(argv[optind++],
+                        &message->chain.policy);
+           else
+               exit_error(PARAMETER_PROBLEM,
+                      "-%c requires a chain and a policy",
+                      cmd2char(CMD_SET_POLICY));
+           break;
+       case 'h':
+           if (!optarg)
+               optarg = argv[optind];
+           if ((optarg ) && (strcasecmp(optarg, "icmp") == 0))
+               print_icmptypes();
+           exit_printhelp(matches);
+                
+       /* Options */   
+       case 's':
+           check_inverse(optarg, &invert, &optind, argc);
+           hbo_parse_hostnetworkmask(argv[optind-1],
+                         DIMID_SRC_IP, rule,
+                         &s_ips, &ns_addrs);
+               rule[DIMID_SRC_IP].invert = invert;
+           break;
+
+       case 'd':
+           check_inverse(optarg, &invert, &optind, argc);
+           hbo_parse_hostnetworkmask(argv[optind-1],
+                         DIMID_DEST_IP, rule,
+                         &d_ips, &nd_addrs);
+                   rule[DIMID_DEST_IP].invert = invert;
+           break;
+       case 'p':
+           check_inverse(optarg, &invert, &optind, argc);
+           for (protocol = argv[optind-1]; *protocol; protocol++)
+               *protocol = tolower(*protocol);
+           parse_native_protocol(argv[optind-1], rule);
+           rule[DIMID_PROTO].invert = invert;
+           if (!invert && 
+               (rule[DIMID_PROTO].left ==
+                rule[DIMID_PROTO].right)){
+               message->rule.e->ip.proto = 
+                   rule[DIMID_PROTO].left;
+           }
+           break;
+       case 'f':
+           set_option(&options, 0, DIMID_FRAGMENT);
+           if (invert){
+               rule[DIMID_FRAGMENT].left =
+                   rule[DIMID_FRAGMENT].right = 0;
+           } else {
+               rule[DIMID_FRAGMENT].left = 1;
+               rule[DIMID_FRAGMENT].right = 65535;
+           }
+           break;
+       case 'j':
+           if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "cannot have ! before -%s",
+                      optflags[OPT_VERBOSE]);
+           set_option(&options, OPT_JUMP, 0);
+           parse_target(optarg, jumpto);
+           if (strcmp(optarg, IPTC_LABEL_ACCEPT) == 0){
+               message->rule.r.action = TARGET_ACCEPT;
+               break;
+           } else if (strcmp(optarg, IPTC_LABEL_DROP) == 0){
+               message->rule.r.action = TARGET_DROP;
+               break;
+           } else if (strcmp(optarg, IPTC_LABEL_RETURN) == 0){
+               message->rule.r.action = TARGET_RETURN;
+               break;
+           } 
+           ipt_target = find_target(jumpto, TRY_LOAD);
+           if (ipt_target) {
+               size_t size;
+               size = IPT_ALIGN(sizeof(struct ipt_entry_target))
+                   + ipt_target->size;
+
+               ipt_target->t = fw_calloc(1, size);
+               ipt_target->t->u.target_size = size;
+               strcpy(ipt_target->t->u.user.name, jumpto);
+               set_revision(ipt_target->t->u.user.name,
+                        ipt_target->revision);
+               if (ipt_target->init != NULL)
+                   ipt_target->init(ipt_target->t,
+                            &message->rule.e
+                            ->nfcache);
+               opts = merge_options(opts,
+                            ipt_target->extra_opts, 
+                            &ipt_target->option_offset);
+               message->rule.r.action = TARGET_EXEC;
+           } else {
+               message->rule.r.action = TARGET_CHAIN;
+           }
+           break;
+       case 'i':
+           check_inverse(optarg, &invert, &optind, argc);
+           set_option(&options, 0, DIMID_INIFACE);
+           parse_native_interface(argv[optind-1], 
+                          message->rule.indev);
+           rule[DIMID_INIFACE].invert = invert;
+           break;
+       case 'o':
+           check_inverse(optarg, &invert, &optind, argc);
+           set_option(&options, 0, DIMID_OUTIFACE);
+           parse_native_interface(argv[optind-1], 
+                          message->rule.outdev);
+           rule[DIMID_OUTIFACE].invert = invert;
+           break;
+       case 'v':
+           set_option(&options, OPT_VERBOSE, 0);
+           if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "cannot have ! before -%s",
+                      optflags[OPT_VERBOSE]);
+           break;
+       case 'V':   
+           printf("%s v%s\n",
+                  program_name, program_version);
+           exit(0);
+       case 'n':
+           set_option(&options, OPT_NUMERIC, 0);
+           if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "cannot have ! before -%s",
+                      optflags[OPT_NUMERIC]);
+           break;
+       case 't':
+           if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "unexpected ! flag before --table");
+           if (strcmp(argv[optind-1], "filter") != 0)
+               exit_error(PARAMETER_PROBLEM,
+                      "no other table than "
+                      "filter supported");
+           break;
+       case 'm': 
+       {
+           int i, found = 0;
+           if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "unexpected ! flag before --match");
+           for (i = 0; i < HIPAC_NATIVE_MATCHES; i++){
+               if (strcmp(optarg, native_matches[i]) == 0){
+                   found = 1;
+                   break;
+               }
+           }
+           // 涓嶅啀鍔犺浇iptables妯″潡
+       }
+           break;
+       case 9:
+           // --debug matched
+           set_option(&options, OPT_DEBUG, 0);
+           if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "cannot have ! before -%s",
+                      optflags[OPT_DEBUG]);
+           break;
+       case 10:
+           // --debugn matched
+           set_option(&options, OPT_DEBUGN, 0);
+           if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "cannot have ! before -%s",
+                      optflags[OPT_DEBUGN]);
+           break;
+           
+       case 11:    
+           // --line matched
+           set_option(&options, OPT_LINENUMBERS, 0);
+           if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "cannot have ! before -%s",
+                      optflags[OPT_LINENUMBERS]);
+           break;
+       case 12:
+           // --state matched
+           check_inverse(optarg, &invert, &optind, argc);
+           set_option(&options, 0, DIMID_STATE);
+           parse_states(argv[optind-1], rule);
+           rule[DIMID_STATE].invert = invert;
+           break;
+       case 14:
+           // --ttl matched
+           check_inverse(optarg, &invert, &optind, argc);
+           if (invert)
+               exit_error(PARAMETER_PROBLEM, 
+                      "no ! with --ttl allowed");
+           parse_ttls(argv[optind-1], rule);
+           break;
+       case 15:
+           // --icmp-type matched
+           check_inverse(optarg, &invert, &optind, argc);
+           if (invert)
+               exit_error(PARAMETER_PROBLEM, 
+                      "no ! with --icmp-type allowed");
+           set_option(&options, 0, DIMID_ICMP_TYPE);
+           parse_icmp(argv[optind-1], rule);
+           break;
+       case 16:
+                        // --source-port matched
+           if (rule[DIMID_PROTO].invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "--sport only works with " 
+                      "protocols tcp or udp, so you can't"
+                      " use ! on the protocol field");
+           check_inverse(optarg, &invert, &optind, argc);
+           if ((rule[DIMID_PROTO].left == IPPROTO_TCP  &&
+                rule[DIMID_PROTO].right == IPPROTO_TCP) ||
+               (rule[DIMID_PROTO].left == IPPROTO_UDP &&
+                rule[DIMID_PROTO].right == IPPROTO_UDP)){
+               parse_ports(argv[optind-1], 
+                       DIMID_SPORT, rule);
+               rule[DIMID_SPORT].invert = invert;
+           } else exit_error(PARAMETER_PROBLEM,
+                     "Can't use --sport without "
+                     "protocol. You have to specify " 
+                     "protocol (tcp or udp) first");
+               
+           break;
+       case 17:
+           // --dest-port matched
+           if (rule[DIMID_PROTO].invert)
+               exit_error(PARAMETER_PROBLEM,
+                      "--dport only works with " 
+                      "protocols tcp or udp, so you can't"
+                      " use ! on the protocol field");
+           check_inverse(optarg, &invert, &optind, argc);
+           if ((rule[DIMID_PROTO].left == IPPROTO_TCP &&
+               rule[DIMID_PROTO].right == IPPROTO_TCP) ||
+               (rule[DIMID_PROTO].left == IPPROTO_UDP &&
+                rule[DIMID_PROTO].right == IPPROTO_UDP)){
+               parse_ports(argv[optind-1], 
+                       DIMID_DPORT, rule);
+               rule[DIMID_DPORT].invert = invert;
+           } else exit_error(PARAMETER_PROBLEM,
+                     "Can't use --dport without "
+                     "protocol. You have to specify " 
+                     "protocol (tcp or udp) first");
+           break;
+       case 18:
+           // --syn matched
+           check_inverse(optarg, &invert, &optind, argc);
+           if (invert)
+               exit_error(PARAMETER_PROBLEM, 
+                      "no ! with --syn allowed");  
+           set_option(&options, 0, DIMID_SYN);
+           parse_syn("SYN", rule);
+           break;
+       case 19:
+           // --not-syn matched
+           check_inverse(optarg, &invert, &optind, argc);
+           if (invert)
+               exit_error(PARAMETER_PROBLEM, 
+                      "no ! with --not-syn allowed");
+           set_option(&options, 0, DIMID_SYN);
+           parse_syn("NOT-SYN", rule);
+           break;
+       case 1: 
+           if (optarg[0] == '!' && optarg[1] == '\0') {
+               if (invert)
+                   exit_error(PARAMETER_PROBLEM,
+                          "multiple consecutive ! not"
+                          " allowed");
+               invert = TRUE;
+               optarg[0] = '\0';
+               continue;
+           }
+           printf("Bad argument `%s'\n", optarg);
+           exit_tryhelp(2);
+           break;
+       default: /* non option */
+           if (!ipt_target
+               || !(ipt_target->parse(c -
+                          ipt_target->option_offset,
+                          argv, invert,
+                          &ipt_target->tflags,
+                          message->rule.e,
+                          &ipt_target->t))) {
+               for (matchp = matches; matchp;
+                    matchp = matchp->next) {
+                   if (matchp->match
+                       ->parse(c - matchp->match
+                           ->option_offset,
+                            argv, invert,
+                            &matchp->match->mflags,
+                            message->rule.e,
+                            &message->rule.e->nfcache,
+                            &matchp->match->m))
+                       break;
+               }
+               m = matchp ? matchp->match : NULL;
+               
+               if (!m)
+                   exit_error(PARAMETER_PROBLEM,
+                          "Unknown arg `%s'",
+                          argv[optind-1]);
+           }
+
+
+       }
+       invert = FALSE;
+   }
+
+   for (matchp = matches; matchp; matchp = matchp->next) {
+       ipt_match_num++;
+       matchp->match->final_check(matchp->match->mflags);
+   }
+
+   if (ipt_target)
+       ipt_target->final_check(ipt_target->tflags);
+
+   if (optind < argc)
+       exit_error(PARAMETER_PROBLEM,
+              "unknown arguments found on commandline");
+   
+   if (!message->cmd)
+       exit_error(PARAMETER_PROBLEM, "no command specified");
+   
+   generic_opt_check(message->cmd, options);
+
+   if ((ns_addrs > 1) && (rule[DIMID_SRC_IP].invert))
+       exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+              " source or destination IP addresses");
+
+   if ((nd_addrs > 1) && (rule[DIMID_DEST_IP].invert))
+       exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+              " source or destination IP addresses");
+
+   if (message->cmd == CMD_REPLACE && (ns_addrs > 1 || nd_addrs > 1))
+       exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+              "specify a unique address");
+
+   if (message->cmd == CMD_APPEND
+       || message->cmd == CMD_DELETE_RULE
+       || message->cmd == CMD_DELETE_POS
+       || message->cmd == CMD_INSERT
+       || message->cmd == CMD_REPLACE) {
+       
+       
+               if (strcasecmp(message->chain.label, "INPUT") == 0) {
+           /* -o not valid with incoming packets. */
+           if (options & dimid_to_option(DIMID_OUTIFACE))
+               exit_error(PARAMETER_PROBLEM,
+                      "Can't use -%s with %s",
+                      optflags[NUMBER_OF_OPT 
+                           + DIMID_OUTIFACE],
+                      message->chain.label);
+       }
+
+       if (strcasecmp(message->chain.label, "OUTPUT") == 0) {
+           /* -i not valid with outgoing packets */
+           if (options & dimid_to_option(DIMID_INIFACE))
+               exit_error(PARAMETER_PROBLEM,
+                      "Can't use -%s with %s",
+                      optflags[NUMBER_OF_OPT + 
+                           DIMID_INIFACE],
+                      message->chain.label);
+       }
+       
+       if ((options & dimid_to_option(DIMID_SYN))
+           && !((options & dimid_to_option(DIMID_PROTO)) &&
+            (rule[DIMID_PROTO].left == IPPROTO_TCP) &&
+            (rule[DIMID_PROTO].right == IPPROTO_TCP) &&
+            (rule[DIMID_PROTO].invert == 0)))
+           exit_error(PARAMETER_PROBLEM,
+                  "Can use --syn or --not-syn only "
+                  "with protocol tcp");
+               
+       if ((options & dimid_to_option(DIMID_ICMP_TYPE))
+           && !((options & dimid_to_option(DIMID_PROTO)) &&
+            (rule[DIMID_PROTO].left == IPPROTO_ICMP) &&
+            (rule[DIMID_PROTO].right == IPPROTO_ICMP) &&
+            (rule[DIMID_PROTO].invert == 0))) 
+           exit_error(PARAMETER_PROBLEM,
+                  "Can use --icmp-type only "
+                  "with protocol icmp");
+       
+       if ((options & dimid_to_option(DIMID_SYN))
+           || (options & dimid_to_option(DIMID_SPORT))
+           || (options & dimid_to_option(DIMID_DPORT))
+           || (options & dimid_to_option(DIMID_ICMP_TYPE))) {
+           if (options & dimid_to_option(DIMID_FRAGMENT)) {
+               if (rule[DIMID_FRAGMENT].left == 1){
+                   exit_error(PARAMETER_PROBLEM,
+                          "syn, port and "
+                          "icmp type matches are "
+                          "not allowed on "
+                          "fragments");
+               }
+           } else {
+               set_option(&options, 0, DIMID_FRAGMENT);
+               rule[DIMID_FRAGMENT].left = 0;
+               rule[DIMID_FRAGMENT].right = 0;
+           }
+       }
+   }
+   
+   if (message->cmd != CMD_LIST){
+       int i, j;
+       int cmd_size = 0;
+       struct nfhp_cmd* new_message = NULL;
+       if (HAS_CHAIN_TARGET(&message->rule.r)){
+           strcpy((void *) message->rule.e  
+                  + IPT_ALIGN(sizeof(struct ipt_entry))
+                  + ipt_matches_size, jumpto);
+           cmd_size = sizeof(struct nfhp_cmd)
+               + IPT_ALIGN(sizeof(struct ipt_entry))
+               + ipt_matches_size
+               + strlen(jumpto) + 1;
+           message->rule.e->target_offset =
+               IPT_ALIGN(sizeof(struct ipt_entry))
+               + ipt_matches_size;
+           message->rule.e->next_offset =
+               IPT_ALIGN(sizeof(struct ipt_entry))
+               + ipt_matches_size
+               + strlen(jumpto) + 1;
+       } else if (HAS_IPT_TARGET(&message->rule.r)){
+       } else if (ipt_match_num){
+           cmd_size =  sizeof(struct nfhp_cmd)
+               + IPT_ALIGN(sizeof(struct ipt_entry))
+               + ipt_matches_size;
+           message->rule.e->target_offset =
+               IPT_ALIGN(sizeof(struct ipt_entry))
+               + ipt_matches_size;
+           message->rule.e->next_offset =
+               IPT_ALIGN(sizeof(struct ipt_entry))
+               + ipt_matches_size;
+       } else {
+           cmd_size = sizeof(struct nfhp_cmd);
+       }
+
+       message->rule.r.match_offset = ipt_matches_size;
+
+       if ((message->cmd == CMD_DELETE_RULE) 
+           && HAS_IPT_ENTRY(&message->rule.r)){
+           u_int16_t *cmp;
+           struct ipt_entry_match *k;
+           cmp = (void *) message->rule.e
+               + message->rule.e->next_offset;
+           k = (void *) message->rule.e
+               + IPT_ALIGN(sizeof(struct ipt_entry));
+           cmd_size += ipt_match_num * (sizeof(u_int16_t));
+           for (i = 0; i < ipt_match_num; i++){
+               for (matchp = matches; matchp;
+                    matchp = matchp->next) {
+                   //if (!matchp->match->used)
+                   //continue;
+                   //fixme: NULL AHNUNG
+                   if (strcmp(k->u.user.name,
+                          matchp->match->name) == 0){
+                       cmp[i] = matchp->
+                           match->userspacesize;
+                       k = NEXT_IPT_MATCH(k);
+                       break;
+                   }
+               }
+           }
+           if (HAS_IPT_TARGET(&message->rule.r)){
+           }
+       }
+       
+       if ((options & OPT_DEBUG) || 
+           (options & OPT_DEBUGN)){
+           print_header(message->chain.label, 
+                    message->chain.policy);
+       }
+       
+       if (ns_addrs < 1) ns_addrs = 1;
+       if (nd_addrs < 1) nd_addrs = 1;
+       for (i = 0; i < ns_addrs; i++)
+           for (j = 0; j < nd_addrs; j++){
+               if (s_ips)
+                   rule[DIMID_SRC_IP].left =
+                       rule[DIMID_SRC_IP].right =
+                       (s_ips[i]).s_addr;
+               if (d_ips)
+                   rule[DIMID_DEST_IP].left = 
+                       rule[DIMID_DEST_IP].right = 
+                       (d_ips[j]).s_addr;
+               
+               fill_hipac_matches(message, rule);
+               
+               if (((i + 1) * (j + 1))
+                   < (ns_addrs * nd_addrs)){
+                   new_message = 
+                       nlhp_copy_cmd(message,
+                                 cmd_size);
+                   if (!new_message)
+                       perror("nf-hipac: low memory");
+               }
+
+               if ((options & OPT_DEBUG) || 
+                   (options & OPT_DEBUGN)){
+                   print_rule((struct nfhp_list_rule *)
+                          &(message->rule), 1);
+                   nlhp_free_cmd(message);
+               } else {
+                   if (options & OPT_VERBOSE)
+                       print_rule(
+                           (struct 
+                            nfhp_list_rule *)
+                           &(message->rule), 1);
+                   cmd_return = nlhp_send_cmd(message,
+                                  cmd_size,
+                                  NULL, &err);
+                   nlhp_free_cmd(message);
+                   ret = handle_error(cmd_return, err);
+                   if (ret != 0)
+                       return ret;
+               }
+               message = new_message;
+           }
+   } else {
+       listing_start = 1;
+       cmd_return = nlhp_send_cmd(message, sizeof(struct nfhp_cmd),
+                      process_list, &err);
+       ret = handle_error(cmd_return, err);
+   }
+
+   clear_rule_matches(&matches);
+
+   if (s_ips) 
+       free(s_ips);
+   if (d_ips)
+       free(d_ips);
+
+   free_opts(1);
+   
+   options = 0;
+
+   return ret;
+}
+
diff -urN nf-hipac/user/nfhp_com.h nfhipac/user/nfhp_com.h
--- nf-hipac/user/nfhp_com.h    1970-01-01 08:00:00.000000000 +0800
+++ nfhipac/user/nfhp_com.h 2014-11-21 12:41:57.000000000 +0800
@@ -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

原文链接: https://blog.csdn.net/dog250/article/details/41383897

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    可编译易用的模块化nf-HiPAC移植成功_hipack rlp

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/408514

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 上午10:54
下一篇 2023年4月26日 上午10:55

相关推荐