- TCP Fast open patch from Sara Dickinson.

git-svn-id: file:///svn/unbound/trunk@3814 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2016-07-14 07:06:34 +00:00
parent 48ca4dc880
commit 95e9dff362
6 changed files with 169 additions and 0 deletions

View file

@ -881,6 +881,42 @@ case "$enable_event_api" in
;; ;;
esac 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 <netinet/tcp.h>
])
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 <sys/socket.h>
])
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 <netinet/tcp.h>
])
AC_DEFINE_UNQUOTED([USE_TCP_FASTOPEN], [1], [Define this to enable server TCP Fast Open.])
;;
no|*)
;;
esac
# check for libevent # check for libevent
AC_ARG_WITH(libevent, AC_HELP_STRING([--with-libevent=pathname], 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.]), [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.]),

View file

@ -1,3 +1,6 @@
14 July 2016: Wouter
- TCP Fast open patch from Sara Dickinson.
7 July 2016: Wouter 7 July 2016: Wouter
- access-control-tag-data implemented. verbose(4) prints tag debug. - access-control-tag-data implemented. verbose(4) prints tag debug.

View file

@ -43,6 +43,9 @@
# include <sys/types.h> # include <sys/types.h>
#endif #endif
#include <sys/time.h> #include <sys/time.h>
#ifdef USE_TCP_FASTOPEN
#include <netinet/tcp.h>
#endif
#include "services/listen_dnsport.h" #include "services/listen_dnsport.h"
#include "services/outside_network.h" #include "services/outside_network.h"
#include "util/netevent.h" #include "util/netevent.h"
@ -669,6 +672,22 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
#endif #endif
return -1; 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; return s;
} }

View file

@ -243,7 +243,32 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len)
return 0; return 0;
fd_set_nonblock(s); 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) { if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
#endif /* USE_MSG_FASTOPEN*/
#endif /* USE_OSX_MSG_FASTOPEN*/
#ifndef USE_WINSOCK #ifndef USE_WINSOCK
#ifdef EINPROGRESS #ifdef EINPROGRESS
if(errno != 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; return 0;
} }
} }
#ifdef USE_MSG_FASTOPEN
}
#endif /* USE_MSG_FASTOPEN */
if(w->outnet->sslctx && w->ssl_upstream) { if(w->outnet->sslctx && w->ssl_upstream) {
pend->c->ssl = outgoing_ssl_fd(w->outnet->sslctx, s); pend->c->ssl = outgoing_ssl_fd(w->outnet->sslctx, s);
if(!pend->c->ssl) { if(!pend->c->ssl) {

View file

@ -1356,6 +1356,63 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
if(c->ssl) if(c->ssl)
return ssl_handle_it(c); 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)) { if(c->tcp_byte_count < sizeof(uint16_t)) {
uint16_t len = htons(sldns_buffer_limit(c->buffer)); uint16_t len = htons(sldns_buffer_limit(c->buffer));
#ifdef HAVE_WRITEV #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->do_not_close = 0;
c->tcp_do_toggle_rw = 0; c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0; c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
c->inuse = 0; c->inuse = 0;
c->callback = callback; c->callback = callback;
c->cb_arg = callback_arg; c->cb_arg = callback_arg;
@ -1601,6 +1661,9 @@ comm_point_create_udp_ancil(struct comm_base *base, int fd,
c->inuse = 0; c->inuse = 0;
c->tcp_do_toggle_rw = 0; c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0; c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
c->callback = callback; c->callback = callback;
c->cb_arg = callback_arg; c->cb_arg = callback_arg;
evbits = UB_EV_READ | UB_EV_PERSIST; 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->do_not_close = 0;
c->tcp_do_toggle_rw = 1; c->tcp_do_toggle_rw = 1;
c->tcp_check_nb_connect = 0; c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
c->repinfo.c = c; c->repinfo.c = c;
c->callback = callback; c->callback = callback;
c->cb_arg = callback_arg; 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->do_not_close = 0;
c->tcp_do_toggle_rw = 0; c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0; c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
c->callback = NULL; c->callback = NULL;
c->cb_arg = NULL; c->cb_arg = NULL;
evbits = UB_EV_READ | UB_EV_PERSIST; 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->do_not_close = 0;
c->tcp_do_toggle_rw = 1; c->tcp_do_toggle_rw = 1;
c->tcp_check_nb_connect = 1; c->tcp_check_nb_connect = 1;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 1;
#endif
c->repinfo.c = c; c->repinfo.c = c;
c->callback = callback; c->callback = callback;
c->cb_arg = callback_arg; 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->do_not_close = 1;
c->tcp_do_toggle_rw = 0; c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0; c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
c->callback = callback; c->callback = callback;
c->cb_arg = callback_arg; c->cb_arg = callback_arg;
/* ub_event stuff */ /* 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->do_not_close = 1;
c->tcp_do_toggle_rw = 0; c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0; c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
c->callback = callback; c->callback = callback;
c->cb_arg = callback_arg; c->cb_arg = callback_arg;
/* ub_event stuff */ /* ub_event stuff */

View file

@ -231,6 +231,11 @@ struct comm_point {
/** if set, checks for pending error from nonblocking connect() call.*/ /** if set, checks for pending error from nonblocking connect() call.*/
int tcp_check_nb_connect; 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 /** number of queries outstanding on this socket, used by
* outside network for udp ports */ * outside network for udp ports */
int inuse; int inuse;