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