mirror of https://github.com/OISF/suricata
radix: implement more compact trees
Implement a more compact set of trees specifically for IPv4 and IPv6 addresses. This allows for more compact data structures and fewer memory allocations. Based on the existing radix tree implementation.pull/12174/head
parent
e9a2352b99
commit
8f03a2eb9b
@ -0,0 +1,966 @@
|
||||
/* 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);
|
||||
}
|
@ -0,0 +1,940 @@
|
||||
/* 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 tree for IPv4
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "util-debug.h"
|
||||
#include "util-error.h"
|
||||
#include "util-ip.h"
|
||||
#include "util-unittest.h"
|
||||
#include "util-memcmp.h"
|
||||
#include "util-print.h"
|
||||
#include "util-byte.h"
|
||||
#include "util-radix4-tree.h"
|
||||
|
||||
#define ADDRESS_BYTES (uint8_t)4
|
||||
#define NETMASK_MAX (uint8_t)32
|
||||
|
||||
#define RADIX_TREE_TYPE SCRadix4Tree
|
||||
#define RADIX_NODE_TYPE SCRadix4Node
|
||||
#define RADIX_CONFIG_TYPE SCRadix4Config
|
||||
#define RADIX_TREE_COMPARE_CALLBACK SCRadix4TreeCompareFunc
|
||||
|
||||
static void PrintUserdata(SCRadix4Node *node, int level, void (*PrintData)(void *));
|
||||
|
||||
static inline void AddNetmaskToMasks(SCRadix4Node *node, int netmask)
|
||||
{
|
||||
SCLogDebug("masks %" PRIX64 ", adding %d/%" PRIX64, (uint64_t)node->masks, netmask,
|
||||
(uint64_t)BIT_U64(netmask));
|
||||
node->masks |= BIT_U64(netmask);
|
||||
SCLogDebug("masks %" PRIX64, (uint64_t)node->masks);
|
||||
}
|
||||
|
||||
static inline void RemoveNetmaskFromMasks(SCRadix4Node *node, int netmask)
|
||||
{
|
||||
SCLogDebug("masks %" PRIX64 ", removing %d/%" PRIX64, (uint64_t)node->masks, netmask,
|
||||
(uint64_t)BIT_U64(netmask));
|
||||
node->masks &= ~BIT_U64(netmask);
|
||||
SCLogDebug("masks %" PRIX64, (uint64_t)node->masks);
|
||||
}
|
||||
|
||||
static inline void AddNetmasksFromNode(SCRadix4Node *dst, SCRadix4Node *src)
|
||||
{
|
||||
dst->masks |= src->masks;
|
||||
}
|
||||
|
||||
static inline bool NetmasksEmpty(const SCRadix4Node *node)
|
||||
{
|
||||
return (node->masks == 0);
|
||||
}
|
||||
|
||||
static inline bool NetmaskEqualsMask(const SCRadix4Node *node, int netmask)
|
||||
{
|
||||
return (node->masks == BIT_U64(netmask));
|
||||
}
|
||||
|
||||
static inline bool NetmaskIssetInMasks(const SCRadix4Node *node, int netmask)
|
||||
{
|
||||
return ((node->masks & BIT_U64(netmask)) != 0);
|
||||
}
|
||||
|
||||
static inline void ProcessInternode(SCRadix4Node *node, SCRadix4Node *inter_node)
|
||||
{
|
||||
const int differ_bit = inter_node->bit;
|
||||
uint64_t rem = 0;
|
||||
for (int x = 0; x <= NETMASK_MAX; x++) {
|
||||
int m = NETMASK_MAX - x;
|
||||
if (m == differ_bit)
|
||||
break;
|
||||
else {
|
||||
rem |= (node->masks & BIT_U64(m));
|
||||
}
|
||||
}
|
||||
|
||||
inter_node->masks |= node->masks;
|
||||
inter_node->masks &= ~rem;
|
||||
node->masks = rem;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Prints the node information from a Radix4 tree
|
||||
*
|
||||
* \param node Pointer to the Radix4 node whose information has to be printed
|
||||
* \param level Used for indentation purposes
|
||||
*/
|
||||
static void PrintNodeInfo(SCRadix4Node *node, int level, void (*PrintData)(void *))
|
||||
{
|
||||
if (node == NULL)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < level; i++)
|
||||
printf(" ");
|
||||
|
||||
printf("%d [", node->bit);
|
||||
|
||||
if (node->masks == 0) {
|
||||
printf(" - ");
|
||||
} else {
|
||||
for (int i = 0, x = 0; i <= 32; i++) {
|
||||
if (node->masks & BIT_U64(i)) {
|
||||
printf("%s%d", (x && x < 32) ? ", " : "", i);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("] (");
|
||||
|
||||
if (node->has_prefix) {
|
||||
char addr[16] = "";
|
||||
PrintInet(AF_INET, &node->prefix_stream, addr, sizeof(addr));
|
||||
printf("%s - user_data %p)\n", addr, node->user_data);
|
||||
PrintUserdata(node, level + 1, PrintData);
|
||||
} else {
|
||||
printf("no prefix)\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#include "util-radix-tree-common.h"
|
||||
|
||||
SCRadix4Node *SCRadix4TreeFindExactMatch(
|
||||
const SCRadix4Tree *tree, const uint8_t *key, void **user_data)
|
||||
{
|
||||
return FindExactMatch(tree, key, user_data);
|
||||
}
|
||||
|
||||
SCRadix4Node *SCRadix4TreeFindNetblock(
|
||||
const SCRadix4Tree *tree, const uint8_t *key, const uint8_t netmask, void **user_data)
|
||||
{
|
||||
return FindNetblock(tree, key, netmask, user_data);
|
||||
}
|
||||
|
||||
SCRadix4Node *SCRadix4TreeFindBestMatch(
|
||||
const SCRadix4Tree *tree, const uint8_t *key, void **user_data)
|
||||
{
|
||||
return FindBestMatch(tree, key, user_data);
|
||||
}
|
||||
|
||||
SCRadix4Node *SCRadix4TreeFindBestMatch2(
|
||||
const SCRadix4Tree *tree, const uint8_t *key, void **user_data, uint8_t *out_netmask)
|
||||
{
|
||||
return FindBestMatch2(tree, key, user_data, out_netmask);
|
||||
}
|
||||
|
||||
SCRadix4Tree SCRadix4TreeInitialize(void)
|
||||
{
|
||||
SCRadix4Tree t = SC_RADIX4_TREE_INITIALIZER;
|
||||
return t;
|
||||
}
|
||||
|
||||
void SCRadix4TreeRelease(SCRadix4Tree *tree, const SCRadix4Config *config)
|
||||
{
|
||||
TreeRelease(tree, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adds a new IPV4 address to the Radix4 tree
|
||||
*
|
||||
* \param key_stream Data that has to be added to the Radix4 tree. In this case
|
||||
* a pointer to an IPV4 address
|
||||
* \param tree Pointer to the Radix4 tree
|
||||
* \param user Pointer to the user data that has to be associated with the
|
||||
* key
|
||||
*
|
||||
* \retval node Pointer to the newly created node
|
||||
*/
|
||||
SCRadix4Node *SCRadix4AddKeyIPV4(
|
||||
SCRadix4Tree *tree, const SCRadix4Config *config, const uint8_t *key_stream, void *user)
|
||||
{
|
||||
return AddKey(tree, config, key_stream, 32, user, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adds a new IPV4 netblock to the Radix4 tree
|
||||
*
|
||||
* \param key_stream Data that has to be added to the Radix4 tree. In this case
|
||||
* a pointer to an IPV4 netblock
|
||||
* \param tree Pointer to the Radix4 tree
|
||||
* \param user Pointer to the user data that has to be associated with the
|
||||
* key
|
||||
* \param netmask The netmask (cidr) if we are adding a netblock
|
||||
*
|
||||
* \retval node Pointer to the newly created node
|
||||
*/
|
||||
SCRadix4Node *SCRadix4AddKeyIPV4Netblock(SCRadix4Tree *tree, const SCRadix4Config *config,
|
||||
const uint8_t *key_stream, uint8_t netmask, void *user)
|
||||
{
|
||||
return AddKey(tree, config, key_stream, netmask, user, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adds a new IPV4/netblock to the Radix4 tree from a string
|
||||
*
|
||||
* \param str IPV4 string with optional /cidr netmask
|
||||
* \param tree Pointer to the Radix4 tree
|
||||
* \param user Pointer to the user data that has to be associated with
|
||||
* the key
|
||||
*
|
||||
* \retval bool true if node was added, false otherwise
|
||||
*
|
||||
* If the function returns false, `sc_errno` is set:
|
||||
* - SC_EEXIST: Node already exists
|
||||
* - SC_EINVAL: Parameter value error
|
||||
* - SC_ENOMEM: Memory allocation failed
|
||||
*/
|
||||
bool SCRadix4AddKeyIPV4String(
|
||||
SCRadix4Tree *tree, const SCRadix4Config *config, const char *str, void *user)
|
||||
{
|
||||
uint32_t ip;
|
||||
uint8_t netmask = 32;
|
||||
char ip_str[32]; /* Max length for full ipv4/mask string with NUL */
|
||||
char *mask_str = NULL;
|
||||
struct in_addr addr;
|
||||
|
||||
/* Make a copy of the string so it can be modified */
|
||||
strlcpy(ip_str, str, sizeof(ip_str) - 2);
|
||||
*(ip_str + (sizeof(ip_str) - 1)) = '\0';
|
||||
|
||||
/* Does it have a mask? */
|
||||
if (NULL != (mask_str = strchr(ip_str, '/'))) {
|
||||
*(mask_str++) = '\0';
|
||||
|
||||
/* Dotted type netmask not supported */
|
||||
if (strchr(mask_str, '.') != NULL) {
|
||||
sc_errno = SC_EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t cidr;
|
||||
if (StringParseU8RangeCheck(&cidr, 10, 0, (const char *)mask_str, 0, 32) < 0) {
|
||||
sc_errno = SC_EINVAL;
|
||||
return false;
|
||||
}
|
||||
netmask = (uint8_t)cidr;
|
||||
}
|
||||
|
||||
/* Validate the IP */
|
||||
if (inet_pton(AF_INET, ip_str, &addr) <= 0) {
|
||||
sc_errno = SC_EINVAL;
|
||||
return false;
|
||||
}
|
||||
ip = addr.s_addr;
|
||||
|
||||
if (AddKey(tree, config, (uint8_t *)&ip, netmask, user, true) == NULL) {
|
||||
DEBUG_VALIDATE_BUG_ON(sc_errno == SC_OK);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Removes an IPV4 address key(not a netblock) from the Radix4 tree.
|
||||
* Instead of using this function, we can also used
|
||||
* SCRadix4RemoveKeyIPV4Netblock(), by supplying a netmask value of 32.
|
||||
*
|
||||
* \param key_stream Data that has to be removed from the Radix4 tree. In this
|
||||
* case an IPV4 address
|
||||
* \param tree Pointer to the Radix4 tree from which the key has to be
|
||||
* removed
|
||||
*/
|
||||
void SCRadix4RemoveKeyIPV4(
|
||||
SCRadix4Tree *tree, const SCRadix4Config *config, const uint8_t *key_stream)
|
||||
{
|
||||
RemoveKey(tree, config, key_stream, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Removes an IPV4 address netblock key from the Radix4 tree.
|
||||
*
|
||||
* \param key_stream Data that has to be removed from the Radix4 tree. In this
|
||||
* case an IPV4 address
|
||||
* \param tree Pointer to the Radix4 tree from which the key has to be
|
||||
* removed
|
||||
*/
|
||||
void SCRadix4RemoveKeyIPV4Netblock(SCRadix4Tree *tree, const SCRadix4Config *config,
|
||||
const uint8_t *key_stream, uint8_t netmask)
|
||||
{
|
||||
SCLogNotice("removing with netmask %u", netmask);
|
||||
RemoveKey(tree, config, key_stream, netmask);
|
||||
}
|
||||
|
||||
void SCRadix4PrintTree(SCRadix4Tree *tree, const SCRadix4Config *config)
|
||||
{
|
||||
PrintTree(tree, config);
|
||||
}
|
||||
|
||||
static void PrintUserdata(SCRadix4Node *node, int level, void (*PrintData)(void *))
|
||||
{
|
||||
if (PrintData != NULL) {
|
||||
RadixUserData *ud = node->user_data;
|
||||
while (ud != NULL) {
|
||||
for (int i = 0; i < level; i++)
|
||||
printf(" ");
|
||||
printf("[%d], ", ud->netmask);
|
||||
PrintData(ud->user);
|
||||
ud = ud->next;
|
||||
}
|
||||
} else {
|
||||
RadixUserData *ud = node->user_data;
|
||||
while (ud != NULL) {
|
||||
for (int i = 0; i < level; i++)
|
||||
printf(" ");
|
||||
printf(" [%d], ", ud->netmask);
|
||||
ud = ud->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int SCRadix4ForEachNodeSub(
|
||||
const SCRadix4Node *node, SCRadix4ForEachNodeFunc Callback, void *data)
|
||||
{
|
||||
BUG_ON(!node);
|
||||
|
||||
/* invoke callback for each stored user data */
|
||||
for (RadixUserData *ud = node->user_data; ud != NULL; ud = ud->next) {
|
||||
if (Callback(node, ud->user, ud->netmask, data) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->left) {
|
||||
if (SCRadix4ForEachNodeSub(node->left, Callback, data) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (node->right) {
|
||||
if (SCRadix4ForEachNodeSub(node->right, Callback, data) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SCRadix4ForEachNode(const SCRadix4Tree *tree, SCRadix4ForEachNodeFunc Callback, void *data)
|
||||
{
|
||||
if (tree->head == NULL)
|
||||
return 0;
|
||||
return SCRadix4ForEachNodeSub(tree->head, Callback, data);
|
||||
}
|
||||
|
||||
bool SCRadix4CompareTrees(
|
||||
const SCRadix4Tree *t1, const SCRadix4Tree *t2, SCRadix4TreeCompareFunc Callback)
|
||||
{
|
||||
return CompareTrees(t1, t2, Callback);
|
||||
}
|
||||
|
||||
/*------------------------------------Unit_Tests------------------------------*/
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
static const SCRadix4Config ut_ip_radix4_config = { NULL, NULL };
|
||||
|
||||
#define GET_IPV4(str) \
|
||||
SCLogDebug("setting up %s", (str)); \
|
||||
memset(&(sa), 0, sizeof((sa))); \
|
||||
FAIL_IF(inet_pton(AF_INET, (str), &(sa).sin_addr) <= 0);
|
||||
|
||||
#define ADD_IPV4(str) \
|
||||
GET_IPV4((str)); \
|
||||
SCRadix4AddKeyIPV4(&tree, &ut_ip_radix4_config, (uint8_t *)&(sa).sin_addr, NULL);
|
||||
|
||||
#define REM_IPV4(str) \
|
||||
GET_IPV4((str)); \
|
||||
SCRadix4RemoveKeyIPV4(&tree, &ut_ip_radix4_config, (uint8_t *)&(sa).sin_addr);
|
||||
|
||||
#define ADD_IPV4_MASK(str, cidr) \
|
||||
GET_IPV4((str)); \
|
||||
SCRadix4AddKeyIPV4Netblock( \
|
||||
&tree, &ut_ip_radix4_config, (uint8_t *)&(sa).sin_addr, (cidr), NULL);
|
||||
|
||||
#define REM_IPV4_MASK(str, cidr) \
|
||||
GET_IPV4((str)); \
|
||||
SCRadix4RemoveKeyIPV4Netblock(&tree, &ut_ip_radix4_config, (uint8_t *)&(sa).sin_addr, (cidr));
|
||||
|
||||
static int SCRadix4TestIPV4Insertion03(void)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
|
||||
ADD_IPV4("192.168.1.1");
|
||||
ADD_IPV4("192.168.1.2");
|
||||
ADD_IPV4("192.167.1.3");
|
||||
ADD_IPV4("192.167.1.4");
|
||||
ADD_IPV4("192.167.1.4");
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV4("192.168.1.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV4("192.167.1.4");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
/* continue adding keys */
|
||||
ADD_IPV4("220.168.1.2");
|
||||
ADD_IPV4("192.168.1.5");
|
||||
ADD_IPV4("192.168.1.18");
|
||||
|
||||
/* test the existence of keys */
|
||||
GET_IPV4("192.168.1.3");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("127.234.2.62");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV4("192.168.1.1");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
GET_IPV4("192.168.1.5");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
GET_IPV4("192.168.1.2");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV4("192.167.1.3");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
GET_IPV4("192.167.1.4");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
GET_IPV4("220.168.1.2");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
GET_IPV4("192.168.1.18");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix4TestIPV4Removal04(void)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV4("192.168.1.1");
|
||||
ADD_IPV4("192.168.1.2");
|
||||
ADD_IPV4("192.167.1.3");
|
||||
ADD_IPV4("192.167.1.4");
|
||||
ADD_IPV4("220.168.1.2");
|
||||
ADD_IPV4("192.168.1.5");
|
||||
ADD_IPV4("192.168.1.18");
|
||||
|
||||
/* remove the keys from the tree */
|
||||
REM_IPV4("192.168.1.1");
|
||||
REM_IPV4("192.167.1.3");
|
||||
REM_IPV4("192.167.1.4");
|
||||
REM_IPV4("192.168.1.18");
|
||||
|
||||
GET_IPV4("192.167.1.1");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("192.168.1.2");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
REM_IPV4("192.167.1.3");
|
||||
REM_IPV4("220.168.1.2");
|
||||
|
||||
GET_IPV4("192.168.1.5");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
GET_IPV4("192.168.1.2");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
REM_IPV4("192.168.1.2");
|
||||
REM_IPV4("192.168.1.5");
|
||||
|
||||
FAIL_IF_NOT_NULL(tree.head);
|
||||
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix4TestIPV4NetblockInsertion09(void)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV4("192.168.1.1");
|
||||
ADD_IPV4("192.168.1.2");
|
||||
ADD_IPV4("192.167.1.3");
|
||||
ADD_IPV4("192.167.1.4");
|
||||
ADD_IPV4("220.168.1.2");
|
||||
ADD_IPV4("192.168.1.5");
|
||||
ADD_IPV4("192.168.1.18");
|
||||
|
||||
ADD_IPV4_MASK("192.168.0.0", 16);
|
||||
ADD_IPV4_MASK("192.171.128.0", 24);
|
||||
ADD_IPV4_MASK("192.171.192.0", 18);
|
||||
ADD_IPV4_MASK("192.175.0.0", 16);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV4("192.168.1.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("192.170.1.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("192.171.128.145");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
GET_IPV4("192.171.64.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("192.171.191.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("192.171.224.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
GET_IPV4("192.171.224.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("192.175.224.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix4TestIPV4NetblockInsertion10(void)
|
||||
{
|
||||
SCRadix4Node *node[2];
|
||||
struct sockaddr_in sa;
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV4_MASK("253.192.0.0", 16);
|
||||
ADD_IPV4_MASK("253.192.235.0", 24);
|
||||
ADD_IPV4_MASK("192.167.0.0", 16);
|
||||
ADD_IPV4("192.167.1.4");
|
||||
ADD_IPV4_MASK("220.168.0.0", 16);
|
||||
ADD_IPV4("253.224.1.5");
|
||||
ADD_IPV4_MASK("192.168.0.0", 16);
|
||||
|
||||
GET_IPV4("192.171.128.0");
|
||||
node[0] = SCRadix4AddKeyIPV4Netblock(
|
||||
&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, 24, NULL);
|
||||
|
||||
GET_IPV4("192.171.128.45");
|
||||
node[1] = SCRadix4AddKeyIPV4(&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, NULL);
|
||||
|
||||
ADD_IPV4_MASK("192.171.0.0", 18);
|
||||
ADD_IPV4_MASK("192.175.0.0", 16);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV4("192.171.128.53");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node[0]);
|
||||
|
||||
GET_IPV4("192.171.128.45");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node[1]);
|
||||
|
||||
GET_IPV4("192.171.128.45");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node[1]);
|
||||
|
||||
GET_IPV4("192.171.128.78");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node[0]);
|
||||
|
||||
REM_IPV4_MASK("192.171.128.0", 24);
|
||||
|
||||
GET_IPV4("192.171.128.78");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("192.171.127.78");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix4TestIPV4NetblockInsertion11(void)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV4_MASK("253.192.0.0", 16);
|
||||
ADD_IPV4_MASK("253.192.235.0", 24);
|
||||
ADD_IPV4_MASK("192.167.0.0", 16);
|
||||
ADD_IPV4("192.167.1.4");
|
||||
ADD_IPV4_MASK("220.168.0.0", 16);
|
||||
ADD_IPV4("253.224.1.5");
|
||||
ADD_IPV4_MASK("192.168.0.0", 16);
|
||||
ADD_IPV4_MASK("192.171.128.0", 24);
|
||||
ADD_IPV4("192.171.128.45");
|
||||
ADD_IPV4_MASK("192.171.0.0", 18);
|
||||
ADD_IPV4_MASK("192.175.0.0", 16);
|
||||
|
||||
GET_IPV4("0.0.0.0");
|
||||
SCRadix4Node *node = SCRadix4AddKeyIPV4Netblock(
|
||||
&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, 0, NULL);
|
||||
FAIL_IF_NULL(node);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV4("192.171.128.53");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV4("192.171.128.45");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV4("192.171.128.78");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV4("192.171.127.78");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node);
|
||||
|
||||
GET_IPV4("1.1.1.1");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node);
|
||||
|
||||
GET_IPV4("192.255.254.25");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node);
|
||||
|
||||
GET_IPV4("169.255.254.25");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node);
|
||||
|
||||
GET_IPV4("0.0.0.0");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node);
|
||||
|
||||
GET_IPV4("253.224.1.5");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != node);
|
||||
|
||||
GET_IPV4("245.63.62.121");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node);
|
||||
|
||||
GET_IPV4("253.224.1.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node);
|
||||
|
||||
/* remove node 0.0.0.0 */
|
||||
REM_IPV4_MASK("0.0.0.0", 0);
|
||||
|
||||
GET_IPV4("253.224.1.6");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("192.171.127.78");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("1.1.1.1");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV4("192.255.254.25");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
GET_IPV4("169.255.254.25");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV4("0.0.0.0");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix4TestIPV4NetblockInsertion12(void)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
SCRadix4Node *node[2];
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV4_MASK("253.192.0.0", 16);
|
||||
ADD_IPV4_MASK("253.192.235.0", 24);
|
||||
ADD_IPV4_MASK("192.167.0.0", 16);
|
||||
ADD_IPV4("192.167.1.4");
|
||||
ADD_IPV4_MASK("220.168.0.0", 16);
|
||||
ADD_IPV4("253.224.1.5");
|
||||
ADD_IPV4_MASK("192.168.0.0", 16);
|
||||
|
||||
GET_IPV4("192.171.128.0");
|
||||
node[0] = SCRadix4AddKeyIPV4Netblock(
|
||||
&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, 24, NULL);
|
||||
FAIL_IF_NULL(node[0]);
|
||||
|
||||
GET_IPV4("192.171.128.45");
|
||||
node[1] = SCRadix4AddKeyIPV4(&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, NULL);
|
||||
FAIL_IF_NULL(node[1]);
|
||||
|
||||
ADD_IPV4_MASK("192.171.0.0", 18);
|
||||
ADD_IPV4_MASK("225.175.21.228", 32);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV4("192.171.128.53");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node[0]);
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV4("192.171.128.45");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node[1]);
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node[1]);
|
||||
|
||||
GET_IPV4("192.171.128.78");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == node[0]);
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV4("225.175.21.228");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV4("225.175.21.224");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV4("225.175.21.229");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV4("225.175.21.230");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindExactMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) == NULL);
|
||||
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check that the best match search works for all the
|
||||
* possible netblocks of a fixed address
|
||||
*/
|
||||
static int SCRadix4TestIPV4NetBlocksAndBestSearch16(void)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
|
||||
GET_IPV4("192.168.1.1");
|
||||
|
||||
for (uint32_t i = 0; i <= 32; i++) {
|
||||
uint32_t *user = SCMalloc(sizeof(uint32_t));
|
||||
FAIL_IF_NULL(user);
|
||||
*user = i;
|
||||
SCRadix4AddKeyIPV4Netblock(&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, i, user);
|
||||
void *user_data = NULL;
|
||||
SCRadix4Node *node = SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != i);
|
||||
}
|
||||
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check special combinations of netblocks and addresses
|
||||
* on best search checking the returned userdata
|
||||
*/
|
||||
static int SCRadix4TestIPV4NetBlocksAndBestSearch19(void)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
void *user_data = NULL;
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
|
||||
GET_IPV4("0.0.0.0");
|
||||
uint32_t *user = SCMalloc(sizeof(uint32_t));
|
||||
FAIL_IF_NULL(user);
|
||||
*user = 100;
|
||||
SCRadix4AddKeyIPV4Netblock(&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, 0, user);
|
||||
|
||||
GET_IPV4("192.168.1.15");
|
||||
SCRadix4Node *node = SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 100);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV4("177.0.0.0");
|
||||
user = SCMalloc(sizeof(uint32_t));
|
||||
FAIL_IF_NULL(user);
|
||||
*user = 200;
|
||||
SCRadix4AddKeyIPV4Netblock(&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, 8, user);
|
||||
|
||||
GET_IPV4("177.168.1.15");
|
||||
node = SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 200);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV4("178.168.1.15");
|
||||
node = SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 100);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV4("177.168.0.0");
|
||||
user = SCMalloc(sizeof(uint32_t));
|
||||
FAIL_IF_NULL(user);
|
||||
*user = 300;
|
||||
SCRadix4AddKeyIPV4Netblock(&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, 12, user);
|
||||
|
||||
GET_IPV4("177.168.1.15");
|
||||
node = SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 300);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV4("177.167.1.15");
|
||||
node = SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 300);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV4("177.178.1.15");
|
||||
node = SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 200);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV4("197.178.1.15");
|
||||
node = SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 100);
|
||||
user_data = NULL;
|
||||
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test SCRadix4TestIPV4NetblockInsertion15 insert a node searching on it.
|
||||
* Should always return true but the purposse of the test is to monitor
|
||||
* the memory usage to detect memleaks (there was one on searching)
|
||||
*/
|
||||
static int SCRadix4TestIPV4NetblockInsertion25(void)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
ADD_IPV4_MASK("192.168.0.0", 16);
|
||||
GET_IPV4("192.168.128.53");
|
||||
FAIL_IF_NOT(SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, NULL) != NULL);
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test SCRadix4TestIPV4NetblockInsertion26 insert a node searching on it.
|
||||
* Should always return true but the purposse of the test is to monitor
|
||||
* the memory usage to detect memleaks (there was one on searching)
|
||||
*/
|
||||
static int SCRadix4TestIPV4NetblockInsertion26(void)
|
||||
{
|
||||
SCRadix4Node *tmp = NULL;
|
||||
struct sockaddr_in sa;
|
||||
char *str = SCStrdup("Hello1");
|
||||
FAIL_IF_NULL(str);
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
GET_IPV4("0.0.0.0");
|
||||
SCRadix4AddKeyIPV4Netblock(&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, 0, str);
|
||||
str = SCStrdup("Hello2");
|
||||
FAIL_IF_NULL(str);
|
||||
GET_IPV4("176.0.0.1");
|
||||
SCRadix4AddKeyIPV4Netblock(&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, 5, str);
|
||||
str = SCStrdup("Hello3");
|
||||
FAIL_IF_NULL(str);
|
||||
GET_IPV4("0.0.0.0");
|
||||
SCRadix4AddKeyIPV4Netblock(&tree, &ut_ip_radix4_config, (uint8_t *)&sa.sin_addr, 7, str);
|
||||
/* test for the existance of a key */
|
||||
void *retptr = NULL;
|
||||
tmp = SCRadix4TreeFindBestMatch(&tree, (uint8_t *)&sa.sin_addr, &retptr);
|
||||
FAIL_IF_NULL(tmp);
|
||||
FAIL_IF_NULL(retptr);
|
||||
FAIL_IF_NOT(strcmp((char *)retptr, "Hello3") == 0);
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix4TestIPV4InsertRemove01(void)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
|
||||
SCRadix4Tree tree = SCRadix4TreeInitialize();
|
||||
ADD_IPV4_MASK("1.0.0.0", 8);
|
||||
ADD_IPV4_MASK("1.1.1.0", 24);
|
||||
ADD_IPV4("1.1.1.1");
|
||||
FAIL_IF(tree.head == NULL);
|
||||
FAIL_IF_NOT(tree.head->bit == 15);
|
||||
FAIL_IF_NULL(tree.head->left);
|
||||
FAIL_IF_NOT(tree.head->left->masks == 0);
|
||||
FAIL_IF_NOT(tree.head->left->bit == 32);
|
||||
FAIL_IF_NULL(tree.head->right);
|
||||
FAIL_IF_NOT(tree.head->right->masks == BIT_U64(24));
|
||||
FAIL_IF_NOT(tree.head->right->bit == 31);
|
||||
SCRadix4PrintTree(&tree, &ut_ip_radix4_config);
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
|
||||
/* tree after adds/removals */
|
||||
tree = SCRadix4TreeInitialize();
|
||||
ADD_IPV4_MASK("1.0.0.0", 8);
|
||||
ADD_IPV4_MASK("1.0.0.0", 10);
|
||||
ADD_IPV4_MASK("1.0.0.0", 12);
|
||||
ADD_IPV4_MASK("1.1.0.0", 16);
|
||||
ADD_IPV4_MASK("1.1.0.0", 18);
|
||||
ADD_IPV4_MASK("1.1.0.0", 20);
|
||||
ADD_IPV4_MASK("1.1.1.0", 24);
|
||||
ADD_IPV4("1.1.1.1");
|
||||
REM_IPV4_MASK("1.1.0.0", 20);
|
||||
REM_IPV4_MASK("1.1.0.0", 18);
|
||||
REM_IPV4_MASK("1.1.0.0", 16);
|
||||
REM_IPV4_MASK("1.0.0.0", 12);
|
||||
REM_IPV4_MASK("1.0.0.0", 10);
|
||||
FAIL_IF(tree.head == NULL);
|
||||
FAIL_IF_NOT(tree.head->bit == 15);
|
||||
FAIL_IF_NULL(tree.head->left);
|
||||
FAIL_IF_NOT(tree.head->left->masks == 0);
|
||||
FAIL_IF_NOT(tree.head->left->bit == 32);
|
||||
FAIL_IF_NULL(tree.head->right);
|
||||
FAIL_IF_NOT(tree.head->right->masks == BIT_U64(24));
|
||||
FAIL_IF_NOT(tree.head->right->bit == 31);
|
||||
SCRadix4PrintTree(&tree, &ut_ip_radix4_config);
|
||||
SCRadix4TreeRelease(&tree, &ut_ip_radix4_config);
|
||||
|
||||
PASS;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SCRadix4RegisterTests(void)
|
||||
{
|
||||
#ifdef UNITTESTS
|
||||
UtRegisterTest("SCRadix4TestIPV4Insertion03", SCRadix4TestIPV4Insertion03);
|
||||
UtRegisterTest("SCRadix4TestIPV4Removal04", SCRadix4TestIPV4Removal04);
|
||||
UtRegisterTest("SCRadix4TestIPV4NetblockInsertion09", SCRadix4TestIPV4NetblockInsertion09);
|
||||
UtRegisterTest("SCRadix4TestIPV4NetblockInsertion10", SCRadix4TestIPV4NetblockInsertion10);
|
||||
UtRegisterTest("SCRadix4TestIPV4NetblockInsertion11", SCRadix4TestIPV4NetblockInsertion11);
|
||||
UtRegisterTest("SCRadix4TestIPV4NetblockInsertion12", SCRadix4TestIPV4NetblockInsertion12);
|
||||
UtRegisterTest(
|
||||
"SCRadix4TestIPV4NetBlocksAndBestSearch16", SCRadix4TestIPV4NetBlocksAndBestSearch16);
|
||||
UtRegisterTest(
|
||||
"SCRadix4TestIPV4NetBlocksAndBestSearch19", SCRadix4TestIPV4NetBlocksAndBestSearch19);
|
||||
UtRegisterTest("SCRadix4TestIPV4NetblockInsertion25", SCRadix4TestIPV4NetblockInsertion25);
|
||||
UtRegisterTest("SCRadix4TestIPV4NetblockInsertion26", SCRadix4TestIPV4NetblockInsertion26);
|
||||
UtRegisterTest("SCRadix4TestIPV4InsertRemove01", SCRadix4TestIPV4InsertRemove01);
|
||||
#endif
|
||||
return;
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/* 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>
|
||||
* Based on util-radix-tree.[ch] by:
|
||||
* \author Anoop Saldanha <anoopsaldanha@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef SURICATA_UTIL_RADIX4_TREE_H
|
||||
#define SURICATA_UTIL_RADIX4_TREE_H
|
||||
|
||||
#include "suricata-common.h"
|
||||
|
||||
struct RadixUserData;
|
||||
|
||||
/**
|
||||
* \brief Structure for the node in the radix tree
|
||||
*/
|
||||
typedef struct SCRadix4Node_ {
|
||||
/** holds bitmap of netmasks that come under this node in the tree */
|
||||
uint64_t masks : 33;
|
||||
uint64_t pad1 : 31;
|
||||
|
||||
/** the bit position where the bits differ in the nodes children. Used
|
||||
* to determine the path to be taken during a lookup */
|
||||
uint8_t bit;
|
||||
|
||||
/** bool to see if prefix_stream is filled */
|
||||
bool has_prefix;
|
||||
|
||||
/** the key that has been stored in the tree */
|
||||
uint8_t prefix_stream[4];
|
||||
|
||||
/** User data that is associated with this key. We need a user data field
|
||||
* for each netblock value possible since one ip can be associated
|
||||
* with any of the 32 netblocks. */
|
||||
struct RadixUserData *user_data;
|
||||
|
||||
/** the left and the right children of a node */
|
||||
struct SCRadix4Node_ *left, *right;
|
||||
|
||||
/** the parent node for this tree */
|
||||
struct SCRadix4Node_ *parent;
|
||||
} SCRadix4Node;
|
||||
|
||||
/**
|
||||
* \brief Structure for the radix tree
|
||||
*/
|
||||
typedef struct SCRadix4Tree_ {
|
||||
/** the root node in the radix tree */
|
||||
SCRadix4Node *head;
|
||||
} SCRadix4Tree;
|
||||
|
||||
typedef struct SCRadix4Config_ {
|
||||
void (*Free)(void *);
|
||||
/** function pointer that is supplied by the user to free the user data
|
||||
* held by the user field of SCRadix4Node */
|
||||
void (*PrintData)(void *); // debug only?
|
||||
} SCRadix4Config;
|
||||
|
||||
#define SC_RADIX4_TREE_INITIALIZER \
|
||||
{ \
|
||||
.head = NULL \
|
||||
}
|
||||
|
||||
SCRadix4Tree SCRadix4TreeInitialize(void);
|
||||
void SCRadix4TreeRelease(SCRadix4Tree *, const SCRadix4Config *);
|
||||
|
||||
SCRadix4Node *SCRadix4AddKeyIPV4(SCRadix4Tree *, const SCRadix4Config *, const uint8_t *, void *);
|
||||
SCRadix4Node *SCRadix4AddKeyIPV4Netblock(
|
||||
SCRadix4Tree *, const SCRadix4Config *, const uint8_t *, uint8_t, void *);
|
||||
bool SCRadix4AddKeyIPV4String(SCRadix4Tree *, const SCRadix4Config *, const char *, void *);
|
||||
|
||||
void SCRadix4RemoveKeyIPV4Netblock(
|
||||
SCRadix4Tree *, const SCRadix4Config *, const uint8_t *, uint8_t);
|
||||
void SCRadix4RemoveKeyIPV4(SCRadix4Tree *, const SCRadix4Config *, const uint8_t *);
|
||||
|
||||
SCRadix4Node *SCRadix4TreeFindExactMatch(const SCRadix4Tree *, const uint8_t *, void **);
|
||||
SCRadix4Node *SCRadix4TreeFindNetblock(
|
||||
const SCRadix4Tree *, const uint8_t *, const uint8_t, void **);
|
||||
SCRadix4Node *SCRadix4TreeFindBestMatch(const SCRadix4Tree *, const uint8_t *, void **);
|
||||
SCRadix4Node *SCRadix4TreeFindBestMatch2(const SCRadix4Tree *, const uint8_t *, void **, uint8_t *);
|
||||
|
||||
void SCRadix4PrintTree(SCRadix4Tree *, const SCRadix4Config *config);
|
||||
void SCRadix4PrintNodeInfo(SCRadix4Node *, int, void (*PrintData)(void *));
|
||||
|
||||
void SCRadix4RegisterTests(void);
|
||||
|
||||
typedef int (*SCRadix4ForEachNodeFunc)(
|
||||
const SCRadix4Node *node, void *user_data, const uint8_t netmask, void *data);
|
||||
|
||||
int SCRadix4ForEachNode(const SCRadix4Tree *tree, SCRadix4ForEachNodeFunc Callback, void *data);
|
||||
|
||||
/** \brief compare content of 2 user data entries
|
||||
* \retval true equal
|
||||
* \retval false not equal
|
||||
*/
|
||||
typedef bool (*SCRadix4TreeCompareFunc)(const void *ud1, const void *ud2);
|
||||
bool SCRadix4CompareTrees(
|
||||
const SCRadix4Tree *t1, const SCRadix4Tree *t2, SCRadix4TreeCompareFunc Callback);
|
||||
|
||||
#endif /* SURICATA_UTIL_RADIX4_TREE_H */
|
@ -0,0 +1,963 @@
|
||||
/* 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 "suricata-common.h"
|
||||
#include "util-debug.h"
|
||||
#include "util-error.h"
|
||||
#include "util-ip.h"
|
||||
#include "util-cidr.h"
|
||||
#include "util-unittest.h"
|
||||
#include "util-memcmp.h"
|
||||
#include "util-print.h"
|
||||
#include "util-byte.h"
|
||||
#include "util-radix6-tree.h"
|
||||
|
||||
#define ADDRESS_BYTES (uint8_t)16
|
||||
#define NETMASK_MAX (uint8_t)128
|
||||
|
||||
#define RADIX_TREE_TYPE SCRadix6Tree
|
||||
#define RADIX_NODE_TYPE SCRadix6Node
|
||||
#define RADIX_TREE_COMPARE_CALLBACK SCRadix6TreeCompareFunc
|
||||
#define RADIX_CONFIG_TYPE SCRadix6Config
|
||||
|
||||
static void PrintUserdata(SCRadix6Node *node, void (*PrintData)(void *));
|
||||
|
||||
static inline void AddNetmaskToMasks(SCRadix6Node *node, int netmask)
|
||||
{
|
||||
uint8_t *masks = node->masks;
|
||||
masks[netmask / 8] |= 1 << (netmask % 8);
|
||||
}
|
||||
|
||||
static inline void RemoveNetmaskFromMasks(SCRadix6Node *node, int netmask)
|
||||
{
|
||||
uint8_t *masks = node->masks;
|
||||
masks[netmask / 8] &= ~(1 << (netmask % 8));
|
||||
}
|
||||
|
||||
static inline void AddNetmasksFromNode(SCRadix6Node *dst, SCRadix6Node *src)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(src->masks); i++) {
|
||||
dst->masks[i] |= src->masks[i];
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool NetmasksEmpty(const SCRadix6Node *node)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(node->masks); i++) {
|
||||
if (node->masks[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool NetmaskEqualsMask(const SCRadix6Node *node, int netmask)
|
||||
{
|
||||
size_t b = netmask / 8;
|
||||
|
||||
for (size_t i = 0; i < sizeof(node->masks); i++) {
|
||||
if (i != b && node->masks[i] != 0)
|
||||
return false;
|
||||
else if (node->masks[i] != (1 << (netmask % 8)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool NetmaskIssetInMasks(const SCRadix6Node *node, int netmask)
|
||||
{
|
||||
return ((node->masks[netmask / 8] & 1 << (netmask % 8)) != 0);
|
||||
}
|
||||
|
||||
static inline void ProcessInternode(SCRadix6Node *node, SCRadix6Node *inter_node)
|
||||
{
|
||||
const int differ_bit = inter_node->bit;
|
||||
uint8_t rem[sizeof(node->masks)];
|
||||
memset(rem, 0, sizeof(rem));
|
||||
|
||||
for (int x = 0; x <= NETMASK_MAX; x++) {
|
||||
int m = NETMASK_MAX - x;
|
||||
if (m == differ_bit)
|
||||
break;
|
||||
else {
|
||||
if (NetmaskIssetInMasks(node, m))
|
||||
rem[m / 8] |= 1 << (m % 8);
|
||||
}
|
||||
}
|
||||
|
||||
AddNetmasksFromNode(inter_node, node);
|
||||
|
||||
for (size_t i = 0; i < sizeof(inter_node->masks); i++) {
|
||||
inter_node->masks[i] &= ~rem[i];
|
||||
}
|
||||
|
||||
memcpy(node->masks, rem, sizeof(node->masks));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Prints the node information from a Radix6 tree
|
||||
*
|
||||
* \param node Pointer to the Radix6 node whose information has to be printed
|
||||
* \param level Used for indentation purposes
|
||||
*/
|
||||
static void PrintNodeInfo(SCRadix6Node *node, int level, void (*PrintData)(void *))
|
||||
{
|
||||
if (node == NULL)
|
||||
return;
|
||||
for (int i = 0; i < level; i++)
|
||||
printf(" ");
|
||||
|
||||
printf("%d [", node->bit);
|
||||
|
||||
if (NetmasksEmpty(node)) {
|
||||
printf(" - ");
|
||||
} else {
|
||||
for (int i = 0, x = 0; i <= NETMASK_MAX; i++) {
|
||||
if (NetmaskIssetInMasks(node, i)) {
|
||||
printf("%s%d", x ? ", " : "", i);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("] (");
|
||||
|
||||
if (node->has_prefix) {
|
||||
char addr[46] = "";
|
||||
PrintInet(AF_INET6, &node->prefix_stream, addr, sizeof(addr));
|
||||
printf("%s)\t%p", addr, node);
|
||||
PrintUserdata(node, PrintData);
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("no prefix) %p\n", node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#include "util-radix-tree-common.h"
|
||||
|
||||
SCRadix6Node *SCRadix6TreeFindExactMatch(
|
||||
const SCRadix6Tree *tree, const uint8_t *key, void **user_data)
|
||||
{
|
||||
return FindExactMatch(tree, key, user_data);
|
||||
}
|
||||
|
||||
SCRadix6Node *SCRadix6TreeFindNetblock(
|
||||
const SCRadix6Tree *tree, const uint8_t *key, const uint8_t netmask, void **user_data)
|
||||
{
|
||||
return FindNetblock(tree, key, netmask, user_data);
|
||||
}
|
||||
|
||||
SCRadix6Node *SCRadix6TreeFindBestMatch(
|
||||
const SCRadix6Tree *tree, const uint8_t *key, void **user_data)
|
||||
{
|
||||
return FindBestMatch(tree, key, user_data);
|
||||
}
|
||||
|
||||
SCRadix6Node *SCRadix6TreeFindBestMatch2(
|
||||
const SCRadix6Tree *tree, const uint8_t *key, void **user_data, uint8_t *out_netmask)
|
||||
{
|
||||
return FindBestMatch2(tree, key, user_data, out_netmask);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adds a new IPV6 address to the Radix6 tree
|
||||
*
|
||||
* \param key_stream Data that has to be added to the Radix6 tree. In this case
|
||||
* a pointer to an IPV6 address
|
||||
* \param tree Pointer to the Radix6 tree
|
||||
* \param user Pointer to the user data that has to be associated with the
|
||||
* key
|
||||
*
|
||||
* \retval node Pointer to the newly created node
|
||||
*/
|
||||
SCRadix6Node *SCRadix6AddKeyIPV6(
|
||||
SCRadix6Tree *tree, const SCRadix6Config *config, const uint8_t *key_stream, void *user)
|
||||
{
|
||||
return AddKey(tree, config, key_stream, 128, user, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adds a new IPV6 netblock to the Radix6 tree
|
||||
*
|
||||
* \param key_stream Data that has to be added to the Radix6 tree. In this case
|
||||
* a pointer to an IPV6 netblock
|
||||
* \param tree Pointer to the Radix6 tree
|
||||
* \param user Pointer to the user data that has to be associated with the
|
||||
* key
|
||||
* \param netmask The netmask (cidr) if we are adding a netblock
|
||||
*
|
||||
* \retval node Pointer to the newly created node
|
||||
*/
|
||||
SCRadix6Node *SCRadix6AddKeyIPV6Netblock(SCRadix6Tree *tree, const SCRadix6Config *config,
|
||||
const uint8_t *key_stream, uint8_t netmask, void *user)
|
||||
{
|
||||
return AddKey(tree, config, key_stream, netmask, user, false);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
|
||||
static void SCRadix6ValidateIPv6Key(uint8_t *key, const uint8_t netmask)
|
||||
{
|
||||
uint32_t address[4];
|
||||
memcpy(&address, key, sizeof(address));
|
||||
|
||||
uint32_t mask[4];
|
||||
memset(&mask, 0, sizeof(mask));
|
||||
struct in6_addr mask6;
|
||||
CIDRGetIPv6(netmask, &mask6);
|
||||
memcpy(&mask, &mask6.s6_addr, sizeof(mask));
|
||||
|
||||
uint32_t masked[4];
|
||||
masked[0] = address[0] & mask[0];
|
||||
masked[1] = address[1] & mask[1];
|
||||
masked[2] = address[2] & mask[2];
|
||||
masked[3] = address[3] & mask[3];
|
||||
|
||||
if (memcmp(masked, address, sizeof(masked)) != 0) {
|
||||
char ostr[64], nstr[64];
|
||||
PrintInet(AF_INET6, (void *)&address, ostr, sizeof(ostr));
|
||||
PrintInet(AF_INET6, (void *)&masked, nstr, sizeof(nstr));
|
||||
SCLogNotice("input %s/%u != expected %s/%u", ostr, netmask, nstr, netmask);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* \brief Adds a new IPV6/netblock to the Radix6 tree from a string
|
||||
*
|
||||
* \param str IPV6 string with optional /cidr netmask
|
||||
* \param tree Pointer to the Radix6 tree
|
||||
* \param user Pointer to the user data that has to be associated with
|
||||
* the key
|
||||
*
|
||||
* \retval bool true if node was added, false otherwise
|
||||
*
|
||||
* If the function returns false, `sc_errno` is set:
|
||||
* - SC_EEXIST: Node already exists
|
||||
* - SC_EINVAL: Parameter value error
|
||||
* - SC_ENOMEM: Memory allocation failed
|
||||
*/
|
||||
bool SCRadix6AddKeyIPV6String(
|
||||
SCRadix6Tree *tree, const SCRadix6Config *config, const char *str, void *user)
|
||||
{
|
||||
uint8_t netmask = 128;
|
||||
char ip_str[80] = ""; /* Max length for full ipv6/cidr string with NUL */
|
||||
char *mask_str = NULL;
|
||||
struct in6_addr addr;
|
||||
|
||||
/* Make a copy of the string so it can be modified */
|
||||
strlcpy(ip_str, str, sizeof(ip_str));
|
||||
|
||||
/* Does it have a mask? */
|
||||
if (NULL != (mask_str = strchr(ip_str, '/'))) {
|
||||
*(mask_str++) = '\0';
|
||||
|
||||
/* Dotted type netmask not valid for ipv6 */
|
||||
if (strchr(mask_str, '.') != NULL) {
|
||||
sc_errno = SC_EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t cidr;
|
||||
if (StringParseU8RangeCheck(&cidr, 10, 0, (const char *)mask_str, 0, 128) < 0) {
|
||||
sc_errno = SC_EINVAL;
|
||||
return false;
|
||||
}
|
||||
netmask = (uint8_t)cidr;
|
||||
}
|
||||
|
||||
/* Validate the IP */
|
||||
if (inet_pton(AF_INET6, ip_str, &addr) <= 0) {
|
||||
sc_errno = SC_EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (netmask != 128) {
|
||||
struct in6_addr maddr;
|
||||
struct in6_addr mask6, check;
|
||||
CIDRGetIPv6(netmask, &mask6);
|
||||
memcpy(&check, &addr, sizeof(check));
|
||||
bool diff = false;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
maddr.s6_addr[i] = addr.s6_addr[i] & mask6.s6_addr[i];
|
||||
diff |= (maddr.s6_addr[i] != check.s6_addr[i]);
|
||||
}
|
||||
if (diff) {
|
||||
char nstr[64];
|
||||
PrintInet(AF_INET6, (void *)&maddr.s6_addr, nstr, sizeof(nstr));
|
||||
SCLogWarning("adding '%s' as '%s/%u'", str, nstr, netmask);
|
||||
memcpy(addr.s6_addr, maddr.s6_addr, 16);
|
||||
#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
|
||||
SCRadix6ValidateIPv6Key((uint8_t *)&addr.s6_addr, netmask);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (AddKey(tree, config, (uint8_t *)&addr.s6_addr, netmask, user, true) == NULL) {
|
||||
DEBUG_VALIDATE_BUG_ON(sc_errno == SC_OK);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Removes an IPV6 address key(not a netblock) from the Radix6 tree.
|
||||
* Instead of using this function, we can also used
|
||||
* SCRadix6RemoveKeyIPV6Netblock(), by supplying a netmask value of 32.
|
||||
*
|
||||
* \param key_stream Data that has to be removed from the Radix6 tree. In this
|
||||
* case an IPV6 address
|
||||
* \param tree Pointer to the Radix6 tree from which the key has to be
|
||||
* removed
|
||||
*/
|
||||
void SCRadix6RemoveKeyIPV6(
|
||||
SCRadix6Tree *tree, const SCRadix6Config *config, const uint8_t *key_stream)
|
||||
{
|
||||
RemoveKey(tree, config, key_stream, 128);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Removes an IPV6 address netblock key from the tree.
|
||||
*
|
||||
* \param key_stream Data that has to be removed from the tree. In this
|
||||
* case an IPV6 address with netmask.
|
||||
* \param tree Pointer to the tree from which the key has to be
|
||||
* removed
|
||||
*/
|
||||
void SCRadix6RemoveKeyIPV6Netblock(SCRadix6Tree *tree, const SCRadix6Config *config,
|
||||
const uint8_t *key_stream, uint8_t netmask)
|
||||
{
|
||||
RemoveKey(tree, config, key_stream, netmask);
|
||||
}
|
||||
|
||||
void SCRadix6PrintTree(SCRadix6Tree *tree, const SCRadix6Config *config)
|
||||
{
|
||||
PrintTree(tree, config);
|
||||
}
|
||||
|
||||
SCRadix6Tree SCRadix6TreeInitialize(void)
|
||||
{
|
||||
SCRadix6Tree t = SC_RADIX6_TREE_INITIALIZER;
|
||||
return t;
|
||||
}
|
||||
|
||||
void SCRadix6TreeRelease(SCRadix6Tree *tree, const SCRadix6Config *config)
|
||||
{
|
||||
TreeRelease(tree, config);
|
||||
}
|
||||
|
||||
static void PrintUserdata(SCRadix6Node *node, void (*PrintData)(void *))
|
||||
{
|
||||
if (PrintData != NULL) {
|
||||
RadixUserData *ud = node->user_data;
|
||||
while (ud != NULL) {
|
||||
printf("[%d], ", ud->netmask);
|
||||
PrintData(ud->user);
|
||||
ud = ud->next;
|
||||
}
|
||||
} else {
|
||||
RadixUserData *ud = node->user_data;
|
||||
while (ud != NULL) {
|
||||
printf(" [%d], ", ud->netmask);
|
||||
ud = ud->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int SCRadix6ForEachNodeSub(
|
||||
const SCRadix6Node *node, SCRadix6ForEachNodeFunc Callback, void *data)
|
||||
{
|
||||
BUG_ON(!node);
|
||||
|
||||
/* invoke callback for each stored user data */
|
||||
for (RadixUserData *ud = node->user_data; ud != NULL; ud = ud->next) {
|
||||
if (Callback(node, ud->user, ud->netmask, data) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node->left) {
|
||||
if (SCRadix6ForEachNodeSub(node->left, Callback, data) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (node->right) {
|
||||
if (SCRadix6ForEachNodeSub(node->right, Callback, data) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SCRadix6ForEachNode(const SCRadix6Tree *tree, SCRadix6ForEachNodeFunc Callback, void *data)
|
||||
{
|
||||
if (tree->head == NULL)
|
||||
return 0;
|
||||
return SCRadix6ForEachNodeSub(tree->head, Callback, data);
|
||||
}
|
||||
|
||||
bool SCRadix6CompareTrees(
|
||||
const SCRadix6Tree *t1, const SCRadix6Tree *t2, SCRadix6TreeCompareFunc Callback)
|
||||
{
|
||||
return CompareTrees(t1, t2, Callback);
|
||||
}
|
||||
|
||||
/*------------------------------------Unit_Tests------------------------------*/
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
static const SCRadix6Config ut_ip_radix6_config = { NULL, NULL };
|
||||
|
||||
#define GET_IPV6(str) \
|
||||
SCLogDebug("setting up %s", (str)); \
|
||||
memset(&(sa), 0, sizeof((sa))); \
|
||||
FAIL_IF(inet_pton(AF_INET6, (str), &(sa).sin6_addr) <= 0);
|
||||
|
||||
#define ADD_IPV6(str) \
|
||||
GET_IPV6((str)); \
|
||||
SCRadix6AddKeyIPV6(&tree, &ut_ip_radix6_config, (uint8_t *)&(sa).sin6_addr, NULL);
|
||||
|
||||
#define REM_IPV6(str) \
|
||||
GET_IPV6((str)); \
|
||||
SCRadix6RemoveKeyIPV6(&tree, &ut_ip_radix6_config, (uint8_t *)&(sa).sin6_addr);
|
||||
|
||||
#define ADD_IPV6_MASK(str, cidr) \
|
||||
GET_IPV6((str)); \
|
||||
SCRadix6AddKeyIPV6Netblock( \
|
||||
&tree, &ut_ip_radix6_config, (uint8_t *)&(sa).sin6_addr, (cidr), NULL);
|
||||
|
||||
#define REM_IPV6_MASK(str, cidr) \
|
||||
GET_IPV6((str)); \
|
||||
SCRadix6RemoveKeyIPV6Netblock(&tree, &ut_ip_radix6_config, (uint8_t *)&(sa).sin6_addr, (cidr));
|
||||
|
||||
static int SCRadix6TestIPV6Insertion03(void)
|
||||
{
|
||||
struct sockaddr_in6 sa;
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
|
||||
ADD_IPV6("2000:1::1");
|
||||
ADD_IPV6("2000:1::2");
|
||||
ADD_IPV6("2000:0::3");
|
||||
ADD_IPV6("2000:0::4");
|
||||
ADD_IPV6("2000:0::4");
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV6("2000:1::6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV6("2000:0::4");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
/* continue adding keys */
|
||||
ADD_IPV6("2000:0::2");
|
||||
ADD_IPV6("2000:1::5");
|
||||
ADD_IPV6("2000:1::18");
|
||||
|
||||
/* test the existence of keys */
|
||||
GET_IPV6("2000:1::3");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2001:1:2:3::62");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV6("2000:1::1");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
GET_IPV6("2000:1::5");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
GET_IPV6("2000:1::2");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV6("2000:0::3");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
GET_IPV6("2000:0::4");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
GET_IPV6("2000:0::2");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
GET_IPV6("2000:1::18");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config);
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix6TestIPV6Removal04(void)
|
||||
{
|
||||
struct sockaddr_in6 sa;
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV6("2000:1::1");
|
||||
ADD_IPV6("2000:1::2");
|
||||
ADD_IPV6("2000:0::3");
|
||||
ADD_IPV6("2000:0::4");
|
||||
ADD_IPV6("1000:1::2");
|
||||
ADD_IPV6("2000:1::5");
|
||||
ADD_IPV6("2000:1::18");
|
||||
|
||||
/* remove the keys from the tree */
|
||||
REM_IPV6("2000:1::1");
|
||||
REM_IPV6("2000:0::3");
|
||||
REM_IPV6("2000:0::4");
|
||||
REM_IPV6("2000:1::18");
|
||||
|
||||
GET_IPV6("2000:0::1");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000:1::2");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
REM_IPV6("2000:0::3");
|
||||
REM_IPV6("1000:1::2");
|
||||
|
||||
GET_IPV6("2000:1::5");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
GET_IPV6("2000:1::2");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
REM_IPV6("2000:1::2");
|
||||
REM_IPV6("2000:1::5");
|
||||
|
||||
FAIL_IF_NOT_NULL(tree.head);
|
||||
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config);
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix6TestIPV6NetblockInsertion09(void)
|
||||
{
|
||||
struct sockaddr_in6 sa;
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV6("2000::1:1");
|
||||
ADD_IPV6("2000::1:2");
|
||||
ADD_IPV6("2000::0:3");
|
||||
ADD_IPV6("2000::0:4");
|
||||
ADD_IPV6("1000::1:2");
|
||||
ADD_IPV6("2000::1:5");
|
||||
ADD_IPV6("2000::1:18");
|
||||
|
||||
ADD_IPV6_MASK("2000::", 16);
|
||||
ADD_IPV6_MASK("2000::192:171:128:0", 128 - 8);
|
||||
ADD_IPV6_MASK("2000::192:171:192:0", 128 - 14);
|
||||
ADD_IPV6_MASK("2000::192:175:0:0", 128 - 16);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV6("2000:1::6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000::192:170:1:6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000::192:171:128:145");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
GET_IPV6("2000::192:171:64:6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000::192:171:191:6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000::192:171:224:6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
GET_IPV6("2000::192:171:224:6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000::192:175:224:6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config);
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix6TestIPV6NetblockInsertion10(void)
|
||||
{
|
||||
SCRadix6Node *node[2];
|
||||
struct sockaddr_in6 sa;
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV6_MASK("2000::253:192:0:0", 112);
|
||||
ADD_IPV6_MASK("2000::253:192:235:0", 112);
|
||||
ADD_IPV6_MASK("2000::192:167:0:0", 112);
|
||||
ADD_IPV6("2000:0::4");
|
||||
ADD_IPV6_MASK("2000::220:168:0:0", 112);
|
||||
ADD_IPV6("2000::253:224:1:5");
|
||||
ADD_IPV6_MASK("2000::192:168:0:0", 112);
|
||||
|
||||
GET_IPV6("2000::192:171:128:0");
|
||||
node[0] = SCRadix6AddKeyIPV6Netblock(
|
||||
&tree, &ut_ip_radix6_config, (uint8_t *)&sa.sin6_addr, 112, NULL);
|
||||
|
||||
GET_IPV6("2000::192:171:128:45");
|
||||
node[1] = SCRadix6AddKeyIPV6(&tree, &ut_ip_radix6_config, (uint8_t *)&sa.sin6_addr, NULL);
|
||||
|
||||
ADD_IPV6_MASK("2000::192:171:0:0", 110);
|
||||
ADD_IPV6_MASK("2000::192:175:0:0", 112);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV6("2000::192:171:128:53");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node[0]);
|
||||
|
||||
GET_IPV6("2000::192:171:128:45");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node[1]);
|
||||
|
||||
GET_IPV6("2000::192:171:128:45");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node[1]);
|
||||
|
||||
GET_IPV6("2000::192:171:128:78");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node[0]);
|
||||
|
||||
REM_IPV6_MASK("2000::192:171:128:0", 112);
|
||||
|
||||
GET_IPV6("2000::192:171:128:78");
|
||||
SCRadix6Node *n = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL);
|
||||
SCLogNotice("n %p", n);
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000::192:171:127:78");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config);
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix6TestIPV6NetblockInsertion11(void)
|
||||
{
|
||||
struct sockaddr_in6 sa;
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV6_MASK("2000::253:192:0:0", 96);
|
||||
ADD_IPV6_MASK("2000::253:192:235:0", 112);
|
||||
ADD_IPV6_MASK("2000::192:167:0:0", 96);
|
||||
ADD_IPV6("2000:0::4");
|
||||
ADD_IPV6_MASK("2000::220:168:0:0", 96);
|
||||
ADD_IPV6("2000::253:224:1:5");
|
||||
ADD_IPV6_MASK("2000::192:168:0:0", 96);
|
||||
ADD_IPV6_MASK("2000::192:171:128:0", 112);
|
||||
ADD_IPV6("2000::192:171:128:45");
|
||||
ADD_IPV6_MASK("2000::192:171:0:0", 112);
|
||||
ADD_IPV6_MASK("2000::192:175:0:0", 96);
|
||||
|
||||
GET_IPV6("::");
|
||||
SCRadix6Node *node = SCRadix6AddKeyIPV6Netblock(
|
||||
&tree, &ut_ip_radix6_config, (uint8_t *)&sa.sin6_addr, 0, NULL);
|
||||
FAIL_IF_NULL(node);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV6("2000::192:171:128:53");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV6("2000::192:171:128:45");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV6("2000::192:171:128:78");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV6("2000::192:171:127:78");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node);
|
||||
|
||||
GET_IPV6("2000::1:1:1:1");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node);
|
||||
|
||||
GET_IPV6("2000::192:255:254:25");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node);
|
||||
|
||||
GET_IPV6("2000::169:255:254:25");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node);
|
||||
|
||||
GET_IPV6("::");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node);
|
||||
|
||||
GET_IPV6("2000::253:224:1:5");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != node);
|
||||
|
||||
GET_IPV6("2000::245:63:62:121");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node);
|
||||
|
||||
GET_IPV6("2000::253:224:1:6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node);
|
||||
|
||||
/* remove node 0.0.0.0 */
|
||||
REM_IPV6_MASK("::", 0);
|
||||
|
||||
GET_IPV6("2000::253:224:1:6");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000::192:171:127:78");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000::1:1:1:1");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV6("2000::192:255:254:25");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
GET_IPV6("2000::169:255:254:25");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV6("::");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config);
|
||||
|
||||
PASS;
|
||||
}
|
||||
|
||||
static int SCRadix6TestIPV6NetblockInsertion12(void)
|
||||
{
|
||||
struct sockaddr_in6 sa;
|
||||
SCRadix6Node *node[2];
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
|
||||
/* add the keys */
|
||||
ADD_IPV6_MASK("2000::253:192:0:0", 96);
|
||||
ADD_IPV6_MASK("2000::253:192:235:0", 112);
|
||||
ADD_IPV6_MASK("2000::192:167:0:0", 96);
|
||||
ADD_IPV6("2000:0::4");
|
||||
ADD_IPV6_MASK("2000::220:168:0:0", 96);
|
||||
ADD_IPV6("2000::253:224:1:5");
|
||||
ADD_IPV6_MASK("2000::192:168:0:0", 96);
|
||||
|
||||
GET_IPV6("2000::192:171:128:0");
|
||||
node[0] = SCRadix6AddKeyIPV6Netblock(
|
||||
&tree, &ut_ip_radix6_config, (uint8_t *)&sa.sin6_addr, 96, NULL);
|
||||
FAIL_IF_NULL(node[0]);
|
||||
|
||||
GET_IPV6("2000::192:171:128:45");
|
||||
node[1] = SCRadix6AddKeyIPV6(&tree, &ut_ip_radix6_config, (uint8_t *)&sa.sin6_addr, NULL);
|
||||
FAIL_IF_NULL(node[1]);
|
||||
|
||||
ADD_IPV6_MASK("2000::192:171:0:0", 96);
|
||||
ADD_IPV6_MASK("2000::225:175:21:228", 128);
|
||||
|
||||
/* test for the existance of a key */
|
||||
GET_IPV6("2000::192:171:128:53");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node[0]);
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV6("2000::192:171:128:45");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node[1]);
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node[1]);
|
||||
|
||||
GET_IPV6("2000::192:171:128:78");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == node[0]);
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV6("2000::225:175:21:228");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
|
||||
GET_IPV6("2000::225:175:21:224");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV6("2000::225:175:21:229");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
GET_IPV6("2000::225:175:21:230");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindExactMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) == NULL);
|
||||
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check that the best match search works for all the
|
||||
* possible netblocks of a fixed address
|
||||
*/
|
||||
static int SCRadix6TestIPV6NetBlocksAndBestSearch16(void)
|
||||
{
|
||||
struct sockaddr_in6 sa;
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
|
||||
GET_IPV6("2000:1::1");
|
||||
for (uint32_t i = 0; i <= 128; i++) {
|
||||
uint32_t *user = SCMalloc(sizeof(uint32_t));
|
||||
FAIL_IF_NULL(user);
|
||||
*user = i;
|
||||
SCRadix6AddKeyIPV6Netblock(&tree, &ut_ip_radix6_config, (uint8_t *)&sa.sin6_addr, i, user);
|
||||
void *user_data = NULL;
|
||||
SCRadix6Node *node = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != i);
|
||||
}
|
||||
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check special combinations of netblocks and addresses
|
||||
* on best search checking the returned userdata
|
||||
*/
|
||||
static int SCRadix6TestIPV6NetBlocksAndBestSearch19(void)
|
||||
{
|
||||
struct sockaddr_in6 sa;
|
||||
void *user_data = NULL;
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
|
||||
GET_IPV6("::");
|
||||
uint32_t *user = SCMalloc(sizeof(uint32_t));
|
||||
FAIL_IF_NULL(user);
|
||||
*user = 100;
|
||||
SCRadix6AddKeyIPV6Netblock(&tree, &ut_ip_radix6_config, (uint8_t *)&sa.sin6_addr, 0, user);
|
||||
|
||||
GET_IPV6("2000:1::15");
|
||||
SCRadix6Node *node = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 100);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV6("2000:177::0:0:0");
|
||||
user = SCMalloc(sizeof(uint32_t));
|
||||
FAIL_IF_NULL(user);
|
||||
*user = 200;
|
||||
SCRadix6AddKeyIPV6Netblock(&tree, &ut_ip_radix6_config, (uint8_t *)&sa.sin6_addr, 64, user);
|
||||
|
||||
GET_IPV6("2000:177::168:1:15");
|
||||
node = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 200);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV6("2000:178::168:1:15");
|
||||
node = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 100);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV6("2000:177::168:0:0");
|
||||
user = SCMalloc(sizeof(uint32_t));
|
||||
FAIL_IF_NULL(user);
|
||||
*user = 300;
|
||||
SCRadix6AddKeyIPV6Netblock(&tree, &ut_ip_radix6_config, (uint8_t *)&sa.sin6_addr, 92, user);
|
||||
|
||||
GET_IPV6("2000:177::168:1:15");
|
||||
node = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 300);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV6("2000:177::167:1:15");
|
||||
node = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 300);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV6("2000:177::178:1:15");
|
||||
node = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 200);
|
||||
user_data = NULL;
|
||||
|
||||
GET_IPV6("2000:197::178:1:15");
|
||||
node = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, &user_data);
|
||||
FAIL_IF_NULL(node);
|
||||
FAIL_IF_NULL(user_data);
|
||||
FAIL_IF(*((uint32_t *)user_data) != 100);
|
||||
user_data = NULL;
|
||||
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test SCRadix6TestIPV6NetblockInsertion15 insert a node searching on it.
|
||||
* Should always return true but the purposse of the test is to monitor
|
||||
* the memory usage to detect memleaks (there was one on searching)
|
||||
*/
|
||||
static int SCRadix6TestIPV6NetblockInsertion25(void)
|
||||
{
|
||||
struct sockaddr_in6 sa;
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
ADD_IPV6_MASK("2000::192:168:0:0", 16);
|
||||
GET_IPV6("2000::192:168:128:53");
|
||||
FAIL_IF_NOT(SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, NULL) != NULL);
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config);
|
||||
PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test SCRadix6TestIPV6NetblockInsertion26 insert a node searching on it.
|
||||
* Should always return true but the purposse of the test is to monitor
|
||||
* the memory usage to detect memleaks (there was one on searching)
|
||||
*/
|
||||
static int SCRadix6TestIPV6NetblockInsertion26(void)
|
||||
{
|
||||
SCRadix6Node *tmp = NULL;
|
||||
struct sockaddr_in6 sa;
|
||||
const SCRadix6Config ut_ip_radix6_config_26 = { free, NULL };
|
||||
|
||||
char *str = SCStrdup("Hello1");
|
||||
FAIL_IF_NULL(str);
|
||||
|
||||
SCRadix6Tree tree = SCRadix6TreeInitialize();
|
||||
|
||||
GET_IPV6("::");
|
||||
SCRadix6AddKeyIPV6Netblock(&tree, &ut_ip_radix6_config_26, (uint8_t *)&sa.sin6_addr, 0, str);
|
||||
|
||||
str = SCStrdup("Hello2");
|
||||
FAIL_IF_NULL(str);
|
||||
|
||||
GET_IPV6("2000::176:0:0:1");
|
||||
SCRadix6AddKeyIPV6Netblock(&tree, &ut_ip_radix6_config_26, (uint8_t *)&sa.sin6_addr, 5, str);
|
||||
|
||||
str = SCStrdup("Hello3");
|
||||
FAIL_IF_NULL(str);
|
||||
|
||||
GET_IPV6("::");
|
||||
SCRadix6AddKeyIPV6Netblock(&tree, &ut_ip_radix6_config_26, (uint8_t *)&sa.sin6_addr, 7, str);
|
||||
|
||||
/* test for the existance of a key */
|
||||
void *retptr = NULL;
|
||||
tmp = SCRadix6TreeFindBestMatch(&tree, (uint8_t *)&sa.sin6_addr, &retptr);
|
||||
FAIL_IF_NULL(tmp);
|
||||
FAIL_IF_NULL(retptr);
|
||||
FAIL_IF_NOT(strcmp((char *)retptr, "Hello3") == 0);
|
||||
|
||||
SCRadix6TreeRelease(&tree, &ut_ip_radix6_config_26);
|
||||
|
||||
PASS;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SCRadix6RegisterTests(void)
|
||||
{
|
||||
#ifdef UNITTESTS
|
||||
UtRegisterTest("SCRadix6TestIPV6Insertion03", SCRadix6TestIPV6Insertion03);
|
||||
UtRegisterTest("SCRadix6TestIPV6Removal04", SCRadix6TestIPV6Removal04);
|
||||
UtRegisterTest("SCRadix6TestIPV6NetblockInsertion09", SCRadix6TestIPV6NetblockInsertion09);
|
||||
UtRegisterTest("SCRadix6TestIPV6NetblockInsertion10", SCRadix6TestIPV6NetblockInsertion10);
|
||||
UtRegisterTest("SCRadix6TestIPV6NetblockInsertion11", SCRadix6TestIPV6NetblockInsertion11);
|
||||
UtRegisterTest("SCRadix6TestIPV6NetblockInsertion12", SCRadix6TestIPV6NetblockInsertion12);
|
||||
UtRegisterTest(
|
||||
"SCRadix6TestIPV6NetBlocksAndBestSearch16", SCRadix6TestIPV6NetBlocksAndBestSearch16);
|
||||
UtRegisterTest(
|
||||
"SCRadix6TestIPV6NetBlocksAndBestSearch19", SCRadix6TestIPV6NetBlocksAndBestSearch19);
|
||||
UtRegisterTest("SCRadix6TestIPV6NetblockInsertion25", SCRadix6TestIPV6NetblockInsertion25);
|
||||
UtRegisterTest("SCRadix6TestIPV6NetblockInsertion26", SCRadix6TestIPV6NetblockInsertion26);
|
||||
#endif
|
||||
return;
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/* 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>
|
||||
* Based on util-radix-tree.[ch] by:
|
||||
* \author Anoop Saldanha <anoopsaldanha@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef SURICATA_UTIL_RADIX6_TREE_H
|
||||
#define SURICATA_UTIL_RADIX6_TREE_H
|
||||
|
||||
#include "suricata-common.h"
|
||||
|
||||
struct RadixUserData;
|
||||
|
||||
/**
|
||||
* \brief Structure for the node in the radix tree
|
||||
*/
|
||||
typedef struct SCRadix6Node_ {
|
||||
/** the key that has been stored in the tree */
|
||||
uint8_t prefix_stream[16];
|
||||
|
||||
/** holds bitmap of netmasks that come under this node in the tree */
|
||||
uint8_t masks[17];
|
||||
|
||||
/** the bit position where the bits differ in the nodes children. Used
|
||||
* to determine the path to be taken during a lookup */
|
||||
uint8_t bit;
|
||||
/** bool to see if prefix_stream is filled */
|
||||
bool has_prefix;
|
||||
|
||||
/** User data that has is associated with this key. We need a user
|
||||
* data field for each netblock value possible since one ip can be associated
|
||||
* with any of the 128 netblocks. */
|
||||
struct RadixUserData *user_data;
|
||||
|
||||
/** the left and the right children of a node */
|
||||
struct SCRadix6Node_ *left, *right;
|
||||
|
||||
/** the parent node for this tree */
|
||||
struct SCRadix6Node_ *parent;
|
||||
} SCRadix6Node;
|
||||
|
||||
/**
|
||||
* \brief Structure for the radix tree
|
||||
*/
|
||||
typedef struct SCRadix6Tree_ {
|
||||
/** the root node in the radix tree */
|
||||
SCRadix6Node *head;
|
||||
} SCRadix6Tree;
|
||||
|
||||
typedef struct SCRadix6Config_ {
|
||||
void (*Free)(void *);
|
||||
/** function pointer that is supplied by the user to free the user data
|
||||
* held by the user field of SCRadix6Node */
|
||||
void (*PrintData)(void *);
|
||||
} SCRadix6Config;
|
||||
|
||||
#define SC_RADIX6_TREE_INITIALIZER \
|
||||
{ \
|
||||
.head = NULL \
|
||||
}
|
||||
|
||||
SCRadix6Tree SCRadix6TreeInitialize(void);
|
||||
void SCRadix6TreeRelease(SCRadix6Tree *, const SCRadix6Config *);
|
||||
|
||||
SCRadix6Node *SCRadix6AddKeyIPV6(SCRadix6Tree *, const SCRadix6Config *, const uint8_t *, void *);
|
||||
SCRadix6Node *SCRadix6AddKeyIPV6Netblock(
|
||||
SCRadix6Tree *, const SCRadix6Config *, const uint8_t *, uint8_t, void *);
|
||||
bool SCRadix6AddKeyIPV6String(SCRadix6Tree *, const SCRadix6Config *, const char *, void *);
|
||||
|
||||
void SCRadix6RemoveKeyIPV6Netblock(
|
||||
SCRadix6Tree *, const SCRadix6Config *, const uint8_t *, uint8_t);
|
||||
void SCRadix6RemoveKeyIPV6(SCRadix6Tree *, const SCRadix6Config *, const uint8_t *);
|
||||
|
||||
SCRadix6Node *SCRadix6TreeFindExactMatch(const SCRadix6Tree *, const uint8_t *, void **);
|
||||
SCRadix6Node *SCRadix6TreeFindNetblock(
|
||||
const SCRadix6Tree *, const uint8_t *, const uint8_t, void **);
|
||||
SCRadix6Node *SCRadix6TreeFindBestMatch(const SCRadix6Tree *, const uint8_t *, void **);
|
||||
SCRadix6Node *SCRadix6TreeFindBestMatch2(const SCRadix6Tree *, const uint8_t *, void **, uint8_t *);
|
||||
|
||||
void SCRadix6PrintTree(SCRadix6Tree *, const SCRadix6Config *);
|
||||
void SCRadix6PrintNodeInfo(SCRadix6Node *, int, void (*PrintData)(void *));
|
||||
|
||||
void SCRadix6RegisterTests(void);
|
||||
|
||||
typedef int (*SCRadix6ForEachNodeFunc)(
|
||||
const SCRadix6Node *node, void *user_data, const uint8_t netmask, void *data);
|
||||
|
||||
int SCRadix6ForEachNode(const SCRadix6Tree *tree, SCRadix6ForEachNodeFunc Callback, void *data);
|
||||
|
||||
/** \brief compare content of 2 user data entries
|
||||
* \retval true equal
|
||||
* \retval false not equal
|
||||
*/
|
||||
typedef bool (*SCRadix6TreeCompareFunc)(const void *ud1, const void *ud2);
|
||||
bool SCRadix6CompareTrees(
|
||||
const SCRadix6Tree *t1, const SCRadix6Tree *t2, SCRadix6TreeCompareFunc Callback);
|
||||
|
||||
#endif /* SURICATA_UTIL_RADIX4_TREE_H */
|
Loading…
Reference in New Issue