#include <ipp/ipgpc/trie.h>
#include <ipp/ipgpc/filters.h>
#include <ipp/ipgpc/classifier.h>
#include <inet/ip6.h>
#define ZERO 0
#define ONE 1
static void t_split(node_t **, uint8_t, uint8_t);
static boolean_t t_traverse_delete(node_t **, uint8_t, key_t, uint32_t,
uint32_t, trie_id_t **);
node_t *
create_node(int flag)
{
node_t *buf = kmem_cache_alloc(trie_node_cache, flag);
if (buf == NULL) {
return (NULL);
}
buf->elements = NULL;
buf->zero = NULL;
buf->one = NULL;
buf->pos = 0;
buf->bits = 0;
buf->val = 0;
buf->mask = 0;
buf->isroot = 0;
return (buf);
}
static void
t_split(node_t **c_node, uint8_t pos, uint8_t key_len)
{
uint8_t old_bits = 0;
uint8_t i;
int bit;
node_t *nodep = *c_node;
node_t *tnodep = NULL;
if (pos == (nodep->pos - nodep->bits)) {
ASSERT(nodep->one == NULL);
ASSERT(nodep->zero == NULL);
nodep->one = create_node(KM_SLEEP);
nodep->zero = create_node(KM_SLEEP);
} else {
old_bits = nodep->bits;
nodep->bits = nodep->pos - pos;
bit = EXTRACTBIT(nodep->val, pos, key_len);
if (bit == ZERO) {
if ((nodep->one == NULL) && (nodep->zero == NULL)) {
nodep->one = create_node(KM_SLEEP);
nodep->zero = create_node(KM_SLEEP);
} else {
tnodep = create_node(KM_SLEEP);
tnodep->one = nodep->one;
tnodep->zero = nodep->zero;
nodep->zero = tnodep;
nodep->one = create_node(KM_SLEEP);
}
nodep->zero->pos = pos - 1;
nodep->zero->bits = (old_bits - nodep->bits) - 1;
for (i = 0; i < nodep->zero->bits; ++i) {
SETBIT(nodep->zero->val,
(nodep->zero->pos - i),
EXTRACTBIT(nodep->val,
(nodep->zero->pos - i), key_len),
key_len);
SETBIT(nodep->zero->mask,
(nodep->zero->pos - i), 1, key_len);
}
nodep->zero->elements = nodep->elements;
nodep->elements = NULL;
} else {
if ((nodep->one == NULL) && (nodep->zero == NULL)) {
nodep->one = create_node(KM_SLEEP);
nodep->zero = create_node(KM_SLEEP);
} else {
tnodep = create_node(KM_SLEEP);
tnodep->one = nodep->one;
tnodep->zero = nodep->zero;
nodep->one = tnodep;
nodep->zero = create_node(KM_SLEEP);
}
nodep->one->pos = pos - 1;
nodep->one->bits = (old_bits - nodep->bits) - 1;
for (i = 0; i < nodep->one->bits; ++i) {
SETBIT(nodep->one->val, (nodep->one->pos - i),
EXTRACTBIT(nodep->val,
(nodep->one->pos - i), key_len),
key_len);
SETBIT(nodep->one->mask,
(nodep->one->pos - i), 1, key_len);
}
nodep->one->elements = nodep->elements;
nodep->elements = NULL;
}
for (i = 0; i <= pos; ++i) {
UNSETBIT(nodep->val, i, key_len);
UNSETBIT(nodep->mask, i, key_len);
}
}
}
int
t_insert(trie_id_t *tid, key_t id, uint32_t key, uint32_t mask)
{
node_t *c_node;
int bit;
uint8_t pos;
uint8_t key_len = (uint8_t)tid->key_len;
if (mask == 0) {
++tid->stats.num_dontcare;
return (DONTCARE_VALUE);
}
rw_enter(&tid->rw_lock, RW_WRITER);
c_node = tid->trie;
key &= mask;
for (pos = key_len; pos > 0; --pos) {
if (EXTRACTBIT(mask, (pos - 1), key_len) != 1) {
if (c_node->bits > 0) {
if ((pos - 1) > (c_node->pos - c_node->bits)) {
t_split(&c_node, (pos - 1), key_len);
ASSERT(c_node->one != NULL);
ASSERT(c_node->zero != NULL);
}
}
break;
}
bit = EXTRACTBIT(key, (pos - 1), key_len);
if (c_node->bits > 0) {
if ((pos - 1) > (c_node->pos - c_node->bits)) {
if (bit != EXTRACTBIT(c_node->val, (pos - 1),
key_len)) {
t_split(&c_node, (pos - 1), key_len);
ASSERT(c_node->one != NULL);
ASSERT(c_node->zero != NULL);
} else {
continue;
}
} else if ((pos - 1) == (c_node->pos - c_node->bits)) {
if ((c_node->one == NULL) &&
(c_node->zero == NULL) &&
(c_node->elements != NULL)) {
t_split(&c_node, (pos - 1), key_len);
ASSERT(c_node->one != NULL);
ASSERT(c_node->zero != NULL);
}
}
}
if (bit == ZERO) {
if (c_node->zero == NULL) {
if (c_node->bits == 0) {
c_node->pos = (pos - 1);
}
c_node->bits++;
UNSETBIT(c_node->val, (pos - 1), key_len);
SETBIT(c_node->mask, (pos - 1), 1, key_len);
} else {
ASSERT(c_node->one != NULL);
c_node = c_node->zero;
}
} else {
if (c_node->one == NULL) {
if (c_node->bits == 0) {
c_node->pos = (pos - 1);
}
c_node->bits++;
SETBIT(c_node->val, (pos - 1), 1, key_len);
SETBIT(c_node->mask, (pos - 1), 1, key_len);
} else {
ASSERT(c_node->zero != NULL);
c_node = c_node->one;
}
}
}
(void) ipgpc_list_insert(&c_node->elements, id);
++tid->stats.num_inserted;
if (tid->info.dontcareonly == B_TRUE) {
tid->info.dontcareonly = B_FALSE;
}
rw_exit(&tid->rw_lock);
return (NORMAL_VALUE);
}
int
t_insert6(trie_id_t *tid, key_t id, in6_addr_t key, in6_addr_t mask)
{
node_t *c_node;
int bit, i;
uint8_t pos;
uint8_t type_len = IP_ABITS;
in6_addr_t zero_addr = IN6ADDR_ANY_INIT;
if (IN6_ARE_ADDR_EQUAL(&mask, &zero_addr)) {
++tid->stats.num_dontcare;
return (DONTCARE_VALUE);
}
rw_enter(&tid->rw_lock, RW_WRITER);
c_node = tid->trie;
V6_MASK_COPY(key, mask, key);
for (i = 0; i < 4; ++i) {
for (pos = type_len; pos > 0; --pos) {
if (EXTRACTBIT(mask.s6_addr32[i], (pos - 1), type_len)
!= ONE) {
break;
}
bit = EXTRACTBIT(key.s6_addr32[i], (pos - 1), type_len);
if (bit == ZERO) {
if (c_node->zero == NULL) {
c_node->zero = create_node(KM_SLEEP);
}
c_node = c_node->zero;
} else {
if (c_node->one == NULL) {
c_node->one = create_node(KM_SLEEP);
}
c_node = c_node->one;
}
}
}
(void) ipgpc_list_insert(&c_node->elements, id);
++tid->stats.num_inserted;
if (tid->info.dontcareonly == B_TRUE) {
tid->info.dontcareonly = B_FALSE;
}
rw_exit(&tid->rw_lock);
return (NORMAL_VALUE);
}
static boolean_t
t_traverse_delete(node_t **in_node, uint8_t pos, key_t id, uint32_t key,
uint32_t mask, trie_id_t **tid)
{
node_t *c_node = *in_node;
node_t *t_node;
int bit;
if (c_node == NULL) {
return (B_FALSE);
}
if ((pos == 0) ||
(EXTRACTBIT(mask, (pos - 1), (uint8_t)(*tid)->key_len) != 1)) {
if (ipgpc_list_remove(&c_node->elements, id) == B_FALSE) {
ipgpc0dbg(("t_traverse_delete: id %d does not " \
"exist in trie\n", id));
return (B_FALSE);
} else {
--(*tid)->stats.num_inserted;
if ((*tid)->stats.num_inserted == 0) {
(*tid)->info.dontcareonly = B_TRUE;
}
}
if ((c_node->elements == NULL) &&
((c_node->one == NULL) && (c_node->zero == NULL))) {
if (c_node->isroot != 1) {
kmem_cache_free(trie_node_cache, c_node);
return (B_TRUE);
} else {
c_node->pos = 0;
c_node->bits = 0;
c_node->val = 0;
c_node->mask = 0;
}
}
return (B_FALSE);
}
if (c_node->bits > 0) {
if ((key & c_node->mask) != c_node->val) {
ipgpc0dbg(("t_traverse_delete: id %d does not " \
"exist in trie\n", id));
return (B_FALSE);
}
pos = (c_node->pos - c_node->bits) + 1;
if ((pos == 0) ||
(EXTRACTBIT(mask, (pos - 1), (uint8_t)(*tid)->key_len)
!= 1)) {
if (ipgpc_list_remove(&c_node->elements,
id) == B_FALSE) {
ipgpc0dbg(("t_traverse_delete: id %d does" \
"not exist in trie\n", id));
return (B_FALSE);
} else {
--(*tid)->stats.num_inserted;
if ((*tid)->stats.num_inserted == 0) {
(*tid)->info.dontcareonly = B_TRUE;
}
}
if ((c_node->elements == NULL) &&
((c_node->one == NULL) &&
(c_node->zero == NULL))) {
if (c_node->isroot != 1) {
kmem_cache_free(trie_node_cache,
c_node);
return (B_TRUE);
} else {
c_node->pos = 0;
c_node->bits = 0;
c_node->val = 0;
c_node->mask = 0;
}
}
return (B_FALSE);
}
}
bit = EXTRACTBIT(key, (pos - 1), (uint8_t)(*tid)->key_len);
if (bit == ZERO) {
if (t_traverse_delete(&c_node->zero, (pos - 1), id, key, mask,
tid) == B_TRUE) {
c_node->zero = NULL;
}
} else {
if (t_traverse_delete(&c_node->one, (pos - 1), id, key, mask,
tid) == B_TRUE) {
c_node->one = NULL;
}
}
if ((c_node->one == NULL) && (c_node->zero != NULL)) {
if (c_node->elements == NULL) {
c_node->elements = c_node->zero->elements;
c_node->bits += c_node->zero->bits + 1;
c_node->mask |= c_node->zero->mask;
SETBIT(c_node->mask, (pos - 1), 1,
(uint8_t)(*tid)->key_len);
c_node->val |= c_node->zero->val;
UNSETBIT(c_node->val, (pos - 1),
(uint8_t)(*tid)->key_len);
t_node = c_node->zero;
c_node->one = c_node->zero->one;
c_node->zero = c_node->zero->zero;
kmem_cache_free(trie_node_cache, t_node);
} else {
ASSERT(c_node->zero->one == NULL);
ASSERT(c_node->zero->zero == NULL);
kmem_cache_free(trie_node_cache, c_node->zero);
c_node->zero = NULL;
}
} else if ((c_node->one != NULL) && (c_node->zero == NULL)) {
if (c_node->elements == NULL) {
c_node->elements = c_node->one->elements;
c_node->bits += c_node->one->bits + 1;
c_node->mask |= c_node->one->mask;
SETBIT(c_node->mask, (pos - 1), 1,
(uint8_t)(*tid)->key_len);
c_node->val |= c_node->one->val;
SETBIT(c_node->val, (pos - 1), 1,
(uint8_t)(*tid)->key_len);
t_node = c_node->one;
c_node->zero = c_node->one->zero;
c_node->one = c_node->one->one;
kmem_cache_free(trie_node_cache, t_node);
} else {
ASSERT(c_node->one->one == NULL);
ASSERT(c_node->one->zero == NULL);
kmem_cache_free(trie_node_cache, c_node->one);
c_node->one = NULL;
}
}
if ((c_node->elements == NULL) &&
((c_node->one == NULL) && (c_node->zero == NULL))) {
if (c_node->isroot != 1) {
kmem_cache_free(trie_node_cache, c_node);
return (B_TRUE);
} else {
c_node->pos = 0;
c_node->bits = 0;
c_node->val = 0;
c_node->mask = 0;
}
}
return (B_FALSE);
}
void
t_remove(trie_id_t *tid, key_t id, uint32_t key, uint32_t mask)
{
node_t *c_node;
if (mask == 0) {
--tid->stats.num_dontcare;
return;
}
key &= mask;
rw_enter(&tid->rw_lock, RW_WRITER);
c_node = tid->trie;
(void) t_traverse_delete(&c_node, (uint8_t)tid->key_len, id, key, mask,
&tid);
rw_exit(&tid->rw_lock);
}
void
t_remove6(trie_id_t *tid, key_t id, in6_addr_t key, in6_addr_t mask)
{
node_t *c_node;
int bit, i;
uint8_t pos;
uint8_t type_len = IP_ABITS;
in6_addr_t zero_addr = IN6ADDR_ANY_INIT;
if (IN6_ARE_ADDR_EQUAL(&mask, &zero_addr)) {
--tid->stats.num_dontcare;
return;
}
rw_enter(&tid->rw_lock, RW_WRITER);
c_node = tid->trie;
V6_MASK_COPY(key, mask, key);
for (i = 0; i < 4; ++i) {
for (pos = type_len; pos > 0; --pos) {
if (EXTRACTBIT(mask.s6_addr32[i], (pos - 1), type_len)
!= ONE) {
break;
}
bit = EXTRACTBIT(key.s6_addr32[i], (pos - 1), type_len);
if (bit == ZERO) {
if (c_node->zero == NULL) {
break;
}
c_node = c_node->zero;
} else {
if (c_node->one == NULL) {
break;
}
c_node = c_node->one;
}
}
}
if (c_node != NULL) {
if (ipgpc_list_remove(&c_node->elements, id)) {
--tid->stats.num_inserted;
if (tid->stats.num_inserted <= 0) {
tid->info.dontcareonly = B_TRUE;
}
}
}
rw_exit(&tid->rw_lock);
}
int
t_retrieve(trie_id_t *tid, uint32_t key, ht_match_t *fid_table)
{
int bit;
uint8_t pos;
int num_found = 0;
int ret;
node_t *c_node;
rw_enter(&tid->rw_lock, RW_READER);
c_node = tid->trie;
if (c_node == NULL) {
rw_exit(&tid->rw_lock);
return (num_found);
}
for (pos = (uint8_t)tid->key_len; pos > 0; --pos) {
if (c_node->bits > 0) {
if ((key & c_node->mask) != c_node->val) {
rw_exit(&tid->rw_lock);
return (num_found);
}
if ((pos = (c_node->pos - c_node->bits) + 1) == 0) {
break;
}
}
if (c_node->elements != NULL) {
if ((ret = ipgpc_mark_found(tid->info.mask,
c_node->elements, fid_table)) == -1) {
rw_exit(&tid->rw_lock);
return (-1);
}
num_found += ret;
}
bit = EXTRACTBIT(key, (pos - 1), (uint8_t)tid->key_len);
if (bit == ZERO) {
c_node = c_node->zero;
} else {
c_node = c_node->one;
}
if (c_node == NULL) {
rw_exit(&tid->rw_lock);
return (num_found);
}
}
if (c_node->elements != NULL) {
if ((ret = ipgpc_mark_found(tid->info.mask, c_node->elements,
fid_table)) == -1) {
rw_exit(&tid->rw_lock);
return (-1);
}
num_found += ret;
}
rw_exit(&tid->rw_lock);
return (num_found);
}
int
t_retrieve6(trie_id_t *tid, in6_addr_t key, ht_match_t *fid_table)
{
int bit, i;
uint8_t pos;
int num_found = 0;
int ret;
node_t *c_node;
uint8_t type_len = IP_ABITS;
rw_enter(&tid->rw_lock, RW_READER);
c_node = tid->trie;
if (c_node == NULL) {
rw_exit(&tid->rw_lock);
return (num_found);
}
for (i = 0; i < 4; ++i) {
for (pos = type_len; pos > 0; --pos) {
bit =
EXTRACTBIT(key.s6_addr32[i], (pos - 1), type_len);
if (bit == ZERO) {
c_node = c_node->zero;
} else {
c_node = c_node->one;
}
if (c_node == NULL) {
rw_exit(&tid->rw_lock);
return (num_found);
}
if (c_node->elements != NULL) {
if ((ret = ipgpc_mark_found(tid->info.mask,
c_node->elements, fid_table)) == -1) {
rw_exit(&tid->rw_lock);
return (-1);
}
num_found += ret;
}
}
}
rw_exit(&tid->rw_lock);
return (num_found);
}