diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl
index ac15b7c934..ae1820de0d 100644
--- a/bin/named/bind9.xsl
+++ b/bin/named/bind9.xsl
@@ -931,6 +931,7 @@
Messages Received |
Records Received |
Bytes Received |
+ Transfer Rate (B/s) |
@@ -959,6 +960,7 @@
|
|
|
+ |
diff --git a/bin/named/config.c b/bin/named/config.c
index 23df890193..e3aaecdd3e 100644
--- a/bin/named/config.c
+++ b/bin/named/config.c
@@ -232,6 +232,7 @@ options {\n\
max-transfer-time-out 120;\n\
min-refresh-time 300;\n\
min-retry-time 500;\n\
+ min-transfer-rate-in 10240 5;\n\
multi-master no;\n\
notify yes;\n\
notify-delay 5;\n\
diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c
index 887f21a1ed..8b1a1cd91c 100644
--- a/bin/named/statschannel.c
+++ b/bin/named/statschannel.c
@@ -1488,6 +1488,7 @@ xfrin_xmlrender(dns_zone_t *zone, void *arg) {
unsigned int nmsg = 0;
unsigned int nrecs = 0;
uint64_t nbytes = 0;
+ uint64_t rate = 0;
statlevel = dns_zone_getstatlevel(zone);
if (statlevel == dns_zonestat_none) {
@@ -1701,7 +1702,7 @@ xfrin_xmlrender(dns_zone_t *zone, void *arg) {
TRY0(xmlTextWriterEndElement(writer));
if (is_running) {
- dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes);
+ dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes, &rate);
}
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nmsg"));
TRY0(xmlTextWriterWriteFormatString(writer, "%u", nmsg));
@@ -1712,6 +1713,9 @@ xfrin_xmlrender(dns_zone_t *zone, void *arg) {
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nbytes"));
TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, nbytes));
TRY0(xmlTextWriterEndElement(writer));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rate"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, rate));
+ TRY0(xmlTextWriterEndElement(writer));
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ixfr"));
if (is_running && is_first_data_received) {
@@ -2559,6 +2563,7 @@ xfrin_jsonrender(dns_zone_t *zone, void *arg) {
unsigned int nmsg = 0;
unsigned int nrecs = 0;
uint64_t nbytes = 0;
+ uint64_t rate = 0;
statlevel = dns_zone_getstatlevel(zone);
if (statlevel == dns_zonestat_none) {
@@ -2756,7 +2761,7 @@ xfrin_jsonrender(dns_zone_t *zone, void *arg) {
}
if (is_running) {
- dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes);
+ dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes, &rate);
}
json_object_object_add(xfrinobj, "nmsg",
json_object_new_int64((int64_t)nmsg));
@@ -2766,6 +2771,10 @@ xfrin_jsonrender(dns_zone_t *zone, void *arg) {
xfrinobj, "nbytes",
json_object_new_int64(nbytes > INT64_MAX ? INT64_MAX
: (int64_t)nbytes));
+ json_object_object_add(xfrinobj, "rate",
+ json_object_new_int64(rate > INT64_MAX
+ ? INT64_MAX
+ : (int64_t)rate));
if (is_running && is_first_data_received) {
json_object_object_add(
diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c
index 00be0fe2a6..43a4943b8d 100644
--- a/bin/named/zoneconf.c
+++ b/bin/named/zoneconf.c
@@ -1874,6 +1874,33 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
}
dns_zone_setoption(mayberaw, DNS_ZONEOPT_MULTIMASTER, multi);
+ obj = NULL;
+ result = named_config_get(maps, "min-transfer-rate-in", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ uint32_t traffic_bytes =
+ cfg_obj_asuint32(cfg_tuple_get(obj, "traffic_bytes"));
+ uint32_t time_minutes =
+ cfg_obj_asuint32(cfg_tuple_get(obj, "time_minutes"));
+ if (traffic_bytes == 0) {
+ cfg_obj_log(obj, ISC_LOG_ERROR,
+ "zone '%s': 'min-transfer-rate-in' bytes"
+ "value can not be '0'",
+ zname);
+ CHECK(ISC_R_FAILURE);
+ }
+ /* Max. 28 days (in minutes). */
+ const unsigned int time_minutes_max = 28 * 24 * 60;
+ if (time_minutes < 1 || time_minutes > time_minutes_max) {
+ cfg_obj_log(obj, ISC_LOG_ERROR,
+ "zone '%s': 'min-transfer-rate-in' minutes"
+ "value is out of range (1..%u)",
+ zname, time_minutes_max);
+ CHECK(ISC_R_FAILURE);
+ }
+ dns_zone_setminxfrratein(mayberaw, traffic_bytes,
+ transferinsecs ? time_minutes
+ : time_minutes * 60);
+
obj = NULL;
result = named_config_get(maps, "max-transfer-time-in", &obj);
INSIST(result == ISC_R_SUCCESS && obj != NULL);
diff --git a/bin/tests/system/xfer/ns1/axfr-min-transfer-rate.db b/bin/tests/system/xfer/ns1/axfr-min-transfer-rate.db
new file mode 100644
index 0000000000..252925f8dc
--- /dev/null
+++ b/bin/tests/system/xfer/ns1/axfr-min-transfer-rate.db
@@ -0,0 +1,15 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; 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 https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 3600
+@ IN SOA . . 0 0 0 0 0
+@ IN NS .
+$GENERATE 1-5000 host$ TXT data-$
diff --git a/bin/tests/system/xfer/ns1/named1.conf.in b/bin/tests/system/xfer/ns1/named1.conf.in
index 990c911580..709187eff6 100644
--- a/bin/tests/system/xfer/ns1/named1.conf.in
+++ b/bin/tests/system/xfer/ns1/named1.conf.in
@@ -47,6 +47,10 @@ zone "edns-expire" {
file "edns-expire.db";
};
+zone "axfr-min-transfer-rate" {
+ type primary;
+ file "axfr-min-transfer-rate.db";
+};
zone "axfr-max-transfer-time" {
type primary;
diff --git a/bin/tests/system/xfer/ns1/named2.conf.in b/bin/tests/system/xfer/ns1/named2.conf.in
index 3c7f2fae20..aec68f3d1a 100644
--- a/bin/tests/system/xfer/ns1/named2.conf.in
+++ b/bin/tests/system/xfer/ns1/named2.conf.in
@@ -36,6 +36,11 @@ zone "." {
file "root.db";
};
+zone "axfr-min-transfer-rate" {
+ type primary;
+ file "axfr-min-transfer-rate.db";
+};
+
zone "axfr-max-transfer-time" {
type primary;
file "axfr-max-transfer-time.db";
diff --git a/bin/tests/system/xfer/ns6/named.conf.in b/bin/tests/system/xfer/ns6/named.conf.in
index d6edf8a8a4..6a51f6e82d 100644
--- a/bin/tests/system/xfer/ns6/named.conf.in
+++ b/bin/tests/system/xfer/ns6/named.conf.in
@@ -32,6 +32,7 @@ options {
ixfr-from-differences primary;
check-integrity no;
tcp-idle-timeout 600;
+ min-transfer-rate-in 10240 300; # this is tested as seconds, when used with '-T transferinsecs' (i.e. convert the default '10240 5' back so that it doesn't interfere with other tests)
};
zone "." {
@@ -57,6 +58,13 @@ zone "edns-expire" {
file "edns-expire.bk";
};
+zone "axfr-min-transfer-rate" {
+ type secondary;
+ min-transfer-rate-in 100000 5; # this is tested as seconds, when used with '-T transferinsecs' (i.e. 100000 bytes in 5 seconds)
+ primaries { 10.53.0.1; };
+ file "axfr-min-transfer-rate.bk";
+};
+
zone "axfr-max-transfer-time" {
type secondary;
max-transfer-time-in 1; # this is tested as seconds, when used with '-T transferinsecs'
diff --git a/bin/tests/system/xfer/tests.sh b/bin/tests/system/xfer/tests.sh
index a8bca3dc3a..8098da23d6 100755
--- a/bin/tests/system/xfer/tests.sh
+++ b/bin/tests/system/xfer/tests.sh
@@ -709,11 +709,22 @@ status=$((status + tmp))
nextpart ns6/named.run >/dev/null
+n=$((n + 1))
+echo_i "test min-transfer-rate-in with 5 seconds timeout ($n)"
+$RNDCCMD 10.53.0.6 retransfer axfr-min-transfer-rate 2>&1 | sed 's/^/ns6 /' | cat_i
+tmp=0
+retry_quiet 10 wait_for_message "minimum transfer rate reached: timed out" || tmp=1
+if test $tmp != 0; then echo_i "failed"; fi
+status=$((status + tmp))
+
+nextpart ns6/named.run >/dev/null
+
n=$((n + 1))
echo_i "test max-transfer-time-in with 1 second timeout ($n)"
$RNDCCMD 10.53.0.6 retransfer axfr-max-transfer-time 2>&1 | sed 's/^/ns6 /' | cat_i
tmp=0
retry_quiet 10 wait_for_message "maximum transfer time exceeded: timed out" || tmp=1
+if test $tmp != 0; then echo_i "failed"; fi
status=$((status + tmp))
# Restart ns1 with -T transferstuck
@@ -730,6 +741,7 @@ start=$(date +%s)
$RNDCCMD 10.53.0.6 retransfer axfr-max-idle-time 2>&1 | sed 's/^/ns6 /' | cat_i
tmp=0
retry_quiet 60 wait_for_message "maximum idle time exceeded: timed out" || tmp=1
+if test $tmp != 0; then echo_i "failed"; fi
if [ $tmp -eq 0 ]; then
now=$(date +%s)
diff=$((now - start))
diff --git a/bin/tests/system/xfer/tests_sh_xfer.py b/bin/tests/system/xfer/tests_sh_xfer.py
index 50efbca298..0b82e0bf12 100644
--- a/bin/tests/system/xfer/tests_sh_xfer.py
+++ b/bin/tests/system/xfer/tests_sh_xfer.py
@@ -41,6 +41,7 @@ pytestmark = pytest.mark.extra_artifacts(
"ns4/root.db",
"ns6/axfr-max-idle-time.bk",
"ns6/axfr-max-transfer-time.bk",
+ "ns6/axfr-min-transfer-rate.bk",
"ns6/axfr-rndc-retransfer-force.bk",
"ns6/edns-expire.bk",
"ns6/ixfr-too-big.bk",
diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst
index 27e789dd42..1af2777594 100644
--- a/doc/arm/reference.rst
+++ b/doc/arm/reference.rst
@@ -3324,6 +3324,16 @@ options apply to zone transfers.
global :any:`also-notify` list are not sent NOTIFY messages for that
zone. The default is the empty list (no global notification list).
+.. namedconf:statement:: min-transfer-rate-in
+ :tags: transfer
+ :short: Specifies the minimum traffic rate below which inbound zone transfers are terminated.
+
+ Inbound zone transfers running slower than the given amount of bytes in the
+ given amount of minutes are terminated. This option takes two non-zero integer values.
+ A check is performed periodically every time the configured time interval
+ passes. The default value is ``10240 5``, i.e. 10240 bytes in 5 minutes.
+ The maximum time value is 28 days (40320 minutes).
+
.. namedconf:statement:: max-transfer-time-in
:tags: transfer
:short: Specifies the number of minutes after which inbound zone transfers are terminated.
@@ -7068,6 +7078,9 @@ Zone Options
:any:`max-records`
See the description of :any:`max-records` in :ref:`server_resource_limits`.
+:any:`min-transfer-rate-in`
+ See the description of :any:`min-transfer-rate-in` in :ref:`zone_transfers`.
+
:any:`max-transfer-time-in`
See the description of :any:`max-transfer-time-in` in :ref:`zone_transfers`.
@@ -7822,6 +7835,12 @@ Incoming Zone Transfers
64-bit unsigned Integer. This is the number of usable bytes
of DNS data. It does not include transport overhead.
+ ``Transfer Rate (B/s)`` (``rate``)
+ 64 bit unsigned Integer. This is the average zone transfer rate in
+ bytes-per-second during the latest full interval that is configured by the
+ :any:`min-transfer-rate-in` configuration option. If no such interval
+ has passed yet, then the overall average rate is reported instead.
+
.. note::
Depending on the current state of the transfer, some of the
values may be empty or set to ``-`` (meaning "not available").
diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt
index 99f1212643..b1c5c08ea7 100644
--- a/doc/misc/mirror.zoneopt
+++ b/doc/misc/mirror.zoneopt
@@ -26,6 +26,7 @@ zone [ ] {
max-types-per-name ;
min-refresh-time ;
min-retry-time ;
+ min-transfer-rate-in ;
multi-master ;
notify ( explicit | master-only | primary-only | );
notify-delay ;
diff --git a/doc/misc/options b/doc/misc/options
index baa4e3696f..f49c0a8800 100644
--- a/doc/misc/options
+++ b/doc/misc/options
@@ -202,6 +202,7 @@ options {
min-ncache-ttl ;
min-refresh-time ;
min-retry-time ;
+ min-transfer-rate-in ;
minimal-any ;
minimal-responses ( no-auth | no-auth-recursive | );
multi-master ;
@@ -484,6 +485,7 @@ view [ ] {
min-ncache-ttl ;
min-refresh-time ;
min-retry-time ;
+ min-transfer-rate-in ;
minimal-any ;
minimal-responses ( no-auth | no-auth-recursive | );
multi-master ;
diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt
index e5bbb1816c..6fbe1fbaf5 100644
--- a/doc/misc/secondary.zoneopt
+++ b/doc/misc/secondary.zoneopt
@@ -38,6 +38,7 @@ zone [ ] {
max-types-per-name ;
min-refresh-time ;
min-retry-time ;
+ min-transfer-rate-in ;
multi-master ;
notify ( explicit | master-only | primary-only | );
notify-delay ;
diff --git a/doc/misc/stub.zoneopt b/doc/misc/stub.zoneopt
index 4781f4d720..97b9ba0578 100644
--- a/doc/misc/stub.zoneopt
+++ b/doc/misc/stub.zoneopt
@@ -18,6 +18,7 @@ zone [ ] {
max-types-per-name ;
min-refresh-time ;
min-retry-time ;
+ min-transfer-rate-in ;
multi-master ;
primaries [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... };
transfer-source ( | * );
diff --git a/lib/dns/include/dns/xfrin.h b/lib/dns/include/dns/xfrin.h
index 52c74ec91a..161737abfa 100644
--- a/lib/dns/include/dns/xfrin.h
+++ b/lib/dns/include/dns/xfrin.h
@@ -140,10 +140,13 @@ dns_xfrin_getendserial(dns_xfrin_t *xfr);
void
dns_xfrin_getstats(dns_xfrin_t *xfr, unsigned int *nmsgp, unsigned int *nrecsp,
- uint64_t *nbytesp);
+ uint64_t *nbytesp, uint64_t *ratep);
/*%<
* Get various statistics values of the xfrin object: number of the received
- * messages, number of the received records, number of the received bytes.
+ * messages, number of the received records, number of the received bytes,
+ * and the average transfer rate (in bytes-per-second) during the last full
+ * 'min-transfer-rate-in ' minutes interval. If no such
+ * interval has passed yet, then the overall average rate is reported instead.
*
* Requires:
*\li 'xfr' is a valid dns_xfrin_t.
diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h
index 4524142525..63b39eb1b8 100644
--- a/lib/dns/include/dns/zone.h
+++ b/lib/dns/include/dns/zone.h
@@ -1233,6 +1233,36 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from,
*\li DNS_R_SUCCESS
*/
+void
+dns_zone_setminxfrratein(dns_zone_t *zone, uint32_t bytes, uint32_t seconds);
+/*%<
+ * Set the minumum traffic rate (in bytes per seconds) that a zone transfer in
+ * (AXFR/IXFR) of this zone will use before being aborted.
+ *
+ * Requires:
+ * \li 'zone' to be valid initialised zone.
+ */
+
+uint32_t
+dns_zone_getminxfrratebytesin(dns_zone_t *zone);
+/*%<
+ * Returns the 'bytes' portion of the minimum traffic rate for the transfer in
+ * for this zone.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ */
+
+uint32_t
+dns_zone_getminxfrratesecondsin(dns_zone_t *zone);
+/*%<
+ * Returns the 'seconds' portion of the minimum traffic rate for the transfer in
+ * for this zone.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ */
+
void
dns_zone_setmaxxfrin(dns_zone_t *zone, uint32_t maxxfrin);
/*%<
diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c
index cefb35f634..a352d93353 100644
--- a/lib/dns/xfrin.c
+++ b/lib/dns/xfrin.c
@@ -154,11 +154,13 @@ struct dns_xfrin {
atomic_uint nrecs; /*%< Number of records recvd */
atomic_uint_fast64_t nbytes; /*%< Number of bytes received */
_Atomic(isc_time_t) start; /*%< Start time of the transfer */
+ atomic_uint_fast64_t rate_bytes_per_second;
_Atomic(dns_transport_type_t) soa_transport_type;
atomic_uint_fast32_t end_serial;
unsigned int maxrecords; /*%< The maximum number of
* records set for the zone */
+ uint64_t nbytes_saved; /*%< For enforcing the minimum transfer rate */
dns_tsigkey_t *tsigkey; /*%< Key used to create TSIG */
isc_buffer_t *lasttsig; /*%< The last TSIG */
@@ -192,6 +194,7 @@ struct dns_xfrin {
isc_loop_t *loop;
+ isc_timer_t *min_rate_timer;
isc_timer_t *max_time_timer;
isc_timer_t *max_idle_timer;
@@ -269,6 +272,8 @@ xfrin_timedout(void *);
static void
xfrin_idledout(void *);
static void
+xfrin_minratecheck(void *);
+static void
xfrin_fail(dns_xfrin_t *xfr, isc_result_t result, const char *msg);
static isc_result_t
render(dns_message_t *msg, isc_mem_t *mctx, isc_buffer_t *buf);
@@ -963,6 +968,32 @@ xfrin_idledout(void *xfr) {
xfrin_fail(xfr, ISC_R_TIMEDOUT, "maximum idle time exceeded");
}
+static void
+xfrin_minratecheck(void *arg) {
+ dns_xfrin_t *xfr = arg;
+
+ REQUIRE(VALID_XFRIN(xfr));
+
+ const uint64_t nbytes = atomic_load_relaxed(&xfr->nbytes);
+ const uint64_t min = dns_zone_getminxfrratebytesin(xfr->zone);
+ uint64_t rate = nbytes - xfr->nbytes_saved;
+
+ if (rate < min) {
+ isc_timer_stop(xfr->min_rate_timer);
+ xfrin_fail(xfr, ISC_R_TIMEDOUT,
+ "minimum transfer rate reached");
+ } else {
+ xfr->nbytes_saved = nbytes;
+
+ /*
+ * Calculate and store for the statistics channel the transfer
+ * rate in bytes-per-second for the latest interval.
+ */
+ rate /= dns_zone_getminxfrratesecondsin(xfr->zone);
+ atomic_store_relaxed(&xfr->rate_bytes_per_second, rate);
+ }
+}
+
isc_time_t
dns_xfrin_getstarttime(dns_xfrin_t *xfr) {
REQUIRE(VALID_XFRIN(xfr));
@@ -1024,13 +1055,29 @@ dns_xfrin_getendserial(dns_xfrin_t *xfr) {
void
dns_xfrin_getstats(dns_xfrin_t *xfr, unsigned int *nmsgp, unsigned int *nrecsp,
- uint64_t *nbytesp) {
+ uint64_t *nbytesp, uint64_t *ratep) {
REQUIRE(VALID_XFRIN(xfr));
REQUIRE(nmsgp != NULL && nrecsp != NULL && nbytesp != NULL);
+ uint64_t rate = atomic_load_relaxed(&xfr->rate_bytes_per_second);
+ if (rate == 0) {
+ /*
+ * Likely the first 'min-transfer-rate-in '
+ * minutes interval hasn't passed yet. Calculate the overall
+ * average transfer rate instead.
+ */
+ isc_time_t now = isc_time_now();
+ isc_time_t start = atomic_load_relaxed(&xfr->start);
+ uint64_t sec = isc_time_microdiff(&now, &start) / US_PER_SEC;
+ if (sec > 0) {
+ rate = atomic_load_relaxed(&xfr->nbytes) / sec;
+ }
+ }
+
SET_IF_NOT_NULL(nmsgp, atomic_load_relaxed(&xfr->nmsg));
SET_IF_NOT_NULL(nrecsp, atomic_load_relaxed(&xfr->nrecs));
SET_IF_NOT_NULL(nbytesp, atomic_load_relaxed(&xfr->nbytes));
+ SET_IF_NOT_NULL(ratep, rate);
}
const isc_sockaddr_t *
@@ -1326,6 +1373,15 @@ xfrin_start(dns_xfrin_t *xfr) {
isc_interval_set(&interval, dns_zone_getidlein(xfr->zone), 0);
isc_timer_start(xfr->max_idle_timer, isc_timertype_once, &interval);
+ /* Set the minimum transfer rate checking timer */
+ if (xfr->min_rate_timer == NULL) {
+ isc_timer_create(dns_zone_getloop(xfr->zone),
+ xfrin_minratecheck, xfr, &xfr->min_rate_timer);
+ }
+ isc_interval_set(&interval, dns_zone_getminxfrratesecondsin(xfr->zone),
+ 0);
+ isc_timer_start(xfr->min_rate_timer, isc_timertype_ticker, &interval);
+
/*
* The connect has to be the last thing that is called before returning,
* as it can end synchronously and destroy the xfr object.
@@ -1606,6 +1662,8 @@ xfrin_send_request(dns_xfrin_t *xfr) {
atomic_store_relaxed(&xfr->nbytes, 0);
atomic_store_relaxed(&xfr->start, isc_time_now());
+ xfr->nbytes_saved = 0;
+
msg->id = xfr->id;
if (xfr->tsigctx != NULL) {
dst_context_destroy(&xfr->tsigctx);
@@ -1724,6 +1782,10 @@ xfrin_end(dns_xfrin_t *xfr, isc_result_t result) {
isc_timer_stop(xfr->max_idle_timer);
isc_timer_destroy(&xfr->max_idle_timer);
}
+ if (xfr->min_rate_timer != NULL) {
+ isc_timer_stop(xfr->min_rate_timer);
+ isc_timer_destroy(&xfr->min_rate_timer);
+ }
if (xfr->shutdown_result == ISC_R_UNSET) {
xfr->shutdown_result = result;
@@ -2017,6 +2079,7 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) {
case XFRST_AXFR_END:
case XFRST_IXFR_END:
/* We are at the end, cancel the timers and IO */
+ isc_timer_stop(xfr->min_rate_timer);
isc_timer_stop(xfr->max_idle_timer);
isc_timer_stop(xfr->max_time_timer);
xfrin_cancelio(xfr);
@@ -2182,6 +2245,7 @@ xfrin_destroy(dns_xfrin_t *xfr) {
INSIST(xfr->max_time_timer == NULL);
INSIST(xfr->max_idle_timer == NULL);
+ INSIST(xfr->min_rate_timer == NULL);
isc_loop_detach(&xfr->loop);
diff --git a/lib/dns/zone.c b/lib/dns/zone.c
index ac41eb3601..9c3eafb95d 100644
--- a/lib/dns/zone.c
+++ b/lib/dns/zone.c
@@ -361,6 +361,8 @@ struct dns_zone {
dns_request_t *request;
dns_loadctx_t *loadctx;
dns_dumpctx_t *dumpctx;
+ uint32_t minxfrratebytesin;
+ uint32_t minxfrratesecondsin;
uint32_t maxxfrin;
uint32_t maxxfrout;
uint32_t idlein;
@@ -16154,6 +16156,28 @@ message_count(dns_message_t *msg, dns_section_t section, dns_rdatatype_t type) {
return count;
}
+void
+dns_zone_setminxfrratein(dns_zone_t *zone, uint32_t bytes, uint32_t seconds) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->minxfrratebytesin = bytes;
+ zone->minxfrratesecondsin = seconds;
+}
+
+uint32_t
+dns_zone_getminxfrratebytesin(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return zone->minxfrratebytesin;
+}
+
+uint32_t
+dns_zone_getminxfrratesecondsin(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return zone->minxfrratesecondsin;
+}
+
void
dns_zone_setmaxxfrin(dns_zone_t *zone, uint32_t maxxfrin) {
REQUIRE(DNS_ZONE_VALID(zone));
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
index e6bef96e3c..6319d0df8f 100644
--- a/lib/isccfg/namedconf.c
+++ b/lib/isccfg/namedconf.c
@@ -2211,6 +2211,20 @@ static cfg_clausedef_t dnssecpolicy_clauses[] = {
{ NULL, NULL, 0 }
};
+/*
+ * For min-transfer-rate-in.
+ */
+static cfg_tuplefielddef_t min_transfer_rate_fields[] = {
+ { "traffic_bytes", &cfg_type_uint32, 0 },
+ { "time_minutes", &cfg_type_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_min_transfer_rate_in = {
+ "min-transfer-rate-in", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, min_transfer_rate_fields
+};
+
/*%
* Clauses that can be found in a 'zone' statement,
* with defaults in the 'view' or 'options' statement.
@@ -2304,6 +2318,8 @@ static cfg_clausedef_t zone_clauses[] = {
CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
{ "max-retry-time", &cfg_type_uint32,
CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "min-transfer-rate-in", &cfg_type_min_transfer_rate_in,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
{ "max-transfer-idle-in", &cfg_type_uint32,
CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
{ "max-transfer-idle-out", &cfg_type_uint32,