From: Aram Sargsyan Date: Tue, 22 Apr 2025 13:33:48 +0000 (+0000) Subject: Implement a new 'notify-defer' configuration option X-Git-Tag: v9.21.9~45^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e42d6b48108e6c879fb7d152194708b0cb6d62b0;p=thirdparty%2Fbind9.git Implement a new 'notify-defer' configuration option This new option sets the delay, in seconds, to wait before sending a set of NOTIFY messages for a zone. Whenever a NOTIFY message is ready to be sent, sending will be deferred for this duration. --- diff --git a/bin/named/config.c b/bin/named/config.c index eb93c8a5f3f..7bbf429456d 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -239,6 +239,7 @@ options {\n\ min-transfer-rate-in 10240 5;\n\ multi-master no;\n\ notify yes;\n\ + notify-defer 0;\n\ notify-delay 5;\n\ notify-to-soa no;\n\ provide-zoneversion yes;\n\ diff --git a/bin/named/server.c b/bin/named/server.c index 0b3e7352855..7cd06cfc353 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -10547,7 +10547,7 @@ named_server_notifycommand(named_server_t *server, isc_lex_t *lex, return ISC_R_UNEXPECTEDEND; } - dns_zone_notify(zone); + dns_zone_notify(zone, true); dns_zone_detach(&zone); (void)putstr(text, msg); (void)putnull(text); diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 84b011b3b69..7091c5b36cf 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1441,6 +1441,11 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, INSIST(result == ISC_R_SUCCESS && obj != NULL); dns_zone_setnotifydelay(zone, cfg_obj_asuint32(obj)); + obj = NULL; + result = named_config_get(maps, "notify-defer", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setnotifydefer(zone, cfg_obj_asuint32(obj)); + obj = NULL; result = named_config_get(maps, "check-sibling", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); diff --git a/bin/tests/system/catz/ns1/named.conf.in b/bin/tests/system/catz/ns1/named.conf.in index fdf8041f319..2c04668280e 100644 --- a/bin/tests/system/catz/ns1/named.conf.in +++ b/bin/tests/system/catz/ns1/named.conf.in @@ -69,6 +69,16 @@ view "default" { /* catalog5 is missing on purpose */ + zone "catalog6.example" { + type primary; + file "catalog6.example.db"; + allow-transfer { any; }; + allow-update { any; }; + also-notify { 10.53.0.2; }; + notify explicit; + notify-defer 5; + }; + # No "version" property zone "catalog-bad1.example" { type primary; diff --git a/bin/tests/system/catz/ns2/named1.conf.in b/bin/tests/system/catz/ns2/named1.conf.in index e16416d22d1..2b8d05217a6 100644 --- a/bin/tests/system/catz/ns2/named1.conf.in +++ b/bin/tests/system/catz/ns2/named1.conf.in @@ -62,6 +62,9 @@ view "default" { #T2 zone "catalog5.example" #T2 min-update-interval 1s #T2 default-primaries { 10.53.0.1; }; + zone "catalog6.example" + min-update-interval 1s + default-primaries { 10.53.0.1; }; zone "catalog-bad1.example" default-masters { 10.53.0.1; } min-update-interval 1s @@ -129,6 +132,12 @@ view "default" { primaries { 10.53.0.1; }; }; + zone "catalog6.example" { + type secondary; + file "catalog6.example.db"; + primaries { 10.53.0.1; }; + }; + # When the following zone configuration is enabled, "dom3.example" should # already exist as a member of "catalog1.example", and named should be able # to deal with that situation (see GL #3911). Make sure that this duplicate diff --git a/bin/tests/system/catz/ns2/named2.conf.in b/bin/tests/system/catz/ns2/named2.conf.in index ead45a85afa..2255c6625ef 100644 --- a/bin/tests/system/catz/ns2/named2.conf.in +++ b/bin/tests/system/catz/ns2/named2.conf.in @@ -79,6 +79,12 @@ view "default" { primaries { 10.53.0.1; }; }; + zone "catalog6.example" { + type secondary; + file "catalog6.example.db"; + primaries { 10.53.0.1; }; + }; + # No "version" property zone "catalog-bad1.example" { type secondary; diff --git a/bin/tests/system/catz/setup.sh b/bin/tests/system/catz/setup.sh index c33ce7f1c16..c7e24fa1de5 100644 --- a/bin/tests/system/catz/setup.sh +++ b/bin/tests/system/catz/setup.sh @@ -22,6 +22,8 @@ cp -f ns1/catalog.example.db.in ns1/catalog1.example.db cp -f ns3/catalog.example.db.in ns3/catalog2.example.db cp -f ns1/catalog.example.db.in ns1/catalog3.example.db cp -f ns1/catalog.example.db.in ns1/catalog4.example.db +# catalog5 is missing on purpose +cp -f ns1/catalog.example.db.in ns1/catalog6.example.db cp -f ns1/catalog.example.db.in ns1/catalog-tls.example.db cp -f ns4/catalog.example.db.in ns4/catalog-self.example.db diff --git a/bin/tests/system/catz/tests.sh b/bin/tests/system/catz/tests.sh index 2dd89ff7f88..5d8c7f36224 100644 --- a/bin/tests/system/catz/tests.sh +++ b/bin/tests/system/catz/tests.sh @@ -2669,6 +2669,133 @@ rndccmd 10.53.0.4 reload || ret=1 if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) +########################################################################## + +nextpart ns2/named.run >/dev/null + +n=$((n + 1)) +echo_i "Adding a domain dom20.example. to primary via RNDC ($n)" +ret=0 +# enough initial content for IXFR response when TXT record is added below +echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" >ns1/dom20.example.db +echo "@ 3600 IN NS invalid." >>ns1/dom20.example.db +echo "foo 3600 IN TXT some content here" >>ns1/dom20.example.db +echo "bar 3600 IN TXT some content here" >>ns1/dom20.example.db +echo "xxx 3600 IN TXT some content here" >>ns1/dom20.example.db +echo "yyy 3600 IN TXT some content here" >>ns1/dom20.example.db +rndccmd 10.53.0.1 addzone dom20.example. in default '{ type primary; file "dom20.example.db"; allow-update { any; }; notify explicit; also-notify { 10.53.0.2; }; };' || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that dom20.example. is now served by primary ($n)" +ret=0 +wait_for_soa @10.53.0.1 dom20.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "Adding domain dom20.example. to catalog6 zone ($n)" +ret=0 +$NSUPDATE -d <>nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.1 ${PORT} + update add dom20.zones.catalog6.example. 3600 IN PTR dom20.example. + send +END +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "waiting for secondary to sync up ($n)" +ret=0 +start=$(date +%s) +wait_for_message ns2/named.run "catz: adding zone 'dom20.example' from catalog 'catalog6.example'" \ + && wait_for_message ns2/named.run "transfer of 'dom20.example/IN/default' from 10.53.0.1#${PORT}: Transfer status: success" || ret=1 +end=$(date +%s) +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that 'notify-defer 5;' worked ($n)" +ret=0 +elapsed=$(($end - $start)) +[ $elapsed -ge 5 ] || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that dom20.example. is served by secondary ($n)" +ret=0 +wait_for_soa @10.53.0.2 dom20.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +nextpart ns2/named.run >/dev/null + +n=$((n + 1)) +echo_i "Adding a domain dom21.example. to primary via RNDC ($n)" +ret=0 +# enough initial content for IXFR response when TXT record is added below +echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" >ns1/dom21.example.db +echo "@ 3600 IN NS invalid." >>ns1/dom21.example.db +echo "foo 3600 IN TXT some content here" >>ns1/dom21.example.db +echo "bar 3600 IN TXT some content here" >>ns1/dom21.example.db +echo "xxx 3600 IN TXT some content here" >>ns1/dom21.example.db +echo "yyy 3600 IN TXT some content here" >>ns1/dom21.example.db +rndccmd 10.53.0.1 addzone dom21.example. in default '{ type primary; file "dom21.example.db"; allow-update { any; }; notify explicit; also-notify { 10.53.0.2; }; };' || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that dom21.example. is now served by primary ($n)" +ret=0 +wait_for_soa @10.53.0.1 dom21.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "Adding domain dom21.example. to catalog6 zone ($n)" +ret=0 +$NSUPDATE -d <>nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.1 ${PORT} + update add dom21.zones.catalog6.example. 3600 IN PTR dom21.example. + send +END +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "Sending 'rndc notify catalog6.example.' to primary via RNDC ($n)" +ret=0 +rndccmd 10.53.0.1 notify catalog6.example. || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "waiting for secondary to sync up ($n)" +ret=0 +start=$(date +%s) +wait_for_message ns2/named.run "catz: adding zone 'dom21.example' from catalog 'catalog6.example'" \ + && wait_for_message ns2/named.run "transfer of 'dom21.example/IN/default' from 10.53.0.1#${PORT}: Transfer status: success" || ret=1 +end=$(date +%s) +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that 'notify-defer 5;' was ignored because of implicit notify command ($n)" +ret=0 +elapsed=$(($end - $start)) +[ $elapsed -lt 5 ] || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that dom21.example. is served by secondary ($n)" +ret=0 +wait_for_soa @10.53.0.2 dom21.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + ########################################################################## echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/bin/tests/system/dyndb/driver/zone.c b/bin/tests/system/dyndb/driver/zone.c index 247caea75ee..662537a75c3 100644 --- a/bin/tests/system/dyndb/driver/zone.c +++ b/bin/tests/system/dyndb/driver/zone.c @@ -213,7 +213,7 @@ load_zone(dns_zone_t *zone) { dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u", serial); if (zone_dynamic) { - dns_zone_notify(zone); + dns_zone_notify(zone, false); } cleanup: diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 35c65fe0353..93f1497e663 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -4460,6 +4460,25 @@ Tuning when resolving a client query, before terminating the query to avoid a CNAME loop. Valid values are 1 to 255. The default is 11. +.. namedconf:statement:: notify-defer + :tags: transfer, zone + :short: Sets the defer time (in seconds) before sending NOTIFY messages for a zone. + + This sets the delay, in seconds, to wait before sending a set of NOTIFY + messages for a zone. Whenever a NOTIFY message is ready to be sent, sending + will be deferred for this duration. This can be useful, for example, when + for some operation needs a catalog zone is updated with new member zones + before these member zones are actually ready to be tranferred. The delay can + be tuned for the catalog zone to an amount of time after which the member + zones are usually known to become ready. The default is 0 seconds. + + .. warning:: + This option is not to be confused with the :any:`notify-delay` option. + + .. note:: + An implicit :option:`rndc notify` command for a zone overrides the + effects of this option. + .. namedconf:statement:: notify-delay :tags: transfer, zone :short: Sets the delay (in seconds) between sending sets of NOTIFY messages for a zone. @@ -4473,6 +4492,9 @@ Tuning The overall rate at which NOTIFY messages are sent for all zones is controlled by :any:`notify-rate`. + .. warning:: + This option is not to be confused with the :any:`notify-defer` option. + .. namedconf:statement:: max-rsa-exponent-size :tags: dnssec, query :short: Sets the maximum RSA exponent size (in bits) when validating. diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index 4db4d9f19b3..2f49a34e9cb 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -29,6 +29,7 @@ zone [ ] { min-transfer-rate-in ; multi-master ; notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-source ( | * ); notify-source-v6 ( | * ); diff --git a/doc/misc/options b/doc/misc/options index 0d05d4f9395..1cb0d964438 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -210,6 +210,7 @@ options { no-case-compress { ; ... }; nocookie-udp-size ; notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-rate ; notify-source ( | * ); @@ -499,6 +500,7 @@ view [ ] { no-case-compress { ; ... }; nocookie-udp-size ; notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-source ( | * ); notify-source-v6 ( | * ); diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt index 28d8dad453f..d43f66a55ae 100644 --- a/doc/misc/primary.zoneopt +++ b/doc/misc/primary.zoneopt @@ -43,6 +43,7 @@ zone [ ] { max-types-per-name ; max-zone-ttl ( unlimited | ); // deprecated notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-source ( | * ); notify-source-v6 ( | * ); diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt index 08c7008ca21..b1dbcea978e 100644 --- a/doc/misc/secondary.zoneopt +++ b/doc/misc/secondary.zoneopt @@ -41,6 +41,7 @@ zone [ ] { min-transfer-rate-in ; multi-master ; notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-source ( | * ); notify-source-v6 ( | * ); diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 9251101dab0..4b4cf9388c9 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -1341,9 +1341,10 @@ dns_zone_getredirecttype(dns_zone_t *zone); */ void -dns_zone_notify(dns_zone_t *zone); +dns_zone_notify(dns_zone_t *zone, bool nodefer); /*%< - * Generate notify events for this zone. + * Generate notify events for this zone. If 'nodefer' is true, the + * 'notify-defer' configuration option is ingored. * * Requires: *\li 'zone' to be a valid zone. @@ -2184,6 +2185,16 @@ dns_zone_setcheckisservedby(dns_zone_t *zone, * 'zone' to be a valid zone. */ +void +dns_zone_setnotifydefer(dns_zone_t *zone, uint32_t defer); +/*%< + * Set the wait/defer time (in seconds) before notify messages are sent when + * they are ready. + * + * Requires: + * 'zone' to be valid. + */ + void dns_zone_setnotifydelay(dns_zone_t *zone, uint32_t delay); /*%< diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 6fef5911c80..89d4466188d 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -408,6 +408,7 @@ struct dns_zone { dns_stats_t *rcvquerystats; dns_stats_t *dnssecsignstats; uint32_t notifydelay; + uint32_t notifydefer; dns_isselffunc_t isself; void *isselfarg; @@ -573,7 +574,12 @@ typedef enum { * notify due to the zone * just being loaded for * the first time. */ - DNS_ZONEFLG_FIRSTREFRESH = 0x100000000U, /*%< First refresh pending */ + DNS_ZONEFLG_NOTIFYNODEFER = 0x100000000U, /*%< ignore the + * notify-defer option. */ + DNS_ZONEFLG_NOTIFYDEFERRED = 0x200000000U, /*%< notify was deferred + * according to the + * notify-defer option. */ + DNS_ZONEFLG_FIRSTREFRESH = 0x400000000U, /*%< First refresh pending */ DNS_ZONEFLG___MAX = UINT64_MAX, /* trick to make the ENUM 64-bit wide */ } dns_zoneflg_t; @@ -1060,6 +1066,19 @@ static const char *dbargv_default[] = { ZONEDB_DEFAULT }; } \ } while (0) +#define DNS_ZONE_TIME_SUBTRACT(a, b, c) \ + do { \ + isc_interval_t _i; \ + isc_interval_set(&_i, (b), 0); \ + if (isc_time_subtract((a), &_i, (c)) != ISC_R_SUCCESS) { \ + dns_zone_log(zone, ISC_LOG_WARNING, \ + "epoch approaching: upgrade required: " \ + "isc_time_subtract() failed"); \ + isc_interval_set(&_i, (b) / 2, 0); \ + (void)isc_time_subtract((a), &_i, (c)); \ + } \ + } while (0) + typedef struct nsec3param nsec3param_t; struct nsec3param { dns_rdata_nsec3param_t rdata; @@ -11336,6 +11355,17 @@ zone_maintenance(dns_zone_t *zone) { * primaries after. */ LOCK_ZONE(zone); + if (zone->notifydefer != 0 && + !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOTIFYNODEFER) && + !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOTIFYDEFERRED)) + { + if (isc_time_compare(&now, &zone->notifytime) > 0) { + zone->notifytime = now; + } + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOTIFYDEFERRED); + DNS_ZONE_TIME_ADD(&zone->notifytime, zone->notifydefer, + &zone->notifytime); + } notify = (zone->type == dns_zone_secondary || zone->type == dns_zone_mirror) && (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) || @@ -12842,14 +12872,27 @@ cleanup: } void -dns_zone_notify(dns_zone_t *zone) { +dns_zone_notify(dns_zone_t *zone, bool nodefer) { isc_time_t now; REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); - + if (nodefer) { + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOTIFYDEFERRED)) { + /* + * We have previously deferred the notify, but we have a + * new request not to defer it. Reverse the deferring + * operation. + */ + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOTIFYDEFERRED); + DNS_ZONE_TIME_SUBTRACT(&zone->notifytime, + zone->notifydefer, + &zone->notifytime); + } + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOTIFYNODEFER); + } now = isc_time_now(); zone_settimer(zone, &now); UNLOCK_ZONE(zone); @@ -12881,8 +12924,10 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { LOCK_ZONE(zone); startup = !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); - DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); - DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY | + DNS_ZONEFLG_NEEDSTARTUPNOTIFY | + DNS_ZONEFLG_NOTIFYNODEFER | + DNS_ZONEFLG_NOTIFYDEFERRED); notifytype = zone->notifytype; DNS_ZONE_TIME_ADD(now, zone->notifydelay, &zone->notifytime); UNLOCK_ZONE(zone); @@ -20186,6 +20231,15 @@ dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) { UNLOCK_ZONE(zone); } +void +dns_zone_setnotifydefer(dns_zone_t *zone, uint32_t defer) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->notifydefer = defer; + UNLOCK_ZONE(zone); +} + void dns_zone_setnotifydelay(dns_zone_t *zone, uint32_t delay) { REQUIRE(DNS_ZONE_VALID(zone)); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index c2c5ae28991..7372a604152 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2342,6 +2342,8 @@ static cfg_clausedef_t zone_clauses[] = { CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB }, { "notify", &cfg_type_notifytype, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, + { "notify-defer", &cfg_type_uint32, + CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, { "notify-delay", &cfg_type_uint32, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, { "notify-source", &cfg_type_sockaddr4wild, diff --git a/lib/ns/update.c b/lib/ns/update.c index c83f0111814..9e536de2f98 100644 --- a/lib/ns/update.c +++ b/lib/ns/update.c @@ -3371,7 +3371,7 @@ update_action(void *arg) { /* * Notify secondaries of the change we just made. */ - dns_zone_notify(zone); + dns_zone_notify(zone, false); } else { update_log(client, zone, LOGLEVEL_DEBUG, "redundant request"); dns_db_closeversion(db, &ver, true);