diff options
Diffstat (limited to 'contrib/unbound/services/listen_dnsport.c')
-rw-r--r-- | contrib/unbound/services/listen_dnsport.c | 147 |
1 files changed, 112 insertions, 35 deletions
diff --git a/contrib/unbound/services/listen_dnsport.c b/contrib/unbound/services/listen_dnsport.c index 647cbe07ebd9..8b1d62e3a209 100644 --- a/contrib/unbound/services/listen_dnsport.c +++ b/contrib/unbound/services/listen_dnsport.c @@ -21,16 +21,16 @@ * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @@ -49,6 +49,7 @@ #include "util/log.h" #include "util/config_file.h" #include "util/net_help.h" +#include "ldns/sbuffer.h" #ifdef HAVE_NETDB_H #include <netdb.h> @@ -75,7 +76,7 @@ verbose_print_addr(struct addrinfo *addr) #endif /* INET6 */ if(inet_ntop(addr->ai_family, sinaddr, buf, (socklen_t)sizeof(buf)) == 0) { - strncpy(buf, "(null)", sizeof(buf)); + (void)strlcpy(buf, "(null)", sizeof(buf)); } buf[sizeof(buf)-1] = 0; verbose(VERB_ALGO, "creating %s%s socket %s %d", @@ -91,10 +92,10 @@ verbose_print_addr(struct addrinfo *addr) int create_udp_sock(int family, int socktype, struct sockaddr* addr, socklen_t addrlen, int v6only, int* inuse, int* noproto, - int rcv, int snd) + int rcv, int snd, int listen, int* reuseport) { int s; -#if defined(IPV6_USE_MIN_MTU) +#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_USE_MIN_MTU) int on=1; #endif #ifdef IPV6_MTU @@ -129,6 +130,50 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr, *noproto = 0; return -1; } + if(listen) { +#ifdef SO_REUSEADDR + if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, + (socklen_t)sizeof(on)) < 0) { +#ifndef USE_WINSOCK + log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s", + strerror(errno)); + if(errno != ENOSYS) { + close(s); + *noproto = 0; + *inuse = 0; + return -1; + } +#else + log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s", + wsa_strerror(WSAGetLastError())); + closesocket(s); + *noproto = 0; + *inuse = 0; + return -1; +#endif + } +#endif /* SO_REUSEADDR */ +#if defined(__linux__) && defined(SO_REUSEPORT) + /* Linux specific: try to set SO_REUSEPORT so that incoming + * queries are distributed evenly among the receiving threads. + * Each thread must have its own socket bound to the same port, + * with SO_REUSEPORT set on each socket. + */ + if (reuseport && *reuseport && + setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on, + (socklen_t)sizeof(on)) < 0) { +#ifdef ENOPROTOOPT + if(errno != ENOPROTOOPT || verbosity >= 3) + log_warn("setsockopt(.. SO_REUSEPORT ..) failed: %s", + strerror(errno)); +#endif + /* this option is not essential, we can continue */ + *reuseport = 0; + } +#else + (void)reuseport; +#endif /* defined(__linux__) && defined(SO_REUSEPORT) */ + } if(rcv) { #ifdef SO_RCVBUF int got; @@ -328,6 +373,8 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr, # else closesocket(s); # endif + *noproto = 0; + *inuse = 0; return -1; } # elif defined(IP_DONTFRAG) @@ -341,12 +388,15 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr, # else closesocket(s); # endif + *noproto = 0; + *inuse = 0; return -1; } # endif /* IPv4 MTU */ } if(bind(s, (struct sockaddr*)addr, addrlen) != 0) { *noproto = 0; + *inuse = 0; #ifndef USE_WINSOCK #ifdef EADDRINUSE *inuse = (errno == EADDRINUSE); @@ -386,10 +436,11 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr, } int -create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto) +create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, + int* reuseport) { int s; -#if defined(SO_REUSEADDR) || defined(IPV6_V6ONLY) +#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) int on = 1; #endif /* SO_REUSEADDR || IPV6_V6ONLY */ verbose_print_addr(addr); @@ -427,6 +478,26 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto) return -1; } #endif /* SO_REUSEADDR */ +#if defined(__linux__) && defined(SO_REUSEPORT) + /* Linux specific: try to set SO_REUSEPORT so that incoming + * connections are distributed evenly among the receiving threads. + * Each thread must have its own socket bound to the same port, + * with SO_REUSEPORT set on each socket. + */ + if (reuseport && *reuseport && + setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on, + (socklen_t)sizeof(on)) < 0) { +#ifdef ENOPROTOOPT + if(errno != ENOPROTOOPT || verbosity >= 3) + log_warn("setsockopt(.. SO_REUSEPORT ..) failed: %s", + strerror(errno)); +#endif + /* this option is not essential, we can continue */ + *reuseport = 0; + } +#else + (void)reuseport; +#endif /* defined(__linux__) && defined(SO_REUSEPORT) */ #if defined(IPV6_V6ONLY) if(addr->ai_family == AF_INET6 && v6only) { if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, @@ -494,7 +565,8 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto) */ static int make_sock(int stype, const char* ifname, const char* port, - struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd) + struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd, + int* reuseport) { struct addrinfo *res = NULL; int r, s, inuse, noproto; @@ -521,14 +593,15 @@ make_sock(int stype, const char* ifname, const char* port, verbose_print_addr(res); s = create_udp_sock(res->ai_family, res->ai_socktype, (struct sockaddr*)res->ai_addr, res->ai_addrlen, - v6only, &inuse, &noproto, (int)rcv, (int)snd); + v6only, &inuse, &noproto, (int)rcv, (int)snd, 1, + reuseport); if(s == -1 && inuse) { log_err("bind: address already in use"); } else if(s == -1 && noproto && hints->ai_family == AF_INET6){ *noip6 = 1; } } else { - s = create_tcp_accept_sock(res, v6only, &noproto); + s = create_tcp_accept_sock(res, v6only, &noproto, reuseport); if(s == -1 && noproto && hints->ai_family == AF_INET6){ *noip6 = 1; } @@ -540,7 +613,8 @@ make_sock(int stype, const char* ifname, const char* port, /** make socket and first see if ifname contains port override info */ static int make_sock_port(int stype, const char* ifname, const char* port, - struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd) + struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd, + int* reuseport) { char* s = strchr(ifname, '@'); if(s) { @@ -557,14 +631,15 @@ make_sock_port(int stype, const char* ifname, const char* port, *noip6 = 0; return -1; } - strncpy(newif, ifname, sizeof(newif)); + (void)strlcpy(newif, ifname, sizeof(newif)); newif[s-ifname] = 0; - strncpy(p, s+1, sizeof(p)); + (void)strlcpy(p, s+1, sizeof(p)); p[strlen(s+1)]=0; return make_sock(stype, newif, p, hints, v6only, noip6, - rcv, snd); + rcv, snd, reuseport); } - return make_sock(stype, ifname, port, hints, v6only, noip6, rcv, snd); + return make_sock(stype, ifname, port, hints, v6only, noip6, rcv, snd, + reuseport); } /** @@ -656,19 +731,21 @@ set_recvpktinfo(int s, int family) * @param rcv: receive buffer size for UDP * @param snd: send buffer size for UDP * @param ssl_port: ssl service port number + * @param reuseport: try to set SO_REUSEPORT if nonNULL and true. + * set to false on exit if reuseport failed due to no kernel support. * @return: returns false on error. */ static int ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, struct addrinfo *hints, const char* port, struct listen_port** list, - size_t rcv, size_t snd, int ssl_port) + size_t rcv, size_t snd, int ssl_port, int* reuseport) { int s, noip6=0; if(!do_udp && !do_tcp) return 0; if(do_auto) { if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, - &noip6, rcv, snd)) == -1) { + &noip6, rcv, snd, reuseport)) == -1) { if(noip6) { log_warn("IPv6 protocol not available"); return 1; @@ -695,7 +772,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, } else if(do_udp) { /* regular udp socket */ if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, - &noip6, rcv, snd)) == -1) { + &noip6, rcv, snd, reuseport)) == -1) { if(noip6) { log_warn("IPv6 protocol not available"); return 1; @@ -716,7 +793,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, atoi(strchr(ifname, '@')+1) == ssl_port) || (!strchr(ifname, '@') && atoi(port) == ssl_port)); if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1, - &noip6, 0, 0)) == -1) { + &noip6, 0, 0, reuseport)) == -1) { if(noip6) { /*log_warn("IPv6 protocol not available");*/ return 1; @@ -767,7 +844,7 @@ listen_create(struct comm_base* base, struct listen_port* ports, if(!front) return NULL; front->cps = NULL; - front->udp_buff = ldns_buffer_new(bufsize); + front->udp_buff = sldns_buffer_new(bufsize); if(!front->udp_buff) { free(front); return NULL; @@ -830,12 +907,12 @@ listen_delete(struct listen_dnsport* front) if(!front) return; listen_list_delete(front->cps); - ldns_buffer_free(front->udp_buff); + sldns_buffer_free(front->udp_buff); free(front); } struct listen_port* -listening_ports_open(struct config_file* cfg) +listening_ports_open(struct config_file* cfg, int* reuseport) { struct listen_port* list = NULL; struct addrinfo hints; @@ -871,7 +948,7 @@ listening_ports_open(struct config_file* cfg) do_auto, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port)) { + cfg->ssl_port, reuseport)) { listening_ports_free(list); return NULL; } @@ -882,7 +959,7 @@ listening_ports_open(struct config_file* cfg) do_auto, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port)) { + cfg->ssl_port, reuseport)) { listening_ports_free(list); return NULL; } @@ -895,7 +972,7 @@ listening_ports_open(struct config_file* cfg) if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port)) { + cfg->ssl_port, reuseport)) { listening_ports_free(list); return NULL; } @@ -906,7 +983,7 @@ listening_ports_open(struct config_file* cfg) if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port)) { + cfg->ssl_port, reuseport)) { listening_ports_free(list); return NULL; } @@ -936,7 +1013,7 @@ size_t listen_get_mem(struct listen_dnsport* listen) { size_t s = sizeof(*listen) + sizeof(*listen->base) + sizeof(*listen->udp_buff) + - ldns_buffer_capacity(listen->udp_buff); + sldns_buffer_capacity(listen->udp_buff); struct listen_list* p; for(p = listen->cps; p; p = p->next) { s += sizeof(*p); |