]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Implement the min-transfer-rate-in configuration option
authorAram Sargsyan <aram@isc.org>
Mon, 10 Jun 2024 16:48:26 +0000 (16:48 +0000)
committerArаm Sаrgsyаn <aram@isc.org>
Thu, 20 Feb 2025 09:32:55 +0000 (09:32 +0000)
This new option sets a minimum amount of transfer rate for
an incoming zone transfer that will abort a transfer, which
for some network related reasons run very slowly.

bin/named/config.c
bin/named/zoneconf.c
doc/misc/mirror.zoneopt
doc/misc/options
doc/misc/secondary.zoneopt
doc/misc/stub.zoneopt
lib/dns/include/dns/zone.h
lib/dns/xfrin.c
lib/dns/zone.c
lib/isccfg/namedconf.c

index 23df890193232f17c1a06c8c62cff0c31bc93968..e3aaecdd3e49f320a9883423f49ab91e781010cc 100644 (file)
@@ -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\
index 00be0fe2a6c945a8502de1fc4ac987878a887b0f..43a4943b8d37040d279117b0c433564ecb4eef90 100644 (file)
@@ -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);
index 99f1212643e49fa8482b322d3c8033ec007bfaa8..b1c5c08ea755e4eb4272a1c31a0300945607d557 100644 (file)
@@ -26,6 +26,7 @@ zone <string> [ <class> ] {
        max-types-per-name <integer>;
        min-refresh-time <integer>;
        min-retry-time <integer>;
+       min-transfer-rate-in <integer> <integer>;
        multi-master <boolean>;
        notify ( explicit | master-only | primary-only | <boolean> );
        notify-delay <integer>;
index baa4e3696f6cfb1db2a996255beaccd08826e45a..f49c0a8800f7d3c4ad99adfba611491d8c4da563 100644 (file)
@@ -202,6 +202,7 @@ options {
        min-ncache-ttl <duration>;
        min-refresh-time <integer>;
        min-retry-time <integer>;
+       min-transfer-rate-in <integer> <integer>;
        minimal-any <boolean>;
        minimal-responses ( no-auth | no-auth-recursive | <boolean> );
        multi-master <boolean>;
@@ -484,6 +485,7 @@ view <string> [ <class> ] {
        min-ncache-ttl <duration>;
        min-refresh-time <integer>;
        min-retry-time <integer>;
+       min-transfer-rate-in <integer> <integer>;
        minimal-any <boolean>;
        minimal-responses ( no-auth | no-auth-recursive | <boolean> );
        multi-master <boolean>;
index e5bbb1816c13e53b59f7f0cf84a606e44d17cbc9..6fbe1fbaf5f6483952944964ce2a9ce791761e69 100644 (file)
@@ -38,6 +38,7 @@ zone <string> [ <class> ] {
        max-types-per-name <integer>;
        min-refresh-time <integer>;
        min-retry-time <integer>;
+       min-transfer-rate-in <integer> <integer>;
        multi-master <boolean>;
        notify ( explicit | master-only | primary-only | <boolean> );
        notify-delay <integer>;
index 4781f4d7202c68506f117d153ef7f6b3ab3f97f9..97b9ba057862a7a6fa59860b1669e538fae0133c 100644 (file)
@@ -18,6 +18,7 @@ zone <string> [ <class> ] {
        max-types-per-name <integer>;
        min-refresh-time <integer>;
        min-retry-time <integer>;
+       min-transfer-rate-in <integer> <integer>;
        multi-master <boolean>;
        primaries [ port <integer> ] [ source ( <ipv4_address> | * ) ] [ source-v6 ( <ipv6_address> | * ) ] { ( <server-list> | <ipv4_address> [ port <integer> ] | <ipv6_address> [ port <integer> ] ) [ key <string> ] [ tls <string> ]; ... };
        transfer-source ( <ipv4_address> | * );
index 4524142525fe41dec93c93cc82d4eed2261bcf2c..63b39eb1b87e5bf72cd7d9f951e6255c60e26854 100644 (file)
@@ -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);
 /*%<
index cefb35f634cb5ee1523c857a382ebc7d3cb71eac..c6167900c4d0472bfd8ebd7d417ab4a8395f49ca 100644 (file)
@@ -159,6 +159,7 @@ struct dns_xfrin {
 
        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 +193,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 +271,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 +967,24 @@ 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);
+
+       if (nbytes - xfr->nbytes_saved < min) {
+               isc_timer_stop(xfr->min_rate_timer);
+               xfrin_fail(xfr, ISC_R_TIMEDOUT,
+                          "minimum transfer rate reached");
+       } else {
+               xfr->nbytes_saved = nbytes;
+       }
+}
+
 isc_time_t
 dns_xfrin_getstarttime(dns_xfrin_t *xfr) {
        REQUIRE(VALID_XFRIN(xfr));
@@ -1326,6 +1348,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 +1637,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 +1757,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 +2054,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 +2220,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);
 
index ac41eb3601b44c736eb74a1b023be4445a02083d..9c3eafb95dd6bffb79af96ff120e4db872c87cbe 100644 (file)
@@ -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));
index e6bef96e3c8e70957202092081b4d49666318e80..6319d0df8f06cab090660dfdb0f37b624a7e781b 100644 (file)
@@ -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,