From 7559d26c93e63ca2f5accdc9ee9715981f934ba7 Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Fri, 10 Jan 2025 12:11:59 +0100 Subject: [PATCH] - Use TCP_NODELAY on TLS sockets to speed up the TLS handshake. --- services/listen_dnsport.c | 33 +++++++++++---------------------- services/outside_network.c | 27 ++++++++++++++++++++++----- services/outside_network.h | 6 ++++-- testcode/fake_event.c | 3 ++- util/config_file.c | 20 ++++++++++++++++++++ util/config_file.h | 4 ++++ 6 files changed, 63 insertions(+), 30 deletions(-) diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index bdd14b04a..8a5cf2a18 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -703,7 +703,10 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, { int s = -1; char* err; -#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) || defined(SO_BINDANY) +#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) \ + || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) \ + || defined(IP_BINDANY) || defined(IP_FREEBIND) \ + || defined(SO_BINDANY) || defined(TCP_NODELAY) int on = 1; #endif #ifdef HAVE_SYSTEMD @@ -1237,26 +1240,6 @@ set_recvpktinfo(int s, int family) return 1; } -/** see if interface is ssl, its port number == the ssl port number */ -static int -if_is_ssl(const char* ifname, const char* port, int ssl_port, - struct config_strlist* tls_additional_port) -{ - struct config_strlist* s; - char* p = strchr(ifname, '@'); - if(!p && atoi(port) == ssl_port) - return 1; - if(p && atoi(p+1) == ssl_port) - return 1; - for(s = tls_additional_port; s; s = s->next) { - if(p && atoi(p+1) == atoi(s->str)) - return 1; - if(!p && atoi(port) == atoi(s->str)) - return 1; - } - return 0; -} - /** * Helper for ports_open. Creates one interface (or NULL for default). * @param ifname: The interface ip address. @@ -1300,10 +1283,16 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, int quic_port, int http_notls_downstream, int sock_queue_timeout) { int s, noip6=0; + int is_ssl = if_is_ssl(ifname, port, ssl_port, tls_additional_port); int is_https = if_is_https(ifname, port, https_port); int is_dnscrypt = if_is_dnscrypt(ifname, port, dnscrypt_port); int is_pp2 = if_is_pp2(ifname, port, proxy_protocol_port); - int nodelay = is_https && http2_nodelay; + /* Always set TCP_NODELAY on TLS connection as it speeds up the TLS + * handshake. DoH had already such option so we respect it. + * Otherwise the server waits before sending more handshake data for + * the client ACK (Nagle's algorithm), which is delayed because the + * client waits for more data before ACKing (delayed ACK). */ + int nodelay = is_https?http2_nodelay:is_ssl; struct unbound_socket* ub_sock; int is_doq = if_is_quic(ifname, port, quic_port); const char* add = NULL; diff --git a/services/outside_network.c b/services/outside_network.c index b9475a368..0d7ec8905 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -262,12 +262,14 @@ pick_outgoing_tcp(struct pending_tcp* pend, struct waiting_tcp* w, int s) /** get TCP file descriptor for address, returns -1 on failure, * tcp_mss is 0 or maxseg size to set for TCP packets. */ int -outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss, int dscp) +outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, + int tcp_mss, int dscp, int nodelay) { int s; int af; char* err; -#if defined(SO_REUSEADDR) || defined(IP_BIND_ADDRESS_NO_PORT) +#if defined(SO_REUSEADDR) || defined(IP_BIND_ADDRESS_NO_PORT) \ + || defined(TCP_NODELAY) int on = 1; #endif #ifdef INET6 @@ -320,6 +322,18 @@ outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss, " setsockopt(.. IP_BIND_ADDRESS_NO_PORT ..) failed"); } #endif /* IP_BIND_ADDRESS_NO_PORT */ + if(nodelay) { +#if defined(IPPROTO_TCP) && defined(TCP_NODELAY) + if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void*)&on, + (socklen_t)sizeof(on)) < 0) { + verbose(VERB_ALGO, "outgoing tcp:" + " setsockopt(.. TCP_NODELAY ..) failed"); + } +#else + verbose(VERB_ALGO, "outgoing tcp:" + " setsockopt(.. TCP_NODELAY ..) unsupported"); +#endif /* defined(IPPROTO_TCP) && defined(TCP_NODELAY) */ + } return s; } @@ -649,7 +663,8 @@ outnet_tcp_take_into_use(struct waiting_tcp* w) } /* open socket */ - s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss, w->outnet->ip_dscp); + s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss, + w->outnet->ip_dscp, w->ssl_upstream); if(s == -1) return 0; @@ -3718,7 +3733,8 @@ outnet_comm_point_for_tcp(struct outside_network* outnet, sldns_buffer* query, int timeout, int ssl, char* host) { struct comm_point* cp; - int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, outnet->ip_dscp); + int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, + outnet->ip_dscp, ssl); if(fd == -1) { return 0; } @@ -3793,7 +3809,8 @@ outnet_comm_point_for_http(struct outside_network* outnet, { /* cp calls cb with err=NETEVENT_DONE when transfer is done */ struct comm_point* cp; - int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, outnet->ip_dscp); + int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, + outnet->ip_dscp, ssl); if(fd == -1) { return 0; } diff --git a/services/outside_network.h b/services/outside_network.h index 467c81f60..0a77e3388 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -743,9 +743,11 @@ void reuse_write_wait_remove(struct reuse_tcp* reuse, struct waiting_tcp* w); void reuse_write_wait_push_back(struct reuse_tcp* reuse, struct waiting_tcp* w); /** get TCP file descriptor for address, returns -1 on failure, - * tcp_mss is 0 or maxseg size to set for TCP packets. */ + * tcp_mss is 0 or maxseg size to set for TCP packets, + * nodelay (TCP_NODELAY) should be set for TLS connections to speed up the TLS + * handshake.*/ int outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, - int tcp_mss, int dscp); + int tcp_mss, int dscp, int nodelay); /** * Create udp commpoint suitable for sending packets to the destination. diff --git a/testcode/fake_event.c b/testcode/fake_event.c index be61ed5c6..840a687d9 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1938,7 +1938,8 @@ int comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet, } int outnet_get_tcp_fd(struct sockaddr_storage* ATTR_UNUSED(addr), - socklen_t ATTR_UNUSED(addrlen), int ATTR_UNUSED(tcp_mss), int ATTR_UNUSED(dscp)) + socklen_t ATTR_UNUSED(addrlen), int ATTR_UNUSED(tcp_mss), + int ATTR_UNUSED(dscp), int ATTR_UNUSED(nodelay)) { log_assert(0); return -1; diff --git a/util/config_file.c b/util/config_file.c index 58567dccc..dbe1b7081 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -2796,6 +2796,26 @@ int cfg_has_https(struct config_file* cfg) return 0; } +/** see if interface is ssl, its port number == the ssl port number */ +int +if_is_ssl(const char* ifname, const char* port, int ssl_port, + struct config_strlist* tls_additional_port) +{ + struct config_strlist* s; + char* p = strchr(ifname, '@'); + if(!p && atoi(port) == ssl_port) + return 1; + if(p && atoi(p+1) == ssl_port) + return 1; + for(s = tls_additional_port; s; s = s->next) { + if(p && atoi(p+1) == atoi(s->str)) + return 1; + if(!p && atoi(port) == atoi(s->str)) + return 1; + } + return 0; +} + /** see if interface is PROXYv2, its port number == the proxy port number */ int if_is_pp2(const char* ifname, const char* port, diff --git a/util/config_file.h b/util/config_file.h index 2969f8433..07e539f06 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -1405,6 +1405,10 @@ int if_is_https(const char* ifname, const char* port, int https_port); */ int cfg_has_https(struct config_file* cfg); +/** see if interface is ssl, its port number == the ssl port number */ +int if_is_ssl(const char* ifname, const char* port, int ssl_port, + struct config_strlist* tls_additional_port); + /** see if interface is PROXYv2, its port number == the proxy port number */ int if_is_pp2(const char* ifname, const char* port, struct config_strlist* proxy_protocol_port);