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;
}
* @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.
}
free(edge);
}
- addrtree_clean_node(tree, node);
+ clean_node(tree, node);
free(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
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;
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;
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);
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);
/* 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;
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);
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;
}
}
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;
+
}
}
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 */
* @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
*/
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.
* @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.
* @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,
/** 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);
/** 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);
/** 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)
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");
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];
}
/** 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;
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);
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));
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 */
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]),
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;
&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;
}
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;
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) );
}