root/usr/src/uts/common/ipp/ipgpc/trie.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2002-2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <ipp/ipgpc/trie.h>
#include <ipp/ipgpc/filters.h>
#include <ipp/ipgpc/classifier.h>
#include <inet/ip6.h>

/* trie data structure used for classifying IP addresses and TCP/UDP ports */

#define ZERO    0
#define ONE     1


/* Statics */
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 **);

/*
 * create_node(flag)
 *
 * generates a pointer to a new trie node
 * flag is passed to kmem_alloc
 * returns NULL to signify memory error
 */
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);
}


/*
 * t_split(c_node, pos, key_len)
 *
 * performs a split on c_node for the following three cases:
 * 1 a mismatch occured between the insert key and the value at the node
 * 2 the insert key specifies a shorter key than the one at the node
 * 3 the insert key specifies a longer key than the one at the node
 * cases 1 and 2 are handled in the same way
 * when t_split returns, c_node->one and c_node->zero must != NULL
 *
 * (note: we assume a key_len = n (where in the real world n = 16 | 32),
 *  and a "key" in this example is actaully some value of key_len n that
 *  has its high order bits masked.
 *  For example: key = 1011 with key_len = 8, would actaully be the key:mask
 *  combo 1011xxxx:11110000.  I am using short keys for ease of example)
 * Case 1 and 2:
 *
 * assume 8 bit keys for all examples
 *
 * trie A contains keys 111011, 0, 10
 *       *
 *      / \
 *         *
 *        / \
 *        *  * bits = 4 pos = 5 val = 1011 mask = 00111100
 * inserting 111100 would result in the following split
 *                       *
 *                      / \
 *                         *
 *                        / \
 *                           *  bits = 1 pos = 5 val = 1 mask = 00100000
 *                          / \
 *  bits = 2 pos = 3 val=11*   * (to be inserted: (bits = 2 pos = 3 val = 00
 *  mask = 00001100                               mask = 00001100))
 *
 * Case 3:
 *
 * trie A same as above, before insert
 * inserting key 11101111 would results in the following split
 *       *
 *      / \
 *         *
 *        / \
 *        *  * bits = 4 pos = 5 val = 1011 mask = 00111100
 *          / \
 *         *   *  (to be inserted: bits = 1 pos = 0 val = 1 mask = 00000001)
 */
/* ARGSUSED */
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;

        /* check if case is that the mask is longer */
        if (pos == (nodep->pos - nodep->bits)) {
                /* pos is past the last bit covered at this node */
                ASSERT(nodep->one == NULL);
                ASSERT(nodep->zero == NULL);
                nodep->one = create_node(KM_SLEEP);
                nodep->zero = create_node(KM_SLEEP);
        } else {                /* pos > (nodep->pos - nodep->bits) */
                old_bits = nodep->bits; /* save old bits entry */
                /* nodep->pos will remain the same */
                nodep->bits = nodep->pos - pos;
                /* find the mismatch bit */
                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);
                        }
                        /* pos is before the last bit covered at this node */
                        nodep->zero->pos = pos - 1; /* link is one bit */
                        /* bits gets remaining bits minus the link */
                        nodep->zero->bits = (old_bits - nodep->bits) - 1;
                        /* set bits that are covered by this node */
                        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 {        /* bit == ONE */
                        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);
                        }
                        /* pos is before the last bit covered at this node */
                        nodep->one->pos = pos - 1; /* link is one bit */
                        /* bits gets remaining bits minus the link */
                        nodep->one->bits = (old_bits - nodep->bits) - 1;
                        /* set bits that are covered by this node */
                        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;
                }

                /* clear bits no longer covered by this node, from pos=>0 */
                for (i = 0; i <= pos; ++i) {
                        UNSETBIT(nodep->val, i, key_len);
                        UNSETBIT(nodep->mask, i, key_len);
                }
        }
}

/*
 * t_insert(tid, id, key, mask)
 *
 * inserts a new value, id, into the trie, tid->trie with the input key
 * - if node exists, id is appended to element list at the node, if id does
 *   not already exist.
 * - if node does not exist, a new node is created and id is the head of a new
 *   element list
 * return DONTCARE_VALUE if mask == 0, otherwise NORMAL_VALUE
 */
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;

        /* don't insert if don't care */
        if (mask == 0) {
                ++tid->stats.num_dontcare;
                return (DONTCARE_VALUE);
        }

        rw_enter(&tid->rw_lock, RW_WRITER);
        c_node = tid->trie;     /* point at trie root */
        key &= mask;            /* apply mask */
        /* traverse trie to the correct position */
        for (pos = key_len; pos > 0; --pos) {
                /* check if bit is significant */
                /* bit in key is significant if it is covered by the mask */
                if (EXTRACTBIT(mask, (pos - 1), key_len) != 1) {
                        /* check if this is a path compressed internal node */
                        if (c_node->bits > 0) {
                                /* check if split is needed */
                                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;
                }
                /* extra bit at current position */
                bit = EXTRACTBIT(key, (pos - 1), key_len);
                /* check if this is a path compressed internal node */
                if (c_node->bits > 0) { /* path compressed node */
                        /* check if split is needed */
                        if ((pos - 1) > (c_node->pos - c_node->bits)) {
                                /* testing for mismatch */
                                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; /* bits match, so go on */
                                }
                        } else if ((pos - 1) == (c_node->pos - c_node->bits)) {
                                /* check if at a leaf node with elements */
                                if ((c_node->one == NULL) &&
                                    (c_node->zero == NULL) &&
                                    (c_node->elements != NULL)) {
                                        /*
                                         * this case occurs when mask for key
                                         * is longer than mask for key at
                                         * current node
                                         */
                                        t_split(&c_node, (pos - 1), key_len);
                                        ASSERT(c_node->one != NULL);
                                        ASSERT(c_node->zero != NULL);
                                }
                        } /* else continue onto child */
                }
                if (bit == ZERO) {
                        if (c_node->zero == NULL) { /* leaf node */
                                if (c_node->bits == 0) {
                                        c_node->pos = (pos - 1);
                                }
                                c_node->bits++;
                                /* bit at pos for node value should be 0 */
                                UNSETBIT(c_node->val, (pos - 1), key_len);
                                SETBIT(c_node->mask, (pos - 1), 1, key_len);
                        } else {
                                /* assert that trie is path compressed */
                                ASSERT(c_node->one != NULL);
                                c_node = c_node->zero; /* internal node */
                        }
                } else {        /* ONE bit */
                        if (c_node->one == NULL) { /* leaf node */
                                if (c_node->bits == 0) {
                                        c_node->pos = (pos - 1);
                                }
                                c_node->bits++;
                                /* bit at pos for node value should be 1 */
                                SETBIT(c_node->val, (pos - 1), 1, key_len);
                                SETBIT(c_node->mask, (pos - 1), 1, key_len);
                        } else {
                                /* assert that trie is path compressed */
                                ASSERT(c_node->zero != NULL);
                                c_node = c_node->one; /* internal node */
                        }
                }
        }
        /* insert at node */
        (void) ipgpc_list_insert(&c_node->elements, id);
        /* update stats */
        ++tid->stats.num_inserted;
        /*
         * check if this is the first key to be inserted that is not a
         * don't care (*)
         */
        if (tid->info.dontcareonly == B_TRUE) {
                tid->info.dontcareonly = B_FALSE;
        }
        rw_exit(&tid->rw_lock);
        return (NORMAL_VALUE);
}

/*
 * t_insert6(tid, id, key, mask)
 *
 * specific to inserting keys of 128 bits in length
 */
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;

        /* don't insert if don't care */
        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;     /* point at root of trie */
        V6_MASK_COPY(key, mask, key); /* apply mask to key */
        /*
         * A IPv6 address is structured as an array of four uint32_t
         * values.  The highest order of the bits are located in array[0]
         */
        for (i = 0; i < 4; ++i) {
                /* traverse trie to the correct position */
                for (pos = type_len; pos > 0; --pos) {
                        /* check if bit is significant */
                        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 {        /* ONE bit */
                                if (c_node->one == NULL) {
                                        c_node->one = create_node(KM_SLEEP);
                                }
                                c_node = c_node->one;
                        }

                }
        }
        /* insert at node */
        (void) ipgpc_list_insert(&c_node->elements, id);
        /* update stats */
        ++tid->stats.num_inserted;
        /*
         * check if this is the first key to be inserted that is not a
         * don't care (*)
         */
        if (tid->info.dontcareonly == B_TRUE) {
                tid->info.dontcareonly = B_FALSE;
        }
        rw_exit(&tid->rw_lock);
        return (NORMAL_VALUE);
}

/*
 * t_traverse_delete(in_node, pos, id, key, mask, tid)
 *
 * used to traverse to the node containing id, as found under key
 * once id is found, it is removed from the trie.
 * Upon removing the id from a given node in the trie, path compression
 * will be applied to nodes that are no longer compressed.
 * If the id is successfully removed, tid->stats are updated
 */
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); /* base failure case */
        }

        /* we've found the node the id is probably at */
        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); /* key does not exist at node */
                } else {
                        /* update stats */
                        --(*tid)->stats.num_inserted;
                        /* check if 0 values are inserted in this trie */
                        if ((*tid)->stats.num_inserted == 0) {
                                /* update dontcareonly boolean */
                                (*tid)->info.dontcareonly = B_TRUE;
                        }
                }
                /* check if node has zero elements, is a LEAF node */
                if ((c_node->elements == NULL) &&
                    ((c_node->one == NULL) && (c_node->zero == NULL))) {
                        /* make sure we don't delete the root */
                        if (c_node->isroot != 1) {
                                kmem_cache_free(trie_node_cache, c_node);
                                return (B_TRUE);
                        } else {
                                /* this is the root, just zero out the info */
                                c_node->pos = 0;
                                c_node->bits = 0;
                                c_node->val = 0;
                                c_node->mask = 0;
                        }
                }
                return (B_FALSE);
        }

        /* check to see if node describes bits to skip */
        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); /* key does not exist at node */
                }
                pos = (c_node->pos - c_node->bits) + 1;
                /* search should continue if mask and pos are valid */
                if ((pos == 0) ||
                    (EXTRACTBIT(mask, (pos - 1), (uint8_t)(*tid)->key_len)
                        != 1)) {
                        /* this node probably contains the id */
                        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 {
                                /* update stats */
                                --(*tid)->stats.num_inserted;
                                /* check if 0 values are inserted */
                                if ((*tid)->stats.num_inserted == 0) {
                                        /* update dontcare boolean */
                                        (*tid)->info.dontcareonly = B_TRUE;
                                }
                        }
                        /* check if node has zero elements & is a LEAF node */
                        if ((c_node->elements == NULL) &&
                            ((c_node->one == NULL) &&
                                (c_node->zero == NULL))) {
                                /* make sure we don't delete the root */
                                if (c_node->isroot != 1) {
                                        kmem_cache_free(trie_node_cache,
                                            c_node);
                                        return (B_TRUE);
                                } else {
                                        /* this is the root, zero out info */
                                        c_node->pos = 0;
                                        c_node->bits = 0;
                                        c_node->val = 0;
                                        c_node->mask = 0;
                                }
                        }
                        return (B_FALSE);
                }
        }
        /* extract next bit and test */
        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 {        /* ONE bit */
                if (t_traverse_delete(&c_node->one, (pos - 1), id, key, mask,
                    tid) == B_TRUE) {
                        c_node->one = NULL;
                }
        }
        /*
         * non path-compressed nodes will contain one child and no elements
         * what occurs here:
         *        *
         *       / \
         *      *   *  <-- p_node->elements == NULL
         *         /
         *        *  <-- c_node->elements = foo
         *       / \
         *      *   *  <-- children of c_node
         * after:
         *        *
         *       / \
         *      *   *   <-- p_node->elements = foo
         *         / \
         *        *   *  <-- p_node adopts children of c_node
         */
        if ((c_node->one == NULL) && (c_node->zero != NULL)) {
                if (c_node->elements == NULL) {
                        /* move child elements to parent */
                        c_node->elements = c_node->zero->elements;
                        /* be sure to include the link in the bits */
                        c_node->bits += c_node->zero->bits + 1;
                        /* c_node->pos will remain the same */
                        c_node->mask |= c_node->zero->mask;
                        /* don't forget to mark the link */
                        SETBIT(c_node->mask, (pos - 1), 1,
                            (uint8_t)(*tid)->key_len);
                        c_node->val |= c_node->zero->val;
                        /* don't forget to mark the link  */
                        UNSETBIT(c_node->val, (pos - 1),
                            (uint8_t)(*tid)->key_len);
                        /* adopt children */
                        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) {
                        /* move child elements to parent */
                        c_node->elements = c_node->one->elements;
                        /* be sure to include the link in the bits */
                        c_node->bits += c_node->one->bits + 1;
                        /* c_node->pos will remain the same */
                        c_node->mask |= c_node->one->mask;
                        /* don't forget to mark the link */
                        SETBIT(c_node->mask, (pos - 1), 1,
                            (uint8_t)(*tid)->key_len);
                        c_node->val |= c_node->one->val;
                        /* don't forget to mark the link  */
                        SETBIT(c_node->val, (pos - 1), 1,
                            (uint8_t)(*tid)->key_len);
                        /* adopt children */
                        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;
                }
        }
        /* check if node has zero elements, is a LEAF node */
        if ((c_node->elements == NULL) &&
            ((c_node->one == NULL) && (c_node->zero == NULL))) {
                /* make sure we don't delete the root */
                if (c_node->isroot != 1) {
                        kmem_cache_free(trie_node_cache, c_node);
                        return (B_TRUE);
                } else {
                        /* this is the root, just zero out the info */
                        c_node->pos = 0;
                        c_node->bits = 0;
                        c_node->val = 0;
                        c_node->mask = 0;
                }
        }
        return (B_FALSE);
}



/*
 * t_remove(tid, id, key, mask)
 *
 * removes a value associated with an id from the trie
 * - if the item does not exist, nothing is removed
 * - if more than one id share the same key, only the id specified is removed
 */
void
t_remove(trie_id_t *tid, key_t id, uint32_t key, uint32_t mask)
{
        node_t *c_node;

        /* don't cares are not inserted */
        if (mask == 0) {
                --tid->stats.num_dontcare;
                return;
        }

        key &= mask;            /* apply mask */
        /* traverse to node containing id and remove the id from the trie */
        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);
}

/*
 * t_remove6(tid, id, key, mask)
 *
 * specific to removing key of 128 bits in length
 */
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;

        /* don't cares are not inserted */
        if (IN6_ARE_ADDR_EQUAL(&mask, &zero_addr)) {
                --tid->stats.num_dontcare;
                return;
        }

        rw_enter(&tid->rw_lock, RW_WRITER);
        c_node = tid->trie;     /* point at root of trie */
        V6_MASK_COPY(key, mask, key);
        /*
         * A IPv6 address is structured as an array of four uint32_t
         * values.  The higest order of the bits are located in array[0]
         */
        for (i = 0; i < 4; ++i) {
                /* traverse trie to the correct position */
                for (pos = type_len; pos > 0; --pos) {
                        /* check if bit is significant */
                        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 {        /* ONE bit */
                                if (c_node->one == NULL) {
                                        break;
                                }
                                c_node = c_node->one;
                        }

                }
        }
        if (c_node != NULL) {
                if (ipgpc_list_remove(&c_node->elements, id)) {
                        /* update stats */
                        --tid->stats.num_inserted;
                        /*
                         * check to see if only dontcare's are inserted
                         */
                        if (tid->stats.num_inserted <= 0) {
                                tid->info.dontcareonly = B_TRUE;
                        }
                }
        }
        rw_exit(&tid->rw_lock);
}


/*
 * t_retrieve(tid, key, fid_table)
 *
 * returns the number of found filters that match the input key
 * - each value that matches either a partial or exact match on the key
 *   is inserted into the fid_table
 * - some nodes may contain multiple id's, all items will be inserted
 *   into the fid_table
 * - the find stops when an edge node is reached, the left and right nodes
 *   for the current node are null
 * - 0 is returned if no matches are found, otherwise the number of matches
 *   is returned
 * - (-1) is returned if a memory error occurred
 */
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;     /* point at root of trie */

        /* ensure trie structure is allocated */
        if (c_node == NULL) {
                rw_exit(&tid->rw_lock);
                return (num_found);
        }
        /*
         * foreach node encountered in the search, collect elements and append
         * to a list to be returned
         */
        for (pos = (uint8_t)tid->key_len; pos > 0; --pos) {
                /* check node for bits to check */
                if (c_node->bits > 0) {
                        if ((key & c_node->mask) != c_node->val) {
                                rw_exit(&tid->rw_lock);
                                return (num_found); /* search is done */
                        }
                        /* pos is set to next bit not covered by node */
                        if ((pos = (c_node->pos - c_node->bits) + 1) == 0) {
                                /* if node covers rest of bits in key */
                                break;
                        }
                }
                /* check node for elements */
                if (c_node->elements != NULL) {
                        if ((ret = ipgpc_mark_found(tid->info.mask,
                            c_node->elements, fid_table)) == -1) {
                                /* signifies a memory error */
                                rw_exit(&tid->rw_lock);
                                return (-1);
                        }
                        num_found += ret; /* increment num_found */
                }

                bit = EXTRACTBIT(key, (pos - 1), (uint8_t)tid->key_len);
                if (bit == ZERO) { /* choose leaf */
                        c_node = c_node->zero;

                } else {        /* bit == ONE */
                        c_node = c_node->one;

                }
                if (c_node == NULL) {
                        /* search is finished, edge node reached */
                        rw_exit(&tid->rw_lock);
                        return (num_found);
                }
        }
        /* see if current node contains elements */
        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); /* signifies a memory error */
                }
                num_found += ret; /* increment num_found */
        }
        rw_exit(&tid->rw_lock);
        return (num_found);
}

/*
 * t_retrieve6(tid, key, fid_table)
 *
 * specific to retrieving keys of 128 bits in length
 */
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;

        /* ensure trie structure is allocated */
        if (c_node == NULL) {
                rw_exit(&tid->rw_lock);
                return (num_found);
        }
        /*
         * A IPv6 address is structured as an array of four uint32_t
         * values.  The higest order of the bits are located in array[0]
         */
        for (i = 0; i < 4; ++i) {
                /*
                 * foreach node encountered in the search, collect elements
                 * and append to a list to be returned
                 */
                for (pos = type_len; pos > 0; --pos) {
                        /* extract bit at pos */
                        bit =
                            EXTRACTBIT(key.s6_addr32[i], (pos - 1), type_len);
                        if (bit == ZERO) { /* choose leaf */
                                c_node = c_node->zero;

                        } else {
                                c_node = c_node->one;

                        }
                        if (c_node == NULL) {
                                /* search is finished, edge node reached */
                                rw_exit(&tid->rw_lock);
                                return (num_found);
                        }
                        /* see if current node contains elements */
                        if (c_node->elements != NULL) {
                                if ((ret = ipgpc_mark_found(tid->info.mask,
                                    c_node->elements, fid_table)) == -1) {
                                        /* signifies a memory error */
                                        rw_exit(&tid->rw_lock);
                                        return (-1);
                                }
                                num_found += ret; /* increment num_found */
                        }
                }
        }
        rw_exit(&tid->rw_lock);
        return (num_found);
}