From fec7dd5a5625e0953a49484408af8268991cde63 Mon Sep 17 00:00:00 2001 From: Sten Spans Date: Wed, 11 Feb 2015 14:50:10 +0100 Subject: [PATCH] add support for non-local binds - add option non-local-bind to request this behaviour - add support to auth and recursor - add utility function to set sockopts --- docs/markdown/authoritative/settings.md | 10 ++++++++++ docs/markdown/recursor/settings.md | 10 ++++++++++ pdns/common_startup.cc | 1 + pdns/nameserver.cc | 6 ++++++ pdns/pdns.conf-dist | 5 +++++ pdns/pdns_recursor.cc | 9 ++++++++- pdns/tcpreceiver.cc | 5 +++++ pdns/unix_utility.cc | 25 +++++++++++++++++++++++++ pdns/utility.hh | 3 +++ 9 files changed, 73 insertions(+), 1 deletion(-) diff --git a/docs/markdown/authoritative/settings.md b/docs/markdown/authoritative/settings.md index 8bd83520c..266973d5c 100644 --- a/docs/markdown/authoritative/settings.md +++ b/docs/markdown/authoritative/settings.md @@ -309,6 +309,16 @@ interfaces and not use the default 'bind to any'. This causes big problems if you have multiple IP addresses. Unix does not provide a way of figuring out what IP address a packet was sent to when binding to any. +## `non-local-bind` +* Boolean +* Default: no + +Bind to addresses even if one or more of the [`local-address`'s](#local-address) +do not exist on this server. Setting this option will enable the needed socket +options to allow binding to non-local addresses. +This feature is intended to facilitate ip-failover setups, but it may also +mask configuration issues and for this reason it is disabled by default. + ## `local-address-nonexist-fail` * Boolean * Default: no diff --git a/docs/markdown/recursor/settings.md b/docs/markdown/recursor/settings.md index 841587b3b..f057f1721 100644 --- a/docs/markdown/recursor/settings.md +++ b/docs/markdown/recursor/settings.md @@ -325,6 +325,16 @@ recommended to bind to explicit addresses. Local port to bind to. +## `non-local-bind` +* Boolean +* Default: no + +Bind to addresses even if one or more of the [`local-address`'s](#local-address) +do not exist on this server. Setting this option will enable the needed socket +options to allow binding to non-local addresses. +This feature is intended to facilitate ip-failover setups, but it may also +mask configuration issues and for this reason it is disabled by default. + ## `loglevel` * Integer between 0 and * Default: 4 diff --git a/pdns/common_startup.cc b/pdns/common_startup.cc index 7d69253dd..d893f53a4 100644 --- a/pdns/common_startup.cc +++ b/pdns/common_startup.cc @@ -56,6 +56,7 @@ void declareArguments() ::arg().setSwitch("log-dns-queries","If PDNS should log all incoming DNS queries")="no"; ::arg().set("local-address","Local IP addresses to which we bind")="0.0.0.0"; ::arg().setSwitch("local-address-nonexist-fail","Fail to start if one or more of the local-address's do not exist on this server")="yes"; + ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options")="no"; ::arg().set("local-ipv6","Local IP address to which we bind")=""; ::arg().setSwitch("reuseport","Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket")="no"; ::arg().setSwitch("local-ipv6-nonexist-fail","Fail to start if one or more of the local-ipv6 addresses do not exist on this server")="yes"; diff --git a/pdns/nameserver.cc b/pdns/nameserver.cc index 99be86b99..c7386b83e 100644 --- a/pdns/nameserver.cc +++ b/pdns/nameserver.cc @@ -123,6 +123,9 @@ void UDPNameserver::bindIPv4() d_can_reuseport = false; #endif + if( ::arg().mustDo("non-local-bind") ) + Utility::setBindAny(AF_INET, s); + locala=ComboAddress(localname, ::arg().asNum("local-port")); if(locala.sin4.sin_family != AF_INET) throw PDNSException("Attempting to bind IPv4 socket to IPv6 address"); @@ -223,6 +226,9 @@ void UDPNameserver::bindIPv6() d_can_reuseport = false; #endif + if( ::arg().mustDo("non-local-bind") ) + Utility::setBindAny(AF_INET6, s); + if( !d_additional_socket ) g_localaddresses.push_back(locala); if(::bind(s, (sockaddr*)&locala, sizeof(locala))<0) { diff --git a/pdns/pdns.conf-dist b/pdns/pdns.conf-dist index 39f7b37aa..92211c1cb 100644 --- a/pdns/pdns.conf-dist +++ b/pdns/pdns.conf-dist @@ -299,6 +299,11 @@ # # no-shuffle=off +################################# +# non-local-bind Enable binding to non-local addresses by using FREEBIND / BINDANY socket options +# +# non-local-bind=no + ################################# # only-notify Only send AXFR NOTIFY to these IP addresses or netmasks # diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index e07e9afd0..b7ec09317 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -1125,6 +1125,9 @@ void makeTCPServerSockets() } #endif + if( ::arg().mustDo("non-local-bind") ) + Utility::setBindAny(AF_INET, fd); + sin.sin4.sin_port = htons(st.port); int socklen=sin.sin4.sin_family==AF_INET ? sizeof(sin.sin4) : sizeof(sin.sin6); if (::bind(fd, (struct sockaddr *)&sin, socklen )<0) @@ -1146,6 +1149,7 @@ void makeTCPServerSockets() void makeUDPServerSockets() { + int one=1; vectorlocals; stringtok(locals,::arg()["local-address"]," ,"); @@ -1174,7 +1178,6 @@ void makeUDPServerSockets() setSocketTimestamps(fd); if(IsAnyAddress(sin)) { - int one=1; setsockopt(fd, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); // linux supports this, so why not - might fail on other systems setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); if(sin.sin6.sin6_family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) { @@ -1183,6 +1186,9 @@ void makeUDPServerSockets() } + if( ::arg().mustDo("non-local-bind") ) + Utility::setBindAny(AF_INET6, fd); + Utility::setCloseOnExec(fd); setSocketReceiveBuffer(fd, 250000); @@ -2243,6 +2249,7 @@ int main(int argc, char **argv) ::arg().set("no-shuffle","Don't change")="off"; ::arg().set("local-port","port to listen on")="53"; ::arg().set("local-address","IP addresses to listen on, separated by spaces or commas. Also accepts ports.")="127.0.0.1"; + ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options")="no"; ::arg().set("trace","if we should output heaps of logging. set to 'fail' to only log failing domains")="off"; ::arg().set("daemon","Operate as a daemon")="yes"; ::arg().set("loglevel","Amount of logging. Higher is more. Do not set below 3")="4"; diff --git a/pdns/tcpreceiver.cc b/pdns/tcpreceiver.cc index 6356d343b..eb321d833 100644 --- a/pdns/tcpreceiver.cc +++ b/pdns/tcpreceiver.cc @@ -1095,6 +1095,9 @@ TCPNameserver::TCPNameserver() exit(1); } + if( ::arg().mustDo("non-local-bind") ) + Utility::setBindAny(AF_INET, s); + if(::bind(s, (sockaddr*)&local, local.getSocklen())<0) { close(s); if( errno == EADDRNOTAVAIL && ! ::arg().mustDo("local-address-nonexist-fail") ) { @@ -1132,6 +1135,8 @@ TCPNameserver::TCPNameserver() L<