]> granicus.if.org Git - pdns/commitdiff
add support for non-local binds
authorSten Spans <sten@blinkenlights.nl>
Wed, 11 Feb 2015 13:50:10 +0000 (14:50 +0100)
committerSten Spans <sten@blinkenlights.nl>
Tue, 17 Feb 2015 15:11:19 +0000 (16:11 +0100)
- 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
docs/markdown/recursor/settings.md
pdns/common_startup.cc
pdns/nameserver.cc
pdns/pdns.conf-dist
pdns/pdns_recursor.cc
pdns/tcpreceiver.cc
pdns/unix_utility.cc
pdns/utility.hh

index 8bd83520cc97ee92f3b4f7b290f88bd4bbb5a1b8..266973d5ce42573dbdcfa35ea93da64c9cc56f03 100644 (file)
@@ -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
index 841587b3b24f36d74dbc94b454102a4a5731de83..f057f1721bf08e262fe653911d227f80c6b45146 100644 (file)
@@ -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
index 7d69253dd20f85deac601dc93ac6a4f1090a493b..d893f53a4deceb730722371a998286eafdc21e2a 100644 (file)
@@ -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";
index 99be86b996f7f15513aa9db2a8e9b29994fd47d4..c7386b83e4c69fd0657f1eabe49bdfc7fd6bb845 100644 (file)
@@ -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) {
index 39f7b37aa4f549e9d7b436e986c0a976a084d228..92211c1cbdff0601e1bd1e8746eae48e3c90a46f 100644 (file)
 #
 # 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
 #
index e07e9afd0c0386e555a185a8942d9550d6c52076..b7ec09317d3a6345f562f082ca355fb1d7c44712 100644 (file)
@@ -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;
   vector<string>locals;
   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";
index 6356d343b8432ae146172e7a4ca7285d0784380b..eb321d83384c817e66a2a11f97db765a44593950 100644 (file)
@@ -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<<Logger::Error<<"Setsockopt failed"<<endl;
       exit(1);  
     }
+    if( ::arg().mustDo("non-local-bind") )
+       Utility::setBindAny(AF_INET6, s);
     if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &tmp, sizeof(tmp)) < 0) {
       L<<Logger::Error<<"Failed to set IPv6 socket to IPv6 only, continuing anyhow: "<<strerror(errno)<<endl;
     }
index c6530cc4c9eb078f6fb37abe9142ce31d8f51ff5..13c2c54ed6ca9876e6b59459932476bdf8a128ee 100644 (file)
@@ -107,6 +107,31 @@ bool Utility::setCloseOnExec(sock_t sock)
   return true;
 }
 
+void Utility::setBindAny(int af, sock_t sock)
+{
+  int one = 1;
+
+#ifdef IP_FREEBIND
+  if (setsockopt(sock, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
+      theL()<<Logger::Warning<<"Warning: IP_FREEBIND setsockopt failed: "<<strerror(errno)<<endl;
+#endif
+
+#ifdef IP_BINDANY
+  if (af == AF_INET)
+    if (setsockopt(s, IPPROTO_IP, IP_BINDANY, &one, sizeof(one)) < 0)
+      theL()<<Logger::Warning<<"Warning: IP_BINDANY setsockopt failed: "<<strerror(errno)<<endl;
+#endif
+#ifdef IPV6_BINDANY
+  if (af == AF_INET6)
+    if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDANY, &one, sizeof(one)) < 0)
+      theL()<<Logger::Warning<<"Warning: IPV6_BINDANY setsockopt failed: "<<strerror(errno)<<endl;
+#endif
+#ifdef SO_BINDANY
+  if (setsockopt(sock, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) < 0)
+      theL()<<Logger::Warning<<"Warning: SO_BINDANY setsockopt failed: "<<strerror(errno)<<endl;
+#endif
+}
+
 const char *Utility::inet_ntop(int af, const char *src, char *dst, size_t size)
 {
   return ::inet_ntop(af,src,dst,size);
index 6280279c8bf84c2a2feb44386e8410fee2d7e124..d90e45006fe51b92e305d1d44119de2b8338f8fe 100644 (file)
@@ -149,6 +149,9 @@ public:
   
   //! Marks the socket to be closed on exec().
   static bool setCloseOnExec ( Utility::sock_t socket );
+
+  //! Sets the socket into Bind-any mode
+  static void setBindAny ( int af, Utility::sock_t socket );
   
   //! Sleeps for a number of seconds.
   static unsigned int sleep( unsigned int seconds );