/* * Copyright (c) 1999-2000 * * The Regents of the University of Michigan ("The Regents") and * Merit Network, Inc. All rights reserved. Redistribution and use * in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. All advertising materials mentioning features or use of * this software must display the following acknowledgement: * * This product includes software developed by the University of * Michigan, Merit Network, Inc., and their contributors. * * 4. Neither the name of the University, Merit Network, nor the * names of their contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Portions Copyright (c) 2004,2005 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "radix.h" /* $Id: radix.c,v 1.17 2007/10/24 06:04:31 djm Exp $ */ /* * Originally from MRT include/defs.h * $MRTId: defs.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ #define BIT_TEST(f, b) ((f) & (b)) /* * Originally from MRT include/mrt.h * $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ #define prefix_tochar(prefix) ((char *)&(prefix)->add) #define prefix_touchar(prefix) ((unsigned char *)&(prefix)->add) /* * Originally from MRT lib/mrt/prefix.c * $MRTId: prefix.c,v 1.1.1.1 2000/08/14 18:46:11 labovit Exp $ */ static int comp_with_mask(unsigned char *addr, unsigned char *dest, unsigned int mask) { if (memcmp(addr, dest, mask / 8) == 0) { unsigned int n = mask / 8; unsigned int m = ((~0) << (8 - (mask % 8))); if (mask % 8 == 0 || (addr[n] & m) == (dest[n] & m)) return (1); } return (0); } static prefix_t *New_Prefix2(int family, void *dest, int bitlen, prefix_t *prefix) { int dynamic_allocated = 0; int default_bitlen = 32; if (family == AF_INET6) { default_bitlen = 128; if (prefix == NULL) { if ((prefix = malloc(sizeof(*prefix))) == NULL) return (NULL); memset(prefix, '\0', sizeof(*prefix)); dynamic_allocated++; } memcpy(&prefix->add.sin6, dest, 16); } else if (family == AF_INET) { if (prefix == NULL) { if ((prefix = malloc(sizeof(*prefix))) == NULL) return (NULL); memset(prefix, '\0', sizeof(*prefix)); dynamic_allocated++; } memcpy(&prefix->add.sin, dest, 4); } else return (NULL); prefix->bitlen = (bitlen >= 0) ? bitlen : default_bitlen; prefix->family = family; prefix->ref_count = 0; if (dynamic_allocated) prefix->ref_count++; return (prefix); } static prefix_t *Ref_Prefix(prefix_t *prefix) { if (prefix == NULL) return (NULL); if (prefix->ref_count == 0) { /* make a copy in case of a static prefix */ return (New_Prefix2(prefix->family, &prefix->add, prefix->bitlen, NULL)); } prefix->ref_count++; return (prefix); } void Deref_Prefix(prefix_t *prefix) { if (prefix == NULL) return; prefix->ref_count--; if (prefix->ref_count <= 0) { free(prefix); return; } } /* * Originally from MRT lib/radix/radix.c * $MRTId: radix.c,v 1.1.1.1 2000/08/14 18:46:13 labovit Exp $ */ /* these routines support continuous mask only */ radix_tree_t *New_Radix(void) { radix_tree_t *radix; if ((radix = malloc(sizeof(*radix))) == NULL) return (NULL); memset(radix, '\0', sizeof(*radix)); radix->maxbits = 128; radix->head = NULL; radix->num_active_node = 0; return (radix); } /* * if func is supplied, it will be called as func(node->data) * before deleting the node */ static void Clear_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) { if (radix->head) { radix_node_t *Xstack[RADIX_MAXBITS + 1]; radix_node_t **Xsp = Xstack; radix_node_t *Xrn = radix->head; while (Xrn) { radix_node_t *l = Xrn->l; radix_node_t *r = Xrn->r; if (Xrn->prefix) { Deref_Prefix(Xrn->prefix); if (Xrn->data && func) func(Xrn, cbctx); } free(Xrn); radix->num_active_node--; if (l) { if (r) *Xsp++ = r; Xrn = l; } else if (r) { Xrn = r; } else if (Xsp != Xstack) { Xrn = *(--Xsp); } else { Xrn = (radix_node_t *) 0; } } } } void Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) { if (radix == NULL) { return; } Clear_Radix(radix, func, cbctx); free(radix); } /* * if func is supplied, it will be called as func(node->prefix, node->data) */ void radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx) { radix_node_t *node; RADIX_WALK(radix->head, node) { func(node, cbctx); } RADIX_WALK_END; } radix_node_t *radix_search_exact(radix_tree_t *radix, prefix_t *prefix) { radix_node_t *node; unsigned char *addr; unsigned int bitlen; if (radix->head == NULL) return (NULL); node = radix->head; addr = prefix_touchar(prefix); bitlen = prefix->bitlen; while (node->bit < bitlen) { if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) node = node->r; else node = node->l; if (node == NULL) return (NULL); } if (node->bit > bitlen || node->prefix == NULL) return (NULL); if (comp_with_mask(prefix_touchar(node->prefix), prefix_touchar(prefix), bitlen)) return (node); return (NULL); } /* if inclusive != 0, "best" may be the given prefix itself */ static radix_node_t *radix_search_best2(radix_tree_t *radix, prefix_t *prefix, int inclusive) { radix_node_t *node; radix_node_t *stack[RADIX_MAXBITS + 1] = {0}; unsigned char *addr; unsigned int bitlen; int cnt = 0; if (radix->head == NULL) return (NULL); node = radix->head; addr = prefix_touchar(prefix); bitlen = prefix->bitlen; while (node->bit < bitlen) { if (node->prefix) stack[cnt++] = node; if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) node = node->r; else node = node->l; if (node == NULL) break; } if (inclusive && node && node->prefix) stack[cnt++] = node; if (cnt <= 0) return (NULL); while (--cnt >= 0) { node = stack[cnt]; if (comp_with_mask(prefix_touchar(node->prefix), prefix_touchar(prefix), node->prefix->bitlen)) return (node); } return (NULL); } radix_node_t *radix_search_best(radix_tree_t *radix, prefix_t *prefix) { return (radix_search_best2(radix, prefix, 1)); } radix_node_t *radix_lookup(radix_tree_t *radix, prefix_t *prefix) { radix_node_t *node, *new_node, *parent, *glue; unsigned char *addr, *test_addr; unsigned int bitlen, check_bit, differ_bit; unsigned int i, j, r; if (radix->head == NULL) { if ((node = malloc(sizeof(*node))) == NULL) return (NULL); memset(node, '\0', sizeof(*node)); node->bit = prefix->bitlen; node->prefix = Ref_Prefix(prefix); node->parent = NULL; node->l = node->r = NULL; node->data = NULL; radix->head = node; radix->num_active_node++; return (node); } addr = prefix_touchar(prefix); bitlen = prefix->bitlen; node = radix->head; while (node->bit < bitlen || node->prefix == NULL) { if (node->bit < radix->maxbits && BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { if (node->r == NULL) break; node = node->r; } else { if (node->l == NULL) break; node = node->l; } } test_addr = prefix_touchar(node->prefix); /* find the first bit different */ check_bit = (node->bit < bitlen) ? node->bit : bitlen; differ_bit = 0; for (i = 0; i * 8 < check_bit; i++) { if ((r = (addr[i] ^ test_addr[i])) == 0) { differ_bit = (i + 1) * 8; continue; } /* I know the better way, but for now */ for (j = 0; j < 8; j++) { if (BIT_TEST(r, (0x80 >> j))) break; } /* must be found */ differ_bit = i * 8 + j; break; } if (differ_bit > check_bit) differ_bit = check_bit; parent = node->parent; while (parent && parent->bit >= differ_bit) { node = parent; parent = node->parent; } if (differ_bit == bitlen && node->bit == bitlen) { if (node->prefix == NULL) node->prefix = Ref_Prefix(prefix); return (node); } if ((new_node = malloc(sizeof(*new_node))) == NULL) return (NULL); memset(new_node, '\0', sizeof(*new_node)); new_node->bit = prefix->bitlen; new_node->prefix = Ref_Prefix(prefix); new_node->parent = NULL; new_node->l = new_node->r = NULL; new_node->data = NULL; radix->num_active_node++; if (node->bit == differ_bit) { new_node->parent = node; if (node->bit < radix->maxbits && BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) node->r = new_node; else node->l = new_node; return (new_node); } if (bitlen == differ_bit) { if (bitlen < radix->maxbits && BIT_TEST(test_addr[bitlen >> 3], 0x80 >> (bitlen & 0x07))) new_node->r = node; else new_node->l = node; new_node->parent = node->parent; if (node->parent == NULL) radix->head = new_node; else if (node->parent->r == node) node->parent->r = new_node; else node->parent->l = new_node; node->parent = new_node; } else { if ((glue = malloc(sizeof(*glue))) == NULL) return (NULL); memset(glue, '\0', sizeof(*glue)); glue->bit = differ_bit; glue->prefix = NULL; glue->parent = node->parent; glue->data = NULL; radix->num_active_node++; if (differ_bit < radix->maxbits && BIT_TEST(addr[differ_bit >> 3], 0x80 >> (differ_bit & 0x07))) { glue->r = new_node; glue->l = node; } else { glue->r = node; glue->l = new_node; } new_node->parent = glue; if (node->parent == NULL) radix->head = glue; else if (node->parent->r == node) node->parent->r = glue; else node->parent->l = glue; node->parent = glue; } return (new_node); } void radix_remove(radix_tree_t *radix, radix_node_t *node) { radix_node_t *parent, *child; if (node->r && node->l) { /* * this might be a placeholder node -- have to check and make * sure there is a prefix aossciated with it ! */ if (node->prefix != NULL) Deref_Prefix(node->prefix); node->prefix = NULL; /* Also I needed to clear data pointer -- masaki */ node->data = NULL; return; } if (node->r == NULL && node->l == NULL) { parent = node->parent; Deref_Prefix(node->prefix); free(node); radix->num_active_node--; if (parent == NULL) { radix->head = NULL; return; } if (parent->r == node) { parent->r = NULL; child = parent->l; } else { parent->l = NULL; child = parent->r; } if (parent->prefix) return; /* we need to remove parent too */ if (parent->parent == NULL) radix->head = child; else if (parent->parent->r == parent) parent->parent->r = child; else parent->parent->l = child; child->parent = parent->parent; free(parent); radix->num_active_node--; return; } if (node->r) child = node->r; else child = node->l; parent = node->parent; child->parent = parent; Deref_Prefix(node->prefix); free(node); radix->num_active_node--; if (parent == NULL) { radix->head = child; return; } if (parent->r == node) parent->r = child; else parent->l = child; } /* Local additions */ static void sanitise_mask(unsigned char *addr, unsigned int masklen, unsigned int maskbits) { unsigned int i = masklen / 8; unsigned int j = masklen % 8; if (j != 0) { addr[i] &= (~0) << (8 - j); i++; } for (; i < maskbits / 8; i++) addr[i] = 0; } prefix_t *prefix_pton(const char *string, long len, prefix_t *prefix, const char **errmsg) { char save[256], *cp, *ep; struct addrinfo hints, *ai; void *addr; prefix_t *ret; size_t slen; int r; ret = NULL; /* Copy the string to parse, because we modify it */ if ((slen = strlen(string) + 1) > sizeof(save)) { *errmsg = "string too long"; return (NULL); } memcpy(save, string, slen); if ((cp = strchr(save, '/')) != NULL) { if (len != -1 ) { *errmsg = "masklen specified twice"; return (NULL); } *cp++ = '\0'; len = strtol(cp, &ep, 10); if (*cp == '\0' || *ep != '\0' || len < 0) { *errmsg = "could not parse masklen"; return (NULL); } /* More checks below */ } memset(&hints, '\0', sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; if ((r = getaddrinfo(save, NULL, &hints, &ai)) != 0) { snprintf(save, sizeof(save), "getaddrinfo: %s:", gai_strerror(r)); *errmsg = save; return NULL; } if (ai == NULL || ai->ai_addr == NULL) { *errmsg = "getaddrinfo returned no result"; goto out; } switch (ai->ai_addr->sa_family) { case AF_INET: if (len == -1) len = 32; else if (len < 0 || len > 32) goto out; addr = &((struct sockaddr_in *) ai->ai_addr)->sin_addr; sanitise_mask(addr, len, 32); break; case AF_INET6: if (len == -1) len = 128; else if (len < 0 || len > 128) goto out; addr = &((struct sockaddr_in6 *) ai->ai_addr)->sin6_addr; sanitise_mask(addr, len, 128); break; default: goto out; } ret = New_Prefix2(ai->ai_addr->sa_family, addr, len, prefix); if (ret == NULL) *errmsg = "New_Prefix2 failed"; out: freeaddrinfo(ai); return (ret); } prefix_t *prefix_from_blob(unsigned char *blob, int len, int prefixlen, prefix_t *prefix) { int family, maxprefix; switch (len) { case 4: /* Assume AF_INET */ family = AF_INET; maxprefix = 32; break; case 16: /* Assume AF_INET6 */ family = AF_INET6; maxprefix = 128; break; default: /* Who knows? */ return NULL; } if (prefixlen == -1) prefixlen = maxprefix; if (prefixlen < 0 || prefixlen > maxprefix) return NULL; return (New_Prefix2(family, blob, prefixlen, prefix)); } const char * prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len) { return (inet_ntop(prefix->family, &prefix->add, buf, len)); } const char * prefix_ntop(prefix_t *prefix, char *buf, size_t len) { char addrbuf[128]; if (prefix_addr_ntop(prefix, addrbuf, sizeof(addrbuf)) == NULL) return (NULL); snprintf(buf, len, "%s/%d", addrbuf, prefix->bitlen); return (buf); }