From 56a1142f033234e3ee3b6361e9a1bcdbe606f816 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Tue, 2 Apr 2013 17:02:58 +0100 Subject: [PATCH] SO_REUSEPORT may be defined, but not supported. --- CHANGELOG | 5 +++++ src/dhcp.c | 16 ++++++++++++---- src/dhcp6.c | 18 +++++++++++++----- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 444f985..141bb1c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -52,6 +52,11 @@ version 2.66 Allow a trailing '*' wildcard in all interface-name configurations. Thanks to Christian Parpart for the patch. + Handle the situation where libc headers define + SO_REUSEPORT, but the kernel in use doesn't, to cope with + the introduction of this option to Linux. Thanks to Rich + Felker for the bug report. + version 2.65 Fix regression which broke forwarding of queries sent via diff --git a/src/dhcp.c b/src/dhcp.c index 6b8b803..dd25632 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -65,14 +65,22 @@ static int make_fd(int port) /* When bind-interfaces is set, there might be more than one dnmsasq instance binding port 67. That's OK if they serve different networks. - Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */ + Need to set REUSEADDR|REUSEPORT to make this posible. + Handle the case that REUSEPORT is defined, but the kernel doesn't + support it. This handles the introduction of REUSEPORT on Linux. */ if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)) { + int rc = -1, porterr = 0; + #ifdef SO_REUSEPORT - int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt)); -#else - int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)); + if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 && + errno != ENOPROTOOPT) + porterr = 1; #endif + + if (rc == -1 && !porterr) + rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)); + if (rc == -1) die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET); } diff --git a/src/dhcp6.c b/src/dhcp6.c index dd53f86..a827b2f 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -48,16 +48,24 @@ void dhcp6_init(void) !set_ipv6pktinfo(fd)) die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET); - /* When bind-interfaces is set, there might be more than one dnmsasq + /* When bind-interfaces is set, there might be more than one dnmsasq instance binding port 547. That's OK if they serve different networks. - Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */ + Need to set REUSEADDR|REUSEPORT to make this posible. + Handle the case that REUSEPORT is defined, but the kernel doesn't + support it. This handles the introduction of REUSEPORT on Linux. */ if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)) { + int rc = -1, porterr = 0; + #ifdef SO_REUSEPORT - int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt)); -#else - int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)); + if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 && + errno != ENOPROTOOPT) + porterr = 1; #endif + + if (rc == -1 && !porterr) + rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)); + if (rc == -1) die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET); }