]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
RTT banding. [rt18441]
authorEvan Hunt <each@isc.org>
Fri, 22 Aug 2008 04:16:17 +0000 (04:16 +0000)
committerEvan Hunt <each@isc.org>
Fri, 22 Aug 2008 04:16:17 +0000 (04:16 +0000)
CHANGES
lib/dns/resolver.c

diff --git a/CHANGES b/CHANGES
index bc58180f8fe980dd04ccb3748f5536deefe44ccb..dbdc79457e11142153a9f2057f0b9a4f55a66492 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,11 +1,19 @@
-2421.  [bug]           Handle the special return value of a empty node as
+2423.   [security]      Randomize server selection on queries, so as to
+                        make forgery a little more difficult.  Instead of
+                        always preferring the server with the lowest RTT,
+                        pick a server with RTT within the same 128
+                        millisecond band.  [RT #18441]
+
+2422.  [bug]           Handle the special return value of a empty node as
                        if it was a NXRRSET in the validator. [RT #18447]
 
-2420.  [func]          Add new command line option '-S' for named to specify
+2421.  [func]          Add new command line option '-S' for named to specify
                        the max number of sockets. [RT #18493]
                        Use caution: this option may not work for some
                        operating systems without rebuilding named.
 
+2420.   [placeholder]
+
 2419.  [cleanup]       Document that isc_socket_create() and isc_socket_open()
                        should not be used for isc_sockettype_fdwatch sockets.
                        [RT #18521]
index c99f845f94a7ed1f686287a602ba314dd5035a5d..c305b000c328b8af24c624dedba2b7a7b3da109d 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: resolver.c,v 1.376 2008/08/06 06:11:15 marka Exp $ */
+/* $Id: resolver.c,v 1.377 2008/08/22 04:16:17 each Exp $ */
 
 /*! \file */
 
@@ -239,6 +239,12 @@ struct fetchctx {
         * response to a query.
         */
        const char *                    reason;
+
+       /*%
+        * Random numbers to use for mixing up server addresses.
+        */
+       isc_uint32_t                    rand_buf;
+       isc_uint32_t                    rand_bits;
 };
 
 #define FCTX_MAGIC                     ISC_MAGIC('F', '!', '!', '!')
@@ -362,10 +368,13 @@ struct dns_resolver {
  */
 #define FCTX_ADDRINFO_MARK              0x0001
 #define FCTX_ADDRINFO_FORWARDER         0x1000
+#define FCTX_ADDRINFO_TRIED             0x2000
 #define UNMARKED(a)                     (((a)->flags & FCTX_ADDRINFO_MARK) \
                                         == 0)
 #define ISFORWARDER(a)                  (((a)->flags & \
                                         FCTX_ADDRINFO_FORWARDER) != 0)
+#define TRIED(a)                        (((a)->flags & \
+                                        FCTX_ADDRINFO_TRIED) != 0)
 
 #define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
 
@@ -626,6 +635,12 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
                dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor);
        }
 
+       /* Remember that the server has been tried. */
+       if (!TRIED(query->addrinfo)) {
+               dns_adb_changeflags(fctx->adb, query->addrinfo,
+                                   FCTX_ADDRINFO_TRIED, FCTX_ADDRINFO_TRIED);
+       }
+
        /*
         * Age RTTs of servers not tried.
         */
@@ -2061,15 +2076,79 @@ add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason) {
                      namebuf, typebuf, classbuf, addrbuf);
 }
 
+/*
+ * Return 'bits' bits of random entropy from fctx->rand_buf,
+ * refreshing it by calling isc_random_get() whenever the requested
+ * number of bits is greater than the number in the buffer.
+ */
+static inline isc_uint32_t
+random_bits(fetchctx_t *fctx, isc_uint32_t bits) {
+       isc_uint32_t ret = 0;
+
+       REQUIRE(VALID_FCTX(fctx));
+       REQUIRE(bits <= 32);
+       if (bits == 0)
+               return (0);
+
+       if (bits >= fctx->rand_bits) {
+               /* if rand_bits == 0, this is unnecessary but harmless */
+               bits -= fctx->rand_bits;
+               ret = fctx->rand_buf << bits;
+
+               /* refresh random buffer now */
+               isc_random_get(&fctx->rand_buf);
+               fctx->rand_bits = sizeof(fctx->rand_buf) * CHAR_BIT;
+       }
+
+       if (bits > 0) {
+               isc_uint32_t mask = 0xffffffff;
+               if (bits < 32) {
+                       mask = (1 << bits) - 1;
+               }
+
+               ret |= fctx->rand_buf & mask;
+               fctx->rand_buf >>= bits;
+               fctx->rand_bits -= bits;
+       }
+
+       return (ret);
+}
+
+/*
+ * Add some random jitter to a server's RTT value so that the
+ * order of queries will be unpredictable.
+ * 
+ * RTT values of servers which have been tried are fuzzed by 128 ms.
+ * Servers that haven't been tried yet have their RTT set to a random 
+ * value between 0 ms and 7 ms; they should get to go first, but in
+ * unpredictable order.
+ */
+static inline void
+randomize_srtt(fetchctx_t *fctx, dns_adbaddrinfo_t *ai) {
+       if (TRIED(ai)) {
+               ai->srtt >>= 10; /* convert to milliseconds, near enough */
+               ai->srtt |= (ai->srtt & 0x80) | random_bits(fctx, 7);
+               ai->srtt <<= 10; /* now back to microseconds */
+       } else
+               ai->srtt = random_bits(fctx, 3) << 10;
+}
+
+/*
+ * Sort addrinfo list by RTT (with random jitter)
+ */
 static void
-sort_adbfind(dns_adbfind_t *find) {
+sort_adbfind(fetchctx_t *fctx, dns_adbfind_t *find) {
        dns_adbaddrinfo_t *best, *curr;
        dns_adbaddrinfolist_t sorted;
 
-       /*
-        * Lame N^2 bubble sort.
-        */
+       /* Add jitter to SRTT values */
+       curr = ISC_LIST_HEAD(find->list);
+       while (curr != NULL) {
+               randomize_srtt(fctx, curr);
+               curr = ISC_LIST_NEXT(curr, publink);
+       }
 
+       /* Lame N^2 bubble sort. */
        ISC_LIST_INIT(sorted);
        while (!ISC_LIST_EMPTY(find->list)) {
                best = ISC_LIST_HEAD(find->list);
@@ -2085,39 +2164,25 @@ sort_adbfind(dns_adbfind_t *find) {
        find->list = sorted;
 }
 
+/*
+ * Sort a list of finds by server RTT (with random jitter)
+ */
 static void
-sort_finds(fetchctx_t *fctx) {
+sort_finds(fetchctx_t *fctx, dns_adbfindlist_t *findlist) {
        dns_adbfind_t *best, *curr;
        dns_adbfindlist_t sorted;
        dns_adbaddrinfo_t *addrinfo, *bestaddrinfo;
 
-       /*
-        * Lame N^2 bubble sort.
-        */
-
-       ISC_LIST_INIT(sorted);
-       while (!ISC_LIST_EMPTY(fctx->finds)) {
-               best = ISC_LIST_HEAD(fctx->finds);
-               bestaddrinfo = ISC_LIST_HEAD(best->list);
-               INSIST(bestaddrinfo != NULL);
-               curr = ISC_LIST_NEXT(best, publink);
-               while (curr != NULL) {
-                       addrinfo = ISC_LIST_HEAD(curr->list);
-                       INSIST(addrinfo != NULL);
-                       if (addrinfo->srtt < bestaddrinfo->srtt) {
-                               best = curr;
-                               bestaddrinfo = addrinfo;
-                       }
-                       curr = ISC_LIST_NEXT(curr, publink);
-               }
-               ISC_LIST_UNLINK(fctx->finds, best, publink);
-               ISC_LIST_APPEND(sorted, best, publink);
-       }
-       fctx->finds = sorted;
+       /* Sort each find's addrinfo list by SRTT (after adding jitter) */
+       for (curr = ISC_LIST_HEAD(*findlist);
+            curr != NULL;
+            curr = ISC_LIST_NEXT(curr, publink))
+               sort_adbfind(fctx, curr);
 
+       /* Lame N^2 bubble sort. */
        ISC_LIST_INIT(sorted);
-       while (!ISC_LIST_EMPTY(fctx->altfinds)) {
-               best = ISC_LIST_HEAD(fctx->altfinds);
+       while (!ISC_LIST_EMPTY(*findlist)) {
+               best = ISC_LIST_HEAD(*findlist);
                bestaddrinfo = ISC_LIST_HEAD(best->list);
                INSIST(bestaddrinfo != NULL);
                curr = ISC_LIST_NEXT(best, publink);
@@ -2130,10 +2195,10 @@ sort_finds(fetchctx_t *fctx) {
                        }
                        curr = ISC_LIST_NEXT(curr, publink);
                }
-               ISC_LIST_UNLINK(fctx->altfinds, best, publink);
+               ISC_LIST_UNLINK(*findlist, best, publink);
                ISC_LIST_APPEND(sorted, best, publink);
        }
-       fctx->altfinds = sorted;
+       *findlist = sorted;
 }
 
 static void
@@ -2184,7 +2249,6 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
                 * name.
                 */
                INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
-               sort_adbfind(find);
                if (flags != 0 || port != 0) {
                        for (ai = ISC_LIST_HEAD(find->list);
                             ai != NULL;
@@ -2453,7 +2517,8 @@ fctx_getaddresses(fetchctx_t *fctx) {
                 * We've found some addresses.  We might still be looking
                 * for more addresses.
                 */
-               sort_finds(fctx);
+               sort_finds(fctx, &fctx->finds);
+               sort_finds(fctx, &fctx->altfinds);
                result = ISC_R_SUCCESS;
        }
 
@@ -3152,6 +3217,8 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
        fctx->spilled = ISC_FALSE;
        fctx->nqueries = 0;
        fctx->reason = NULL;
+       fctx->rand_buf = 0;
+       fctx->rand_bits = 0;
 
        dns_name_init(&fctx->nsname, NULL);
        fctx->nsfetch = NULL;