2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 14:35:26 +00:00

add semantics to name trees to support counters

name trees can now also hold trees of counters. each time a name
dns_nametree_add() is called with a given name, the counter for that
name is incremented; the name is not deleted until dns_nametree_delete()
is called the same number of times.

this is meant to be used for synth-from-dnssec, which is incremented for
each key defined at a name, and decremented when a key is removed, the
name must continue to exist until the number of keys has reached zero.
This commit is contained in:
Evan Hunt
2023-08-16 23:26:50 -07:00
committed by Ondřej Surý
parent bc3fd1a2ef
commit 0ebaa26da7
3 changed files with 125 additions and 24 deletions

View File

@@ -40,7 +40,11 @@
/* Define to 1 for detailed reference tracing */
#undef DNS_NAMETREE_TRACE
typedef enum { DNS_NAMETREE_BOOL, DNS_NAMETREE_BITS } dns_nametree_type_t;
typedef enum {
DNS_NAMETREE_BOOL,
DNS_NAMETREE_BITS,
DNS_NAMETREE_COUNT
} dns_nametree_type_t;
ISC_LANG_BEGINDECLS
@@ -54,7 +58,8 @@ dns_nametree_create(isc_mem_t *mctx, dns_nametree_type_t type, const char *name,
* for debugging purposes.
*
* 'type' indicates whether the tree will be used for storing boolean
* values (DNS_NAMETREE_BOOL) or bitfields (DNS_NAMETREE_BITS).
* values (DNS_NAMETREE_BOOL), bitfields (DNS_NAMETREE_BITS), or counters
* (DNS_NAMETREE_COUNT).
*
* Requires:
*
@@ -72,6 +77,12 @@ dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *name,
* represents a single boolean value, true or false. If the name already
* exists within the tree, then return ISC_R_EXISTS.
*
* If the nametree type was set to DNS_NAMETREE_COUNT, then 'value'
* can only be true. Each time the same name is added to the tree,
* ISC_R_SUCCESS is returned and a counter is incremented.
* dns_nametree_delete() must be deleted the same number of times
* as dns_nametree_add() before the name is removed from the tree.
*
* If the nametree type was set to DNS_NAMETREE_BITS, then 'value' is
* a bit number within a bit field, which is sized to accomodate at least
* 'value' bits. If the name already exists, then that bit will be set
@@ -96,6 +107,10 @@ dns_nametree_delete(dns_nametree_t *nametree, const dns_name_t *name);
/*%<
* Delete 'name' from 'nametree'.
*
* If the nametree type was set to DNS_NAMETREE_COUNT, then this must
* be called for each name the same number of times as dns_nametree_add()
* was called before the name is removed.
*
* Requires:
*
*\li 'nametree' points to a valid nametree.

View File

@@ -67,7 +67,8 @@ static void
destroy_ntnode(dns_ntnode_t *node) {
isc_refcount_destroy(&node->references);
if (node->bits != NULL) {
isc_mem_cput(node->mctx, node->bits, 8, sizeof(uint32_t));
isc_mem_cput(node->mctx, node->bits, node->bits[0],
sizeof(char));
}
isc_mem_putanddetach(&node->mctx, node, sizeof(dns_ntnode_t));
}
@@ -103,8 +104,20 @@ dns_nametree_create(isc_mem_t *mctx, dns_nametree_type_t type, const char *name,
static void
destroy_nametree(dns_nametree_t *nametree) {
/* dns_qpread_t qpr; */
/* dns_qpiter_t iter; */
/* void *pval = NULL; */
nametree->magic = 0;
/* dns_qpmulti_query(nametree->table, &qpr); */
/* dns_qpiter_init(&qpr, &iter); */
/* while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) { */
/* dns_ntnode_t *n = pval; */
/* dns_ntnode_detach(&n); */
/* } */
/* dns_qpread_destroy(nametree->table, &qpr); */
dns_qpmulti_destroy(&nametree->table);
isc_refcount_destroy(&nametree->references);
@@ -132,10 +145,10 @@ newnode(isc_mem_t *mctx, const dns_name_t *name) {
static bool
matchbit(unsigned char *bits, uint32_t val) {
unsigned int len = val / 8;
unsigned int len = val / 8 + 2;
unsigned int mask = 1 << (val % 8);
if ((bits[len] & mask) != 0) {
if (len <= bits[0] && (bits[len - 1] & mask) != 0) {
return (true);
}
return (false);
@@ -146,7 +159,7 @@ dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *name,
uint32_t value) {
isc_result_t result;
dns_qp_t *qp = NULL;
unsigned int len, mask;
uint32_t size, pos, mask, count = 0;
dns_ntnode_t *old = NULL, *new = NULL;
REQUIRE(VALID_NAMETREE(nametree));
@@ -160,32 +173,44 @@ dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *name,
new->set = value;
break;
case DNS_NAMETREE_COUNT:
new = newnode(nametree->mctx, name);
new->set = true;
result = dns_qp_deletename(qp, name, (void **)&old, &count);
if (result == ISC_R_SUCCESS) {
count += 1;
}
break;
case DNS_NAMETREE_BITS:
result = dns_qp_getname(qp, name, (void **)&old, NULL);
if (result == ISC_R_SUCCESS && matchbit(old->bits, value)) {
goto out;
}
len = value / 8;
size = pos = value / 8 + 2;
mask = 1 << (value % 8);
if (old != NULL && old->bits[0] > pos) {
size = old->bits[0];
}
new = newnode(nametree->mctx, name);
new->bits = isc_mem_cget(nametree->mctx, 8, sizeof(value));
new->bits = isc_mem_cget(nametree->mctx, size, sizeof(char));
if (result == ISC_R_SUCCESS) {
INSIST(old != NULL);
memmove(new->bits, old->bits, old->bits[0]);
result = dns_qp_deletename(qp, name, NULL, NULL);
INSIST(result == ISC_R_SUCCESS);
}
new->bits[len] |= mask;
new->bits[pos - 1] |= mask;
new->bits[0] = size;
break;
default:
UNREACHABLE();
}
result = dns_qp_insert(qp, new, 0);
result = dns_qp_insert(qp, new, count);
/*
* We detach the node here, so any dns_qp_deletename() will
* destroy the node directly.
@@ -202,16 +227,30 @@ isc_result_t
dns_nametree_delete(dns_nametree_t *nametree, const dns_name_t *name) {
isc_result_t result;
dns_qp_t *qp = NULL;
void *pval = NULL;
dns_ntnode_t *old = NULL;
uint32_t count;
REQUIRE(VALID_NAMETREE(nametree));
REQUIRE(name != NULL);
dns_qpmulti_write(nametree->table, &qp);
result = dns_qp_deletename(qp, name, &pval, NULL);
if (result == ISC_R_SUCCESS) {
dns_ntnode_t *n = pval;
dns_ntnode_detach(&n);
result = dns_qp_deletename(qp, name, (void **)&old, &count);
switch (nametree->type) {
case DNS_NAMETREE_BOOL:
case DNS_NAMETREE_BITS:
break;
case DNS_NAMETREE_COUNT:
if (result == ISC_R_SUCCESS && count-- != 0) {
dns_ntnode_t *new = newnode(nametree->mctx, name);
new->set = true;
result = dns_qp_insert(qp, new, count);
INSIST(result == ISC_R_SUCCESS);
dns_ntnode_detach(&new);
}
break;
default:
UNREACHABLE();
}
dns_qp_compact(qp, DNS_QPGC_MAYBE);
dns_qpmulti_commit(nametree->table, &qp);
@@ -223,18 +262,17 @@ isc_result_t
dns_nametree_find(dns_nametree_t *nametree, const dns_name_t *name,
dns_ntnode_t **ntnodep) {
isc_result_t result;
dns_ntnode_t *node = NULL;
dns_qpread_t qpr;
void *pval = NULL;
REQUIRE(VALID_NAMETREE(nametree));
REQUIRE(name != NULL);
REQUIRE(ntnodep != NULL && *ntnodep == NULL);
dns_qpmulti_query(nametree->table, &qpr);
result = dns_qp_getname(&qpr, name, &pval, NULL);
result = dns_qp_getname(&qpr, name, (void **)&node, NULL);
if (result == ISC_R_SUCCESS) {
dns_ntnode_t *knode = pval;
dns_ntnode_attach(knode, ntnodep);
dns_ntnode_attach(node, ntnodep);
}
dns_qpread_destroy(nametree->table, &qpr);
@@ -258,10 +296,16 @@ dns_nametree_covered(dns_nametree_t *nametree, const dns_name_t *name,
dns_qpmulti_query(nametree->table, &qpr);
result = dns_qp_findname_ancestor(&qpr, name, 0, (void **)&node, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
if (nametree->type == DNS_NAMETREE_BOOL) {
switch (nametree->type) {
case DNS_NAMETREE_BOOL:
ret = node->set;
} else {
break;
case DNS_NAMETREE_COUNT:
ret = true;
break;
case DNS_NAMETREE_BITS:
ret = matchbit(node->bits, bit);
break;
}
}

View File

@@ -39,6 +39,7 @@
dns_nametree_t *booltree = NULL;
dns_nametree_t *bitstree = NULL;
dns_nametree_t *counttree = NULL;
/*
* Test utilities. In general, these assume input parameters are valid
@@ -55,6 +56,7 @@ create_tables(void) {
dns_nametree_create(mctx, DNS_NAMETREE_BOOL, "bool test", &booltree);
dns_nametree_create(mctx, DNS_NAMETREE_BITS, "bits test", &bitstree);
dns_nametree_create(mctx, DNS_NAMETREE_COUNT, "count test", &counttree);
/* Add a positive boolean node */
dns_test_namefromstring("example.com.", &fn);
@@ -73,7 +75,7 @@ create_tables(void) {
assert_int_equal(dns_nametree_add(booltree, name, false),
ISC_R_SUCCESS);
/* Add a bitfield nodes under a parent */
/* Add a bitfield node under a parent */
dns_test_namefromstring("sub.example.com.", &fn);
assert_int_equal(dns_nametree_add(bitstree, name, 2), ISC_R_SUCCESS);
}
@@ -86,6 +88,9 @@ destroy_tables(void) {
if (bitstree != NULL) {
dns_nametree_detach(&bitstree);
}
if (counttree != NULL) {
dns_nametree_detach(&counttree);
}
rcu_barrier();
}
@@ -161,6 +166,42 @@ ISC_RUN_TEST_IMPL(add_bits) {
destroy_tables();
}
ISC_RUN_TEST_IMPL(add_count) {
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
create_tables();
/* add a counter node five times */
dns_test_namefromstring("example.com.", &fn);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
/* delete it five times, checking coverage each time */
assert_true(dns_nametree_covered(counttree, name, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(counttree, name, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(counttree, name, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(counttree, name, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(counttree, name, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_false(dns_nametree_covered(counttree, name, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_NOTFOUND);
destroy_tables();
}
ISC_RUN_TEST_IMPL(covered_bool) {
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
@@ -301,6 +342,7 @@ ISC_RUN_TEST_IMPL(find) {
ISC_TEST_LIST_START
ISC_TEST_ENTRY(add_bool)
ISC_TEST_ENTRY(add_bits)
ISC_TEST_ENTRY(add_count)
ISC_TEST_ENTRY(covered_bool)
ISC_TEST_ENTRY(covered_bits)
ISC_TEST_ENTRY(delete)