src/knot/zone/adjust.h
src/knot/zone/contents.c
src/knot/zone/contents.h
+src/knot/zone/measure.c
+src/knot/zone/measure.h
src/knot/zone/node.c
src/knot/zone/node.h
src/knot/zone/semantic-check.c
knot/zone/adjust.h \
knot/zone/contents.c \
knot/zone/contents.h \
+ knot/zone/measure.h \
+ knot/zone/measure.c \
knot/zone/node.c \
knot/zone/node.h \
knot/zone/semantic-check.c \
#include "knot/common/log.h"
#include "knot/dnssec/zone-nsec.h"
#include "knot/zone/adds_tree.h"
-
-typedef enum {
- ADJUST_MEASURE_SIZE_NONE = 0,
- ADJUST_MEASURE_SIZE_NORM = 1,
- ADJUST_MEASURE_SIZE_DIFF = -1,
-} adjust_measure_size_t;
+#include "knot/zone/measure.h"
int adjust_cb_flags(zone_node_t *node, const zone_contents_t *zone)
{
zone_node_t *first_node;
const zone_contents_t *zone;
zone_node_t *previous_node;
- size_t zone_size;
- size_t counter_size;
- uint32_t zone_max_ttl;
adjust_cb_t adjust_cb;
bool adjust_prevs;
- adjust_measure_size_t measure_size;
+ measure_t *m;
} zone_adjust_arg_t;
static int adjust_single(zone_node_t *node, void *data)
assert(data != NULL);
zone_adjust_arg_t *args = (zone_adjust_arg_t *)data;
- if (args->measure_size == ADJUST_MEASURE_SIZE_DIFF) {
- node_size(binode_counterpart(node), &args->counter_size);
- }
+
+ knot_measure_node(node, args->m);
if ((node->flags & NODE_FLAGS_DELETED)) {
return KNOT_EOK;
args->previous_node = node;
}
- if (args->measure_size) {
- node_size(node, &args->zone_size);
- }
-
- node_max_ttl(node, &args->zone_max_ttl);
-
return args->adjust_cb(node, args->zone);
}
static int zone_adjust_tree(zone_tree_t *tree, const zone_contents_t *zone, adjust_cb_t adjust_cb,
- ssize_t *tree_size, uint32_t *tree_max_ttl, bool adjust_prevs,
- adjust_measure_size_t measure_size)
+ bool adjust_prevs, measure_t *measure_ctx)
{
if (zone_tree_is_empty(tree)) {
return KNOT_EOK;
zone_adjust_arg_t arg = { 0 };
arg.zone = zone;
- if (tree_size != NULL) {
- arg.zone_size = *tree_size;
- }
- if (tree_max_ttl != NULL) {
- arg.zone_max_ttl = *tree_max_ttl;
- }
arg.adjust_cb = adjust_cb;
arg.adjust_prevs = adjust_prevs;
- arg.measure_size = measure_size;
+ arg.m = measure_ctx;
int ret = zone_tree_apply(tree, adjust_single, &arg);
if (ret != KNOT_EOK) {
arg.first_node->prev = arg.previous_node;
}
- if (tree_size != NULL) {
- *tree_size = arg.zone_size - arg.counter_size;
- }
- if (tree_max_ttl != NULL) {
- *tree_max_ttl = arg.zone_max_ttl;
- }
return KNOT_EOK;
}
-int zone_adjust_contents(zone_contents_t *zone, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_size)
+int zone_adjust_contents(zone_contents_t *zone, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_zone)
{
int ret = zone_contents_load_nsec3param(zone);
if (ret != KNOT_EOK) {
}
zone->dnssec = node_rrtype_is_signed(zone->apex, KNOT_RRTYPE_SOA);
- ssize_t nodes_size = 0;
- uint32_t nodes_max_ttl = 0;
- adjust_measure_size_t ms = (measure_size && nodes_cb != NULL && nsec3_cb != NULL ? ADJUST_MEASURE_SIZE_NORM : ADJUST_MEASURE_SIZE_NONE);
+ measure_t m = knot_measure_init(measure_zone, false);
if (nsec3_cb != NULL) {
- ret = zone_adjust_tree(zone->nsec3_nodes, zone, nsec3_cb, &nodes_size, &nodes_max_ttl, true, ms);
+ ret = zone_adjust_tree(zone->nsec3_nodes, zone, nsec3_cb, true, &m);
}
if (ret == KNOT_EOK && nodes_cb != NULL) {
- ret = zone_adjust_tree(zone->nodes, zone, nodes_cb, &nodes_size, &nodes_max_ttl, true, ms);
+ ret = zone_adjust_tree(zone->nodes, zone, nodes_cb, true, &m);
}
- if (ret == KNOT_EOK && nodes_cb != NULL && nsec3_cb != NULL) {
- if (measure_size) {
- zone->size = nodes_size;
- }
- zone->max_ttl = nodes_max_ttl;
+ if (ret == KNOT_EOK && measure_zone && nodes_cb != NULL && nsec3_cb != NULL) {
+ knot_measure_finish_zone(&m, zone);
}
return ret;
}
-int zone_adjust_update(zone_update_t *update, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_size)
+int zone_adjust_update(zone_update_t *update, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_diff)
{
int ret = KNOT_EOK;
- adjust_measure_size_t ms = (measure_size && nsec3_cb != NULL ? ADJUST_MEASURE_SIZE_DIFF : ADJUST_MEASURE_SIZE_NONE);
- ssize_t nodes_size = 0, nsec3_size = 0;
+ measure_t m = knot_measure_init(false, measure_diff);
if (nsec3_cb != NULL) {
- ret = zone_adjust_tree(update->a_ctx->nsec3_ptrs, update->new_cont, nsec3_cb, &nsec3_size, NULL, false, ms);
+ ret = zone_adjust_tree(update->a_ctx->nsec3_ptrs, update->new_cont, nsec3_cb, false, &m);
}
if (ret == KNOT_EOK && nodes_cb != NULL) {
- ret = zone_adjust_tree(update->a_ctx->node_ptrs, update->new_cont, nodes_cb, &nodes_size, NULL, false, ms);
+ ret = zone_adjust_tree(update->a_ctx->node_ptrs, update->new_cont, nodes_cb, false, &m);
}
- if (ret == KNOT_EOK && measure_size && nodes_cb != NULL && nsec3_cb != NULL) {
- update->new_cont->size += nodes_size + nsec3_size;
+ if (ret == KNOT_EOK && measure_diff && nodes_cb != NULL && nsec3_cb != NULL) {
+ knot_measure_finish_update(&m, update);
}
return ret;
}
* \param zone Zone to be adjusted.
* \param nodes_cb Callback for NORMAL nodes.
* \param nsec3_cb Callback for NSEC3 nodes.
- * \param measure_size While adjusting, count the size of the zone and store it to contents.
+ * \param measure_zone While adjusting, count the size and max TTL of the zone.
*
* \return KNOT_E*
*/
-int zone_adjust_contents(zone_contents_t *zone, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_size);
+int zone_adjust_contents(zone_contents_t *zone, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_zone);
/*!
* \brief Apply callback to nodes affected by the zone update.
* \param update Zone update being finalized.
* \param nodes_cb Callback for NORMAL nodes.
* \param nsec3_cb Callback for NSEC3 nodes.
- * \param measure_size While adjusting, count the size of the zone and store it to contents.
+ * \param measure_diff While adjusting, count the size difference and max TTL change.
*
* \return KNOT_E*
*/
-int zone_adjust_update(zone_update_t *update, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_size);
+int zone_adjust_update(zone_update_t *update, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_diff);
/*!
* \brief Do a general-purpose full update.
--- /dev/null
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "knot/zone/measure.h"
+
+measure_t knot_measure_init(bool measure_whole, bool measure_diff)
+{
+ assert(!measure_whole || !measure_diff);
+ measure_t m = { 0 };
+ if (measure_whole) {
+ m.how_size = MEASURE_SIZE_WHOLE;
+ m.how_ttl = MEASURE_TTL_WHOLE;
+ }
+ if (measure_diff) {
+ m.how_size = MEASURE_SIZE_DIFF;
+ m.how_ttl = MEASURE_TTL_DIFF;
+ }
+ return m;
+}
+
+bool knot_measure_node(zone_node_t *node, measure_t *m)
+{
+ if (m->how_size == MEASURE_SIZE_NONE && (m->how_ttl == MEASURE_TTL_NONE ||
+ (m->how_ttl == MEASURE_TTL_LIMIT && m->max_ttl >= m->limit_max_ttl))) {
+ return false;
+ }
+
+ int rrset_count = node->rrset_count;
+ for (int i = 0; i < rrset_count; i++) {
+ if (m->how_size != MEASURE_SIZE_NONE) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ m->zone_size += knot_rrset_size(&rrset);
+ }
+ if (m->how_ttl != MEASURE_TTL_NONE) {
+ m->max_ttl = MAX(m->max_ttl, node->rrs[i].ttl);
+ }
+ }
+
+ if (m->how_size != MEASURE_SIZE_DIFF && m->how_ttl != MEASURE_TTL_DIFF) {
+ return true;
+ }
+
+ node = binode_counterpart(node);
+ rrset_count = node->rrset_count;
+ for (int i = 0; i < rrset_count; i++) {
+ if (m->how_size == MEASURE_SIZE_DIFF) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ m->zone_size -= knot_rrset_size(&rrset);
+ }
+ if (m->how_ttl == MEASURE_TTL_DIFF) {
+ m->rem_max_ttl = MAX(m->rem_max_ttl, node->rrs[i].ttl);
+ }
+ }
+
+ return true;
+}
+
+static uint32_t re_measure_max_ttl(zone_contents_t *zone, uint32_t limit)
+{
+ measure_t m = {0 };
+ m.how_ttl = MEASURE_TTL_LIMIT;
+ m.limit_max_ttl = limit;
+
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_double_begin(zone->nodes, zone->nsec3_nodes, &it);
+ if (ret != KNOT_EOK) {
+ return limit;
+ }
+
+ while (!zone_tree_it_finished(&it) && knot_measure_node(zone_tree_it_val(&it), &m)) {
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+
+ return m.max_ttl;
+}
+
+void knot_measure_finish_zone(measure_t *m, zone_contents_t *zone)
+{
+ assert(m->how_size == MEASURE_SIZE_WHOLE || m->how_size == MEASURE_SIZE_NONE);
+ assert(m->how_ttl == MEASURE_TTL_WHOLE || m->how_ttl == MEASURE_TTL_NONE);
+ if (m->how_size == MEASURE_SIZE_WHOLE) {
+ zone->size = m->zone_size;
+ }
+ if (m->how_ttl == MEASURE_TTL_WHOLE) {
+ zone->max_ttl = m->max_ttl;
+ }
+}
+
+void knot_measure_finish_update(measure_t *m, zone_update_t *update)
+{
+ switch (m->how_size) {
+ case MEASURE_SIZE_NONE:
+ break;
+ case MEASURE_SIZE_WHOLE:
+ update->new_cont->size = m->zone_size;
+ break;
+ case MEASURE_SIZE_DIFF:
+ update->new_cont->size = update->zone->contents->size + m->zone_size;
+ break;
+ }
+
+ switch (m->how_ttl) {
+ case MEASURE_TTL_NONE:
+ break;
+ case MEASURE_TTL_WHOLE:
+ case MEASURE_TTL_LIMIT:
+ update->new_cont->max_ttl = m->max_ttl;
+ break;
+ case MEASURE_TTL_DIFF:
+ if (m->max_ttl >= update->zone->contents->max_ttl) {
+ update->new_cont->max_ttl = m->max_ttl;
+ } else if (update->zone->contents->max_ttl > m->rem_max_ttl) {
+ update->new_cont->max_ttl = update->zone->contents->max_ttl;
+ } else {
+ update->new_cont->max_ttl = re_measure_max_ttl(update->new_cont, update->zone->contents->max_ttl);
+ }
+ break;
+ }
+}
--- /dev/null
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "knot/updates/zone-update.h"
+
+typedef enum {
+ MEASURE_SIZE_NONE = 0, // don't measure size of zone
+ MEASURE_SIZE_WHOLE, // measure complete size of zone nodes
+ MEASURE_SIZE_DIFF, // measure difference in size for bi-nodes in zone update
+} measure_size_t;
+
+typedef enum {
+ MEASURE_TTL_NONE = 0, // don't measure max TTL of zone records
+ MEASURE_TTL_WHOLE, // measure max TTL among all zone records
+ MEASURE_TTL_DIFF, // check out zone update (bi-nodes) if the max TTL is affected
+ MEASURE_TTL_LIMIT, // measure max TTL whole; stop if a specific value is reached
+} measure_ttl_t;
+
+typedef struct {
+ measure_size_t how_size;
+ measure_ttl_t how_ttl;
+ ssize_t zone_size;
+ uint32_t max_ttl;
+ uint32_t rem_max_ttl;
+ uint32_t limit_max_ttl;
+} measure_t;
+
+/*! \brief Initialize measure struct. */
+measure_t knot_measure_init(bool measure_whole, bool measure_diff);
+
+/*!
+ * \brief Measure one node's size and max TTL, collecting into measure struct.
+ *
+ * \param node Node to be measured.
+ * \param m Measure context with instructions and results.
+ *
+ * \return False if no more measure is needed.
+ * \note You will probably ignore the return value.
+ */
+bool knot_measure_node(zone_node_t *node, measure_t *m);
+
+/*!
+ * \brief Collect the measured results and update the new zone with measured properties.
+ *
+ * \param zone Zone.
+ * \param m Measured results.
+ */
+void knot_measure_finish_zone(measure_t *m, zone_contents_t *zone);
+
+/*!
+ * \brief Collect the measured results and update the new zone with measured properties.
+ *
+ * \param update Zone update with the zone.
+ * \param m Measured results.
+ */
+void knot_measure_finish_update(measure_t *m, zone_update_t *update);
}
return true;
}
-
-void node_size(const zone_node_t *node, size_t *size)
-{
- if (node != NULL) {
- int rrset_count = node->rrset_count;
- for (int i = 0; i < rrset_count; i++) {
- knot_rrset_t rrset = node_rrset_at(node, i);
- *size += knot_rrset_size(&rrset);
- }
- }
-}
-
-void node_max_ttl(const zone_node_t *node, uint32_t *max)
-{
- int rrset_count = node->rrset_count;
- for (int i = 0; i < rrset_count; i++) {
- *max = MAX(*max, node->rrs[i].ttl);
- }
-}
rrset.additional = rr_data->additional;
return rrset;
}
-
-/*!
- * \brief Compute node size.
- *
- * \param node Node in question.
- * \param size In/out: node size will be added to this value.
- */
-void node_size(const zone_node_t *node, size_t *size);
-
-/*!
- * \brief Compute node maximum TTL.
- *
- * \param node Node in question.
- * \param size In/out: this value will be maximalized with max TTL of node rrsets.
- */
-void node_max_ttl(const zone_node_t *node, uint32_t *max);
#include "knot/server/server.h"
static const char *zone_str1 = "test. 600 IN SOA ns.test. m.test. 1 900 300 4800 900 \n";
-static const char *zone_str2 = "test. IN TXT \"test\"\n";
-static const char *add_str = "test. IN TXT \"test2\"\n";
-static const char *del_str = "test. IN TXT \"test\"\n";
-static const char *node_str1 = "node.test. IN TXT \"abc\"\n";
-static const char *node_str2 = "node.test. IN TXT \"def\"\n";
+static const char *zone_str2 = "test. 600 IN TXT \"test\"\n";
+static const char *add_str = "test. 600 IN TXT \"test2\"\n";
+static const char *del_str = "test. 600 IN TXT \"test\"\n";
+static const char *node_str1 = "node.test. 601 IN TXT \"abc\"\n";
+static const char *node_str2 = "node.test. 601 IN TXT \"def\"\n";
knot_rrset_t rrset;
ret = zone_update_commit(conf(), &update);
node = zone_contents_find_node_for_rr(zone->contents, &rrset);
rrset_present = node_contains_rr(node, &rrset);
- ok(ret == KNOT_EOK && rrset_present, "full zone update: commit");
+ ok(ret == KNOT_EOK && rrset_present, "full zone update: commit (max TTL: %u)", zone->contents->max_ttl);
test_zone_unified(zone);
knot_rdataset_clear(&rrset.rrs, NULL);
size_t zone_size1 = zone->contents->size;
+ uint32_t zone_max_ttl1 = zone->contents->max_ttl;
ret = zone_adjust_full(zone->contents);
ok(ret == KNOT_EOK, "zone adjust full shall work");
size_t zone_size2 = zone->contents->size;
- ok(zone_size1 == zone_size2, "zone size measured the same by incremental and full way (%zu, %zu)", zone_size1, zone_size2);
+ uint32_t zone_max_ttl2 = zone->contents->max_ttl;
+ ok(zone_size1 == zone_size2, "zone size measured the same incremental vs full (%zu, %zu)", zone_size1, zone_size2);
+ ok(zone_max_ttl1 == zone_max_ttl2, "zone max TTL measured the same incremental vs full (%u, %u)", zone_max_ttl1, zone_max_ttl2);
// TODO test more things after re-adjust, search for non-unified bi-nodes
}