From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jeho-Park Subject: modification to the nf-hipac-0.9.1 patch Date: Mon, 29 May 2006 13:56:01 +0900 Message-ID: <447A7EE1.3060508@kernelproject.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------090408020704070107000703" Return-path: To: netfilter-devel@lists.netfilter.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: netfilter-devel-bounces@lists.netfilter.org Errors-To: netfilter-devel-bounces@lists.netfilter.org List-Id: netfilter-devel.vger.kernel.org This is a multi-part message in MIME format. --------------090408020704070107000703 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit hi there nf-hipac list looks dead so i post here. recently i modified the nf-hipac-0.9.1.patch to apply it to the linux-2.6.16.16. please refer to my attached patch file i can't sure it is useful but i hope so (^^ -- Jeho-Park or --------------090408020704070107000703 Content-Type: text/x-patch; name="nf-hipac-0.9.1-to-linux-2.6.16.16.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="nf-hipac-0.9.1-to-linux-2.6.16.16.patch" diff -uNr linux-2.6.16.16/include/linux/netfilter_ipv4/ip_tables.h ../linux-2.6.16.16/include/linux/netfilter_ipv4/ip_tables.h --- linux-2.6.16.16/include/linux/netfilter_ipv4/ip_tables.h 2006-05-11 10:56:24.000000000 +0900 +++ ../linux-2.6.16.16/include/linux/netfilter_ipv4/ip_tables.h 2006-05-28 23:29:05.000000000 +0900 @@ -336,6 +336,8 @@ /* net/sched/ipt.c: Gimme access to your targets! Gets target->me. */ extern struct ipt_target *ipt_find_target(const char *name, u8 revision); +/* Gimme access to your matches too! */ +extern struct ipt_match *ipt_find_match(const char *name, u8 revision); /* Standard entry. */ struct ipt_standard diff -uNr linux-2.6.16.16/include/linux/netfilter_ipv4/ipt_sctp.h ../linux-2.6.16.16/include/linux/netfilter_ipv4/ipt_sctp.h --- linux-2.6.16.16/include/linux/netfilter_ipv4/ipt_sctp.h 2006-05-11 10:56:24.000000000 +0900 +++ ../linux-2.6.16.16/include/linux/netfilter_ipv4/ipt_sctp.h 2006-05-28 23:33:28.000000000 +0900 @@ -6,7 +6,7 @@ #define IPT_SCTP_CHUNK_TYPES 0x04 #define IPT_SCTP_VALID_FLAGS 0x07 - +#define ELEMCOUNT(x) (sizeof(x)/sizeof(x[0])) struct ipt_sctp_flag_info { u_int8_t chunktype; diff -uNr linux-2.6.16.16/net/ipv4/netfilter/Kconfig ../linux-2.6.16.16/net/ipv4/netfilter/Kconfig --- linux-2.6.16.16/net/ipv4/netfilter/Kconfig 2006-05-11 10:56:24.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/Kconfig 2006-05-28 23:13:50.000000000 +0900 @@ -577,6 +577,42 @@ If you want to compile it as a module, say M here and read . If unsure, say `N'. +config IP_NF_HIPAC + tristate 'nf-HiPAC support (High Performance Packet Classification)' + depends on IP_NF_IPTABLES + help + nf-HiPAC is a high performance packet classification framework on + top of netfilter. It is based on a novel classification algorithm + that is very much superior to the linear classication algorithm + implemented by iptables. It provides highly efficient packet + matching which is especially useful when large rulesets and/or high + bandwidth networks are involved. Ruleset updates are submitted + dynamically to the kernel via netlink on a per rule basis. + In addition to its native matches (e.g. ip, proto, port match) + nf-HiPAC allows the usage of iptables matches and targets and thus + provides the same flexibility as iptables. Furthermore the semantics + and construction of a ruleset is identical to the iptables way so + that the internal representation is completely transparent to the + user. + Basically, you can think of nf-HiPAC as an alternative, optimized + iptables filter table. Note that it cannot be used for packet + mangling or NAT but you can still adopt iptables' mangle or nat + table for that purpose since nf-HiPAC and iptables can be used + together at the same time. + +config IP_NF_HIPAC_SINGLE_PATH + bool 'Single path optimization' + depends on IP_NF_HIPAC + help + This optimization will significantly improve the performance of + nf-HiPAC but may lead to an unacceptable memory usage. Whether memory + consumption becomes a problem depends on your rule set. You can + monitor the memory used by NF-HIPAC via /proc/net/nf-hipac/info. + In almost all scenarios the performance will already be more than good + enough without this optimization. + + If unsure, say N. + # ARP tables config IP_NF_ARPTABLES tristate "ARP tables support" diff -uNr linux-2.6.16.16/net/ipv4/netfilter/Makefile ../linux-2.6.16.16/net/ipv4/netfilter/Makefile --- linux-2.6.16.16/net/ipv4/netfilter/Makefile 2006-05-11 10:56:24.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/Makefile 2006-05-28 23:15:43.000000000 +0900 @@ -75,6 +75,9 @@ obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o +# nf-hipac/Makefile +obj-$(CONFIG_IP_NF_HIPAC) += nf-hipac/ + # generic ARP tables obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o diff -uNr linux-2.6.16.16/net/ipv4/netfilter/ip_tables.c ../linux-2.6.16.16/net/ipv4/netfilter/ip_tables.c --- linux-2.6.16.16/net/ipv4/netfilter/ip_tables.c 2006-05-11 10:56:24.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/ip_tables.c 2006-05-28 23:27:49.000000000 +0900 @@ -457,6 +457,31 @@ return 1; } +struct ipt_match *ipt_find_match(const char *name, u8 revision) +{ + struct ipt_match *match; + + match = try_then_request_module(xt_find_match(AF_INET, name, revision), + "ipt_%s", name); + if (IS_ERR(match) || !match) + return NULL; + + return match; +} + +struct ipt_target *ipt_find_target(const char *name, u8 revision) +{ + struct ipt_target *target; + + target = try_then_request_module(xt_find_target(AF_INET, name, revision), + "ipt_%s", name); + if (IS_ERR(target) || !target) { + return NULL; + } + + return target; +} + static inline int cleanup_match(struct ipt_entry_match *m, unsigned int *i) { @@ -1386,5 +1411,7 @@ EXPORT_SYMBOL(ipt_register_table); EXPORT_SYMBOL(ipt_unregister_table); EXPORT_SYMBOL(ipt_do_table); +EXPORT_SYMBOL(ipt_find_match); /* nf-hipac use these two function jeho-park */ +EXPORT_SYMBOL(ipt_find_target); module_init(init); module_exit(fini); diff -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/Makefile ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/Makefile --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/Makefile 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/Makefile 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,33 @@ +# +# Makefile for nf-hipac on top of IPv4. +# + +obj-$(CONFIG_IP_NF_HIPAC) += nf_hipac.o +nf_hipac-objs := ihash.o global.o rlp.o dimtree.o hipac.o nfhp_dev.o nfhp_proc.o nfhp_mod.o + +ifdef CONFIG_IP_NF_HIPAC +ifeq ($(CONFIG_IP_NF_CONNTRACK),m) +obj-m += nf_hipac_cthelp.o +EXTRA_CFLAGS += -D CONNTRACK_MODULE +endif +endif + +ifdef CONFIG_IP_NF_HIPAC_SINGLE_PATH +EXTRA_CFLAGS += -D SINGLE_PATH +endif + +ifneq ($(ARCH), alpha) +ifneq ($(ARCH), ia64) +ifneq ($(ARCH), ppc64) +ifneq ($(ARCH), s390x) +ifneq ($(ARCH), sh64) +ifneq ($(ARCH), sparc64) +ifneq ($(ARCH), x86_64) +EXTRA_CFLAGS += -D BIT32_ARCH +endif +endif +endif +endif +endif +endif +endif diff -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/dimtree.c ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/dimtree.c --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/dimtree.c 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/dimtree.c 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,4307 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * 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, " => 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, " \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: \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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/dimtree.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/dimtree.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/dimtree.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/dimtree.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,280 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/global.c ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/global.c --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/global.c 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/global.c 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,964 @@ +/* + * High performance packet classification + * + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/global.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/global.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/global.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/global.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,387 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * Licenced under the GNU General Public Licence, version 2. + */ + + +#ifndef _GLOBAL_H +#define _GLOBAL_H + +#include "mode.h" +#include "hipac.h" // hipac_error + +#define INITIAL_MEMHASH_LEN 64 +#define MEMHASH_AVRG_ELEM_PER_BUCKET 15 +#define INITIAL_NEWSPEC_LEN 1024 +#define NEWSPEC_AVRG_ELEM_PER_BUCKET 3 + +#ifdef DEBUG +# define DPRINT(type, str, args...) if ((type) & hipac_debug) \ + printk(str , ## args) +#else +# define DPRINT(type, str, args...) do {} while (0) +#endif + +/* the single space before the last ',' is vital to make this macro work the + expected way because of some idiosyncrasy of gcc */ +#ifdef DEBUG +# define MSG(type, str, args...) \ + printk(type "%-15s : %-30s : %6d : " str "\n", __FILE__, __FUNCTION__, \ + __LINE__ , ## args) +#else +# define MSG(type, str, args...) \ + printk(type "%s:%s:%d: " str "\n", __FILE__, __FUNCTION__, \ + __LINE__ , ## args) +#endif + +#define ERR(str, args...) MSG(KERN_ERR, str , ## args) +#define WARN(str, args...) MSG(KERN_WARNING, str , ## args) +#define NOTICE(str, args...) MSG(KERN_NOTICE, str , ## args) +#define DBG(str, args...) MSG(KERN_DEBUG, str , ## args) +#define ARG_MSG MSG(KERN_ERR, "function arguments invalid") + +#define ARG_ERR \ +do { \ + MSG(KERN_ERR, "function arguments invalid"); \ + return HE_IMPOSSIBLE_CONDITION; \ +} while (0) + +#define IMPOSSIBLE_CONDITION(str, args...) \ +do { \ + MSG(KERN_ERR, str , ## args); \ + return HE_IMPOSSIBLE_CONDITION; \ +} while (0) + + + +/* generic header for dimtree rules, elementary intervals and rlps */ +struct gen_spec +{ + unsigned rlp : 1; +}; + +/* dimid to bittype array */ +extern __u8 *dim2btype; + +/* match executor function */ +extern hipac_match_exec_t match_fn; + +/* target executor function */ +extern hipac_target_exec_t target_fn; + +/* dimension extractor function */ +extern hipac_extract_t *extract_fn; + + + +/* + * memory management wrappers + */ + +/* upper bound for memory consumption in bytes */ +extern __u64 mem_max; + +/* current memory consumption in bytes in terms of how much + has been requested */ +extern __u64 mem_current_tight; + +/* current memory consumption in bytes in terms of how much + has actually been allocated */ +extern __u64 mem_current_real; + +/* do_add indicates whether mem_current_tight and mem_current_real + should be updated or not */ +void * +hp_alloc(__u32 size, int do_add); + +void +hp_free(void *p); + +void * +hp_realloc(void *p, __u32 newsize); + +/* add the number of bytes requested for p to *size_tight and the number + of bytes allocated for p to *size_real; if p is NULL, size_tight and + size_real are not modified; + possible errors: HE_IMPOSSIBLE_CONDITION */ +hipac_error +hp_size(void *p, __u64 *size_real, __u64 *size_tight); + +/* internal memhash is freed; if it is not empty a warning is printed */ +void +hp_mem_exit(void); + + + +/* + * statistical distributions + */ + +/* initialize statistical distribution dist of length len, i.e. set it to 0 */ +static inline void +stat_distribution_init(__u32 dist[], __u32 len) +{ + if (unlikely(dist == NULL || len == 0)) { + ARG_MSG; + return; + } + memset(dist, 0, len * sizeof(*dist)); +} + +/* dist is an array of length len representing a statistical distribution; + val is added to dist */ +void +stat_distribution_add(__u32 dist[], __u32 len, __u32 val); + + + +/* + * pointer block + */ +struct ptrblock +{ + __u32 len; + void *p[0]; +}; + +/* return new pointer block with p as the only element; do_add indicates + whether mem_current_tight and mem_current_real should be updated or not */ +struct ptrblock * +ptrblock_new(void *p, int do_add); + +static inline void +ptrblock_free(struct ptrblock *b) +{ + hp_free(b); +} + +static inline __u32 +ptrblock_size(const struct ptrblock *b) +{ + if (unlikely(b == NULL)) { + ARG_MSG; + return 0; + } + return sizeof(*b) + b->len * sizeof(*b->p); +} + +/* returns 1 if b1 and b2 are equal and 0 otherwise; b1->len or b2->len might + be 0 in order allow equality test on embedded ptrblocks */ +int +ptrblock_eq(const struct ptrblock *b1, const struct ptrblock *b2); + +/* clone b and store the result in clone; the memory for clone is allocated + via hp_alloc if necessary and do_add is 1; b might be NULL; + possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_clone(struct ptrblock *b, struct ptrblock **clone); + +/* insert p into b at position pos; if *b is NULL and pos is 0 + ptrblock_new(p, 1) is called and the result is assigned to b; + possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_insert(struct ptrblock **b, void *p, __u32 pos); + +/* insert p into (struct ptrblock *) ((char *) *o + ptrblock_offset) + at position pos; o is assumed to end after the embedded ptrblock; + hp_realloc is used to resize o; + possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_insert_embed(void **o, __u32 ptrblock_offset, void *p, __u32 pos); + +/* append p to b; if *b is NULL ptrblock_new(p, 1) is called and the result + is assigned to b; + possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_append(struct ptrblock **b, void *p); + +/* delete pointer at position pos in b; if b contains only one element and + pos is 0 then *b is freed and NULL is assigned to *b; + possible errors: HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_delete_pos(struct ptrblock **b, __u32 pos); + +/* delete pointer at position pos in + (struct ptrblock *) ((char *) *o + ptrblock_offset); o is assumed to end + after the embedded ptrblock; hp_realloc is used to resize o; + possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_delete_pos_embed(void **o, __u32 ptrblock_offset, __u32 pos); + +/* delete p in b; if p is the only element in b then *b is freed and NULL + is assigned to *b; + possible errors: HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_delete(struct ptrblock **b, void *p); + +/* delete trailing pointer in b; if b contains only one element then *b is + freed and NULL is assigned to *b; + possible errors: HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_delete_tail(struct ptrblock **b); + +/* for all mark->p[i] == NULL: delete the pointer at the position i fom b; + if b is empty after the delete operation NULL is assigned to *b; + note that mark->len must be >= (*b)->len; + possible errors: HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_delete_multi(struct ptrblock **b, const struct ptrblock *mark); + +/* similar to ptrblock_delete_multi: the pointers in b which are NULL are + deleted; + possible errors: HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrblock_delete_null(struct ptrblock **b); + + + +/* + * block of structs + */ +struct strblock +{ + __u32 len, size; + char d[0]; +}; + +#define STRBLOCK_ITH(b, i, cast) ((cast) ((b)->d + (i) * (b)->size)) + +/* return new struct block with s as the only element; size is the size of + the struct pointed to by s in bytes; do_add indicates whether + mem_current_tight and mem_current_real should be updated or not */ +struct strblock * +strblock_new(const void *s, __u32 size, int do_add); + +static inline void +strblock_free(struct strblock *b) +{ + hp_free(b); +} + +static inline __u32 +strblock_size(const struct strblock *b) +{ + if (unlikely(b == NULL)) { + ARG_MSG; + return 0; + } + return sizeof(*b) + b->len * b->size; +} + +/* returns 1 if b1 and b2 are equal and 0 otherwise; eq is an equality test + function for the embedded structs; eq(a, b) returns 1 if a equals to b + and 0 otherwise */ +int +strblock_eq(const struct strblock *b1, const struct strblock *b2, + int (* eq) (void *, void *)); + +/* clone b and store the result in clone; the memory for clone is allocated + via hp_alloc if necessary and do_add is 1; b might be NULL; + possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */ +hipac_error +strblock_clone(struct strblock *b, struct strblock **clone); + +/* insert struct s into b at position pos; if *b is NULL and pos is 0 + strblock_new(s, size, 1) is called and the result is assigned to b; + possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */ +hipac_error +strblock_insert(struct strblock **b, const void *s, __u32 size, __u32 pos); + +/* append struct s to b; if *b is NULL then strblock_new(s, size, 1) is + called and the result is assigned to b; + possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */ +hipac_error +strblock_append(struct strblock **b, const void *s, __u32 size); + +/* delete struct at position pos in b; if b contains only one element and + pos is 0 then *b is freed and NULL is assigned to *b; + possible errors: HE_IMPOSSIBLE_CONDITION */ +hipac_error +strblock_delete_pos(struct strblock **b, __u32 pos); + +/* delete trailing struct in b; if b contains only one element then *b is + freed and NULL is assigned to *b; + possible errors: HE_IMPOSSIBLE_CONDITION */ +hipac_error +strblock_delete_tail(struct strblock **b); + + + +/* + * pointer list + */ +struct ptrlist +{ + struct list_head head; + __u32 len; +}; + +struct ptrlist_entry +{ + struct list_head head; + void *p; +}; + +/* return new empty pointer list or NULL if allocation fails */ +struct ptrlist * +ptrlist_new(void); + +/* return new pointer list entry containing p or NULL if allocation fails */ +struct ptrlist_entry * +ptrlist_new_entry(void *p); + +/* free all entries from the pointer list l */ +void +ptrlist_flush(struct ptrlist *l); + +/* free all entries from the pointer list l and l itself */ +void +ptrlist_free(struct ptrlist *l); + +/* free ptrlist entry */ +static inline void +ptrlist_free_entry(struct ptrlist_entry *e) +{ + if (unlikely(e == NULL)) { + ARG_MSG; + return; + } + list_del(&e->head); + mini_free(e); +} + +/* return 1 if l is empty and 0 otherwise */ +static inline int +ptrlist_is_empty(const struct ptrlist *l) +{ + if (unlikely(l == NULL)) { + ARG_MSG; + return 0; + } + assert((l->len != 0 || l->head.next == &l->head) && + (l->head.next != &l->head || l->len == 0)); + return l->len == 0; +} + +/* add a new pointer list entry containing p to l; if check_dup is not 0 + the new entry is only added if p is not already contained in a list + entry; + possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */ +hipac_error +ptrlist_add(struct ptrlist *l, void *p, int check_dup); + +#endif diff -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/hipac.c ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/hipac.c --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/hipac.c 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/hipac.c 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,3750 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/hipac.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/hipac.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/hipac.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/hipac.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,623 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/ihash.c ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/ihash.c --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/ihash.c 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/ihash.c 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,463 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/ihash.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/ihash.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/ihash.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/ihash.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,285 @@ +/* + * High performance packet classification + * + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/mode.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/mode.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/mode.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/mode.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,240 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * Licenced under the GNU General Public Licence, version 2. + */ + + +#ifndef _MODE_H +#define _MODE_H + +#include // offsetof +#include + +/* maximal number of bytes allocatable by mini_alloc */ +#define MINI_ALLOC_MAX 131072 + + +/* + * NEVER use big_alloc and big_free. Use hp_alloc and hp_free instead. + * The only exceptions to this rule is the implementation of hp_alloc, + * hp_realloc and hp_free. + * + * mini_alloc and mini_free can be used for small (<= MINI_ALLOC_MAX bytes) + * data structures if one wants to avoid the overhead of hp_alloc and hp_free + */ + +static inline unsigned +big_alloc_size(unsigned size) +{ + return size == 0 ? 0 : (((size - 1) + PAGE_SIZE) & ~(PAGE_SIZE - 1)); +} + + +#ifdef __KERNEL__ +/* + * Kernel space + */ +#include +#include +#include +#include // ULONG_MAX +#include // likely, unlikely +#include // smp_num_cpus, cpu_number_map, smp_processor_id +#include // Read Copy Update: sychronize_rcu +#include // __cacheline_aligned +#include // NF_ACCEPT, NF_DROP +#include +#include +#include +#include +#include + +#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 + return 0; +found: + return s; +} + +/* for small amounts of memory only (up to 128 KB) */ +static inline void * +mini_alloc(unsigned size) +{ + if (size > 0 && size <= MINI_ALLOC_MAX) { + return kmalloc(size, GFP_KERNEL); + } + return NULL; +} + +static inline void +mini_free(void *p) +{ + kfree(p); +} + +/* memory is allocated in amounts of multiples of PAGE_SIZE */ +static inline void * +big_alloc(unsigned size) +{ + return vmalloc(size); +} + +static inline void +big_free(void *p) +{ + vfree(p); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) +#define synchronize_rcu(x) synchronize_kernel(x) +#endif + +/* dirty hack to make stuff work with uml (otherwise high_physmem and end_vm + are not defined) */ +#ifdef CONFIG_UML_NET +# undef TOP +# ifdef CONFIG_HOST_2G_2G +# define TOP 0x80000000 +# else +# define TOP 0xc0000000 +# endif +# undef SIZE +# define SIZE ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000) +# undef START +# define START (TOP - SIZE) +# undef VMALLOC_OFFSET +# define VMALLOC_OFFSET (8 * 1024 * 1024) +# undef VMALLOC_START +# define VMALLOC_START (((unsigned long) (START + 32 * 1024 * 1024) + \ + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET - 1)) +static unsigned long high_physmem = START + 32 * 1024 * 1024; +static unsigned long end_vm = VMALLOC_START + 32 * 1024 * 1024; +#endif /* CONFIG_UML_NET */ + + + + +#else /* __KERNEL__ */ +/* + * User space + */ +#include +#if defined(__GLIBC__) && __GLIBC__ == 2 +# include +#else /* libc5 */ +# include +#endif +#include +#include +#include +#include // ULONG_MAX +#include +#include + +/* 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nf_hipac_cthelp.c ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nf_hipac_cthelp.c --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nf_hipac_cthelp.c 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nf_hipac_cthelp.c 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,49 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * Licenced under the GNU General Public Licence, version 2. + */ + + +#include +#include +#include +#include +#include +#include +#include "nfhp_mod.h" + +static int +__init init(void) +{ + need_ip_conntrack(); + if (nfhp_register_cthelp(THIS_MODULE)) { + return -EINVAL; + } + return 0; +} + +static void +__exit fini(void) +{ + nfhp_unregister_cthelp(THIS_MODULE); +} + +module_init(init); +module_exit(fini); +MODULE_AUTHOR("Michael Bellion and Thomas Heinz"); +MODULE_DESCRIPTION("nf-HiPAC - connection tracking dependency helper module"); +MODULE_LICENSE("GPL"); diff -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_com.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_com.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_com.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_com.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,330 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * Licenced under the GNU General Public Licence, version 2. + */ + + +#ifndef _NFHP_COM_H +#define _NFHP_COM_H + +#ifdef __KERNEL__ +# include +# include +# include +#endif +#include +#include +#include "hipac.h" + + +/* a similar line will hopefully make its way into netlink.h */ +#define NETLINK_NFHIPAC 26 +#define NLHP_PROTO NETLINK_NFHIPAC +#define NLHP_TYPE 0xFADE + +/* dimension id's */ +enum { + DIMID_STATE, + DIMID_SRC_IP, + DIMID_DEST_IP, + DIMID_INIFACE, + DIMID_OUTIFACE, + DIMID_PROTO, + DIMID_FRAGMENT, + DIMID_DPORT, + DIMID_SPORT, + DIMID_SYN, + DIMID_ICMP_TYPE, + DIMID_TTL, + NUMBER_OF_DIM +}; + +/* bit types */ +#define BIT_STATE BIT_U16 +#define BIT_SRC_IP BIT_U32 +#define BIT_DEST_IP BIT_U32 +#define BIT_INIFACE BIT_U16 +#define BIT_OUTIFACE BIT_U16 +#define BIT_PROTO BIT_U16 +#define BIT_FRAGMENT BIT_U16 +#define BIT_DPORT BIT_U16 +#define BIT_SPORT BIT_U16 +#define BIT_SYN BIT_U16 +#define BIT_ICMP_TYPE BIT_U16 +#define BIT_TTL BIT_U16 + +/* origin bits */ +#define NFHP_ORIGIN_INPUT 0x1 +#define NFHP_ORIGIN_FORWARD 0x2 +#define NFHP_ORIGIN_OUTPUT 0x4 + +/* hipac_error and nfhipac_error together form the netlink error messages */ +typedef enum +{ + NFHE_INDEX = HE_NEXT_ERROR, // Unable to retrieve unused ifindex + NFHE_NOMSG = HE_NEXT_ERROR - 1, // Incorrect message format + NFHE_CMD = HE_NEXT_ERROR - 2, // Invalid command + NFHE_LABEL = HE_NEXT_ERROR - 3, // Empty chain label + NFHE_NLABEL = HE_NEXT_ERROR - 4, // Empty new chain label + NFHE_POLICY = HE_NEXT_ERROR - 5, // Invalid policy + NFHE_ACTION = HE_NEXT_ERROR - 6, // Invalid action + NFHE_NMCT = HE_NEXT_ERROR - 7, // Invalid native match count + NFHE_IEOFF = HE_NEXT_ERROR - 8, // Invalid target_offset/next_offset + // in ipt_entry + NFHE_SORT = HE_NEXT_ERROR - 9, // Native matches not sorted or + // dimid duplicate + NFHE_MINT = HE_NEXT_ERROR - 10, // Invalid interval in native match + NFHE_DEVA = HE_NEXT_ERROR - 11, // Native interface match but no + // corresponding interface name + NFHE_DEVB = HE_NEXT_ERROR - 12, // Interface name but no corres- + // ponding native interface match + NFHE_FRAG = HE_NEXT_ERROR - 13, // Invalid fragment match + NFHE_PROTO = HE_NEXT_ERROR - 14, // Invalid protocol match + NFHE_SYN = HE_NEXT_ERROR - 15, // Invalid syn match + NFHE_STATE = HE_NEXT_ERROR - 16, // Invalid state match + NFHE_TCP = HE_NEXT_ERROR - 17, // tcp match dependency failure + NFHE_TCPUDP = HE_NEXT_ERROR - 18, // tcp or udp match dependency failure + NFHE_ICMP = HE_NEXT_ERROR - 19, // icmp match dependency failure + NFHE_CMPMIS = HE_NEXT_ERROR - 20, // Missing cmp_len array + NFHE_CMPSH = HE_NEXT_ERROR - 21, // cmp_len array too short + NFHE_CMPLA = HE_NEXT_ERROR - 22, // cmp_len array contains a value + // larger than the corresponding + // ipt match/target size + NFHE_ORIGIN = HE_NEXT_ERROR - 23, // Illegal combination of matches + // (no valid origin) + NFHE_IPTMSZ = HE_NEXT_ERROR - 24, // Invalid ipt match size + NFHE_IPTMCH = HE_NEXT_ERROR - 25, // checkentry fails for ipt match + NFHE_IPTTMI = HE_NEXT_ERROR - 26, // Missing ipt target + NFHE_IPTTSZ = HE_NEXT_ERROR - 27, // Invalid ipt target size + NFHE_IPTTCH = HE_NEXT_ERROR - 28, // checkentry fails for ipt target + NFHE_TOFF = HE_NEXT_ERROR - 29, // Invalid target_offset + NFHE_CHAINE = HE_NEXT_ERROR - 30, // Empty chain name + NFHE_CHAINL = HE_NEXT_ERROR - 31, // Chain name too long + NFHE_CT = HE_NEXT_ERROR - 32, // Kernel does not have support for + // connection tracking, please recompile + NFHE_CTHELP = HE_NEXT_ERROR - 33, // Unable to load connection tracking + // helper module (nf_hipac_cthelp.o) + NFHE_ILL = HE_NEXT_ERROR - 34, // Illegal condition + NFHE_IMPL = HE_NEXT_ERROR - 35, // Feature not yet implemented + NFHE_SYSOFF = HE_NEXT_ERROR - 36 // - offset for system errno's - +} nfhipac_error; + +/* errno is positive */ +#define ERRNO_TO_NFHE(e) (NFHE_SYSOFF - e) +#define NFHE_TO_ERRNO(e) (NFHE_SYSOFF - e) + +/* connection tracking states */ +typedef enum +{ + NFHP_STATE_ESTABLISHED, + NFHP_STATE_RELATED, + NFHP_STATE_NEW, + NFHP_STATE_INVALID, + NFHP_STATE_UNTRACKED, + NFHP_STATE_NUM_VALID = NFHP_STATE_INVALID +} nfhp_state; + +/* netlink commands */ +#define CMD_NONE 0 +#define CMD_MIN 1 +#define CMD_APPEND 1 +#define CMD_INSERT 2 +#define CMD_DELETE_RULE 3 +#define CMD_DELETE_POS 4 +#define CMD_REPLACE 5 +#define CMD_FLUSH 6 +#define CMD_NEW_CHAIN 7 +#define CMD_DELETE_CHAIN 8 +#define CMD_RENAME_CHAIN 9 +#define CMD_SET_POLICY 10 +#define CMD_LIST 11 +#define CMD_MAX 11 + +struct nfhp_rule +{ + char indev[IFNAMSIZ]; + char outdev[IFNAMSIZ]; + struct hipac_rule r; + struct hipac_match m[NUMBER_OF_DIM]; // == r.first_match + /* we cannot use aligned(__alignof__(u_int64_t)) instead of + aligned(8) because of incompatibilities in gcc versions */ + struct ipt_entry e[0] __attribute__((aligned(8))); +}; + +struct nfhp_chain +{ + char label[HIPAC_CHAIN_NAME_MAX_LEN]; + char newlabel[HIPAC_CHAIN_NAME_MAX_LEN]; + u_int8_t policy; +}; + +/* universal macros which can be used for USER <-> KERNEL (both directions) */ +#define HAS_IPT_MATCH(r) ((r)->match_offset > 0) +#define HAS_IPT_TARGET(r) ((r)->action == TARGET_EXEC) +#define HAS_CHAIN_TARGET(r) ((r)->action == TARGET_CHAIN) +#define NEXT_IPT_MATCH(m) ((struct ipt_entry_match *) \ + ((char *) (m) + (m)->u.match_size)) + + +/* + * netlink communication: USER -> KERNEL + */ + +/* command sent to kernel; only the necessary parts (depending on the command + type) must be filled in; + + this is how a nfhp_cmd really looks like: + -------------------------------------------- + | nfhp_cmd struct | + |------------------------------------------| + | ipt_entry | + |------------------------------------------| + | ipt_entry_match 1 | + |------------------------------------------| + | . . . | + |------------------------------------------| + | ipt_entry_match m | + |------------------------------------------| + | ipt_entry_target | + | or | + | chain label | + |------------------------------------------| + | cmp_len array of size m | + | or m + 1 if ipt_entry_target available | + -------------------------------------------- + + ipt_entry, ipt_entry_matches, ipt_entry_target / chain label and cmp_len are + optional; here are the rules defining their presence: + 1) if the rule action is TARGET_EXEC there is an ipt_entry_target + 2) if the rule action is TARGET_CHAIN there is a chain label + 3) if there is an ipt_entry_match or ipt_entry_target or chain label there + is an ipt_entry + 4) if there is an ipt_entry and cmd is CMD_DELETE_RULE there is cmp_len + + => the smallest command simply consists of the nfhp_cmd struct + + struct nfhp_cmd contains an embedded struct hipac_rule; set its member + match_offset to a value > 0 if there is at least one ipt_entry_match; + otherwise it must be 0; you don't have to specify the following members: + size, origin, target_offset + + if ipt_entry exists you only have to specify the following members: + target_offset, next_offset + + note: - the iptables_matches and the iptables_target must be aligned with + the IPT_ALIGN macro +*/ +struct nfhp_cmd +{ + u_int32_t cmd; + struct nfhp_chain chain; + struct nfhp_rule rule; +}; + +/* macros to access nfhp_cmd; hr is a pointer to the embedded hipac_rule */ +#define HAS_IPT_ENTRY(hr) (HAS_IPT_MATCH(hr) || HAS_IPT_TARGET(hr) || \ + HAS_CHAIN_TARGET(hr)) +#define HAS_CMP_LEN(cmd, hr) ((cmd) == CMD_DELETE_RULE && \ + (HAS_IPT_MATCH(hr) || HAS_IPT_TARGET(hr))) +#define NFHP_RULE(hr) ((struct nfhp_rule *) \ + ((char *) (hr) - (unsigned long) \ + (&((struct nfhp_rule *) 0)->r))) +#define IPT_ENTRY(hr) (NFHP_RULE(hr)->e) +#define FIRST_IPT_MATCH_IE(hr) ((struct ipt_entry_match *) \ + NFHP_RULE(hr)->e->elems) +#define IPT_ENTRY_END(hr) FIRST_IPT_MATCH_IE(hr) +#define IPT_TARGET_IE(hr) ((struct ipt_entry_target *) \ + ((char *) NFHP_RULE(hr)->e + \ + NFHP_RULE(hr)->e->target_offset)) +#define IPT_MATCH_END_IE(r) ((struct ipt_entry_match *) IPT_TARGET_IE(r)) +#define CHAIN_TARGET_IE(hr) ((char *) IPT_TARGET_IE(hr)) +#define CMP_LEN(hr) ((u16 *) ((char *) IPT_ENTRY(hr) + \ + IPT_ENTRY(hr)->next_offset)) + + + +/* + * netlink communication: KERNEL -> USER + */ + +/* + in reply to a CMD_LIST command the kernel sends a series of packets to + the userspace; each packet is filled as much as possible so that the + number of packets being transfered is reduced to a minimum; + in case of an error which can happen sometime during the + transmission a packet containing the error number is sent (int32_t); + the data sent to the userspace is organized in the following way: + |struct nfhp_list_chain (chain 1)|rule 1|padding|...|rule n_1|padding| + | . . . . | + |struct nfhp_list_chain (chain k)|rule 1|padding|...|rule n_k|padding| + the rules are of the type struct nfhp_rule; + + this is how a nfhp_list_rule really looks like: + -------------------------------------------- + | nfhp_list_rule struct | + |------------------------------------------| + | hipac_match 1 | + |------------------------------------------| + | . . . | + |------------------------------------------| + | hipac_match n | + |------------------------------------------| + | ipt_entry_match 1 | + |------------------------------------------| + | . . . | + |------------------------------------------| + | ipt_entry_match m | + |------------------------------------------| + | ipt_entry_target | + | or | + | chain label | + -------------------------------------------- + + the number of hipac_matches depends on native_mct (member of hipac_rule); + there is neither ipt_entry nor cmp_len; + + IMPORTANT: - there might be a padding between two consecutive rules + in order to meet the alignment requirements for rules which + contain 64 bit members; so you have to use the IPT_ALIGN + macro to jump to the next rule; note that there is no padding + after a chain because it contains 64 bit members which + enforce the strictest alignment on the system +*/ +struct nfhp_list_chain +{ + char label[HIPAC_CHAIN_NAME_MAX_LEN]; + u_int8_t policy; + u_int32_t rule_num; +}; + +struct nfhp_list_rule +{ + char indev[IFNAMSIZ]; + char outdev[IFNAMSIZ]; + struct hipac_rule r; +}; + +/* these macros together with the universal macros can be used to access + nfhp_list_rule */ +#define FIRST_IPT_MATCH(r) ((struct ipt_entry_match *) \ + ((char *) (r) + (r)->match_offset)) +#define IPT_TARGET(r) ((struct ipt_entry_target *) \ + ((char *) (r) + (r)->target_offset)) +#define IPT_MATCH_END(r) ((struct ipt_entry_match *) IPT_TARGET(r)) +#define CHAIN_TARGET(r) ((char *) IPT_TARGET(r)) + +#endif diff -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_dev.c ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_dev.c --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_dev.c 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_dev.c 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,308 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * Licenced under the GNU General Public Licence, version 2. + */ + + +#include +#include +#include +#include +#include "nfhp_com.h" +#include "nfhp_dev.h" + + +#define IFNAME_MAP_INIT_LEN 31 + +struct ifname_map_t +{ + u32 len; + u32 size; + struct + { + char *ifname; + u16 vindex; + } map[0]; +}; + +static struct ifname_map_t *ifname_map; +static char (*ifnames)[IFNAMSIZ]; +struct nf_hipac_dev_ifindex_map_t nf_hipac_dev_ifindex_map +__attribute__((aligned(SMP_CACHE_BYTES))); + +static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED; + +static int +init_data(void) +{ + ifname_map = kmalloc(sizeof(ifname_map) + IFNAME_MAP_INIT_LEN * + sizeof(*ifname_map->map), GFP_KERNEL); + if (ifname_map == NULL) { + return -ENOMEM; + } + + ifnames = kmalloc(IFNAME_MAP_INIT_LEN * sizeof(*ifnames), GFP_KERNEL); + if (ifnames == NULL) { + kfree(ifname_map); + return -ENOMEM; + } + memset(&nf_hipac_dev_ifindex_map, 0, sizeof(nf_hipac_dev_ifindex_map)); + memset(ifname_map, 0, sizeof(ifname_map) + IFNAME_MAP_INIT_LEN * + sizeof(*ifname_map->map)); + memset(ifnames, 0, IFNAME_MAP_INIT_LEN * sizeof(*ifnames)); + ifname_map->size = IFNAME_MAP_INIT_LEN; + return 0; +} + +static void +free_data(void) +{ + if (ifname_map != NULL) { + kfree(ifname_map); + ifname_map = NULL; + } + + if (ifnames != NULL) { + kfree(ifnames); + ifnames = NULL; + } +} + +static void +ifindex_map_add_replace(u16 ifindex, u16 vindex) +{ + u16 i; + for (i = 0; i < nf_hipac_dev_ifindex_map.len; i++) { + if (nf_hipac_dev_ifindex_map.map[i].ifindex == ifindex) { + nf_hipac_dev_ifindex_map.map[i].vindex = vindex; + return; + } + } + for (i = 0; i < nf_hipac_dev_ifindex_map.len; i++) { + if (nf_hipac_dev_ifindex_map.map[i].ifindex == 0) { + nf_hipac_dev_ifindex_map.map[i].ifindex = ifindex; + nf_hipac_dev_ifindex_map.map[i].vindex = vindex; + return; + } + } + if (nf_hipac_dev_ifindex_map.len < NF_HIPAC_MAX_UP_INTERFACES) { + nf_hipac_dev_ifindex_map.map[nf_hipac_dev_ifindex_map.len] + .ifindex = ifindex; + nf_hipac_dev_ifindex_map.map[nf_hipac_dev_ifindex_map.len] + .vindex = vindex; + nf_hipac_dev_ifindex_map.len++; + } else { + printk(KERN_ERR "NF_HiPAC: too much interfaces UP at the " + "same time. Please increase NF_HIPAC_MAX_UP_INTERFACES " + "in nf_hipac_dev.h and recompile!"); + } + return; +} + +static void +ifindex_map_del(u16 ifindex) +{ + u16 i; + for (i = 0; i < nf_hipac_dev_ifindex_map.len; i++) { + if (nf_hipac_dev_ifindex_map.map[i].ifindex == ifindex) { + nf_hipac_dev_ifindex_map.map[i].ifindex = 0; + nf_hipac_dev_ifindex_map.map[i].vindex = 0; + return; + } + } + return; +} + +int +ifname_map_lookup_vindex(const char *ifname) +{ + u16 pos; + int cmp; + u32 start = 1; + u32 stop = ifname_map->len; + + while (stop >= start) { + pos = ((start + stop) >> 1) - 1; + cmp = strcmp(ifname_map->map[pos].ifname, ifname); + if (cmp < 0) { + start = pos + 2; + } else if (cmp > 0) { + stop = pos; + } else { + return ifname_map->map[pos].vindex; + } + } + return -1; +} + +int +nf_hipac_dev_lookup_ifname(int vindex, char ifname[]) +{ + if (vindex < 1 || vindex > ifname_map->len) + return -1; + strlcpy(ifname, ifnames[vindex - 1], IFNAMSIZ); + return 0; +} + +int +nf_hipac_dev_get_vindex(const char *ifname) +{ + u32 max = 0; + u32 start = 1; + u16 pos; + u32 stop; + int cmp; + struct net_device *dev; + + if (unlikely(ifname_map->len == 0)) { + strlcpy(ifnames[0], ifname, sizeof(*ifnames)); + dev = dev_get_by_name(ifname); + spin_lock_bh(&dev_lock); + ifname_map->len = 1; + ifname_map->map[0].ifname = ifnames[0]; + ifname_map->map[0].vindex = 1; + if (dev) { + if (dev->flags & IFF_UP) + ifindex_map_add_replace(dev->ifindex, 1); + dev_put(dev); + } + spin_unlock_bh(&dev_lock); + return 1; + } + + stop = ifname_map->len; + while (stop >= start) { + pos = ((start + stop) >> 1) - 1; + cmp = strcmp(ifname_map->map[pos].ifname, ifname); + if (cmp < 0) { + start = pos + 2; + } else if (cmp > 0) { + stop = pos; + max = pos + 1; + } else { + return ifname_map->map[pos].vindex; + } + } + if (max == 0) { + /* max has not been touched (otherwise it must be >= 1) + => new ifname is "maximal" */ + pos = ifname_map->len; + } else { + pos = max - 1; + } + + if (ifname_map->len == 65535) { + return NFHE_INDEX; + } + + /* new vindex required -> do reallocations first if necessary */ + if (unlikely(ifname_map->len == ifname_map->size)) { + u32 newsize = ((ifname_map->size + 1) << 1) - 1; + struct ifname_map_t *new_ifname_map; + char (*new_ifnames)[IFNAMSIZ]; + new_ifname_map = kmalloc(sizeof(new_ifname_map) + newsize * + sizeof(*new_ifname_map->map), + GFP_KERNEL); + if (new_ifname_map == NULL) { + return HE_LOW_MEMORY; + } + new_ifnames = kmalloc(newsize * sizeof(*new_ifnames), + GFP_KERNEL); + if (new_ifnames == NULL) { + kfree(new_ifname_map); + return HE_LOW_MEMORY; + } + memcpy(new_ifname_map, ifname_map, sizeof(new_ifname_map) + + ifname_map->size * sizeof(*new_ifname_map->map)); + new_ifname_map->size = newsize; + memcpy(new_ifnames, ifnames, + ifname_map->size * sizeof(*new_ifnames)); + strlcpy(new_ifnames[ifname_map->len], ifname, + sizeof(*new_ifnames)); + dev = dev_get_by_name(ifname); + spin_lock_bh(&dev_lock); + kfree(ifname_map); + kfree(ifnames); + ifname_map = new_ifname_map; + ifnames = new_ifnames; + } else { + strlcpy(ifnames[ifname_map->len], ifname, sizeof(*ifnames)); + dev = dev_get_by_name(ifname); + spin_lock_bh(&dev_lock); + } + + if (pos < ifname_map->len) { + memmove(&ifname_map->map[pos + 1], &ifname_map->map[pos], + (ifname_map->len - pos) * sizeof(*ifname_map->map)); + } + ifname_map->map[pos].ifname = ifnames[ifname_map->len]; + ifname_map->map[pos].vindex = ++ifname_map->len; + if (dev) { + if (dev->flags & IFF_UP) + ifindex_map_add_replace(dev->ifindex, ifname_map->len); + dev_put(dev); + } + spin_unlock_bh(&dev_lock); + return ifname_map->len; +} + +static int +nf_hipac_dev_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + int vindex; + struct net_device *dev = ptr; + switch (event) { + case NETDEV_UP: + spin_lock_bh(&dev_lock); + vindex = ifname_map_lookup_vindex(dev->name); + if (vindex > 0) { + // interface is in ruleset => add to ifindex_map + ifindex_map_add_replace(dev->ifindex, vindex); + } + spin_unlock_bh(&dev_lock); + break; + + case NETDEV_DOWN: + spin_lock_bh(&dev_lock); + ifindex_map_del(dev->ifindex); + spin_unlock_bh(&dev_lock); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block nf_hipac_dev_notifier = { + .notifier_call = nf_hipac_dev_event, +}; + +int +nf_hipac_dev_init(void) +{ + int stat; + + stat = init_data(); + if (stat < 0) { + return stat; + } + stat = register_netdevice_notifier(&nf_hipac_dev_notifier); + if (stat < 0) { + free_data(); + return stat; + } + return 0; +} + +void +nf_hipac_dev_exit(void) +{ + unregister_netdevice_notifier(&nf_hipac_dev_notifier); + free_data(); +} diff -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_dev.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_dev.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_dev.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_dev.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,66 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_mod.c ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_mod.c --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_mod.c 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_mod.c 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,1734 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * Licenced under the GNU General Public Licence, version 2. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nfhp_mod.h" +#include "nfhp_com.h" +#include "nfhp_dev.h" +#include "nfhp_proc.h" +#include "hipac.h" + +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) +#define MIN(a, b) ((a) >= (b) ? (b) : (a)) +#define SKB_LEN(s) (((s)->end - (s)->tail) - NLMSG_LENGTH(0)) + +/* hook match functions */ +static nf_hookfn input_match; +static nf_hookfn forward_match; +static nf_hookfn output_match; + +/* hipac data structures for INPUT, FORWARD and OUPUT hook plus + their corresponding netfilter ops */ +void *hipac_input = NULL; +void *hipac_forward = NULL; +void *hipac_output = NULL; +struct nf_hook_ops input_op = +{ + .hook = input_match, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_IP_LOCAL_IN, + .priority = NF_IP_PRI_FILTER - 1, +}; +struct nf_hook_ops forward_op = +{ + .hook = forward_match, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_IP_FORWARD, + .priority = NF_IP_PRI_FILTER - 1, +}; +struct nf_hook_ops output_op = +{ + .hook = output_match, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_IP_LOCAL_OUT, + .priority = NF_IP_PRI_FILTER - 1, +}; + +/* used to serialize hipac modifications caused by netlink commands and + the interface handling module */ +DECLARE_MUTEX(nlhp_lock); + +static struct sock *nfhp_sock = NULL; + +/* connection tracking dependency helper module */ +static DECLARE_MUTEX(cthelp_lock); +static struct module *nfhp_cthelp_module = NULL; + +DECLARE_COMPLETION(thread_comp); +static int threadID = 0; +DECLARE_WAIT_QUEUE_HEAD(thread_wait); + +/* latest hipac_get_chain_info snapshot */ +struct list_info +{ + struct hipac_chain_info *inf; + u32 len; +}; +static struct list_info linfo = {NULL, 0}; + +struct packet +{ + int hook; + struct sk_buff **skbuff; + const struct net_device *indev, *outdev; +}; + + + +/* + * dimension extractor functions + */ + +static u32 +get_state(const void *pkt, int *hotdrop) +{ +#ifdef CONFIG_IP_NF_CONNTRACK + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + unsigned int result; + enum ip_conntrack_info ctinfo; + if (skb->nfct == &ip_conntrack_untracked.ct_general) + result = NFHP_STATE_UNTRACKED; + else if (!ip_conntrack_get(skb, &ctinfo)) + result = NFHP_STATE_INVALID; + else result = ctinfo % NFHP_STATE_NUM_VALID; + return result; +#else + return 0; +#endif +} + +static u32 +get_src_ip(const void *pkt, int *hotdrop) +{ + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + return ntohl(skb->nh.iph->saddr); +} + +static u32 +get_dst_ip(const void *pkt, int *hotdrop) +{ + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + return ntohl(skb->nh.iph->daddr); +} + +static u32 +get_iniface(const void *pkt, int *hotdrop) +{ + return nf_hipac_dev_ifindex_to_vindex(((struct packet*) pkt) + ->indev->ifindex); +} + +static u32 +get_outiface(const void *pkt, int *hotdrop) +{ + return nf_hipac_dev_ifindex_to_vindex(((struct packet*) pkt) + ->outdev->ifindex); +} + +static u32 +get_proto(const void *pkt, int *hotdrop) +{ + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + return skb->nh.iph->protocol; +} + +static u32 +get_fragment(const void *pkt, int *hotdrop) +{ + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + int offset = ntohs(skb->nh.iph->frag_off) & IP_OFFSET; + if (unlikely(offset)) { + if (unlikely(offset == 1 && + skb->nh.iph->protocol == IPPROTO_TCP)) { + printk(KERN_NOTICE "Dropping evil TCP offset=1 " + "fragment.\n"); + *hotdrop = 1; + } + return 1; + } + return 0; +} + +static u32 +get_dport(const void *pkt, int *hotdrop) +{ + struct udphdr _udph, *uh; + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + uh = skb_header_pointer(skb, skb->nh.iph->ihl*4, + sizeof(_udph), &_udph); + if (unlikely(uh == NULL)) { + /* We've been asked to examine this packet, and we + can't. Hence, no choice but to drop. */ + *hotdrop = 1; + return 0; + } + return ntohs(uh->dest); +} + +static u32 +get_sport(const void *pkt, int *hotdrop) +{ + struct udphdr _udph, *uh; + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + uh = skb_header_pointer(skb, skb->nh.iph->ihl*4, + sizeof(_udph), &_udph); + if (unlikely(uh == NULL)) { + *hotdrop = 1; + return 0; + } + return ntohs(uh->source); +} + +static u32 +get_syn(const void *pkt, int *hotdrop) +{ + struct tcphdr _tcph, *th; + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + th = skb_header_pointer(skb, skb->nh.iph->ihl*4, + sizeof(_tcph), &_tcph); + if (unlikely(th == NULL)) { + *hotdrop = 1; + return 0; + } + return !(th->syn && !th->ack && !th->fin && !th->rst); +} + +static u32 +get_icmptypes(const void *pkt, int *hotdrop) +{ + struct icmphdr _icmph, *ic; + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + ic = skb_header_pointer(skb, skb->nh.iph->ihl*4, + sizeof(_icmph), &_icmph); + if (unlikely(ic == NULL)) { + *hotdrop = 1; + return 0; + } + return (ic->type << 8) + ic->code; +} + +static u32 +get_ttl(const void *pkt, int *hotdrop) +{ + const struct sk_buff *skb = *((struct packet *) pkt)->skbuff; + return skb->nh.iph->ttl; +} + + +/* + * conntrack dependency management + */ + +#ifdef CONNTRACK_MODULE /* conntrack built as module */ + +int +nfhp_register_cthelp(struct module *cthelp) +{ + int ret; + + ret = down_interruptible(&cthelp_lock); + if (ret != 0) { + return ret; + } + if (nfhp_cthelp_module != NULL) { + printk(KERN_ERR "nfhp_register_cthelp: module already " + "registered\n"); + ret = -EINVAL; + } + nfhp_cthelp_module = cthelp; + up(&cthelp_lock); + return ret; +} + +void +nfhp_unregister_cthelp(struct module *cthelp) +{ + down(&cthelp_lock); + if (nfhp_cthelp_module != cthelp) { + printk(KERN_ERR "nfhp_unregister_cthelp: unregistered " + "module tries to unregister\n"); + up(&cthelp_lock); + return; + } + nfhp_cthelp_module = NULL; + up(&cthelp_lock); +} + +static inline int +cthelp_use(void) +{ + int ret; + + /* check whether the conntrack dependency helper is registered */ + ret = down_interruptible(&cthelp_lock); + if (ret < 0) { + return ERRNO_TO_NFHE(-ret); + } + if (nfhp_cthelp_module != NULL) { + try_module_get(nfhp_cthelp_module); + up(&cthelp_lock); + return 0; + } + + /* try to load the module */ + up(&cthelp_lock); + request_module("nf_hipac_cthelp"); + + /* did we succeed? */ + ret = down_interruptible(&cthelp_lock); + if (ret < 0) { + return ERRNO_TO_NFHE(-ret); + } + if (nfhp_cthelp_module != NULL) { + try_module_get(nfhp_cthelp_module); + up(&cthelp_lock); + return 0; + } + up(&cthelp_lock); + return NFHE_CTHELP; +} + +static inline void +cthelp_unuse(void) +{ + if (nfhp_cthelp_module == NULL) { + printk(KERN_ERR "%s: conntrack dependency helper " + "module not registered\n", __FUNCTION__); + return; + } + module_put(nfhp_cthelp_module); +} + +#else /* conntrack built in or not available */ + +int +nfhp_register_cthelp(struct module *cthelp) +{ +} + +void +nfhp_unregister_cthelp(struct module *cthelp) +{ +} + +static inline int +cthelp_use(void) +{ +#ifdef CONFIG_IP_NF_CONNTRACK + return 0; +#else + return NFHE_CT; +#endif +} + +static inline void +cthelp_unuse(void) +{ +} + +#endif + + +/* + * functions and data structures necessary for hipac initialization + */ + +/* dimension id to bit type mapping */ +static const u8 dim2btype[] = +{ + [DIMID_STATE] = BIT_STATE, + [DIMID_SRC_IP] = BIT_SRC_IP, + [DIMID_DEST_IP] = BIT_DEST_IP, + [DIMID_INIFACE] = BIT_INIFACE, + [DIMID_OUTIFACE] = BIT_OUTIFACE, + [DIMID_PROTO] = BIT_PROTO, + [DIMID_FRAGMENT] = BIT_FRAGMENT, + [DIMID_DPORT] = BIT_DPORT, + [DIMID_SPORT] = BIT_SPORT, + [DIMID_SYN] = BIT_SYN, + [DIMID_ICMP_TYPE] = BIT_ICMP_TYPE, + [DIMID_TTL] = BIT_TTL +}; + +/* dimension extractor functions */ +static const hipac_extract_t extract[] = +{ + [DIMID_STATE] = get_state, + [DIMID_SRC_IP] = get_src_ip, + [DIMID_DEST_IP] = get_dst_ip, + [DIMID_INIFACE] = get_iniface, + [DIMID_OUTIFACE] = get_outiface, + [DIMID_PROTO] = get_proto, + [DIMID_FRAGMENT] = get_fragment, + [DIMID_DPORT] = get_dport, + [DIMID_SPORT] = get_sport, + [DIMID_SYN] = get_syn, + [DIMID_ICMP_TYPE] = get_icmptypes, + [DIMID_TTL] = get_ttl, +}; + +/* iptables_match executor */ +static hipac_match_t +hipac_match_exec(const void *packet, void *first_match, void *end) +{ + const struct packet *p = packet; + hipac_match_t match = MATCH_YES; + struct ipt_entry_match *m = first_match; + int hotdrop = 0; + struct iphdr *ip = (*p->skbuff)->nh.iph; + u16 offset = ntohs(ip->frag_off) & IP_OFFSET; + + for (; m < (struct ipt_entry_match *) end; m = NEXT_IPT_MATCH(m)) { + if (!m->u.kernel.match->match(*p->skbuff, p->indev, + p->outdev, m->data, + offset, &hotdrop)) { + match = MATCH_NO; + break; + } + } + if (hotdrop) { + return MATCH_HOTDROP; + } + return match; +} + +/* iptables_target executor */ +static hipac_target_t +hipac_target_exec(const void *packet, void *target) +{ + const struct packet *p = packet; + struct ipt_entry_target *t = target; + hipac_target_t ht; + + ht = t->u.kernel.target->target(p->skbuff, p->indev, p->outdev, + p->hook, t->data, NULL); + if (ht == IPT_CONTINUE) { + return TARGET_NONE; + } + return ht; +} + +/* equality test - rnl is the hipac_rule in netlink format which implies + that it contains ipt_entry and cmp_len if the rule has an ipt_entry_match + or ipt_entry_target or chain label; rhp is in hipac format which means + that it does not contain ipt_entry and cmp_len */ +static int +hipac_eq_exec(const struct hipac_rule *rnl, const struct hipac_rule *rhp) +{ + u32 cmp_len_ind = 0; + u16 *cmp_len; + + if (rnl == rhp) { + printk(KERN_ERR "%s: rnl == rhp error\n", __FUNCTION__); + return 0; + } + if (rnl == NULL || rhp == NULL || rnl->size != rhp->size || + rnl->native_mct != rhp->native_mct || + memcmp(rnl->cmp_start, rhp->cmp_start, + sizeof(*rnl) - offsetof(struct hipac_rule, cmp_start) + + rnl->native_mct * sizeof(*rnl->first_match))) { + return 0; + } + cmp_len = CMP_LEN(rnl); + if (HAS_IPT_MATCH(rnl)) { + struct ipt_entry_match *mnl, *mhp, *endhp; + mnl = FIRST_IPT_MATCH_IE(rnl); + mhp = FIRST_IPT_MATCH(rhp); + endhp = IPT_MATCH_END(rhp); + for (; mhp < endhp; + mnl = NEXT_IPT_MATCH(mnl), mhp = NEXT_IPT_MATCH(mhp), + cmp_len_ind++) { + if (strncmp(mnl->u.user.name, + mhp->u.kernel.match->name, + sizeof(mnl->u.user.name)) || + mnl->u.match_size != mhp->u.match_size || + memcmp(&mnl->data, &mhp->data, + cmp_len[cmp_len_ind])) { + return 0; + } + } + } + if (HAS_IPT_TARGET(rnl)) { + struct ipt_entry_target *tnl, *thp; + tnl = IPT_TARGET_IE(rnl); + thp = IPT_TARGET(rhp); + if (strncmp(tnl->u.user.name, thp->u.kernel.target->name, + sizeof(tnl->u.user.name)) || + tnl->u.target_size != thp->u.target_size || + memcmp(&tnl->data, &thp->data, + cmp_len[cmp_len_ind])) { + return 0; + } + } else if (HAS_CHAIN_TARGET(rnl)) { + char *tnl, *thp; + tnl = CHAIN_TARGET_IE(rnl); + thp = CHAIN_TARGET(rhp); + /* strlen(tnl) < HIPAC_CHAIN_NAME_MAX_LEN */ + if (strcmp(tnl, thp)) { + return 0; + } + } + return 1; +} + +/* r is constructed by copying rnl to the exclusion of ipt_entry and + cmp_len (if present); rnl->size already states the size of r _but_ + rnl may be smaller than rnl->size if it has a chain target */ +static void +hipac_copy_constructor(const struct hipac_rule *rnl, struct hipac_rule *r) +{ + if (HAS_IPT_ENTRY(rnl)) { + u32 size = rnl->size; + if (HAS_CHAIN_TARGET(rnl)) { + size -= HIPAC_CHAIN_NAME_MAX_LEN - + strlen(CHAIN_TARGET_IE(rnl)) - 1; + } + memcpy(r, rnl, sizeof(*rnl) + rnl->native_mct * + sizeof(*rnl->first_match)); + if (HAS_IPT_MATCH(rnl)) { + memcpy(FIRST_IPT_MATCH(r), IPT_ENTRY_END(rnl), + size - rnl->match_offset); + } else { + memcpy(IPT_TARGET(r), IPT_ENTRY_END(rnl), + size - rnl->target_offset); + } + } else { + memcpy(r, rnl, rnl->size); + } +} + +/* destructor for iptables matches/target */ +static void +hipac_destroy_exec(struct hipac_rule *r) +{ + int i; + + if (r == NULL) { + return; + } + for (i = 0; i < r->native_mct && + r->first_match[i].dimid < DIMID_STATE; i++); + if (i < r->native_mct && r->first_match[i].dimid == DIMID_STATE) { + cthelp_unuse(); + } + if (HAS_IPT_MATCH(r)) { + struct ipt_entry_match *m, *end; + m = FIRST_IPT_MATCH(r); + end = IPT_MATCH_END(r); + for (; m < end; m = NEXT_IPT_MATCH(m)) { + if (m->u.kernel.match->destroy) { + m->u.kernel.match->destroy( + m->data, m->u.match_size - sizeof(*m)); + } + module_put(m->u.kernel.match->me); + } + } + if (HAS_IPT_TARGET(r)) { + struct ipt_entry_target *t; + t = IPT_TARGET(r); + if (t->u.kernel.target->destroy) { + t->u.kernel.target->destroy( + t->data, t->u.target_size - sizeof(*t)); + } + module_put(t->u.kernel.target->me); + } +} + +/* destructor for iptables matches/target (rnl is the hipac_rule in + netlink format) */ +static void +hipac_destroy_exec_nl(struct hipac_rule *rnl) +{ + int i; + + if (rnl == NULL) { + return; + } + for (i = 0; i < rnl->native_mct && + rnl->first_match[i].dimid < DIMID_STATE; i++); + if (i < rnl->native_mct && rnl->first_match[i].dimid == DIMID_STATE) { + cthelp_unuse(); + } + if (HAS_IPT_MATCH(rnl)) { + struct ipt_entry_match *m, *end; + m = FIRST_IPT_MATCH_IE(rnl); + end = IPT_MATCH_END_IE(rnl); + for (; m < end; m = NEXT_IPT_MATCH(m)) { + if (m->u.kernel.match->destroy) { + m->u.kernel.match->destroy( + m->data, m->u.match_size - sizeof(*m)); + } + module_put(m->u.kernel.match->me); + } + } + if (HAS_IPT_TARGET(rnl)) { + struct ipt_entry_target *t; + t = IPT_TARGET_IE(rnl); + if (t->u.kernel.target->destroy) { + t->u.kernel.target->destroy( + t->data, t->u.target_size - sizeof(*t)); + } + module_put(t->u.kernel.target->me); + } +} + +static unsigned int +input_match(unsigned int hooknum, + struct sk_buff **skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn) (struct sk_buff *)) +{ + const struct packet pkt = {hooknum, skb, in, out}; + return hipac_match(hipac_input, &pkt); +} + +static unsigned int +forward_match(unsigned int hooknum, + struct sk_buff **skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn) (struct sk_buff *)) +{ + const struct packet pkt = {hooknum, skb, in, out}; + return hipac_match(hipac_forward, &pkt); +} + +static unsigned int +output_match(unsigned int hooknum, + struct sk_buff **skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn) (struct sk_buff *)) +{ + const struct packet pkt = {hooknum, skb, in, out}; + + /* root is playing with raw sockets. */ + if (unlikely((*skb)->len < sizeof(struct iphdr) || + ((*skb)->nh.iph->ihl << 2) < sizeof(struct iphdr))) { + return NF_ACCEPT; + } + return hipac_match(hipac_output, &pkt); +} + + +/* + * kernel-user netlink communication + */ + +static inline void * +nlhp_list_rule(struct nfhp_list_rule *r, struct hipac_rule *rule, int *len) +{ + int size = IPT_ALIGN(offsetof(struct nfhp_list_rule, r) + rule->size); + u32 i; + + if (*len < size) { + return NULL; + } + r->indev[0] = '\0'; + r->outdev[0] = '\0'; + memcpy(&r->r, rule, rule->size); + + /* fill in interface names if necessary */ + for (i = 0; i < r->r.native_mct; i++) { + switch (r->r.first_match[i].dimid) { + case DIMID_INIFACE: + if (nf_hipac_dev_lookup_ifname( + r->r.first_match[i].left, + r->indev) < 0) { + printk(KERN_ERR "%s: interface name look" + "up failed\n", __FUNCTION__); + } + break; + case DIMID_OUTIFACE: + if (nf_hipac_dev_lookup_ifname( + r->r.first_match[i].left, + r->outdev) < 0) { + printk(KERN_ERR "%s: interface name look" + "up failed\n", __FUNCTION__); + } + break; + } + } + + /* prepare iptables matches/target for userspace */ + if (HAS_IPT_MATCH(&r->r)) { + struct ipt_entry_match *m, *end; + m = FIRST_IPT_MATCH(&r->r); + end = IPT_MATCH_END(&r->r); + for (; m < end; m = NEXT_IPT_MATCH(m)) { + strncpy(m->u.user.name, m->u.kernel.match->name, + sizeof(m->u.user.name)); + } + } + if (HAS_IPT_TARGET(&r->r)) { + struct ipt_entry_target *t; + t = IPT_TARGET(&r->r); + strncpy(t->u.user.name, t->u.kernel.target->name, + sizeof(t->u.user.name)); + } + + *len -= size; + return (char *) r + size; +} + +static inline void * +nlhp_list_chain(struct nfhp_list_chain *c, int pos, int *len) +{ + if (*len < sizeof(*c)) { + return NULL; + } + strncpy(c->label, linfo.inf[pos].label, sizeof(c->label)); + c->label[sizeof(c->label) - 1] = '\0'; + c->policy = linfo.inf[pos].policy; + c->rule_num = linfo.inf[pos].rule_num; + *len -= sizeof(*c); + return c + 1; +} + +static inline int +nlhp_list_next_rule(struct hipac_rule *prev, struct hipac_rule **rule, int pos) +{ + int stat; + + stat = hipac_get_next_rule(&linfo.inf[pos], prev, rule); + switch (stat) { + case HE_OK: + return 0; + case HE_RULE_NOT_EXISTENT: + *rule = NULL; + return 0; + default: + if (unlikely(stat > 0)) { + /* this should never happen */ + printk(KERN_ERR "%s: hipac_get_next_rule returned " + "status > 0\n", __FUNCTION__); + stat = -stat; + } + return stat; + } +} + +/* callback function for CMD_LIST command */ +static int +nlhp_list(struct sk_buff *skb, struct netlink_callback *cb) +{ + static u32 pos; + static struct hipac_rule *rule; + struct nlmsghdr *nlh; + int len, total, stat; + void *data; + + total = skb_tailroom(skb) - NLMSG_SPACE(0); + switch (cb->args[0]) { + case 0: + /* first callback in the series */ + pos = 0; + rule = NULL; + data = NLMSG_DATA(skb->data); + len = total; + cb->args[0] = 1; + break; + case 1: + /* pos, rule represent the current state */ + data = NLMSG_DATA(skb->data); + len = total; + break; + default: + return 0; + } + + while (1) { + if (rule == NULL) { + /* send chain info */ + data = nlhp_list_chain(data, pos, &len); + if (data == NULL) { + /* skb full - chain sent next time */ + break; + } + stat = nlhp_list_next_rule(NULL, &rule, pos); + if (stat < 0) { + /* rule listing aborted due to error */ + return stat; + } + } else { + /* send next rule */ + data = nlhp_list_rule(data, rule, &len); + if (data == NULL) { + /* skb full - rule sent next time */ + break; + } + stat = nlhp_list_next_rule(rule, &rule, pos); + if (stat < 0) { + /* rule listing aborted due to error */ + return stat; + } + } + if (rule == NULL) { + if (++pos == linfo.len) { + /* we are done */ + cb->args[0] = 2; + break; + } + } + } + nlh = NLMSG_PUT(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + NLHP_TYPE, total - len); + nlh->nlmsg_flags |= NLM_F_MULTI; + return NLMSG_SPACE(total - len); + +nlmsg_failure: + skb_trim(skb, skb->tail - skb->data); + return NFHE_ILL; +} + +static int +nlhp_done(struct netlink_callback *cb) +{ + up(&nlhp_lock); + return 0; +} + +static void +nlhp_send_reply(struct sk_buff *skb, struct nlmsghdr *nlh, int err) +{ + struct sk_buff *r_skb; + struct nlmsghdr *r_nlh; + + r_skb = alloc_skb(NLMSG_SPACE(sizeof(int32_t)), GFP_KERNEL); + if (r_skb == NULL) { + return; + } + + r_nlh = NLMSG_PUT(r_skb, NETLINK_CB(skb).pid, nlh->nlmsg_seq, + NLMSG_ERROR, sizeof(int32_t)); + *(int32_t *) NLMSG_DATA(r_nlh) = err; + if (!NLMSG_OK(r_nlh, NLMSG_LENGTH(sizeof(int32_t)))) { + printk(KERN_ERR "netlink message not ok\n"); + return; + } + if (netlink_unicast(nfhp_sock, r_skb, NETLINK_CB(skb).pid, + MSG_DONTWAIT) <= 0) { + printk(KERN_ERR "netlink_unicast failed\n"); + return; + } + return; +nlmsg_failure: + printk(KERN_ERR "NLMSG_PUT failed\n"); + kfree(r_skb); +} + +static int +do_cmd(struct sk_buff *skb, int msg_len); + +static inline int +nlhp_chk_user_skb(struct sk_buff *skb) +{ + struct nlmsghdr *nlh = (struct nlmsghdr *) skb->data; + + if (skb->len < sizeof(struct nlmsghdr) || + nlh->nlmsg_len < sizeof(struct nlmsghdr) || + skb->len < nlh->nlmsg_len || + nlh->nlmsg_pid <= 0 || + nlh->nlmsg_type != NLHP_TYPE || + nlh->nlmsg_flags & NLM_F_MULTI || + !(nlh->nlmsg_flags & NLM_F_REQUEST) || + !(nlh->nlmsg_flags & NLM_F_ACK)) { + nlhp_send_reply(skb, nlh, NFHE_NOMSG); + return 1; + } + if (nlh->nlmsg_flags & MSG_TRUNC) { + nlhp_send_reply(skb, nlh, ERRNO_TO_NFHE(ECOMM)); + return 1; + } + if (security_netlink_recv(skb)) { + nlhp_send_reply(skb, nlh, ERRNO_TO_NFHE(EPERM)); + return 1; + } + return do_cmd(skb, skb->len - NLMSG_LENGTH(0)); +} + +static void +nlhp_data_ready(struct sock *sk, int len) +{ + wake_up_interruptible(&thread_wait); +} + +static int +nlhp_thread_func(void *data) +{ + struct sk_buff *skb; + + daemonize("nf_hipac"); + allow_signal(SIGTERM); + while (1) { + if (wait_event_interruptible( + thread_wait, + (skb = skb_dequeue(&nfhp_sock->sk_receive_queue)) + != NULL) + || down_interruptible(&nlhp_lock)) { + complete_and_exit(&thread_comp, 0); + } + if (nlhp_chk_user_skb(skb)) { + /* in the other case nlhp_done releases + the lock */ + up(&nlhp_lock); + } + kfree_skb(skb); + } + return 0; +} + + +/* + * request handling + */ + +static int +cmd_check_init_native_matches(struct nfhp_rule *rule, int inc_modct, + int *inc_cthelp) +{ + u32 dimbitv = 0; + u8 frag_match = 0; + u16 proto_match = 0; + struct ipt_entry *e = NULL; + struct hipac_rule *r = &rule->r; + int i, ifindex, stat; + u8 dimid, inv, devbf; + u32 left, right; + + devbf = (rule->indev[0] != '\0'); + devbf |= (rule->outdev[0] != '\0') << 1; + if (HAS_IPT_ENTRY(r)) { + /* as nf-hipac does not care about ipt_entry we just fill in + the info needed by the existing modules */ + e = IPT_ENTRY(r); + memset(&e->ip, 0, sizeof(e->ip)); + if (devbf & 1) { + strncpy(e->ip.iniface, rule->indev, + sizeof(e->ip.iniface)); + memset(e->ip.iniface_mask, 0xff, + strlen(rule->indev) + 1); + } + if (devbf & 2) { + strncpy(e->ip.outiface, rule->outdev, + sizeof(e->ip.outiface)); + memset(e->ip.outiface_mask, 0xff, + strlen(rule->outdev) + 1); + } + e->nfcache = 0; + e->comefrom = 0; + memset(&e->counters, 0, sizeof(e->counters)); + } + for (i = 0; i < r->native_mct; i++) { + r->first_match[i].invert = !!r->first_match[i].invert; + dimid = r->first_match[i].dimid; + inv = r->first_match[i].invert; + left = r->first_match[i].left; + right = r->first_match[i].right; + if (i > 0 && dimid <= r->first_match[i - 1].dimid) { + return NFHE_SORT; + } + if (left > right || right > hipac_maxkey(dim2btype[dimid]) || + (left == 0 && right == hipac_maxkey(dim2btype[dimid]))) { + return NFHE_MINT; + } + dimbitv |= 1 << dimid; + switch (dimid) { + case DIMID_INIFACE: + if (!(devbf & 1)) { + return NFHE_DEVA; + } + ifindex = nf_hipac_dev_get_vindex(rule->indev); + if (ifindex < 0) { + return ifindex; + } + r->first_match[i].left = ifindex; + r->first_match[i].right = ifindex; + if (e != NULL && inv) { + e->ip.invflags |= IPT_INV_VIA_IN; + } + devbf &= 0xfe; + r->origin &= NFHP_ORIGIN_INPUT | + NFHP_ORIGIN_FORWARD; + break; + case DIMID_OUTIFACE: + if (!(devbf & 2)) { + return NFHE_DEVA; + } + ifindex = nf_hipac_dev_get_vindex(rule->outdev); + if (ifindex < 0) { + return ifindex; + } + r->first_match[i].left = ifindex; + r->first_match[i].right = ifindex; + if (e != NULL && inv) { + e->ip.invflags |= IPT_INV_VIA_OUT; + } + devbf &= 0xfd; + r->origin &= NFHP_ORIGIN_OUTPUT | + NFHP_ORIGIN_FORWARD; + break; + case DIMID_PROTO: + if (!inv && left == right) { + proto_match = left; + } + if (e != NULL) { + e->ip.proto = r->first_match[i].left; + /* iptables does not support protocol + ranges; treating a range match as + inverted point match avoids illegal use + of iptables matches */ + if (inv || left != right) { + e->ip.invflags |= IPT_INV_PROTO; + } + } + break; + case DIMID_FRAGMENT: + if (inv || (left != right && left == 0)) { + return NFHE_FRAG; + } + if (e != NULL) { + e->ip.flags = IPT_F_FRAG; + } + if (left > 0) { + r->first_match[i].left = 1; + r->first_match[i].right = + hipac_maxkey(dim2btype[dimid]); + } else { + frag_match = 1; + if (e != NULL) { + e->ip.invflags |= IPT_INV_FRAG; + } + } + break; + case DIMID_SYN: + if (inv || (left != right && left == 0)) { + return NFHE_SYN; + } + if (left > 0) { + r->first_match[i].left = 1; + r->first_match[i].right = + hipac_maxkey(dim2btype[dimid]); + } + break; + case DIMID_STATE: + if (left > NFHP_STATE_UNTRACKED) { + return NFHE_STATE; + } + if (inc_modct) { + stat = cthelp_use(); + if (stat < 0) { + return stat; + } + (*inc_cthelp)++; + } + break; + } + } + if (devbf != 0) { + return NFHE_DEVB; + } + + /* check inter-match dependencies */ + if (dimbitv & (1 << DIMID_SYN)) { + if (proto_match != IPPROTO_TCP || !frag_match || + dimbitv & (1 << DIMID_ICMP_TYPE)) { + return NFHE_TCP; + } + } else if (dimbitv & (1 << DIMID_DPORT) || + dimbitv & (1 << DIMID_SPORT)) { + if ((proto_match != IPPROTO_UDP && proto_match != IPPROTO_TCP) || !frag_match || + dimbitv & (1 << DIMID_ICMP_TYPE)) { + return NFHE_TCPUDP; + } + } else if (dimbitv & (1 << DIMID_ICMP_TYPE) && + (proto_match != IPPROTO_ICMP || !frag_match)) { + return NFHE_ICMP; + } + return 0; +} + +static int +init_ipt_match(struct ipt_entry_match *m, char *table, + const struct ipt_ip *ip, unsigned int hook) +{ + struct ipt_match *match; + int ret = 0; + + match = ipt_find_match(m->u.user.name, m->u.user.revision); + if (!match) + return ERRNO_TO_NFHE(ENOENT); + m->u.kernel.match = match; + if (m->u.kernel.match->checkentry + && !m->u.kernel.match->checkentry(table, ip, m->data, + m->u.match_size - sizeof(*m), + hook)) { + module_put(m->u.kernel.match->me); + ret = NFHE_IPTMCH; + } + return ret; +} + +static int +init_ipt_target(struct ipt_entry_target *t, char *table, + const struct ipt_entry *e, unsigned int hook) +{ + struct ipt_target *target; + int ret = 0; + + target = ipt_find_target(t->u.user.name, t->u.user.revision); + if (!target) + return ERRNO_TO_NFHE(ENOENT); + t->u.kernel.target = target; + if (t->u.kernel.target->checkentry + && !t->u.kernel.target->checkentry(table, e, t->data, + t->u.target_size - sizeof(*t), + hook)) { + module_put(t->u.kernel.target->me); + ret = NFHE_IPTTCH; + } + return ret; +} + +static inline u32 +origin_to_hookmask(u32 origin) +{ + return (origin & NFHP_ORIGIN_INPUT ? 1 << NF_IP_LOCAL_IN : 0) | + (origin & NFHP_ORIGIN_FORWARD ? 1 << NF_IP_FORWARD : 0) | + (origin & NFHP_ORIGIN_OUTPUT ? 1 << NF_IP_LOCAL_OUT : 0); +} + +static int +cmd_check_init_ipt_matches(struct hipac_rule *r, int len, u16 **cmp, + int *cmp_len, int inc_modct, int *num_done) +{ + struct ipt_entry_match *match; + int stat; + + match = FIRST_IPT_MATCH_IE(r); + while (len >= sizeof(*match) && len >= match->u.match_size) { + if (match->u.match_size != IPT_ALIGN(match->u.match_size)) { + return NFHE_IPTMSZ; + } + if (*cmp != NULL) { + if (*cmp_len < sizeof(**cmp)) { + return NFHE_CMPSH; + } + if (**cmp > match->u.match_size - sizeof(*match)) { + return NFHE_CMPLA; + } + } + + /* this is really ugly but we have to hardcode the maximum + allowed origin bit vector since the actual origin of this + rule might change after another rule has been inserted; + in order to dynamically handle this setting a module would + be required to be asked for its maximum allowed origin bit + vector */ + if (!strcmp(match->u.user.name, "owner")) { + r->origin &= NFHP_ORIGIN_OUTPUT; + } else if (!strcmp(match->u.user.name, "owner-socketlookup")) { + r->origin &= NFHP_ORIGIN_INPUT | NFHP_ORIGIN_OUTPUT; + } + if (r->origin == 0) { + return NFHE_ORIGIN; + } + + if (inc_modct) { + if (r->action == TARGET_CHAIN) { + /* TEMPORARY FIX: since hipac currently treats + jump rules in a way that leads to problems + with stateful ipt_matches we restrict + ourselves to certain known stateless + ipt_matches; if you absolutely need a jump + rule with another stateless ipt_match you + are free to add its name here after but + make sure that it is __really__ + stateless */ + if (strcmp(match->u.user.name, "ah") && + strcmp(match->u.user.name, "dscp") && + strcmp(match->u.user.name, "ecn") && + strcmp(match->u.user.name, "esp") && + strcmp(match->u.user.name, "length") && + strcmp(match->u.user.name, "owner") && + strcmp(match->u.user.name, "pkttype") && + strcmp(match->u.user.name, "tcpmss") && + strcmp(match->u.user.name, "tos") && + strcmp(match->u.user.name, "unclean")) { + return NFHE_IMPL; + } + } + stat = init_ipt_match(match, "filter", + &IPT_ENTRY(r)->ip, + origin_to_hookmask(r->origin)); + if (stat < 0) { + return stat; + } + } + (*num_done)++; + len -= match->u.match_size; + match = NEXT_IPT_MATCH(match); + if (*cmp != NULL) { + (*cmp_len) -= sizeof(**cmp); + (*cmp)++; + } + } + if (len > 0) { + return NFHE_TOFF; + } + return 0; +} + +static int +cmd_check_init_ipt_target(struct hipac_rule *r, int len, u16 *cmp, + int cmp_len, int inc_modct, int *done) +{ + struct ipt_entry_target *target; + int stat; + + target = IPT_TARGET_IE(r); + if (len < sizeof(*target) || len < target->u.target_size || + target->u.target_size != IPT_ALIGN(target->u.target_size)) { + return NFHE_IPTTSZ; + } + if (cmp != NULL) { + if (cmp_len < sizeof(*cmp)) { + return NFHE_CMPSH; + } + if (*cmp > target->u.target_size - sizeof(*target)) { + return NFHE_CMPLA; + } + } + + /* this is really ugly but we have to hardcode the maximum allowed + origin bit vector since the actual origin of this rule might + change after another rule has been inserted; + in order to dynamically handle this setting a module would be + required to be asked for its maximum allowed origin bit vector */ + if (!strcmp(target->u.user.name, "MIRROR")) { + r->origin &= NFHP_ORIGIN_FORWARD | NFHP_ORIGIN_INPUT; + } else if (!strcmp(target->u.user.name, "TCPMSS")) { + r->origin &= NFHP_ORIGIN_FORWARD | NFHP_ORIGIN_OUTPUT; + } else if (!strcmp(target->u.user.name, "TARPIT")) { + r->origin &= NFHP_ORIGIN_FORWARD | NFHP_ORIGIN_INPUT; + } + if (r->origin == 0) { + return NFHE_ORIGIN; + } + + if (inc_modct) { + stat = init_ipt_target(target, "filter", IPT_ENTRY(r), + origin_to_hookmask(r->origin)); + if (stat < 0) { + return stat; + } + } + (*done)++; + len -= target->u.target_size; + if (len > 0) { + /* netlink message contains unnecessary padding between the + end of the ipt target and the beginning of the cmp_len + array */ + r->size -= len; + } + return 0; +} + +static void +cmd_cleanup(struct hipac_rule *r, int inc_cthelp, int num_matches, int target) +{ + struct ipt_entry_match *m; + struct ipt_entry_target *t; + int i; + + if (inc_cthelp) { + cthelp_unuse(); + } + for (m = FIRST_IPT_MATCH_IE(r), i = 0; i < num_matches; + m = NEXT_IPT_MATCH(m), i++) { + if (m->u.kernel.match->destroy) { + m->u.kernel.match->destroy(m->data, m->u.match_size - + sizeof(*m)); + } + module_put(m->u.kernel.match->me); + } + if (target) { + t = IPT_TARGET_IE(r); + if (t->u.kernel.target->destroy) { + t->u.kernel.target->destroy( + t->data, t->u.target_size - sizeof(*t)); + } + module_put(t->u.kernel.target->me); + } +} + +static int +cmd_check_init(struct nfhp_cmd *cmd, int msg_len) +{ + u16 *cmp = NULL; + int inc_cthelp = 0, num_matches = 0, target = 0, cmp_len = 0; + int stat, len, inc_modct; + struct hipac_rule *r; + u32 c; + + if (msg_len < sizeof(*cmd)) { + return NFHE_NOMSG; + } + + /* basic checks */ + c = cmd->cmd; + inc_modct = (c == CMD_APPEND || c == CMD_INSERT || c == CMD_REPLACE); + if (c < CMD_MIN || c > CMD_MAX) { + return NFHE_CMD; + } + cmd->chain.label[HIPAC_CHAIN_NAME_MAX_LEN - 1] = '\0'; + cmd->chain.newlabel[HIPAC_CHAIN_NAME_MAX_LEN - 1] = '\0'; + if (cmd->chain.label[0] == '\0' && + !(c == CMD_FLUSH || c == CMD_DELETE_CHAIN || c == CMD_LIST)) { + return NFHE_LABEL; + } + if (c == CMD_RENAME_CHAIN && cmd->chain.newlabel[0] == '\0') { + return NFHE_NLABEL; + } + if (c == CMD_SET_POLICY && cmd->chain.policy != TARGET_ACCEPT && + cmd->chain.policy != TARGET_DROP) { + return NFHE_POLICY; + } + if (!(c == CMD_APPEND || c == CMD_INSERT || c == CMD_DELETE_RULE || + c == CMD_REPLACE)) { + /* we are finished since cmd->rule is irrelevant; + if c == CMD_DELETE_POS then cmd->rule.r.pos is verified + by hipac */ + return 0; + } + + /* rule checks */ + r = &cmd->rule.r; + cmd->rule.indev[IFNAMSIZ - 1] = '\0'; + cmd->rule.outdev[IFNAMSIZ - 1] = '\0'; + r->origin = NFHP_ORIGIN_INPUT | NFHP_ORIGIN_FORWARD | + NFHP_ORIGIN_OUTPUT; + /* TEMPORARY FIX: TARGET_RETURN is not yet implemented */ + if (r->action == TARGET_RETURN) { + return NFHE_IMPL; + } + if (!(r->action == TARGET_ACCEPT || r->action == TARGET_DROP || + r->action == TARGET_NONE || r->action == TARGET_RETURN || + r->action == TARGET_EXEC || r->action == TARGET_CHAIN)) { + return NFHE_ACTION; + } + if (r->native_mct > NUMBER_OF_DIM) { + return NFHE_NMCT; + } + if (HAS_IPT_ENTRY(r)) { + struct ipt_entry *e = IPT_ENTRY(r); + if (e->target_offset > e->next_offset || + e->target_offset < sizeof(*e) || + offsetof(struct nfhp_cmd, rule.e) + + e->next_offset > msg_len) { + return NFHE_IEOFF; + } + /* assume target_offset/next_offset are correct; if they prove + to be wrong we reject the packet anyway; + the values are chosen to be correct for the target of + hipac_copy_constructor */ + if (HAS_IPT_MATCH(r)) { + r->match_offset = IPT_ALIGN(sizeof(*r) + + r->native_mct * + sizeof(*r->first_match)); + r->target_offset = (e->target_offset - sizeof(*e)) + + r->match_offset; + } else { + if (e->target_offset != sizeof(*e)) { + return NFHE_IEOFF; + } + r->match_offset = 0; + if (HAS_CHAIN_TARGET(r)) { + r->target_offset = sizeof(*r) + r->native_mct * + sizeof(*r->first_match); + } else { + r->target_offset = + IPT_ALIGN(sizeof(*r) + + r->native_mct * + sizeof(*r->first_match)); + } + } + r->size = (e->next_offset - sizeof(*e)) + r->target_offset; + } else { + /* no iptables matches/target, no chain target */ + r->size = sizeof(*r) + r->native_mct * sizeof(*r->first_match); + r->match_offset = r->target_offset = 0; + } + + /* check the native matches */ + stat = cmd_check_init_native_matches(&cmd->rule, inc_modct, + &inc_cthelp); + if (stat < 0) { + goto error; + } + + /* set maximum size of cmp_len based on r->size */ + if (HAS_CMP_LEN(c, r)) { + cmp = CMP_LEN(r); + cmp_len = msg_len - ((char *) cmp - (char *) cmd); + if (cmp_len <= 0) { + stat = NFHE_CMPMIS; + goto error; + } + } + + /* check and initialize ipt matches */ + if (HAS_IPT_MATCH(r)) { + len = r->target_offset - r->match_offset; + stat = cmd_check_init_ipt_matches(r, len, &cmp, &cmp_len, + inc_modct, &num_matches); + if (stat < 0) { + goto error; + } + } + + /* check and initialize ipt target / chain target */ + if (HAS_IPT_TARGET(r)) { + len = r->size - r->target_offset; + if (len <= 0) { + stat = NFHE_IPTTMI; + goto error; + } + stat = cmd_check_init_ipt_target(r, len, cmp, cmp_len, + inc_modct, &target); + if (stat < 0) { + goto error; + } + } else if (HAS_CHAIN_TARGET(r)) { + char *chain = CHAIN_TARGET_IE(r); + u32 real_len; + len = r->size - r->target_offset; + if (len <= 0 || chain[0] == '\0') { + stat = NFHE_CHAINE; + goto error; + } + real_len = strnlen(chain, len); + if (len > HIPAC_CHAIN_NAME_MAX_LEN || real_len == len) { + stat = NFHE_CHAINL; + goto error; + } + /* we have to reserve HIPAC_CHAIN_NAME_MAX_LEN bytes for + the chain label */ + r->size += HIPAC_CHAIN_NAME_MAX_LEN - len; + } + + /* rule _syntactically_ correct; it might still be invalid because + of a violation of the hipac semantics */ + return 0; + + error: + cmd_cleanup(r, inc_cthelp, num_matches, target); + return stat; +} + +static int +do_cmd(struct sk_buff *skb, int msg_len) +{ + struct nlmsghdr *nlh = (struct nlmsghdr *) skb->data; + struct nfhp_cmd *cmd = (struct nfhp_cmd *) NLMSG_DATA(nlh); + char *chain_label; + int stat; + + stat = cmd_check_init(cmd, msg_len); + if (stat < 0) { + nlhp_send_reply(skb, nlh, stat); + return 1; + } + if (cmd->chain.label[0] == '\0') { + chain_label = NULL; + } else { + chain_label = cmd->chain.label; + } + + switch (cmd->cmd) { + case CMD_APPEND: + stat = hipac_append(chain_label, &cmd->rule.r); + if (stat != HE_OK) { + hipac_destroy_exec_nl(&cmd->rule.r); + } + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_INSERT: + stat = hipac_insert(chain_label, &cmd->rule.r); + if (stat != HE_OK) { + hipac_destroy_exec_nl(&cmd->rule.r); + } + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_DELETE_RULE: + stat = hipac_delete(chain_label, &cmd->rule.r); + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_DELETE_POS: + stat = hipac_delete_pos(chain_label, cmd->rule.r.pos); + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_REPLACE: + stat = hipac_replace(chain_label, &cmd->rule.r); + if (stat != HE_OK) { + hipac_destroy_exec_nl(&cmd->rule.r); + } + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_FLUSH: + stat = hipac_flush_chain(chain_label); + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_NEW_CHAIN: + stat = hipac_new_chain(chain_label); + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_DELETE_CHAIN: + stat = hipac_delete_chain(chain_label); + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_RENAME_CHAIN: + stat = hipac_rename_chain(chain_label, + cmd->chain.newlabel); + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_SET_POLICY: + stat = hipac_set_policy(chain_label, cmd->chain.policy); + nlhp_send_reply(skb, nlh, stat); + break; + case CMD_LIST: + { + if (linfo.inf != NULL) { + if (hipac_free_chain_infos(linfo.inf) != HE_OK) { + /* this should never happen */ + printk(KERN_ERR "%s: hipac_free_chain_info" + " failed\n", __FUNCTION__); + } + linfo.inf = NULL; + linfo.len = 0; + } + stat = hipac_get_chain_infos(chain_label, &linfo.inf, + &linfo.len); + if (stat < 0) { + linfo.inf = NULL; + linfo.len = 0; + nlhp_send_reply(skb, nlh, stat); + return 1; + } + if (netlink_dump_start(nfhp_sock, skb, nlh, nlhp_list, + nlhp_done) != 0) { + printk(KERN_ERR "netlink_dump_start failed\n"); + return 1; + } + /* nlhp_done will or already has released nlhp_lock so + don't release it again */ + return 0; + } + default: + printk(KERN_ERR "invalid command type although " + "cmd_check_init reported a valid command\n"); + nlhp_send_reply(skb, nlh, NFHE_NOMSG); + break; + } + return 1; +} + + +/* + * initialization, finalization + */ + +static int +__init init(void) +{ + struct sysinfo sys; + u64 total_mem; + int ret; + + si_meminfo(&sys); + total_mem = (u64) sys.totalram << PAGE_SHIFT; + + /* initialize hipac layer */ + if (hipac_init(dim2btype, extract, + sizeof(dim2btype) / sizeof(*dim2btype), + hipac_copy_constructor, hipac_destroy_exec, + hipac_match_exec, hipac_target_exec, hipac_eq_exec, + total_mem >> 1) != HE_OK) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "initialize hipac algorithm\n"); + return -ENOMEM; + } + if (hipac_new("INPUT", "__/INPUT_INTERN\\__", TARGET_ACCEPT, + NFHP_ORIGIN_INPUT, &hipac_input) != HE_OK) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "create hipac data structure for input hook\n"); + ret = -ENOMEM; + goto cleanup_hipac; + } + if (hipac_new("FORWARD", "__/FORWARD_INTERN\\__", TARGET_ACCEPT, + NFHP_ORIGIN_FORWARD, &hipac_forward) != HE_OK) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "create hipac data structure for forward hook\n"); + ret = -ENOMEM; + goto cleanup_hipac; + } + if (hipac_new("OUTPUT", "__/OUTPUT_INTERN\\__", TARGET_ACCEPT, + NFHP_ORIGIN_OUTPUT, &hipac_output) != HE_OK) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "create hipac data structure for output hook\n"); + ret = -ENOMEM; + goto cleanup_hipac; + } + + /* register to netfilter */ + if ((ret = nf_register_hook(&input_op)) < 0) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "register input hook\n"); + goto cleanup_hipac; + } + if ((ret = nf_register_hook(&forward_op)) < 0) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "register forward hook\n"); + goto cleanup_input; + } + if ((ret = nf_register_hook(&output_op)) < 0) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "register output hook\n"); + goto cleanup_forward; + } + + /* initialize interface manager */ + if ((ret = nf_hipac_dev_init()) != 0) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "initialize device management\n"); + goto cleanup_output; + } + + /* initialize proc interface */ + hpproc_init(total_mem); + + /* enable netlink user communication */ + nfhp_sock = netlink_kernel_create(NLHP_PROTO, 0, nlhp_data_ready, THIS_MODULE); + if (nfhp_sock == NULL) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "create kernel netlink socket\n"); + ret = -ENOMEM; + goto cleanup_hpproc; + } + + /* start kernel thread */ + threadID = kernel_thread(nlhp_thread_func, NULL, CLONE_KERNEL); + if (threadID == 0) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "start kernel thread\n"); + ret = -EIO; + goto cleanup_netlink; + } + + printk(KERN_INFO "nf_hipac: (C) 2002-2003 HIPAC core team " + "(Michael Bellion, Thomas Heinz)\n"); + printk(KERN_INFO "nf_hipac: (C) 2004-2005 MARA Systems AB " + "(Michael Bellion)\n"); + return 0; + +cleanup_netlink: + if (nfhp_sock == NULL || + nfhp_sock->sk_socket == NULL) { + /* this should never happen */ + printk(KERN_ERR "nfhp_sock is broken\n"); + } else { + sock_release(nfhp_sock->sk_socket); + } +cleanup_hpproc: + hpproc_exit(); + nf_hipac_dev_exit(); +cleanup_output: + nf_unregister_hook(&output_op); +cleanup_forward: + nf_unregister_hook(&forward_op); +cleanup_input: + nf_unregister_hook(&input_op); +cleanup_hipac: + hipac_exit(); + return ret; +} + +static void +__exit fini(void) +{ + /* wait for ongoing netlink or proc operations to finish */ + down(&nlhp_lock); + kill_proc(threadID, SIGTERM, 1); + wait_for_completion(&thread_comp); + if (nfhp_sock == NULL || + nfhp_sock->sk_socket == NULL) { + /* this should never happen */ + printk(KERN_ERR "nfhp_sock is broken\n"); + } else { + sock_release(nfhp_sock->sk_socket); + } + if (linfo.inf != NULL && + hipac_free_chain_infos(linfo.inf) != HE_OK) { + /* this should never happen */ + printk(KERN_ERR "%s: hipac_free_chain_info" + " failed\n", __FUNCTION__); + } + hpproc_exit(); + nf_hipac_dev_exit(); + nf_unregister_hook(&input_op); + nf_unregister_hook(&forward_op); + nf_unregister_hook(&output_op); + hipac_exit(); + up(&nlhp_lock); +} + + +module_init(init); +module_exit(fini); +MODULE_AUTHOR("Michael Bellion and Thomas Heinz"); +MODULE_DESCRIPTION("NF-HIPAC - netfilter high performance " + "packet classification"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(nfhp_register_cthelp); +EXPORT_SYMBOL(nfhp_unregister_cthelp); + diff -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_mod.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_mod.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_mod.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_mod.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,47 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * Licenced under the GNU General Public Licence, version 2. + */ + + +#ifndef _NFHP_MOD_H +#define _NFHP_MOD_H + +#include +#include + +/* 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_proc.c ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_proc.c --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_proc.c 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_proc.c 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,888 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * Licenced under the GNU General Public Licence, version 2. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nfhp_mod.h" +#include "hipac.h" + +#define BT_I "rlp_input" +#define BT_F "rlp_forward" +#define BT_O "rlp_output" +#define DT_I "dimtree_input" +#define DT_F "dimtree_forward" +#define DT_O "dimtree_output" +#define HP_I "hipac_rules_input" +#define HP_F "hipac_rules_forward" +#define HP_O "hipac_rules_output" +#define HP_C "hipac_chains" +#define MEM "mem" +#define INF "info" + +#define INF_M S_IRUGO | S_IWUSR +#define OTH_M S_IRUSR + +struct proc_data +{ + char *text; + void *stat; + u32 len, valid_len; + rwlock_t lock; + void *hipac; + char *hipac_name; +}; + +struct nfhp_proc_entry +{ + const char *name; + struct proc_dir_entry *entry; + struct proc_dir_entry *parent; + mode_t mode; + read_proc_t *read_fn; + write_proc_t *write_fn; + void *hipac; + char *hipac_name; + u32 text_mem_required; + u32 stat_mem_required; +}; +static struct proc_dir_entry *nfhipac_dir, *stat_dir; +static const char proc_nfhipac_dir[] = "nf-hipac"; +static const char proc_stat_dir[] = "statistics"; + +static write_proc_t info_write; +static read_proc_t info_read; +static read_proc_t mem_read; +static read_proc_t rlp_read; +static read_proc_t dimtree_read; +static read_proc_t hipac_r_read; +static read_proc_t hipac_c_read; + +/* the non constant members are initialized by init_nfhp_proc() */ +static struct nfhp_proc_entry nfhp_proc[] = +{ + { INF, NULL, NULL, INF_M, info_read, info_write, NULL, NULL, + 1000, sizeof(struct hipac_user_stat) }, + + { MEM, NULL, NULL, OTH_M, mem_read, NULL, NULL, NULL, + 2000, sizeof(struct hipac_mem_stat) }, + + { BT_I, NULL, NULL, OTH_M, rlp_read, NULL, NULL, "INPUT", + 25000, sizeof(struct hipac_rlp_stat) }, + + { BT_F, NULL, NULL, OTH_M, rlp_read, NULL, NULL, "FORWARD", + 25000, sizeof(struct hipac_rlp_stat) }, + + { BT_O, NULL, NULL, OTH_M, rlp_read, NULL, NULL, "OUTPUT", + 25000, sizeof(struct hipac_rlp_stat) }, + + { DT_I, NULL, NULL, OTH_M, dimtree_read, NULL, NULL, "INPUT", + 3000, sizeof(struct hipac_dimtree_stat) }, + + { DT_F, NULL, NULL, OTH_M, dimtree_read, NULL, NULL, "FORWARD", + 3000, sizeof(struct hipac_dimtree_stat) }, + + { DT_O, NULL, NULL, OTH_M, dimtree_read, NULL, NULL, "OUTPUT", + 3000, sizeof(struct hipac_dimtree_stat) }, + + { HP_I, NULL, NULL, OTH_M, hipac_r_read, NULL, NULL, "INPUT", + 3000, sizeof(struct hipac_rule_stat) }, + + { HP_F, NULL, NULL, OTH_M, hipac_r_read, NULL, NULL, "FORWARD", + 3000, sizeof(struct hipac_rule_stat) }, + + { HP_O, NULL, NULL, OTH_M, hipac_r_read, NULL, NULL, "OUTPUT", + 3000, sizeof(struct hipac_rule_stat) }, + + { HP_C, NULL, NULL, OTH_M, hipac_c_read, NULL, NULL, NULL, + 4000, sizeof(struct hipac_chain_stat) } +}; + +static const char indent_spc[] = " "; +static u64 nfhp_total_mem = 0; + + + +/* + * helpers + */ + +static inline void +init_nfhp_proc(struct proc_dir_entry *nfhipac_dir, + struct proc_dir_entry *stat_dir) +{ + int i; + + for (i = 0; i < sizeof(nfhp_proc) / sizeof(*nfhp_proc); i++) { + if (nfhp_proc[i].write_fn == info_write) { + nfhp_proc[i].parent = nfhipac_dir; + } else { + nfhp_proc[i].parent = stat_dir; + } + if (nfhp_proc[i].hipac_name == NULL) { + continue; + } + if (strcmp(nfhp_proc[i].hipac_name, "INPUT") == 0) { + nfhp_proc[i].hipac = hipac_input; + } else if (strcmp(nfhp_proc[i].hipac_name, "FORWARD") == 0) { + nfhp_proc[i].hipac = hipac_forward; + } else { + nfhp_proc[i].hipac = hipac_output; + } + } +} + +static inline int +init_data(struct proc_data *data, const struct nfhp_proc_entry *e) +{ + data->text = kmalloc(e->text_mem_required, GFP_KERNEL); + if (data->text == NULL) { + return -1; + } + data->stat = kmalloc(e->stat_mem_required, GFP_KERNEL); + if (data->stat == NULL) { + kfree(data->text); + return -1; + } + data->len = e->text_mem_required; + data->valid_len = 0; + data->lock = RW_LOCK_UNLOCKED; + data->hipac = e->hipac; + data->hipac_name = e->hipac_name; + return 0; +} + +static inline void +free_data(struct proc_data *data) +{ + if (data == NULL) { + return; + } + if (data->text != NULL) { + kfree(data->text); + } + if (data->stat != NULL) { + kfree(data->stat); + } + kfree(data); +} + +static inline void +print_inline(struct proc_data *data, int indent) +{ + int i; + + for (i = 0; i < indent; i++) { + data->valid_len += sprintf(data->text + data->valid_len, + indent_spc); + } +} + +static int +print_desc(struct proc_data *data, int indent, const char *desc) +{ + if (data->len < data->valid_len + indent * strlen(indent_spc) + + strlen(desc)) { + /* this should never happen */ + printk(KERN_ERR "%s: too little memory reserved\n", + __FUNCTION__); + return -1; + } + print_inline(data, indent); + data->valid_len += sprintf(data->text + data->valid_len, desc); + return 0; +} + +static int +print_scalar(struct proc_data *data, int indent, const char *desc, u64 val) +{ + if (data->len < data->valid_len + indent * strlen(indent_spc) + + strlen(desc) + 22) { + /* this should never happen */ + printk(KERN_ERR "%s: too little memory reserved\n", + __FUNCTION__); + return -1; + } + print_inline(data, indent); + data->valid_len += sprintf(data->text + data->valid_len, desc); + data->valid_len += sprintf(data->text + data->valid_len, + " %9llu\n", val); + return 0; +} + +static int +print_map(struct proc_data *data, int indent, const char *desc, + u32 map[], int len) +{ + int i, empty = 1; + + if (data->len < data->valid_len + (1 + len) * indent * + strlen(indent_spc) + strlen(desc) + 1 + len * 25) { + /* this should never happen */ + printk(KERN_ERR "%s: too little memory reserved\n", + __FUNCTION__); + return -1; + } + for (i = 0; i < len; i++) { + if (map[i] == 0) { + continue; + } + if (empty) { + empty = 0; + print_inline(data, indent); + data->valid_len += sprintf(data->text + + data->valid_len, desc); + data->valid_len += sprintf(data->text + + data->valid_len, "\n"); + } + print_inline(data, indent); + data->valid_len += sprintf(data->text + data->valid_len, + " %2u: %9u\n", i, map[i]); + } + return 0; +} + +static int +print_dist(struct proc_data *data, int indent, const char *desc, + u32 dist[], u32 len) +{ + int i, empty = 1; + + if (data->len < data->valid_len + (1 + len) * indent * + strlen(indent_spc) + strlen(desc) + 1 + (len - 1) * 39 + 38) { + /* this should never happen */ + printk(KERN_ERR "%s: too little memory reserved\n", + __FUNCTION__); + return -1; + } + if (len == 0) { + return 0; + } + for (i = 0; i < len - 1; i++) { + if (dist[i] == 0) { + continue; + } + if (empty) { + empty = 0; + print_inline(data, indent); + data->valid_len += sprintf(data->text + + data->valid_len, desc); + data->valid_len += sprintf(data->text + + data->valid_len, "\n"); + } + print_inline(data, indent); + data->valid_len += + sprintf(data->text + data->valid_len, + " [%9u, %9u]: %9u\n", + i == 0 ? 0 : 1 << (i - 1), (1 << i) - 1, + dist[i]); + } + if (dist[i] == 0) { + return 0; + } + if (empty) { + print_inline(data, indent); + data->valid_len += sprintf(data->text + data->valid_len, desc); + data->valid_len += sprintf(data->text + data->valid_len, "\n"); + } + print_inline(data, indent); + data->valid_len += sprintf(data->text + data->valid_len, + " [%9u, infinity[: %9u\n", 1 << (i - 1), + dist[i]); + return 0; +} + +static int +write_stat(char *buf, char **start, off_t off, int count, int *eof, + struct proc_data *d) +{ + int len = d->valid_len - off; + + if (len <= 0) { + *eof = 1; + return 0; + } + if (len <= count) { + *eof = 1; + } else { + len = count; + } + read_lock(&d->lock); + memcpy(buf, d->text + off, len); + read_unlock(&d->lock); + *start = buf; + return len; +} + + + +/* + * i/o functions + */ + +static int +info_write(struct file *file, const char *buffer, unsigned long count, + void *data) +{ + static const char nfhp_first[] = "nf-hipac-first\n"; + static const char ipt_first[] = "iptables-first\n"; + static char buf[32] = {0}; + int len = count > sizeof(buf) - 1 ? sizeof(buf) - 1 : count; + u64 new_max_mem; + int ret; + + if (copy_from_user(buf, buffer, len)) { + return -EFAULT; + } + + /* strings don't have to contain \n at the end */ + if (!(count == sizeof(nfhp_first) - 1 || + count == sizeof(ipt_first) - 1 || + count == sizeof(nfhp_first) - 2 || + count == sizeof(ipt_first) - 2)) { + if (count >= 9 && !(count == 10 && buf[9] != '\n')) { + /* input definitely too large */ + return -EINVAL; + } + + /* interpret as number */ + new_max_mem = simple_strtoul(buf, NULL, 10) << 20; + if (new_max_mem > nfhp_total_mem) { + new_max_mem = nfhp_total_mem; + } + if (new_max_mem == hipac_get_maxmem()) { + return len; + } + down(&nlhp_lock); + switch (hipac_set_maxmem(new_max_mem)) { + case HE_LOW_MEMORY: + up(&nlhp_lock); + printk(KERN_NOTICE "nf_hipac: actual memory " + "consumption larger than memory bound " + "written to " INF "\n"); + return -EINVAL; + case HE_OK: + up(&nlhp_lock); + return len; + default: + /* this should never happen */ + up(&nlhp_lock); + printk(KERN_ERR "%s: unexpected return value\n", + __FUNCTION__); + return -EINVAL; + } + } + + /* change order */ + if (strncmp(buf, nfhp_first, len) == 0) { + if (input_op.priority >= NF_IP_PRI_FILTER) { + nf_unregister_hook(&input_op); + nf_unregister_hook(&forward_op); + nf_unregister_hook(&output_op); + input_op.priority = forward_op.priority = + output_op.priority = NF_IP_PRI_FILTER - 1; + goto hook_register; + } + } else if (strncmp(buf, ipt_first, len) == 0) { + if (input_op.priority <= NF_IP_PRI_FILTER) { + nf_unregister_hook(&input_op); + nf_unregister_hook(&forward_op); + nf_unregister_hook(&output_op); + input_op.priority = forward_op.priority = + output_op.priority = NF_IP_PRI_FILTER + 1; + goto hook_register; + } + } + return len; + +hook_register: + if ((ret = nf_register_hook(&input_op)) < 0) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "register input hook\n"); + goto cleanup; + } + if ((ret = nf_register_hook(&forward_op)) < 0) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "register forward hook\n"); + goto cleanup_input; + } + if ((ret = nf_register_hook(&output_op)) < 0) { + printk(KERN_ERR "nf_hipac: initialization failed: unable to " + "register output hook\n"); + goto cleanup_forward; + } + return len; +cleanup_forward: + nf_unregister_hook(&forward_op); +cleanup_input: + nf_unregister_hook(&input_op); +cleanup: + return ret; +} + +/* + the statistics are being rebuilt if the proc entry is read from its + beginning; if you modify the ruleset while at the same time reading + a proc file with a pager strange things might happen to your pager + output ;-) + nonetheless this is the best we can do ... at least I think so :-) +*/ + +#define NEED_REBUILD (off == 0 || d->valid_len == 0) +#define LEN(x) (sizeof(x) / sizeof(*(x))) +#define EXEC(fn) \ +do { \ + if (fn < 0) { \ + goto error; \ + } \ +} while (0) + +static int +info_read(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + struct proc_data *d = data; + struct hipac_user_stat *stat = d->stat; + + if (!NEED_REBUILD) { + return write_stat(page, start, off, count, eof, d); + } + + /* (re)compute statistics */ + down(&nlhp_lock); + if (hipac_get_user_stat(stat) != HE_OK) { + /* this should never happen */ + up(&nlhp_lock); + printk(KERN_ERR "%s: hipac_get_user_stat failed\n", + __FUNCTION__); + *eof = 1; + return 0; + } + up(&nlhp_lock); + + /* (re)build text */ + write_lock(&d->lock); + d->valid_len = 0; + EXEC(print_scalar(d, 0, "maximum memory bound: ", + hipac_get_maxmem())); + EXEC(print_scalar(d, 0, "total memory (used): ", + stat->total_mem_tight)); + EXEC(print_scalar(d, 0, "total memory (allocated):", + stat->total_mem_real)); + EXEC(print_scalar(d, 0, "total number of chains: ", + stat->chain_num)); + EXEC(print_scalar(d, 0, "total number of rules: ", + stat->rule_num)); + if (input_op.priority < NF_IP_PRI_FILTER) { + EXEC(print_desc(d, 0, "nf-hipac is invoked before " + "iptables\n")); + } else { + EXEC(print_desc(d, 0, "iptables is invoked before " + "nf-hipac\n")); + } +#ifdef SINGLE_PATH + EXEC(print_desc(d, 0, "compiled with SINGLE_PATH optimization\n")); +#else + EXEC(print_desc(d, 0, "compiled without SINGLE_PATH optimization\n")); +#endif + write_unlock(&d->lock); + return write_stat(page, start, off, count, eof, d); + + error: + write_unlock(&d->lock); + *eof = 1; + return 0; +} + +static int +mem_read(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + struct proc_data *d = data; + struct hipac_mem_stat *stat = d->stat; + + if (!NEED_REBUILD) { + return write_stat(page, start, off, count, eof, d); + } + + /* (re)compute statistics */ + down(&nlhp_lock); + if (hipac_get_mem_stat(stat) != HE_OK) { + /* this should never happen */ + up(&nlhp_lock); + printk(KERN_ERR "%s: hipac_get_mem_stat failed\n", + __FUNCTION__); + *eof = 1; + return 0; + } + up(&nlhp_lock); + + /* (re)build text */ + write_lock(&d->lock); + d->valid_len = 0; + EXEC(print_scalar(d, 0, "total memory (used): ", + stat->total_mem_tight)); + EXEC(print_scalar(d, 0, "total memory (allocated):", + stat->total_mem_real)); + EXEC(print_desc(d, 0, "memhash:\n")); + EXEC(print_scalar(d, 1, "number of entries: ", + stat->memhash_elem_num)); + EXEC(print_scalar(d, 1, "number of buckets: ", + stat->memhash_len)); + EXEC(print_scalar(d, 1, "number of entries in smallest bucket: ", + stat->memhash_smallest_bucket_len)); + EXEC(print_scalar(d, 1, "number of entries in largest bucket: ", + stat->memhash_biggest_bucket_len)); + EXEC(print_dist(d, 1, "number of buckets with [x, y] entries:", + stat->memhash_bucket_stat, + LEN(stat->memhash_bucket_stat))); + write_unlock(&d->lock); + return write_stat(page, start, off, count, eof, d); + + error: + write_unlock(&d->lock); + *eof = 1; + return 0; +} + +static int +rlp_read(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + static char buf[100] = {0}; + struct proc_data *d = data; + struct hipac_rlp_stat *stat = d->stat; + int i; + + if (!NEED_REBUILD) { + return write_stat(page, start, off, count, eof, d); + } + + /* (re)compute statistics */ + down(&nlhp_lock); + if (hipac_get_rlp_stat(d->hipac, stat) != HE_OK) { + /* this should never happen */ + up(&nlhp_lock); + printk(KERN_ERR "%s: hipac_get_rlp_stat failed\n", + __FUNCTION__); + *eof = 1; + return 0; + } + up(&nlhp_lock); + + /* (re)build text */ + write_lock(&d->lock); + d->valid_len = 0; + EXEC(print_desc(d, 0, "root chain: ")); + EXEC(print_desc(d, 0, d->hipac_name)); + EXEC(print_desc(d, 0, "\n")); + EXEC(print_scalar(d, 0, "total memory (used): ", + stat->total_mem_tight)); + EXEC(print_scalar(d, 0, "total memory (allocated): ", + stat->total_mem_real)); + EXEC(print_scalar(d, 1, "rlp memory (used): ", + stat->rlp_mem_tight)); + EXEC(print_scalar(d, 1, "rlp memory (allocated): ", + stat->rlp_mem_real)); + EXEC(print_scalar(d, 1, "termrule memory (used): ", + stat->termrule_mem_tight)); + EXEC(print_scalar(d, 1, "termrule memory (allocated):", + stat->termrule_mem_real)); + EXEC(print_scalar(d, 0, "number of rlps: ", + stat->rlp_num)); + EXEC(print_map(d, 1, "number of rlps in dimid x:", + stat->rlp_dimid_num, LEN(stat->rlp_dimid_num))); + EXEC(print_map(d, 1, "number of rlps in depth x:", + stat->rlp_depth_num, LEN(stat->rlp_depth_num))); + EXEC(print_scalar(d, 0, "number of termrule blocks: ", + stat->termrule_num)); + EXEC(print_scalar(d, 0, "total number of termrule entries:", + stat->termrule_ptr_num)); + EXEC(print_scalar(d, 0, "number of keys: ", + stat->keys_num)); + for (i = 0; i < LEN(stat->rlp_dimid_keys_stat); i++) { + if (snprintf(buf, sizeof(buf) - 1, "number of rlps in dimid" + " %d with [x, y] keys:", i) < 0) { + printk(KERN_ERR "%s: static buffer too small\n", + __FUNCTION__); + break; + } + EXEC(print_dist(d, 1, buf, stat->rlp_dimid_keys_stat[i], + LEN(*stat->rlp_dimid_keys_stat))); + } + EXEC(print_scalar(d, 0, "number of terminal pointers: ", + stat->termptr_num)); + EXEC(print_map(d, 1, "number of terminal pointers in dimid x:", + stat->termptr_dimid_num, + LEN(stat->termptr_dimid_num))); + EXEC(print_map(d, 1, "number of terminal pointers in depth x:", + stat->termptr_depth_num, + LEN(stat->termptr_depth_num))); + EXEC(print_scalar(d, 0, "number of non-terminal pointers: ", + stat->nontermptr_num)); + EXEC(print_map(d, 1, "number of non-terminal pointers in dimid x:", + stat->nontermptr_dimid_num, + LEN(stat->nontermptr_dimid_num))); + EXEC(print_map(d, 1, "number of non-terminal pointers in depth x:", + stat->nontermptr_depth_num, + LEN(stat->nontermptr_depth_num))); + EXEC(print_scalar(d, 0, "number of dt_elem structs: ", + stat->dt_elem_num)); + EXEC(print_scalar(d, 1, "total number of dt_elem entries:" + " ", stat->dt_elem_ptr_num)); + EXEC(print_dist(d, 1, "number of dt_elem structs with [x, y] entries:", + stat->dt_elem_stat, LEN(stat->dt_elem_stat))); + write_unlock(&d->lock); + return write_stat(page, start, off, count, eof, d); + + error: + write_unlock(&d->lock); + *eof = 1; + return 0; +} + +static int +dimtree_read(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + struct proc_data *d = data; + struct hipac_dimtree_stat *stat = d->stat; + + if (!NEED_REBUILD) { + return write_stat(page, start, off, count, eof, d); + } + + /* (re)compute statistics */ + down(&nlhp_lock); + if (hipac_get_dimtree_stat(d->hipac, stat) != HE_OK) { + /* this should never happen */ + up(&nlhp_lock); + printk(KERN_ERR "%s: hipac_get_dimtree_stat failed\n", + __FUNCTION__); + *eof = 1; + return 0; + } + up(&nlhp_lock); + + /* (re)build text */ + write_lock(&d->lock); + d->valid_len = 0; + EXEC(print_desc(d, 0, "root chain: ")); + EXEC(print_desc(d, 0, d->hipac_name)); + EXEC(print_desc(d, 0, "\n")); + EXEC(print_scalar(d, 0, "chain memory (used): ", + stat->chain_mem_tight)); + EXEC(print_scalar(d, 0, "chain memory (allocated): ", + stat->chain_mem_real)); + EXEC(print_scalar(d, 0, "number of rules: ", + stat->rule_num)); + EXEC(print_scalar(d, 1, "number of rules with ipt matches: " + " ", stat->rules_with_exec_matches)); + EXEC(print_scalar(d, 1, "number of rules with ipt target: " + " ", stat->rules_with_exec_target)); + EXEC(print_dist(d, 1, "number of \"same pos rules\" series of " + "length [x, y]:", stat->rules_same_pos_stat, + LEN(stat->rules_same_pos_stat))); + EXEC(print_map(d, 0, "number of rules with x dt_matches:", + stat->dt_match_stat, LEN(stat->dt_match_stat))); + write_unlock(&d->lock); + return write_stat(page, start, off, count, eof, d); + + error: + write_unlock(&d->lock); + *eof = 1; + return 0; +} + +static int +hipac_r_read(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + struct proc_data *d = data; + struct hipac_rule_stat *stat = d->stat; + + if (!NEED_REBUILD) { + return write_stat(page, start, off, count, eof, d); + } + + /* (re)compute statistics */ + down(&nlhp_lock); + if (hipac_get_rule_stat(d->hipac, stat) != HE_OK) { + /* this should never happen */ + up(&nlhp_lock); + printk(KERN_ERR "%s: hipac_get_rule_stat failed\n", + __FUNCTION__); + *eof = 1; + return 0; + } + up(&nlhp_lock); + + /* (re)build text */ + write_lock(&d->lock); + d->valid_len = 0; + EXEC(print_desc(d, 0, "root chain: ")); + EXEC(print_desc(d, 0, d->hipac_name)); + EXEC(print_desc(d, 0, "\n")); + EXEC(print_scalar(d, 0, "number of rules: ", + stat->rule_num)); + EXEC(print_scalar(d, 1, "number of rules with ipt matches: ", + stat->exec_match_num)); + EXEC(print_scalar(d, 1, "number of rules with ipt target: ", + stat->exec_target_num)); + EXEC(print_scalar(d, 1, "number of rules with jump target: ", + stat->jump_target_num)); + EXEC(print_scalar(d, 1, "number of rules with return target:", + stat->return_target_num)); + EXEC(print_map(d, 0, "number of rules with x hipac_matches: ", + stat->hipac_match_stat, LEN(stat->hipac_match_stat))); + EXEC(print_map(d, 0, "number of rules with x inverted matches:", + stat->inv_rules_stat, LEN(stat->inv_rules_stat))); + write_unlock(&d->lock); + return write_stat(page, start, off, count, eof, d); + + error: + write_unlock(&d->lock); + *eof = 1; + return 0; +} + +static int +hipac_c_read(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + struct proc_data *d = data; + struct hipac_chain_stat *stat = d->stat; + + if (!NEED_REBUILD) { + return write_stat(page, start, off, count, eof, d); + } + + /* (re)compute statistics */ + down(&nlhp_lock); + if (hipac_get_chain_stat(stat) != HE_OK) { + /* this should never happen */ + up(&nlhp_lock); + printk(KERN_ERR "%s: hipac_get_chain_stat failed\n", + __FUNCTION__); + *eof = 1; + return 0; + } + up(&nlhp_lock); + + /* (re)build text */ + write_lock(&d->lock); + d->valid_len = 0; + EXEC(print_scalar(d, 0, "chain memory (used): ", stat->mem_tight)); + EXEC(print_scalar(d, 0, "chain memory (allocated):", stat->mem_real)); + EXEC(print_scalar(d, 0, "number of chains: ", stat->chain_num)); + EXEC(print_scalar(d, 0, "number of rules: ", stat->rule_num)); + EXEC(print_dist(d, 1, "number of chains with [x, y] prefixes: ", + stat->prefix_stat, LEN(stat->prefix_stat))); + EXEC(print_dist(d, 1, "number of chains with [x, y] incoming arcs:", + stat->incoming_stat, LEN(stat->incoming_stat))); + EXEC(print_dist(d, 1, "number of chains with [x, y] outgoing arcs:", + stat->outgoing_stat, LEN(stat->outgoing_stat))); + write_unlock(&d->lock); + return write_stat(page, start, off, count, eof, d); + + error: + write_unlock(&d->lock); + *eof = 1; + return 0; +} + +void +hpproc_init(u64 total_mem) +{ + struct proc_data *data; + int i, j; + + nfhp_total_mem = total_mem; + + /* create proc directories */ + nfhipac_dir = proc_mkdir(proc_nfhipac_dir, proc_net); + if (nfhipac_dir == NULL) { + printk(KERN_NOTICE "nf_hipac: unable to create proc " + "directory\n"); + return; + } + nfhipac_dir->owner = THIS_MODULE; + stat_dir = proc_mkdir(proc_stat_dir, nfhipac_dir); + if (stat_dir == NULL) { + printk(KERN_NOTICE "nf_hipac: unable to create proc " + "directory\n"); + goto cleanup_nfhipac_dir; + } + stat_dir->owner = THIS_MODULE; + + /* create statistics entries */ + init_nfhp_proc(nfhipac_dir, stat_dir); + for (i = 0; i < sizeof(nfhp_proc) / sizeof(*nfhp_proc); i++) { + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) { + printk(KERN_NOTICE "nf_hipac: unable to create " + "proc infrastructure because of low memory\n"); + goto cleanup; + } + if (init_data(data, &nfhp_proc[i]) < 0) { + printk(KERN_NOTICE "nf_hipac: unable to create " + "proc infrastructure because of low memory\n"); + goto cleanup; + } + nfhp_proc[i].entry = create_proc_entry(nfhp_proc[i].name, + nfhp_proc[i].mode, + nfhp_proc[i].parent); + if (nfhp_proc[i].entry == NULL) { + printk(KERN_NOTICE "nf_hipac: unable to create proc " + "entry\n"); + goto cleanup; + } + nfhp_proc[i].entry->owner = THIS_MODULE; + nfhp_proc[i].entry->data = data; + nfhp_proc[i].entry->read_proc = nfhp_proc[i].read_fn; + nfhp_proc[i].entry->write_proc = nfhp_proc[i].write_fn; + } + return; + + cleanup: + for (j = 0; j <= i; j++) + remove_proc_entry(nfhp_proc[j].name, nfhp_proc[i].parent); + remove_proc_entry(proc_stat_dir, nfhipac_dir); + cleanup_nfhipac_dir: + remove_proc_entry(proc_nfhipac_dir, proc_net); + return; +} + +void +hpproc_exit(void) +{ + int i; + + for (i = 0; i < sizeof(nfhp_proc) / sizeof(*nfhp_proc); i++) + remove_proc_entry(nfhp_proc[i].name, nfhp_proc[i].parent); + remove_proc_entry(proc_stat_dir, nfhipac_dir); + remove_proc_entry(proc_nfhipac_dir, proc_net); +} diff -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_proc.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_proc.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_proc.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/nfhp_proc.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,24 @@ +/* + * High performance packet classification + * + * + * (c) 2002-2003 hipac core team : + * +---------------------------+--------------------------+ + * | Michael Bellion | Thomas Heinz | + * | | | + * +---------------------------+--------------------------+ + * + * 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/rlp.c ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/rlp.c --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/rlp.c 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/rlp.c 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,537 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * 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 -uNr linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/rlp.h ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/rlp.h --- linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/rlp.h 1970-01-01 09:00:00.000000000 +0900 +++ ../linux-2.6.16.16/net/ipv4/netfilter/nf-hipac/rlp.h 2006-05-28 23:38:08.000000000 +0900 @@ -0,0 +1,142 @@ +/* + * High performance packet classification + * + * + * (c) 2004-2005 MARA Systems AB + * +-----------------------------+ + * | Michael Bellion | + * | | + * +-----------------------------+ + * + * 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 --------------090408020704070107000703--