2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-09-03 16:15:27 +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 */ /* Define to 1 for detailed reference tracing */
#undef DNS_NAMETREE_TRACE #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 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. * for debugging purposes.
* *
* 'type' indicates whether the tree will be used for storing boolean * '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: * 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 * represents a single boolean value, true or false. If the name already
* exists within the tree, then return ISC_R_EXISTS. * 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 * 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 * 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 * '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'. * 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: * Requires:
* *
*\li 'nametree' points to a valid nametree. *\li 'nametree' points to a valid nametree.

View File

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

View File

@@ -39,6 +39,7 @@
dns_nametree_t *booltree = NULL; dns_nametree_t *booltree = NULL;
dns_nametree_t *bitstree = NULL; dns_nametree_t *bitstree = NULL;
dns_nametree_t *counttree = NULL;
/* /*
* Test utilities. In general, these assume input parameters are valid * 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_BOOL, "bool test", &booltree);
dns_nametree_create(mctx, DNS_NAMETREE_BITS, "bits test", &bitstree); 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 */ /* Add a positive boolean node */
dns_test_namefromstring("example.com.", &fn); dns_test_namefromstring("example.com.", &fn);
@@ -73,7 +75,7 @@ create_tables(void) {
assert_int_equal(dns_nametree_add(booltree, name, false), assert_int_equal(dns_nametree_add(booltree, name, false),
ISC_R_SUCCESS); ISC_R_SUCCESS);
/* Add a bitfield nodes under a parent */ /* Add a bitfield node under a parent */
dns_test_namefromstring("sub.example.com.", &fn); dns_test_namefromstring("sub.example.com.", &fn);
assert_int_equal(dns_nametree_add(bitstree, name, 2), ISC_R_SUCCESS); assert_int_equal(dns_nametree_add(bitstree, name, 2), ISC_R_SUCCESS);
} }
@@ -86,6 +88,9 @@ destroy_tables(void) {
if (bitstree != NULL) { if (bitstree != NULL) {
dns_nametree_detach(&bitstree); dns_nametree_detach(&bitstree);
} }
if (counttree != NULL) {
dns_nametree_detach(&counttree);
}
rcu_barrier(); rcu_barrier();
} }
@@ -161,6 +166,42 @@ ISC_RUN_TEST_IMPL(add_bits) {
destroy_tables(); 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) { ISC_RUN_TEST_IMPL(covered_bool) {
dns_fixedname_t fn; dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn); dns_name_t *name = dns_fixedname_name(&fn);
@@ -301,6 +342,7 @@ ISC_RUN_TEST_IMPL(find) {
ISC_TEST_LIST_START ISC_TEST_LIST_START
ISC_TEST_ENTRY(add_bool) ISC_TEST_ENTRY(add_bool)
ISC_TEST_ENTRY(add_bits) ISC_TEST_ENTRY(add_bits)
ISC_TEST_ENTRY(add_count)
ISC_TEST_ENTRY(covered_bool) ISC_TEST_ENTRY(covered_bool)
ISC_TEST_ENTRY(covered_bits) ISC_TEST_ENTRY(covered_bits)
ISC_TEST_ENTRY(delete) ISC_TEST_ENTRY(delete)