From: Evan Hunt Date: Fri, 22 Aug 2008 04:16:17 +0000 (+0000) Subject: RTT banding. [rt18441] X-Git-Tag: v9.3.6b1~36^2~13 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=a45f57a2d5fa5a4cb50c4232c09dd7676a256599;p=thirdparty%2Fbind9.git RTT banding. [rt18441] --- diff --git a/CHANGES b/CHANGES index bc58180f8fe..dbdc79457e1 100644 --- 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] diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index c99f845f94a..c305b000c32 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -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;