2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-09-04 00:25:29 +00:00

[master] fixed several RRL issues

3554.	[bug]		RRL failed to correctly rate-limit upward
			referrals and failed to count dropped error
			responses in the statistics. [RT #33225]
This commit is contained in:
Evan Hunt
2013-04-25 14:40:32 -07:00
parent 330f98fe3b
commit a6d43d18b1
13 changed files with 365 additions and 323 deletions

View File

@@ -1,3 +1,7 @@
3554. [bug] RRL failed to correctly rate-limit upward
referrals and failed to count dropped error
responses in the statistics. [RT #33225]
3553. [bug] Address suspected double free in acache. [RT #33252] 3553. [bug] Address suspected double free in acache. [RT #33252]
3552. [bug] Wrong getopt option string for 'nsupdate -r'. 3552. [bug] Wrong getopt option string for 'nsupdate -r'.

View File

@@ -1257,11 +1257,13 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
} }
/* /*
* Some error responses cannot be 'slipped', * Some error responses cannot be 'slipped',
* so don't try. * so don't try to slip any error responses.
* This will counted with dropped queries in the
* QryDropped counter.
*/ */
if (!client->view->rrl->log_only) { if (!client->view->rrl->log_only) {
isc_stats_increment(ns_g_server->nsstats,
dns_nsstatscounter_ratedropped);
isc_stats_increment(ns_g_server->nsstats,
dns_nsstatscounter_dropped);
ns_client_next(client, DNS_R_DROP); ns_client_next(client, DNS_R_DROP);
return; return;
} }

View File

@@ -191,7 +191,7 @@ inc_stats(ns_client_t *client, isc_statscounter_t counter) {
/* Do query type statistics /* Do query type statistics
* *
* We only increment per-type if we're using the authoriative * We only increment per-type if we're using the authoritative
* answer counter, preventing double-counting. * answer counter, preventing double-counting.
*/ */
if (counter == dns_nsstatscounter_authans) { if (counter == dns_nsstatscounter_authans) {
@@ -6246,7 +6246,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
* Rate limit these responses to this client. * Rate limit these responses to this client.
*/ */
if (client->view->rrl != NULL && if (client->view->rrl != NULL &&
fname != NULL && dns_name_isabsolute(fname) && ((fname != NULL && dns_name_isabsolute(fname)) ||
(result == ISC_R_NOTFOUND && !RECURSIONOK(client))) &&
(client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0) { (client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0) {
dns_rdataset_t nc_rdataset; dns_rdataset_t nc_rdataset;
isc_boolean_t wouldlog; isc_boolean_t wouldlog;
@@ -6289,8 +6290,19 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
dns_rdataset_disassociate(&nc_rdataset); dns_rdataset_disassociate(&nc_rdataset);
} }
rrl_result = DNS_R_NXDOMAIN; rrl_result = DNS_R_NXDOMAIN;
} else if (result == DNS_R_NXRRSET ||
result == DNS_R_EMPTYNAME) {
rrl_result = DNS_R_NXRRSET;
} else if (result == DNS_R_DELEGATION) { } else if (result == DNS_R_DELEGATION) {
rrl_result = result; rrl_result = result;
} else if (result == ISC_R_NOTFOUND) {
/*
* Handle referral to ".", including when recursion
* is off or not requested and the hints have not
* been loaded or we have "additional-from-cache no".
*/
tname = dns_rootname;
rrl_result = DNS_R_DELEGATION;
} else { } else {
rrl_result = ISC_R_SUCCESS; rrl_result = ISC_R_SUCCESS;
} }

View File

@@ -1835,7 +1835,7 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
return (ISC_R_SUCCESS); return (ISC_R_SUCCESS);
} }
#define CHECK_RRL(obj, cond, pat, val1, val2) \ #define CHECK_RRL(cond, pat, val1, val2) \
do { \ do { \
if (!(cond)) { \ if (!(cond)) { \
cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, \ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, \
@@ -1845,6 +1845,22 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
} \ } \
} while (0) } while (0)
#define CHECK_RRL_RATE(rate, def, max_rate, name) \
do { \
obj = NULL; \
rrl->rate.str = name; \
result = cfg_map_get(map, name, &obj); \
if (result == ISC_R_SUCCESS) { \
rrl->rate.r = cfg_obj_asuint32(obj); \
CHECK_RRL(rrl->rate.r <= max_rate, \
name" %d > %d", \
rrl->rate.r, max_rate); \
} else { \
rrl->rate.r = def; \
} \
rrl->rate.scaled = rrl->rate.r; \
} while (0)
static isc_result_t static isc_result_t
configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) { configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
const cfg_obj_t *obj; const cfg_obj_t *obj;
@@ -1875,86 +1891,39 @@ configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
result = cfg_map_get(map, "max-table-size", &obj); result = cfg_map_get(map, "max-table-size", &obj);
if (result == ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) {
i = cfg_obj_asuint32(obj); i = cfg_obj_asuint32(obj);
CHECK_RRL(obj, i >= min_entries, CHECK_RRL(i >= min_entries,
"max-table-size %d < min-table-size %d", "max-table-size %d < min-table-size %d",
i, min_entries); i, min_entries);
} }
rrl->max_entries = i; rrl->max_entries = i;
i = 0; CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE,
obj = NULL; "responses-per-second");
result = cfg_map_get(map, "responses-per-second", &obj); CHECK_RRL_RATE(referrals_per_second,
if (result == ISC_R_SUCCESS) { rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
i = cfg_obj_asuint32(obj); "referrals-per-second");
CHECK_RRL(obj, i <= DNS_RRL_MAX_RATE, CHECK_RRL_RATE(nodata_per_second,
"responses-per-second %d > %d", rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
i, DNS_RRL_MAX_RATE); "nodata-per-second");
} CHECK_RRL_RATE(nxdomains_per_second,
rrl->responses_per_second = i; rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
rrl->scaled_responses_per_second = rrl->responses_per_second; "nxdomains-per-second");
CHECK_RRL_RATE(errors_per_second,
rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
"errors-per-second");
/* CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE,
* The default error rate is the response rate, "all-per-second");
* and so off by default.
*/
i = rrl->responses_per_second;
obj = NULL;
result = cfg_map_get(map, "errors-per-second", &obj);
if (result == ISC_R_SUCCESS) {
i = cfg_obj_asuint32(obj);
CHECK_RRL(obj, i <= DNS_RRL_MAX_RATE,
"errors-per-second %d > %d",
i, DNS_RRL_MAX_RATE);
}
rrl->errors_per_second = i;
rrl->scaled_errors_per_second = rrl->errors_per_second;
/*
* The default NXDOMAIN rate is the response rate,
* and so off by default.
*/
i = rrl->responses_per_second;
obj = NULL;
result = cfg_map_get(map, "nxdomains-per-second", &obj);
if (result == ISC_R_SUCCESS) {
i = cfg_obj_asuint32(obj);
CHECK_RRL(obj, i <= DNS_RRL_MAX_RATE,
"nxdomains-per-second %d > %d",
i, DNS_RRL_MAX_RATE);
}
rrl->nxdomains_per_second = i;
rrl->scaled_nxdomains_per_second = rrl->nxdomains_per_second;
/* CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP,
* The all-per-second rate is off by default. "slip");
*/
i = 0;
obj = NULL;
result = cfg_map_get(map, "all-per-second", &obj);
if (result == ISC_R_SUCCESS) {
i = cfg_obj_asuint32(obj);
CHECK_RRL(obj, i <= DNS_RRL_MAX_RATE, "all-per-second %d > %d",
i, DNS_RRL_MAX_RATE);
}
rrl->all_per_second = i;
rrl->scaled_all_per_second = rrl->all_per_second;
i = 2;
obj = NULL;
result = cfg_map_get(map, "slip", &obj);
if (result == ISC_R_SUCCESS) {
i = cfg_obj_asuint32(obj);
CHECK_RRL(obj, i <= DNS_RRL_MAX_SLIP,
"slip %d > %d", i, DNS_RRL_MAX_SLIP);
}
rrl->slip = i;
rrl->scaled_slip = rrl->slip;
i = 15; i = 15;
obj = NULL; obj = NULL;
result = cfg_map_get(map, "window", &obj); result = cfg_map_get(map, "window", &obj);
if (result == ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) {
i = cfg_obj_asuint32(obj); i = cfg_obj_asuint32(obj);
CHECK_RRL(obj, i >= 1 && i <= DNS_RRL_MAX_WINDOW, CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW,
"window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW); "window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW);
} }
rrl->window = i; rrl->window = i;
@@ -1964,18 +1933,18 @@ configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
result = cfg_map_get(map, "qps-scale", &obj); result = cfg_map_get(map, "qps-scale", &obj);
if (result == ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) {
i = cfg_obj_asuint32(obj); i = cfg_obj_asuint32(obj);
CHECK_RRL(obj, i >= 1, "invalid 'qps-scale %d'%s", i, ""); CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, "");
} }
rrl->qps_scale = i; rrl->qps_scale = i;
rrl->qps = 1.0; rrl->qps = 1.0;
i = 24; i = 24;
obj = NULL; obj = NULL;
result = cfg_map_get(map, "IPv4-prefix-length", &obj); result = cfg_map_get(map, "ipv4-prefix-length", &obj);
if (result == ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) {
i = cfg_obj_asuint32(obj); i = cfg_obj_asuint32(obj);
CHECK_RRL(obj, i >= 8 && i <= 32, CHECK_RRL(i >= 8 && i <= 32,
"invalid 'IPv4-prefix-length %d'%s", i, ""); "invalid 'ipv4-prefix-length %d'%s", i, "");
} }
rrl->ipv4_prefixlen = i; rrl->ipv4_prefixlen = i;
if (i == 32) if (i == 32)
@@ -1985,11 +1954,11 @@ configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
i = 56; i = 56;
obj = NULL; obj = NULL;
result = cfg_map_get(map, "IPv6-prefix-length", &obj); result = cfg_map_get(map, "ipv6-prefix-length", &obj);
if (result == ISC_R_SUCCESS) { if (result == ISC_R_SUCCESS) {
i = cfg_obj_asuint32(obj); i = cfg_obj_asuint32(obj);
CHECK_RRL(obj, i >= 16 && i <= DNS_RRL_MAX_PREFIX, CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX,
"IPv6-prefix-length %d < 16 or > %d", "ipv6-prefix-length %d < 16 or > %d",
i, DNS_RRL_MAX_PREFIX); i, DNS_RRL_MAX_PREFIX);
} }
rrl->ipv6_prefixlen = i; rrl->ipv6_prefixlen = i;
@@ -2010,7 +1979,7 @@ configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
result = cfg_acl_fromconfig(obj, config, ns_g_lctx, result = cfg_acl_fromconfig(obj, config, ns_g_lctx,
ns_g_aclconfctx, ns_g_mctx, ns_g_aclconfctx, ns_g_mctx,
0, &rrl->exempt); 0, &rrl->exempt);
CHECK_RRL(obj, result == ISC_R_SUCCESS, CHECK_RRL(result == ISC_R_SUCCESS,
"invalid %s%s", "address match list", ""); "invalid %s%s", "address match list", "");
} }

View File

@@ -17,5 +17,5 @@
# Clean up after rrl tests. # Clean up after rrl tests.
rm -f dig.out* rm -f dig.out*
rm -f */named.memstats */named.run */named.stats */log */session.key rm -f */named.memstats */named.run */named.stats */log-* */session.key
rm -f ns3/bl*.db */*.jnl */*.core */*.pid rm -f ns3/bl*.db */*.jnl */*.core */*.pid

View File

@@ -32,15 +32,14 @@ options {
rate-limit { rate-limit {
responses-per-second 2; responses-per-second 2;
all-per-second 70; all-per-second 70;
IPv4-prefix-length 24;
IPv6-prefix-length 64;
slip 3; slip 3;
/* qps-scale 2; */
exempt-clients { 10.53.0.7; }; exempt-clients { 10.53.0.7; };
window 1;
max-table-size 100; // small enough to force a table expansion
min-table-size 2; min-table-size 75;
}; };
additional-from-cache no;
}; };
key rndc_key { key rndc_key {

View File

@@ -22,8 +22,10 @@ $TTL 120
NS . NS .
ns A 10.53.0.2 ns A 10.53.0.2
; basic rate limiting
a1 A 192.0.2.1 a1 A 192.0.2.1
; wildcards
*.a2 A 192.0.2.2 *.a2 A 192.0.2.2
; a3 is in tld3 ; a3 is in tld3
@@ -38,5 +40,8 @@ a6 A 192.0.2.6
; a7 for SERVFAIL ; a7 for SERVFAIL
; a8 for all-per-second limit ; a8 for NODATA
$GENERATE 101-180 all$.a8 A 192.0.2.8 a8 A 192.0.2.8
; a9 for all-per-second limit
$GENERATE 101-180 all$.a9 A 192.0.2.8

View File

@@ -27,6 +27,22 @@ options {
listen-on { 10.53.0.3; }; listen-on { 10.53.0.3; };
listen-on-v6 { none; }; listen-on-v6 { none; };
notify no; notify no;
// check that all of the options are parsed without limiting anything
rate-limit {
responses-per-second 200;
referrals-per-second 220;
nodata-per-second 230;
nxdomains-per-second 240;
errors-per-second 250;
all-per-second 700;
ipv4-prefix-length 24;
ipv6-prefix-length 64;
qps-scale 10;
window 1;
max-table-size 1000;
};
}; };
zone "." { type hint; file "hints"; }; zone "." { type hint; file "hints"; };

View File

@@ -64,15 +64,20 @@ sec_start () {
} }
# turn off ${HOME}/.digrc
HOME=/dev/null; export HOME
# $1=result name $2=domain name $3=dig options # $1=result name $2=domain name $3=dig options
digcmd () { digcmd () {
OFILE=$1; shift OFILE=$1; shift
DIG_DOM=$1; shift DIG_DOM=$1; shift
ARGS="+noadd +noauth +nosearch +time=1 +tries=1 +ignore $* -p 5300 $DIG_DOM @$ns2" ARGS="+nosearch +time=1 +tries=1 +ignore -p 5300 $* $DIG_DOM @$ns2"
#echo I:dig $ARGS 1>&2 #echo I:dig $ARGS 1>&2
START=`date +%y%m%d%H%M.%S` START=`date +%y%m%d%H%M.%S`
RESULT=`$DIG $ARGS 2>&1 | tee $OFILE=TEMP \ RESULT=`$DIG $ARGS 2>&1 | tee $OFILE=TEMP \
| sed -n -e 's/^[^;].* \([^ ]\{1,\}\)$/\1/p' \ | sed -n -e '/^;; AUTHORITY/,/^$/d' \
-e '/^;; ADDITIONAL/,/^$/d' \
-e 's/^[^;].* \([^ ]\{1,\}\)$/\1/p' \
-e 's/;; flags.* tc .*/TC/p' \ -e 's/;; flags.* tc .*/TC/p' \
-e 's/;; .* status: NXDOMAIN.*/NXDOMAIN/p' \ -e 's/;; .* status: NXDOMAIN.*/NXDOMAIN/p' \
-e 's/;; .* status: SERVFAIL.*/SERVFAIL/p' \ -e 's/;; .* status: SERVFAIL.*/SERVFAIL/p' \
@@ -117,7 +122,7 @@ ck_result() {
NXDOMAIN=`ls dig.out-$1-*=NXDOMAIN 2>/dev/null | wc -l | tr -d ' '` NXDOMAIN=`ls dig.out-$1-*=NXDOMAIN 2>/dev/null | wc -l | tr -d ' '`
SERVFAIL=`ls dig.out-$1-*=SERVFAIL 2>/dev/null | wc -l | tr -d ' '` SERVFAIL=`ls dig.out-$1-*=SERVFAIL 2>/dev/null | wc -l | tr -d ' '`
if test $ADDRS -ne "$3"; then if test $ADDRS -ne "$3"; then
setret "I:$ADDRS instead of $3 $2 responses for $1" setret "I:$ADDRS instead of $3 '$2' responses for $1"
BAD=yes BAD=yes
fi fi
if test $TC -ne "$4"; then if test $TC -ne "$4"; then
@@ -142,26 +147,47 @@ ck_result() {
} }
ckstats () {
LABEL="$1"; shift
TYPE="$1"; shift
EXPECTED="$1"; shift
CNT=`sed -n -e "s/[ ]*\([0-9]*\).responses $TYPE for rate limits.*/\1/p" \
ns2/named.stats | tail -1`
CNT=`expr 0$CNT + 0`
if test "$CNT" -ne $EXPECTED; then
setret "I:wrong $LABEL $TYPE statistics of $CNT instead of $EXPECTED"
fi
}
######### #########
sec_start sec_start
# Tests of referrals to "." must be done before the hints are loaded
# or with "additional-from-cache no"
burst 5 a1.tld3 +norec
# basic rate limiting # basic rate limiting
burst 3 a1.tld2 burst 3 a1.tld2
# 1 second delay allows an additional response. # 1 second delay allows an additional response.
sleep 1 sleep 1
burst 21 a1.tld2 burst 21 a1.tld2
# request 30 different qnames to try a wild card # Request 30 different qnames to try a wildcard.
burst 30 'x$CNT.a2.tld2' burst 30 'x$CNT.a2.tld2'
# These should be counted and limited but are not. See RT33138.
burst 10 'y.x$CNT.a2.tld2'
# IP TC drop NXDOMAIN SERVFAIL # IP TC drop NXDOMAIN SERVFAIL
# check for 24 results # referrals to "."
# including the 1 second delay ck_result a1.tld3 '' 2 1 2 0 0
# check 24 results including 1 second delay that allows an additional response
ck_result a1.tld2 192.0.2.1 3 7 14 0 0 ck_result a1.tld2 192.0.2.1 3 7 14 0 0
# Check the wild card answers. # Check the wild card answers.
# The parent name of the 30 requests is counted. # The parent name of the 30 requests is counted.
ck_result 'x*.a2.tld2' 192.0.2.2 2 10 18 0 0 ck_result 'x*.a2.tld2' 192.0.2.2 2 10 18 0 0
# These should be limited but are not. See RT33138.
ck_result 'y.x*.a2.tld2' 192.0.2.2 10 0 0 0 0
######### #########
sec_start sec_start
@@ -178,6 +204,10 @@ ck_result 'y*.a3.tld3' 192.0.3.3 3 6 12 0 0
# NXDOMAIN responses are also limited based on the parent name. # NXDOMAIN responses are also limited based on the parent name.
ck_result 'z*.a4.tld2' x 0 6 12 2 0 ck_result 'z*.a4.tld2' x 0 6 12 2 0
$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
ckstats first dropped 58
ckstats first truncated 30
######### #########
sec_start sec_start
@@ -185,6 +215,9 @@ sec_start
burst 20 a5.tld2 +tcp burst 20 a5.tld2 +tcp
burst 20 a6.tld2 -b $ns7 burst 20 a6.tld2 -b $ns7
burst 20 a7.tld4 burst 20 a7.tld4
burst 2 a8.tld2 AAAA
burst 2 a8.tld2 TXT
burst 2 a8.tld2 SPF
# TCP responses are not rate limited # TCP responses are not rate limited
ck_result a5.tld2 192.0.2.5 20 0 0 0 0 ck_result a5.tld2 192.0.2.5 20 0 0 0 0
@@ -196,6 +229,13 @@ ck_result a6.tld2 192.0.2.6 20 0 0 0 0
# other rate limiting can be triggered before the SERVFAIL limit is reached. # other rate limiting can be triggered before the SERVFAIL limit is reached.
ck_result a7.tld4 192.0.2.1 0 6 12 0 2 ck_result a7.tld4 192.0.2.1 0 6 12 0 2
# NODATA responses are counted as the same regardless of qtype.
ck_result a8.tld2 '' 2 2 2 0 0
$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
ckstats second dropped 72
ckstats second truncated 38
######### #########
sec_start sec_start
@@ -203,23 +243,14 @@ sec_start
# all-per-second # all-per-second
# The qnames are all unique but the client IP address is constant. # The qnames are all unique but the client IP address is constant.
CNT=101 CNT=101
burst 80 'all$CNT.a8.tld2' burst 80 'all$CNT.a9.tld2'
ck_result 'a*.a8.tld2' 192.0.2.8 70 0 10 0 0
ck_result 'a*.a9.tld2' 192.0.2.8 70 0 10 0 0
$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats $RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
ckstats () { ckstats final dropped 82
CNT=`sed -n -e "s/[ ]*\([0-9]*\).responses $1 for rate limits.*/\1/p" \ ckstats final truncated 38
ns2/named.stats`
CNT=`expr 0$CNT + 0`
if test "$CNT" -ne $2; then
setret "I:wrong $1 statistics of $CNT instead of $2"
fi
}
ckstats dropped 77
ckstats truncated 35
echo "I:exit status: $ret" echo "I:exit status: $ret"
# exit $ret exit $ret
[ $ret -ne 0 ] && echo "I:test failure overridden"
exit 0

View File

@@ -5449,7 +5449,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
<optional> filter-aaaa-on-v4 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional> <optional> filter-aaaa-on-v4 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
<optional> filter-aaaa-on-v6 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional> <optional> filter-aaaa-on-v6 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
<optional> filter-aaaa { <replaceable>address_match_list</replaceable> }; </optional> <optional> filter-aaaa { <replaceable>address_match_list</replaceable> }; </optional>
<optional> dns64 <replaceable>IPv6-prefix</replaceable> { <optional> dns64 <replaceable>ipv6-prefix</replaceable> {
<optional> clients { <replaceable>address_match_list</replaceable> }; </optional> <optional> clients { <replaceable>address_match_list</replaceable> }; </optional>
<optional> mapped { <replaceable>address_match_list</replaceable> }; </optional> <optional> mapped { <replaceable>address_match_list</replaceable> }; </optional>
<optional> exclude { <replaceable>address_match_list</replaceable> }; </optional> <optional> exclude { <replaceable>address_match_list</replaceable> }; </optional>
@@ -5487,14 +5487,16 @@ badresp:1,adberr:0,findfail:0,valfail:0]
<optional> deny-answer-aliases { <replaceable>namelist</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional> <optional> deny-answer-aliases { <replaceable>namelist</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
<optional> rate-limit { <optional> rate-limit {
<optional> responses-per-second <replaceable>number</replaceable> ; </optional> <optional> responses-per-second <replaceable>number</replaceable> ; </optional>
<optional> errors-per-second <replaceable>number</replaceable> ; </optional> <optional> referrals-per-second <replaceable>number</replaceable> ; </optional>
<optional> nodata-per-second <replaceable>number</replaceable> ; </optional>
<optional> nxdomains-per-second <replaceable>number</replaceable> ; </optional> <optional> nxdomains-per-second <replaceable>number</replaceable> ; </optional>
<optional> errors-per-second <replaceable>number</replaceable> ; </optional>
<optional> all-per-second <replaceable>number</replaceable> ; </optional> <optional> all-per-second <replaceable>number</replaceable> ; </optional>
<optional> window <replaceable>number</replaceable> ; </optional> <optional> window <replaceable>number</replaceable> ; </optional>
<optional> log-only <replaceable>yes_or_no</replaceable> ; </optional> <optional> log-only <replaceable>yes_or_no</replaceable> ; </optional>
<optional> qps-scale <replaceable>number</replaceable> ; </optional> <optional> qps-scale <replaceable>number</replaceable> ; </optional>
<optional> IPv4-prefix-length <replaceable>number</replaceable> ; </optional> <optional> ipv4-prefix-length <replaceable>number</replaceable> ; </optional>
<optional> IPv6-prefix-length <replaceable>number</replaceable> ; </optional> <optional> ipv6-prefix-length <replaceable>number</replaceable> ; </optional>
<optional> slip <replaceable>number</replaceable> ; </optional> <optional> slip <replaceable>number</replaceable> ; </optional>
<optional> exempt-clients { <replaceable>address_match_list</replaceable> } ; </optional> <optional> exempt-clients { <replaceable>address_match_list</replaceable> } ; </optional>
<optional> max-table-size <replaceable>number</replaceable> ; </optional> <optional> max-table-size <replaceable>number</replaceable> ; </optional>
@@ -10089,61 +10091,95 @@ ns.domain.com.rpz-nsdname CNAME .
</sect3> </sect3>
<sect3> <sect3>
<title>Rate Limiting</title> <title>Response Rate Limiting</title>
<para> <para>
Excessive essentially identical UDP <emphasis>responses</emphasis> Excessive almost identical UDP <emphasis>responses</emphasis>
can be discarded by configuring a can be controlled by configuring a
<command>rate-limit</command> clause in an <command>rate-limit</command> clause in an
<command>options</command> statement. <command>options</command> or <command>view</command> statement.
This mechanism keeps BIND 9 from being used This mechanism keeps authoritative BIND 9 from being used
in amplifying reflection denial of service attacks in amplifying reflection denial of service (DoS) attacks.
as well as partially protecting BIND 9 itself from Short truncated (TC=1) responses can be sent to provide
some denial of service attacks. rate-limited responses to legitimate clients within
Very short truncated responses can be sent to provide a range of forged, attacked IP addresses.
rate-limited responses to legitimate Legitimate clients react to dropped or truncated response
clients within a range of attacked and forged IP addresses, by retrying with UDP or with TCP respectively.
Legitimate clients react to truncated response by retrying
with TCP.
</para> </para>
<para> <para>
Rate limiting works by setting This mechanism is intended for authoritative DNS servers.
<command>responses-per-second</command> It can be used on recursive servers but can slow
to a number of repetitions per second for responses for a given name applications such as SMTP servers (mail receivers) and
and record type to a DNS client. HTTP clients (web browsers) that repeatedly request the
same domains.
When possible, closing "open" recursive servers is better.
</para> </para>
<para> <para>
<command>Responses-per-second</command> is a limit on Response rate limiting uses a "credit" or "token bucket" scheme.
identical responses instead of a limit on all responses or Each combination of identical response and client
even all responses to a single client. has a conceptual account that earns a specified number
10 identical responses per second is a generous limit except perhaps of credits every second.
when many clients are using a single IP address via network A prospective response debits its account by one.
address translation (NAT). Responses are dropped or truncated
The default limit of zero specifies an unbounded limit to turn off while the account is negative.
rate-limiting in a view or to only rate-limit NXDOMAIN or other Responses are tracked within a rolling window of time
errors. which defaults to 15 seconds, but can be configured with
the <command>window</command> option to any value from
1 to 3600 seconds (1 hour).
The account cannot become more positive than
the per-second limit
or more negative than <command>window</command>
times the per-second limit.
When the specified number of credits for a class of
responses is set to 0, those responses are not rate limited.
</para> </para>
<para> <para>
The notion of "identical responses" The notions of "identical response" and "DNS client"
and "single DNS client" cannot be simplistic. for rate limiting are not simplistic.
All responses to a CIDR block with prefix All responses to an address block are counted as if to a
length specified with <command>IPv4-prefix-length</command> single client.
(default 24) or <command>IPv6-prefix-length</command> The prefix lengths of addresses blocks are
(default 56) are assumed to come from a single DNS client. specified with <command>ipv4-prefix-length</command> (default 24)
Requests for a name that result in DNS NXDOMAIN and <command>ipv6-prefix-length</command> (default 56).
errors are considered identical. </para>
<para>
All non-empty responses for a valid domain name (qname)
and record type (qtype) are identical and have a limit specified
with <command>responses-per-second</command>
(default 0 or no limit).
All empty (NODATA) responses for a valid domain,
regardless of query type, are identical.
Responses in the NODATA class are limited by
<command>nodata-per-second</command>
(default <command>responses-per-second</command>).
Requests for any and all undefined subdomains of a given
valid domain result in NXDOMAIN errors, and are identical
regardless of query type.
They are limited by <command>nxdomain-per-second</command>
(default <command>responses-per-second</command>).
This controls some attacks using random names, but This controls some attacks using random names, but
accommodates servers that expect many legitimate NXDOMAIN responses can be relaxed or turned off (set to 0)
such as anti-spam blacklists. on servers that expect many legitimate
By default the limit on NXDOMAIN errors is the same as the NXDOMAIN responses, such as from anti-spam blacklists.
<command>responses-per-second</command> value, Referrals or delegations to the server of a given
but it can be set separately with domain are identical and are limited by
<command>nxdomains-per-second</command>. <command>referrals-per-second</command>
All requests for all names or types that result in DNS errors (default <command>responses-per-second</command>).
such as SERVFAIL and FORMERR (but not NXDOMAIN) are considered </para>
identical.
<para>
Responses generated from local wildcards are counted and limited
as if they were for the parent domain name.
This controls flooding using random.wild.example.com.
</para>
<para>
All requests that result in DNS errors other
than NXDOMAIN, such as SERVFAIL and FORMERR, are identical
regardless of requested name (qname) or record type (qtype).
This controls attacks using invalid requests or distant, This controls attacks using invalid requests or distant,
broken authoritative servers. broken authoritative servers.
By default the limit on errors is the same as the By default the limit on errors is the same as the
@@ -10152,33 +10188,6 @@ ns.domain.com.rpz-nsdname CNAME .
<command>errors-per-second</command>. <command>errors-per-second</command>.
</para> </para>
<para>
Rate limiting uses a "credit" or "token bucket" scheme.
Each identical response has a conceptual account
that is given <command>responses-per-second</command>,
<command>errors-per-second</command>, and
<command>nxdomains-per-second</command> credits every second.
A DNS request triggering some desired response debits
the account by one.
Responses are not sent while the account is negative.
The account cannot become more positive than
the per-second limit
or more negative than <command>window</command>
times the per-second limit.
A DNS client that sends requests that are not
answered can be penalized for up to <command>window</command>
seconds (default 15).
</para>
<para>
Responses generated from local wildcards are counted and limited
as if they were for the parent domain name.
This prevents flooding by requesting random.wild.example.com.
For similar reasons, NXDOMAIN responses are counted and rate
limited by the valid domain name nearest to the
query name with an SOA record.
</para>
<para> <para>
Many attacks using DNS involve UDP requests with forged source Many attacks using DNS involve UDP requests with forged source
addresses. addresses.
@@ -10188,14 +10197,15 @@ ns.domain.com.rpz-nsdname CNAME .
There is a mechanism that can answer some legitimate There is a mechanism that can answer some legitimate
requests from a client whose address is being forged in a flood. requests from a client whose address is being forged in a flood.
Setting <command>slip</command> to 2 (its default) causes every Setting <command>slip</command> to 2 (its default) causes every
other UDP request to be answered with a small response other UDP request to be answered with a small truncated (TC=1)
claiming that the response would have been truncated. response.
The small size and relative infrequency of the response make The small size and reduced frequency, and so lack of
it unattractive for abuse. amplification, of "slipped" responses make them unattractive
<command>Slip</command> must be between 0 and 10. for reflection DoS attacks.
A value of 0 does not "slip" <command>slip</command> must be between 0 and 10.
or sends no rate limiting truncated responses. A value of 0 does not "slip";
Some error responses includinge REFUSED and SERVFAIL no truncated responses are sent due to rate limiting.
Some error responses including REFUSED and SERVFAIL
cannot be replaced with truncated responses and are instead cannot be replaced with truncated responses and are instead
leaked at the <command>slip</command> rate. leaked at the <command>slip</command> rate.
</para> </para>
@@ -10225,8 +10235,8 @@ ns.domain.com.rpz-nsdname CNAME .
<command>rate-limit</command> statements in <command>view</command> <command>rate-limit</command> statements in <command>view</command>
statements instead of the global <command>option</command> statements instead of the global <command>option</command>
statement. statement.
A <command>rate-limit</command> statement in a view replaces A <command>rate-limit</command> statement in a view replaces,
instead of being merged with a <command>rate-limit</command> rather than supplementing, a <command>rate-limit</command>
statement among the main options. statement among the main options.
DNS clients within a view can be exempted from rate limits DNS clients within a view can be exempted from rate limits
with the <command>exempt-clients</command> clause. with the <command>exempt-clients</command> clause.

View File

@@ -71,7 +71,8 @@ typedef struct dns_rrl_hash dns_rrl_hash_t;
typedef enum { typedef enum {
DNS_RRL_RTYPE_FREE = 0, DNS_RRL_RTYPE_FREE = 0,
DNS_RRL_RTYPE_QUERY, DNS_RRL_RTYPE_QUERY,
DNS_RRL_RTYPE_DELEGATION, DNS_RRL_RTYPE_REFERRAL,
DNS_RRL_RTYPE_NODATA,
DNS_RRL_RTYPE_NXDOMAIN, DNS_RRL_RTYPE_NXDOMAIN,
DNS_RRL_RTYPE_ERROR, DNS_RRL_RTYPE_ERROR,
DNS_RRL_RTYPE_ALL, DNS_RRL_RTYPE_ALL,
@@ -190,6 +191,13 @@ struct dns_rrl_qname_buf {
dns_fixedname_t qname; dns_fixedname_t qname;
}; };
typedef struct dns_rrl_rate dns_rrl_rate_t;
struct dns_rrl_rate {
int r;
int scaled;
const char *str;
};
/* /*
* Per-view query rate limit parameters and a pointer to database. * Per-view query rate limit parameters and a pointer to database.
*/ */
@@ -199,12 +207,14 @@ struct dns_rrl {
isc_mem_t *mctx; isc_mem_t *mctx;
isc_boolean_t log_only; isc_boolean_t log_only;
int responses_per_second; dns_rrl_rate_t responses_per_second;
int errors_per_second; dns_rrl_rate_t referrals_per_second;
int nxdomains_per_second; dns_rrl_rate_t nodata_per_second;
int all_per_second; dns_rrl_rate_t nxdomains_per_second;
dns_rrl_rate_t errors_per_second;
dns_rrl_rate_t all_per_second;
dns_rrl_rate_t slip;
int window; int window;
int slip;
double qps_scale; double qps_scale;
int max_entries; int max_entries;
@@ -215,11 +225,6 @@ struct dns_rrl {
int qps_responses; int qps_responses;
isc_stdtime_t qps_time; isc_stdtime_t qps_time;
double qps; double qps;
int scaled_responses_per_second;
int scaled_errors_per_second;
int scaled_nxdomains_per_second;
int scaled_all_per_second;
int scaled_slip;
unsigned int probes; unsigned int probes;
unsigned int searches; unsigned int searches;

View File

@@ -167,11 +167,11 @@ set_age(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_stdtime_t now) {
if (ts >= DNS_RRL_MAX_TS) { if (ts >= DNS_RRL_MAX_TS) {
ts_gen = (ts_gen + 1) % DNS_RRL_TS_BASES; ts_gen = (ts_gen + 1) % DNS_RRL_TS_BASES;
for (e_old = ISC_LIST_TAIL(rrl->lru), i = 0; for (e_old = ISC_LIST_TAIL(rrl->lru), i = 0;
e_old != NULL && e_old->ts_gen == ts_gen; e_old != NULL && (e_old->ts_gen == ts_gen ||
!ISC_LINK_LINKED(e_old, hlink));
e_old = ISC_LIST_PREV(e_old, lru), ++i) e_old = ISC_LIST_PREV(e_old, lru), ++i)
{ {
if (e_old->ts_valid) e_old->ts_valid = ISC_FALSE;
e_old->ts_valid = ISC_FALSE;
} }
if (i != 0) if (i != 0)
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
@@ -403,9 +403,16 @@ make_key(const dns_rrl_t *rrl, dns_rrl_key_t *key,
memset(key, 0, sizeof(*key)); memset(key, 0, sizeof(*key));
key->s.rtype = rtype; key->s.rtype = rtype;
if (rtype == DNS_RRL_RTYPE_QUERY || rtype == DNS_RRL_RTYPE_DELEGATION) { if (rtype == DNS_RRL_RTYPE_QUERY) {
key->s.qclass = qclass & 0xff;
key->s.qtype = qtype; key->s.qtype = qtype;
key->s.qclass = qclass & 0xff;
} else if (rtype == DNS_RRL_RTYPE_REFERRAL ||
rtype == DNS_RRL_RTYPE_NODATA) {
/*
* Because there is no qtype in the empty answer sections of
* referral and NODATA responses, count them as the same.
*/
key->s.qclass = qclass & 0xff;
} }
if (qname != NULL && qname->labels != 0) { if (qname != NULL && qname->labels != 0) {
@@ -440,33 +447,40 @@ make_key(const dns_rrl_t *rrl, dns_rrl_key_t *key,
} }
} }
static inline int static inline dns_rrl_rate_t *
response_balance(const dns_rrl_t *rrl, const dns_rrl_entry_t *e, int age) { get_rate(dns_rrl_t *rrl, dns_rrl_rtype_t rtype) {
int balance, rate = 0; switch (rtype) {
case DNS_RRL_RTYPE_QUERY:
balance = e->responses; return (&rrl->responses_per_second);
if (balance < 0) case DNS_RRL_RTYPE_REFERRAL:
switch (e->key.s.rtype) { return (&rrl->referrals_per_second);
case DNS_RRL_RTYPE_QUERY: case DNS_RRL_RTYPE_NODATA:
case DNS_RRL_RTYPE_DELEGATION: return (&rrl->nodata_per_second);
rate = rrl->scaled_responses_per_second; case DNS_RRL_RTYPE_NXDOMAIN:
break; return (&rrl->nxdomains_per_second);
case DNS_RRL_RTYPE_NXDOMAIN: case DNS_RRL_RTYPE_ERROR:
rate = rrl->scaled_nxdomains_per_second; return (&rrl->errors_per_second);
break; case DNS_RRL_RTYPE_ALL:
case DNS_RRL_RTYPE_ERROR: return (&rrl->all_per_second);
rate = rrl->scaled_errors_per_second; default:
break; INSIST(0);
case DNS_RRL_RTYPE_ALL:
rate = rrl->scaled_all_per_second;
break;
case DNS_RRL_RTYPE_TCP:
rate = 1;
break;
default:
INSIST(0);
} }
balance += age * rate; return (NULL);
}
static int
response_balance(dns_rrl_t *rrl, const dns_rrl_entry_t *e, int age) {
dns_rrl_rate_t *ratep;
int balance, rate;
if (e->key.s.rtype == DNS_RRL_RTYPE_TCP) {
rate = 1;
} else {
ratep = get_rate(rrl, e->key.s.rtype);
rate = ratep->scaled;
}
balance = e->responses + age * rate;
if (balance > rate) if (balance > rate)
balance = rate; balance = rate;
return (balance); return (balance);
@@ -551,7 +565,7 @@ get_entry(dns_rrl_t *rrl, const isc_sockaddr_t *client_addr,
e = NULL; e = NULL;
break; break;
} }
if (!e->logged && response_balance(rrl, e, age) >= 0) if (!e->logged && response_balance(rrl, e, age) > 0)
break; break;
} }
if (e == NULL) { if (e == NULL) {
@@ -598,35 +612,16 @@ debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale,
const isc_sockaddr_t *client_addr, isc_stdtime_t now, const isc_sockaddr_t *client_addr, isc_stdtime_t now,
char *log_buf, unsigned int log_buf_len) char *log_buf, unsigned int log_buf_len)
{ {
int rate, new_rate, *ratep, slip, new_slip, age, log_secs, min; int rate, new_rate, slip, new_slip, age, log_secs, min;
const char *rate_str = NULL; dns_rrl_rate_t *ratep;
dns_rrl_entry_t const *credit_e; dns_rrl_entry_t const *credit_e;
/* /*
* Pick the rate counter. * Pick the rate counter.
* Optionally adjust the rate by the estimated query/second rate. * Optionally adjust the rate by the estimated query/second rate.
*/ */
switch (e->key.s.rtype) { ratep = get_rate(rrl, e->key.s.rtype);
case DNS_RRL_RTYPE_QUERY: rate = ratep->r;
case DNS_RRL_RTYPE_DELEGATION:
rate = rrl->responses_per_second;
ratep = &rrl->scaled_responses_per_second;
break;
case DNS_RRL_RTYPE_NXDOMAIN:
rate = rrl->nxdomains_per_second;
ratep = &rrl->scaled_nxdomains_per_second;
break;
case DNS_RRL_RTYPE_ERROR:
rate = rrl->errors_per_second;
ratep = &rrl->scaled_errors_per_second;
break;
case DNS_RRL_RTYPE_ALL:
rate = rrl->all_per_second;
ratep = &rrl->scaled_all_per_second;
break;
default:
INSIST(0);
}
if (rate == 0) if (rate == 0)
return (DNS_RRL_RESULT_OK); return (DNS_RRL_RESULT_OK);
@@ -648,36 +643,16 @@ debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale,
new_rate = (int) (rate * scale); new_rate = (int) (rate * scale);
if (new_rate < 1) if (new_rate < 1)
new_rate = 1; new_rate = 1;
if (*ratep != new_rate) { if (ratep->scaled != new_rate) {
if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1)) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
switch (e->key.s.rtype) { DNS_LOGMODULE_REQUEST,
case DNS_RRL_RTYPE_QUERY: DNS_RRL_LOG_DEBUG1,
case DNS_RRL_RTYPE_DELEGATION: "%d qps scaled %s by %.2f"
rate_str = "responses-per-second"; " from %d to %d",
break; (int)qps, ratep->str, scale,
case DNS_RRL_RTYPE_NXDOMAIN: rate, new_rate);
rate_str = "nxdomains-per-second";
break;
case DNS_RRL_RTYPE_ERROR:
rate_str = "errors-per-second";
break;
case DNS_RRL_RTYPE_ALL:
rate_str = "all-per-second";
break;
case DNS_RRL_RTYPE_FREE:
case DNS_RRL_RTYPE_TCP:
INSIST(0);
}
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
DNS_LOGMODULE_REQUEST,
DNS_RRL_LOG_DEBUG1,
"%d qps scaled %s by %.2f"
" from %d to %d",
(int)qps, rate_str, scale,
rate, new_rate);
}
rate = new_rate; rate = new_rate;
*ratep = rate; ratep->scaled = rate;
} }
} }
@@ -736,22 +711,21 @@ debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale,
/* /*
* Drop this response unless it should slip or leak. * Drop this response unless it should slip or leak.
*/ */
slip = rrl->slip; slip = rrl->slip.r;
if (slip > 2 && scale < 1.0) { if (slip > 2 && scale < 1.0) {
new_slip = (int) (slip * scale); new_slip = (int) (slip * scale);
if (new_slip < 2) if (new_slip < 2)
new_slip = 2; new_slip = 2;
if (rrl->scaled_slip != new_slip) { if (rrl->slip.scaled != new_slip) {
if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1)) isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL, DNS_LOGMODULE_REQUEST,
DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG1,
DNS_RRL_LOG_DEBUG1, "%d qps scaled slip"
"%d qps scaled slip" " by %.2f from %d to %d",
" by %.2f from %d to %d", (int)qps, scale,
(int)qps, scale, slip, new_slip);
slip, new_slip);
slip = new_slip; slip = new_slip;
rrl->scaled_slip = slip; rrl->slip.scaled = slip;
} }
} }
if (slip != 0 && e->key.s.rtype != DNS_RRL_RTYPE_ALL) { if (slip != 0 && e->key.s.rtype != DNS_RRL_RTYPE_ALL) {
@@ -853,34 +827,36 @@ make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
switch (e->key.s.rtype) { switch (e->key.s.rtype) {
case DNS_RRL_RTYPE_QUERY: case DNS_RRL_RTYPE_QUERY:
ADD_LOG_CSTR(&lb, "response");
break; break;
case DNS_RRL_RTYPE_DELEGATION: case DNS_RRL_RTYPE_REFERRAL:
ADD_LOG_CSTR(&lb, "referral"); ADD_LOG_CSTR(&lb, "referral ");
break;
case DNS_RRL_RTYPE_NODATA:
ADD_LOG_CSTR(&lb, "NODATA ");
break; break;
case DNS_RRL_RTYPE_NXDOMAIN: case DNS_RRL_RTYPE_NXDOMAIN:
ADD_LOG_CSTR(&lb, "NXDOMAIN response"); ADD_LOG_CSTR(&lb, "NXDOMAIN ");
break; break;
case DNS_RRL_RTYPE_ERROR: case DNS_RRL_RTYPE_ERROR:
if (resp_result == ISC_R_SUCCESS) { if (resp_result == ISC_R_SUCCESS) {
ADD_LOG_CSTR(&lb, "error response"); ADD_LOG_CSTR(&lb, "error ");
} else { } else {
rstr = isc_result_totext(resp_result); rstr = isc_result_totext(resp_result);
add_log_str(&lb, rstr, strlen(rstr)); add_log_str(&lb, rstr, strlen(rstr));
ADD_LOG_CSTR(&lb, " response"); ADD_LOG_CSTR(&lb, " error ");
} }
break; break;
case DNS_RRL_RTYPE_ALL: case DNS_RRL_RTYPE_ALL:
ADD_LOG_CSTR(&lb, "all response"); ADD_LOG_CSTR(&lb, "all ");
break; break;
default: default:
INSIST(0); INSIST(0);
} }
if (plural) if (plural)
ADD_LOG_CSTR(&lb, "s to "); ADD_LOG_CSTR(&lb, "responses to ");
else else
ADD_LOG_CSTR(&lb, " to "); ADD_LOG_CSTR(&lb, "response to ");
memset(&cidr, 0, sizeof(cidr)); memset(&cidr, 0, sizeof(cidr));
if (e->key.s.ipv6) { if (e->key.s.ipv6) {
@@ -899,7 +875,8 @@ make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
add_log_str(&lb, strbuf, strlen(strbuf)); add_log_str(&lb, strbuf, strlen(strbuf));
if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY || if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY ||
e->key.s.rtype == DNS_RRL_RTYPE_DELEGATION || e->key.s.rtype == DNS_RRL_RTYPE_REFERRAL ||
e->key.s.rtype == DNS_RRL_RTYPE_NODATA ||
e->key.s.rtype == DNS_RRL_RTYPE_NXDOMAIN) { e->key.s.rtype == DNS_RRL_RTYPE_NXDOMAIN) {
qbuf = get_qname(rrl, e); qbuf = get_qname(rrl, e);
if (save_qname && qbuf == NULL && if (save_qname && qbuf == NULL &&
@@ -947,8 +924,10 @@ make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
if (e->key.s.rtype != DNS_RRL_RTYPE_NXDOMAIN) { if (e->key.s.rtype != DNS_RRL_RTYPE_NXDOMAIN) {
ADD_LOG_CSTR(&lb, " "); ADD_LOG_CSTR(&lb, " ");
(void)dns_rdataclass_totext(e->key.s.qclass, &lb); (void)dns_rdataclass_totext(e->key.s.qclass, &lb);
ADD_LOG_CSTR(&lb, " "); if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY) {
(void)dns_rdatatype_totext(e->key.s.qtype, &lb); ADD_LOG_CSTR(&lb, " ");
(void)dns_rdatatype_totext(e->key.s.qtype, &lb);
}
} }
snprintf(strbuf, sizeof(strbuf), " (%08x)", snprintf(strbuf, sizeof(strbuf), " (%08x)",
e->key.s.qname_hash); e->key.s.qname_hash);
@@ -1117,14 +1096,23 @@ dns_rrl(dns_view_t *view,
* Find the right kind of entry, creating it if necessary. * Find the right kind of entry, creating it if necessary.
* If that is impossible, then nothing more can be done * If that is impossible, then nothing more can be done
*/ */
if (resp_result == ISC_R_SUCCESS) switch (resp_result) {
case ISC_R_SUCCESS:
rtype = DNS_RRL_RTYPE_QUERY; rtype = DNS_RRL_RTYPE_QUERY;
else if (resp_result == DNS_R_DELEGATION) break;
rtype = DNS_RRL_RTYPE_DELEGATION; case DNS_R_DELEGATION:
else if (resp_result == DNS_R_NXDOMAIN) rtype = DNS_RRL_RTYPE_REFERRAL;
break;
case DNS_R_NXRRSET:
rtype = DNS_RRL_RTYPE_NODATA;
break;
case DNS_R_NXDOMAIN:
rtype = DNS_RRL_RTYPE_NXDOMAIN; rtype = DNS_RRL_RTYPE_NXDOMAIN;
else break;
default:
rtype = DNS_RRL_RTYPE_ERROR; rtype = DNS_RRL_RTYPE_ERROR;
break;
}
e = get_entry(rrl, client_addr, qclass, qtype, qname, rtype, e = get_entry(rrl, client_addr, qclass, qtype, qname, rtype,
now, ISC_TRUE, log_buf, log_buf_len); now, ISC_TRUE, log_buf, log_buf_len);
if (e == NULL) { if (e == NULL) {
@@ -1148,7 +1136,7 @@ dns_rrl(dns_view_t *view,
rrl_result = debit_rrl_entry(rrl, e, qps, scale, client_addr, now, rrl_result = debit_rrl_entry(rrl, e, qps, scale, client_addr, now,
log_buf, log_buf_len); log_buf, log_buf_len);
if (rrl->all_per_second != 0) { if (rrl->all_per_second.r != 0) {
/* /*
* We must debit the all-per-second token bucket if we have * We must debit the all-per-second token bucket if we have
* an all-per-second limit for the IP address. * an all-per-second limit for the IP address.

View File

@@ -1324,16 +1324,17 @@ static cfg_type_t cfg_type_rpz = {
*/ */
static cfg_clausedef_t rrl_clauses[] = { static cfg_clausedef_t rrl_clauses[] = {
{ "responses-per-second", &cfg_type_uint32, 0 }, { "responses-per-second", &cfg_type_uint32, 0 },
{ "errors-per-second", &cfg_type_uint32, 0 }, { "referrals-per-second", &cfg_type_uint32, 0 },
{ "nodata-per-second", &cfg_type_uint32, 0 },
{ "nxdomains-per-second", &cfg_type_uint32, 0 }, { "nxdomains-per-second", &cfg_type_uint32, 0 },
{ "responses-per-second", &cfg_type_uint32, 0 }, { "errors-per-second", &cfg_type_uint32, 0 },
{ "all-per-second", &cfg_type_uint32, 0 }, { "all-per-second", &cfg_type_uint32, 0 },
{ "slip", &cfg_type_uint32, 0 }, { "slip", &cfg_type_uint32, 0 },
{ "window", &cfg_type_uint32, 0 }, { "window", &cfg_type_uint32, 0 },
{ "log-only", &cfg_type_boolean, 0 }, { "log-only", &cfg_type_boolean, 0 },
{ "qps-scale", &cfg_type_uint32, 0 }, { "qps-scale", &cfg_type_uint32, 0 },
{ "IPv4-prefix-length", &cfg_type_uint32, 0 }, { "ipv4-prefix-length", &cfg_type_uint32, 0 },
{ "IPv6-prefix-length", &cfg_type_uint32, 0 }, { "ipv6-prefix-length", &cfg_type_uint32, 0 },
{ "exempt-clients", &cfg_type_bracketed_aml, 0 }, { "exempt-clients", &cfg_type_bracketed_aml, 0 },
{ "max-table-size", &cfg_type_uint32, 0 }, { "max-table-size", &cfg_type_uint32, 0 },
{ "min-table-size", &cfg_type_uint32, 0 }, { "min-table-size", &cfg_type_uint32, 0 },