]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] fixed several RRL issues
authorEvan Hunt <each@isc.org>
Thu, 25 Apr 2013 21:40:32 +0000 (14:40 -0700)
committerEvan Hunt <each@isc.org>
Thu, 25 Apr 2013 21:42:44 +0000 (14:42 -0700)
3554. [bug] RRL failed to correctly rate-limit upward
referrals and failed to count dropped error
responses in the statistics. [RT #33225]

13 files changed:
CHANGES
bin/named/client.c
bin/named/query.c
bin/named/server.c
bin/tests/system/rrl/clean.sh
bin/tests/system/rrl/ns2/named.conf
bin/tests/system/rrl/ns2/tld2.db
bin/tests/system/rrl/ns3/named.conf
bin/tests/system/rrl/tests.sh
doc/arm/Bv9ARM-book.xml
lib/dns/include/dns/rrl.h
lib/dns/rrl.c
lib/isccfg/namedconf.c

diff --git a/CHANGES b/CHANGES
index 515e37e03f476fe308194018e3e53fe32590ea9a..605183c34fffd8f4cc560d4a8e5fba1da6184259 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -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]
 
 3552.  [bug]           Wrong getopt option string for 'nsupdate -r'.
index 67403d07a48df23d6a05b757e957679c38e3e38a..1276dcbdc0e77c5d1e832058391a29330106e4db 100644 (file)
@@ -1257,11 +1257,13 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
                        }
                        /*
                         * Some error responses cannot be 'slipped',
-                        * so don't try.
-                        * This will counted with dropped queries in the
-                        * QryDropped counter.
+                        * so don't try to slip any error responses.
                         */
                        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);
                                return;
                        }
index b9d4891f9d0a07206ddf4c03b1cc72a6aa139f65..c6c58581dc7304c1608aea2c76a671239e05e2e5 100644 (file)
@@ -191,7 +191,7 @@ inc_stats(ns_client_t *client, isc_statscounter_t counter) {
 
        /* 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.
         */
        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.
         */
        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) {
                dns_rdataset_t nc_rdataset;
                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);
                        }
                        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) {
                        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 {
                        rrl_result = ISC_R_SUCCESS;
                }
index fbf7db0555641e7d7864623dd101a2be2c12f3c5..f22d09ee0404a32afc0b0b1f589a2d1ee0e4255f 100644 (file)
@@ -1835,7 +1835,7 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
        return (ISC_R_SUCCESS);
 }
 
-#define CHECK_RRL(obj, cond, pat, val1, val2)                          \
+#define CHECK_RRL(cond, pat, val1, val2)                               \
        do {                                                            \
                if (!(cond)) {                                          \
                        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)
 
+#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
 configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
        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);
        if (result == ISC_R_SUCCESS) {
                i = cfg_obj_asuint32(obj);
-               CHECK_RRL(obj, i >= min_entries,
+               CHECK_RRL(i >= min_entries,
                          "max-table-size %d < min-table-size %d",
                          i, min_entries);
        }
        rrl->max_entries = i;
 
-       i = 0;
-       obj = NULL;
-       result = cfg_map_get(map, "responses-per-second", &obj);
-       if (result == ISC_R_SUCCESS) {
-               i = cfg_obj_asuint32(obj);
-               CHECK_RRL(obj, i <= DNS_RRL_MAX_RATE,
-                         "responses-per-second %d > %d",
-                         i, DNS_RRL_MAX_RATE);
-       }
-       rrl->responses_per_second = i;
-       rrl->scaled_responses_per_second = rrl->responses_per_second;
-
-       /*
-        * The default error rate is the response rate,
-        * 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;
-
-       /*
-        * The all-per-second rate is off by default.
-        */
-       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;
+       CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE,
+                      "responses-per-second");
+       CHECK_RRL_RATE(referrals_per_second,
+                      rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
+                      "referrals-per-second");
+       CHECK_RRL_RATE(nodata_per_second,
+                      rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
+                      "nodata-per-second");
+       CHECK_RRL_RATE(nxdomains_per_second,
+                      rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
+                      "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,
+                      "all-per-second");
+
+       CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP,
+                      "slip");
 
        i = 15;
        obj = NULL;
        result = cfg_map_get(map, "window", &obj);
        if (result == ISC_R_SUCCESS) {
                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);
        }
        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);
        if (result == ISC_R_SUCCESS) {
                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 = 1.0;
 
        i = 24;
        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) {
                i = cfg_obj_asuint32(obj);
-               CHECK_RRL(obj, i >= 8 && i <= 32,
-                         "invalid 'IPv4-prefix-length %d'%s", i, "");
+               CHECK_RRL(i >= 8 && i <= 32,
+                         "invalid 'ipv4-prefix-length %d'%s", i, "");
        }
        rrl->ipv4_prefixlen = i;
        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;
        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) {
                i = cfg_obj_asuint32(obj);
-               CHECK_RRL(obj, i >= 16 && i <= DNS_RRL_MAX_PREFIX,
-                         "IPv6-prefix-length %d < 16 or > %d",
+               CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX,
+                         "ipv6-prefix-length %d < 16 or > %d",
                          i, DNS_RRL_MAX_PREFIX);
        }
        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,
                                            ns_g_aclconfctx, ns_g_mctx,
                                            0, &rrl->exempt);
-               CHECK_RRL(obj, result == ISC_R_SUCCESS,
+               CHECK_RRL(result == ISC_R_SUCCESS,
                          "invalid %s%s", "address match list", "");
        }
 
index 7b9a2ec0591831e4ef25a791a2ccbf69cabbd3bf..e42c43868e3ff5a5856e48f2d49e00988562e08f 100644 (file)
@@ -17,5 +17,5 @@
 # Clean up after rrl tests.
 
 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
index 9f2bb865db01cd29172e21c96e87ec7453cdf0a3..7c00577703bf07ee99e70e47fa9e214181fda2fc 100644 (file)
@@ -32,15 +32,14 @@ options {
        rate-limit {
            responses-per-second 2;
            all-per-second 70;
-           IPv4-prefix-length 24;
-           IPv6-prefix-length 64;
            slip 3;
-           /* qps-scale 2; */
            exempt-clients { 10.53.0.7; };
-           window 1;
-           max-table-size 100;
-           min-table-size 2;
+
+           // small enough to force a table expansion
+           min-table-size 75;
        };
+
+       additional-from-cache no;
 };
 
 key rndc_key {
index 8b965814ad2b57431c1dff86eed420cbf5effea6..0d9c8d07a86c4094c4858d65b39b58804498d312 100644 (file)
@@ -22,8 +22,10 @@ $TTL 120
                NS      .
 ns             A       10.53.0.2
 
+; basic rate limiting
 a1             A       192.0.2.1
 
+; wildcards
 *.a2           A       192.0.2.2
 
 ; a3 is in tld3
@@ -38,5 +40,8 @@ a6            A       192.0.2.6
 
 ; a7 for SERVFAIL
 
-; a8 for all-per-second limit
-$GENERATE 101-180 all$.a8 A 192.0.2.8
+; a8 for NODATA
+a8             A       192.0.2.8
+
+; a9 for all-per-second limit
+$GENERATE 101-180 all$.a9 A 192.0.2.8
index fb3622ac00f5e82efd85f8a19ea1639b79e848cd..75d5a808c8ca39b04b65e2da5d3a1766f01761b8 100644 (file)
@@ -27,6 +27,22 @@ options {
        listen-on { 10.53.0.3; };
        listen-on-v6 { none; };
        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"; };
index 5797889529b6a85c8e725b649f72a2d9fa4f92f4..c5a050b450b90d2af5468c23bed5f79718043908 100644 (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
 digcmd () {
     OFILE=$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
     START=`date +%y%m%d%H%M.%S`
     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/;; .* status: NXDOMAIN.*/NXDOMAIN/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 ' '`
     SERVFAIL=`ls dig.out-$1-*=SERVFAIL 2>/dev/null     | wc -l | tr -d ' '`
     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
     fi
     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
 
+# 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
 burst 3 a1.tld2
 # 1 second delay allows an additional response.
 sleep 1
 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'
+# These should be counted and limited but are not.  See RT33138.
+burst 10 'y.x$CNT.a2.tld2'
 
 #                                      IP      TC      drop  NXDOMAIN SERVFAIL
-# check for 24 results
-# including the 1 second delay
+# referrals to "."
+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
 
 # Check the wild card answers.
 # The parent name of the 30 requests is counted.
 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
@@ -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.
 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
@@ -185,6 +215,9 @@ sec_start
 burst 20 a5.tld2 +tcp
 burst 20 a6.tld2 -b $ns7
 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
 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.
 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
@@ -203,23 +243,14 @@ sec_start
 # all-per-second
 #   The qnames are all unique but the client IP address is constant.
 CNT=101
-burst 80 'all$CNT.a8.tld2'
-ck_result 'a*.a8.tld2' 192.0.2.8       70      0       10      0       0
+burst 80 'all$CNT.a9.tld2'
 
+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
-ckstats () {
-    CNT=`sed -n -e "s/[         ]*\([0-9]*\).responses $1 for rate limits.*/\1/p"  \
-               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
+ckstats final dropped 82
+ckstats final truncated 38
+
 
 echo "I:exit status: $ret"
-# exit $ret
-[ $ret -ne 0 ] && echo "I:test failure overridden"
-exit 0
+exit $ret
index dcfcfb22b740d06f3694843c832cca841b3cd6fd..c89a27c2c3c282e16350fb6084478aa02ec2e705 100644 (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-v6 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</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> mapped { <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> rate-limit {
        <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> errors-per-second <replaceable>number</replaceable> ; </optional>
        <optional> all-per-second <replaceable>number</replaceable> ; </optional>
        <optional> window <replaceable>number</replaceable> ; </optional>
        <optional> log-only <replaceable>yes_or_no</replaceable> ; </optional>
        <optional> qps-scale <replaceable>number</replaceable> ; </optional>
-       <optional> IPv4-prefix-length <replaceable>number</replaceable> ; </optional>
-       <optional> IPv6-prefix-length <replaceable>number</replaceable> ; </optional>
+       <optional> ipv4-prefix-length <replaceable>number</replaceable> ; </optional>
+       <optional> ipv6-prefix-length <replaceable>number</replaceable> ; </optional>
        <optional> slip <replaceable>number</replaceable> ; </optional>
        <optional> exempt-clients  { <replaceable>address_match_list</replaceable> } ; </optional>
        <optional> max-table-size <replaceable>number</replaceable> ; </optional>
@@ -10089,94 +10091,101 @@ ns.domain.com.rpz-nsdname   CNAME   .
         </sect3>
 
        <sect3>
-         <title>Rate Limiting</title>
+         <title>Response Rate Limiting</title>
          <para>
-           Excessive essentially identical UDP <emphasis>responses</emphasis>
-           can be discarded by configuring a
+           Excessive almost identical UDP <emphasis>responses</emphasis>
+           can be controlled by configuring a
            <command>rate-limit</command> clause in an
-           <command>options</command> statement.
-           This mechanism keeps BIND 9 from being used
-           in amplifying reflection denial of service attacks
-           as well as partially protecting BIND 9 itself from
-           some denial of service attacks.
-           Very short truncated responses can be sent to provide
-           rate-limited responses to legitimate
-           clients within a range of attacked and forged IP addresses,
-           Legitimate clients react to truncated response by retrying
-           with TCP.
+           <command>options</command> or <command>view</command> statement.
+           This mechanism keeps authoritative BIND 9 from being used
+           in amplifying reflection denial of service (DoS) attacks.
+           Short truncated (TC=1) responses can be sent to provide
+           rate-limited responses to legitimate clients within
+           a range of forged, attacked IP addresses.
+           Legitimate clients react to dropped or truncated response
+           by retrying with UDP or with TCP respectively.
          </para>
 
          <para>
-           Rate limiting works by setting
-           <command>responses-per-second</command>
-           to a number of repetitions per second for responses for a given name
-           and record type to a DNS client.
+           This mechanism is intended for authoritative DNS servers.
+           It can be used on recursive servers but can slow
+           applications such as SMTP servers (mail receivers) and
+           HTTP clients (web browsers) that repeatedly request the
+           same domains.
+           When possible, closing "open" recursive servers is better.
          </para>
 
          <para>
-           <command>Responses-per-second</command> is a limit on
-           identical responses instead of a limit on all responses or
-           even all responses to a single client.
-           10 identical responses per second is a generous limit except perhaps
-           when many clients are using a single IP address via network
-           address translation (NAT).
-           The default limit of zero specifies an unbounded limit to turn off
-           rate-limiting in a view or to only rate-limit NXDOMAIN or other
-           errors.
+           Response rate limiting uses a "credit" or "token bucket" scheme.
+           Each combination of identical response and client
+           has a conceptual account that earns a specified number
+           of credits every second.
+           A prospective response debits its account by one.
+           Responses are dropped or truncated
+           while the account is negative.
+            Responses are tracked within a rolling window of time
+            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>
-           The notion of "identical responses"
-           and "single DNS client" cannot be simplistic.
-           All responses to a CIDR block with prefix
-           length specified with <command>IPv4-prefix-length</command>
-           (default 24) or <command>IPv6-prefix-length</command>
-           (default 56) are assumed to come from a single DNS client.
-           Requests for a name that result in DNS NXDOMAIN
-           errors are considered identical.
-           This controls some attacks using random names, but
-           accommodates servers that expect many legitimate NXDOMAIN responses
-           such as anti-spam blacklists.
-           By default the limit on NXDOMAIN errors is the same as the
-           <command>responses-per-second</command> value,
-           but it can be set separately with
-           <command>nxdomains-per-second</command>.
-           All requests for all names or types that result in DNS errors
-           such as SERVFAIL and FORMERR (but not NXDOMAIN) are considered
-           identical.
-           This controls attacks using invalid requests or distant,
-           broken authoritative servers.
-           By default the limit on errors is the same as the
-           <command>responses-per-second</command> value,
-           but it can be set separately with
-           <command>errors-per-second</command>.
+           The notions of "identical response" and "DNS client"
+           for rate limiting are not simplistic.
+           All responses to an address block are counted as if to a
+           single client.
+           The prefix lengths of addresses blocks are
+           specified with <command>ipv4-prefix-length</command> (default 24)
+           and <command>ipv6-prefix-length</command> (default 56).
          </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).
+           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
+           can be relaxed or turned off (set to 0)
+           on servers that expect many legitimate
+           NXDOMAIN responses, such as from anti-spam blacklists.
+           Referrals or delegations to the server of a given
+           domain are identical and are limited by
+           <command>referrals-per-second</command>
+           (default <command>responses-per-second</command>).
          </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.
+           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,
+           broken authoritative servers.
+           By default the limit on errors is the same as the
+           <command>responses-per-second</command> value,
+           but it can be set separately with
+           <command>errors-per-second</command>.
          </para>
 
          <para>
@@ -10188,14 +10197,15 @@ ns.domain.com.rpz-nsdname   CNAME   .
            There is a mechanism that can answer some legitimate
            requests from a client whose address is being forged in a flood.
            Setting <command>slip</command> to 2 (its default) causes every
-           other UDP request to be answered with a small response
-           claiming that the response would have been truncated.
-           The small size and relative infrequency of the response make
-           it unattractive for abuse.
-           <command>Slip</command> must be between 0 and 10.
-           A value of 0 does not "slip"
-           or sends no rate limiting truncated responses.
-           Some error responses includinge REFUSED and SERVFAIL
+           other UDP request to be answered with a small truncated (TC=1)
+           response.
+           The small size and reduced frequency, and so lack of
+           amplification, of "slipped" responses make them unattractive
+           for reflection DoS attacks.
+           <command>slip</command> must be between 0 and 10.
+           A value of 0 does not "slip";
+           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
            leaked at the <command>slip</command> rate.
          </para>
@@ -10225,8 +10235,8 @@ ns.domain.com.rpz-nsdname   CNAME   .
            <command>rate-limit</command> statements in <command>view</command>
            statements instead of the global <command>option</command>
            statement.
-           A <command>rate-limit</command> statement in a view replaces
-           instead of being merged with a <command>rate-limit</command>
+           A <command>rate-limit</command> statement in a view replaces,
+           rather than supplementing, a <command>rate-limit</command>
            statement among the main options.
            DNS clients within a view can be exempted from rate limits
            with the <command>exempt-clients</command> clause.
index 4cabfa84c95751741f6146a5690dc1f96f6b448e..ef6b72b4280d2bcf105a2d33c13f6b84ab75d479 100644 (file)
@@ -71,7 +71,8 @@ typedef struct dns_rrl_hash dns_rrl_hash_t;
 typedef enum {
        DNS_RRL_RTYPE_FREE = 0,
        DNS_RRL_RTYPE_QUERY,
-       DNS_RRL_RTYPE_DELEGATION,
+       DNS_RRL_RTYPE_REFERRAL,
+       DNS_RRL_RTYPE_NODATA,
        DNS_RRL_RTYPE_NXDOMAIN,
        DNS_RRL_RTYPE_ERROR,
        DNS_RRL_RTYPE_ALL,
@@ -190,6 +191,13 @@ struct dns_rrl_qname_buf {
        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.
  */
@@ -199,12 +207,14 @@ struct dns_rrl {
        isc_mem_t       *mctx;
 
        isc_boolean_t   log_only;
-       int             responses_per_second;
-       int             errors_per_second;
-       int             nxdomains_per_second;
-       int             all_per_second;
+       dns_rrl_rate_t  responses_per_second;
+       dns_rrl_rate_t  referrals_per_second;
+       dns_rrl_rate_t  nodata_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             slip;
        double          qps_scale;
        int             max_entries;
 
@@ -215,11 +225,6 @@ struct dns_rrl {
        int             qps_responses;
        isc_stdtime_t   qps_time;
        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    searches;
index 063e0474720f19c55ffc0836163543e65e61f58e..3a6abfa462116a2f87b71f8877fa40ae9f8af506 100644 (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) {
                ts_gen = (ts_gen + 1) % DNS_RRL_TS_BASES;
                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)
                {
-                       if (e_old->ts_valid)
-                               e_old->ts_valid = ISC_FALSE;
+                       e_old->ts_valid = ISC_FALSE;
                }
                if (i != 0)
                        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));
 
        key->s.rtype = rtype;
-       if (rtype == DNS_RRL_RTYPE_QUERY || rtype == DNS_RRL_RTYPE_DELEGATION) {
-               key->s.qclass = qclass & 0xff;
+       if (rtype == DNS_RRL_RTYPE_QUERY) {
                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) {
@@ -440,33 +447,40 @@ make_key(const dns_rrl_t *rrl, dns_rrl_key_t *key,
        }
 }
 
-static inline int
-response_balance(const dns_rrl_t *rrl, const dns_rrl_entry_t *e, int age) {
-       int balance, rate = 0;
-
-       balance = e->responses;
-       if (balance < 0)
-               switch (e->key.s.rtype) {
-               case DNS_RRL_RTYPE_QUERY:
-               case DNS_RRL_RTYPE_DELEGATION:
-                       rate = rrl->scaled_responses_per_second;
-                       break;
-               case DNS_RRL_RTYPE_NXDOMAIN:
-                       rate = rrl->scaled_nxdomains_per_second;
-                       break;
-               case DNS_RRL_RTYPE_ERROR:
-                       rate = rrl->scaled_errors_per_second;
-                       break;
-               case DNS_RRL_RTYPE_ALL:
-                       rate = rrl->scaled_all_per_second;
-                       break;
-               case DNS_RRL_RTYPE_TCP:
-                       rate = 1;
-                       break;
-               default:
-                       INSIST(0);
+static inline dns_rrl_rate_t *
+get_rate(dns_rrl_t *rrl, dns_rrl_rtype_t rtype) {
+       switch (rtype) {
+       case DNS_RRL_RTYPE_QUERY:
+               return (&rrl->responses_per_second);
+       case DNS_RRL_RTYPE_REFERRAL:
+               return (&rrl->referrals_per_second);
+       case DNS_RRL_RTYPE_NODATA:
+               return (&rrl->nodata_per_second);
+       case DNS_RRL_RTYPE_NXDOMAIN:
+               return (&rrl->nxdomains_per_second);
+       case DNS_RRL_RTYPE_ERROR:
+               return (&rrl->errors_per_second);
+       case DNS_RRL_RTYPE_ALL:
+               return (&rrl->all_per_second);
+       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)
                balance = rate;
        return (balance);
@@ -551,7 +565,7 @@ get_entry(dns_rrl_t *rrl, const isc_sockaddr_t *client_addr,
                        e = NULL;
                        break;
                }
-               if (!e->logged && response_balance(rrl, e, age) >= 0)
+               if (!e->logged && response_balance(rrl, e, age) > 0)
                        break;
        }
        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,
                char *log_buf, unsigned int log_buf_len)
 {
-       int rate, new_rate, *ratep, slip, new_slip, age, log_secs, min;
-       const char *rate_str = NULL;
+       int rate, new_rate, slip, new_slip, age, log_secs, min;
+       dns_rrl_rate_t *ratep;
        dns_rrl_entry_t const *credit_e;
 
        /*
         * Pick the rate counter.
         * Optionally adjust the rate by the estimated query/second rate.
         */
-       switch (e->key.s.rtype) {
-       case DNS_RRL_RTYPE_QUERY:
-       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);
-       }
+       ratep = get_rate(rrl, e->key.s.rtype);
+       rate = ratep->r;
        if (rate == 0)
                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);
                if (new_rate < 1)
                        new_rate = 1;
-               if (*ratep != new_rate) {
-                       if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1)) {
-                               switch (e->key.s.rtype) {
-                               case DNS_RRL_RTYPE_QUERY:
-                               case DNS_RRL_RTYPE_DELEGATION:
-                                       rate_str = "responses-per-second";
-                                       break;
-                               case DNS_RRL_RTYPE_NXDOMAIN:
-                                       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);
-                       }
+               if (ratep->scaled != new_rate) {
+                       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, ratep->str, scale,
+                                     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.
         */
-       slip = rrl->slip;
+       slip = rrl->slip.r;
        if (slip > 2 && scale < 1.0) {
                new_slip = (int) (slip * scale);
                if (new_slip < 2)
                        new_slip = 2;
-               if (rrl->scaled_slip != new_slip) {
-                       if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1))
-                               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
-                                             DNS_LOGMODULE_REQUEST,
-                                             DNS_RRL_LOG_DEBUG1,
-                                             "%d qps scaled slip"
-                                             " by %.2f from %d to %d",
-                                             (int)qps, scale,
-                                             slip, new_slip);
+               if (rrl->slip.scaled != new_slip) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
+                                     DNS_LOGMODULE_REQUEST,
+                                     DNS_RRL_LOG_DEBUG1,
+                                     "%d qps scaled slip"
+                                     " by %.2f from %d to %d",
+                                     (int)qps, scale,
+                                     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) {
@@ -853,34 +827,36 @@ make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
 
        switch (e->key.s.rtype) {
        case DNS_RRL_RTYPE_QUERY:
-               ADD_LOG_CSTR(&lb, "response");
                break;
-       case DNS_RRL_RTYPE_DELEGATION:
-               ADD_LOG_CSTR(&lb, "referral");
+       case DNS_RRL_RTYPE_REFERRAL:
+               ADD_LOG_CSTR(&lb, "referral ");
+               break;
+       case DNS_RRL_RTYPE_NODATA:
+               ADD_LOG_CSTR(&lb, "NODATA ");
                break;
        case DNS_RRL_RTYPE_NXDOMAIN:
-               ADD_LOG_CSTR(&lb, "NXDOMAIN response");
+               ADD_LOG_CSTR(&lb, "NXDOMAIN ");
                break;
        case DNS_RRL_RTYPE_ERROR:
                if (resp_result == ISC_R_SUCCESS) {
-                       ADD_LOG_CSTR(&lb, "error response");
+                       ADD_LOG_CSTR(&lb, "error ");
                } else {
                        rstr = isc_result_totext(resp_result);
                        add_log_str(&lb, rstr, strlen(rstr));
-                       ADD_LOG_CSTR(&lb, " response");
+                       ADD_LOG_CSTR(&lb, " error ");
                }
                break;
        case DNS_RRL_RTYPE_ALL:
-               ADD_LOG_CSTR(&lb, "all response");
+               ADD_LOG_CSTR(&lb, "all ");
                break;
        default:
                INSIST(0);
        }
 
        if (plural)
-               ADD_LOG_CSTR(&lb, "s to ");
+               ADD_LOG_CSTR(&lb, "responses to ");
        else
-               ADD_LOG_CSTR(&lb, " to ");
+               ADD_LOG_CSTR(&lb, "response to ");
 
        memset(&cidr, 0, sizeof(cidr));
        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));
 
        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) {
                qbuf = get_qname(rrl, e);
                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) {
                        ADD_LOG_CSTR(&lb, " ");
                        (void)dns_rdataclass_totext(e->key.s.qclass, &lb);
-                       ADD_LOG_CSTR(&lb, " ");
-                       (void)dns_rdatatype_totext(e->key.s.qtype, &lb);
+                       if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY) {
+                               ADD_LOG_CSTR(&lb, " ");
+                               (void)dns_rdatatype_totext(e->key.s.qtype, &lb);
+                       }
                }
                snprintf(strbuf, sizeof(strbuf), "  (%08x)",
                         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.
         * 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;
-       else if (resp_result == DNS_R_DELEGATION)
-               rtype = DNS_RRL_RTYPE_DELEGATION;
-       else if (resp_result == DNS_R_NXDOMAIN)
+               break;
+       case DNS_R_DELEGATION:
+               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;
-       else
+               break;
+       default:
                rtype = DNS_RRL_RTYPE_ERROR;
+               break;
+       }
        e = get_entry(rrl, client_addr, qclass, qtype, qname, rtype,
                      now, ISC_TRUE, log_buf, log_buf_len);
        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,
                                     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
                 * an all-per-second limit for the IP address.
index 1df333b384559f1a44a73dc5b6c5d86454480860..c65727f341d8fff6f156ce2c52bc93e2c56c47e3 100644 (file)
@@ -1324,16 +1324,17 @@ static cfg_type_t cfg_type_rpz = {
  */
 static cfg_clausedef_t rrl_clauses[] = {
        { "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 },
-       { "responses-per-second", &cfg_type_uint32, 0 },
+       { "errors-per-second", &cfg_type_uint32, 0 },
        { "all-per-second", &cfg_type_uint32, 0 },
        { "slip", &cfg_type_uint32, 0 },
        { "window", &cfg_type_uint32, 0 },
        { "log-only", &cfg_type_boolean, 0 },
        { "qps-scale", &cfg_type_uint32, 0 },
-       { "IPv4-prefix-length", &cfg_type_uint32, 0 },
-       { "IPv6-prefix-length", &cfg_type_uint32, 0 },
+       { "ipv4-prefix-length", &cfg_type_uint32, 0 },
+       { "ipv6-prefix-length", &cfg_type_uint32, 0 },
        { "exempt-clients", &cfg_type_bracketed_aml, 0 },
        { "max-table-size", &cfg_type_uint32, 0 },
        { "min-table-size", &cfg_type_uint32, 0 },