2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 10:10:06 +00:00

Add stale-cache-enable option and disable serve-stable by default

The current serve-stale implementation in BIND 9 stores all received
records in the cache for a max-stale-ttl interval (default 12 hours).

This allows DNS operators to turn the serve-stale answers in an event of
large authoritative DNS outage.  The caching of the stale answers needs
to be enabled before the outage happens or the feature would be
otherwise useless.

The negative consequence of the default setting is the inevitable
cache-bloat that happens for every and each DNS operator running named.

In this MR, a new configuration option `stale-cache-enable` is
introduced that allows the operators to selectively enable or disable
the serve-stale feature of BIND 9 based on their decision.

The newly introduced option has been disabled by default,
e.g. serve-stale is disabled in the default configuration and has to be
enabled if required.
This commit is contained in:
Ondřej Surý 2020-07-21 10:38:55 +02:00 committed by Matthijs Mekking
parent f2040a0039
commit ce53db34d6
16 changed files with 54 additions and 26 deletions

View File

@ -196,6 +196,7 @@ options {\n\
# sortlist <none>\n\ # sortlist <none>\n\
stale-answer-enable false;\n\ stale-answer-enable false;\n\
stale-answer-ttl 1; /* 1 second */\n\ stale-answer-ttl 1; /* 1 second */\n\
stale-cache-enable false;\n\
synth-from-dnssec no;\n\ synth-from-dnssec no;\n\
# topology <none>\n\ # topology <none>\n\
transfer-format many-answers;\n\ transfer-format many-answers;\n\

View File

@ -401,6 +401,7 @@ OPTIONS
stacksize ( default | unlimited | sizeval ); stacksize ( default | unlimited | sizeval );
stale-answer-enable boolean; stale-answer-enable boolean;
stale-answer-ttl duration; stale-answer-ttl duration;
stale-cache-enable boolean;
startup-notify-rate integer; startup-notify-rate integer;
statistics-file quoted_string; statistics-file quoted_string;
synth-from-dnssec boolean; synth-from-dnssec boolean;
@ -785,6 +786,7 @@ VIEW
sortlist { address_match_element; ... }; sortlist { address_match_element; ... };
stale-answer-enable boolean; stale-answer-enable boolean;
stale-answer-ttl duration; stale-answer-ttl duration;
stale-cache-enable boolean;
synth-from-dnssec boolean; synth-from-dnssec boolean;
transfer-format ( many-answers | one-answer ); transfer-format ( many-answers | one-answer );
transfer-source ( ipv4_address | * ) [ port ( integer | * ) ] [ transfer-source ( ipv4_address | * ) [ port ( integer | * ) ] [

View File

@ -3895,7 +3895,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
uint32_t max_cache_size_percent = 0; uint32_t max_cache_size_percent = 0;
size_t max_adb_size; size_t max_adb_size;
uint32_t lame_ttl, fail_ttl; uint32_t lame_ttl, fail_ttl;
uint32_t max_stale_ttl; uint32_t max_stale_ttl = 0;
dns_tsig_keyring_t *ring = NULL; dns_tsig_keyring_t *ring = NULL;
dns_view_t *pview = NULL; /* Production view */ dns_view_t *pview = NULL; /* Production view */
isc_mem_t *cmctx = NULL, *hmctx = NULL; isc_mem_t *cmctx = NULL, *hmctx = NULL;
@ -4358,9 +4358,14 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
view->synthfromdnssec = cfg_obj_asboolean(obj); view->synthfromdnssec = cfg_obj_asboolean(obj);
obj = NULL; obj = NULL;
result = named_config_get(maps, "max-stale-ttl", &obj); result = named_config_get(maps, "stale-cache-enable", &obj);
INSIST(result == ISC_R_SUCCESS); INSIST(result == ISC_R_SUCCESS);
max_stale_ttl = ISC_MAX(cfg_obj_asduration(obj), 1); if (cfg_obj_asboolean(obj)) {
obj = NULL;
result = named_config_get(maps, "max-stale-ttl", &obj);
INSIST(result == ISC_R_SUCCESS);
max_stale_ttl = ISC_MAX(cfg_obj_asduration(obj), 1);
}
obj = NULL; obj = NULL;
result = named_config_get(maps, "stale-answer-enable", &obj); result = named_config_get(maps, "stale-answer-enable", &obj);

Binary file not shown.

View File

@ -30,6 +30,7 @@ options {
max-stale-ttl 3600; max-stale-ttl 3600;
stale-answer-ttl 2; stale-answer-ttl 2;
stale-answer-enable yes; stale-answer-enable yes;
stale-cache-enable yes;
servfail-ttl 0; servfail-ttl 0;
}; };

View File

@ -30,6 +30,7 @@ options {
max-stale-ttl 20; max-stale-ttl 20;
stale-answer-ttl 3; stale-answer-ttl 3;
stale-answer-enable yes; stale-answer-enable yes;
stale-cache-enable yes;
servfail-ttl 0; servfail-ttl 0;
}; };

View File

@ -28,7 +28,7 @@ options {
listen-on-v6 { none; }; listen-on-v6 { none; };
recursion yes; recursion yes;
dump-file "named_dump3.db"; dump-file "named_dump3.db";
// This configuration has no serve-stale options set. stale-cache-enable yes;
}; };
zone "." { zone "." {

View File

@ -29,6 +29,7 @@ options {
recursion yes; recursion yes;
dump-file "named_dump4.db"; dump-file "named_dump4.db";
stale-answer-enable no; stale-answer-enable no;
stale-cache-enable yes;
}; };
zone "." { zone "." {

View File

@ -16,3 +16,4 @@ $SHELL clean.sh
copy_setports ns1/named1.conf.in ns1/named.conf copy_setports ns1/named1.conf.in ns1/named.conf
copy_setports ns3/named.conf.in ns3/named.conf copy_setports ns3/named.conf.in ns3/named.conf
copy_setports ns4/named.conf.in ns4/named.conf copy_setports ns4/named.conf.in ns4/named.conf
copy_setports ns5/named.conf.in ns5/named.conf

View File

@ -861,7 +861,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
# #
# Now test server with serve-stale disabled. # Now test server with serve-stale answers disabled.
# #
echo_i "test server with serve-stale disabled" echo_i "test server with serve-stale disabled"
@ -875,7 +875,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "prime cache longttl.example (serve-stale disabled) ($n)" echo_i "prime cache longttl.example (serve-stale answers disabled) ($n)"
ret=0 ret=0
$DIG -p ${PORT} @10.53.0.4 longttl.example TXT > dig.out.test$n $DIG -p ${PORT} @10.53.0.4 longttl.example TXT > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
@ -884,7 +884,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "prime cache data.example (serve-stale disabled) ($n)" echo_i "prime cache data.example (serve-stale answers disabled) ($n)"
ret=0 ret=0
$DIG -p ${PORT} @10.53.0.4 data.example TXT > dig.out.test$n $DIG -p ${PORT} @10.53.0.4 data.example TXT > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
@ -894,7 +894,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "prime cache othertype.example (serve-stale disabled) ($n)" echo_i "prime cache othertype.example (serve-stale answers disabled) ($n)"
ret=0 ret=0
$DIG -p ${PORT} @10.53.0.4 othertype.example CAA > dig.out.test$n $DIG -p ${PORT} @10.53.0.4 othertype.example CAA > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
@ -904,7 +904,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "prime cache nodata.example (serve-stale disabled) ($n)" echo_i "prime cache nodata.example (serve-stale answers disabled) ($n)"
ret=0 ret=0
$DIG -p ${PORT} @10.53.0.4 nodata.example TXT > dig.out.test$n $DIG -p ${PORT} @10.53.0.4 nodata.example TXT > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
@ -914,7 +914,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "prime cache nxdomain.example (serve-stale disabled) ($n)" echo_i "prime cache nxdomain.example (serve-stale answers disabled) ($n)"
ret=0 ret=0
$DIG -p ${PORT} @10.53.0.4 nxdomain.example TXT > dig.out.test$n $DIG -p ${PORT} @10.53.0.4 nxdomain.example TXT > dig.out.test$n
grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1 grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
@ -924,7 +924,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "verify prime cache statistics (serve-stale disabled) ($n)" echo_i "verify prime cache statistics (serve-stale answers disabled) ($n)"
ret=0 ret=0
rm -f ns4/named.stats rm -f ns4/named.stats
$RNDCCMD 10.53.0.4 stats > /dev/null 2>&1 $RNDCCMD 10.53.0.4 stats > /dev/null 2>&1
@ -972,7 +972,7 @@ waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4)) waitfile dig.out.test$((n+4))
n=$((n+1)) n=$((n+1))
echo_i "check fail of data.example (serve-stale disabled) ($n)" echo_i "check fail of data.example (serve-stale answers disabled) ($n)"
ret=0 ret=0
grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1 grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
@ -980,7 +980,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "check fail of othertype.example (serve-stale disabled) ($n)" echo_i "check fail of othertype.example (serve-stale answers disabled) ($n)"
ret=0 ret=0
grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1 grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
@ -988,7 +988,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "check fail of nodata.example (serve-stale disabled) ($n)" echo_i "check fail of nodata.example (serve-stale answers disabled) ($n)"
ret=0 ret=0
grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1 grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
@ -996,7 +996,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "check fail of nxdomain.example (serve-stale disabled) ($n)" echo_i "check fail of nxdomain.example (serve-stale answers disabled) ($n)"
ret=0 ret=0
grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1 grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
@ -1004,7 +1004,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret)) status=$((status+ret))
n=$((n+1)) n=$((n+1))
echo_i "verify stale cache statistics (serve-stale disabled) ($n)" echo_i "verify stale cache statistics (serve-stale answers disabled) ($n)"
ret=0 ret=0
rm -f ns4/named.stats rm -f ns4/named.stats
$RNDCCMD 10.53.0.4 stats > /dev/null 2>&1 $RNDCCMD 10.53.0.4 stats > /dev/null 2>&1
@ -1024,7 +1024,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
# Dump the cache. # Dump the cache.
n=$((n+1)) n=$((n+1))
echo_i "dump the cache (serve-stale disabled) ($n)" echo_i "dump the cache (serve-stale answers disabled) ($n)"
ret=0 ret=0
$RNDCCMD 10.53.0.4 dumpdb -cache > rndc.out.test$n 2>&1 || ret=1 $RNDCCMD 10.53.0.4 dumpdb -cache > rndc.out.test$n 2>&1 || ret=1
done=0 done=0
@ -1049,7 +1049,7 @@ LASTWEEK=`TZ=UTC perl -e 'my $now = time();
printf("%04d%02d%02d%02d%02d%02d", $y+1900, $mo+1, $d, $h, $m, $s);'` printf("%04d%02d%02d%02d%02d%02d", $y+1900, $mo+1, $d, $h, $m, $s);'`
n=$((n+1)) n=$((n+1))
echo_i "mock the cache date to $LASTWEEK (serve-stale disabled) ($n)" echo_i "mock the cache date to $LASTWEEK (serve-stale answers disabled) ($n)"
ret=0 ret=0
sed -E "s/DATE [0-9]{14}/DATE $LASTWEEK/g" ns4/named_dump4.db > ns4/named_dumpdb4.db.out || ret=1 sed -E "s/DATE [0-9]{14}/DATE $LASTWEEK/g" ns4/named_dump4.db > ns4/named_dumpdb4.db.out || ret=1
cp ns4/named_dumpdb4.db.out ns4/named_dumpdb4.db cp ns4/named_dumpdb4.db.out ns4/named_dumpdb4.db
@ -1060,7 +1060,7 @@ echo_i "start ns4"
start_server --noclean --restart --port ${PORT} serve-stale ns4 start_server --noclean --restart --port ${PORT} serve-stale ns4
n=$((n+1)) n=$((n+1))
echo_i "verify ancient cache statistics (serve-stale disabled) ($n)" echo_i "verify ancient cache statistics (serve-stale answers disabled) ($n)"
ret=0 ret=0
rm -f ns4/named.stats rm -f ns4/named.stats
$RNDCCMD 10.53.0.4 stats #> /dev/null 2>&1 $RNDCCMD 10.53.0.4 stats #> /dev/null 2>&1

View File

@ -1815,9 +1815,9 @@ Boolean Options
The default is ``yes``. The default is ``yes``.
``stale-answer-enable`` ``stale-answer-enable``
If ``yes``, enable the returning of "stale" cached answers when the name servers If ``yes``, enable the returning of "stale" cached answers when the name
for a zone are not answering. The default is not to return stale servers for a zone are not answering and the ``stale-cache-enable`` option is
answers. also enabled. The default is not to return stale answers.
Stale answers can also be enabled or disabled at runtime via Stale answers can also be enabled or disabled at runtime via
``rndc serve-stale on`` or ``rndc serve-stale off``; these override ``rndc serve-stale on`` or ``rndc serve-stale off``; these override
@ -1831,6 +1831,9 @@ Boolean Options
Information about stale answers is logged under the ``serve-stale`` Information about stale answers is logged under the ``serve-stale``
log category. log category.
``stale-cache-enable``
If ``yes``, enable the retaining of "stale" cached answers. Default ``no``.
``nocookie-udp-size`` ``nocookie-udp-size``
This sets the maximum size of UDP responses that are sent to queries This sets the maximum size of UDP responses that are sent to queries
without a valid server COOKIE. A value below 128 is silently without a valid server COOKIE. A value below 128 is silently
@ -3246,15 +3249,20 @@ Tuning
(such as NS and glue AAAA/A records) in the resolution process. (such as NS and glue AAAA/A records) in the resolution process.
``max-stale-ttl`` ``max-stale-ttl``
If stale answers are enabled, ``max-stale-ttl`` sets the maximum time If retaining stale RRsets in cache is enabled, and returning of stale cached
answers is also enabled, ``max-stale-ttl`` sets the maximum time
for which the server retains records past their normal expiry to for which the server retains records past their normal expiry to
return them as stale records, when the servers for those records are return them as stale records, when the servers for those records are
not reachable. The default is 12 hours. The minimum allowed is 1 not reachable. The default is 12 hours. The minimum allowed is 1
second; a value of 0 is updated silently to 1 second. second; a value of 0 is updated silently to 1 second.
For stale answers to be returned, they must be enabled, either in the For stale answers to be returned, the retaining of them in cache must be
configuration file using ``stale-answer-enable`` or via enabled via the configuration option ``stale-cache-enable``, and returning
``rndc serve-stale on``. cached answers must be enabled, either in the configuration file using the
``stale-answer-enable`` option or by calling ``rndc serve-stale on``.
When ``stale-cache-enable`` is set to ``no``, setting the ``max-stale-ttl``
has no effect, the value of ``max-cache-ttl`` will be ``0`` in such case.
``resolver-nonbackoff-tries`` ``resolver-nonbackoff-tries``
This specifies how many retries occur before exponential backoff kicks in. The This specifies how many retries occur before exponential backoff kicks in. The

View File

@ -464,6 +464,7 @@ options {
stacksize ( default | unlimited | sizeval ); stacksize ( default | unlimited | sizeval );
stale\-answer\-enable boolean; stale\-answer\-enable boolean;
stale\-answer\-ttl duration; stale\-answer\-ttl duration;
stale\-cache\-enable boolean;
startup\-notify\-rate integer; startup\-notify\-rate integer;
statistics\-file quoted_string; statistics\-file quoted_string;
synth\-from\-dnssec boolean; synth\-from\-dnssec boolean;
@ -876,6 +877,7 @@ view string [ class ] {
sortlist { address_match_element; ... }; sortlist { address_match_element; ... };
stale\-answer\-enable boolean; stale\-answer\-enable boolean;
stale\-answer\-ttl duration; stale\-answer\-ttl duration;
stale\-cache\-enable boolean;
synth\-from\-dnssec boolean; synth\-from\-dnssec boolean;
transfer\-format ( many\-answers | one\-answer ); transfer\-format ( many\-answers | one\-answer );
transfer\-source ( ipv4_address | * ) [ port ( integer | * ) ] [ transfer\-source ( ipv4_address | * ) [ port ( integer | * ) ] [

View File

@ -365,6 +365,7 @@ options {
stacksize ( default | unlimited | <sizeval> ); stacksize ( default | unlimited | <sizeval> );
stale-answer-enable <boolean>; stale-answer-enable <boolean>;
stale-answer-ttl <duration>; stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
startup-notify-rate <integer>; startup-notify-rate <integer>;
statistics-file <quoted_string>; statistics-file <quoted_string>;
statistics-interval <integer>; // ancient statistics-interval <integer>; // ancient
@ -747,6 +748,7 @@ view <string> [ <class> ] {
sortlist { <address_match_element>; ... }; sortlist { <address_match_element>; ... };
stale-answer-enable <boolean>; stale-answer-enable <boolean>;
stale-answer-ttl <duration>; stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
suppress-initial-notify <boolean>; // not yet implemented suppress-initial-notify <boolean>; // not yet implemented
synth-from-dnssec <boolean>; synth-from-dnssec <boolean>;
topology { <address_match_element>; ... }; // ancient topology { <address_match_element>; ... }; // ancient

View File

@ -329,6 +329,7 @@ options {
stacksize ( default | unlimited | <sizeval> ); stacksize ( default | unlimited | <sizeval> );
stale-answer-enable <boolean>; stale-answer-enable <boolean>;
stale-answer-ttl <duration>; stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
startup-notify-rate <integer>; startup-notify-rate <integer>;
statistics-file <quoted_string>; statistics-file <quoted_string>;
synth-from-dnssec <boolean>; synth-from-dnssec <boolean>;
@ -676,6 +677,7 @@ view <string> [ <class> ] {
sortlist { <address_match_element>; ... }; sortlist { <address_match_element>; ... };
stale-answer-enable <boolean>; stale-answer-enable <boolean>;
stale-answer-ttl <duration>; stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
synth-from-dnssec <boolean>; synth-from-dnssec <boolean>;
transfer-format ( many-answers | one-answer ); transfer-format ( many-answers | one-answer );
transfer-source ( <ipv4_address> | * ) [ port ( <integer> | * ) ] [ transfer-source ( <ipv4_address> | * ) [ port ( <integer> | * ) ] [

View File

@ -258,6 +258,7 @@
stacksize ( default | unlimited | <sizeval> ); stacksize ( default | unlimited | <sizeval> );
stale-answer-enable <boolean>; stale-answer-enable <boolean>;
stale-answer-ttl <duration>; stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
startup-notify-rate <integer>; startup-notify-rate <integer>;
statistics-file <quoted_string>; statistics-file <quoted_string>;
synth-from-dnssec <boolean>; synth-from-dnssec <boolean>;

View File

@ -2045,6 +2045,7 @@ static cfg_clausedef_t view_clauses[] = {
{ "sortlist", &cfg_type_bracketed_aml, 0 }, { "sortlist", &cfg_type_bracketed_aml, 0 },
{ "stale-answer-enable", &cfg_type_boolean, 0 }, { "stale-answer-enable", &cfg_type_boolean, 0 },
{ "stale-answer-ttl", &cfg_type_duration, 0 }, { "stale-answer-ttl", &cfg_type_duration, 0 },
{ "stale-cache-enable", &cfg_type_boolean, 0 },
{ "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI }, { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
{ "synth-from-dnssec", &cfg_type_boolean, 0 }, { "synth-from-dnssec", &cfg_type_boolean, 0 },
{ "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_ANCIENT }, { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_ANCIENT },