Implement the 'request-ixfr-max-diffs' configuration option

This limits the maximum number of received incremental zone
transfer differences for a secondary server. Upon reaching the
confgiured limit, the secondary aborts IXFR and initiates a full
zone transfer (AXFR).
This commit is contained in:
Aram Sargsyan 2024-06-07 14:47:55 +00:00 committed by Arаm Sаrgsyаn
parent d40b722d46
commit c05a823e8b
16 changed files with 173 additions and 25 deletions

View file

@ -190,6 +190,7 @@ options {\n\
recursion true;\n\
request-expire true;\n\
request-ixfr true;\n\
request-ixfr-max-diffs 0;\n\
require-server-cookie no;\n\
root-key-sentinel yes;\n\
servfail-ttl 1;\n\

View file

@ -1469,6 +1469,13 @@ configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj)));
}
obj = NULL;
(void)cfg_map_get(cpeer, "request-ixfr-max-diffs", &obj);
if (obj != NULL) {
CHECK(dns_peer_setrequestixfrmaxdiffs(peer,
cfg_obj_asuint32(obj)));
}
obj = NULL;
(void)cfg_map_get(cpeer, "request-nsid", &obj);
if (obj != NULL) {

View file

@ -1418,6 +1418,11 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
INSIST(result == ISC_R_SUCCESS);
dns_zone_setrequestixfr(zone, cfg_obj_asboolean(obj));
obj = NULL;
result = named_config_get(maps, "request-ixfr-max-diffs", &obj);
INSIST(result == ISC_R_SUCCESS);
dns_zone_setrequestixfrmaxdiffs(zone, cfg_obj_asuint32(obj));
obj = NULL;
checknames(ztype, maps, &obj);
INSIST(obj != NULL);

View file

@ -0,0 +1,22 @@
/*
* 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.
*/
/*
* request-ixfr clause is not allowed in zone of type primary.
*/
zone dummy {
type primary;
request-ixfr-max-diffs 100;
file "xxxx";
};

View file

@ -29,6 +29,7 @@ server 0.0.0.0 {
query-source 0.0.0.0;
request-expire no;
request-ixfr no;
request-ixfr-max-diffs 0;
request-nsid no;
require-cookie no;
send-cookie no;
@ -52,6 +53,7 @@ server :: {
query-source-v6 ::;
request-expire no;
request-ixfr no;
request-ixfr-max-diffs 0;
request-nsid no;
require-cookie no;
send-cookie no;

View file

@ -34,6 +34,7 @@ zone <string> [ <class> ] {
primaries [ port <integer> ] [ source ( <ipv4_address> | * ) ] [ source-v6 ( <ipv6_address> | * ) ] { ( <remote-servers> | <ipv4_address> [ port <integer> ] | <ipv6_address> [ port <integer> ] ) [ key <string> ] [ tls <string> ]; ... };
request-expire <boolean>;
request-ixfr <boolean>;
request-ixfr-max-diffs <integer>;
transfer-source ( <ipv4_address> | * );
transfer-source-v6 ( <ipv6_address> | * );
try-tcp-refresh <boolean>;

View file

@ -257,6 +257,7 @@ options {
recursive-clients <integer>;
request-expire <boolean>;
request-ixfr <boolean>;
request-ixfr-max-diffs <integer>;
request-nsid <boolean>;
require-server-cookie <boolean>;
resolver-query-timeout <integer>;
@ -346,6 +347,7 @@ server <netprefix> {
query-source-v6 [ address ] ( <ipv6_address> | * );
request-expire <boolean>;
request-ixfr <boolean>;
request-ixfr-max-diffs <integer>;
request-nsid <boolean>;
require-cookie <boolean>;
send-cookie <boolean>;
@ -539,6 +541,7 @@ view <string> [ <class> ] {
recursion <boolean>;
request-expire <boolean>;
request-ixfr <boolean>;
request-ixfr-max-diffs <integer>;
request-nsid <boolean>;
require-server-cookie <boolean>;
resolver-query-timeout <integer>;
@ -564,6 +567,7 @@ view <string> [ <class> ] {
query-source-v6 [ address ] ( <ipv6_address> | * );
request-expire <boolean>;
request-ixfr <boolean>;
request-ixfr-max-diffs <integer>;
request-nsid <boolean>;
require-cookie <boolean>;
send-cookie <boolean>;

View file

@ -51,6 +51,7 @@ zone <string> [ <class> ] {
primaries [ port <integer> ] [ source ( <ipv4_address> | * ) ] [ source-v6 ( <ipv6_address> | * ) ] { ( <remote-servers> | <ipv4_address> [ port <integer> ] | <ipv6_address> [ port <integer> ] ) [ key <string> ] [ tls <string> ]; ... };
request-expire <boolean>;
request-ixfr <boolean>;
request-ixfr-max-diffs <integer>;
sig-signing-nodes <integer>;
sig-signing-signatures <integer>;
sig-signing-type <integer>;

View file

@ -100,6 +100,12 @@ dns_peer_setrequestixfr(dns_peer_t *peer, bool newval);
isc_result_t
dns_peer_getrequestixfr(dns_peer_t *peer, bool *retval);
isc_result_t
dns_peer_setrequestixfrmaxdiffs(dns_peer_t *peer, uint32_t newval);
isc_result_t
dns_peer_getrequestixfrmaxdiffs(dns_peer_t *peer, uint32_t *retval);
isc_result_t
dns_peer_setprovideixfr(dns_peer_t *peer, bool newval);

View file

@ -53,7 +53,7 @@ ISC_LANG_BEGINDECLS
isc_result_t
dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
const isc_sockaddr_t *primaryaddr,
uint32_t ixfr_maxdiffs, const isc_sockaddr_t *primaryaddr,
const isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey,
dns_transport_type_t soa_transport_type,
dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache,

View file

@ -2442,6 +2442,25 @@ dns_zone_setrequestixfr(dns_zone_t *zone, bool flag);
* \li 'zone' to be valid.
*/
bool
dns_zone_getrequestixfrmaxdiffs(dns_zone_t *zone);
/*%
* Returns the value of the request-ixfr-max-diffs option in the zone.
*
* Requires:
* \li 'zone' to be valid.
*/
void
dns_zone_setrequestixfrmaxdiffs(dns_zone_t *zone, uint32_t maxmsgs);
/*%
* Sets the request-ixfr-max-diffs option for the zone. 0 means unlimited. The
* default value is determined by the setting of this option in the view.
*
* Requires:
* \li 'zone' to be valid.
*/
uint32_t
dns_zone_getixfrratio(dns_zone_t *zone);
/*%

View file

@ -14,6 +14,7 @@
/*! \file */
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <isc/mem.h>
@ -50,6 +51,7 @@ struct dns_peer {
bool bogus;
dns_transfer_format_t transfer_format;
uint32_t transfers;
uint32_t request_ixfr_maxdiffs;
bool support_ixfr;
bool provide_ixfr;
bool request_ixfr;
@ -78,22 +80,29 @@ struct dns_peer {
/*%
* Bit positions in the dns_peer_t structure flags field
*/
#define BOGUS_BIT 0
#define SERVER_TRANSFER_FORMAT_BIT 1
#define TRANSFERS_BIT 2
#define PROVIDE_IXFR_BIT 3
#define REQUEST_IXFR_BIT 4
#define SUPPORT_EDNS_BIT 5
#define SERVER_UDPSIZE_BIT 6
#define SERVER_MAXUDP_BIT 7
#define REQUEST_NSID_BIT 8
#define SEND_COOKIE_BIT 9
#define REQUEST_EXPIRE_BIT 10
#define EDNS_VERSION_BIT 11
#define FORCE_TCP_BIT 12
#define SERVER_PADDING_BIT 13
#define REQUEST_TCP_KEEPALIVE_BIT 14
#define REQUIRE_COOKIE_BIT 15
enum {
BOGUS_BIT = 0,
SERVER_TRANSFER_FORMAT_BIT,
TRANSFERS_BIT,
PROVIDE_IXFR_BIT,
REQUEST_IXFR_BIT,
REQUEST_IXFRMAXDIFFS_BIT,
SUPPORT_EDNS_BIT,
SERVER_UDPSIZE_BIT,
SERVER_MAXUDP_BIT,
REQUEST_NSID_BIT,
SEND_COOKIE_BIT,
REQUEST_EXPIRE_BIT,
EDNS_VERSION_BIT,
FORCE_TCP_BIT,
SERVER_PADDING_BIT,
REQUEST_TCP_KEEPALIVE_BIT,
REQUIRE_COOKIE_BIT,
DNS_PEER_FLAGS_COUNT
};
STATIC_ASSERT(DNS_PEER_FLAGS_COUNT <= CHAR_BIT * sizeof(uint32_t),
"dns_peer_t structure flags fields are too many for uint32_t");
static void
peerlist_delete(dns_peerlist_t **list);
@ -372,6 +381,8 @@ ACCESS_OPTION(maxudp, SERVER_MAXUDP_BIT, uint16_t, maxudp)
ACCESS_OPTION(provideixfr, PROVIDE_IXFR_BIT, bool, provide_ixfr)
ACCESS_OPTION(requestexpire, REQUEST_EXPIRE_BIT, bool, request_expire)
ACCESS_OPTION(requestixfr, REQUEST_IXFR_BIT, bool, request_ixfr)
ACCESS_OPTION(requestixfrmaxdiffs, REQUEST_IXFRMAXDIFFS_BIT, uint32_t,
request_ixfr_maxdiffs)
ACCESS_OPTION(requestnsid, REQUEST_NSID_BIT, bool, request_nsid)
ACCESS_OPTION(requirecookie, REQUIRE_COOKIE_BIT, bool, require_cookie)
ACCESS_OPTION(sendcookie, SEND_COOKIE_BIT, bool, send_cookie)

View file

@ -178,6 +178,8 @@ struct dns_xfrin {
dns_rdatacallbacks_t axfr;
struct {
uint32_t diffs;
uint32_t maxdiffs;
uint32_t request_serial;
uint32_t current_serial;
dns_journal_t *journal;
@ -212,7 +214,8 @@ typedef struct xfrin_work {
static void
xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_loop_t *loop,
dns_name_t *zonename, dns_rdataclass_t rdclass,
dns_rdatatype_t reqtype, const isc_sockaddr_t *primaryaddr,
dns_rdatatype_t reqtype, uint32_t ixfr_maxdiffs,
const isc_sockaddr_t *primaryaddr,
const isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey,
dns_transport_type_t soa_transport_type,
dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache,
@ -459,6 +462,7 @@ ixfr_putdata(dns_xfrin_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
dns_difftuple_create(xfr->diff.mctx, op, name, ttl, rdata, &tuple);
dns_diff_append(&xfr->diff, &tuple);
xfr->ixfr.diffs++;
failure:
return (result);
}
@ -863,7 +867,7 @@ failure:
isc_result_t
dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
const isc_sockaddr_t *primaryaddr,
uint32_t ixfr_maxdiffs, const isc_sockaddr_t *primaryaddr,
const isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey,
dns_transport_type_t soa_transport_type,
dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache,
@ -889,7 +893,7 @@ dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
}
xfrin_create(mctx, zone, db, loop, zonename, dns_zone_getclass(zone),
xfrtype, primaryaddr, sourceaddr, tsigkey,
xfrtype, ixfr_maxdiffs, primaryaddr, sourceaddr, tsigkey,
soa_transport_type, transport, tlsctx_cache, &xfr);
if (db != NULL) {
@ -1096,6 +1100,8 @@ xfrin_reset(dns_xfrin_t *xfr) {
dns_diff_clear(&xfr->diff);
xfr->ixfr.diffs = 0;
if (xfr->ixfr.journal != NULL) {
dns_journal_destroy(&xfr->ixfr.journal);
}
@ -1145,7 +1151,8 @@ xfrin_fail(dns_xfrin_t *xfr, isc_result_t result, const char *msg) {
static void
xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_loop_t *loop,
dns_name_t *zonename, dns_rdataclass_t rdclass,
dns_rdatatype_t reqtype, const isc_sockaddr_t *primaryaddr,
dns_rdatatype_t reqtype, uint32_t ixfr_maxdiffs,
const isc_sockaddr_t *primaryaddr,
const isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey,
dns_transport_type_t soa_transport_type,
dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache,
@ -1157,6 +1164,7 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_loop_t *loop,
.shutdown_result = ISC_R_UNSET,
.rdclass = rdclass,
.reqtype = reqtype,
.ixfr.maxdiffs = ixfr_maxdiffs,
.maxrecords = dns_zone_getmaxrecords(zone),
.primaryaddr = *primaryaddr,
.sourceaddr = *sourceaddr,
@ -1902,6 +1910,19 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) {
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdataset_current(rds, &rdata);
CHECK(xfr_rr(xfr, name, rds->ttl, &rdata));
/*
* Did we hit the maximum ixfr diffs limit?
*/
if (xfr->reqtype == dns_rdatatype_ixfr &&
xfr->ixfr.maxdiffs != 0 &&
xfr->ixfr.diffs >= xfr->ixfr.maxdiffs)
{
xfrin_log(xfr, ISC_LOG_DEBUG(3),
"too many diffs, "
"retrying with AXFR");
goto try_axfr;
}
}
}
}

View file

@ -465,6 +465,7 @@ struct dns_zone {
* whether ixfr is requested
*/
bool requestixfr;
uint32_t requestixfr_maxdiffs;
uint32_t ixfr_ratio;
/*%
@ -18260,6 +18261,7 @@ got_transfer_quota(void *arg) {
char primary[ISC_SOCKADDR_FORMATSIZE];
char source[ISC_SOCKADDR_FORMATSIZE];
dns_rdatatype_t xfrtype;
uint32_t ixfr_maxdiffs = 0;
isc_netaddr_t primaryip;
isc_sockaddr_t primaryaddr;
isc_sockaddr_t sourceaddr;
@ -18326,12 +18328,20 @@ got_transfer_quota(void *arg) {
UNLOCK_ZONE(zone);
} else {
bool use_ixfr = true;
if (peer != NULL) {
result = dns_peer_getrequestixfr(peer, &use_ixfr);
}
if (peer == NULL || result != ISC_R_SUCCESS) {
use_ixfr = zone->requestixfr;
}
if (peer != NULL) {
result = dns_peer_getrequestixfrmaxdiffs(
peer, &ixfr_maxdiffs);
}
if (peer == NULL || result != ISC_R_SUCCESS) {
ixfr_maxdiffs = zone->requestixfr_maxdiffs;
}
if (!use_ixfr) {
dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN,
ISC_LOG_DEBUG(1),
@ -18415,10 +18425,10 @@ got_transfer_quota(void *arg) {
zmgr_tlsctx_attach(zone->zmgr, &zmgr_tlsctx_cache);
result = dns_xfrin_create(zone, xfrtype, &primaryaddr, &sourceaddr,
zone->tsigkey, soa_transport_type,
zone->transport, zmgr_tlsctx_cache,
zone->mctx, zone_xfrdone, &zone->xfr);
result = dns_xfrin_create(
zone, xfrtype, ixfr_maxdiffs, &primaryaddr, &sourceaddr,
zone->tsigkey, soa_transport_type, zone->transport,
zmgr_tlsctx_cache, zone->mctx, zone_xfrdone, &zone->xfr);
isc_tlsctx_cache_detach(&zmgr_tlsctx_cache);
@ -23139,6 +23149,18 @@ dns_zone_getrequestixfr(dns_zone_t *zone) {
return (zone->requestixfr);
}
void
dns_zone_setrequestixfrmaxdiffs(dns_zone_t *zone, uint32_t maxdiffs) {
REQUIRE(DNS_ZONE_VALID(zone));
zone->requestixfr_maxdiffs = maxdiffs;
}
bool
dns_zone_getrequestixfrmaxdiffs(dns_zone_t *zone) {
REQUIRE(DNS_ZONE_VALID(zone));
return (zone->requestixfr_maxdiffs);
}
void
dns_zone_setixfrratio(dns_zone_t *zone, uint32_t ratio) {
REQUIRE(DNS_ZONE_VALID(zone));

View file

@ -4344,6 +4344,13 @@ static struct {
{ "tcp-only", dns_peer_setforcetcp },
};
static struct {
const char *name;
isc_result_t (*set)(dns_peer_t *peer, uint32_t newval);
} uint32s[] = {
{ "request-ixfr-max-diffs", dns_peer_setrequestixfrmaxdiffs },
};
static isc_result_t
check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions,
isc_symtab_t *symtab, isc_mem_t *mctx) {
@ -4502,6 +4509,22 @@ check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions,
}
}
}
for (i = 0; i < ARRAY_SIZE(uint32s); i++) {
const cfg_obj_t *opt = NULL;
cfg_map_get(v1, uint32s[i].name, &opt);
if (opt != NULL) {
tresult = (uint32s[i].set)(
peer, cfg_obj_asuint32(opt));
if (tresult != ISC_R_SUCCESS) {
cfg_obj_log(opt, ISC_LOG_ERROR,
"setting server option "
"'%s' failed: %s",
uint32s[i].name,
isc_result_totext(tresult));
result = ISC_R_FAILURE;
}
}
}
dns_peer_detach(&peer);
}
return (result);

View file

@ -2484,6 +2484,8 @@ static cfg_clausedef_t zone_clauses[] = {
CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
{ "request-ixfr", &cfg_type_boolean,
CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
{ "request-ixfr-max-diffs", &cfg_type_uint32,
CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
{ "serial-update-method", &cfg_type_updatemethod, CFG_ZONE_PRIMARY },
{ "sig-signing-nodes", &cfg_type_uint32,
CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
@ -2721,6 +2723,7 @@ static cfg_clausedef_t server_clauses[] = {
{ "query-source-v6", &cfg_type_querysource6, 0 },
{ "request-expire", &cfg_type_boolean, 0 },
{ "request-ixfr", &cfg_type_boolean, 0 },
{ "request-ixfr-max-diffs", &cfg_type_uint32, 0 },
{ "request-nsid", &cfg_type_boolean, 0 },
{ "request-sit", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "require-cookie", &cfg_type_boolean, 0 },