]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- TCP Fast open patch from Sara Dickinson.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 14 Jul 2016 07:06:34 +0000 (07:06 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Thu, 14 Jul 2016 07:06:34 +0000 (07:06 +0000)
git-svn-id: file:///svn/unbound/trunk@3814 be551aaa-1e26-0410-a405-d3ace91eadb9

configure.ac
doc/Changelog
services/listen_dnsport.c
services/outside_network.c
util/netevent.c
util/netevent.h

index ada4d259ca2e4a567fe8141984e71cb2233c03cc..9e269246c27ba217262ac85e040d9d78da3dd54c 100644 (file)
@@ -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 <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
 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.]),
index aaf454073b9e5547700ae852f5b6a00bc05a066f..472d4f9b7ea1df37025feedce7c0b6cadc3f9a71 100644 (file)
@@ -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.
 
index 63c0a5b3453f811a8797d133cc731dc14f7685b9..6287049755e9c802814da2a828986ba84c8f29fb 100644 (file)
@@ -43,6 +43,9 @@
 #  include <sys/types.h>
 #endif
 #include <sys/time.h>
+#ifdef USE_TCP_FASTOPEN
+#include <netinet/tcp.h>
+#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;
 }
 
index 8d6e873113ceecd4a166ac556fac7264290e7064..ac7f08697287fdda98cc297c9071ae88fd988d6f 100644 (file)
@@ -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) {
index a9340f3e2162e62879d1c42e243ebb0394b34b6d..bbf5079c44b7d29e68c433841b7ad0fc2ee6dfd2 100644 (file)
@@ -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 */
index 60121fe77e9de0e0aa65b620ddda66904d862680..1d7ac0bc118bb89a1d86e24bb623926eba3bb4d1 100644 (file)
@@ -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;