From: Yuri Schaeffer Date: Mon, 7 Oct 2013 15:11:53 +0000 (+0000) Subject: added node purge functionality X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ccde79163429be06bd9754b207a54f3a2350460d;p=thirdparty%2Funbound.git added node purge functionality ttl in tree git-svn-id: file:///svn/unbound/branches/edns-subnet@2972 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/edns-subnet/addrtree.c b/edns-subnet/addrtree.c index 103c00814..46105cdc5 100644 --- a/edns-subnet/addrtree.c +++ b/edns-subnet/addrtree.c @@ -24,18 +24,18 @@ static struct addredge* edge_create(struct addrnode* node, const addrkey_t* addr, addrlen_t addrlen) { size_t n; - struct addredge* edge = (struct addredge*)malloc( sizeof(*edge) ); + struct addredge* edge = (struct addredge*)malloc( sizeof (*edge) ); if (!edge) return NULL; edge->node = node; edge->len = addrlen; n = (size_t)((addrlen / KEYWIDTH) + ((addrlen % KEYWIDTH != 0)?1:0)); /*ceil()*/ - edge->str = (addrkey_t*)calloc(n, sizeof(addrkey_t)); + edge->str = (addrkey_t*)calloc(n, sizeof (addrkey_t)); if (!edge->str) { free(edge); return NULL; } - memcpy(edge->str, addr, n * sizeof(addrkey_t)); + memcpy(edge->str, addr, n * sizeof (addrkey_t)); return edge; } @@ -46,78 +46,99 @@ edge_create(struct addrnode* node, const addrkey_t* addr, addrlen_t addrlen) * @return new addrnode or NULL on failure */ static struct addrnode* -node_create(struct reply_info* elem, addrlen_t scope) +node_create(void* elem, addrlen_t scope, time_t ttl) { - struct addrnode* node = (struct addrnode*)malloc( sizeof(*node) ); + struct addrnode* node = (struct addrnode*)malloc( sizeof (*node) ); if (!node) return NULL; node->elem = elem; node->scope = scope; + node->ttl = ttl; node->edge[0] = NULL; node->edge[1] = NULL; return node; } -struct addrtree* addrtree_create(addrlen_t max_depth, struct module_env* env) +struct addrtree* addrtree_create(addrlen_t max_depth, + void (*delfunc)(void *, void *), size_t (*sizefunc)(void *), void *env) { struct addrtree* tree; - log_assert(env != NULL); - tree = (struct addrtree*)malloc( sizeof(*tree) ); + log_assert(delfunc != NULL); + log_assert(sizefunc != NULL); + tree = (struct addrtree*)malloc( sizeof (*tree) ); if(!tree) return NULL; - tree->root = node_create(NULL, 0); + tree->root = node_create(NULL, 0, 0); if (!tree->root) { free(tree); return NULL; } tree->max_depth = max_depth; + tree->delfunc = delfunc; + tree->sizefunc = sizefunc; tree->env = env; return tree; } /** - * Recursively calculate size of tree from this node on downwards. + * Recursively calculate size of tree from this node on downwards. * */ -static size_t tree_size(const struct addrnode* node) +static size_t tree_size(const struct addrtree* tree, + const struct addrnode* node) { - int i; - size_t s = 0; - - if (!node) return s; - s += sizeof(struct addrnode); - if (node->elem) { - s += sizeof(struct reply_info) - sizeof(struct rrset_ref); - s += node->elem->rrset_count * sizeof(struct rrset_ref); - s += node->elem->rrset_count * sizeof(struct ub_packed_rrset_key*); - } + size_t s, i; + if (!node) return 0; + s = sizeof(struct addrnode); + if (node->elem && tree->sizefunc) + s += tree->sizefunc(node->elem); for (i = 0; i < 2; i++) { if (!node->edge[i]) continue; - s += sizeof(struct addredge); - s += (node->edge[i]->len / KEYWIDTH) + ((node->edge[i]->len % KEYWIDTH)!=0); - s += tree_size(node->edge[i]->node); + s += sizeof (struct addredge); + s += (node->edge[i]->len / KEYWIDTH); + s += (node->edge[i]->len % KEYWIDTH) != 0; + s += tree_size(tree, node->edge[i]->node); } return s; } size_t addrtree_size(const struct addrtree* tree) { - size_t s = 0; - if (tree) { - s += sizeof(struct addrtree); - s += tree_size(tree->root); - } - return s; + if (!tree) return 0; + return sizeof (struct addrtree) + tree_size(tree, tree->root); } -void addrtree_clean_node(const struct addrtree* tree, struct addrnode* node) +static void +clean_node(const struct addrtree* tree, struct addrnode* node) { - if (node->elem) { - reply_info_delete(node->elem, NULL); //YBS niet NULL: regel 244 - node->elem = NULL; + if (!node->elem) return; + tree->delfunc(tree->env, node->elem); + node->elem = NULL; +} + +static void +purge_node(const struct addrtree* tree, struct addrnode* node, + struct addrnode* parentnode, struct addredge* parentedge) +{ + struct addredge *child_edge; + + int childcount = (node->edge[0] != NULL) + (node->edge[1] != NULL); + clean_node(tree, node); + if (childcount == 2 || !parentnode) + return; + log_assert(parentedge); /* parent node implies parent edge */ + if (childcount == 1) { + child_edge = node->edge[0]?node->edge[0]:node->edge[1]; + if (parentedge == parentnode->edge[0]) + parentnode->edge[0] = child_edge; + else + parentnode->edge[1] = child_edge; } + free(parentedge->str); + free(parentedge); } + /** * Free node and all nodes below * @param tree: Tree the node lives in. @@ -139,7 +160,7 @@ freenode_recursive(struct addrtree* tree, struct addrnode* node) } free(edge); } - addrtree_clean_node(tree, node); + clean_node(tree, node); free(node); } @@ -149,11 +170,10 @@ freenode_recursive(struct addrtree* tree, struct addrnode* node) */ void addrtree_delete(struct addrtree* tree) { - if (tree) { - if (tree->root) - freenode_recursive(tree, tree->root); - free(tree); - } + if (!tree) return; + if (tree->root) + freenode_recursive(tree, tree->root); + free(tree); } /** Get N'th bit from address @@ -219,15 +239,14 @@ issub(const addrkey_t* s1, addrlen_t l1, void addrtree_insert(struct addrtree* tree, const addrkey_t* addr, - addrlen_t sourcemask, addrlen_t scope, struct reply_info* elem) + addrlen_t sourcemask, addrlen_t scope, void* elem, time_t ttl) { /*TODO: - * problem: code might delete elem so effectively owns elem. - * but fail is silent and elem may leak. - * plan return NULL or elem on insert to let caller decide. - * Also try again to make tree agnostic of elem type. We need a - * elem_size callback and clean_tree might have to return a list - * of elem or require delete callback */ + * check ttl of visited nodes. + * pass current time as arg + * + * purge_node(tree, node, parent_node, edge); + */ struct addrnode* newnode, *node; struct addredge* edge, *newedge; @@ -249,8 +268,7 @@ addrtree_insert(struct addrtree* tree, const addrkey_t* addr, if (depth == sourcemask) { /* update this node's scope and data */ if (node->elem) - reply_info_parsedelete(node->elem, tree->env->alloc); - //~ reply_info_parsedelete(node->elem, NULL); + tree->delfunc(tree->env, node->elem); node->elem = elem; node->scope = scope; return; @@ -259,7 +277,7 @@ addrtree_insert(struct addrtree* tree, const addrkey_t* addr, edge = node->edge[index]; /* Case 2: New leafnode */ if (!edge) { - newnode = node_create(elem, scope); + newnode = node_create(elem, scope, ttl); node->edge[index] = edge_create(newnode, addr, sourcemask); if (!node->edge[index]) free(newnode); @@ -277,7 +295,7 @@ addrtree_insert(struct addrtree* tree, const addrkey_t* addr, continue; } /* Case 4: split. */ - if (!(newnode = node_create(NULL, 0))) + if (!(newnode = node_create(NULL, 0, 0))) return; if (!(newedge = edge_create(newnode, addr, common))) { free(newnode); @@ -291,10 +309,11 @@ addrtree_insert(struct addrtree* tree, const addrkey_t* addr, /* Data is stored in the node */ newnode->elem = elem; newnode->scope = scope; + newnode->ttl = ttl; } else { /* Data is stored in other leafnode */ node = newnode; - newnode = node_create(elem, scope); + newnode = node_create(elem, scope, ttl); node->edge[index^1] = edge_create(newnode, addr, sourcemask); } return; @@ -303,10 +322,10 @@ addrtree_insert(struct addrtree* tree, const addrkey_t* addr, struct addrnode* addrtree_find(const struct addrtree* tree, const addrkey_t* addr, - addrlen_t sourcemask) + addrlen_t sourcemask, time_t now) { - struct addrnode* node = tree->root; - struct addredge* edge; + struct addrnode *parent_node = NULL, *node = tree->root; + struct addredge *edge = NULL; addrlen_t depth = 0; log_assert(node != NULL); @@ -319,7 +338,9 @@ addrtree_find(const struct addrtree* tree, const addrkey_t* addr, if (depth == node->scope || (node->scope > sourcemask && depth == sourcemask)) { /* Authority indicates it does not have a more precise - * answer or we cannot ask a more specific question */ + * answer or we cannot ask a more specific question. + * If expired don't clean up just now. */ + if (node->ttl < now) return NULL; return node; } } @@ -335,8 +356,10 @@ addrtree_find(const struct addrtree* tree, const addrkey_t* addr, if (!issub(edge->str, edge->len, addr, sourcemask, depth)) return NULL; log_assert(depth < edge->len); + parent_node = node; depth = edge->len; node = edge->node; + } } diff --git a/edns-subnet/addrtree.h b/edns-subnet/addrtree.h index 0f148c5a9..5133b7894 100644 --- a/edns-subnet/addrtree.h +++ b/edns-subnet/addrtree.h @@ -40,13 +40,17 @@ struct addrtree { struct addrnode* root; /** Maximum prefix length we are willing to cache. */ addrlen_t max_depth; - struct module_env* env; + void (*delfunc)(void *, void *); + size_t (*sizefunc)(void *); + void *env; }; struct addrnode { /** Payload of node, may be NULL */ struct reply_info* elem; + /** Abs time in seconds in which elem is meaningful */ + time_t ttl; /** Number of significant bits in address. */ addrlen_t scope; /** A node can have 0-2 edges, set to NULL for unused */ @@ -73,7 +77,9 @@ size_t addrtree_size(const struct addrtree* tree); * @param env: Module environment for alloc information * @return new addrtree or NULL on failure */ -struct addrtree* addrtree_create(addrlen_t max_depth, struct module_env* env); +struct addrtree* +addrtree_create(addrlen_t max_depth, void (*delfunc)(void *, void *), + size_t (*sizefunc)(void *), void *env); /** * Free tree and all nodes below @@ -81,13 +87,6 @@ struct addrtree* addrtree_create(addrlen_t max_depth, struct module_env* env); */ void addrtree_delete(struct addrtree* tree); -/** - * Free data stored at node - * @param tree: Tree the node lives in. - * @param node: Node to be scrubbed - */ -void addrtree_clean_node(const struct addrtree* tree, struct addrnode* node); - /** * Insert an element in the tree. Failures are silent. Sourcemask and * scope might be changed according to local policy. @@ -99,7 +98,7 @@ void addrtree_clean_node(const struct addrtree* tree, struct addrnode* node); * @param elem: data to store in the tree */ void addrtree_insert(struct addrtree* tree, const addrkey_t* addr, - addrlen_t sourcemask, addrlen_t scope, struct reply_info* elem); + addrlen_t sourcemask, addrlen_t scope, void* elem, time_t ttl); /** * Find a node containing an element in the tree. @@ -110,7 +109,7 @@ void addrtree_insert(struct addrtree* tree, const addrkey_t* addr, * @return addrnode or NULL on miss */ struct addrnode* addrtree_find(const struct addrtree* tree, - const addrkey_t* addr, addrlen_t sourcemask); + const addrkey_t* addr, addrlen_t sourcemask, time_t now); /** Wrappers for static functions to unit test */ int unittest_wrapper_addrtree_cmpbit(const addrkey_t* key1, diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index e12dbfbb2..5b6dd48eb 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -29,9 +29,9 @@ /** externally called */ void -subnet_data_delete(void* d, void* ATTR_UNUSED(arg)) +subnet_data_delete(void *d, void *ATTR_UNUSED(arg)) { - struct subnet_msg_cache_data* r; + struct subnet_msg_cache_data *r; r = (struct subnet_msg_cache_data*)d; addrtree_delete(r->tree4); addrtree_delete(r->tree6); @@ -40,10 +40,10 @@ subnet_data_delete(void* d, void* ATTR_UNUSED(arg)) /** externally called */ size_t -msg_cache_sizefunc(void* k, void* d) +msg_cache_sizefunc(void *k, void *d) { - struct msgreply_entry* q = (struct msgreply_entry*)k; - struct subnet_msg_cache_data* r = (struct subnet_msg_cache_data*)d; + struct msgreply_entry *q = (struct msgreply_entry*)k; + struct subnet_msg_cache_data *r = (struct subnet_msg_cache_data*)d; size_t s = sizeof(struct msgreply_entry) + sizeof(struct subnet_msg_cache_data) + q->key.qname_len + lock_get_mem(&q->entry.lock); @@ -54,9 +54,9 @@ msg_cache_sizefunc(void* k, void* d) /** new query for sn */ static int -subnet_new_qstate(struct module_qstate* qstate, int id) +subnet_new_qstate(struct module_qstate *qstate, int id) { - struct subnet_qstate* iq = (struct subnet_qstate*)regional_alloc( + struct subnet_qstate *iq = (struct subnet_qstate*)regional_alloc( qstate->region, sizeof(struct subnet_qstate)); qstate->minfo[id] = iq; if(!iq) @@ -65,9 +65,9 @@ subnet_new_qstate(struct module_qstate* qstate, int id) return 1; } -int subnetmod_init(struct module_env* env, int id) +int subnetmod_init(struct module_env *env, int id) { - struct subnet_env* sn_env = (struct subnet_env*)calloc(1, + struct subnet_env *sn_env = (struct subnet_env*)calloc(1, sizeof(struct subnet_env)); if(!sn_env) { log_err("malloc failure"); @@ -86,9 +86,9 @@ int subnetmod_init(struct module_env* env, int id) return 1; } -void subnetmod_deinit(struct module_env* env, int id) +void subnetmod_deinit(struct module_env *env, int id) { - struct subnet_env* sn_env; + struct subnet_env *sn_env; if(!env || !env->modinfo[id]) return; sn_env = (struct subnet_env*)env->modinfo[id]; @@ -98,52 +98,64 @@ void subnetmod_deinit(struct module_env* env, int id) } /** Tells client that upstream has no/improper support */ -void cp_edns_bad_response(struct edns_data* target, struct edns_data* source) +void cp_edns_bad_response(struct edns_data *target, struct edns_data *source) { target->subnet_scope_mask = 0; target->subnet_source_mask = source->subnet_source_mask; target->subnet_addr_fam = source->subnet_addr_fam; - memcpy(target->subnet_addr, source->subnet_addr, INET6_SIZE); + memcpy(target->subnet_addr, source->subnet_addr, INET6_SIZE); target->subnet_validdata = 1; } +static void delfunc(void *envptr, void *elemptr) { + struct reply_info *elem = (struct reply_info *)elemptr; + struct module_env *env = (struct module_env *)envptr; + reply_info_parsedelete(elem, env->alloc); +} + +static size_t sizefunc(void *elemptr) { + struct reply_info *elem = (struct reply_info *)elemptr; + return sizeof (struct reply_info) - sizeof (struct rrset_ref) + + elem->rrset_count * sizeof (struct rrset_ref) + + elem->rrset_count * sizeof (struct ub_packed_rrset_key *); +} /* select tree from cache entry based on edns data. * If for address family not present it will create a new one. * NULL on failure to create. */ static struct addrtree* -get_tree(struct subnet_msg_cache_data* data, struct edns_data* edns, - struct module_env* env) +get_tree(struct subnet_msg_cache_data *data, struct edns_data *edns, + struct module_env *env) { - struct addrtree* tree; + struct addrtree *tree; if (edns->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) { if (!data->tree4) - data->tree4 = addrtree_create(EDNSSUBNET_MAX_SUBNET_IP4, env); + data->tree4 = addrtree_create(EDNSSUBNET_MAX_SUBNET_IP4, &delfunc, &sizefunc, env); tree = data->tree4; } else { if (!data->tree6) - data->tree6 = addrtree_create(EDNSSUBNET_MAX_SUBNET_IP6, env); + data->tree6 = addrtree_create(EDNSSUBNET_MAX_SUBNET_IP6, &delfunc, &sizefunc, env); tree = data->tree6; } return tree; } -void update_cache(struct module_qstate* qstate, int id) +void update_cache(struct module_qstate *qstate, int id) { - struct msgreply_entry* mrep_entry; - struct addrtree* tree; + struct msgreply_entry *mrep_entry; + struct addrtree *tree; struct reply_info *rep; struct query_info qinf; - struct slabhash* subnet_msg_cache = + struct slabhash *subnet_msg_cache = ((struct subnet_env*)qstate->env->modinfo[id])->subnet_msg_cache; - struct edns_data* edns = &qstate->edns_client_in; + struct edns_data *edns = &qstate->edns_client_in; /** We already calculated hash upon lookup */ hashvalue_t h = qstate->minfo[id] ? ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash : query_info_hash(&qstate->qinfo); /** Step 1, general qinfo lookup */ - struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h, &qstate->qinfo, 1); + struct lruhash_entry *lru_entry = slabhash_lookup(subnet_msg_cache, h, &qstate->qinfo, 1); int acquired_lock = (lru_entry != NULL); if (!lru_entry) { qinf = qstate->qinfo; @@ -177,24 +189,24 @@ void update_cache(struct module_qstate* qstate, int id) rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */ addrtree_insert(tree, (addrkey_t*)edns->subnet_addr, edns->subnet_source_mask, - qstate->edns_server_in.subnet_scope_mask, rep); + qstate->edns_server_in.subnet_scope_mask, rep, rep->ttl); if (acquired_lock) lock_rw_unlock(&lru_entry->lock); } /* return true iff reply is sent. */ -int lookup_and_reply(struct module_qstate* qstate, int id) +int lookup_and_reply(struct module_qstate *qstate, int id) { - struct lruhash_entry* e; - struct module_env* env = qstate->env; - struct subnet_env* sne = (struct subnet_env*)env->modinfo[id]; - struct subnet_qstate* iq = (struct subnet_qstate*)qstate->minfo[id]; + struct lruhash_entry *e; + struct module_env *env = qstate->env; + struct subnet_env *sne = (struct subnet_env*)env->modinfo[id]; + struct subnet_qstate *iq = (struct subnet_qstate*)qstate->minfo[id]; hashvalue_t h = query_info_hash(&qstate->qinfo); - struct subnet_msg_cache_data* data; - struct edns_data* edns = &qstate->edns_client_in; - struct addrtree* tree; - struct addrnode* node; - struct reply_info* rep; + struct subnet_msg_cache_data *data; + struct edns_data *edns = &qstate->edns_client_in; + struct addrtree *tree; + struct addrnode *node; + struct reply_info *rep; if (iq) iq->qinfo_hash = h; /** Might be useful on cache miss */ e = slabhash_lookup(sne->subnet_msg_cache, h, &qstate->qinfo, 0); @@ -207,18 +219,13 @@ int lookup_and_reply(struct module_qstate* qstate, int id) return 0; } node = addrtree_find(tree, (addrkey_t*)edns->subnet_addr, - edns->subnet_source_mask); + edns->subnet_source_mask, *env->now); if (!node) { /** plain old cache miss */ lock_rw_unlock(&e->lock); return 0; } rep = node->elem; - if(rep->ttl < *env->now) { /** msg expired, remove from node */ - addrtree_clean_node(tree, node); - lock_rw_unlock(&e->lock); - return 0; - } - rep = reply_info_copy(rep, env->alloc, NULL); + rep = reply_info_copy(rep, env->alloc, qstate->region); lock_rw_unlock(&e->lock); qstate->return_msg = (struct dns_msg*)regional_alloc( qstate->region, sizeof(struct dns_msg)); @@ -248,7 +255,7 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net) return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]); } -enum module_ext_state eval_response(struct module_qstate* qstate, int id) +enum module_ext_state eval_response(struct module_qstate *qstate, int id) { size_t sn_octs; struct edns_data *c_in = &qstate->edns_client_in; /* rcvd from client */ @@ -307,8 +314,8 @@ enum module_ext_state eval_response(struct module_qstate* qstate, int id) return module_finished; } -void subnetmod_operate(struct module_qstate* qstate, enum module_ev event, - int id, struct outbound_entry* ATTR_UNUSED(outbound)) +void subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + int id, struct outbound_entry *ATTR_UNUSED(outbound)) { verbose(VERB_QUERY, "subnet[module %d] operate: extstate:%s " "event:%s", id, strextstate(qstate->ext_state[id]), @@ -361,18 +368,18 @@ void subnetmod_operate(struct module_qstate* qstate, enum module_ev event, return; } -void subnetmod_clear(struct module_qstate* qstate, int id) +void subnetmod_clear(struct module_qstate *qstate, int id) { /* qstate has no data outside region */ } -void subnetmod_inform_super(struct module_qstate* qstate, int id, - struct module_qstate* super) +void subnetmod_inform_super(struct module_qstate *qstate, int id, + struct module_qstate *super) { /* Not used */ } -size_t subnetmod_get_mem(struct module_env* env, int id) +size_t subnetmod_get_mem(struct module_env *env, int id) { verbose(VERB_ALGO, "subnetmod: get_mem, id: %d, NOTIMPL", id); return 550; @@ -386,7 +393,7 @@ static struct module_func_block subnetmod_block = { &subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem }; -struct module_func_block* subnetmod_get_funcblock(void) +struct module_func_block *subnetmod_get_funcblock(void) { return &subnetmod_block; } diff --git a/testcode/unitvandergaast.c b/testcode/unitvandergaast.c index aca2271d1..ff408ea0b 100644 --- a/testcode/unitvandergaast.c +++ b/testcode/unitvandergaast.c @@ -138,6 +138,13 @@ static int randomkey(addrkey_t **k, int maxlen) return bits; } +static void elemfree(void *envptr, void *elemptr) +{ + struct reply_info *elem = (struct reply_info *)elemptr; + struct module_env *env = (struct module_env *)envptr; + free(elem); +} + static void consistency_test(void) { int i, l, r; @@ -145,16 +152,16 @@ static void consistency_test(void) struct addrtree* t; struct module_env env; struct reply_info *elem; + time_t timenow = 0; unit_show_func("edns-subnet/addrtree.h", "Tree consistency check"); srand(9195); /* just some value for reproducibility */ - env.alloc = NULL; - t = addrtree_create(100, &env); + t = addrtree_create(100, &elemfree, NULL, &env); 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); + addrtree_insert(t, k, l, 64, elem, timenow + 10); free(k); unit_assert( !addrtree_inconsistent(t) ); }