log_assert(daemon);
if(daemon->cfg->port != daemon->listening_port) {
size_t i;
-#if defined(__linux__) && defined(SO_REUSEPORT)
- if(daemon->cfg->so_reuseport && daemon->cfg->num_threads > 0)
- daemon->num_ports = daemon->cfg->num_threads;
- else
- daemon->num_ports = 1;
-#else
- daemon->num_ports = 1;
-#endif
+ int reuseport = 0;
+ struct listen_port* p0;
+ /* free and close old ports */
if(daemon->ports != NULL) {
for(i=0; i<daemon->num_ports; i++)
listening_ports_free(daemon->ports[i]);
free(daemon->ports);
daemon->ports = NULL;
}
+ /* see if we want to reuseport */
+#if defined(__linux__) && defined(SO_REUSEPORT)
+ if(daemon->cfg->so_reuseport && daemon->cfg->num_threads > 0)
+ reuseport = 1;
+#endif
+ /* try to use reuseport */
+ p0 = listening_ports_open(daemon->cfg, &reuseport);
+ if(!p0) {
+ listening_ports_free(p0);
+ return 0;
+ }
+ if(reuseport) {
+ /* reuseport was successful, allocate for it */
+ daemon->num_ports = daemon->cfg->num_threads;
+ } else {
+ /* do the normal, singleportslist thing,
+ * reuseport not enabled or did not work */
+ daemon->num_ports = 1;
+ }
if(!(daemon->ports = (struct listen_port**)calloc(
daemon->num_ports, sizeof(*daemon->ports)))) {
+ listening_ports_free(p0);
return 0;
}
- for(i=0; i<daemon->num_ports; i++) {
- if(!(daemon->ports[i]=
- listening_ports_open(daemon->cfg))) {
- for(i=0; i<daemon->num_ports; i++)
- listening_ports_free(daemon->ports[i]);
- free(daemon->ports);
- daemon->ports = NULL;
- return 0;
+ daemon->ports[0] = p0;
+ if(reuseport) {
+ /* continue to use reuseport */
+ for(i=1; i<daemon->num_ports; i++) {
+ if(!(daemon->ports[i]=
+ listening_ports_open(daemon->cfg,
+ &reuseport)) || !reuseport ) {
+ for(i=0; i<daemon->num_ports; i++)
+ listening_ports_free(daemon->ports[i]);
+ free(daemon->ports);
+ daemon->ports = NULL;
+ return 0;
+ }
}
}
daemon->listening_port = daemon->cfg->port;
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 listen, int reuseport)
+ int rcv, int snd, int listen, int* reuseport)
{
int s;
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_USE_MIN_MTU)
* Each thread must have its own socket bound to the same port,
* with SO_REUSEPORT set on each socket.
*/
- if (reuseport &&
+ if (reuseport && *reuseport &&
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
- log_err("setsockopt(.. SO_REUSEPORT ..) failed: %s",
- strerror(errno));
- close(s);
- *noproto = 0;
- *inuse = 0;
- return -1;
+#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;
int
create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
- int reuseport)
+ int* reuseport)
{
int s;
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY)
* Each thread must have its own socket bound to the same port,
* with SO_REUSEPORT set on each socket.
*/
- if (reuseport && setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on,
+ if (reuseport && *reuseport &&
+ setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
- log_err("setsockopt(.. SO_REUSEPORT ..) failed: %s",
- strerror(errno));
- close(s);
- return -1;
+#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;
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,
- int reuseport)
+ int* reuseport)
{
struct addrinfo *res = NULL;
int r, s, inuse, noproto;
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,
- int reuseport)
+ int* reuseport)
{
char* s = strchr(ifname, '@');
if(s) {
* @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.
+ * @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, int reuseport)
+ size_t rcv, size_t snd, int ssl_port, int* reuseport)
{
int s, noip6=0;
if(!do_udp && !do_tcp)
}
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;
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
- cfg->ssl_port, cfg->so_reuseport)) {
+ cfg->ssl_port, reuseport)) {
listening_ports_free(list);
return NULL;
}
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
- cfg->ssl_port, cfg->so_reuseport)) {
+ cfg->ssl_port, reuseport)) {
listening_ports_free(list);
return NULL;
}
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->so_reuseport)) {
+ cfg->ssl_port, reuseport)) {
listening_ports_free(list);
return NULL;
}
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->so_reuseport)) {
+ cfg->ssl_port, reuseport)) {
listening_ports_free(list);
return NULL;
}
* interfaces for IP4 and/or IP6, for UDP and/or TCP.
* On the given port number. It creates the sockets.
* @param cfg: settings on what ports to open.
+ * @param reuseport: set to true if you want reuseport, or NULL to not have it,
+ * set to false on exit if reuseport failed to apply (because of no
+ * kernel support).
* @return: linked list of ports or NULL on error.
*/
-struct listen_port* listening_ports_open(struct config_file* cfg);
+struct listen_port* listening_ports_open(struct config_file* cfg,
+ int* reuseport);
/**
* Close and delete the (list of) listening ports.
* @param snd: set size on sndbuf with socket option, if 0 it is not set.
* @param listen: if true, this is a listening UDP port, eg port 53, and
* set SO_REUSEADDR on it.
- * @param reuseport: if true, try to set SO_REUSEPORT on listening UDP port.
+ * @param reuseport: if nonNULL and true, try to set SO_REUSEPORT on
+ * listening UDP port. Set to false on return if it failed to do so.
* @return: the socket. -1 on error.
*/
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 listen, int reuseport);
+ int snd, int listen, int* reuseport);
/**
* Create and bind TCP listening socket
* @param addr: address info ready to make socket.
* @param v6only: enable ip6 only flag on ip6 sockets.
* @param noproto: if error caused by lack of protocol support.
- * @param reuseport: if true, try to set SO_REUSEPORT.
+ * @param reuseport: if nonNULL and true, try to set SO_REUSEPORT on
+ * listening UDP port. Set to false on return if it failed to do so.
* @return: the socket. -1 on error.
*/
int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
- int reuseport);
+ int* reuseport);
#endif /* LISTEN_DNSPORT_H */