advertised = MAX_TCP_TIMEOUT;
}
- ns_server_settimeouts(named_g_server->sctx,
- initial, idle, keepalive, advertised);
+ isc_nm_tcp_settimeouts(named_g_nm, initial, idle,
+ keepalive, advertised);
/*
* Configure sets of UDP query source ports.
if (ptr == NULL)
return (ISC_R_UNEXPECTEDEND);
- ns_server_gettimeouts(named_g_server->sctx,
- &initial, &idle, &keepalive, &advertised);
+ isc_nm_tcp_gettimeouts(named_g_nm, &initial, &idle,
+ &keepalive, &advertised);
/* Look for optional arguments. */
ptr = next_token(lex, NULL);
result = isc_task_beginexclusive(named_g_server->task);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
- ns_server_settimeouts(named_g_server->sctx, initial, idle,
+ isc_nm_tcp_settimeouts(named_g_nm, initial, idle,
keepalive, advertised);
isc_task_endexclusive(named_g_server->task);
stop_signal($name, "TERM", 1);
}
-@ans = wait_for_servers(60, @ans);
+@ans = wait_for_servers(1200, @ans);
# Pass 3: SIGABRT
foreach my $name (@ns) {
isc_sockaddr_t
isc_nmhandle_peeraddr(isc_nmhandle_t *handle);
+/*%<
+ * Return the peer address for the given handle.
+ */
isc_sockaddr_t
isc_nmhandle_localaddr(isc_nmhandle_t *handle);
+/*%<
+ * Return the local address for the given handle.
+ */
+
+isc_nm_t *
+isc_nmhandle_netmgr(isc_nmhandle_t *handle);
+/*%<
+ * Return a pointer to the netmgr object for the given handle.
+ */
typedef void (*isc_nm_recv_cb_t)(isc_nmhandle_t *handle, isc_region_t *region,
void *cbarg);
isc_nm_listentcp(isc_nm_t *mgr, isc_nmiface_t *iface,
isc_nm_cb_t cb, void *cbarg,
size_t extrahandlesize, isc_quota_t *quota,
- isc_nmsocket_t **rv);
+ isc_nmsocket_t **sockp);
/*%<
* Start listening for raw messages over the TCP interface 'iface', using
* net manager 'mgr'.
* quota. This allows us to enforce TCP client quota limits.
*
* NOTE: This is currently only called inside isc_nm_listentcpdns(), which
- * creates a 'wrapper' socket that sends and receives DNS messages -
- * prepended with a two-byte length field - and handles buffering.
+ * creates a 'wrapper' socket that sends and receives DNS messages
+ * prepended with a two-byte length field, and handles buffering.
*/
void
* net manager 'mgr'.
*
* On success, 'sockp' will be updated to contain a new listening TCPDNS
- * socket. This is a wrapper around a TCP socket, and handles DNS length
- * processing.
+ * socket. This is a wrapper around a raw TCP socket, which sends and
+ * receives DNS messages via that socket. It handles message buffering
+ * and pipelining, and automatically prepends messages with a two-byte
+ * length field.
*
* When a complete DNS message is received on the socket, 'cb' will be
* called with 'cbarg' as its argument.
* When handles are allocated for the socket, 'extrasize' additional bytes
* will be allocated along with the handle for an associated object
* (typically ns_client).
+ *
+ * 'quota' is passed to isc_nm_listentcp() when opening the raw TCP socket.
*/
void
void
isc_nm_tcpdns_sequential(isc_nmhandle_t *handle);
/*%<
- * Disable pipelining on this connection. Each DNS packet
- * will be only processed after the previous completes.
+ * Disable pipelining on this connection. Each DNS packet will be only
+ * processed after the previous completes.
*
- * The socket must be unpaused after the query is processed.
- * This is done the response is sent, or if we're dropping the
- * query, it will be done when a handle is fully dereferenced
- * by calling the socket's closehandle_cb callback.
+ * The socket must be unpaused after the query is processed. This is done
+ * the response is sent, or if we're dropping the query, it will be done
+ * when a handle is fully dereferenced by calling the socket's
+ * closehandle_cb callback.
*
- * Note: This can only be run while a message is being processed;
- * if it is run before any messages are read, no messages will
- * be read.
+ * Note: This can only be run while a message is being processed; if it is
+ * run before any messages are read, no messages will be read.
+ *
+ * Also note: once this has been set, it cannot be reversed for a given
+ * connection.
+ */
+
+void
+isc_nm_tcpdns_keepalive(isc_nmhandle_t *handle);
+/*%<
+ * Enable keepalive on this connection.
*
- * Also note: once this has been set, it cannot be reversed for a
- * given connection.
+ * When keepalive is active, we switch to using the keepalive timeout
+ * to determine when to close a connection, rather than the idle timeout.
+ */
+
+void
+isc_nm_tcp_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle,
+ uint32_t keepalive, uint32_t advertised);
+/*%<
+ * Sets the initial, idle, and keepalive timeout values to use for
+ * TCP connections, and the timeout value to advertise in responses using
+ * the EDNS TCP Keepalive option (which should ordinarily be the same
+ * as 'keepalive'), in tenths of seconds.
+ *
+ * Requires:
+ * \li 'mgr' is a valid netmgr.
+ */
+
+void
+isc_nm_tcp_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
+ uint32_t *keepalive, uint32_t *advertised);
+/*%<
+ * Gets the initial, idle, keepalive, or advertised timeout values,
+ * in tenths of seconds.
+ *
+ * Any integer pointer parameter not set to NULL will be updated to
+ * contain the corresponding timeout value.
+ *
+ * Requires:
+ * \li 'mgr' is a valid netmgr.
*/
void
isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp);
/*%<
- * Simulate a broken firewall that blocks UDP messages larger
- * than a given size.
+ * Simulate a broken firewall that blocks UDP messages larger than a given
+ * size.
*/
* event or wait for the other one to finish if we want to pause.
*/
atomic_bool interlocked;
+
+ /*
+ * Timeout values for TCP connections, coresponding to
+ * tcp-intiial-timeout, tcp-idle-timeout, tcp-keepalive-timeout,
+ * and tcp-advertised-timeout. Note that these are stored in
+ * milliseconds so they can be used directly with the libuv timer,
+ * but they are configured in tenths of seconds.
+ */
+ uint32_t init;
+ uint32_t idle;
+ uint32_t keepalive;
+ uint32_t advertised;
};
typedef enum isc_nmsocket_type {
*/
atomic_bool readpaused;
+ /*%
+ * A TCP or TCPDNS socket has been set to use the keepalive
+ * timeout instead of the default idle timeout.
+ */
+ atomic_bool keepalive;
+
/*%
* 'spare' handles for that can be reused to avoid allocations,
* for UDP.
atomic_init(&mgr->paused, false);
atomic_init(&mgr->interlocked, false);
+ /*
+ * Default TCP timeout values.
+ * May be updated by isc_nm_listentcp().
+ */
+ mgr->init = 30000;
+ mgr->idle = 30000;
+ mgr->keepalive = 30000;
+ mgr->advertised = 30000;
+
mgr->workers = isc_mem_get(mctx, workers * sizeof(isc__networker_t));
for (size_t i = 0; i < workers; i++) {
int r;
atomic_store(&mgr->maxudp, maxudp);
}
+void
+isc_nm_tcp_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle,
+ uint32_t keepalive, uint32_t advertised)
+{
+ REQUIRE(VALID_NM(mgr));
+
+ mgr->init = init * 100;
+ mgr->idle = idle * 100;
+ mgr->keepalive = keepalive * 100;
+ mgr->advertised = advertised * 100;
+}
+
+void
+isc_nm_tcp_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
+ uint32_t *keepalive, uint32_t *advertised)
+{
+ REQUIRE(VALID_NM(mgr));
+
+ if (initial != NULL) {
+ *initial = mgr->init / 100;
+ }
+
+ if (idle != NULL) {
+ *idle = mgr->idle / 100;
+ }
+
+ if (keepalive != NULL) {
+ *keepalive = mgr->keepalive / 100;
+ }
+
+ if (advertised != NULL) {
+ *advertised = mgr->advertised / 100;
+ }
+}
+
/*
* nm_thread is a single worker thread, that runs uv_run event loop
* until asked to stop.
.inactivehandles = isc_astack_new(mgr->mctx, 60),
.inactivereqs = isc_astack_new(mgr->mctx, 60)
};
-
isc_nm_attach(mgr, &sock->mgr);
sock->uv_handle.handle.data = sock;
if (sock->closehandle_cb != NULL) {
if (sock->tid == isc_nm_tid()) {
sock->closehandle_cb(sock);
-
- /*
- * If we do this asynchronously then
- * the async event will clean it up.
- */
- if (sock->ah == 0 &&
- !atomic_load(&sock->active) &&
- !atomic_load(&sock->destroying))
- {
- nmsocket_maybe_destroy(sock);
- }
} else {
-
isc__netievent_closecb_t * event =
isc__nm_get_ievent(sock->mgr,
netievent_closecb);
isc_nmsocket_attach(sock, &event->sock);
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *) event);
+ /*
+ * If we do this asynchronously then the async event
+ * will clean the socket, so just exit.
+ */
+ return;
}
}
+
+ if (atomic_load(&sock->ah) == 0 &&
+ !atomic_load(&sock->active) &&
+ !atomic_load(&sock->destroying))
+ {
+ nmsocket_maybe_destroy(sock);
+ }
}
void *
return (handle->local);
}
+isc_nm_t *
+isc_nmhandle_netmgr(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ return (handle->sock->mgr);
+}
+
isc__nm_uvreq_t *
isc__nm_uvreq_get(isc_nm_t *mgr, isc_nmsocket_t *sock) {
isc__nm_uvreq_t *req = NULL;
isc_nm_listentcp(isc_nm_t *mgr, isc_nmiface_t *iface,
isc_nm_cb_t cb, void *cbarg,
size_t extrahandlesize, isc_quota_t *quota,
- isc_nmsocket_t **rv)
+ isc_nmsocket_t **sockp)
{
isc__netievent_tcplisten_t *ievent = NULL;
isc_nmsocket_t *nsock = NULL;
ievent->sock = nsock;
isc__nm_enqueue_ievent(&mgr->workers[nsock->tid],
(isc__netievent_t *) ievent);
- *rv = nsock;
+ *sockp = nsock;
return (ISC_R_SUCCESS);
}
INSIST(sock->rcb.recv != NULL);
sock->rcb.recv(sock->tcphandle, ®ion, sock->rcbarg);
+
+ sock->read_timeout = (atomic_load(&sock->keepalive)
+ ? sock->mgr->keepalive
+ : sock->mgr->idle);
+
if (sock->timer_initialized && sock->read_timeout != 0) {
/* The timer will be updated */
uv_timer_start(&sock->timer, readtimeout_cb,
handle = isc__nmhandle_get(csock, NULL, &local);
INSIST(ssock->rcb.accept != NULL);
- csock->read_timeout = 1000;
+ csock->read_timeout = ssock->mgr->init;
ssock->rcb.accept(handle, ISC_R_SUCCESS, ssock->rcbarg);
isc_nmsocket_detach(&csock);
static void
dnstcp_readtimeout(uv_timer_t *timer) {
isc_nmsocket_t *sock = (isc_nmsocket_t *) timer->data;
+
REQUIRE(VALID_NMSOCK(sock));
+
isc_nmsocket_detach(&sock->outer);
uv_close((uv_handle_t*) &sock->timer, timer_close_cb);
}
isc_nmsocket_attach(handle->sock, &dnssock->outer);
dnssock->peer = handle->sock->peer;
dnssock->iface = handle->sock->iface;
- dnssock->read_timeout = 5000;
+ dnssock->read_timeout = handle->sock->mgr->init;
dnssock->tid = isc_nm_tid();
dnssock->closehandle_cb = resume_processing;
memmove(dnssock->buf + dnssock->buf_len, base, len);
dnssock->buf_len += len;
+ dnssock->read_timeout = (atomic_load(&dnssock->keepalive)
+ ? dnssock->mgr->keepalive
+ : dnssock->mgr->idle);
+
do {
isc_result_t result;
isc_nmhandle_t *dnshandle = NULL;
atomic_store(&dnssock->outer->processing, true);
uv_timer_stop(&dnssock->timer);
- if (dnssock->sequential) {
+ if (atomic_load(&dnssock->sequential)) {
/*
* We're in sequential mode and we processed
* one packet, so we're done until the next read
isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface,
isc_nm_recv_cb_t cb, void *cbarg,
size_t extrahandlesize, isc_quota_t *quota,
- isc_nmsocket_t **rv)
+ isc_nmsocket_t **sockp)
{
/* A 'wrapper' socket object with outer set to true TCP socket */
isc_nmsocket_t *dnslistensock =
quota, &dnslistensock->outer);
atomic_store(&dnslistensock->listening, true);
- *rv = dnslistensock;
+ *sockp = dnslistensock;
+
return (result);
}
atomic_store(&handle->sock->sequential, true);
}
+void
+isc_nm_tcpdns_keepalive(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ if (handle->sock->type != isc_nm_tcpdnssocket ||
+ handle->sock->outer == NULL)
+ {
+ return;
+ }
+
+ atomic_store(&handle->sock->keepalive, true);
+ atomic_store(&handle->sock->outer->keepalive, true);
+}
+
typedef struct tcpsend {
isc_mem_t *mctx;
isc_nmhandle_t *handle;
* For sequential sockets: Process what's in the buffer, or
* if there aren't any messages buffered, resume reading.
*/
- if (sock->sequential) {
+ if (atomic_load(&sock->sequential)) {
isc_nmhandle_t *handle = NULL;
result = processbuffer(sock, &handle);
isc_nmhandle_getdata
isc_nmhandle_getextra
isc_nmhandle_is_stream
+isc_nmhandle_netmgr
isc_nmhandle_localaddr
isc_nmhandle_peeraddr
isc_nmhandle_ref
isc_nm_maxudp
isc_nm_send
isc_nm_start
+isc_nm_tcp_gettimeouts
+isc_nm_tcp_settimeouts
isc_nmsocket_detach
+isc_nm_tcpdns_keepalive
isc_nm_tcpdns_sequential
isc_nm_tcpdns_stoplistening
isc_nm_tid
}
if (TCP_CLIENT(client) && USEKEEPALIVE(client)) {
isc_buffer_t buf;
+ uint32_t adv;
INSIST(count < DNS_EDNSOPTIONS);
+ isc_nm_tcp_gettimeouts(isc_nmhandle_netmgr(client->handle),
+ NULL, NULL, NULL, &adv);
isc_buffer_init(&buf, advtimo, sizeof(advtimo));
- isc_buffer_putuint16(&buf,
- (uint16_t) client->sctx->advertisedtimo);
+ isc_buffer_putuint16(&buf, (uint16_t) adv);
ednsopts[count].code = DNS_OPT_TCP_KEEPALIVE;
ednsopts[count].length = 2;
ednsopts[count].value = advtimo;
#if CLIENT_NMCTXS > 0
LOCK(&manager->lock);
- if (isc_nm_tid()>=0) {
+ if (isc_nm_tid() >= 0) {
nextmctx = isc_nm_tid();
} else {
nextmctx = manager->nextmctx++;
uint32_t options;
unsigned int delay;
- unsigned int initialtimo;
- unsigned int idletimo;
- unsigned int keepalivetimo;
- unsigned int advertisedtimo;
-
dns_acl_t *blackholeacl;
dns_acl_t *keepresporder;
uint16_t udpsize;
*\li 'sctx' is valid.
*/
-void
-ns_server_settimeouts(ns_server_t *sctx, unsigned int initial,
- unsigned int idle, unsigned int keepalive,
- unsigned int advertised);
-void
-ns_server_gettimeouts(ns_server_t *sctx, unsigned int *initial,
- unsigned int *idle, unsigned int *keepalive,
- unsigned int *advertised);
-/*%<
- * Set/get tcp-timeout values.
- *
- * Requires:
- *\li 'sctx' is valid.
- */
-
void
ns_server_setoption(ns_server_t *sctx, unsigned int option,
bool value);
ns_interface_listentcp(ns_interface_t *ifp) {
isc_result_t result;
- /* Reserve space for an ns_client_t with the netmgr handle */
result = isc_nm_listentcpdns(ifp->mgr->nm,
(isc_nmiface_t *) &ifp->addr,
ns__client_request, ifp,
CHECKFATAL(isc_stats_create(mctx, &sctx->tcpoutstats6,
dns_sizecounter_out_max));
- sctx->initialtimo = 300;
- sctx->idletimo = 300;
- sctx->keepalivetimo = 300;
- sctx->advertisedtimo = 300;
-
sctx->udpsize = 4096;
sctx->transfer_tcp_message_size = 20480;
return (ISC_R_SUCCESS);
}
-void
-ns_server_settimeouts(ns_server_t *sctx, unsigned int initial,
- unsigned int idle, unsigned int keepalive,
- unsigned int advertised)
-{
- REQUIRE(SCTX_VALID(sctx));
-
- sctx->initialtimo = initial;
- sctx->idletimo = idle;
- sctx->keepalivetimo = keepalive;
- sctx->advertisedtimo = advertised;
-}
-
-void
-ns_server_gettimeouts(ns_server_t *sctx, unsigned int *initial,
- unsigned int *idle, unsigned int *keepalive,
- unsigned int *advertised)
-{
- REQUIRE(SCTX_VALID(sctx));
- REQUIRE(initial != NULL && idle != NULL &&
- keepalive != NULL && advertised != NULL);
-
- *initial = sctx->initialtimo;
- *idle = sctx->idletimo;
- *keepalive = sctx->keepalivetimo;
- *advertised = sctx->advertisedtimo;
-}
-
void
ns_server_setoption(ns_server_t *sctx, unsigned int option,
bool value)
ns_server_create
ns_server_detach
ns_server_getoption
-ns_server_gettimeouts
ns_server_setoption
ns_server_setserverid
-ns_server_settimeouts
ns_sortlist_addrorder1
ns_sortlist_addrorder2
ns_sortlist_byaddrsetup