From: Wouter Wijngaards Date: Thu, 14 Jul 2016 07:06:34 +0000 (+0000) Subject: - TCP Fast open patch from Sara Dickinson. X-Git-Tag: release-1.5.10~51 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=95e9dff362dc4468377f4942df57803c61052a0b;p=thirdparty%2Funbound.git - TCP Fast open patch from Sara Dickinson. git-svn-id: file:///svn/unbound/trunk@3814 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/configure.ac b/configure.ac index ada4d259c..9e269246c 100644 --- a/configure.ac +++ b/configure.ac @@ -881,6 +881,42 @@ case "$enable_event_api" in ;; esac +AC_ARG_ENABLE(tfo-client, AC_HELP_STRING([--enable-tfo-client], [Enable TCP Fast Open for client mode])) +case "$enable_tfo_client" in + yes) + case `uname` in + Linux) AC_CHECK_DECL([MSG_FASTOPEN], [AC_MSG_WARN([Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO])], + [AC_MSG_ERROR([TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client])], + [AC_INCLUDES_DEFAULT +#include +]) + AC_DEFINE_UNQUOTED([USE_MSG_FASTOPEN], [1], [Define this to enable client TCP Fast Open.]) + ;; + Darwin) AC_CHECK_DECL([CONNECT_RESUME_ON_READ_WRITE], [AC_MSG_WARN([Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO])], + [AC_MSG_ERROR([TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client])], + [AC_INCLUDES_DEFAULT +#include +]) + AC_DEFINE_UNQUOTED([USE_OSX_MSG_FASTOPEN], [1], [Define this to enable client TCP Fast Open.]) + ;; + esac + ;; + no|*) + ;; +esac + +AC_ARG_ENABLE(tfo-server, AC_HELP_STRING([--enable-tfo-server], [Enable TCP Fast Open for server mode])) +case "$enable_tfo_server" in + yes) + AC_CHECK_DECL([TCP_FASTOPEN], [AC_MSG_WARN([Check the platform specific TFO kernel parameters are correctly configured to support server mode TFO])], [AC_MSG_ERROR([TCP Fast Open is not available for server mode: please rerun without --enable-tfo-server])], [AC_INCLUDES_DEFAULT +#include + ]) + AC_DEFINE_UNQUOTED([USE_TCP_FASTOPEN], [1], [Define this to enable server TCP Fast Open.]) + ;; + no|*) + ;; +esac + # check for libevent AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=pathname], [use libevent (will check /usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr or you can specify an explicit path). Slower, but allows use of large outgoing port ranges.]), diff --git a/doc/Changelog b/doc/Changelog index aaf454073..472d4f9b7 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +14 July 2016: Wouter + - TCP Fast open patch from Sara Dickinson. + 7 July 2016: Wouter - access-control-tag-data implemented. verbose(4) prints tag debug. diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 63c0a5b34..628704975 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -43,6 +43,9 @@ # include #endif #include +#ifdef USE_TCP_FASTOPEN +#include +#endif #include "services/listen_dnsport.h" #include "services/outside_network.h" #include "util/netevent.h" @@ -669,6 +672,22 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, #endif return -1; } +#ifdef USE_TCP_FASTOPEN + /* qlen specifies how many outstanding TFO requests to allow. Limit is a defense + against IP spoofing attacks as suggested in RFC7413 */ +#ifdef __APPLE__ + /* OS X implementation only supports qlen of 1 via this call. Actual + value is configured by the net.inet.tcp.fastopen_backlog kernel parm. */ + int qlen = 1; +#else + /* 5 is recommended on linux */ + int qlen = 5; +#endif + if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen, + sizeof(qlen))) == -1 ) { + log_err("Setting TCP Fast Open as server failed: %s", strerror(errno)); + } +#endif return s; } diff --git a/services/outside_network.c b/services/outside_network.c index 8d6e87311..ac7f08697 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -243,7 +243,32 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) return 0; fd_set_nonblock(s); +#ifdef USE_OSX_MSG_FASTOPEN + /* API for fast open is different here. We use a connectx() function and + then writes can happen as normal even using SSL.*/ + /* connectx requires that the len be set in the sockaddr struct*/ + struct sockaddr_in *addr_in = (struct sockaddr_in *)&w->addr; + addr_in->sin_len = w->addrlen; + sa_endpoints_t endpoints; + endpoints.sae_srcif = 0; + endpoints.sae_srcaddr = NULL; + endpoints.sae_srcaddrlen = 0; + endpoints.sae_dstaddr = (struct sockaddr *)&w->addr; + endpoints.sae_dstaddrlen = w->addrlen; + if (connectx(s, &endpoints, SAE_ASSOCID_ANY, + CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE, + NULL, 0, NULL, NULL) == -1) { +#else /* USE_OSX_MSG_FASTOPEN*/ +#ifdef USE_MSG_FASTOPEN + /* Only do TFO for TCP in which case no connect() is required here. + Don't combine client TFO with SSL, since OpenSSL can't + currently support doing a handshake on fd that already isn't connected*/ + if (w->outnet->sslctx && w->ssl_upstream) { + if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) { +#else /* USE_MSG_FASTOPEN*/ if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) { +#endif /* USE_MSG_FASTOPEN*/ +#endif /* USE_OSX_MSG_FASTOPEN*/ #ifndef USE_WINSOCK #ifdef EINPROGRESS if(errno != EINPROGRESS) { @@ -263,6 +288,9 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) return 0; } } +#ifdef USE_MSG_FASTOPEN + } +#endif /* USE_MSG_FASTOPEN */ if(w->outnet->sslctx && w->ssl_upstream) { pend->c->ssl = outgoing_ssl_fd(w->outnet->sslctx, s); if(!pend->c->ssl) { diff --git a/util/netevent.c b/util/netevent.c index a9340f3e2..bbf5079c4 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -1356,6 +1356,63 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c) if(c->ssl) return ssl_handle_it(c); +#ifdef USE_MSG_FASTOPEN + /* Only try this on first use of a connection that uses tfo, + otherwise fall through to normal write */ + /* Also, TFO not available on WINDOWS at the moment */ + if(c->tcp_do_fastopen == 1) { + c->tcp_do_fastopen = 0; + /* We need to have all the bytes to send in one buffer to try a single + sendto() for the message to go in the syn packet, so we must + create that buffer here, even though it means a malloc. + NOTE: When there is a general solution for composing a TCP message + (inc length) in one buffer this code should use that mechanism.*/ + struct sldns_buffer* sendto_buf = NULL; + uint16_t len = sldns_buffer_limit(c->buffer); + sendto_buf = sldns_buffer_new(len + sizeof(uint16_t)); + if (!sendto_buf) + return 0; + sldns_buffer_write_u16_at(sendto_buf, 0, len); + sldns_buffer_write_at(sendto_buf, sizeof(uint16_t), + sldns_buffer_begin(c->buffer), + sldns_buffer_limit(c->buffer)); + r = sendto(fd, sldns_buffer_begin(sendto_buf), + sldns_buffer_limit(sendto_buf), + MSG_FASTOPEN, (struct sockaddr*)&(c->repinfo.addr), + c->repinfo.addrlen); + sldns_buffer_free(sendto_buf); + /* this form of sendto() does both a connect() and send() so need to + look for various flavours of error*/ + if (r == -1) { +#if defined(EINPROGRESS) && defined(EWOULDBLOCK) + /* Handshake is underway, maybe because no TFO cookie available. + Come back to write the messsage*/ + if(errno == EINPROGRESS || errno == EWOULDBLOCK) + return 1; +#endif + if(errno == EINTR || errno == EAGAIN) + return 1; + /* Not handling EISCONN here as shouldn't ever hit that case.*/ + if(errno != 0 && verbosity < 2) + return 0; /* silence lots of chatter in the logs */ + else if(errno != 0) + log_err_addr("tcp sendto", strerror(errno), + &c->repinfo.addr, c->repinfo.addrlen); + return 0; + } else { + c->tcp_byte_count += r; + if(c->tcp_byte_count < sizeof(uint16_t)) + return 1; + sldns_buffer_set_position(c->buffer, c->tcp_byte_count - + sizeof(uint16_t)); + if(sldns_buffer_remaining(c->buffer) == 0) { + tcp_callback_writer(c); + return 1; + } + } + } +#endif /* USE_MSG_FASTOPEN */ + if(c->tcp_byte_count < sizeof(uint16_t)) { uint16_t len = htons(sldns_buffer_limit(c->buffer)); #ifdef HAVE_WRITEV @@ -1548,6 +1605,9 @@ comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer, c->do_not_close = 0; c->tcp_do_toggle_rw = 0; c->tcp_check_nb_connect = 0; +#ifdef USE_MSG_FASTOPEN + c->tcp_do_fastopen = 0; +#endif c->inuse = 0; c->callback = callback; c->cb_arg = callback_arg; @@ -1601,6 +1661,9 @@ comm_point_create_udp_ancil(struct comm_base *base, int fd, c->inuse = 0; c->tcp_do_toggle_rw = 0; c->tcp_check_nb_connect = 0; +#ifdef USE_MSG_FASTOPEN + c->tcp_do_fastopen = 0; +#endif c->callback = callback; c->cb_arg = callback_arg; evbits = UB_EV_READ | UB_EV_PERSIST; @@ -1663,6 +1726,9 @@ comm_point_create_tcp_handler(struct comm_base *base, c->do_not_close = 0; c->tcp_do_toggle_rw = 1; c->tcp_check_nb_connect = 0; +#ifdef USE_MSG_FASTOPEN + c->tcp_do_fastopen = 0; +#endif c->repinfo.c = c; c->callback = callback; c->cb_arg = callback_arg; @@ -1723,6 +1789,9 @@ comm_point_create_tcp(struct comm_base *base, int fd, int num, size_t bufsize, c->do_not_close = 0; c->tcp_do_toggle_rw = 0; c->tcp_check_nb_connect = 0; +#ifdef USE_MSG_FASTOPEN + c->tcp_do_fastopen = 0; +#endif c->callback = NULL; c->cb_arg = NULL; evbits = UB_EV_READ | UB_EV_PERSIST; @@ -1788,6 +1857,9 @@ comm_point_create_tcp_out(struct comm_base *base, size_t bufsize, c->do_not_close = 0; c->tcp_do_toggle_rw = 1; c->tcp_check_nb_connect = 1; +#ifdef USE_MSG_FASTOPEN + c->tcp_do_fastopen = 1; +#endif c->repinfo.c = c; c->callback = callback; c->cb_arg = callback_arg; @@ -1842,6 +1914,9 @@ comm_point_create_local(struct comm_base *base, int fd, size_t bufsize, c->do_not_close = 1; c->tcp_do_toggle_rw = 0; c->tcp_check_nb_connect = 0; +#ifdef USE_MSG_FASTOPEN + c->tcp_do_fastopen = 0; +#endif c->callback = callback; c->cb_arg = callback_arg; /* ub_event stuff */ @@ -1895,6 +1970,9 @@ comm_point_create_raw(struct comm_base* base, int fd, int writing, c->do_not_close = 1; c->tcp_do_toggle_rw = 0; c->tcp_check_nb_connect = 0; +#ifdef USE_MSG_FASTOPEN + c->tcp_do_fastopen = 0; +#endif c->callback = callback; c->cb_arg = callback_arg; /* ub_event stuff */ diff --git a/util/netevent.h b/util/netevent.h index 60121fe77..1d7ac0bc1 100644 --- a/util/netevent.h +++ b/util/netevent.h @@ -231,6 +231,11 @@ struct comm_point { /** if set, checks for pending error from nonblocking connect() call.*/ int tcp_check_nb_connect; +#ifdef USE_MSG_FASTOPEN + /** used to track if the sendto() call should be done when using TFO. */ + int tcp_do_fastopen; +#endif + /** number of queries outstanding on this socket, used by * outside network for udp ports */ int inuse;