2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 05:57:52 +00:00

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.

(cherry picked from commit e42d6b48108e6c879fb7d152194708b0cb6d62b0)
This commit is contained in:
Aram Sargsyan 2025-04-22 13:33:48 +00:00
parent 0dd96098f9
commit a90e3b9e6f
18 changed files with 269 additions and 11 deletions

View File

@ -242,6 +242,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\
serial-update-method increment;\n\

View File

@ -11245,7 +11245,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);

View File

@ -1449,6 +1449,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);

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 <<END >>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 <<END >>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

View File

@ -219,7 +219,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:

View File

@ -4670,6 +4670,29 @@ 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.
.. note::
This options is ignored for notifies sent during the :any:`dialup`
process.
.. namedconf:statement:: notify-delay
:tags: transfer, zone
:short: Sets the delay (in seconds) between sending sets of NOTIFY messages for a zone.
@ -4683,6 +4706,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.

View File

@ -29,6 +29,7 @@ zone <string> [ <class> ] {
min-transfer-rate-in <integer> <integer>;
multi-master <boolean>;
notify ( explicit | master-only | primary-only | <boolean> );
notify-defer <integer>;
notify-delay <integer>;
notify-source ( <ipv4_address> | * );
notify-source-v6 ( <ipv6_address> | * );

View File

@ -217,6 +217,7 @@ options {
no-case-compress { <address_match_element>; ... };
nocookie-udp-size <integer>;
notify ( explicit | master-only | primary-only | <boolean> );
notify-defer <integer>;
notify-delay <integer>;
notify-rate <integer>;
notify-source ( <ipv4_address> | * );
@ -507,6 +508,7 @@ view <string> [ <class> ] {
no-case-compress { <address_match_element>; ... };
nocookie-udp-size <integer>;
notify ( explicit | master-only | primary-only | <boolean> );
notify-defer <integer>;
notify-delay <integer>;
notify-source ( <ipv4_address> | * );
notify-source-v6 ( <ipv6_address> | * );

View File

@ -43,6 +43,7 @@ zone <string> [ <class> ] {
max-types-per-name <integer>;
max-zone-ttl ( unlimited | <duration> ); // deprecated
notify ( explicit | master-only | primary-only | <boolean> );
notify-defer <integer>;
notify-delay <integer>;
notify-source ( <ipv4_address> | * );
notify-source-v6 ( <ipv6_address> | * );

View File

@ -41,6 +41,7 @@ zone <string> [ <class> ] {
min-transfer-rate-in <integer> <integer>;
multi-master <boolean>;
notify ( explicit | master-only | primary-only | <boolean> );
notify-defer <integer>;
notify-delay <integer>;
notify-source ( <ipv4_address> | * );
notify-source-v6 ( <ipv6_address> | * );

View File

@ -1375,9 +1375,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.
@ -2233,6 +2234,16 @@ dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns);
* '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);
/*%<

View File

@ -401,6 +401,7 @@ struct dns_zone {
dns_stats_t *rcvquerystats;
dns_stats_t *dnssecsignstats;
uint32_t notifydelay;
uint32_t notifydefer;
dns_isselffunc_t isself;
void *isselfarg;
@ -568,7 +569,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;
@ -1055,6 +1061,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;
@ -11167,6 +11186,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) ||
@ -12694,14 +12724,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);
@ -12733,8 +12776,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);
@ -19833,7 +19878,7 @@ dns_zone_dialup(dns_zone_t *zone) {
DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH));
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY)) {
dns_zone_notify(zone);
dns_zone_notify(zone, true);
}
if (zone->type != dns_zone_primary &&
dns_remote_addresses(&zone->primaries) != NULL &&
@ -20170,6 +20215,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));

View File

@ -2496,6 +2496,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,

View File

@ -3431,7 +3431,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);