Exemplo n.º 1
0
/**
 * Match against the BinRadix tree considering data as a binary IP address
 * This is the main difference with the other radix matcher (where data is
 * considered ascii)
 *
 * @param mpi provider instance
 * @param flags extra flags
 * @param data the data to search in
 * @param dlen length of the the data to search in
 *
 * @return status of the operation
 */
static ib_status_t modbinradix_match(ib_provider_inst_t *mpi,
                                 ib_flags_t flags,
                                 const uint8_t *data,
                                 size_t dlen,
                                 void *ctx)
{
    IB_FTRACE_INIT();
    ib_status_t rc;
    modbinradix_provider_data_t *dt = mpi->data;

    if (dt == NULL) {
        IB_FTRACE_RET_STATUS(IB_EINVAL);
    }

    ib_log_debug(mpi->pr->ib, "Matching AGAINST BinRadix tree %x",
                     dt->binradix_tree);

    ib_radix_t *binradix_tree = dt->binradix_tree;

    ib_radix_prefix_t *pre = NULL;

    /* Create the prefix directly. Data should be a binary ip address already */
    rc = ib_radix_prefix_create(&pre, (uint8_t *)data, (uint8_t)dlen * 8,
                                mpi->mp);
    if (rc != IB_OK) {
        IB_FTRACE_RET_STATUS(rc);
    }

    void *result = NULL;

    rc = ib_radix_match_closest(binradix_tree, pre, &result);
    if (rc == IB_OK) {
        modbinradix_content_t *mrc = (modbinradix_content_t *)result;
        if (mrc->callback != NULL && mrc->data != NULL) {
            *(void **)ctx = result;
            IB_FTRACE_RET_STATUS(mrc->callback(mrc->data));
        }
        else if (mrc->data != NULL) {
            *(void **)ctx = result;
            IB_FTRACE_RET_STATUS(IB_OK);
        }
        else {
            IB_FTRACE_RET_STATUS(IB_ENOENT);
        }
    }

    IB_FTRACE_RET_STATUS(rc);
}
Exemplo n.º 2
0
/*
 * Inserts a new user data associated to the prefix passed. The prefix is not
 * used, so developers are responsible to free that prefixs
 * Keys can be of "any size" but this will be probably used for
 * CIDR data prefixes only (from 0 to 32 ~ 128 depending on IPv4
 * or IPv6 respectively)
 *
 * @param radix the radix of the node
 * @param prefix the prefix to use as index
 * @param prefix_data, the data to store under that prefix
 *
 * @returns Status code
 */
ib_status_t ib_radix_insert_data(ib_radix_t *radix,
                                 ib_radix_prefix_t *prefix,
                                 void *prefix_data)
{
    IB_FTRACE_INIT(ib_radix_insert_data);
    ib_status_t st;

    if (prefix == NULL) {
        IB_FTRACE_RET_STATUS(IB_EINVAL);
    }

    if (radix->start == NULL) {
        st = ib_radix_node_new(&radix->start, radix->mp);
        if (st != IB_OK) {
            IB_FTRACE_RET_STATUS(st);
        }

        /* The start node should have an empty prefix always */
        st = ib_radix_prefix_create(&radix->start->prefix, NULL, 0, radix->mp);
        if (st != IB_OK) {
            IB_FTRACE_RET_STATUS(st);
        }
    }

    if (prefix->rawbits == NULL || prefix->prefixlen == 0) {
        if (radix->start->data != NULL) {
            if (radix->update_data != NULL) {
                radix->update_data(radix->start, prefix_data);
            }
            else if (radix->free_data != NULL) {
                radix->free_data((void*)radix->start->data);
                radix->start->data = prefix_data;
            }
            else {
                /* @todo: warn the user */
                radix->start->data = prefix_data;
            }
        }
        else {
            radix->start->data = prefix_data;
        }

        radix->data_cnt++;
        IB_FTRACE_RET_STATUS(IB_OK);
    }

    ib_radix_node_t *cur_node = NULL;
    if (IB_GET_DIR(prefix->rawbits[0]) == 0) {
        cur_node = radix->start->zero;
    }
    else {
        cur_node = radix->start->one;
    }

    /* store the reference to parent node just in case we
     * need to update something */
    ib_radix_node_t *prev_cur_node = radix->start;

    uint8_t cnt = 0;
    uint8_t cur_prefix_offset = 0;

    /* We are going to select each node as "cur_node" walking
     * the tree checking each prefix data and bit length */
    while (cnt < prefix->prefixlen && cur_node != NULL) {
        cur_prefix_offset = 0;

        for (; cur_prefix_offset < cur_node->prefix->prefixlen &&
               cnt < prefix->prefixlen; cur_prefix_offset++, cnt++)
        {
            if (IB_READ_BIT(cur_node->prefix->rawbits[cur_prefix_offset / 8],
                            cur_prefix_offset % 8) !=
                            IB_READ_BIT(prefix->rawbits[cnt / 8], cnt % 8))
            {
                /* prefix chunks different. Split here the cur_node node into
                 * common prefix (of the cur_node and the new prefix)
                 * and different suffix. */

                /* Create the new sufix from the end of the cur_node rawbits */
                uint8_t *rawbits = NULL;
                int size = 0;
                size = IB_BITS_TO_BYTES(
                               cur_node->prefix->prefixlen - cur_prefix_offset);
                rawbits = (uint8_t *) ib_mpool_calloc(radix->mp, 1,
                                                      sizeof(uint8_t) * size);
                memset(rawbits, 0, sizeof(uint8_t) * size);

                /* Copy the new sufix bits starting from the cur_prefix_offset
                 * bit offset (where the difference begins) */
                int i = cur_prefix_offset;
                int ni = 0;
                for (; i < cur_node->prefix->prefixlen; i++, ni++) {
                    if (IB_READ_BIT(cur_node->prefix->rawbits[i / 8],
                                    i % 8) == 0x01)
                    {
                        IB_SET_BIT_ARRAY(rawbits, ni);
                    }
                }

                /* Create a prefix for the new sufix */
                ib_radix_prefix_t *k = NULL;
                st = ib_radix_prefix_create(&k, rawbits,
                               cur_node->prefix->prefixlen -(cur_prefix_offset),
                               radix->mp);

                if (st != IB_OK) {
                    IB_FTRACE_RET_STATUS(IB_EALLOC);
                }

                /* Create a node for the new sufix */
                ib_radix_node_t *n = NULL;
                st = ib_radix_node_new(&n, radix->mp);
                if (st != IB_OK) {
                    IB_FTRACE_RET_STATUS(IB_EALLOC);
                }

                /* Assign the prefix to the new node */
                n->prefix = k;

                /* Copy reference to user data and remove the old reference */
                n->data = cur_node->data;
                cur_node->data = NULL;

                /* Update pointers */
                n->zero = cur_node->zero;
                n->one = cur_node->one;

                if ( IB_READ_BIT(rawbits[0], 0) == 0) {
                    cur_node->zero = n;
                    cur_node->one = NULL;
                }
                else {
                    cur_node->one = n;
                    cur_node->zero = NULL;
                }

                /* Update the old prefix of the cur_node */
                size = IB_BITS_TO_BYTES(cur_prefix_offset);
                rawbits = (uint8_t *) ib_mpool_calloc(radix->mp, 1,
                                                      sizeof(uint8_t) * size);
                memset(rawbits, 0, sizeof(uint8_t) * size);

                /* Copy cur_prefix_offset bits of the old prefix starting from 0
                 * offset */
                i = 0;
                for (; i < cur_prefix_offset; i++) {
                    if (IB_READ_BIT(cur_node->prefix->rawbits[i / 8],
                                    i % 8) == 0x01)
                    {
                        IB_SET_BIT_ARRAY(rawbits, i);
                    }
                }

                /* Update the len of the cur_node with cur_prefix_offset */
                cur_node->prefix->prefixlen = cur_prefix_offset;

                /* @todo: (mpool) Here we should free the old prefix */

                /* update the pointer to new rawbits with the common prefix */
                cur_node->prefix->rawbits = rawbits;

                size = IB_BITS_TO_BYTES(prefix->prefixlen - cnt);
                rawbits = (uint8_t *) ib_mpool_calloc(radix->mp, 1,
                                                      sizeof(uint8_t) * size);
                memset(rawbits, 0, sizeof(uint8_t) * size);

                /* Copy the new sufix bits from offset cnt
                 * (where the difference begins) to the end of the prefix */
                i = cnt;
                ni = 0;
                for (; i < prefix->prefixlen; i++, ni++) {
                    if (IB_READ_BIT(prefix->rawbits[i / 8], i % 8) == 0x01) {
                        IB_SET_BIT_ARRAY(rawbits, ni);
                    }
                }

                /* Create the prefix for the new rawbits sufix */
                st = ib_radix_prefix_create(&k, rawbits,
                                            prefix->prefixlen - cnt, radix->mp);

                if (st != IB_OK) {
                    IB_FTRACE_RET_STATUS(IB_EALLOC);
                }

                /* Create the node for the new prefix */
                st = ib_radix_node_new(&n, radix->mp);
                if (st != IB_OK) {
                    IB_FTRACE_RET_STATUS(IB_EALLOC);
                }

                n->prefix = k;

                /* Update the cur_node to point to this sufix aswell */
                if (IB_READ_BIT(prefix->rawbits[cnt / 8], cnt % 8) == 0) {
                    cur_node->zero = n;
                }
                else {
                    cur_node->one = n;
                }

                /* Set the user data */
                n->data = prefix_data;

                radix->data_cnt++;
                IB_FTRACE_RET_STATUS(IB_OK);
            }
        }

        /* If we are here then we didn't find the palce yet. Check if
         * 1. We are just on a node with the same prefix
         * 2. We don't need to split any old prefix, just to append
         * 3. We just need to append because we are at an ending node
         * 4. We need to walk more */

        if (cur_prefix_offset >= cur_node->prefix->prefixlen &&
            cnt >= prefix->prefixlen)
        {
            /* we are exactly on the node */
            if (cur_node->data == NULL) {
                cur_node->data = prefix_data;
            }
            else {
                if (radix->update_data != NULL) {
                    radix->update_data(cur_node, prefix_data);
                }
                else if (radix->free_data != NULL) {
                    radix->free_data((void*)prefix_data);
                    cur_node->data = prefix_data;
                }
                else {
                    cur_node->data = prefix_data;
                }
            }

            radix->data_cnt++;
            IB_FTRACE_RET_STATUS(IB_OK);

        }
        else if (cur_prefix_offset >= cur_node->prefix->prefixlen &&
                   cnt < prefix->prefixlen)
        {
            /* If we matched all the cur_node prefix, look if we have to jump to
            the next cur_node (otherwise break the loop to append the
            next prefix chunk) */

            prev_cur_node = cur_node;
            if (IB_READ_BIT(prefix->rawbits[cnt / 8], cnt % 8) == 0x00) {
                if (cur_node->zero) {
                    cur_node = cur_node->zero;
                }
                else {
                    /*printf("NO PATH TO LEFT, Need to append!\n"); */
                    break;
                }
            }
            else if (IB_READ_BIT(prefix->rawbits[cnt / 8], cnt % 8) == 0x01) {
                if (cur_node->one) {
                    cur_node = cur_node->one;
                }
                else {
                    /*printf("NO PATH TO RIGHT, Need to append!\n"); */
                    break;
                }
            }
            /* Continue walking the cur_node */
            continue;

        }
        else if (cnt >= prefix->prefixlen &&
                   cur_prefix_offset < cur_node->prefix->prefixlen)
        {
            /* Look at the remaining bits in the cur_node prefix and split it */

            uint8_t *rawbits = NULL;
            uint8_t size = 0;
            size = IB_BITS_TO_BYTES(cur_node->prefix->prefixlen -
                                    cur_prefix_offset);
            rawbits = (uint8_t *) ib_mpool_calloc(radix->mp, 1,
                                                  sizeof(uint8_t) * size);
            memset(rawbits, 0, sizeof(uint8_t) * size);

            /* Copy the new sufix */
            int i = cur_prefix_offset;
            int ni = 0;

            for (; i < cur_node->prefix->prefixlen; i++, ni++) {
                if (IB_READ_BIT(cur_node->prefix->rawbits[i / 8],
                                i % 8) ==0x01)
                {
                    IB_SET_BIT_ARRAY(rawbits, ni);
                }
            }

            /* Create prefix for the new sufix */
            ib_radix_prefix_t *k = NULL;
            st = ib_radix_prefix_create(&k, rawbits,
                      cur_node->prefix->prefixlen-cur_prefix_offset, radix->mp);

            if (st != IB_OK) {
                IB_FTRACE_RET_STATUS(IB_EALLOC);
            }

            /* Create node for the new prefix */
            ib_radix_node_t *n = NULL;
            st = ib_radix_node_new(&n, radix->mp);
            if (st != IB_OK) {
                IB_FTRACE_RET_STATUS(IB_EALLOC);
            }

            /* Update pointers */
            n->zero = cur_node->zero;
            n->one = cur_node->one;
            n->prefix = k;

            if (IB_GET_DIR(rawbits[0]) == 0) {
                cur_node->zero = n;
                cur_node->one = NULL;
            }
            else {
                cur_node->one = n;
                cur_node->zero = NULL;
            }

            /* Copy reference to user data to the new sufix */
            n->data = cur_node->data;
            cur_node->data = prefix_data;

            /* OK, now update the old cur_node prefix len
             * Update / cut the old prefix */
            size = IB_BITS_TO_BYTES(cur_prefix_offset);
            rawbits = (uint8_t *) ib_mpool_calloc(radix->mp, 1,
                                                  sizeof(uint8_t) * size);
            memset(rawbits, 0, sizeof(uint8_t) * size);

            /* Copy cur_prefix_offset bits of the old prefix */
            i = 0;
            for (; i < cur_prefix_offset; i++) {
                if (IB_READ_BIT(cur_node->prefix->rawbits[i / 8],
                                i % 8) ==0x01)
                {
                    IB_SET_BIT_ARRAY(rawbits, i);
                }
            }

            /* @todo: (mpool) Here we should free the old prefix */

            /* Update the pointer to new rawbits */
            cur_node->prefix->rawbits = rawbits;
            /* Update len of the cur_prefix_offset */
            cur_node->prefix->prefixlen = cur_prefix_offset;

            radix->data_cnt++;
            IB_FTRACE_RET_STATUS(IB_OK);
        }
        else {
            IB_FTRACE_RET_STATUS(IB_EUNKNOWN);
        }

        /* Else try to jump to the next branch and continue,
         * or break to append */
        if (IB_READ_BIT(prefix->rawbits[cnt / 8], cnt % 8) == 0) {
            if (cur_node->zero == NULL) {
                break;
            }
            else {
                prev_cur_node = cur_node;
                cur_node = cur_node->zero;
            }
        }
        else {
            if (cur_node->one == NULL) {
                break;
            }
            else {
                prev_cur_node = cur_node;
                cur_node = cur_node->one;
            }
        }
    }

    /* Here we just need to append a new node without splitting anything */
    if (cnt < prefix->prefixlen) {
        cur_node = prev_cur_node;

        uint8_t size = IB_BITS_TO_BYTES(prefix->prefixlen - cnt);
        uint8_t *rawbits = (uint8_t *) ib_mpool_calloc(radix->mp, 1,
                                                       sizeof(uint8_t) * size);

        memset(rawbits, 0, sizeof(uint8_t) * size);

        /* Copy the new sufix bits from offset cnt (where the difference begins)
        to the end of the prefix */
        int i = cnt;
        int ni = 0;

        for (; i < prefix->prefixlen; i++, ni++) {
            if (IB_READ_BIT(prefix->rawbits[i / 8], i % 8) == 0x01) {
                IB_SET_BIT_ARRAY(rawbits, ni);
            }
        }

        ib_radix_node_t *node = NULL;
        st = ib_radix_node_new(&node, radix->mp);

        if (st != IB_OK) {
            IB_FTRACE_RET_STATUS(IB_EALLOC);
        }

        st = ib_radix_prefix_create(&node->prefix, rawbits,
                                    prefix->prefixlen - cnt, radix->mp);

        if (st != IB_OK) {
            IB_FTRACE_RET_STATUS(IB_EALLOC);
        }

        node->data = prefix_data;

        if (IB_READ_BIT(prefix->rawbits[cnt / 8], cnt % 8) == 0 &&
            cur_node->zero == NULL)
        {
            cur_node->zero = node;
        }
        else if (IB_READ_BIT(prefix->rawbits[cnt / 8], cnt % 8) == 1 &&
                   cur_node->one == NULL)
        {
            cur_node->one = node;
        }
        else {
            IB_FTRACE_RET_STATUS(IB_EUNKNOWN);
        }

        radix->data_cnt++;
        IB_FTRACE_RET_STATUS(IB_OK);
    }
    IB_FTRACE_RET_STATUS(IB_EUNKNOWN);
}
Exemplo n.º 3
0
/*
 * Create a prefix of type ib_radix_prefix_t given the cidr ascii representation
 * Valid for ipv4 and ipv6.
 * warning:
 *  the criteria to determine if ipv6 or ipv4 is the presence of ':' (ipv6)
 *  so the functions using this API should implement their own checks for valid
 *  formats, with regex, or functions, thought
 *
 * @param cidr ascii representation
 * @param mp pool where we should allocate the prefix
 *
 * @returns struct in6_addr*
 */
ib_status_t ib_radix_ip_to_prefix(const char *cidr,
                                  ib_radix_prefix_t **prefix,
                                  ib_mpool_t *mp)
{
    IB_FTRACE_INIT(ib_radix_ip_to_prefix);

    /* If we got a mask, we will need to copy the IP to separate it from
     the mask, and the max length should be the length of a IPv6 in ascii,
     so 39 plus \0 */
    char ip_tmp[40];

    const char *mask = NULL;
    uint64_t nmask = 0;

    if (IB_RADIX_IS_IPV4(cidr)) {
        mask = strstr(cidr, "/");

        if (mask != NULL) {
            nmask = strtoull(mask+1, NULL, 10);

            if (nmask > 32) {
                IB_FTRACE_RET_STATUS(IB_EINVAL);
            }

            /* Don't modify the orign cidr, instead of that, create a local copy
             in stack memory to avoid allocations */
            memcpy(ip_tmp, cidr, mask - cidr);
            ip_tmp[mask - cidr] = '\0';
            cidr = ip_tmp;
        }
        else {
            nmask = 32;
        }

        struct in_addr *cidrv4 = ib_radix_get_IPV4_addr(cidr, mp);
        if (cidrv4 == NULL) {
            IB_FTRACE_RET_STATUS(IB_EINVAL);
        }

        /* Return a prefix for IPV4 */
        IB_FTRACE_RET_STATUS(ib_radix_prefix_create(prefix,
                                                    (uint8_t *) cidrv4,
                                                    (uint8_t) nmask, mp));
    }
    else if (IB_RADIX_IS_IPV6(cidr)) {
        mask = strstr(cidr, "/");

        if (mask != NULL) {
            nmask = strtoull(mask+1, NULL, 10);

            if (nmask > 128) {
                IB_FTRACE_RET_STATUS(IB_EINVAL);
            }

            /* Don't modify the orign cidr, instead of that, create a local copy
             in stack memory to avoid allocations */
            memcpy(ip_tmp, cidr, mask - cidr);
            ip_tmp[mask - cidr] = '\0';
            cidr = ip_tmp;
        }
        else {
            nmask = 128;
        }

        struct in6_addr *cidrv6 = ib_radix_get_IPV6_addr(cidr, mp);
        if (cidrv6 == NULL) {
            IB_FTRACE_RET_STATUS(IB_EINVAL);
        }

        /* Return a prefix for IPV6 */
        IB_FTRACE_RET_STATUS(ib_radix_prefix_create(prefix,
                                                    (uint8_t *) cidrv6,
                                                    (uint8_t) nmask, mp));
    }
    IB_FTRACE_RET_STATUS(IB_EINVAL);
}