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.
This commit is contained in:
Victor Julien
2024-11-28 14:59:20 +01:00
committed by Victor Julien
parent e9a2352b99
commit 8f03a2eb9b
7 changed files with 3114 additions and 0 deletions
+5
View File
@@ -542,6 +542,9 @@ noinst_HEADERS = \
util-profiling-locks.h \
util-proto-name.h \
util-radix-tree.h \
util-radix-tree-common.h \
util-radix4-tree.h \
util-radix6-tree.h \
util-random.h \
util-reference-config.h \
util-rohash.h \
@@ -1089,6 +1092,8 @@ libsuricata_c_a_SOURCES = \
util-profiling-rules.c \
util-proto-name.c \
util-radix-tree.c \
util-radix4-tree.c \
util-radix6-tree.c \
util-random.c \
util-reference-config.c \
util-rohash.c \
+4
View File
@@ -65,6 +65,8 @@
#include "util-action.h"
#include "util-radix-tree.h"
#include "util-radix4-tree.h"
#include "util-radix6-tree.h"
#include "util-host-os-info.h"
#include "util-cidr.h"
#include "util-unittest-helper.h"
@@ -172,6 +174,8 @@ static void RegisterUnittests(void)
IPPairRegisterUnittests();
SCSigRegisterSignatureOrderingTests();
SCRadixRegisterTests();
SCRadix4RegisterTests();
SCRadix6RegisterTests();
DefragRegisterTests();
SigGroupHeadRegisterTests();
SCHInfoRegisterTests();
+966
View File
@@ -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 <[email protected]>
* \author Anoop Saldanha <[email protected]>
*
* 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);
}
+940
View File
@@ -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 <[email protected]>
* \author Anoop Saldanha <[email protected]>
*
* 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;
}
+119
View File
@@ -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 <[email protected]>
* Based on util-radix-tree.[ch] by:
* \author Anoop Saldanha <[email protected]>
*/
#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 */
+963
View File
@@ -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 <[email protected]>
* \author Anoop Saldanha <[email protected]>
*
* 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;
}
+117
View File
@@ -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 <[email protected]>
* Based on util-radix-tree.[ch] by:
* \author Anoop Saldanha <[email protected]>
*/
#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 */