From 57aadd6cea70189f979351f1d516f4e5a554b54c Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 21 Feb 2020 10:53:08 -0800 Subject: [PATCH 1/8] add syntax and setter/getter functions to configure max-ixfr-ratio --- bin/named/config.c | 1 + bin/named/zoneconf.c | 9 +++++++ bin/tests/system/checkconf/bad-maxratio1.conf | 17 +++++++++++++ bin/tests/system/checkconf/bad-maxratio2.conf | 17 +++++++++++++ .../system/checkconf/good-maxratio1.conf | 17 +++++++++++++ .../system/checkconf/good-maxratio2.conf | 17 +++++++++++++ bin/tests/system/checkconf/good.conf | 3 +++ bin/tests/system/checkconf/tests.sh | 9 ++++++- .../system/checkconf/warn-maxratio1.conf | 17 +++++++++++++ bin/tests/system/ixfr/ns4/named.conf.in | 1 + bin/tests/system/run.sh | 0 doc/misc/master.zoneopt | 1 + doc/misc/mirror.zoneopt | 1 + doc/misc/options | 4 +++ doc/misc/options.active | 4 +++ doc/misc/slave.zoneopt | 1 + lib/bind9/check.c | 18 +++++++++++++ lib/dns/include/dns/zone.h | 19 ++++++++++++++ lib/dns/win32/libdns.def.in | 2 ++ lib/dns/zone.c | 14 +++++++++++ lib/isccfg/namedconf.c | 25 +++++++++++++++++++ 21 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 bin/tests/system/checkconf/bad-maxratio1.conf create mode 100644 bin/tests/system/checkconf/bad-maxratio2.conf create mode 100644 bin/tests/system/checkconf/good-maxratio1.conf create mode 100644 bin/tests/system/checkconf/good-maxratio2.conf create mode 100644 bin/tests/system/checkconf/warn-maxratio1.conf mode change 100644 => 100755 bin/tests/system/run.sh diff --git a/bin/named/config.c b/bin/named/config.c index 210ac8bde6..9c3e8ca799 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -81,6 +81,7 @@ options {\n\ listen-on-v6 {any;};\n\ # lock-file \"" NAMED_LOCALSTATEDIR "/run/named/named.lock\";\n\ match-mapped-addresses no;\n\ + max-ixfr-ratio 100%;\n\ max-rsa-exponent-size 0; /* no limit */\n\ max-udp-size 1232;\n\ memstatistics-file \"named.memstats\";\n\ diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index b2bb3731fb..32b6aedb7f 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1398,6 +1398,15 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, ixfrdiff); } + obj = NULL; + result = named_config_get(maps, "max-ixfr-ratio", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (cfg_obj_isstring(obj)) { + dns_zone_setixfrratio(zone, 0); + } else { + dns_zone_setixfrratio(zone, cfg_obj_aspercentage(obj)); + } + obj = NULL; result = named_config_get(maps, "request-expire", &obj); INSIST(result == ISC_R_SUCCESS); diff --git a/bin/tests/system/checkconf/bad-maxratio1.conf b/bin/tests/system/checkconf/bad-maxratio1.conf new file mode 100644 index 0000000000..f0d06be669 --- /dev/null +++ b/bin/tests/system/checkconf/bad-maxratio1.conf @@ -0,0 +1,17 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone example { + type master; + masterfile-format map; + file "example.db"; + max-ixfr-ratio 0.9; +}; diff --git a/bin/tests/system/checkconf/bad-maxratio2.conf b/bin/tests/system/checkconf/bad-maxratio2.conf new file mode 100644 index 0000000000..902c334423 --- /dev/null +++ b/bin/tests/system/checkconf/bad-maxratio2.conf @@ -0,0 +1,17 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone example { + type master; + masterfile-format map; + file "example.db"; + max-ixfr-ratio 0%; +}; diff --git a/bin/tests/system/checkconf/good-maxratio1.conf b/bin/tests/system/checkconf/good-maxratio1.conf new file mode 100644 index 0000000000..80ff113f21 --- /dev/null +++ b/bin/tests/system/checkconf/good-maxratio1.conf @@ -0,0 +1,17 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone example { + type master; + masterfile-format map; + file "example.db"; + max-ixfr-ratio 50%; +}; diff --git a/bin/tests/system/checkconf/good-maxratio2.conf b/bin/tests/system/checkconf/good-maxratio2.conf new file mode 100644 index 0000000000..e057dfe6e1 --- /dev/null +++ b/bin/tests/system/checkconf/good-maxratio2.conf @@ -0,0 +1,17 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone example { + type master; + masterfile-format map; + file "example.db"; + max-ixfr-ratio unlimited; +}; diff --git a/bin/tests/system/checkconf/good.conf b/bin/tests/system/checkconf/good.conf index 117143ad29..e23fd25060 100644 --- a/bin/tests/system/checkconf/good.conf +++ b/bin/tests/system/checkconf/good.conf @@ -81,6 +81,7 @@ options { "corp"; }; dnssec-policy "test"; + max-ixfr-ratio 90%; transfer-source 0.0.0.0 dscp 63; zone-statistics none; }; @@ -92,11 +93,13 @@ view "first" { type master; file "xxx"; update-policy local; + max-ixfr-ratio 20%; notify-source 10.10.10.10 port 53 dscp 55; }; zone "clone" { type master; file "yyy"; + max-ixfr-ratio unlimited; }; dnssec-validation auto; zone-statistics terse; diff --git a/bin/tests/system/checkconf/tests.sh b/bin/tests/system/checkconf/tests.sh index 61e3b8fa4a..19baf1c19b 100644 --- a/bin/tests/system/checkconf/tests.sh +++ b/bin/tests/system/checkconf/tests.sh @@ -559,9 +559,16 @@ awk 'BEGIN { ok = 0; } /cut here/ { ok = 1; getline } ok == 1 { print }' good-ka [ -s good-kasp.conf.in ] || ret=1 $CHECKCONF -p good-kasp.conf.in | grep -v '^good-kasp.conf.in:' > good-kasp.conf.out 2>&1 || ret=1 cmp good-kasp.conf.in good-kasp.conf.out || ret=1 - if [ $ret != 0 ]; then echo_i "failed"; fi status=`expr $status + $ret` +n=`expr $n + 1` +echo_i "check that max-ixfr-ratio 100% generates a warning ($n)" +ret=0 +$CHECKCONF warn-maxratio1.conf > checkconf.out$n 2>/dev/null || ret=1 +grep "exceeds 100%" < checkconf.out$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; ret=1; fi +status=`expr $status + $ret` + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/bin/tests/system/checkconf/warn-maxratio1.conf b/bin/tests/system/checkconf/warn-maxratio1.conf new file mode 100644 index 0000000000..519dcc484c --- /dev/null +++ b/bin/tests/system/checkconf/warn-maxratio1.conf @@ -0,0 +1,17 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone example { + type master; + masterfile-format map; + file "example.db"; + max-ixfr-ratio 101%; +}; diff --git a/bin/tests/system/ixfr/ns4/named.conf.in b/bin/tests/system/ixfr/ns4/named.conf.in index be96c1191d..ef70229ad9 100644 --- a/bin/tests/system/ixfr/ns4/named.conf.in +++ b/bin/tests/system/ixfr/ns4/named.conf.in @@ -37,6 +37,7 @@ view "primary" { type secondary; file "mytest.db"; primaries { 10.53.0.3; }; + max-ixfr-ratio unlimited; }; zone "sub.test" IN { type secondary; diff --git a/bin/tests/system/run.sh b/bin/tests/system/run.sh old mode 100644 new mode 100755 diff --git a/doc/misc/master.zoneopt b/doc/misc/master.zoneopt index bb3d6281bd..f9903b34e8 100644 --- a/doc/misc/master.zoneopt +++ b/doc/misc/master.zoneopt @@ -35,6 +35,7 @@ zone [ ] { key-directory ; masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; max-transfer-idle-out ; diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index d403bfd1b8..c142586fe2 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -16,6 +16,7 @@ zone [ ] { masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ]; ... }; + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; max-refresh-time ; diff --git a/doc/misc/options b/doc/misc/options index f98abd8e78..27b047fba8 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -241,6 +241,7 @@ options { max-cache-ttl ; max-clients-per-query ; max-ixfr-log-size ( default | unlimited | ); // ancient + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-ncache-ttl ; max-records ; @@ -608,6 +609,7 @@ view [ ] { max-cache-ttl ; max-clients-per-query ; max-ixfr-log-size ( default | unlimited | ); // ancient + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-ncache-ttl ; max-records ; @@ -835,6 +837,7 @@ view [ ] { ... }; max-ixfr-log-size ( default | unlimited | ); // ancient + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; max-refresh-time ; @@ -943,6 +946,7 @@ zone [ ] { [ port ] | [ port ] ) [ key ]; ... }; max-ixfr-log-size ( default | unlimited | ); // ancient + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; max-refresh-time ; diff --git a/doc/misc/options.active b/doc/misc/options.active index 8542116912..8e778f135f 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -215,6 +215,7 @@ options { max-cache-size ( default | unlimited | | ); max-cache-ttl ; max-clients-per-query ; + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-ncache-ttl ; max-records ; @@ -545,6 +546,7 @@ view [ ] { max-cache-size ( default | unlimited | | ); max-cache-ttl ; max-clients-per-query ; + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-ncache-ttl ; max-records ; @@ -755,6 +757,7 @@ view [ ] { | [ port ] | [ port ] ) [ key ]; ... }; + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; max-refresh-time ; @@ -856,6 +859,7 @@ zone [ ] { masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ]; ... }; + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; max-refresh-time ; diff --git a/doc/misc/slave.zoneopt b/doc/misc/slave.zoneopt index 7bc7f35946..040f2d5b69 100644 --- a/doc/misc/slave.zoneopt +++ b/doc/misc/slave.zoneopt @@ -28,6 +28,7 @@ zone [ ] { masterfile-format ( map | raw | text ); masterfile-style ( full | relative ); masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ]; ... }; + max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; max-refresh-time ; diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 64d7aaeedf..f8e937d6d3 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -1567,6 +1567,24 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, } } + obj = NULL; + (void)cfg_map_get(options, "max-ixfr-ratio", &obj); + if (obj != NULL && cfg_obj_ispercentage(obj)) { + uint32_t percent = cfg_obj_aspercentage(obj); + if (percent == 0) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'ixfr-max-ratio' must be a nonzero " + "percentage or 'unlimited')"); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } else if (percent > 100) { + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "'ixfr-max-ratio %d%%' exceeds 100%%", + percent); + } + } + obj = NULL; (void)cfg_map_get(options, "check-names", &obj); if (obj != NULL && !cfg_obj_islist(obj)) { diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index d61322bc9c..412ed3744c 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -2338,6 +2338,25 @@ dns_zone_setrequestixfr(dns_zone_t *zone, bool flag); * \li 'zone' to be valid. */ +uint32_t +dns_zone_getixfrratio(dns_zone_t *zone); +/*% + * Returns the zone's current IXFR ratio. + * + * Requires: + * \li 'zone' to be valid. + */ + +void +dns_zone_setixfrratio(dns_zone_t *zone, uint32_t ratio); +/*% + * Sets the ratio of IXFR size to zone size above which we use an AXFR + * response, expressed as a percentage. Cannot exceed 100. + * + * Requires: + * \li 'zone' to be valid. + */ + void dns_zone_setserialupdatemethod(dns_zone_t *zone, dns_updatemethod_t method); /*% diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 040e8d1280..88aaa682ef 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -1221,6 +1221,7 @@ dns_zone_getgluecachestats dns_zone_getidlein dns_zone_getidleout dns_zone_getincludes +dns_zone_getixfrratio dns_zone_getjournal dns_zone_getjournalsize dns_zone_getkasp @@ -1323,6 +1324,7 @@ dns_zone_setforwardacl dns_zone_setidlein dns_zone_setidleout dns_zone_setisself +dns_zone_setixfrratio dns_zone_setjournal dns_zone_setjournalsize dns_zone_setkasp diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 029311c52e..94c9365f9c 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -407,6 +407,7 @@ struct dns_zone { * whether ixfr is requested */ bool requestixfr; + uint32_t ixfr_ratio; /*% * whether EDNS EXPIRE is requested @@ -1128,6 +1129,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->sourceserial = 0; zone->sourceserialset = false; zone->requestixfr = true; + zone->ixfr_ratio = 100; zone->requestexpire = true; ISC_LIST_INIT(zone->rss_events); ISC_LIST_INIT(zone->rss_post); @@ -20561,6 +20563,18 @@ dns_zone_getrequestixfr(dns_zone_t *zone) { return (zone->requestixfr); } +void +dns_zone_setixfrratio(dns_zone_t *zone, uint32_t ratio) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->ixfr_ratio = ratio; +} + +uint32_t +dns_zone_getixfrratio(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->ixfr_ratio); +} + void dns_zone_setrequestexpire(dns_zone_t *zone, bool flag) { REQUIRE(DNS_ZONE_VALID(zone)); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 28dbde65ff..d7853a3443 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -92,6 +92,7 @@ static cfg_type_t cfg_type_dnstapoutput; static cfg_type_t cfg_type_dyndb; static cfg_type_t cfg_type_plugin; static cfg_type_t cfg_type_ixfrdifftype; +static cfg_type_t cfg_type_ixfrratio; static cfg_type_t cfg_type_key; static cfg_type_t cfg_type_logfile; static cfg_type_t cfg_type_logging; @@ -2203,6 +2204,8 @@ static cfg_clausedef_t zone_clauses[] = { CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR | CFG_ZONE_STUB | CFG_ZONE_REDIRECT }, { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_ANCIENT }, + { "max-ixfr-ratio", &cfg_type_ixfrratio, + CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR }, { "max-journal-size", &cfg_type_size, CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_MIRROR }, { "max-records", &cfg_type_uint32, @@ -2740,6 +2743,28 @@ static cfg_type_t cfg_type_sizeorpercent = { doc_parse_size_or_percent, &cfg_rep_string, sizeorpercent_enums }; +/*% + * An IXFR size ratio: percentage, or "unlimited". + */ + +static isc_result_t +parse_ixfrratio(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (cfg_parse_enum_or_other(pctx, type, &cfg_type_percentage, ret)); +} + +static void +doc_ixfrratio(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(type); + cfg_print_cstr(pctx, "( unlimited | "); + cfg_doc_terminal(pctx, &cfg_type_percentage); + cfg_print_cstr(pctx, " )"); +} + +static const char *ixfrratio_enums[] = { "unlimited", NULL }; +static cfg_type_t cfg_type_ixfrratio = { "ixfr_ratio", parse_ixfrratio, + NULL, doc_ixfrratio, + NULL, ixfrratio_enums }; + /*% * optional_keyvalue */ From 70df95e9f5100d25b79efcd2fb1f7c76159c9cf8 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 21 Feb 2020 17:05:04 -0800 Subject: [PATCH 2/8] dns_journal_iter_init() can now return the size of the delta the call initailizing a journal iterator can now optionally return to the caller the size in bytes of an IXFR message (not including DNS header overhead, signatures etc) containing the differences from the beginning to the ending serial number. this is calculated by scanning the journal transaction headers to calculate the transfer size. since journal file records contain a length field that is not included in IXFR messages, we subtract out the length of those fields from the overall transaction length. this necessitated adding an "RR count" field to the journal transaction header, so we know how many length fields to subract. NOTE: this will make existing journal files stop working! --- lib/dns/include/dns/journal.h | 8 ++++- lib/dns/journal.c | 67 ++++++++++++++++++++++++++++------- lib/dns/zone.c | 2 +- lib/ns/xfrout.c | 3 +- 4 files changed, 64 insertions(+), 16 deletions(-) diff --git a/lib/dns/include/dns/journal.h b/lib/dns/include/dns/journal.h index a086aaa62f..8acf9ab44e 100644 --- a/lib/dns/include/dns/journal.h +++ b/lib/dns/include/dns/journal.h @@ -190,11 +190,17 @@ dns_journal_last_serial(dns_journal_t *j); isc_result_t dns_journal_iter_init(dns_journal_t *j, uint32_t begin_serial, - uint32_t end_serial); + uint32_t end_serial, size_t *xfrsizep); /*%< * Prepare to iterate over the transactions that will bring the database * from SOA serial number 'begin_serial' to 'end_serial'. * + * If 'xfrsizep' is not NULL, then on success it will be set to the + * total size of all records in the iteration (excluding headers). This + * is meant to be a rough approximation of the size of an incremental + * zone transfer, though it does not account for DNS message overhead + * or name compression.) + * * Returns: *\li ISC_R_SUCCESS *\li ISC_R_RANGE begin_serial is outside the addressable range. diff --git a/lib/dns/journal.c b/lib/dns/journal.c index c1e3a7a4df..bc1ad96314 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -221,6 +221,7 @@ typedef union { */ typedef struct { unsigned char size[4]; /*%< In bytes, excluding header. */ + unsigned char count[4]; /*%< Number of records in transaction */ unsigned char serial0[4]; /*%< SOA serial before update. */ unsigned char serial1[4]; /*%< SOA serial after update. */ } journal_rawxhdr_t; @@ -256,9 +257,9 @@ typedef struct { /*% * The in-core representation of the transaction header. */ - typedef struct { uint32_t size; + uint32_t count; uint32_t serial0; uint32_t serial1; } journal_xhdr_t; @@ -304,13 +305,13 @@ struct dns_journal { isc_offset_t offset; /*%< Current file offset */ journal_header_t header; /*%< In-core journal header */ unsigned char *rawindex; /*%< In-core buffer for journal index - * in - * on-disk format */ + * in on-disk format */ journal_pos_t *index; /*%< In-core journal index */ /*% Current transaction state (when writing). */ struct { unsigned int n_soa; /*%< Number of SOAs seen */ + unsigned int n_rr; /*%< Number of RRs to write */ journal_pos_t pos[2]; /*%< Begin/end position */ } x; @@ -323,8 +324,7 @@ struct dns_journal { uint32_t current_serial; /*%< Current SOA serial * */ isc_buffer_t source; /*%< Data from disk */ - isc_buffer_t target; /*%< Data from _fromwire check - * */ + isc_buffer_t target; /*%< Data from _fromwire check */ dns_decompress_t dctx; /*%< Dummy decompression ctx */ dns_name_t name; /*%< Current domain name */ dns_rdata_t rdata; /*%< Current rdata */ @@ -462,16 +462,18 @@ journal_read_xhdr(dns_journal_t *j, journal_xhdr_t *xhdr) { return (result); } xhdr->size = decode_uint32(raw.size); + xhdr->count = decode_uint32(raw.count); xhdr->serial0 = decode_uint32(raw.serial0); xhdr->serial1 = decode_uint32(raw.serial1); return (ISC_R_SUCCESS); } static isc_result_t -journal_write_xhdr(dns_journal_t *j, uint32_t size, uint32_t serial0, - uint32_t serial1) { +journal_write_xhdr(dns_journal_t *j, uint32_t size, uint32_t count, + uint32_t serial0, uint32_t serial1) { journal_rawxhdr_t raw; encode_uint32(size, raw.size); + encode_uint32(count, raw.count); encode_uint32(serial0, raw.serial0); encode_uint32(serial1, raw.serial1); return (journal_write(j, &raw, sizeof(raw))); @@ -1026,7 +1028,8 @@ dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { dns_difftuple_t *t; isc_buffer_t buffer; void *mem = NULL; - uint64_t size; + uint64_t size = 0; + uint32_t rrcount = 0; isc_result_t result; isc_region_t used; @@ -1040,7 +1043,6 @@ dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { * Pass 1: determine the buffer size needed, and * keep track of SOA serial numbers. */ - size = 0; for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; t = ISC_LIST_NEXT(t, link)) { if (t->rdata.type == dns_rdatatype_soa) { @@ -1089,12 +1091,15 @@ dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { isc_buffer_putuint16(&buffer, (uint16_t)t->rdata.length); INSIST(isc_buffer_availablelength(&buffer) >= t->rdata.length); isc_buffer_putmem(&buffer, t->rdata.data, t->rdata.length); + + rrcount++; } isc_buffer_usedregion(&buffer, &used); INSIST(used.length == size); j->x.pos[1].offset += used.length; + j->x.n_rr = rrcount; /* * Write the buffer contents to the journal file. @@ -1205,7 +1210,8 @@ dns_journal_commit(dns_journal_t *j) { * Update the transaction header. */ CHECK(journal_seek(j, j->x.pos[0].offset)); - CHECK(journal_write_xhdr(j, offset, j->x.pos[0].serial, + CHECK(journal_write_xhdr(j, offset, j->x.n_rr, + j->x.pos[0].serial, j->x.pos[1].serial)); } @@ -1355,7 +1361,7 @@ roll_forward(dns_journal_t *j, dns_db_t *db, unsigned int options) { CHECK(DNS_R_UPTODATE); } - CHECK(dns_journal_iter_init(j, db_serial, end_serial)); + CHECK(dns_journal_iter_init(j, db_serial, end_serial, NULL)); for (result = dns_journal_first_rr(j); result == ISC_R_SUCCESS; result = dns_journal_next_rr(j)) @@ -1515,7 +1521,7 @@ dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { start_serial = dns_journal_first_serial(j); end_serial = dns_journal_last_serial(j); - CHECK(dns_journal_iter_init(j, start_serial, end_serial)); + CHECK(dns_journal_iter_init(j, start_serial, end_serial, NULL)); for (result = dns_journal_first_rr(j); result == ISC_R_SUCCESS; result = dns_journal_next_rr(j)) @@ -1672,7 +1678,7 @@ size_buffer(isc_mem_t *mctx, isc_buffer_t *b, unsigned size) { isc_result_t dns_journal_iter_init(dns_journal_t *j, uint32_t begin_serial, - uint32_t end_serial) { + uint32_t end_serial, size_t *xfrsizep) { isc_result_t result; CHECK(journal_find(j, begin_serial, &j->it.bpos)); @@ -1681,6 +1687,41 @@ dns_journal_iter_init(dns_journal_t *j, uint32_t begin_serial, CHECK(journal_find(j, end_serial, &j->it.epos)); INSIST(j->it.epos.serial == end_serial); + if (xfrsizep != NULL) { + journal_pos_t pos = j->it.bpos; + journal_xhdr_t xhdr; + uint64_t size = 0; + uint32_t count = 0; + + /* + * We already know the beginning and ending serial + * numbers are in the journal. Scan through them, + * adding up sizes and RR counts so we can calculate + * the IXFR size. + */ + CHECK(journal_seek(j, pos.offset)); + do { + CHECK(journal_read_xhdr(j, &xhdr)); + + size += xhdr.size; + count += xhdr.count; + + result = journal_next(j, &pos); + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } + CHECK(result); + } while (pos.serial != end_serial); + + /* + * For each RR, subtract the length of the RR header, + * as this would not be present in IXFR messages. + * (We don't need to worry about the transaction header + * because that was already excluded from xdr.size.) + */ + *xfrsizep = size - (count * sizeof(journal_rawrrhdr_t)); + } + result = ISC_R_SUCCESS; failure: j->it.result = result; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 94c9365f9c..656bbf74a0 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15743,7 +15743,7 @@ sync_secure_journal(dns_zone_t *zone, dns_zone_t *raw, dns_journal_t *journal, return (DNS_R_UNCHANGED); } - CHECK(dns_journal_iter_init(journal, start, end)); + CHECK(dns_journal_iter_init(journal, start, end, NULL)); for (result = dns_journal_first_rr(journal); result == ISC_R_SUCCESS; result = dns_journal_next_rr(journal)) { diff --git a/lib/ns/xfrout.c b/lib/ns/xfrout.c index 3227c5eaa7..3b8ce93c51 100644 --- a/lib/ns/xfrout.c +++ b/lib/ns/xfrout.c @@ -240,7 +240,8 @@ ixfr_rrstream_create(isc_mem_t *mctx, const char *journal_filename, CHECK(dns_journal_open(mctx, journal_filename, DNS_JOURNAL_READ, &s->journal)); - CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial)); + CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial, + NULL)); *sp = (rrstream_t *)s; return (ISC_R_SUCCESS); From 9950247c785391561fe01624b9504d0d37a899f0 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Sat, 22 Feb 2020 00:37:05 -0800 Subject: [PATCH 3/8] improve calculation of database transfer size - change name of 'bytes' to 'xfrsize' in dns_db_getsize() parameter list and related variables; this is a more accurate representation of what the function is doing - change the size calculations in dns_db_getsize() to more accurately represent the space needed for a *XFR message or journal file to contain the data in the database. previously we returned the sizes of all rdataslabs, including header overhead and offset tables, which resulted in the database size being reported as much larger than the equivalent *XFR or journal. - map files caused a particular problem here: the fullname can't be determined from the node while a file is being deserialized, because the uppernode pointers aren't set yet. so we store "full name length" in the dns_rbtnode structure while serializing, and clear it after deserialization is complete. --- aclocal.m4 | 2 +- lib/dns/include/dns/db.h | 6 ++--- lib/dns/rbtdb.c | 50 ++++++++++++++++++++-------------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/aclocal.m4 b/aclocal.m4 index fd128f0963..afaa95a961 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,4 +1,4 @@ -# generated automatically by aclocal 1.16.2 -*- Autoconf -*- +# generated automatically by aclocal 1.16.3 -*- Autoconf -*- # Copyright (C) 1996-2020 Free Software Foundation, Inc. diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index d00a407ec9..b79dcae0fa 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -1489,10 +1489,10 @@ dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, isc_result_t dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records, - uint64_t *bytes); + uint64_t *xfrsize); /*%< * On success if 'records' is not NULL, it is set to the number of records - * in the given version of the database. If 'bytes' is not NULL, it is + * in the given version of the database. If 'xfrisize' is not NULL, it is * set to the approximate number of bytes needed to transfer the records, * counting name, TTL, type, class, and rdata for each RR. (This is meant * to be a rough approximation of the size of a full zone transfer, though @@ -1502,7 +1502,7 @@ dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records, * \li 'db' is a valid zone database. * \li 'version' is NULL or a valid version. * \li 'records' is NULL or a pointer to return the record count in. - * \li 'bytes' is NULL or a pointer to return the byte count in. + * \li 'xfrsize' is NULL or a pointer to return the byte count in. * * Returns: * \li #ISC_R_SUCCESS diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index ef45109600..a05a0e4113 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -441,11 +441,11 @@ typedef struct rbtdb_version { unsigned char salt[DNS_NSEC3_SALTSIZE]; /* - * records and bytes are covered by rwlock. + * records and xfrsize are covered by rwlock. */ isc_rwlock_t rwlock; uint64_t records; - uint64_t bytes; + uint64_t xfrsize; isc_rwlock_t glue_rwlock; size_t glue_table_bits; @@ -1396,7 +1396,7 @@ newversion(dns_db_t *db, dns_dbversion_t **versionp) { RWLOCK(&rbtdb->current_version->rwlock, isc_rwlocktype_read); version->records = rbtdb->current_version->records; - version->bytes = rbtdb->current_version->bytes; + version->xfrsize = rbtdb->current_version->xfrsize; RWUNLOCK(&rbtdb->current_version->rwlock, isc_rwlocktype_read); rbtdb->next_serial++; @@ -6067,18 +6067,18 @@ recordsize(rdatasetheader_t *header, unsigned int namelen) { } static void -update_recordsandbytes(bool add, rbtdb_version_t *rbtversion, - rdatasetheader_t *header, unsigned int namelen) { +update_recordsandxfrsize(bool add, rbtdb_version_t *rbtversion, + rdatasetheader_t *header, unsigned int namelen) { unsigned char *hdr = (unsigned char *)header; size_t hdrsize = sizeof(*header); RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write); if (add) { rbtversion->records += dns_rdataslab_count(hdr, hdrsize); - rbtversion->bytes += recordsize(header, namelen); + rbtversion->xfrsize += recordsize(header, namelen); } else { rbtversion->records -= dns_rdataslab_count(hdr, hdrsize); - rbtversion->bytes -= recordsize(header, namelen); + rbtversion->xfrsize -= recordsize(header, namelen); } RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write); } @@ -6491,9 +6491,9 @@ find_header: } newheader->next = topheader->next; if (rbtversion != NULL && !header_nx) { - update_recordsandbytes(false, rbtversion, - header, - nodename->length); + update_recordsandxfrsize(false, rbtversion, + header, + nodename->length); } free_rdataset(rbtdb, rbtdb->common.mctx, header); } else { @@ -6544,9 +6544,9 @@ find_header: } } if (rbtversion != NULL && !header_nx) { - update_recordsandbytes(false, rbtversion, - header, - nodename->length); + update_recordsandxfrsize(false, rbtversion, + header, + nodename->length); } } } else { @@ -6623,8 +6623,8 @@ find_header: } if (rbtversion != NULL && !newheader_nx) { - update_recordsandbytes(true, rbtversion, newheader, - nodename->length); + update_recordsandxfrsize(true, rbtversion, newheader, + nodename->length); } /* @@ -7118,8 +7118,8 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, * to additional info. We need to clear these fields * to avoid having duplicated references. */ - update_recordsandbytes(true, rbtversion, newheader, - nodename->length); + update_recordsandxfrsize(true, rbtversion, newheader, + nodename->length); } else if (result == DNS_R_NXRRSET) { /* * This subtraction would remove all of the rdata; @@ -7155,8 +7155,8 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, * topheader. */ INSIST(rbtversion->serial >= topheader->serial); - update_recordsandbytes(false, rbtversion, header, - nodename->length); + update_recordsandxfrsize(false, rbtversion, header, + nodename->length); if (topheader_prev != NULL) { topheader_prev->next = newheader; } else { @@ -7520,8 +7520,8 @@ rbt_datafixer(dns_rbtnode_t *rbtnode, void *base, size_t filesize, void *arg, } } - update_recordsandbytes(true, rbtdb->current_version, header, - rbtnode->fullnamelen); + update_recordsandxfrsize(true, rbtdb->current_version, header, + rbtnode->fullnamelen); } /* We're done deserializing; clear fullnamelen */ @@ -8142,7 +8142,7 @@ getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash, static isc_result_t getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records, - uint64_t *bytes) { + uint64_t *xfrsize) { dns_rbtdb_t *rbtdb; isc_result_t result = ISC_R_SUCCESS; rbtdb_version_t *rbtversion = version; @@ -8162,8 +8162,8 @@ getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records, *records = rbtversion->records; } - if (bytes != NULL) { - *bytes = rbtversion->bytes; + if (xfrsize != NULL) { + *xfrsize = rbtversion->xfrsize; } RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_read); RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read); @@ -8805,7 +8805,7 @@ dns_rbtdb_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type, } rbtdb->current_version->records = 0; - rbtdb->current_version->bytes = 0; + rbtdb->current_version->xfrsize = 0; rbtdb->future_version = NULL; ISC_LIST_INIT(rbtdb->open_versions); /* From 2df6ffc051c5b0222f5b699607bbe80270d8afdc Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 21 Feb 2020 17:22:06 -0800 Subject: [PATCH 4/8] check size ratio when responding to IXFR requests --- lib/ns/xfrout.c | 54 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/lib/ns/xfrout.c b/lib/ns/xfrout.c index 3b8ce93c51..da44f88c50 100644 --- a/lib/ns/xfrout.c +++ b/lib/ns/xfrout.c @@ -225,10 +225,10 @@ static rrstream_methods_t ixfr_rrstream_methods; static isc_result_t ixfr_rrstream_create(isc_mem_t *mctx, const char *journal_filename, - uint32_t begin_serial, uint32_t end_serial, + uint32_t begin_serial, uint32_t end_serial, size_t *sizep, rrstream_t **sp) { - ixfr_rrstream_t *s; isc_result_t result; + ixfr_rrstream_t *s = NULL; INSIST(sp != NULL && *sp == NULL); @@ -241,7 +241,7 @@ ixfr_rrstream_create(isc_mem_t *mctx, const char *journal_filename, CHECK(dns_journal_open(mctx, journal_filename, DNS_JOURNAL_READ, &s->journal)); CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial, - NULL)); + sizep)); *sp = (rrstream_t *)s; return (ISC_R_SUCCESS); @@ -960,6 +960,26 @@ got_soa: current_serial = dns_soa_getserial(¤t_soa_tuple->rdata); if (reqtype == dns_rdatatype_ixfr) { + size_t jsize; + uint64_t dbsize; + + /* + * Outgoing IXFR may have been disabled for this peer + * or globally. + */ + if ((client->attributes & NS_CLIENTATTR_TCP) != 0) { + bool provide_ixfr; + + provide_ixfr = client->view->provideixfr; + if (peer != NULL) { + (void)dns_peer_getprovideixfr(peer, + &provide_ixfr); + } + if (provide_ixfr == false) { + goto axfr_fallback; + } + } + if (!have_soa) { FAILC(DNS_R_FORMERR, "IXFR request missing SOA"); } @@ -1011,7 +1031,7 @@ got_soa: if (journalfile != NULL) { result = ixfr_rrstream_create( mctx, journalfile, begin_serial, current_serial, - &data_stream); + &jsize, &data_stream); } else { result = ISC_R_NOTFOUND; } @@ -1024,6 +1044,32 @@ got_soa: goto axfr_fallback; } CHECK(result); + + result = dns_db_getsize(db, ver, NULL, &dbsize); + if (result == ISC_R_SUCCESS) { + uint32_t ratio = dns_zone_getixfrratio(zone); + if (ratio != 0 && ((100 * jsize) / dbsize) > ratio) { + data_stream->methods->destroy(&data_stream); + data_stream = NULL; + xfrout_log1(client, question_name, + question_class, ISC_LOG_DEBUG(4), + "IXFR delta size (%zu bytes) " + "exceeds the maximum ratio to " + "database size " + "(%" PRIu64 " bytes), " + "falling back to AXFR", + jsize, dbsize); + mnemonic = "AXFR-style IXFR"; + goto axfr_fallback; + } else { + xfrout_log1(client, question_name, + question_class, ISC_LOG_DEBUG(4), + "IXFR delta size (%zu bytes); " + "database size " + "(%" PRIu64 " bytes)", + jsize, dbsize); + } + } is_ixfr = true; } else { axfr_fallback: From 9529d1ed0d4da3de8943519d2d61b8a299e8d4a5 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 21 Feb 2020 22:48:34 -0800 Subject: [PATCH 5/8] add a system test for AXFR fallback when max-ixfr-ratio is exceeded also cleaned up the ixfr system test: - use retry_quiet when applicable - use scripts to generate test zones - improve consistency --- bin/tests/system/ixfr/clean.sh | 4 +- bin/tests/system/ixfr/ixfr-stats.good | 4 +- bin/tests/system/ixfr/ns1/.gitignore | 1 - bin/tests/system/ixfr/ns1/named.conf.in | 31 +++ bin/tests/system/ixfr/ns1/startme | 0 bin/tests/system/ixfr/ns3/mytest0.db | 26 -- bin/tests/system/ixfr/ns3/mytest1.db | 26 -- bin/tests/system/ixfr/ns3/mytest2.db | 26 -- bin/tests/system/ixfr/ns3/named.conf.in | 1 + bin/tests/system/ixfr/ns3/subtest0.db | 22 -- bin/tests/system/ixfr/ns3/subtest1.db | 22 -- bin/tests/system/ixfr/setup.sh | 73 ++++-- bin/tests/system/ixfr/tests.sh | 319 +++++++++--------------- 13 files changed, 205 insertions(+), 350 deletions(-) delete mode 100644 bin/tests/system/ixfr/ns1/.gitignore create mode 100644 bin/tests/system/ixfr/ns1/named.conf.in delete mode 100644 bin/tests/system/ixfr/ns1/startme delete mode 100644 bin/tests/system/ixfr/ns3/mytest0.db delete mode 100644 bin/tests/system/ixfr/ns3/mytest1.db delete mode 100644 bin/tests/system/ixfr/ns3/mytest2.db delete mode 100644 bin/tests/system/ixfr/ns3/subtest0.db delete mode 100644 bin/tests/system/ixfr/ns3/subtest1.db diff --git a/bin/tests/system/ixfr/clean.sh b/bin/tests/system/ixfr/clean.sh index 3de07b777b..e1dd69327b 100644 --- a/bin/tests/system/ixfr/clean.sh +++ b/bin/tests/system/ixfr/clean.sh @@ -11,12 +11,12 @@ rm -f stats.* rm -f ns1/*.db ns1/*.jnl -rm -f ns3/*.jnl ns3/mytest.db ns3/subtest.db +rm -f ns3/*.jnl ns3/mytest*.db ns3/subtest*.db rm -f ns4/*.jnl ns4/*.db rm -f ns5/*.jnl ns5/*.db rm -f */named.memstats rm -f */named.conf -rm -f */named.run +rm -f */named.run */named.run.prev rm -f */ans.run rm -f dig.out.test* dig.out1.test* dig.out2.test* dig.out3.test* rm -f ns3/large.db diff --git a/bin/tests/system/ixfr/ixfr-stats.good b/bin/tests/system/ixfr/ixfr-stats.good index dd62e121d6..3d0d2dde32 100644 --- a/bin/tests/system/ixfr/ixfr-stats.good +++ b/bin/tests/system/ixfr/ixfr-stats.good @@ -1,3 +1,3 @@ messages=1 -records=6 -bytes=219 +records=5 +bytes=204 diff --git a/bin/tests/system/ixfr/ns1/.gitignore b/bin/tests/system/ixfr/ns1/.gitignore deleted file mode 100644 index 58e5c9282d..0000000000 --- a/bin/tests/system/ixfr/ns1/.gitignore +++ /dev/null @@ -1 +0,0 @@ -named.conf diff --git a/bin/tests/system/ixfr/ns1/named.conf.in b/bin/tests/system/ixfr/ns1/named.conf.in new file mode 100644 index 0000000000..854d04dedb --- /dev/null +++ b/bin/tests/system/ixfr/ns1/named.conf.in @@ -0,0 +1,31 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify yes; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; diff --git a/bin/tests/system/ixfr/ns1/startme b/bin/tests/system/ixfr/ns1/startme deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/bin/tests/system/ixfr/ns3/mytest0.db b/bin/tests/system/ixfr/ns3/mytest0.db deleted file mode 100644 index 644086fb85..0000000000 --- a/bin/tests/system/ixfr/ns3/mytest0.db +++ /dev/null @@ -1,26 +0,0 @@ -; Copyright (C) Internet Systems Consortium, Inc. ("ISC") -; -; This Source Code Form is subject to the terms of the Mozilla Public -; License, v. 2.0. If a copy of the MPL was not distributed with this -; file, You can obtain one at http://mozilla.org/MPL/2.0/. -; -; See the COPYRIGHT file distributed with this work for additional -; information regarding copyright ownership. - -$ORIGIN test. -$TTL 15 -test. 15 IN SOA ns1.test. hostmaster.test. ( - 1 ; serial - 3H ; refresh - 15 ; retry - 1w ; expire - 3h ; minimum - ) - IN NS ns1.test. - IN NS ns2.test. - IN NS ns5.test. -ns1 IN A 10.53.0.3 -ns2 IN A 10.53.0.4 -ns5 IN A 10.53.0.5 -host1 IN A 192.168.10.3 -host2 IN A 192.168.10.4 diff --git a/bin/tests/system/ixfr/ns3/mytest1.db b/bin/tests/system/ixfr/ns3/mytest1.db deleted file mode 100644 index ddc7e05d24..0000000000 --- a/bin/tests/system/ixfr/ns3/mytest1.db +++ /dev/null @@ -1,26 +0,0 @@ -; Copyright (C) Internet Systems Consortium, Inc. ("ISC") -; -; This Source Code Form is subject to the terms of the Mozilla Public -; License, v. 2.0. If a copy of the MPL was not distributed with this -; file, You can obtain one at http://mozilla.org/MPL/2.0/. -; -; See the COPYRIGHT file distributed with this work for additional -; information regarding copyright ownership. - -$ORIGIN test. -$TTL 15 -test. 15 IN SOA ns1.test. hostmaster.test. ( - 2 ; serial - 3H ; refresh - 15 ; retry - 1w ; expire - 3h ; minimum - ) - IN NS ns1.test. - IN NS ns2.test. - IN NS ns5.test. -ns1 IN A 10.53.0.3 -ns2 IN A 10.53.0.4 -ns5 IN A 10.53.0.5 -host1 IN A 192.168.10.13 -host2 IN A 192.168.10.4 diff --git a/bin/tests/system/ixfr/ns3/mytest2.db b/bin/tests/system/ixfr/ns3/mytest2.db deleted file mode 100644 index 6ea7cf9466..0000000000 --- a/bin/tests/system/ixfr/ns3/mytest2.db +++ /dev/null @@ -1,26 +0,0 @@ -; Copyright (C) Internet Systems Consortium, Inc. ("ISC") -; -; This Source Code Form is subject to the terms of the Mozilla Public -; License, v. 2.0. If a copy of the MPL was not distributed with this -; file, You can obtain one at http://mozilla.org/MPL/2.0/. -; -; See the COPYRIGHT file distributed with this work for additional -; information regarding copyright ownership. - -$ORIGIN test. -$TTL 15 -test. 15 IN SOA ns1.test. hostmaster.test. ( - 3 ; serial - 3H ; refresh - 15 ; retry - 1w ; expire - 3h ; minimum - ) - IN NS ns1.test. - IN NS ns2.test. - IN NS ns5.test. -ns1 IN A 10.53.0.3 -ns2 IN A 10.53.0.4 -ns5 IN A 10.53.0.5 -host1 IN A 192.168.10.13 -host2 IN A 192.168.10.14 diff --git a/bin/tests/system/ixfr/ns3/named.conf.in b/bin/tests/system/ixfr/ns3/named.conf.in index c16ac8541c..96d5ab027a 100644 --- a/bin/tests/system/ixfr/ns3/named.conf.in +++ b/bin/tests/system/ixfr/ns3/named.conf.in @@ -37,6 +37,7 @@ view "primary" { zone "test" IN { type primary; file "mytest.db"; + max-ixfr-ratio 75%; }; zone "sub.test" IN { type primary; diff --git a/bin/tests/system/ixfr/ns3/subtest0.db b/bin/tests/system/ixfr/ns3/subtest0.db deleted file mode 100644 index 7dc1ed79e6..0000000000 --- a/bin/tests/system/ixfr/ns3/subtest0.db +++ /dev/null @@ -1,22 +0,0 @@ -; Copyright (C) Internet Systems Consortium, Inc. ("ISC") -; -; This Source Code Form is subject to the terms of the Mozilla Public -; License, v. 2.0. If a copy of the MPL was not distributed with this -; file, You can obtain one at http://mozilla.org/MPL/2.0/. -; -; See the COPYRIGHT file distributed with this work for additional -; information regarding copyright ownership. - -$ORIGIN sub.test. -$TTL 15 -sub.test. 15 IN SOA ns1.test. hostmaster.test. ( - 1 ; serial - 3H ; refresh - 15 ; retry - 1w ; expire - 3h ; minimum - ) - IN NS ns1.test. - IN NS ns2.test. -host3 IN A 192.168.10.23 -host4 IN A 192.168.10.24 diff --git a/bin/tests/system/ixfr/ns3/subtest1.db b/bin/tests/system/ixfr/ns3/subtest1.db deleted file mode 100644 index 5af027a4c7..0000000000 --- a/bin/tests/system/ixfr/ns3/subtest1.db +++ /dev/null @@ -1,22 +0,0 @@ -; Copyright (C) Internet Systems Consortium, Inc. ("ISC") -; -; This Source Code Form is subject to the terms of the Mozilla Public -; License, v. 2.0. If a copy of the MPL was not distributed with this -; file, You can obtain one at http://mozilla.org/MPL/2.0/. -; -; See the COPYRIGHT file distributed with this work for additional -; information regarding copyright ownership. - -$ORIGIN sub.test. -$TTL 15 -sub.test. 15 IN SOA ns1.test. hostmaster.test. ( - 3 ; serial - 3H ; refresh - 15 ; retry - 1w ; expire - 3h ; minimum - ) - IN NS ns1.test. - IN NS ns2.test. -host3 IN A 192.168.10.123 -host4 IN A 192.168.10.24 diff --git a/bin/tests/system/ixfr/setup.sh b/bin/tests/system/ixfr/setup.sh index dc3b5acb78..7b6b8ff5cb 100644 --- a/bin/tests/system/ixfr/setup.sh +++ b/bin/tests/system/ixfr/setup.sh @@ -12,35 +12,56 @@ SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh -cat <ns1/named.conf -options { - query-source address 10.53.0.1; - notify-source 10.53.0.1; - transfer-source 10.53.0.1; - port ${PORT}; - pid-file "named.pid"; - listen-on { 10.53.0.1; }; - listen-on-v6 { none; }; - recursion no; - notify yes; -}; - -key rndc_key { - secret "1234abcd8765"; - algorithm hmac-sha256; -}; - -controls { - inet 10.53.0.1 port ${CONTROLPORT} allow { any; } keys { rndc_key; }; -}; -EOF +$SHELL clean.sh +copy_setports ns1/named.conf.in ns1/named.conf copy_setports ns3/named.conf.in ns3/named.conf copy_setports ns4/named.conf.in ns4/named.conf copy_setports ns5/named.conf.in ns5/named.conf -# Setup initial db files for ns3 -cp ns3/mytest0.db ns3/mytest.db -cp ns3/subtest0.db ns3/subtest.db +# Set up db files for zone "test" - this is a series of four +# versions of the zone, the second and third having small changes +# and the fourth having a large one. + +testdb () { + cat << EOF +\$ORIGIN $1 +\$TTL 15 +@ 15 IN SOA ns1.test. hostmaster.test. ( + $2 ; serial + 3H ; refresh + 15 ; retry + 1w ; expire + 3h ; minimum + ) + IN NS ns1.test. + IN NS ns2.test. + IN NS ns5.test. +ns1 IN A 10.53.0.3 +ns2 IN A 10.53.0.4 +ns5 IN A 10.53.0.5 +EOF + + i=0 + while [ $i -lt $3 ]; do + echo "host$i IN A 192.0.2.$i" + i=$((i+1)) + done +} + +testdb test. 1 60 > ns3/mytest.db +testdb test. 2 61 > ns3/mytest1.db +testdb test. 3 62 > ns3/mytest2.db +testdb test. 4 0 > ns3/mytest3.db + +# Set up similar db files for sub.test, which will have IXFR disabled +testdb sub.test. 1 60 > ns3/subtest.db +testdb sub.test. 3 61 > ns3/subtest1.db + +# Set up a large zone +i=0 $SHELL ../genzone.sh 3 > ns3/large.db -awk 'END { for (i = 0; i < 10000; i++) printf("record%d 10 IN TXT this is record %d\n", i, i) }' < /dev/null >> ns3/large.db +while [ $i -lt 10000 ]; do + echo "record$i 10 IN TXT this is record %i" >> ns3/large.db + i=$((i+1)) +done diff --git a/bin/tests/system/ixfr/tests.sh b/bin/tests/system/ixfr/tests.sh index 571d0a403a..853ca112d7 100644 --- a/bin/tests/system/ixfr/tests.sh +++ b/bin/tests/system/ixfr/tests.sh @@ -18,6 +18,12 @@ SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh +wait_for_serial() ( + $DIG $DIGOPTS "@$1" "$2" SOA > "$4" + serial=$(awk '$4 == "SOA" { print $7 }' "$4") + [ "$3" -eq "${serial:--1}" ] +) + status=0 n=0 @@ -27,6 +33,7 @@ RNDCCMD="$RNDC -p ${CONTROLPORT} -c ../common/rndc.conf -s" n=$((n+1)) echo_i "testing initial AXFR ($n)" +ret=0 $SENDCMD < dig.out.test$n - grep "SOA" dig.out.test$n > /dev/null && break - sleep 1 -done +retry_quiet 10 wait_for_serial 10.53.0.1 nil. 1 dig.out.test$n || ret=1 -$DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'initial AXFR' >/dev/null || { - echo_i "failed" - status=1 -} +$DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'initial AXFR' >/dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) n=$((n+1)) echo_i "testing successful IXFR ($n)" +ret=0 # We change the IP address of a.nil., and the TXT record at the apex. # Then we do a SOA-only update. @@ -94,17 +96,17 @@ EOF sleep 1 -$RNDCCMD 10.53.0.1 refresh nil +$RNDCCMD 10.53.0.1 refresh nil | sed 's/^/ns1 /' | cat_i sleep 2 -$DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'successful IXFR' >/dev/null || { - echo_i "failed" - status=1 -} +$DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'successful IXFR' >/dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) n=$((n+1)) echo_i "testing AXFR fallback after IXFR failure ($n)" +ret=0 # Provide a broken IXFR response and a working fallback AXFR response @@ -129,14 +131,13 @@ EOF sleep 1 -$RNDCCMD 10.53.0.1 refresh nil +$RNDCCMD 10.53.0.1 refresh nil | sed 's/^/ns1 /' | cat_i sleep 2 -$DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'fallback AXFR' >/dev/null || { - echo_i "failed" - status=1 -} +$DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'fallback AXFR' >/dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) n=$((n+1)) echo_i "testing ixfr-from-differences option ($n)" @@ -146,150 +147,84 @@ if [ $? -ne 0 ] then echo_i "named-checkzone returned failure on ns3/mytest.db" fi -# modify the primary -#echo_i "digging against primary: " -#$DIG $DIGOPTS @10.53.0.3 a host1.test. -#echo_i "digging against secondary: " -#$DIG $DIGOPTS @10.53.0.4 a host1.test. -# wait for secondary to be stable -for i in 0 1 2 3 4 5 6 7 8 9 -do - $DIG $DIGOPTS +tcp @10.53.0.4 SOA test > dig.out.test$n - grep -i "hostmaster\.test\..1" dig.out.test$n > /dev/null && break - sleep 1 -done +retry_quiet 10 wait_for_serial 10.53.0.4 test. 1 dig.out.test$n || ret=1 + +nextpart ns4/named.run > /dev/null # modify the primary cp ns3/mytest1.db ns3/mytest.db $RNDCCMD 10.53.0.3 reload | sed 's/^/ns3 /' | cat_i -#wait for primary to reload load -for i in 0 1 2 3 4 5 6 7 8 9 -do - $DIG $DIGOPTS +tcp @10.53.0.3 SOA test > dig.out.test$n - grep -i "hostmaster\.test\..2" dig.out.test$n > /dev/null && break - sleep 1 -done +# wait for primary to reload +retry_quiet 10 wait_for_serial 10.53.0.3 test. 2 dig.out.test$n || ret=1 -#wait for secondary to transfer zone -for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -do - $DIG $DIGOPTS +tcp @10.53.0.4 SOA test > dig.out.test$n - grep -i "hostmaster\.test\..2" dig.out.test$n > /dev/null && break - - # re-notify if we've been waiting a long time - if [ $i -ge 5 ]; then - $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i - fi - sleep 1 -done - -# secondary should have gotten notify and updated - -for i in 0 1 2 3 4 5 6 7 8 9 -do - INCR=`grep "test/IN/primary" ns4/named.run|grep "got incremental"|wc -l` - [ $INCR -eq 1 ] && break - sleep 1 -done -if [ $INCR -ne 1 ] -then - echo_i "failed to get incremental response" - status=1 +# wait for secondary to reload +tret=0 +retry_quiet 5 wait_for_serial 10.53.0.4 test. 2 dig.out.test$n || tret=1 +if [ $tret -eq 1 ]; then + # re-noitfy after 5 seconds, then wait another 10 + $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i + retry_quiet 10 wait_for_serial 10.53.0.4 test. 2 dig.out.test$n || ret=1 fi +wait_for_log 10 'got incremental' ns4/named.run || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + n=$((n+1)) -echo_i "testing request-ixfr option in view vs zone ($n)" +echo_i "testing 'request-ixfr no' option inheritance from view ($n)" +ret=0 # There's a view with 2 zones. In the view, "request-ixfr yes" # but in the zone "sub.test", request-ixfr no" # we want to make sure that a change to sub.test results in AXFR, while # changes to test. result in IXFR -echo_ic "this result should be AXFR" cp ns3/subtest1.db ns3/subtest.db # change to sub.test zone, should be AXFR +nextpart ns4/named.run > /dev/null $RNDCCMD 10.53.0.3 reload | sed 's/^/ns3 /' | cat_i -#wait for primary to reload zone -for i in 0 1 2 3 4 5 6 7 8 9 -do - $DIG $DIGOPTS +tcp @10.53.0.3 SOA sub.test > dig.out.test$n - grep -i "hostmaster\.test\..3" dig.out.test$n > /dev/null && break - sleep 1 -done +# wait for primary to reload +retry_quiet 10 wait_for_serial 10.53.0.3 sub.test. 3 dig.out.test$n || ret=1 -#wait for secondary to transfer zone -for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -do - $DIG $DIGOPTS +tcp @10.53.0.4 SOA sub.test > dig.out.test$n - grep -i "hostmaster\.test\..3" dig.out.test$n > /dev/null && break - - # re-notify if we've been waiting a long time - if [ $i -ge 5 ]; then - $RNDCCMD 10.53.0.3 notify sub.test | set 's/^/ns3 /' | cat_i - fi - sleep 1 -done - -echo_ic "this result should be AXFR" -for i in 0 1 2 3 4 5 6 7 8 9 -do - NONINCR=`grep 'sub\.test/IN/primary' ns4/named.run|grep "got nonincremental" | wc -l` - [ $NONINCR -eq 2 ] && break - sleep 1 -done -if [ $NONINCR -ne 2 ] -then - echo_ic "failed to get nonincremental response in 2nd AXFR test" - - echo_i "failed" - status=1 -else - echo_ic "success: AXFR it was" +# wait for secondary to reload +tret=0 +retry_quiet 5 wait_for_serial 10.53.0.4 sub.test. 3 dig.out.test$n || tret=1 +if [ $tret -eq 1 ]; then + # re-noitfy after 5 seconds, then wait another 10 + $RNDCCMD 10.53.0.3 notify sub.test | set 's/^/ns3 /' | cat_i + retry_quiet 10 wait_for_serial 10.53.0.4 sub.test. 3 dig.out.test$n || ret=1 fi -echo_ic "this result should be IXFR" -cp ns3/mytest2.db ns3/mytest.db # change to test zone, should be IXFR -$RNDCCMD 10.53.0.3 reload | sed 's/^/ns3 /' | cat_i - -# wait for primary to reload zone -for i in 0 1 2 3 4 5 6 7 8 9 -do - $DIG +tcp -p 5300 @10.53.0.3 SOA test > dig.out.test$n - grep -i "hostmaster\.test\..4" dig.out.test$n > /dev/null && break - sleep 1 -done - -# wait for secondary to transfer zone -for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -do - $DIG $DIGOPTS +tcp @10.53.0.4 SOA test > dig.out.test$n - grep -i "hostmaster\.test\..4" dig.out.test$n > /dev/null && break - - # re-notify if we've been waiting a long time - if [ $i -ge 5 ]; then - $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i - fi - sleep 1 -done - -for i in 0 1 2 3 4 5 6 7 8 9 -do - INCR=`grep "test/IN/primary" ns4/named.run|grep "got incremental"|wc -l` - [ $INCR -eq 2 ] && break - sleep 1 -done -if [ $INCR -ne 2 ] -then - echo_ic "failed to get incremental response in 2nd IXFR test" - - echo_i "failed" - status=1 -else - echo_ic "success: IXFR it was" -fi +wait_for_log 10 'got nonincremental response' ns4/named.run || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) n=$((n+1)) +echo_i "testing 'request-ixfr yes' option inheritance from view ($n)" +ret=0 +cp ns3/mytest2.db ns3/mytest.db # change to test zone, should be IXFR +nextpart ns4/named.run > /dev/null +$RNDCCMD 10.53.0.3 reload | sed 's/^/ns3 /' | cat_i + +# wait for primary to reload +retry_quiet 10 wait_for_serial 10.53.0.3 test. 3 dig.out.test$n || ret=1 + +# wait for secondary to reload +tret=0 +retry_quiet 5 wait_for_serial 10.53.0.4 test. 3 dig.out.test$n || tret=1 +if [ $tret -eq 1 ]; then + # re-noitfy after 5 seconds, then wait another 10 + $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i + retry_quiet 10 wait_for_serial 10.53.0.4 test. 3 dig.out.test$n || ret=1 +fi + +wait_for_log 10 'got incremental response' ns4/named.run || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +ret=0 echo_i "testing DiG's handling of a multi message AXFR style IXFR response ($n)" ( (sleep 10 && kill $$) 2>/dev/null & @@ -298,9 +233,11 @@ $DIG -p ${PORT} ixfr=0 large @10.53.0.3 > dig.out.test$n kill $sub ) lines=`grep hostmaster.large dig.out.test$n | wc -l` -test ${lines:-0} -eq 2 || { echo_i "failed"; status=1; } +test ${lines:-0} -eq 2 || ret=1 messages=`sed -n 's/^;;.*messages \([0-9]*\),.*/\1/p' dig.out.test$n` -test ${messages:-0} -gt 1 || { echo_i "failed"; status=1; } +test ${messages:-0} -gt 1 || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) n=$((n+1)) echo_i "test 'dig +notcp ixfr=' vs 'dig ixfr= +notcp' vs 'dig ixfr=' ($n)" @@ -314,24 +251,19 @@ awk '$4 == "SOA" { if ($7 == 3) exit(0); else exit(1);}' dig.out1.test$n || ret= # Should be incremental transfer. $DIG $DIGOPTS ixfr=1 test @10.53.0.4 > dig.out3.test$n || ret=1 awk '$4 == "SOA" { soacnt++} END { if (soacnt == 6) exit(0); else exit(1);}' dig.out3.test$n || ret=1 -if [ ${ret} != 0 ]; then - echo_i "failed" - status=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +# make sure ns5 has transfered the zone +# wait for secondary to reload +tret=0 +retry_quiet 5 wait_for_serial 10.53.0.5 test. 4 dig.out.test$n || tret=1 +if [ $tret -eq 1 ]; then + # re-noitfy after 5 seconds, then wait another 10 + $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i + retry_quiet 10 wait_for_serial 10.53.0.5 test. 3 dig.out.test$n || ret=1 fi -# wait for secondary to transfer zone -for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -do - $DIG $DIGOPTS +tcp @10.53.0.5 SOA test > dig.out.test$n - grep -i "hostmaster\.test\..4" dig.out.test$n > /dev/null && break - - # re-notify if we've been waiting a long time - if [ $i -ge 5 ]; then - $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i - fi - sleep 1 -done - n=$((n+1)) echo_i "test 'provide-ixfr no;' (serial < current) ($n)" ret=0 @@ -342,32 +274,6 @@ $DIG $DIGOPTS ixfr=1 test @10.53.0.5 > dig.out1.test$n || ret=1 $DIG $DIGOPTS ixfr=1 +notcp test @10.53.0.5 > dig.out2.test$n || ret=1 awk '$4 == "SOA" { soacnt++} END {if (soacnt == 2) exit(0); else exit(1);}' dig.out1.test$n || ret=1 awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out2.test$n || ret=1 -msg="IXFR delta response disabled due to 'provide-ixfr no;' being set" -nextpart ns5/named.run | grep "$msg" > /dev/null || ret=1 -if [ $ret != 0 ]; then echo_i "failed"; fi -status=$((status+ret)) - -n=$((n+1)) -echo_i "test 'provide-ixfr no;' (serial = current) ($n)" -ret=0 -# Should be "AXFR style" response -$DIG $DIGOPTS ixfr=3 test @10.53.0.5 > dig.out1.test$n || ret=1 -# Should be "switch to TCP" response -$DIG $DIGOPTS ixfr=3 +notcp test @10.53.0.5 > dig.out2.test$n || ret=1 -awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out1.test$n || ret=1 -awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out2.test$n || ret=1 -if [ $ret != 0 ]; then echo_i "failed"; fi -status=$((status+ret)) - -n=$((n+1)) -echo_i "test 'provide-ixfr no;' (serial > current) ($n)" -ret=0 -# Should be "AXFR style" response -$DIG $DIGOPTS ixfr=4 test @10.53.0.5 > dig.out1.test$n || ret=1 -# Should be "switch to TCP" response -$DIG $DIGOPTS ixfr=4 +notcp test @10.53.0.5 > dig.out2.test$n || ret=1 -awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out1.test$n || ret=1 -awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out2.test$n || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) @@ -376,32 +282,51 @@ echo_i "checking whether dig calculates IXFR statistics correctly ($n)" ret=0 $DIG $DIGOPTS +noedns +stat -b 10.53.0.4 @10.53.0.4 test. ixfr=2 > dig.out1.test$n get_dig_xfer_stats dig.out1.test$n > stats.dig -diff ixfr-stats.good stats.dig || ret=1 +diff ixfr-stats.good stats.dig > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) # Note: in the next two tests, we use ns4 logs for checking both incoming and # outgoing transfer statistics as ns4 is both a secondary server (for ns3) and a # primary server (for dig queries from the previous test) for "test". + +_wait_for_stats () { + get_named_xfer_stats ns4/named.run "$1" test "$2" > "$3" + diff ixfr-stats.good "$3" > /dev/null || return 1 + return 0 +} + n=$((n+1)) echo_i "checking whether named calculates incoming IXFR statistics correctly ($n)" ret=0 -get_named_xfer_stats ns4/named.run 10.53.0.3 test "Transfer completed" > stats.incoming -diff ixfr-stats.good stats.incoming || ret=1 +retry_quiet 10 _wait_for_stats 10.53.0.3 "Transfer completed" stats.incoming if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) echo_i "checking whether named calculates outgoing IXFR statistics correctly ($n)" -ret=1 -for i in 0 1 2 3 4 5 6 7 8 9; do - get_named_xfer_stats ns4/named.run 10.53.0.4 test "IXFR ended" > stats.outgoing - if diff ixfr-stats.good stats.outgoing > /dev/null; then - ret=0 - break - fi - sleep 1 -done +retry_quiet 10 _wait_for_stats 10.53.0.4 "IXFR ended" stats.outgoing +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +ret=0 +echo_i "testing fallback to AXFR when max-ixfr-ratio is exceeded ($n)" +nextpart ns4/named.run > /dev/null + +cp ns3/mytest3.db ns3/mytest.db # change to test zone, too big for IXFR +$RNDCCMD 10.53.0.3 reload | sed 's/^/ns3 /' | cat_i + +# wait for secondary to reload +tret=0 +retry_quiet 5 wait_for_serial 10.53.0.4 test. 4 dig.out.test$n || tret=1 +if [ $tret -eq 1 ]; then + # re-noitfy after 5 seconds, then wait another 10 + $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i + retry_quiet 10 wait_for_serial 10.53.0.4 test. 4 dig.out.test$n || ret=1 +fi + +wait_for_log 10 'got nonincremental response' ns4/named.run || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) From 077e2c2a74bebbf2b633064477f04f8bb66b9135 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 27 Feb 2020 11:43:03 -0800 Subject: [PATCH 6/8] add serial number to "transfer ended" log messages --- lib/dns/xfrin.c | 4 ++-- lib/ns/xfrout.c | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index a46b3d9b74..d218f747d6 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -1529,10 +1529,10 @@ maybe_free(dns_xfrin_ctx_t *xfr) { xfrin_log(xfr, ISC_LOG_INFO, "Transfer completed: %d messages, %d records, " "%" PRIu64 " bytes, " - "%u.%03u secs (%u bytes/sec)", + "%u.%03u secs (%u bytes/sec) (serial %u)", xfr->nmsg, xfr->nrecs, xfr->nbytes, (unsigned int)(msecs / 1000), (unsigned int)(msecs % 1000), - (unsigned int)persec); + (unsigned int)persec, xfr->end_serial); if (xfr->socket != NULL) { isc_socket_detach(&xfr->socket); diff --git a/lib/ns/xfrout.c b/lib/ns/xfrout.c index da44f88c50..7736dcc040 100644 --- a/lib/ns/xfrout.c +++ b/lib/ns/xfrout.c @@ -664,6 +664,7 @@ typedef struct { bool shuttingdown; bool poll; const char *mnemonic; /* Style of transfer */ + uint32_t end_serial; /* Serial number after XFR is done */ struct xfr_stats stats; /*%< Transfer statistics */ } xfrout_ctx_t; @@ -1109,6 +1110,7 @@ have_stream: (format == dns_many_answers) ? true : false, &xfr); } + xfr->end_serial = current_serial; xfr->mnemonic = mnemonic; stream = NULL; quota = NULL; @@ -1722,10 +1724,11 @@ xfrout_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) { "%s ended: " "%" PRIu64 " messages, %" PRIu64 " records, " "%" PRIu64 " bytes, " - "%u.%03u secs (%u bytes/sec)", + "%u.%03u secs (%u bytes/sec) (serial %u)", xfr->mnemonic, xfr->stats.nmsg, xfr->stats.nrecs, xfr->stats.nbytes, (unsigned int)(msecs / 1000), - (unsigned int)(msecs % 1000), (unsigned int)persec); + (unsigned int)(msecs % 1000), (unsigned int)persec, + xfr->end_serial); /* * We're done, unreference the handle and destroy the xfr From 62202b0e6dac088b4d9815a7bc9ee8ede215a65f Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Mon, 21 Jan 2013 14:16:15 -0800 Subject: [PATCH 7/8] prevent ixfr/ns1 being removed --- bin/tests/system/ixfr/ns1/startme | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 bin/tests/system/ixfr/ns1/startme diff --git a/bin/tests/system/ixfr/ns1/startme b/bin/tests/system/ixfr/ns1/startme new file mode 100644 index 0000000000..e69de29bb2 From f5362ed13527c6c3b8795a2037add2465b6bc14b Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Sun, 23 Feb 2020 20:54:58 -0800 Subject: [PATCH 8/8] CHANGES and release note --- CHANGES | 7 +++++++ doc/notes/notes-current.rst | 13 ++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3331514d33..f425a84cea 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,13 @@ BIND 9 version number, in an effort to tightly couple internal libraries with a specific release. [GL #2387] +5562. [func] Limit the size of IXFR responses so that AXFR will + be used instead if it would be smaller. This is + controlled by the "max-ixfr-ratio" option, which + is a percentage representing the ratio of IXFR size + to the size of the entire zone. This value cannot + exceed 100%, which is the default. [GL #1515] + 5561. [bug] KASP incorrectly set signature validity to the value of the DNSKEY signature validity. This is now fixed. [GL #2383] diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 1a9a6ee168..010b9b948e 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -24,7 +24,18 @@ Known Issues New Features ~~~~~~~~~~~~ -- None. +- When a secondary server receives a large incremental zone + transfer (IXFR), it can have a negative impact on query + performance while the incremental changes are applied to + the zone. To address this, ``named`` can now + limit the size of IXFR responses it sends in response to zone + transfer requests. If an IXFR response would be larger than an + AXFR of the entire zone, it will send an AXFR resonse instead. + + This behavior is controlled by the ``max-ixfr-ratio`` + option - a percentage value representing the ratio of IXFR size + to the size of a full zone transfer. This value cannot exceed + 100%, which is also the default. [GL #1515] Removed Features ~~~~~~~~~~~~~~~~