From 74717eef53ba5d6aefc80eb262bbb090ff4bb3b5 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 4 Sep 2014 10:37:45 +1000 Subject: [PATCH] 3939. [func] Improve UPDATE forwarding performance by allowing TCP connections to be shared. [RT #37039] --- CHANGES | 3 + bin/tests/system/nsupdate/clean.sh | 2 + bin/tests/system/nsupdate/ns1/many.test.db.in | 25 +++++ bin/tests/system/nsupdate/ns1/named.conf | 6 + bin/tests/system/nsupdate/ns3/named.conf | 9 +- bin/tests/system/nsupdate/setup.sh | 3 + bin/tests/system/nsupdate/tests.sh | 26 +++++ lib/dns/dispatch.c | 104 +++++++++++++++++- lib/dns/include/dns/dispatch.h | 15 +++ lib/dns/request.c | 44 +++++--- lib/dns/win32/libdns.def.in | 2 + lib/dns/zone.c | 12 +- 12 files changed, 227 insertions(+), 24 deletions(-) create mode 100644 bin/tests/system/nsupdate/ns1/many.test.db.in diff --git a/CHANGES b/CHANGES index 7b30a34c81..43ed8c5199 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +3939. [func] Improve UPDATE forwarding performance by allowing TCP + connections to be shared. [RT #37039] + 3938. [placeholder] 3937. [func] Added some debug logging to better indicate the diff --git a/bin/tests/system/nsupdate/clean.sh b/bin/tests/system/nsupdate/clean.sh index 424cdf40c6..d4c1113fa2 100644 --- a/bin/tests/system/nsupdate/clean.sh +++ b/bin/tests/system/nsupdate/clean.sh @@ -36,3 +36,5 @@ rm -f dig.out.* rm -f jp.out.ns3.* rm -f Kxxx.* rm -f typelist.out.* +rm -f ns1/many.test.db ns3/many.test.db.jnl +rm -f ns3/many.test.bk ns3/many.test.bk.jnl diff --git a/bin/tests/system/nsupdate/ns1/many.test.db.in b/bin/tests/system/nsupdate/ns1/many.test.db.in new file mode 100644 index 0000000000..955e95dd48 --- /dev/null +++ b/bin/tests/system/nsupdate/ns1/many.test.db.in @@ -0,0 +1,25 @@ +; Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$ORIGIN . +$TTL 300 ; 5 minutes +many.test IN SOA ns1.example.nil. hostmaster.example.nil. ( + 1 ; serial + 2000 ; refresh (2000 seconds) + 2000 ; retry (2000 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) +many.test NS ns1.example.nil. +many.test NS ns2.example.nil. diff --git a/bin/tests/system/nsupdate/ns1/named.conf b/bin/tests/system/nsupdate/ns1/named.conf index f9e3b20d30..4263e381e4 100644 --- a/bin/tests/system/nsupdate/ns1/named.conf +++ b/bin/tests/system/nsupdate/ns1/named.conf @@ -127,3 +127,9 @@ zone "keytests.nil" { grant sha512-key name sha512.keytests.nil. ANY; }; }; + +zone "many.test" { + type master; + allow-update { any; }; + file "many.test.db"; +}; diff --git a/bin/tests/system/nsupdate/ns3/named.conf b/bin/tests/system/nsupdate/ns3/named.conf index 2abd522510..8606b53f7d 100644 --- a/bin/tests/system/nsupdate/ns3/named.conf +++ b/bin/tests/system/nsupdate/ns3/named.conf @@ -16,7 +16,7 @@ /* $Id: named.conf,v 1.5 2011/02/03 12:18:11 tbox Exp $ */ -// NS1 +// NS3 controls { /* empty */ }; @@ -60,3 +60,10 @@ zone "dnskey.test" { allow-update { any; }; file "dnskey.test.db.signed"; }; + +zone "many.test" { + type slave; + masters { 10.53.0.1; }; + allow-update-forwarding { any; }; + file "many.test.bk"; +}; diff --git a/bin/tests/system/nsupdate/setup.sh b/bin/tests/system/nsupdate/setup.sh index e3528dbf7e..eb048976eb 100644 --- a/bin/tests/system/nsupdate/setup.sh +++ b/bin/tests/system/nsupdate/setup.sh @@ -62,3 +62,6 @@ $DDNSCONFGEN -q -r $RANDFILE -a hmac-sha384 -k sha384-key -z keytests.nil > ns1/ $DDNSCONFGEN -q -r $RANDFILE -a hmac-sha512 -k sha512-key -z keytests.nil > ns1/sha512.key (cd ns3; $SHELL -e sign.sh) + +cp -f ns1/many.test.db.in ns1/many.test.db +rm -f ns1/many.test.db.jnl diff --git a/bin/tests/system/nsupdate/tests.sh b/bin/tests/system/nsupdate/tests.sh index 7dad50fef3..309aa6b514 100755 --- a/bin/tests/system/nsupdate/tests.sh +++ b/bin/tests/system/nsupdate/tests.sh @@ -597,5 +597,31 @@ serial=`$DIG +short yyyymmddvv.nil. soa @10.53.0.1 -p 5300 | awk '{print $3}'` | [ "$serial" -eq "$now" ] || ret=1 [ $ret = 0 ] || { echo I:failed; status=1; } +n=`expr $n + 1` +echo "I:send many simultaneous updates via a update forwarder ($n)" +ret=0 +for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +do +( + for j in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + do + ( + $NSUPDATE << EOF +server 10.53.0.3 5300 +zone many.test +update add $i-$j.many.test 0 IN A 1.2.3.4 +send +EOF + ) & + done + wait +) & +done +wait +dig axfr many.test @10.53.0.1 -p 5300 > dig.out.test$n +lines=`awk '$4 == "A" { l++ } END { print l }' dig.out.test$n` +test ${lines:-0} -eq 289 || ret=1 +[ $ret = 0 ] || { echo I:failed; status=1; } + echo "I:exit status: $status" exit $status diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c index 5a90ee5e7b..9bfd6b9837 100644 --- a/lib/dns/dispatch.c +++ b/lib/dns/dispatch.c @@ -218,6 +218,7 @@ struct dns_dispatch { isc_socket_t *socket; /*%< isc socket attached to */ isc_sockaddr_t local; /*%< local address */ in_port_t localport; /*%< local UDP port */ + isc_sockaddr_t peer; /*%< peer address (TCP) */ isc_dscp_t dscp; /*%< "listen-on" DSCP value */ unsigned int maxrequests; /*%< max requests */ isc_event_t *ctlevent; @@ -2126,7 +2127,6 @@ dns_dispatchmgr_destroy(dns_dispatchmgr_t **mgrp) { LOCK(&mgr->lock); mgr->state |= MGR_SHUTTINGDOWN; - killit = destroy_mgr_ok(mgr); UNLOCK(&mgr->lock); @@ -2400,6 +2400,7 @@ dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests, disp->refcount = 1; disp->recv_pending = 0; memset(&disp->local, 0, sizeof(disp->local)); + memset(&disp->peer, 0, sizeof(disp->peer)); disp->localport = 0; disp->shutting_down = 0; disp->shutdown_out = 0; @@ -2507,6 +2508,23 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, unsigned int buckets, unsigned int increment, unsigned int attributes, dns_dispatch_t **dispp) { + + attributes |= DNS_DISPATCHATTR_PRIVATE; /* XXXMLG */ + + return (dns_dispatch_createtcp2(mgr, sock, taskmgr, NULL, NULL, + buffersize, maxbuffers, maxrequests, + buckets, increment, attributes, + dispp)); +} + +isc_result_t +dns_dispatch_createtcp2(dns_dispatchmgr_t *mgr, isc_socket_t *sock, + isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, + isc_sockaddr_t *destaddr, unsigned int buffersize, + unsigned int maxbuffers, unsigned int maxrequests, + unsigned int buckets, unsigned int increment, + unsigned int attributes, dns_dispatch_t **dispp) +{ isc_result_t result; dns_dispatch_t *disp; @@ -2518,7 +2536,8 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, REQUIRE((attributes & DNS_DISPATCHATTR_TCP) != 0); REQUIRE((attributes & DNS_DISPATCHATTR_UDP) == 0); - attributes |= DNS_DISPATCHATTR_PRIVATE; /* XXXMLG */ + if (destaddr == NULL) + attributes |= DNS_DISPATCHATTR_PRIVATE; /* XXXMLG */ LOCK(&mgr->lock); @@ -2565,6 +2584,23 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, disp->attributes = attributes; + if (localaddr == NULL) { + if (destaddr != NULL) { + switch (isc_sockaddr_pf(destaddr)) { + case AF_INET: + isc_sockaddr_any(&disp->local); + break; + case AF_INET6: + isc_sockaddr_any6(&disp->local); + break; + } + } + } else + disp->local = *localaddr; + + if (destaddr != NULL) + disp->peer = *destaddr; + /* * Append it to the dispatcher list. */ @@ -2573,7 +2609,6 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, mgr_log(mgr, LVL(90), "created TCP dispatcher %p", disp); dispatch_log(disp, LVL(90), "created task %p", disp->task[0]); - *dispp = disp; return (ISC_R_SUCCESS); @@ -2593,6 +2628,69 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, return (result); } +isc_result_t +dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, isc_sockaddr_t *destaddr, + isc_sockaddr_t *localaddr, dns_dispatch_t **dispp) +{ + dns_dispatch_t *disp; + isc_result_t result; + isc_sockaddr_t peeraddr; + isc_sockaddr_t sockname; + isc_sockaddr_t any; + unsigned int attributes, mask; + isc_boolean_t match = ISC_FALSE; + + REQUIRE(VALID_DISPATCHMGR(mgr)); + REQUIRE(destaddr != NULL); + REQUIRE(dispp != NULL && *dispp == NULL); + + attributes = DNS_DISPATCHATTR_TCP; + mask = DNS_DISPATCHATTR_TCP | DNS_DISPATCHATTR_PRIVATE | + DNS_DISPATCHATTR_EXCLUSIVE; + + if (localaddr == NULL) { + switch (isc_sockaddr_pf(destaddr)) { + case AF_INET: + isc_sockaddr_any(&any); + break; + case AF_INET6: + isc_sockaddr_any6(&any); + break; + default: + return (ISC_R_NOTFOUND); + } + localaddr = &any; + } + + LOCK(&mgr->lock); + disp = ISC_LIST_HEAD(mgr->list); + while (disp != NULL && !match) { + LOCK(&disp->lock); + if ((disp->shutting_down == 0) && + ATTRMATCH(disp->attributes, attributes, mask) && + (localaddr == NULL || + isc_sockaddr_eqaddr(localaddr, &disp->local))) { + result = isc_socket_getsockname(disp->socket, + &sockname); + if (result == ISC_R_SUCCESS) + result = isc_socket_getpeername(disp->socket, + &peeraddr); + if (result == ISC_R_SUCCESS && + isc_sockaddr_equal(destaddr, &peeraddr) && + isc_sockaddr_eqaddr(localaddr, &sockname)) { + /* attach */ + disp->refcount++; + *dispp = disp; + match = ISC_TRUE; + } + } + UNLOCK(&disp->lock); + disp = ISC_LIST_NEXT(disp, link); + } + UNLOCK(&mgr->lock); + return (match ? ISC_R_SUCCESS : ISC_R_NOTFOUND); +} + isc_result_t dns_dispatch_getudp_dup(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, diff --git a/lib/dns/include/dns/dispatch.h b/lib/dns/include/dns/dispatch.h index ef5b9179dd..358a85984b 100644 --- a/lib/dns/include/dns/dispatch.h +++ b/lib/dns/include/dns/dispatch.h @@ -298,6 +298,13 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock, unsigned int maxbuffers, unsigned int maxrequests, unsigned int buckets, unsigned int increment, unsigned int attributes, dns_dispatch_t **dispp); +isc_result_t +dns_dispatch_createtcp2(dns_dispatchmgr_t *mgr, isc_socket_t *sock, + isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, + isc_sockaddr_t *destaddr, unsigned int buffersize, + unsigned int maxbuffers, unsigned int maxrequests, + unsigned int buckets, unsigned int increment, + unsigned int attributes, dns_dispatch_t **dispp); /*%< * Create a new dns_dispatch and attach it to the provided isc_socket_t. * @@ -369,6 +376,14 @@ dns_dispatch_starttcp(dns_dispatch_t *disp); *\li 'disp' is valid. */ +isc_result_t +dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, isc_sockaddr_t *destaddr, + isc_sockaddr_t *localaddr, dns_dispatch_t **dispp); +/* + * Attempt to connect to a existing TCP connection. + */ + + isc_result_t dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest, isc_task_t *task, isc_taskaction_t action, void *arg, diff --git a/lib/dns/request.c b/lib/dns/request.c index b21786875c..be8072c632 100644 --- a/lib/dns/request.c +++ b/lib/dns/request.c @@ -525,7 +525,7 @@ isblackholed(dns_dispatchmgr_t *dispatchmgr, isc_sockaddr_t *destaddr) { static isc_result_t create_tcp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr, isc_dscp_t dscp, - dns_dispatch_t **dispatchp) + isc_boolean_t *connected, dns_dispatch_t **dispatchp) { isc_result_t result; isc_socket_t *socket = NULL; @@ -533,6 +533,17 @@ create_tcp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr, unsigned int attrs; isc_sockaddr_t bind_any; + result = dns_dispatch_gettcp(requestmgr->dispatchmgr, destaddr, + srcaddr, dispatchp); + if (result == ISC_R_SUCCESS) { + *connected = ISC_TRUE; + char peer[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(destaddr, peer, sizeof(peer)); + req_log(ISC_LOG_DEBUG(1), "attached to existing TCP " + "connection to %s", peer); + return (result); + } + result = isc_socket_create(requestmgr->socketmgr, isc_sockaddr_pf(destaddr), isc_sockettype_tcp, &socket); @@ -554,7 +565,6 @@ create_tcp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr, attrs = 0; attrs |= DNS_DISPATCHATTR_TCP; - attrs |= DNS_DISPATCHATTR_PRIVATE; if (isc_sockaddr_pf(destaddr) == AF_INET) attrs |= DNS_DISPATCHATTR_IPV4; else @@ -562,10 +572,11 @@ create_tcp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr, attrs |= DNS_DISPATCHATTR_MAKEQUERY; isc_socket_dscp(socket, dscp); - result = dns_dispatch_createtcp(requestmgr->dispatchmgr, - socket, requestmgr->taskmgr, - 4096, 2, 1, 1, 3, attrs, - dispatchp); + result = dns_dispatch_createtcp2(requestmgr->dispatchmgr, + socket, requestmgr->taskmgr, + srcaddr, destaddr, + 4096, 32768, 32768, 16411, 16433, + attrs, dispatchp); cleanup: isc_socket_detach(&socket); return (result); @@ -627,12 +638,15 @@ find_udp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr, static isc_result_t get_dispatch(isc_boolean_t tcp, dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr, - isc_dscp_t dscp, dns_dispatch_t **dispatchp) + isc_dscp_t dscp, isc_boolean_t *connected, + dns_dispatch_t **dispatchp) { isc_result_t result; + if (tcp) result = create_tcp_dispatch(requestmgr, srcaddr, - destaddr, dscp, dispatchp); + destaddr, dscp, connected, + dispatchp); else result = find_udp_dispatch(requestmgr, srcaddr, destaddr, dispatchp); @@ -719,6 +733,7 @@ dns_request_createraw4(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf, dns_messageid_t id; isc_boolean_t tcp = ISC_FALSE; isc_region_t r; + isc_boolean_t connected = ISC_FALSE; REQUIRE(VALID_REQUESTMGR(requestmgr)); REQUIRE(msgbuf != NULL); @@ -781,7 +796,7 @@ dns_request_createraw4(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf, tcp = ISC_TRUE; result = get_dispatch(tcp, requestmgr, srcaddr, destaddr, dscp, - &request->dispatch); + &connected, &request->dispatch); if (result != ISC_R_SUCCESS) goto cleanup; @@ -828,14 +843,14 @@ dns_request_createraw4(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf, goto unlink; request->destaddr = *destaddr; - if (tcp) { + if (tcp && !connected) { result = isc_socket_connect(socket, destaddr, task, req_connected, request); if (result != ISC_R_SUCCESS) goto unlink; request->flags |= DNS_REQUEST_F_CONNECTING|DNS_REQUEST_F_TCP; } else { - result = req_send(request, task, destaddr); + result = req_send(request, task, connected ? NULL : destaddr); if (result != ISC_R_SUCCESS) goto unlink; } @@ -935,6 +950,7 @@ dns_request_createvia4(dns_requestmgr_t *requestmgr, dns_message_t *message, dns_messageid_t id; isc_boolean_t tcp; isc_boolean_t setkey = ISC_TRUE; + isc_boolean_t connected = ISC_FALSE; REQUIRE(VALID_REQUESTMGR(requestmgr)); REQUIRE(message != NULL); @@ -994,7 +1010,7 @@ dns_request_createvia4(dns_requestmgr_t *requestmgr, dns_message_t *message, use_tcp: tcp = ISC_TF((options & DNS_REQUESTOPT_TCP) != 0); result = get_dispatch(tcp, requestmgr, srcaddr, destaddr, dscp, - &request->dispatch); + &connected, &request->dispatch); if (result != ISC_R_SUCCESS) goto cleanup; @@ -1050,14 +1066,14 @@ dns_request_createvia4(dns_requestmgr_t *requestmgr, dns_message_t *message, goto unlink; request->destaddr = *destaddr; - if (tcp) { + if (tcp && !connected) { result = isc_socket_connect(socket, destaddr, task, req_connected, request); if (result != ISC_R_SUCCESS) goto unlink; request->flags |= DNS_REQUEST_F_CONNECTING|DNS_REQUEST_F_TCP; } else { - result = req_send(request, task, destaddr); + result = req_send(request, task, connected ? NULL : destaddr); if (result != ISC_R_SUCCESS) goto unlink; } diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index 422f21c3ee..7cb9b7665d 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -209,10 +209,12 @@ dns_dispatch_attach dns_dispatch_cancel dns_dispatch_changeattributes dns_dispatch_createtcp +dns_dispatch_createtcp2 dns_dispatch_detach dns_dispatch_getdscp dns_dispatch_getlocaladdress dns_dispatch_getsocket +dns_dispatch_gettcp dns_dispatch_getudp dns_dispatch_getudp_dup dns_dispatch_importrecv diff --git a/lib/dns/zone.c b/lib/dns/zone.c index b1c0f544e8..709d5f8dd4 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -14927,12 +14927,12 @@ sendtomaster(dns_forward_t *forward) { goto unlock; } result = dns_request_createraw4(forward->zone->view->requestmgr, - forward->msgbuf, - &src, &forward->addr, dscp, - DNS_REQUESTOPT_TCP, 15 /* XXX */, - 0, 0, forward->zone->task, - forward_callback, forward, - &forward->request); + forward->msgbuf, + &src, &forward->addr, dscp, + DNS_REQUESTOPT_TCP, 15 /* XXX */, + 0, 0, forward->zone->task, + forward_callback, forward, + &forward->request); if (result == ISC_R_SUCCESS) { if (!ISC_LINK_LINKED(forward, link)) ISC_LIST_APPEND(forward->zone->forwards, forward, link);