Skip to content
Snippets Groups Projects
Commit d94b5788 authored by David Vasek's avatar David Vasek Committed by Daniel Salzman
Browse files

refresh: improve EDNS Expire (RFC 7314, section 4, second paragraph)

parent 3f3929df
No related branches found
No related tags found
1 merge request!1433RFC 7314 (EDNS Expire) implementation
......@@ -164,14 +164,28 @@ static time_t bootstrap_next(const zone_timers_t *timers)
return interval;
}
static void consume_edns_expire(struct refresh_data *data, knot_pkt_t *pkt)
/*!
* \brief Modify the expire timer wrt the received EDNS EXPIRE (RFC 7314, section 4)
*
* \param data The refresh data.
* \param pkt A received packet to parse.
* \param strictly_follow Strictly use EDNS EXPIRE as the expire timer value.
* (false == RFC 7314, section 4, second paragraph,
* true == third paragraph)
*/
static void consume_edns_expire(struct refresh_data *data, knot_pkt_t *pkt, bool strictly_follow)
{
uint8_t *expire_opt = knot_pkt_edns_option(pkt, KNOT_EDNS_OPTION_EXPIRE);
if (expire_opt != NULL && knot_edns_opt_get_length(expire_opt) == sizeof(uint32_t)) {
data->expire_timer = knot_wire_read_u32(knot_edns_opt_get_data(expire_opt));
uint32_t edns_expire = knot_wire_read_u32(knot_edns_opt_get_data(expire_opt));
data->expire_timer = strictly_follow ? edns_expire :
MAX(edns_expire, data->zone->timers.next_expire - time(NULL));
}
}
/*!
* \brief RFC 7314, section 4, fourth paragraph
*/
static void finalize_edns_expire(struct refresh_data *data)
{
data->expire_timer = MIN(data->expire_timer, zone_soa_expire(data->zone));
......@@ -869,8 +883,10 @@ static int ixfr_consume(knot_pkt_t *pkt, struct refresh_data *data)
"receiving AXFR-style IXFR");
return axfr_consume(pkt, data);
case XFR_TYPE_UPTODATE:
consume_edns_expire(data, pkt, false);
finalize_edns_expire(data);
IXFRIN_LOG(LOG_INFO, data,
"zone is up-to-date");
"zone is up-to-date, expires in %u seconds", data->expire_timer);
xfr_stats_begin(&data->stats);
xfr_stats_add(&data->stats, pkt->size);
xfr_stats_end(&data->stats);
......@@ -980,7 +996,7 @@ static int soa_query_consume(knot_layer_t *layer, knot_pkt_t *pkt)
data->state = STATE_TRANSFER;
return KNOT_STATE_RESET; // continue with transfer
} else if (master_uptodate) {
consume_edns_expire(data, pkt);
consume_edns_expire(data, pkt, false);
finalize_edns_expire(data);
REFRESH_LOG(LOG_INFO, data, LOG_DIRECTION_NONE,
"remote serial %u, zone is up-to-date, expires in %u seconds",
......@@ -1040,7 +1056,7 @@ static int transfer_consume(knot_layer_t *layer, knot_pkt_t *pkt)
{
struct refresh_data *data = layer->data;
consume_edns_expire(data, pkt);
consume_edns_expire(data, pkt, true);
if (data->expire_timer < 2) {
REFRESH_LOG(LOG_WARNING, data, LOG_DIRECTION_NONE,
"remote is expired, ignoring");
......
......@@ -5,18 +5,30 @@
from dnstest.test import Test
import time
def test_expire(zone, server):
def test_status(zone, server, status):
resp = server.dig(zone[0].name, "SOA")
resp.check(rcode="SERVFAIL")
resp.check(rcode=status)
def break_xfrout(server):
def test_expire(zone, server):
test_status(zone, server, "SERVFAIL")
def test_not_expired(zone, server):
test_status(zone, server, "NOERROR")
def replace_in_config(server, what, with_what):
with open(server.confile, "r+") as f:
config = f.read()
f.seek(0)
f.truncate()
config = config.replace(" acl:", " #acl:")
config = config.replace(what, with_what)
f.write(config)
def break_xfrout(server):
replace_in_config(server, " acl:", " #acl:")
def fix_xfrout(server):
replace_in_config(server, " #acl:", " acl:")
t = Test(tsig=False)
# this zone has refresh = 2s, retry = 2s and expire = 16s
......@@ -60,4 +72,50 @@ remain = max(0, EXPIRE_SLEEP - int(time.time() - timer))
t.sleep(remain)
test_expire(zone, slave)
test_expire(zone, sub_slave) # both slaves expire at once despite sub_slave updated more recently. Thanks to EDNS Expire.
# Test for expiration prolonging via EDNS (RFC 7314, section 4, second paragraph).
# 1) Test expiration prolonging by EDNS Expire in SOA query responses.
# bring back the servers once more and reset the expire timers
fix_xfrout(master)
master.reload()
slave.zone_wait(zone)
sub_slave.zone_wait(zone)
# disallow actual updates from slave, SOA queries are still allowed
break_xfrout(slave)
slave.reload()
# let the original expire timer (without EDNS) on sub_slave run out
t.sleep(2 * EXPIRE_SLEEP)
# the expire timer on sub_slave should be kept prolonged just by SOA queries
test_not_expired(zone, slave)
test_not_expired(zone, sub_slave)
# 2) Test that the expire timer in sub_slave isn't directly set to
# shorter EDNS Expire received in SOA query responses. Simulate an
# expire timer difference (normally caused by multi-path propagation)
# by lowering the expire value while keeping the serial.
master.ctl("zone-begin example.")
master.ctl("zone-set example. example. 1200 SOA ns admin 4242 2 2 5 600")
master.ctl("zone-commit example.")
fix_xfrout(slave)
slave.reload()
t.sleep(1)
slave.ctl("zone-refresh", wait=True) # SOA query only, same serial
sub_slave.ctl("zone-refresh", wait=True) # SOA query only, same serial
slave.stop()
t.sleep(8)
# the new expire (5 seconds) from slave would have run out on sub_slave,
# but the original expire timer (started as 16 seconds) has been retained
test_not_expired(zone, sub_slave)
t.sleep(8)
# the original expire time has finally run out
test_expire(zone, sub_slave)
t.stop()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment