/* 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 * \author Anoop Saldanha * * 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); DEBUG_VALIDATE_BUG_ON(1); } } #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 void SCRadix6UtFree(void *ptr) { SCFree(ptr); } static const SCRadix6Config ut_ip_radix6_config = { SCRadix6UtFree, 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; }