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

dnssec+slave: storing master serial in kasp_db, handling when xfr

parent 90cb18ee
......@@ -35,6 +35,7 @@ typedef enum {
KASPDBKEY_POLICYLAST = 0x2,
KASPDBKEY_NSEC3SALT = 0x3,
KASPDBKEY_NSEC3TIME = 0x4,
KASPDBKEY_MASTERSERIAL = 0x5,
} keyclass_t;
static const knot_db_api_t *db_api = NULL;
......@@ -105,6 +106,17 @@ int kasp_db_reconfigure(kasp_db_t **db, const char *new_path, size_t new_mapsize
return KNOT_EOK;
}
bool kasp_db_exists(kasp_db_t *db)
{
if (db->keys_db == NULL) {
struct stat st;
if (stat(db->db_path, &st) != 0) {
return false;
}
}
return true;
}
int kasp_db_open(kasp_db_t *db)
{
if (db == NULL || db->db_path == NULL) {
......@@ -554,6 +566,46 @@ int kasp_db_load_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name,
return ret;
}
int kasp_db_store_master_serial(kasp_db_t *db, const knot_dname_t *zone_name,
uint32_t master_serial)
{
if (db == NULL || db->keys_db == NULL || zone_name == NULL) {
return KNOT_EINVAL;
}
uint32_t ms_be = htobe32(master_serial);
with_txn(KEYS_RW, NULL);
knot_db_val_t key = make_key(KASPDBKEY_MASTERSERIAL, zone_name, NULL);
knot_db_val_t val = { .len = sizeof(uint32_t), .data = &ms_be };
ret = db_api->insert(txn, &key, &val, 0);
free_key(&key);
with_txn_end(NULL);
return ret;
}
int kasp_db_load_master_serial(kasp_db_t *db, const knot_dname_t *zone_name,
uint32_t *master_serial)
{
if (db == NULL || db->keys_db == NULL ||
zone_name == NULL || master_serial == NULL) {
return KNOT_EINVAL;
}
with_txn(KEYS_RO, NULL);
knot_db_val_t key = make_key(KASPDBKEY_MASTERSERIAL, zone_name, NULL), val = { 0 };
ret = db_api->find(txn, &key, &val, 0);
free_key(&key);
if (ret == KNOT_EOK) {
if (val.len == sizeof(uint32_t)) {
*master_serial = be32toh(*(uint32_t *)val.data);
}
else {
ret = KNOT_EMALF;
}
}
with_txn_end(NULL);
return ret;
}
int kasp_db_get_policy_last(kasp_db_t *db, const char *policy_string, knot_dname_t **lp_zone,
char **lp_keyid)
{
......
......@@ -59,6 +59,13 @@ int kasp_db_init(kasp_db_t **db, const char *path, size_t mapsize);
*/
int kasp_db_reconfigure(kasp_db_t **db, const char *new_path, size_t new_mapsize);
/*!
* \brief Determine if kasp_db possibly exists at all.
*
* This is useful to avoid creating kasp_db by opening it just to check if anything is there.
*/
bool kasp_db_exists(kasp_db_t *db);
/*!
* \brief Perform real ctreate/open of KASP db.
*/
......@@ -154,6 +161,30 @@ int kasp_db_store_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name,
int kasp_db_load_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name,
dnssec_binary_t *nsec3salt, knot_time_t *salt_created);
/*!
* \brief Store SOA serial number of master.
*
* \param db KASP db
* \param zone_name zone name
* \param master_serial new master's serial
*
* \return KNOT_E*
*/
int kasp_db_store_master_serial(kasp_db_t *db, const knot_dname_t *zone_name,
uint32_t master_serial);
/*!
* \brief Load saved SOA serial number of master.
*
* \param db KASP db
* \param zone_name zone name
* \param master_serial output if KNOT_EOK: master's serial
*
* \return KNOT_E* (KNOT_ENOENT if not stored before)
*/
int kasp_db_load_master_serial(kasp_db_t *db, const knot_dname_t *zone_name,
uint32_t *master_serial);
/*!
* \brief For given policy name, obtain last generated key.
*
......
......@@ -16,7 +16,6 @@
#include <assert.h>
#include <stdint.h>
#include <urcu.h>
#include "contrib/mempattern.h"
#include "contrib/trim.h"
......@@ -208,34 +207,6 @@ static void axfr_cleanup(struct refresh_data *data)
zone_contents_deep_free(&data->axfr.zone);
}
/*! \brief Routine for calling call_rcu() easier way.
*
* TODO: move elsewhere, as it has no direct relation to AXFR
*/
typedef struct {
struct rcu_head rcuhead;
void (*ptr_free_fun)(void **);
void *ptr;
} callrcu_wrapper_t;
static void callrcu_wrapper_cb(struct rcu_head *param)
{
callrcu_wrapper_t *wrap = (callrcu_wrapper_t *)param;
wrap->ptr_free_fun(&wrap->ptr);
free(wrap);
}
/* note: does nothing if not-enough-memory */
static void callrcu_wrapper(void *ptr, void (*ptr_free_fun)(void **))
{
callrcu_wrapper_t *wrap = calloc(1, sizeof(callrcu_wrapper_t));
if (wrap != NULL) {
wrap->ptr = ptr;
wrap->ptr_free_fun = ptr_free_fun;
call_rcu((struct rcu_head *)wrap, callrcu_wrapper_cb);
}
}
static int axfr_finalize(struct refresh_data *data)
{
zone_contents_t *new_zone = data->axfr.zone;
......@@ -250,6 +221,15 @@ static int axfr_finalize(struct refresh_data *data)
return ret;
}
uint32_t master_serial = zone_contents_serial(new_zone);
uint32_t local_serial = zone_contents_serial(data->zone->contents);
bool bootstrap = (data->soa == NULL);
if (!bootstrap && serial_compare(master_serial, local_serial) <= 0) {
conf_val_t val = conf_zone_get(data->conf, C_SERIAL_POLICY, data->zone->name);
local_serial = serial_next(local_serial, conf_opt(&val));
zone_contents_set_soa_serial(new_zone, local_serial);
}
zone_update_t up = { 0 };
ret = zone_update_from_contents(&up, data->zone, new_zone, UPDATE_FULL);
if (ret != KNOT_EOK) {
......@@ -265,9 +245,7 @@ static int axfr_finalize(struct refresh_data *data)
zone_update_clear(&up);
return ret;
}
// no dnssec replanning, next dnssec action applies on next refresh
zone_events_schedule_at(data->zone, ZONE_EVENT_PARENT_DS_Q,
resch.plan_ds_query ? time(NULL) : -1);
event_dnssec_reschedule(data->conf, data->zone, &resch, true);
}
ret = zone_update_commit(data->conf, &up);
......@@ -276,6 +254,14 @@ static int axfr_finalize(struct refresh_data *data)
return ret;
}
if (dnssec_enable) {
ret = zone_set_master_serial(data->zone, master_serial);
if (ret != KNOT_EOK) {
log_zone_warning(data->zone->name,
"unable to save master serial, future transfers might be broken");
}
}
xfr_log_publish(data->zone->name, data->remote, data->zone->contents, new_zone);
data->axfr.zone = NULL; // seized
......@@ -415,6 +401,22 @@ static void ixfr_cleanup(struct refresh_data *data)
static int ixfr_finalize(struct refresh_data *data)
{
uint32_t master_serial;
(void)zone_get_master_serial(data->zone, &master_serial);
uint32_t local_serial = zone_contents_serial(data->zone->contents);
changeset_t *chs = NULL;
WALK_LIST(chs, data->ixfr.changesets) {
master_serial = knot_soa_serial(&chs->soa_to->rrs);
knot_soa_serial_set(&chs->soa_from->rrs, local_serial);
if (serial_compare(master_serial, local_serial) <= 0) {
conf_val_t val = conf_zone_get(data->conf, C_SERIAL_POLICY, data->zone->name);
local_serial = serial_next(local_serial, conf_opt(&val));
knot_soa_serial_set(&chs->soa_to->rrs, local_serial);
} else {
local_serial = master_serial;
}
}
zone_update_t up = { 0 };
int ret = zone_update_init(&up, data->zone, UPDATE_INCREMENTAL | UPDATE_STRICT);
if (ret != KNOT_EOK) {
......@@ -448,14 +450,19 @@ static int ixfr_finalize(struct refresh_data *data)
zone_update_clear(&up);
return ret;
}
// no dnssec replanning, next dnssec action applies on next refresh
zone_events_schedule_at(data->zone, ZONE_EVENT_PARENT_DS_Q,
resch.plan_ds_query ? time(NULL) : -1);
event_dnssec_reschedule(data->conf, data->zone, &resch, true);
}
ret = zone_update_commit(data->conf, &up);
if (ret == KNOT_EOK) {
if (dnssec_enable && !EMPTY_LIST(data->ixfr.changesets)) {
ret = zone_set_master_serial(data->zone, master_serial);
if (ret != KNOT_EOK) {
log_zone_warning(data->zone->name,
"unable to save master serial, future transfers might be broken");
}
}
xfr_log_publish(data->zone->name, data->remote, up.zone->contents, up.new_cont);
} else {
IXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote,
......@@ -702,7 +709,9 @@ static int ixfr_consume(knot_pkt_t *pkt, struct refresh_data *data)
if (data->ixfr.proc == NULL) {
const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
data->xfr_type = determine_xfr_type(answer, knot_soa_serial(&data->soa->rrs),
uint32_t master_serial = knot_soa_serial(&data->soa->rrs);
(void)zone_get_master_serial(data->zone, &master_serial);
data->xfr_type = determine_xfr_type(answer, master_serial,
data->initial_soa_copy);
switch (data->xfr_type) {
case XFR_TYPE_ERROR:
......@@ -808,6 +817,7 @@ static int soa_query_consume(knot_layer_t *layer, knot_pkt_t *pkt)
}
uint32_t local_serial = knot_soa_serial(&data->soa->rrs);
(void)zone_get_master_serial(data->zone, &local_serial);
uint32_t remote_serial = knot_soa_serial(&rr->rrs);
bool current = serial_is_current(local_serial, remote_serial);
bool master_uptodate = serial_is_current(remote_serial, local_serial);
......@@ -837,8 +847,17 @@ static int transfer_produce(knot_layer_t *layer, knot_pkt_t *pkt)
if (ixfr) {
assert(data->soa);
knot_rrset_t *sending_soa = knot_rrset_copy(data->soa, data->mm);
uint32_t master_serial;
int ret = zone_get_master_serial(data->zone, &master_serial);
if (sending_soa == NULL || ret != KNOT_EOK) {
knot_rrset_free(&sending_soa, data->mm);
return KNOT_STATE_FAIL;
}
knot_soa_serial_set(&sending_soa->rrs, master_serial);
knot_pkt_begin(pkt, KNOT_AUTHORITY);
knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, data->soa, 0);
knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, sending_soa, 0);
knot_rrset_free(&sending_soa, data->mm);
}
query_put_edns(pkt, &data->edns);
......
......@@ -69,6 +69,7 @@ int notify_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
if (soa->type == KNOT_RRTYPE_SOA) {
uint32_t serial = knot_soa_serial(&soa->rrs);
uint32_t zone_serial = zone_contents_serial(zone->contents);
(void)zone_get_master_serial(zone, &zone_serial);
NOTIFY_LOG(LOG_INFO, qdata, "received, serial %u", serial);
if (serial_equal(serial, zone_serial)) {
// NOTIFY serial == zone serial => ignore, keep timers
......
......@@ -1152,6 +1152,14 @@ uint32_t zone_contents_serial(const zone_contents_t *zone)
return knot_soa_serial(soa);
}
void zone_contents_set_soa_serial(zone_contents_t *zone, uint32_t new_serial)
{
knot_rdataset_t *soa;
if (zone != NULL && (soa = node_rdataset(zone->apex, KNOT_RRTYPE_SOA)) != NULL) {
knot_soa_serial_set(soa, new_serial);
}
}
bool zone_contents_is_signed(const zone_contents_t *zone)
{
return node_rrtype_is_signed(zone->apex, KNOT_RRTYPE_SOA);
......
......@@ -252,6 +252,16 @@ void zone_contents_deep_free(zone_contents_t **contents);
*/
uint32_t zone_contents_serial(const zone_contents_t *zone);
/*!
* \brief Adjust zone serial.
*
* Works only if there is a SOA in given contents.
*
* \param zone Zone.
* \param new_serial New serial to be set.
*/
void zone_contents_set_soa_serial(zone_contents_t *zone, uint32_t new_serial);
/*!
* \brief Return true if zone is signed.
*/
......
......@@ -21,6 +21,7 @@
#include "knot/common/log.h"
#include "knot/conf/module.h"
#include "knot/dnssec/kasp/kasp_db.h"
#include "knot/nameserver/process_query.h"
#include "knot/query/requestor.h"
#include "knot/updates/zone-update.h"
......@@ -634,3 +635,30 @@ int zone_dump_to_dir(conf_t *conf, zone_t *zone, const char *dir)
return zonefile_write(target, zone->contents);
}
int zone_set_master_serial(zone_t *zone, uint32_t serial)
{
int ret = kasp_db_open(*kaspdb());
if (ret == KNOT_EOK) {
ret = kasp_db_store_master_serial(*kaspdb(), zone->name, serial);
}
return ret;
}
int zone_get_master_serial(zone_t *zone, uint32_t *serial)
{
if (!kasp_db_exists(*kaspdb())) {
*serial = zone_contents_serial(zone->contents);
return KNOT_EOK;
}
int ret = kasp_db_open(*kaspdb());
if (ret != KNOT_EOK) {
return ret;
}
ret = kasp_db_load_master_serial(*kaspdb(), zone->name, serial);
if (ret == KNOT_ENOENT) {
*serial = zone_contents_serial(zone->contents);
return KNOT_EOK;
}
return ret;
}
......@@ -183,4 +183,8 @@ size_t zone_update_dequeue(zone_t *zone, list_t *updates);
/*! \brief Write zone contents to zonefile, but into different directory. */
int zone_dump_to_dir(conf_t *conf, zone_t *zone, const char *dir);
int zone_set_master_serial(zone_t *zone, uint32_t serial);
int zone_get_master_serial(zone_t *zone, uint32_t *serial);
/*! @} */
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