]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
enforce limits on the addrtree structure
authorYuri Schaeffer <yuri@nlnetlabs.nl>
Mon, 14 Oct 2013 13:07:36 +0000 (13:07 +0000)
committerYuri Schaeffer <yuri@nlnetlabs.nl>
Mon, 14 Oct 2013 13:07:36 +0000 (13:07 +0000)
git-svn-id: file:///svn/unbound/branches/edns-subnet@2982 be551aaa-1e26-0410-a405-d3ace91eadb9

edns-subnet/addrtree.c
edns-subnet/addrtree.h
edns-subnet/subnetmod.c
testcode/unitvandergaast.c

index 95ea61f1569050f492979ede6613969ee10c251b..a8e1a517a856b0cb7d95d8b3b58f911571ad5d4f 100644 (file)
@@ -75,7 +75,8 @@ node_create(struct addrtree *tree, void* elem, addrlen_t scope,
 }
 
 struct addrtree* addrtree_create(addrlen_t max_depth, 
-       void (*delfunc)(void *, void *), size_t (*sizefunc)(void *), void *env)
+       void (*delfunc)(void *, void *), size_t (*sizefunc)(void *), 
+       void *env, unsigned int max_node_count)
 {
        struct addrtree* tree;
        log_assert(delfunc != NULL);
@@ -95,46 +96,35 @@ struct addrtree* addrtree_create(addrlen_t max_depth,
        tree->sizefunc = sizefunc;
        tree->env = env;
        tree->node_count = 0;
+       tree->max_node_count = max_node_count;
        return tree;
 }
 
-/** Add node to LRU list as most recently used. */
-void lru_push(struct addrtree *tree, struct addrnode *node)
-{
-       if (!tree->first) {
-               tree->first = node;
-               tree->last = node;
-               node->prev = NULL;
-       } else {
-               tree->last->next = node;
-               node->prev = tree->last->next;
-               tree->last = node;
-       }
-       node->next = NULL;
-}
-
-/** Remove least recently used from LRU list */
-struct addrnode* lru_popfirst(struct addrtree *tree)
+/** 
+ * Scrub a node clean of elem
+ * @param tree: Tree the node lives in.
+ * @param node: Node to be cleaned
+ */
+static void
+clean_node(struct addrtree* tree, struct addrnode* node)
 {
-       struct addrnode* pop;
-       pop = tree->first;
-       log_assert(first != NULL);
-       if (!pop->next) {
-               tree->first = NULL;
-               tree->last = NULL;
-       } else {
-               tree->first = pop->next;
-               tree->first->prev = NULL;
-       }
-       return pop;
+       if (!node->elem) return;
+       tree->delfunc(tree->env, node->elem);
+       node->elem = NULL;
 }
 
 /** Remove specified node from LRU list */
 void lru_pop(struct addrtree *tree, struct addrnode *node)
 {
        if (node == tree->first) {
-               (void)lru_popfirst(tree);
-       } else if (node == tree->last) {
+               if (!node->next) { /* it is the last as well */
+                       tree->first = NULL;
+                       tree->last = NULL;
+               } else {
+                       tree->first = node->next;
+                       tree->first->prev = NULL;
+               }
+       } else if (node == tree->last) { /* but not the first */
                tree->last = node->prev;
                tree->last->next = NULL;
        } else {
@@ -143,41 +133,26 @@ void lru_pop(struct addrtree *tree, struct addrnode *node)
        }
 }
 
-/** Move node to the end of LRU list */
-void lru_update(struct addrtree *tree, struct addrnode *node)
-{
-       lru_pop(tree, node);
-       lru_push(tree, node);
-}
-
-/** Size in bytes of this data structure */
-size_t addrtree_size(const struct addrtree* tree)
+/** Add node to LRU list as most recently used. */
+void lru_push(struct addrtree *tree, struct addrnode *node)
 {
-       struct addrnode* n;
-       size_t s;
-
-       if (!tree) return 0;
-       s = sizeof (struct addrnode); /* root always exists but not in LRU */
-       if (tree->root->elem) s += tree->sizefunc(tree->root->elem);
-       for(n = tree->first; n; n = n->next) {
-               s += sizeof (struct addredge) + sizeof (struct addrnode);
-               s += tree->sizefunc(n->elem);
+       if (!tree->first) {
+               tree->first = node;
+               tree->last = node;
+               node->prev = NULL;
+       } else {
+               tree->last->next = node;
+               node->prev = tree->last;
+               tree->last = node;
        }
-       return s;
+       node->next = NULL;
 }
 
-/** 
- * Scrub a node clean of elem
- * @param tree: Tree the node lives in.
- * @param node: Node to be cleaned
- */
-static void
-clean_node(struct addrtree* tree, struct addrnode* node)
+/** Move node to the end of LRU list */
+void lru_update(struct addrtree *tree, struct addrnode *node)
 {
-       tree->node_count--;
-       if (!node->elem) return;
-       tree->delfunc(tree->env, node->elem);
-       node->elem = NULL;
+       lru_pop(tree, node);
+       lru_push(tree, node);
 }
 
 /** 
@@ -186,7 +161,7 @@ clean_node(struct addrtree* tree, struct addrnode* node)
  * @param tree: Tree the node lives in.
  * @param node: Node to be freed
  */
-static void
+static int
 purge_node(struct addrtree *tree, struct addrnode *node)
 {
        struct addredge *parent_edge, *child_edge = NULL;
@@ -196,6 +171,7 @@ purge_node(struct addrtree *tree, struct addrnode *node)
        clean_node(tree, node);
        parent_edge = node->parent_edge;
        if (keep || !parent_edge) return;
+       tree->node_count--;
        index = parent_edge->parent_index;
        child_edge = node->edge[!node->edge[0]];
        if (child_edge) {
@@ -205,9 +181,54 @@ purge_node(struct addrtree *tree, struct addrnode *node)
        parent_edge->parent_node->edge[index] = child_edge;
        free(parent_edge->str);
        free(parent_edge);
+       lru_pop(tree, node);
        free(node);
 }
 
+/** If a limit is set remove old nodes while above that limit */
+void lru_cleanup(struct addrtree *tree)
+{
+       struct addrnode *n, *p;
+       int children;
+       if (tree->max_node_count == 0) return;
+       while (tree->node_count > tree->max_node_count) {
+               n = tree->first;
+               if (!n) break;
+               children = (n->edge[0] != NULL) + (n->edge[1] != NULL);
+               /** Don't remove this node, it is either the root or we can't
+                * do without it because it has 2 children */
+               if (children == 2 || !n->parent_edge) {
+                       lru_update(tree, n);
+                       continue;
+               }
+               p = n->parent_edge->parent_node;
+               purge_node(tree, n);
+               /** Since we removed n, n's parent p is eligible for deletion
+                * if it is not the root node, caries no data and has only 1
+                * child */
+               children = (p->edge[0] != NULL) + (p->edge[1] != NULL);
+               if (!p->elem && children == 1 && p->parent_edge) {
+                       purge_node(tree, p);
+               }
+       }
+}
+
+/** Size in bytes of this data structure */
+size_t addrtree_size(const struct addrtree* tree)
+{
+       struct addrnode* n;
+       size_t s;
+
+       if (!tree) return 0;
+       s = sizeof (struct addrnode); /* root always exists but not in LRU */
+       if (tree->root->elem) s += tree->sizefunc(tree->root->elem);
+       for(n = tree->first; n; n = n->next) {
+               s += sizeof (struct addredge) + sizeof (struct addrnode);
+               s += tree->sizefunc(n->elem);
+       }
+       return s;
+}
+
 /** 
  * Free complete addrtree structure
  * @param tree: Tree to free
@@ -218,7 +239,7 @@ void addrtree_delete(struct addrtree* tree)
        if (!tree) return;
        clean_node(tree, tree->root);
        free(tree->root);
-       while(n = tree->first) {
+       while((n = tree->first)) {
                tree->first = n->next;
                clean_node(tree, n);
                free(n->parent_edge->str);
@@ -326,7 +347,6 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr,
                        if (!edge->node->elem || edge->node->ttl >= now)
                                break;
                        purge_node(tree, edge->node);
-                       lru_pop(tree, edge->node);
                        edge = node->edge[index];
                }
                /* Case 2: New leafnode */
@@ -335,10 +355,12 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr,
                        if (!newnode) return;
                        if (!edge_create(newnode, addr, sourcemask, node, index)) {
                                clean_node(tree, newnode);
+                               tree->node_count--;
                                free(newnode);
                                return;
                        }
                        lru_push(tree, newnode);
+                       lru_cleanup(tree);
                        return;
                }
                /* Case 3: Traverse edge */
@@ -357,11 +379,12 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr,
                        return;
                if (!edge_create(newnode, addr, common, node, index)) {
                        clean_node(tree, newnode);
+                       tree->node_count--;
                        free(newnode);
                        return;
                }
                lru_push(tree, newnode);
-               /** connect existing child to our new node */
+               /* connect existing child to our new node */
                index = getbit(edge->str, edge->len, common);
                newnode->edge[index] = edge;
                edge->parent_node = newnode;
@@ -378,11 +401,13 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr,
                        newnode = node_create(tree, elem, scope, ttl);
                        if (!edge_create(newnode, addr, sourcemask, node, index^1)) {
                                clean_node(tree, newnode);
+                               tree->node_count--;
                                free(newnode);
                                return;
                        }
                        lru_push(tree, newnode);
                }
+               lru_cleanup(tree);
                return;
        }
 }
index 8a3969b0e2449855bef1d2070486811f488160f2..1a376d0eb850e8329223f2391669da7dd01d85d1 100644 (file)
@@ -41,6 +41,9 @@ struct addrtree {
        /** Number of elements in the tree (not always equal to number of 
         * nodes) */
        unsigned int node_count;
+       /** Maximum number of allowed nodes, will be enforced by LRU list.
+        * Excluding the root node, 0 for unlimited */
+       unsigned int max_node_count;
        /** Maximum prefix length we are willing to cache. */
        addrlen_t max_depth;
        /** External function to delete elem. Called as 
@@ -102,7 +105,7 @@ size_t addrtree_size(const struct addrtree* tree);
  */
 struct addrtree* 
 addrtree_create(addrlen_t max_depth, void (*delfunc)(void *, void *), 
-       size_t (*sizefunc)(void *), void *env);
+       size_t (*sizefunc)(void *), void *env, unsigned int max_node_count);
 
 /** 
  * Free tree and all nodes below
index 9b342d0f46caf5a86c8bc361f14d06ff0e97acd8..2ea394b09f9e91340ce07e050773499c5f4cf0e4 100644 (file)
@@ -130,11 +130,11 @@ get_tree(struct subnet_msg_cache_data *data, struct edns_data *edns,
        struct addrtree *tree;
        if (edns->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
                if (!data->tree4)
-                       data->tree4 = addrtree_create(EDNSSUBNET_MAX_SUBNET_IP4, &delfunc, &sizefunc, env);
+                       data->tree4 = addrtree_create(EDNSSUBNET_MAX_SUBNET_IP4, &delfunc, &sizefunc, env, 0);
                tree = data->tree4;
        } else {
                if (!data->tree6)
-                       data->tree6 = addrtree_create(EDNSSUBNET_MAX_SUBNET_IP6, &delfunc, &sizefunc, env);
+                       data->tree6 = addrtree_create(EDNSSUBNET_MAX_SUBNET_IP6, &delfunc, &sizefunc, env, 0);
                tree = data->tree6;
        }
        return tree;
index 875467eaeab2ff696d516f18e83e78ad13ef06a1..85428939d31e9fb56955fa346b5a99ec4c417d52 100644 (file)
        {
                struct addredge* edge;
                int i, s, byte;
-               if (indent == 0) printf("-----Tree-----");
+               if (indent == 0) printf("-----Tree-----\n");
                if (indent > maxdepth) {
                        printf("\n");
                        return;
                }
-               printf("[node elem:%d]\n", node->elem != NULL);
+               printf("[node elem:%d] (%d)\n", node->elem != NULL, node);
                for (i = 0; i<2; i++) {
                        if (node->edge[i]) {
                                for (s = 0; s < indent; s++) printf(" ");
@@ -165,7 +165,7 @@ static void consistency_test(void)
        unit_show_func("edns-subnet/addrtree.h", "Tree consistency check");
        srand(9195); /* just some value for reproducibility */
 
-       t = addrtree_create(100, &elemfree, NULL, &env);
+       t = addrtree_create(100, &elemfree, NULL, &env, 0);
        count = t->node_count;
        unit_assert(count == 0);
        for (i = 0; i < 1000; i++) {
@@ -181,12 +181,24 @@ static void consistency_test(void)
        }
        addrtree_delete(t);
        unit_show_func("edns-subnet/addrtree.h", "Tree consistency with purge");
-       t = addrtree_create(8, &elemfree, NULL, &env);
+       t = addrtree_create(8, &elemfree, NULL, &env, 0);
+       unit_assert(t->node_count == 0);
+       for (i = 0; i < 1000; i++) {
+               l = randomkey(&k, 128);
+               elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
+               addrtree_insert(t, k, l, 64, elem, i + 10, i);
+               free(k);
+               unit_assert( !addrtree_inconsistent(t) );
+       }
+       addrtree_delete(t);
+       unit_show_func("edns-subnet/addrtree.h", "Tree consistency with limit");
+       t = addrtree_create(8, &elemfree, NULL, &env, 27);
        unit_assert(t->node_count == 0);
        for (i = 0; i < 1000; i++) {
                l = randomkey(&k, 128);
                elem = (struct reply_info *) calloc(1, sizeof(struct reply_info));
                addrtree_insert(t, k, l, 64, elem, i + 10, i);
+               unit_assert( t->node_count <= 27);
                free(k);
                unit_assert( !addrtree_inconsistent(t) );
        }