]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add an optional interface parameter to addLocal()/setLocal()
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 25 May 2017 20:43:23 +0000 (21:43 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 29 May 2017 09:20:11 +0000 (11:20 +0200)
pdns/README-dnsdist.md
pdns/dnsdist-console.cc
pdns/dnsdist-lua.cc
pdns/dnsdist-lua2.cc
pdns/dnsdist.cc
pdns/dnsdist.hh

index 2f61a352b8f3f5cbe41a94fb45d5213a267f8ad9..1670f1b7bed2f88dd81399802c6ad8024a7b37b0 100644 (file)
@@ -1341,8 +1341,8 @@ Here are all functions:
     * member `muted`: if set to true, UDP responses will not be sent for queries received on this bind. Default to false
     * member `toString()`: print the address this bind listens to
  * Network related:
-    * `addLocal(netmask, [true], [false], [TCP Fast Open queue size])`: add to addresses we listen on. Second optional parameter sets TCP or not (UDP is always enabled). Third optional parameter sets SO_REUSEPORT when available. Last parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0.
-    * `setLocal(netmask, [true], [false], [TCP Fast Open queue size])`: reset list of addresses we listen on to this address. Second optional parameter sets TCP or not (UDP is always enabled). Third optional parameter sets SO_REUSEPORT when available. Last parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0.
+    * `addLocal(netmask, [true], [false], [TCP Fast Open queue size], [interface])`: add to addresses we listen on. Second optional parameter sets TCP or not (UDP is always enabled). Third optional parameter sets SO_REUSEPORT when available. Fourth parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0. Last one specifies the network interface to use
+    * `setLocal(netmask, [true], [false], [TCP Fast Open queue size], [interface])`: reset list of addresses we listen on to this address. Second optional parameter sets TCP or not (UDP is always enabled). Third optional parameter sets SO_REUSEPORT when available. Fourth parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0. Last one specifies the network interface to use
  * Blocking related:
     * `addDomainBlock(domain)`: block queries within this domain
  * Carbon/Graphite/Metronome statistics related:
@@ -1631,7 +1631,7 @@ instantiate a server with additional parameters
     * `setTCPSendTimeout(n)`: set the write timeout on TCP connections from the client, in seconds
     * `setUDPTimeout(n)`: set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds. Defaults to 2
  * DNSCrypt related:
-    * `addDNSCryptBind("127.0.0.1:8443", "provider name", "/path/to/resolver.cert", "/path/to/resolver.key", [false], [TCP Fast Open queue size]):` listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of "provider name", using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter sets SO_REUSEPORT when available. The last parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0.
+    * `addDNSCryptBind("127.0.0.1:8443", "provider name", "/path/to/resolver.cert", "/path/to/resolver.key", [false], [TCP Fast Open queue size], [interface]):` listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of "provider name", using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter sets SO_REUSEPORT when available. The sixth parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0. The last one specifies the network interface to use
     * `generateDNSCryptProviderKeys("/path/to/providerPublic.key", "/path/to/providerPrivate.key"):` generate a new provider keypair
     * `generateDNSCryptCertificate("/path/to/providerPrivate.key", "/path/to/resolver.cert", "/path/to/resolver.key", serial, validFrom, validUntil):` generate a new resolver private key and related certificate, valid from the `validFrom` UNIX timestamp until the `validUntil` one, signed with the provider private key
     * `printDNSCryptProviderFingerprint("/path/to/providerPublic.key")`: display the fingerprint of the provided resolver public key
index 4bd01e23df94d7485cd109aabf2c798f8d1ae76c..73e0be0275e8d7a7826c5ed63b0beec3b4dfdaaa 100644 (file)
@@ -275,11 +275,11 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "addAnyTCRule", true, "", "generate TC=1 answers to ANY queries received over UDP, moving them to TCP" },
   { "addDelay", true, "domain, n", "delay answers within that domain by n milliseconds" },
   { "addDisableValidationRule", true, "DNS rule", "set the CD flags to 1 for all queries matching the specified domain" },
-  { "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", [false], [TCP Fast Open queue size]", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter sets SO_REUSEPORT when available. The last parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0" },
+  { "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", [false], [TCP Fast Open queue size], [interface]", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter sets SO_REUSEPORT when available. The sixth parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0. The last one specifies the network interface to use" },
   { "addDomainBlock", true, "domain", "block queries within this domain" },
   { "addDomainSpoof", true, "domain, ip[, ip6]", "generate answers for A/AAAA/ANY queries using the ip parameters" },
   { "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
-  { "addLocal", true, "netmask, [true], [false], [TCP Fast Open queue size]", "add to addresses we listen on. Second optional parameter sets TCP or not. Third optional parameter sets SO_REUSEPORT when available. Last parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0" },
+  { "addLocal", true, "netmask, [true], [false], [TCP Fast Open queue size], [interface]", "add to addresses we listen on. Second optional parameter sets TCP or not. Third optional parameter sets SO_REUSEPORT when available. Fourth optional parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0. Last one specifies the network interface to use" },
   { "addLuaAction", true, "x, func", "where 'x' is all the combinations from `addPoolRule`, and func is a function with the parameter `dq`, which returns an action to be taken on this packet. Good for rare packets but where you want to do a lot of processing" },
   { "addLuaResponseAction", true, "x, func", "where 'x' is all the combinations from `addPoolRule`, and func is a function with the parameter `dr`, which returns an action to be taken on this response packet. Good for rare packets but where you want to do a lot of processing" },
   { "addNoRecurseRule", true, "domain", "clear the RD flag for all queries matching the specified domain" },
@@ -362,7 +362,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "setECSSourcePrefixV4", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv4 queries" },
   { "setECSSourcePrefixV6", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv6 queries" },
   { "setKey", true, "key", "set access key to that key" },
-  { "setLocal", true, "netmask, [true], [false], [TCP Fast Open queue size]", "reset list of addresses we listen on to this address. Second optional parameter sets TCP or not. Third optional parameter sets SO_REUSEPORT when available. Last parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0." },
+  { "setLocal", true, "netmask, [true], [false], [TCP Fast Open queue size], [interface]", "reset list of addresses we listen on to this address. Second optional parameter sets TCP or not. Third optional parameter sets SO_REUSEPORT when available. Fourth parameter sets the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0. Last one specifies the network interface to use" },
   { "setMaxTCPClientThreads", true, "n", "set the maximum of TCP client threads, handling TCP connections" },
   { "setMaxTCPConnectionDuration", true, "n", "set the maximum duration of an incoming TCP connection, in seconds. 0 means unlimited" },
   { "setMaxTCPConnectionsPerClient", true, "n", "set the maximum number of TCP connections per client. 0 means unlimited" },
index 3f494f825f9768b283708872d6712202d892e822..67028e84d0cddb5869d51c9a74fb563861016ed1 100644 (file)
@@ -602,7 +602,7 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
       g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
     });
 
-  g_lua.writeFunction("setLocal", [client](const std::string& addr, boost::optional<bool> doTCP, boost::optional<bool> reusePort, boost::optional<int> tcpFastOpenQueueSize) {
+  g_lua.writeFunction("setLocal", [client](const std::string& addr, boost::optional<bool> doTCP, boost::optional<bool> reusePort, boost::optional<int> tcpFastOpenQueueSize, boost::optional<std::string> interface) {
       setLuaSideEffect();
       if(client)
        return;
@@ -613,14 +613,14 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
       try {
        ComboAddress loc(addr, 53);
        g_locals.clear();
-       g_locals.push_back(std::make_tuple(loc, doTCP ? *doTCP : true, reusePort ? *reusePort : false, tcpFastOpenQueueSize ? *tcpFastOpenQueueSize : 0)); /// only works pre-startup, so no sync necessary
+       g_locals.push_back(std::make_tuple(loc, doTCP ? *doTCP : true, reusePort ? *reusePort : false, tcpFastOpenQueueSize ? *tcpFastOpenQueueSize : 0, interface ? *interface : "")); /// only works pre-startup, so no sync necessary
       }
       catch(std::exception& e) {
        g_outputBuffer="Error: "+string(e.what())+"\n";
       }
     });
 
-  g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<bool> doTCP, boost::optional<bool> reusePort, boost::optional<int> tcpFastOpenQueueSize) {
+  g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<bool> doTCP, boost::optional<bool> reusePort, boost::optional<int> tcpFastOpenQueueSize, boost::optional<std::string> interface) {
       setLuaSideEffect();
       if(client)
        return;
@@ -630,7 +630,7 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
       }
       try {
        ComboAddress loc(addr, 53);
-       g_locals.push_back(std::make_tuple(loc, doTCP ? *doTCP : true, reusePort ? *reusePort : false, tcpFastOpenQueueSize ? *tcpFastOpenQueueSize : 0)); /// only works pre-startup, so no sync necessary
+       g_locals.push_back(std::make_tuple(loc, doTCP ? *doTCP : true, reusePort ? *reusePort : false, tcpFastOpenQueueSize ? *tcpFastOpenQueueSize : 0, interface ? *interface : "")); /// only works pre-startup, so no sync necessary
       }
       catch(std::exception& e) {
        g_outputBuffer="Error: "+string(e.what())+"\n";
index 2a98618c5b2ba8200f7d5eff00e8a63929f12e4d..37ee13458604838751136626dc72bb2638d1b48d 100644 (file)
@@ -512,7 +512,7 @@ void moreLua(bool client)
       }
     });
 
-  g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, const std::string& certFile, const std::string keyFile, boost::optional<bool> reusePort, boost::optional<int> tcpFastOpenQueueSize) {
+  g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, const std::string& certFile, const std::string keyFile, boost::optional<bool> reusePort, boost::optional<int> tcpFastOpenQueueSize, boost::optional<std::string> interface) {
       if (g_configurationDone) {
         g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
         return;
@@ -520,7 +520,7 @@ void moreLua(bool client)
 #ifdef HAVE_DNSCRYPT
       try {
         DnsCryptContext ctx(providerName, certFile, keyFile);
-        g_dnsCryptLocals.push_back(std::make_tuple(ComboAddress(addr, 443), ctx, reusePort ? *reusePort : false, tcpFastOpenQueueSize ? *tcpFastOpenQueueSize : 0));
+        g_dnsCryptLocals.push_back(std::make_tuple(ComboAddress(addr, 443), ctx, reusePort ? *reusePort : false, tcpFastOpenQueueSize ? *tcpFastOpenQueueSize : 0, interface ? *interface : ""));
       }
       catch(std::exception& e) {
         errlog(e.what());
index cb6f93cc3a7c810a9e815c72eb8fd6efd99e4d93..025dd8701ff8e069180d84ce0ee09666b95462fe 100644 (file)
@@ -78,9 +78,9 @@ bool g_syslog{true};
 
 GlobalStateHolder<NetmaskGroup> g_ACL;
 string g_outputBuffer;
-vector<std::tuple<ComboAddress, bool, bool, int>> g_locals;
+vector<std::tuple<ComboAddress, bool, bool, int, string>> g_locals;
 #ifdef HAVE_DNSCRYPT
-std::vector<std::tuple<ComboAddress,DnsCryptContext,bool, int>> g_dnsCryptLocals;
+std::vector<std::tuple<ComboAddress,DnsCryptContext,bool, int, string>> g_dnsCryptLocals;
 #endif
 #ifdef HAVE_EBPF
 shared_ptr<BPFFilter> g_defaultBPFFilter;
@@ -1907,11 +1907,11 @@ try
   if(g_cmdLine.locals.size()) {
     g_locals.clear();
     for(auto loc : g_cmdLine.locals)
-      g_locals.push_back(std::make_tuple(ComboAddress(loc, 53), true, false, 0));
+      g_locals.push_back(std::make_tuple(ComboAddress(loc, 53), true, false, 0, ""));
   }
   
   if(g_locals.empty())
-    g_locals.push_back(std::make_tuple(ComboAddress("127.0.0.1", 53), true, false, 0));
+    g_locals.push_back(std::make_tuple(ComboAddress("127.0.0.1", 53), true, false, 0, ""));
 
   g_configurationDone = true;
 
@@ -1937,6 +1937,7 @@ try
       setsockopt(cs->udpFD, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
 #endif
     }
+
     if (std::get<2>(local)) {
 #ifdef SO_REUSEPORT
       SSetsockopt(cs->udpFD, SOL_SOCKET, SO_REUSEPORT, 1);
@@ -1945,6 +1946,18 @@ try
 #endif
     }
 
+    const std::string& itf = std::get<4>(local);
+    if (!itf.empty()) {
+#ifdef SO_BINDTODEVICE
+      int res = setsockopt(cs->udpFD, SOL_SOCKET, SO_BINDTODEVICE, itf.c_str(), itf.length());
+      if (res != 0) {
+        warnlog("Error setting up the interface on local address '%s': %s", std::get<0>(local).toStringWithPort(), strerror(errno));
+      }
+#else
+      warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", std::get<0>(local).toStringWithPort());
+#endif
+    }
+
 #ifdef HAVE_EBPF
     if (g_defaultBPFFilter) {
       cs->attachFilter(g_defaultBPFFilter);
@@ -1988,6 +2001,19 @@ try
       SSetsockopt(cs->tcpFD, SOL_SOCKET, SO_REUSEPORT, 1);
     }
 #endif
+
+    const std::string& itf = std::get<4>(local);
+    if (!itf.empty()) {
+#ifdef SO_BINDTODEVICE
+      int res = setsockopt(cs->tcpFD, SOL_SOCKET, SO_BINDTODEVICE, itf.c_str(), itf.length());
+      if (res != 0) {
+        warnlog("Error setting up the interface on local address '%s': %s", std::get<0>(local).toStringWithPort(), strerror(errno));
+      }
+#else
+      warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", std::get<0>(local).toStringWithPort());
+#endif
+    }
+
 #ifdef HAVE_EBPF
     if (g_defaultBPFFilter) {
       cs->attachFilter(g_defaultBPFFilter);
@@ -2030,6 +2056,19 @@ try
       warnlog("SO_REUSEPORT has been configured on local address '%s' but is not supported", std::get<0>(dcLocal).toStringWithPort());
 #endif
     }
+
+    const std::string& itf = std::get<4>(dcLocal);
+    if (!itf.empty()) {
+#ifdef SO_BINDTODEVICE
+      int res = setsockopt(cs->udpFD, SOL_SOCKET, SO_BINDTODEVICE, itf.c_str(), itf.length());
+      if (res != 0) {
+        warnlog("Error setting up the interface on local address '%s': %s", std::get<0>(dcLocal).toStringWithPort(), strerror(errno));
+      }
+#else
+      warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", std::get<0>(dcLocal).toStringWithPort());
+#endif
+    }
+
 #ifdef HAVE_EBPF
     if (g_defaultBPFFilter) {
       cs->attachFilter(g_defaultBPFFilter);
@@ -2056,12 +2095,25 @@ try
       warnlog("TCP Fast Open has been configured on local address '%s' but is not supported", std::get<0>(dcLocal).toStringWithPort());
 #endif
     }
+
 #ifdef SO_REUSEPORT
     /* no need to warn again if configured but support is not available, we already did for UDP */
     if (std::get<2>(dcLocal)) {
       SSetsockopt(cs->tcpFD, SOL_SOCKET, SO_REUSEPORT, 1);
     }
 #endif
+
+    if (!itf.empty()) {
+#ifdef SO_BINDTODEVICE
+      int res = setsockopt(cs->tcpFD, SOL_SOCKET, SO_BINDTODEVICE, itf.c_str(), itf.length());
+      if (res != 0) {
+        warnlog("Error setting up the interface on local address '%s': %s", std::get<0>(dcLocal).toStringWithPort(), strerror(errno));
+      }
+#else
+      warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", std::get<0>(dcLocal).toStringWithPort());
+#endif
+    }
+
     if(cs->local.sin4.sin_family == AF_INET6) {
       SSetsockopt(cs->tcpFD, IPPROTO_IPV6, IPV6_V6ONLY, 1);
     }
index 9658e015e99b6a88dd283954c3cf81223cd460cc..a65060f1f5ab497aa849a8c6afca2e0982841b4d 100644 (file)
@@ -651,7 +651,7 @@ extern GlobalStateHolder<NetmaskGroup> g_ACL;
 
 extern ComboAddress g_serverControl; // not changed during runtime
 
-extern std::vector<std::tuple<ComboAddress, bool, bool, int>> g_locals; // not changed at runtime (we hope XXX)
+extern std::vector<std::tuple<ComboAddress, bool, bool, int, std::string>> g_locals; // not changed at runtime (we hope XXX)
 extern vector<ClientState*> g_frontends;
 extern std::string g_key; // in theory needs locking
 extern bool g_truncateTC;
@@ -742,7 +742,7 @@ bool fixUpResponse(char** response, uint16_t* responseLen, size_t* responseSize,
 void restoreFlags(struct dnsheader* dh, uint16_t origFlags);
 
 #ifdef HAVE_DNSCRYPT
-extern std::vector<std::tuple<ComboAddress,DnsCryptContext,bool,int>> g_dnsCryptLocals;
+extern std::vector<std::tuple<ComboAddress,DnsCryptContext,bool,int, std::string>> g_dnsCryptLocals;
 
 int handleDnsCryptQuery(DnsCryptContext* ctx, char* packet, uint16_t len, std::shared_ptr<DnsCryptQuery>& query, uint16_t* decryptedQueryLen, bool tcp, std::vector<uint8_t>& response);
 bool encryptResponse(char* response, uint16_t* responseLen, size_t responseSize, bool tcp, std::shared_ptr<DnsCryptQuery> dnsCryptQuery);