You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/util-radix-tree-common.h

967 lines
31 KiB
C

/* Copyright (C) 2007-2022 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
* \author Anoop Saldanha <anoopsaldanha@gmail.com>
*
* Implementation of radix trees
*/
#include "util-validate.h"
#ifndef ADDRESS_BYTES
#error "define ADDRESS_BYTES"
#endif
#ifndef NETMASK_MAX
#error "define NETMASK_MAX"
#endif
#define RADIX_BITTEST(x, y) ((x) & (y))
/**
* \brief Structure that hold the user data and the netmask associated with it.
*/
typedef struct RadixUserData {
/* holds a pointer to the user data associated with the particular netmask */
void *user;
/* pointer to the next user data in the list */
struct RadixUserData *next;
/* holds the netmask value that corresponds to this user data pointer */
uint8_t netmask;
} RadixUserData;
/**
* \brief Allocates and returns a new instance of RadixUserData.
*
* \param netmask The netmask entry (cidr) that has to be made in the new
* RadixUserData instance
* \param user The user data that has to be set for the above
* netmask in the newly created RadixUserData instance.
*
* \retval user_data Pointer to a new instance of RadixUserData.
*/
static RadixUserData *AllocUserData(uint8_t netmask, void *user)
{
RadixUserData *user_data = SCCalloc(1, sizeof(RadixUserData));
if (unlikely(user_data == NULL)) {
sc_errno = SC_ENOMEM;
return NULL;
}
user_data->netmask = netmask;
user_data->user = user;
return user_data;
}
/**
* \brief Deallocates an instance of RadixUserData.
*
* \param user_data Pointer to the instance of RadixUserData that has to be
* freed.
*/
static void FreeUserData(RadixUserData *user_data)
{
SCFree(user_data);
}
/**
* \brief Appends a user_data instance(RadixUserData) to a
* user_data(RadixUserData) list. We add the new entry in descending
* order with respect to the netmask contained in the RadixUserData.
*
* \param new Pointer to the RadixUserData to be added to the list.
* \param list Pointer to the RadixUserData list head, to which "new" has to
* be appended.
*/
static void AppendToUserDataList(RadixUserData *add, RadixUserData **list)
{
RadixUserData *temp = NULL;
BUG_ON(add == NULL || list == NULL);
/* add to the list in descending order. The reason we do this is for
* optimizing key retrieval for a ip key under a netblock */
RadixUserData *prev = temp = *list;
while (temp != NULL) {
if (add->netmask > temp->netmask)
break;
prev = temp;
temp = temp->next;
}
if (temp == *list) {
add->next = *list;
*list = add;
} else {
add->next = prev->next;
prev->next = add;
}
}
/**
* \brief Adds a netmask and its user_data for a particular prefix stream.
*
* \param prefix The prefix stream to which the netmask and its corresponding
* user data has to be added.
* \param netmask The netmask value (cidr) that has to be added to the prefix.
* \param user The pointer to the user data corresponding to the above
* netmask.
*/
static void AddNetmaskUserDataToNode(RADIX_NODE_TYPE *node, uint8_t netmask, void *user)
{
BUG_ON(!node);
AppendToUserDataList(AllocUserData(netmask, user), &node->user_data);
}
/**
* \brief Removes a particular user_data corresponding to a particular netmask
* entry, from a prefix.
*
* \param prefix Pointer to the prefix from which the user_data/netmask entry
* has to be removed.
* \param netmask The netmask value (cidr) whose user_data has to be deleted.
*/
static void RemoveNetmaskUserDataFromNode(RADIX_NODE_TYPE *node, uint8_t netmask)
{
BUG_ON(!node);
RadixUserData *temp = NULL, *prev = NULL;
prev = temp = node->user_data;
while (temp != NULL) {
if (temp->netmask == netmask) {
if (temp == node->user_data)
node->user_data = temp->next;
else
prev->next = temp->next;
FreeUserData(temp);
break;
}
prev = temp;
temp = temp->next;
}
}
/**
* \brief Indicates if prefix contains an entry for an ip with a specific netmask.
*
* \param prefix Pointer to the ip prefix that is being checked.
* \param netmask The netmask value (cidr) that has to be checked for
* presence in the prefix.
*
* \retval 1 On match.
* \retval 0 On no match.
*/
static int ContainNetmask(RADIX_NODE_TYPE *node, uint8_t netmask)
{
BUG_ON(!node);
RadixUserData *user_data = node->user_data;
while (user_data != NULL) {
if (user_data->netmask == netmask)
return 1;
user_data = user_data->next;
}
return 0;
}
/**
* \brief Returns the total netmask count for this prefix.
*
* \param prefix Pointer to the prefix
*
* \retval count The total netmask count for this prefix.
*/
static int NetmaskCount(RADIX_NODE_TYPE *node)
{
BUG_ON(!node);
uint32_t count = 0;
RadixUserData *user_data = node->user_data;
while (user_data != NULL) {
count++;
user_data = user_data->next;
}
return count;
}
/**
* \brief Indicates if prefix contains an entry for an ip with a specific netmask
* and if it does, it sets `user_data_result` to the netmask user_data entry.
*
* \param prefix Pointer to the ip prefix that is being checked.
* \param netmask The netmask value for which we will have to return the user_data
* \param exact_match Bool flag which indicates if we should check if the prefix
* holds proper netblock or not.
* \param[out] user_data_result user data pointer
*
* \retval 1 On match.
* \retval 0 On no match.
*/
static int ContainNetmaskAndSetUserData(
RADIX_NODE_TYPE *node, uint8_t netmask, bool exact_match, void **user_data_result)
{
BUG_ON(!node);
RadixUserData *user_data = node->user_data;
/* Check if we have a match for an exact ip. An exact ip as in not a proper
* netblock, i.e. an ip with a netmask of 32. */
if (exact_match) {
if (user_data->netmask == netmask) {
if (user_data_result)
*user_data_result = user_data->user;
return 1;
} else {
goto no_match;
}
}
/* Check for the user_data entry for this netmask_value */
while (user_data != NULL) {
if (user_data->netmask == netmask) {
if (user_data_result)
*user_data_result = user_data->user;
return 1;
}
user_data = user_data->next;
}
no_match:
if (user_data_result != NULL)
*user_data_result = NULL;
return 0;
}
/**
* \brief Creates a new node for the Radix tree
*
* \retval node The newly created node for the radix tree
*/
static inline RADIX_NODE_TYPE *RadixCreateNode(void)
{
RADIX_NODE_TYPE *node = NULL;
if ((node = SCCalloc(1, sizeof(RADIX_NODE_TYPE))) == NULL) {
sc_errno = SC_ENOMEM;
return NULL;
}
node->bit = NETMASK_MAX;
return node;
}
/**
* \brief Frees a Radix tree node
*
* \param node Pointer to a Radix tree node
* \param tree Pointer to the Radix tree to which this node belongs
*/
static void ReleaseNode(
RADIX_NODE_TYPE *node, RADIX_TREE_TYPE *tree, const RADIX_CONFIG_TYPE *config)
{
DEBUG_VALIDATE_BUG_ON(config == NULL);
if (node != NULL) {
RadixUserData *ud = node->user_data;
while (ud != NULL) {
RadixUserData *next = ud->next;
if (config->Free != NULL && ud->user) {
config->Free(ud->user);
}
FreeUserData(ud);
ud = next;
}
SCFree(node);
}
}
/**
* \brief Internal helper function used by TreeRelease to free a subtree
*
* \param node Pointer to the root of the subtree that has to be freed
* \param tree Pointer to the Radix tree to which this subtree belongs
*/
static void ReleaseSubtree(
RADIX_NODE_TYPE *node, RADIX_TREE_TYPE *tree, const RADIX_CONFIG_TYPE *config)
{
DEBUG_VALIDATE_BUG_ON(config == NULL);
if (node != NULL) {
ReleaseSubtree(node->left, tree, config);
ReleaseSubtree(node->right, tree, config);
ReleaseNode(node, tree, config);
}
}
/**
* \brief frees a Radix tree and all its nodes
*
* \param tree Pointer to the Radix tree that has to be freed
*/
static void TreeRelease(RADIX_TREE_TYPE *tree, const RADIX_CONFIG_TYPE *config)
{
DEBUG_VALIDATE_BUG_ON(config == NULL);
if (tree == NULL)
return;
ReleaseSubtree(tree->head, tree, config);
tree->head = NULL;
return;
}
/**
* \brief Adds a key to the Radix tree. Used internally by the API.
*
* \param tree Pointer to the Radix tree
* \param key_stream Data that has to added to the Radix tree
* \param netmask The netmask (cidr)
* \param user Pointer to the user data that has to be associated with
* this key
* \param exclusive True if the node should be added iff it doesn't exist.
*
* \retval node Pointer to the newly created node
*/
static RADIX_NODE_TYPE *AddKey(RADIX_TREE_TYPE *tree, const RADIX_CONFIG_TYPE *config,
const uint8_t *key_stream, uint8_t netmask, void *user, const bool exclusive)
{
DEBUG_VALIDATE_BUG_ON(config == NULL);
RADIX_NODE_TYPE *node = NULL;
RADIX_NODE_TYPE *parent = NULL;
RADIX_NODE_TYPE *bottom_node = NULL;
uint8_t tmp_stream[ADDRESS_BYTES];
memcpy(tmp_stream, key_stream, sizeof(tmp_stream));
if (tree == NULL) {
SCLogError("Argument \"tree\" NULL");
sc_errno = SC_EINVAL;
return NULL;
}
/* chop the ip address against a netmask */
MaskIPNetblock(tmp_stream, netmask, NETMASK_MAX);
/* the very first element in the radix tree */
if (tree->head == NULL) {
node = RadixCreateNode();
if (node == NULL)
return NULL;
memcpy(node->prefix_stream, tmp_stream, sizeof(tmp_stream));
node->has_prefix = true;
node->user_data = AllocUserData(netmask, user);
if (node->user_data == NULL) {
ReleaseNode(node, tree, config);
return NULL;
}
tree->head = node;
if (netmask == NETMASK_MAX)
return node;
AddNetmaskToMasks(node, netmask);
return node;
}
node = tree->head;
/* we walk down the tree only when we satisfy 2 conditions. The first one
* being the incoming prefix is shorter than the differ bit of the current
* node. In case we fail in this aspect, we walk down to the tree, till we
* arrive at a node that ends in a prefix */
while (node->bit < NETMASK_MAX || node->has_prefix == false) {
/* if the bitlen isn't long enough to handle the bit test, we just walk
* down along one of the paths, since either paths should end up with a
* node that has a common prefix whose differ bit is greater than the
* bitlen of the incoming prefix */
if (NETMASK_MAX <= node->bit) {
if (node->right == NULL)
break;
node = node->right;
} else {
if (RADIX_BITTEST(tmp_stream[node->bit >> 3], (0x80 >> (node->bit % 8)))) {
if (node->right == NULL)
break;
node = node->right;
} else {
if (node->left == NULL)
break;
node = node->left;
}
}
}
/* we need to keep a reference to the bottom-most node, that actually holds
* the prefix */
bottom_node = node;
/* get the first bit position where the ips differ */
uint8_t check_bit = MIN(node->bit, NETMASK_MAX);
uint8_t differ_bit = 0;
uint8_t j = 0;
for (uint8_t i = 0; (i * 8) < check_bit; i++) {
int temp = 0;
if ((temp = (tmp_stream[i] ^ bottom_node->prefix_stream[i])) == 0) {
differ_bit = (i + 1) * 8;
continue;
}
/* find out the position where the first bit differs. This method is
* faster, but at the cost of being larger. But with larger caches
* these days we don't have to worry about cache misses */
temp = temp * 2;
if (temp >= 256)
j = 0;
else if (temp >= 128)
j = 1;
else if (temp >= 64)
j = 2;
else if (temp >= 32)
j = 3;
else if (temp >= 16)
j = 4;
else if (temp >= 8)
j = 5;
else if (temp >= 4)
j = 6;
else if (temp >= 2)
j = 7;
differ_bit = i * 8 + j;
break;
}
if (check_bit < differ_bit)
differ_bit = check_bit;
/* walk up the tree till we find the position, to fit our new node in */
parent = node->parent;
while (parent && differ_bit <= parent->bit) {
node = parent;
parent = node->parent;
}
BUG_ON(differ_bit == NETMASK_MAX && node->bit != NETMASK_MAX);
/* We already have the node in the tree with the same differing bit position */
if (differ_bit == NETMASK_MAX && node->bit == NETMASK_MAX) {
if (node->has_prefix) {
/* Check if we already have this netmask entry covered by this prefix */
if (ContainNetmask(node, netmask)) {
/* Basically we already have this stream prefix, as well as the
* netblock entry for this. A perfect duplicate. */
if (exclusive) {
SCLogDebug("not inserting since it already exists");
sc_errno = SC_EEXIST;
return NULL;
}
SCLogDebug("Duplicate entry for this ip address/netblock");
} else {
/* Basically we already have this stream prefix, but we don't
* have an entry for this particular netmask value for this
* prefix. For example, we have an entry for 192.168.0.0 and
* 192.168.0.0/16 and now we are trying to enter 192.168.0.0/20 */
AddNetmaskUserDataToNode(node, netmask, user);
/* if we are adding a netmask of 32 it indicates we are adding
* an exact host ip into the radix tree, in which case we don't
* need to add the netmask value into the tree */
if (netmask == NETMASK_MAX)
return node;
/* looks like we have a netmask which is != 32, in which
* case we walk up the tree to insert this netmask value in the
* correct node */
parent = node->parent;
while (parent != NULL && netmask < (parent->bit + 1)) {
node = parent;
parent = parent->parent;
}
AddNetmaskToMasks(node, netmask);
if (NetmaskEqualsMask(node, netmask)) {
return node;
}
}
}
return node;
}
/* create the leaf node for the new key */
RADIX_NODE_TYPE *new_node = RadixCreateNode();
if (new_node == NULL)
return NULL;
memcpy(new_node->prefix_stream, tmp_stream, sizeof(tmp_stream));
new_node->has_prefix = true;
new_node->user_data = AllocUserData(netmask, user);
if (new_node->user_data == NULL) {
ReleaseNode(new_node, tree, config);
return NULL;
}
/* stick our new_node into the tree. Create a node that holds the
* differing bit position and break the branch. Also handle the
* tranfer of netmasks between node and inter_node(explained in more
* detail below) */
RADIX_NODE_TYPE *inter_node = RadixCreateNode();
if (inter_node == NULL) {
ReleaseNode(new_node, tree, config);
return NULL;
}
inter_node->has_prefix = false;
inter_node->bit = differ_bit;
inter_node->parent = node->parent;
SCLogDebug("inter_node: differ_bit %u", differ_bit);
/* update netmasks for node and set them for inter_node */
ProcessInternode(node, inter_node);
if (RADIX_BITTEST(tmp_stream[differ_bit >> 3], (0x80 >> (differ_bit % 8)))) {
inter_node->left = node;
inter_node->right = new_node;
} else {
inter_node->left = new_node;
inter_node->right = node;
}
new_node->parent = inter_node;
if (node->parent == NULL)
tree->head = inter_node;
else if (node->parent->right == node)
node->parent->right = inter_node;
else
node->parent->left = inter_node;
node->parent = inter_node;
/* insert the netmask into the tree */
if (netmask != NETMASK_MAX) {
node = new_node;
parent = new_node->parent;
while (parent != NULL && netmask < (parent->bit + 1)) {
node = parent;
parent = parent->parent;
}
AddNetmaskToMasks(node, netmask);
}
return new_node;
}
/**
* \brief Removes a netblock entry from an ip node. The function first
* deletes the netblock/user_data entry for the prefix and then
* removes the netmask entry that has been made in the tree, by
* walking up the tree and deleting the entry from the specific node.
*
* \param node The node from which the netblock entry has to be removed.
* \param netmask The netmask entry (cidr) that has to be removed.
*/
static void RemoveNetblockEntry(RADIX_NODE_TYPE *node, uint8_t netmask)
{
BUG_ON(!node);
RemoveNetmaskUserDataFromNode(node, netmask);
if (netmask == NETMASK_MAX) {
SCLogDebug("%d == %d", netmask, NETMASK_MAX);
return;
}
RemoveNetmaskFromMasks(node, netmask);
if (node->parent != NULL)
RemoveNetmaskFromMasks(node->parent, netmask);
return;
}
/**
* \brief Removes a key from the Radix tree
*
* \param key_stream Data that has to be removed from the Radix tree
* \param tree Pointer to the Radix tree from which the key has to be
* removed
*/
static void RemoveKey(RADIX_TREE_TYPE *tree, const RADIX_CONFIG_TYPE *config,
const uint8_t *key_stream, const uint8_t netmask)
{
RADIX_NODE_TYPE *node = tree->head;
RADIX_NODE_TYPE *parent = NULL;
RADIX_NODE_TYPE *temp_dest = NULL;
if (node == NULL) {
SCLogDebug("tree is empty");
return;
}
uint8_t tmp_stream[ADDRESS_BYTES];
memcpy(tmp_stream, key_stream, sizeof(tmp_stream));
while (node->bit < NETMASK_MAX) {
if (RADIX_BITTEST(tmp_stream[node->bit >> 3], (0x80 >> (node->bit % 8)))) {
node = node->right;
} else {
node = node->left;
}
if (node == NULL) {
SCLogDebug("no matching node found");
return;
}
}
if (node->bit != NETMASK_MAX || node->has_prefix == false) {
SCLogDebug("node %p bit %d != %d, or not has_prefix %s", node, node->bit, NETMASK_MAX,
node->has_prefix ? "true" : "false");
return;
}
if (SCMemcmp(node->prefix_stream, tmp_stream, sizeof(tmp_stream)) == 0) {
if (!ContainNetmask(node, netmask)) {
SCLogDebug("key exists in the tree, but this (%d) "
"netblock entry doesn't exist",
netmask);
return;
}
} else {
SCLogDebug("You are trying to remove a key that doesn't exist in the "
"Radix Tree");
return;
}
/* The ip node does exist, and the netblock entry does exist in this node, if
* we have reached this point. If we have more than one netblock entry, it
* indicates we have multiple entries for this key. So we delete that
* particular netblock entry, and make our way out of this function */
if (NetmaskCount(node) > 1) { // || !NoneNegated(node)) {
RemoveNetblockEntry(node, netmask);
SCLogDebug("NetmaskCount");
return;
}
SCLogDebug("not netmask cnt");
/* we are deleting the root of the tree. This would be the only node left
* in the tree */
if (tree->head == node) {
ReleaseNode(node, tree, config);
tree->head = NULL;
SCLogDebug("tree->head == node");
return;
}
parent = node->parent;
/* parent->parent is not the root of the tree */
if (parent->parent != NULL) {
if (parent->parent->left == parent) {
if (node->parent->left == node) {
temp_dest = parent->right;
parent->parent->left = parent->right;
parent->right->parent = parent->parent;
} else {
temp_dest = parent->left;
parent->parent->left = parent->left;
parent->left->parent = parent->parent;
}
} else {
if (node->parent->left == node) {
temp_dest = parent->right;
parent->parent->right = parent->right;
parent->right->parent = parent->parent;
} else {
temp_dest = parent->left;
parent->parent->right = parent->left;
parent->left->parent = parent->parent;
}
}
/* parent is the root of the tree */
} else {
if (parent->left == node) {
temp_dest = tree->head->right;
tree->head->right->parent = NULL;
tree->head = tree->head->right;
} else {
temp_dest = tree->head->left;
tree->head->left->parent = NULL;
tree->head = tree->head->left;
}
}
/* We need to shift the netmask entries from the node that would be
* deleted to its immediate descendant */
AddNetmasksFromNode(temp_dest, parent);
RemoveNetmaskFromMasks(temp_dest, netmask);
/* release the nodes */
ReleaseNode(parent, tree, config);
ReleaseNode(node, tree, config);
SCLogDebug("end (netmask %d)", netmask);
return;
}
/**
* \brief Checks if an IP prefix falls under a netblock, in the path to the root
* of the tree, from the node. Used internally by FindKey()
*
* \param prefix Pointer to the prefix that contains the ip address
* \param node Pointer to the node from where we have to climb the tree
*/
static inline RADIX_NODE_TYPE *FindKeyIPNetblock(const uint8_t *key_stream, RADIX_NODE_TYPE *node,
void **user_data_result, uint8_t *out_netmask)
{
while (node != NULL && NetmasksEmpty(node))
node = node->parent;
if (node == NULL)
return NULL;
uint8_t tmp_stream[ADDRESS_BYTES];
memcpy(tmp_stream, key_stream, sizeof(tmp_stream));
/* hold the node found containing a netmask. We will need it when we call
* this function recursively */
RADIX_NODE_TYPE *netmask_node = node;
for (uint8_t j = 0; j <= NETMASK_MAX; j++) {
uint8_t m = NETMASK_MAX - j;
if (!(NetmaskIssetInMasks(netmask_node, m)))
continue;
for (uint8_t i = 0; i < ADDRESS_BYTES; i++) {
uint32_t mask = UINT_MAX;
if (((i + 1) * 8) > m) {
if (((i + 1) * 8 - m) < 8)
mask = UINT_MAX << ((i + 1) * 8 - m);
else
mask = 0;
}
tmp_stream[i] &= mask;
}
while (node->bit < NETMASK_MAX) {
if (RADIX_BITTEST(tmp_stream[node->bit >> 3], (0x80 >> (node->bit % 8)))) {
node = node->right;
} else {
node = node->left;
}
if (node == NULL)
return NULL;
}
if (node->bit != NETMASK_MAX || node->has_prefix == false)
return NULL;
if (SCMemcmp(node->prefix_stream, tmp_stream, sizeof(tmp_stream)) == 0) {
if (ContainNetmaskAndSetUserData(node, m, false, user_data_result)) {
*out_netmask = m;
return node;
}
}
}
return FindKeyIPNetblock(tmp_stream, netmask_node->parent, user_data_result, out_netmask);
}
/**
* \brief Checks if an IP address key is present in the tree. The function
* apart from handling any normal data, also handles ipv4/ipv6 netblocks
*
* \param key_stream Data that has to be found in the Radix tree
* \param tree Pointer to the Radix tree
* \param exact_match The key to be searched is an ip address
*/
static RADIX_NODE_TYPE *FindKey(const RADIX_TREE_TYPE *tree, const uint8_t *key_stream,
const uint8_t netmask, bool exact_match, void **user_data_result, uint8_t *out_netmask)
{
if (tree == NULL || tree->head == NULL)
return NULL;
RADIX_NODE_TYPE *node = tree->head;
uint8_t tmp_stream[ADDRESS_BYTES];
memcpy(tmp_stream, key_stream, sizeof(tmp_stream));
while (node->bit < NETMASK_MAX) {
if (RADIX_BITTEST(tmp_stream[node->bit >> 3], (0x80 >> (node->bit % 8)))) {
node = node->right;
} else {
node = node->left;
}
if (node == NULL) {
return NULL;
}
}
if (node->bit != NETMASK_MAX || node->has_prefix == false) {
return NULL;
}
if (SCMemcmp(node->prefix_stream, tmp_stream, sizeof(tmp_stream)) == 0) {
SCLogDebug("stream match");
if (ContainNetmaskAndSetUserData(node, netmask, true, user_data_result)) {
SCLogDebug("contains netmask etc");
*out_netmask = netmask;
return node;
}
}
/* if you are not an ip key, get out of here */
if (exact_match) {
SCLogDebug("no node found and need exact match, so failed");
return NULL;
}
RADIX_NODE_TYPE *ret = FindKeyIPNetblock(tmp_stream, node, user_data_result, out_netmask);
return ret;
}
/**
* \brief Checks if an IPV4 address is present in the tree
*
* \param key_stream Data that has to be found in the Radix tree. In this case
* an IPV4 address
* \param tree Pointer to the Radix tree instance
*/
static RADIX_NODE_TYPE *FindExactMatch(
const RADIX_TREE_TYPE *tree, const uint8_t *key_stream, void **user_data_result)
{
uint8_t unused = 0;
return FindKey(tree, key_stream, NETMASK_MAX, true, user_data_result, &unused);
}
/**
* \brief Checks if an IPV4 address is present in the tree under a netblock
*
* \param key_stream Data that has to be found in the Radix tree. In this case
* an IPV4 address
* \param tree Pointer to the Radix tree instance
*/
static RADIX_NODE_TYPE *FindBestMatch(
const RADIX_TREE_TYPE *tree, const uint8_t *key_stream, void **user_data_result)
{
uint8_t unused = 0;
return FindKey(tree, key_stream, NETMASK_MAX, false, user_data_result, &unused);
}
static RADIX_NODE_TYPE *FindBestMatch2(const RADIX_TREE_TYPE *tree, const uint8_t *key_stream,
void **user_data_result, uint8_t *out_netmask)
{
return FindKey(tree, key_stream, NETMASK_MAX, false, user_data_result, out_netmask);
}
/**
* \brief Checks if an IPV4 Netblock address is present in the tree
*
* \param key_stream Data that has to be found in the Radix tree. In this case
* an IPV4 netblock address
* \param tree Pointer to the Radix tree instance
*/
static RADIX_NODE_TYPE *FindNetblock(const RADIX_TREE_TYPE *tree, const uint8_t *key_stream,
const uint8_t netmask, void **user_data_result)
{
uint8_t unused = 0;
RADIX_NODE_TYPE *node = FindKey(tree, key_stream, netmask, true, user_data_result, &unused);
return node;
}
/**
* \brief Helper function used by PrintTree. Prints the subtree with
* node as the root of the subtree
*
* \param node Pointer to the node that is the root of the subtree to be printed
* \param level Used for indentation purposes
*/
static void PrintSubtree(RADIX_NODE_TYPE *node, int level, void (*PrintData)(void *))
{
if (node != NULL) {
PrintNodeInfo(node, level, PrintData);
PrintSubtree(node->left, level + 1, PrintData);
PrintSubtree(node->right, level + 1, PrintData);
}
return;
}
/**
* \brief Prints the Radix Tree. While printing the radix tree we use the
* following format
*
* Parent_0
* Left_Child_1
* Left_Child_2
* Right_Child_2
* Right_Child_1
* Left_Child_2
* Right_Child_2 and so on
*
* Each node printed out holds details on the next bit that differs
* amongst its children, and if the node holds a prefix, the perfix is
* printed as well.
*
* \param tree Pointer to the Radix tree that has to be printed
*/
static void PrintTree(RADIX_TREE_TYPE *tree, const RADIX_CONFIG_TYPE *config)
{
printf("Printing the Radix Tree: \n");
PrintSubtree(tree->head, 0, config->PrintData);
}
static bool CompareTreesSub(
RADIX_NODE_TYPE *n1, RADIX_NODE_TYPE *n2, RADIX_TREE_COMPARE_CALLBACK Callback)
{
// compare nodes
bool n1_has_left = n1->left != NULL;
bool n2_has_left = n2->left != NULL;
if (n1_has_left != n2_has_left)
return false;
bool n1_has_right = n1->right != NULL;
bool n2_has_right = n2->right != NULL;
if (n1_has_right != n2_has_right)
return false;
if (SCMemcmp(n1->prefix_stream, n2->prefix_stream, ADDRESS_BYTES) != 0)
return false;
RadixUserData *u1 = n1->user_data;
RadixUserData *u2 = n2->user_data;
while (1) {
if (u1 == NULL && u2 == NULL)
break;
if ((u1 != NULL && u2 == NULL) || (u1 == NULL && u2 != NULL))
return false;
if (u1->netmask != u2->netmask)
return false;
if (Callback != NULL) {
if (Callback(u1->user, u2->user) == false)
return false;
}
u1 = u1->next;
u2 = u2->next;
}
if (n1->left && n2->left)
if (CompareTreesSub(n1->left, n2->left, Callback) == false)
return false;
if (n1->right && n2->right)
if (CompareTreesSub(n1->right, n2->right, Callback) == false)
return false;
return true;
}
static bool CompareTrees(
const RADIX_TREE_TYPE *t1, const RADIX_TREE_TYPE *t2, RADIX_TREE_COMPARE_CALLBACK Callback)
{
if (t1->head == NULL && t2->head == NULL)
return true;
if ((t1->head == NULL && t2->head != NULL) || (t1->head != NULL && t2->head == NULL))
return false;
return CompareTreesSub(t1->head, t2->head, Callback);
}