/*
* High performance packet classification
* <http://www.hipac.org>
*
* (c) 2004-2005 MARA Systems AB <http://www.marasystems.com>
* +-----------------------------+
* | Michael Bellion |
* | <michael@marasystems.com> |
* +-----------------------------+
*
* (c) 2002-2003 hipac core team <nf@hipac.org>:
* +---------------------------+--------------------------+
* | Michael Bellion | Thomas Heinz |
* | <mbellion@hipac.org> | <creatix@hipac.org> |
* +---------------------------+--------------------------+
*
* Licenced under the GNU General Public Licence, version 2.
*/
#include "global.h"
#include "ihash.h"
#include "rlp.h"
#include "dimtree.h"
#define HAS_DT_MATCH(rule) ((rule)->dt_match_len > 0)
#define ITH_DT_MATCH(rule, i) ((rule)->first_dt_match + (i))
#define LAST_DT_MATCH(rule) ITH_DT_MATCH(rule, (rule)->dt_match_len - 1)
#define LEN(array) (sizeof(array) / sizeof(*(array)))
/*
* newspec keeps track of the rlps and elementary intervals that have been
* newly allocated during a series of dimtree operations;
* orgspec keeps track of the rlps and elementary intervals that can be
* freed after the series of dimtree operations has been successfully finished
*/
static struct ptrlist orgspec = {LIST_HEAD_INIT(orgspec.head), 0};
static struct ihash *newspec = NULL;
static inline void
elem_free(struct dt_elem *e)
{
if (unlikely(e == NULL)) {
ARG_MSG;
return;
}
hp_free(e);
}
/* free s which can be an elemtary interval or a rlp */
static inline void
rlp_elem_free(struct gen_spec *s)
{
if (unlikely(s == NULL)) {
ARG_MSG;
return;
}
if (IS_RLP(s)) {
rlp_free((struct rlp_spec *) s);
} else {
/* s must be elemtary interval */
assert(IS_ELEM(s));
elem_free((struct dt_elem *) s);
}
}
/* set newspec bit of s which can be an elementary interval or a rlp to 0 */
static inline void
rlp_elem_newspec_set(struct gen_spec *s, int newspec_set)
{
if (unlikely(s == NULL)) {
ARG_MSG;
return;
}
if (IS_RLP(s)) {
((struct rlp_spec *) s)->newspec = !!newspec_set;
} else {
/* s must be elemtary interval */
assert(IS_ELEM(s));
((struct dt_elem_spec *) s)->newspec = !!newspec_set;
}
}
/* call rlp_elem_free for each member of orgspec and empty orgspec */
static inline void
orgspec_dofree(void)
{
struct list_head *lh;
struct ptrlist_entry* e;
for (lh = orgspec.head.next; lh != &orgspec.head;) {
e = list_entry(lh, struct ptrlist_entry, head);
lh = lh->next;
assert((IS_RLP(e->p) &&
!((struct rlp_spec *) e->p)->newspec) ||
(IS_ELEM(e->p) &&
!((struct dt_elem_spec *) e->p)->newspec));
rlp_elem_free(e->p);
mini_free(e);
}
INIT_LIST_HEAD(&orgspec.head);
orgspec.len = 0;
}
/* call rlp_elem_free for each member of newspec and empty newspec */
static inline void
newspec_dofree(void)
{
if (unlikely(newspec == NULL)) {
return;
}
IHASH_KEY_ITERATE(newspec, struct gen_spec *, rlp_elem_free);
ihash_free(newspec);
newspec = NULL;
}
/* add s to orgspec;
possible errors: HE_LOW_MEMORY, HE_IMPOSSIBLE_CONDITION */
static inline hipac_error
orgspec_add(struct gen_spec *s)
{
if (unlikely(s == NULL)) {
ARG_ERR;
}
assert((IS_RLP(s) && !((struct rlp_spec *) s)->newspec) ||
(IS_ELEM(s) && !((struct dt_elem_spec *) s)->newspec));
#ifdef DEBUG
return ptrlist_add(&orgspec, s, 1);
#else
return ptrlist_add(&orgspec, s, 0);
#endif
}
/* empty orgspec */
static inline void
orgspec_flush(void)
{
ptrlist_flush(&orgspec);
}
/* empty newspec; if newspec_reset is not 0 the newspec bit is set
to 0 for each element of newspec */
static inline void
newspec_flush(int newspec_reset)
{
if (unlikely(newspec == NULL)) {
return;
}
if (newspec_reset) {
IHASH_KEY_ITERATE(newspec, struct gen_spec *,
rlp_elem_newspec_set, 0);
}
ihash_free(newspec);
newspec = NULL;
}
/*
* history operations
*/
static void
history_undo(void)
{
newspec_dofree();
orgspec_flush();
}
static void
history_commit(int newspec_set)
{
orgspec_dofree();
newspec_flush(newspec_set);
}
#ifdef DEBUG
/* return 1 if orgspec and newspec are empty and 0 otherwise */
static int
history_is_empty(void)
{
return newspec == NULL && list_empty(&orgspec.head);
}
#endif
/* s is a new rlp or elementary interval layer which does __not__
replace another */
static hipac_error
history_new(struct gen_spec *s, int newspec_set)
{
int stat;
if (unlikely(s == NULL)) {
ARG_ERR;
}
assert((IS_RLP(s) || IS_ELEM(s)));
if (unlikely(newspec == NULL)) {
newspec = ihash_new(INITIAL_NEWSPEC_LEN, 0,
NEWSPEC_AVRG_ELEM_PER_BUCKET,
ihash_func_val, eq_val);
if (newspec == NULL) {
return HE_LOW_MEMORY;
}
}
stat = ihash_insert(&newspec, s, NULL);
if (stat < 0) {
return stat;
}
if (newspec_set) {
rlp_elem_newspec_set(s, 1);
}
return stat;
}
static hipac_error
history_replace(struct gen_spec *old, struct gen_spec *new, int newspec_set)
{
int stat;
if (unlikely(old == NULL || new == NULL)) {
ARG_ERR;
}
assert((IS_RLP(old) && IS_RLP(new)) ||
(IS_ELEM(old) && IS_ELEM(new)));
assert(newspec_set ||
(IS_RLP(old) && !((struct rlp_spec *) old)->newspec) ||
(IS_ELEM(old) && !((struct dt_elem_spec *) old)->newspec));
assert(newspec_set ||
(IS_RLP(new) && !((struct rlp_spec *) new)->newspec) ||
(IS_ELEM(new) && !((struct dt_elem_spec *) new)->newspec));
if (unlikely(newspec == NULL)) {
if (newspec_set &&
((IS_RLP(old) &&
((struct rlp_spec *) old)->newspec) ||
(IS_ELEM(old) &&
((struct dt_elem_spec *) old)->newspec))) {
IMPOSSIBLE_CONDITION("old must be contained in new"
"spec but newspec is empty");
}
newspec = ihash_new(INITIAL_NEWSPEC_LEN, 0,
NEWSPEC_AVRG_ELEM_PER_BUCKET,
ihash_func_val, eq_val);
if (newspec == NULL) {
return HE_LOW_MEMORY;
}
}
if (newspec_set &&
((IS_RLP(old) && ((struct rlp_spec *) old)->newspec) ||
(IS_ELEM(old) && ((struct dt_elem_spec *) old)->newspec))) {
stat = ihash_replace(&newspec, old, NULL, new, NULL);
if (stat == HE_OK) {
rlp_elem_newspec_set(new, 1);
rlp_elem_free(old);
}
} else {
stat = orgspec_add(old);
if (stat < 0) {
return stat;
}
stat = ihash_insert(&newspec, new, NULL);
if (stat < 0) {
return stat;
}
if (newspec_set) {
rlp_elem_newspec_set(new, 1);
}
}
return stat;
}
/* s is an obsolete rlp or elementary interval layer */
static hipac_error
history_obsolete(struct gen_spec *s, int newspec_set)
{
if (unlikely(s == NULL)) {
ARG_ERR;
}
assert((IS_RLP(s) || IS_ELEM(s)));
assert(newspec_set ||
(IS_RLP(s) && !((struct rlp_spec *) s)->newspec) ||
(IS_ELEM(s) && !((struct dt_elem_spec *) s)->newspec));
if (unlikely(newspec == NULL && newspec_set &&
((IS_RLP(s) && ((struct rlp_spec *) s)->newspec) ||
(IS_ELEM(s) && ((struct dt_elem_spec *) s)->newspec)))) {
IMPOSSIBLE_CONDITION("s is obsolete, newspec_set is not 0 and"
" the newspec bit of s is set __but__ s "
"is not contained in newspec");
}
if (newspec_set &&
((IS_RLP(s) && ((struct rlp_spec *) s)->newspec) ||
(IS_ELEM(s) && ((struct dt_elem_spec *) s)->newspec))) {
if (ihash_delete(newspec, s, NULL) < 0) {
IMPOSSIBLE_CONDITION("unable to remove s from "
"newspec");
}
rlp_elem_free(s);
return HE_OK;
}
return orgspec_add(s);
}
/* hp_realloc can result in a pointer becoming invalid; this function is used
to apply this fact to the history */
static void
history_del_invalid(struct gen_spec *s)
{
if (unlikely(s == NULL)) {
ARG_MSG;
return;
}
if (ihash_delete(newspec, s, NULL) < 0) {
ERR("unable to remove invalid pointer from newspec");
}
}
/*
* termrule operations
*/
/* insert 'rule' in 'term' in sorted order (sorted after pointer addresses);
'term' m