Commit f9aae574 authored by Libor Peltan's avatar Libor Peltan Committed by Daniel Salzman

zone load: zonefile-load config option implemented

parent 3b5bd4ef
......@@ -1037,6 +1037,22 @@ char* conf_zonefile_txn(
return get_filename(conf, txn, zone, file);
}
unsigned conf_zonefile_load_txn(
conf_t *conf,
knot_db_txn_t *txn,
const knot_dname_t *zone)
{
conf_val_t val = conf_zone_get_txn(conf, txn, C_ZONEFILE_LOAD, zone);
// obsolete, to be removed
if (val.code == KNOT_ENOENT) {
val = conf_zone_get_txn(conf, txn, C_IXFR_DIFF, zone);
return (conf_bool(&val) ? ZONEFILE_LOAD_DIFF : ZONEFILE_LOAD_WHOLE);
}
return conf_opt(&val);
}
char* conf_old_journalfile(
conf_t *conf,
const knot_dname_t *zone)
......
......@@ -592,6 +592,28 @@ static inline char* conf_zonefile(
return conf_zonefile_txn(conf, &conf->read_txn, zone);
}
/*!
* Gets the way zonefile is loaded.
*
* \param[in] conf Configuration.
* \param[in] txn Configuration DB transaction.
* \param[in] zone Zone name.
*
* \return ZONEFILE_LOAD_*
*/
unsigned conf_zonefile_load_txn(
conf_t *conf,
knot_db_txn_t *txn,
const knot_dname_t *zone
);
static inline unsigned conf_zonefile_load(
conf_t *conf,
const knot_dname_t *zone)
{
return conf_zonefile_load_txn(conf, &conf->read_txn, zone);
}
/*!
* Gets the absolute journal file path.
*
......
......@@ -92,6 +92,13 @@ static const knot_lookup_t journal_content[] = {
{ 0, NULL }
};
static const knot_lookup_t zonefile_load[] = {
{ ZONEFILE_LOAD_NONE, "none" },
{ ZONEFILE_LOAD_DIFF, "difference" },
{ ZONEFILE_LOAD_WHOLE, "whole" },
{ 0, NULL }
};
static const knot_lookup_t log_severities[] = {
{ LOG_UPTO(LOG_CRIT), "critical" },
{ LOG_UPTO(LOG_ERR), "error" },
......@@ -268,7 +275,7 @@ static const yp_item_t desc_policy[] = {
{ C_DISABLE_ANY, YP_TBOOL, YP_VNONE }, \
{ C_ZONEFILE_SYNC, YP_TINT, YP_VINT = { -1, INT32_MAX, 0, YP_STIME } }, \
{ C_JOURNAL_CONTENT, YP_TOPT, YP_VOPT = { journal_content, JOURNAL_CONTENT_CHANGES } }, \
{ C_IXFR_DIFF, YP_TBOOL, YP_VNONE }, \
{ C_ZONEFILE_LOAD, YP_TOPT, YP_VOPT = { zonefile_load, ZONEFILE_LOAD_WHOLE } }, \
{ C_MAX_ZONE_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, SSIZE_MAX, YP_SSIZE }, FLAGS }, \
{ C_MAX_JOURNAL_USAGE, YP_TINT, YP_VINT = { KILO(40), SSIZE_MAX, MEGA(100), YP_SSIZE } }, \
{ C_MAX_JOURNAL_DEPTH, YP_TINT, YP_VINT = { 2, SSIZE_MAX, SSIZE_MAX } }, \
......@@ -284,6 +291,7 @@ static const yp_item_t desc_policy[] = {
/* Obsolete, old journal items. */ \
{ C_JOURNAL, YP_TSTR, YP_VNONE, FLAGS }, \
{ C_MAX_JOURNAL_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, SSIZE_MAX, YP_SSIZE }, FLAGS }, \
{ C_IXFR_DIFF, YP_TBOOL, YP_VNONE }, \
static const yp_item_t desc_template[] = {
{ C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF },
......
......@@ -125,6 +125,7 @@
#define C_VERSION "\x07""version"
#define C_VIA "\x03""via"
#define C_ZONE "\x04""zone"
#define C_ZONEFILE_LOAD "\x0D""zonefile-load"
#define C_ZONEFILE_SYNC "\x0D""zonefile-sync"
#define C_ZSK_LIFETIME "\x0C""zsk-lifetime"
#define C_ZSK_SIZE "\x08""zsk-size"
......@@ -145,6 +146,12 @@ enum {
JOURNAL_CONTENT_ALL = 2,
};
enum {
ZONEFILE_LOAD_NONE = 0,
ZONEFILE_LOAD_DIFF = 1,
ZONEFILE_LOAD_WHOLE = 2,
};
extern const knot_lookup_t acl_actions[];
extern const yp_item_t conf_schema[];
......
......@@ -422,6 +422,14 @@ int check_remote(
int check_template(
knotd_conf_check_args_t *args)
{
conf_val_t ixfr_diff = conf_rawid_get_txn(args->extra->conf, args->extra->txn,
C_TPL, C_IXFR_DIFF, args->id, args->id_len);
if (ixfr_diff.code == KNOT_EOK) {
CONF_LOG(LOG_NOTICE, "IXFR from differences setting is obsolete "
"and will be removed in the next major release, use "
"template.zonefile-load instead");
}
// Stop if the default template.
if (is_default_id(args->id, args->id_len)) {
return KNOT_EOK;
......@@ -461,6 +469,14 @@ int check_zone(
"use zone.dnssec-policy in server configuration instead");
}
conf_val_t ixfr_diff = conf_rawid_get_txn(args->extra->conf, args->extra->txn,
C_ZONE, C_IXFR_DIFF, args->id, args->id_len);
if (ixfr_diff.code == KNOT_EOK) {
CONF_LOG(LOG_NOTICE, "IXFR from differences setting is obsolete "
"and will be removed in the next major release, use "
"zone.zonefile-load instead");
}
return KNOT_EOK;
}
......
......@@ -60,31 +60,36 @@ int event_load(conf_t *conf, zone_t *zone)
conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, zone->name);
unsigned load_from = conf_opt(&val);
// if configured, load journal contents
if (load_from == JOURNAL_CONTENT_ALL && !old_contents_exist) {
int ret = zone_load_from_journal(conf, zone, &journal_conts);
unsigned zf_from = conf_zonefile_load(conf, zone->name);
int ret = KNOT_EOK;
// If configured, load journal contents.
if (load_from == JOURNAL_CONTENT_ALL && !old_contents_exist && zf_from != ZONEFILE_LOAD_WHOLE) {
ret = zone_load_from_journal(conf, zone, &journal_conts);
if (ret != KNOT_EOK) {
journal_conts = NULL;
}
old_serial = zone_contents_serial(journal_conts);
}
// always attempt to load zonefile
time_t mtime;
char *filename = conf_zonefile(conf, zone->name);
int ret = zonefile_exists(filename, &mtime);
bool zonefile_unchanged = (zone->zonefile.exists && zone->zonefile.mtime == mtime);
free(filename);
if (ret == KNOT_EOK) {
ret = zone_load_contents(conf, zone->name, &zf_conts);
// If configured, attempt to load zonefile.
if (zf_from != ZONEFILE_LOAD_NONE) {
time_t mtime;
char *filename = conf_zonefile(conf, zone->name);
ret = zonefile_exists(filename, &mtime);
bool zonefile_unchanged = (zone->zonefile.exists && zone->zonefile.mtime == mtime);
free(filename);
if (ret == KNOT_EOK) {
ret = zone_load_contents(conf, zone->name, &zf_conts);
}
if (ret != KNOT_EOK) {
zf_conts = NULL;
log_zone_warning(zone->name, "failed to parse zonefile (%s)",
knot_strerror(ret));
log_zone_error(zone->name, "failed to parse zonefile (%s)",
knot_strerror(ret));
goto cleanup;
}
}
// if configured and appliable to zonefile, load journal changes
if (ret == KNOT_EOK) {
// If configured and appliable to zonefile, load journal changes.
zone->zonefile.serial = zone_contents_serial(zf_conts);
zone->zonefile.exists = (zf_conts != NULL);
zone->zonefile.mtime = mtime;
......@@ -98,18 +103,18 @@ int event_load(conf_t *conf, zone_t *zone)
if (ret != KNOT_EOK) {
zone_contents_deep_free(&zf_conts);
log_zone_warning(zone->name, "failed to load journal (%s)",
knot_strerror(ret));
knot_strerror(ret));
}
}
}
// if configured contents=all, but not present, store zonefile
// If configured contents=all, but not present, store zonefile.
if (load_from == JOURNAL_CONTENT_ALL &&
journal_conts == NULL && zf_conts != NULL) {
ret = zone_in_journal_store(conf, zone, zf_conts);
if (ret != KNOT_EOK) {
log_zone_warning(zone->name, "failed to write zone-in-journal (%s)",
knot_strerror(ret));
knot_strerror(ret));
}
}
......@@ -117,60 +122,67 @@ int event_load(conf_t *conf, zone_t *zone)
bool dnssec_enable = conf_bool(&val);
zone_update_t up = { 0 };
// create zone_update structure according to current state
// Create zone_update structure according to current state.
if (old_contents_exist) {
if (zf_conts == NULL) {
if (load_from == JOURNAL_CONTENT_ALL) {
// reload does nothing if we use purely journal
ret = KNOT_EOK;
goto cleanup;
} else {
ret = KNOT_ENOENT;
}
} else if (load_from == JOURNAL_CONTENT_NONE) {
ret = KNOT_EOK;
goto cleanup;
} else if (zf_from == ZONEFILE_LOAD_WHOLE) {
ret = zone_update_from_contents(&up, zone, zf_conts, UPDATE_FULL);
} else {
ret = zone_update_from_differences(&up, zone, zone->contents, zf_conts, UPDATE_INCREMENTAL);
if (ret == KNOT_ERANGE || ret == KNOT_ESEMCHECK) {
// when reload invoked, we force new zonefile if IXFR from diff fails
if (load_from != JOURNAL_CONTENT_ALL) {
log_zone_warning(zone->name, (ret == KNOT_ESEMCHECK ?
"failed to create journal entry, zone file changed without "
"SOA serial update" : "IXFR history will be lost, "
"zone file changed, but SOA serial decreased"));
ret = zone_update_from_contents(&up, zone, zf_conts, UPDATE_FULL);
}
}
}
} else {
if (journal_conts != NULL) {
if (journal_conts != NULL && zf_from != ZONEFILE_LOAD_WHOLE) {
if (zf_conts == NULL) {
ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_INCREMENTAL);
} else {
ret = zone_update_from_differences(&up, zone, journal_conts, zf_conts, UPDATE_INCREMENTAL);
zone_contents_deep_free(&journal_conts);
if (ret == KNOT_ESEMCHECK || ret == KNOT_ERANGE) {
log_zone_warning(zone->name, "zonefile changed with SOA serial %s, "
"ignoring zonefile and loading from journal",
(ret == KNOT_ESEMCHECK ? "unupdated" : "decreased"));
zone_contents_deep_free(&zf_conts);
ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_INCREMENTAL);
} else {
zone_contents_deep_free(&journal_conts);
}
}
} else {
if (zf_conts == NULL) {
ret = KNOT_ENOENT;
} else {
ret = zone_update_from_contents(&up, zone, zf_conts, (load_from == JOURNAL_CONTENT_NONE ?
UPDATE_FULL : UPDATE_INCREMENTAL));
ret = zone_update_from_contents(&up, zone, zf_conts,
(load_from == JOURNAL_CONTENT_NONE ?
UPDATE_FULL : UPDATE_INCREMENTAL));
}
}
}
if (ret != KNOT_EOK) {
// TODO do we need some logging ?
switch (ret) {
case KNOT_ENOENT:
if (zone_load_can_bootstrap(conf, zone->name)) {
log_zone_info(zone->name, "zone will be bootstrapped");
} else {
log_zone_info(zone->name, "zone not found");
}
break;
case KNOT_ESEMCHECK:
log_zone_warning(zone->name, "zone file changed without SOA serial update");
break;
case KNOT_ERANGE:
log_zone_warning(zone->name, "zone file changed, but SOA serial decreased");
break;
}
goto cleanup;
}
// the contents are already part of zone_update
// The contents are already part of zone_update.
zf_conts = NULL;
journal_conts = NULL;
// Sign zone using DNSSEC (if configured).
zone_sign_reschedule_t dnssec_refresh = { 0 };
dnssec_refresh.allow_rollover = true;
// Sign zone using DNSSEC if configured.
zone_sign_reschedule_t dnssec_refresh = { .allow_rollover = true };
if (dnssec_enable) {
ret = post_load_dnssec_actions(conf, zone);
if (ret == KNOT_EOK) {
......@@ -182,7 +194,7 @@ int event_load(conf_t *conf, zone_t *zone)
}
}
// commit zone_update back to zone. This includes updating journal, rcu, ...
// Commit zone_update back to zone (including journal update, rcu,...).
ret = zone_update_commit(conf, &up);
zone_update_clear(&up);
if (ret != KNOT_EOK) {
......@@ -190,17 +202,17 @@ int event_load(conf_t *conf, zone_t *zone)
}
uint32_t new_serial = zone_contents_serial(zone->contents);
if (old_contents_exist) {
log_zone_info(zone->name, "loaded, serial %u -> %u",
old_serial, new_serial);
} else {
log_zone_info(zone->name, "loaded, serial %u", new_serial);
}
if (zone->control_update != NULL) {
log_zone_warning(zone->name, "control transaction aborted");
zone_control_clear(zone);
}
if (old_contents_exist) {
log_zone_info(zone->name, "loaded, serial %u -> %u",
old_serial, new_serial);
} else {
log_zone_info(zone->name, "loaded, serial %u", new_serial);
}
if (zone->control_update != NULL) {
log_zone_warning(zone->name, "control transaction aborted");
zone_control_clear(zone);
}
// Schedule depedent events.
const knot_rdataset_t *soa = zone_soa(zone);
......
$ORIGIN serial.
$TTL 3600
@ SOA dns1 hostmaster 2010111212 10800 3600 1209600 7200
NS dns1
NS dns2
dns1 A 192.0.2.1
AAAA 2001:DB8::1
dns2 A 192.0.2.2
AAAA 2001:DB8::2
new-record4 A 1.2.3.4 ; RR tagging the zone version (independent on serial)
......@@ -19,30 +19,29 @@ t.start()
# Load zones
serial = master.zone_wait(zone)
def reload_zone(serial, version):
def reload_zone(serial, version, exp_serial, exp_version):
master.update_zonefile(zone, version)
master.reload()
new_serial = master.zone_wait(zone)
if new_serial != serial:
if new_serial != exp_serial:
set_err("SOA MISMATCH")
detail_log("!Zone '%s' SOA serial %s != %s" % (zone[0].name, new_serial, serial))
detail_log("!Zone '%s' SOA serial %s != %s" % (zone[0].name, new_serial, exp_serial))
return
resp = master.dig("new-record%d.%s" % (version, zone[0].name), "A")
resp = master.dig("new-record%d.%s" % (exp_version, zone[0].name), "A")
resp.check(rcode="NOERROR")
# Zone changes, serial increases (create changeset)
version = 1
serial = serial + 1
reload_zone(serial, version)
reload_zone(serial, version, serial, version)
# Zone changes, serial doesn't change (no new changeset)
version += 1
reload_zone(serial, version)
reload_zone(serial, version, serial, version - 1)
# Zone changes, serial jumps out-of-range (journal is not applicable)
version += 1
serial = serial - 2
reload_zone(serial, version)
reload_zone(serial - 2, version, serial, version - 2)
# Stop master.
master.stop()
......
......@@ -1202,6 +1202,11 @@ class Knot(Server):
s.item_str("journal-content", z.journal_content)
if z.journal_content == "all" and z.masters:
s.item_str("zonefile-load", "none")
elif z.ixfr:
s.item_str("zonefile-load", "difference")
if z.dnssec.enable:
s.item_str("dnssec-signing", "on")
s.item_str("dnssec-policy", z.name)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment