]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add a parameter to limit the number of TCP conns per frontend
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 23 Mar 2021 17:58:54 +0000 (18:58 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 26 Mar 2021 09:53:33 +0000 (10:53 +0100)
pdns/dnsdist-lua.cc
pdns/dnsdist-tcp.cc
pdns/dnsdist.hh
pdns/dnsdistdist/docs/reference/config.rst
pdns/dnsdistdist/docs/reference/dnscrypt.rst
pdns/dnsdistdist/doh.cc

index f234741d80b9652d2d3798a429c9bd9a34d377cb..010478e9cf4ed7fed7f5d06cbbf00bc3561df8c4 100644 (file)
@@ -99,7 +99,7 @@ void resetLuaSideEffect()
 
 typedef std::unordered_map<std::string, boost::variant<bool, int, std::string, std::vector<std::pair<int,int> >, std::vector<std::pair<int, std::string> >, std::map<std::string,std::string>  > > localbind_t;
 
-static void parseLocalBindVars(boost::optional<localbind_t> vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus, int& tcpListenQueueSize, size_t& maxInFlightQueriesPerConnection)
+static void parseLocalBindVars(boost::optional<localbind_t> vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus, int& tcpListenQueueSize, size_t& maxInFlightQueriesPerConnection, size_t& tcpMaxConcurrentConnections)
 {
   if (vars) {
     if (vars->count("reusePort")) {
@@ -111,6 +111,9 @@ static void parseLocalBindVars(boost::optional<localbind_t> vars, bool& reusePor
     if (vars->count("tcpListenQueueSize")) {
       tcpListenQueueSize = boost::get<int>((*vars)["tcpListenQueueSize"]);
     }
+    if (vars->count("maxConcurrentTCPConnections")) {
+      tcpMaxConcurrentConnections = boost::get<int>((*vars)["maxConcurrentTCPConnections"]);
+    }
     if (vars->count("maxInFlight")) {
       maxInFlightQueriesPerConnection = boost::get<int>((*vars)["maxInFlight"]);
     }
@@ -602,10 +605,11 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
       int tcpFastOpenQueueSize = 0;
       int tcpListenQueueSize = 0;
       size_t maxInFlightQueriesPerConn = 0;
+      size_t tcpMaxConcurrentConnections = 0;
       std::string interface;
       std::set<int> cpus;
 
-      parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn);
+      parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections);
 
       try {
        ComboAddress loc(addr, 53);
@@ -628,6 +632,10 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
         if (maxInFlightQueriesPerConn > 0) {
           tcpCS->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
         }
+        if (tcpMaxConcurrentConnections > 0) {
+          tcpCS->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
+        }
+
         g_frontends.push_back(std::move(tcpCS));
       }
       catch(const std::exception& e) {
@@ -647,10 +655,11 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
       int tcpFastOpenQueueSize = 0;
       int tcpListenQueueSize = 0;
       size_t maxInFlightQueriesPerConn = 0;
+      size_t tcpMaxConcurrentConnections = 0;
       std::string interface;
       std::set<int> cpus;
 
-      parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn);
+      parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections);
 
       try {
        ComboAddress loc(addr, 53);
@@ -663,6 +672,9 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
         if (maxInFlightQueriesPerConn > 0) {
           tcpCS->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
         }
+        if (tcpMaxConcurrentConnections > 0) {
+          tcpCS->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
+        }
         g_frontends.push_back(std::move(tcpCS));
       }
       catch(std::exception& e) {
@@ -1336,11 +1348,12 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
       int tcpFastOpenQueueSize = 0;
       int tcpListenQueueSize = 0;
       size_t maxInFlightQueriesPerConn = 0;
+      size_t tcpMaxConcurrentConnections = 0;
       std::string interface;
       std::set<int> cpus;
       std::vector<DNSCryptContext::CertKeyPaths> certKeys;
 
-      parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn);
+      parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections);
 
       if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
         auto certFile = boost::get<std::string>(certFiles);
@@ -1382,6 +1395,12 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
         if (tcpListenQueueSize > 0) {
           cs->tcpListenQueueSize = tcpListenQueueSize;
         }
+        if (maxInFlightQueriesPerConn > 0) {
+            cs->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
+        }
+        if (tcpMaxConcurrentConnections > 0) {
+          cs->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
+        }
 
         g_frontends.push_back(std::move(cs));
       }
@@ -2139,11 +2158,12 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
     int tcpFastOpenQueueSize = 0;
     int tcpListenQueueSize = 0;
     size_t maxInFlightQueriesPerConn = 0;
+    size_t tcpMaxConcurrentConnections = 0;
     std::string interface;
     std::set<int> cpus;
 
     if (vars) {
-      parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn);
+      parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections);
 
       if (vars->count("idleTimeout")) {
         frontend->d_idleTimeout = boost::get<int>((*vars)["idleTimeout"]);
@@ -2184,7 +2204,9 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
     if (tcpListenQueueSize > 0) {
       cs->tcpListenQueueSize = tcpListenQueueSize;
     }
-
+    if (tcpMaxConcurrentConnections > 0) {
+      cs->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
+    }
     g_frontends.push_back(std::move(cs));
 #else
     throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!");
@@ -2330,11 +2352,12 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
         int tcpFastOpenQueueSize = 0;
         int tcpListenQueueSize = 0;
         size_t maxInFlightQueriesPerConn = 0;
+        size_t tcpMaxConcurrentConns = 0;
         std::string interface;
         std::set<int> cpus;
 
         if (vars) {
-          parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn);
+          parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConns);
 
           if (vars->count("provider")) {
             frontend->d_provider = boost::get<const string>((*vars)["provider"]);
@@ -2365,6 +2388,9 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
           if (maxInFlightQueriesPerConn > 0) {
             cs->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
           }
+          if (tcpMaxConcurrentConns > 0) {
+            cs->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConns;
+          }
 
           g_tlslocals.push_back(cs->tlsFrontend);
           g_frontends.push_back(std::move(cs));
index c52015021b29ae1cf203470080ef67975365438b..32af86fcc9df96ba8ead2d8b00cda336e0d38c98 100644 (file)
@@ -1241,6 +1241,10 @@ void tcpAcceptorThread(ClientState* cs)
 #endif
       // will be decremented when the ConnectionInfo object is destroyed, no matter the reason
       auto concurrentConnections = ++cs->tcpCurrentConnections;
+      if (cs->d_tcpConcurrentConnectionsLimit > 0 && concurrentConnections > cs->d_tcpConcurrentConnectionsLimit) {
+        continue;
+      }
+
       if (concurrentConnections > cs->tcpMaxConcurrentConnections) {
         cs->tcpMaxConcurrentConnections = concurrentConnections;
       }
index a9ecda0a06df70a95ce36f307b456e41de18ffda..9fbca3861b81b8d5f88f7720a7f68f05d347968a 100644 (file)
@@ -738,7 +738,7 @@ struct ClientState
   stat_t tcpDownstreamTimeouts{0};
   /* current number of connections to this frontend */
   stat_t tcpCurrentConnections{0};
-  /* maximum number of concurrent connections to this frontend */
+  /* maximum number of concurrent connections to this frontend seen */
   stat_t tcpMaxConcurrentConnections{0};
   stat_t tlsNewSessions{0}; // A new TLS session has been negotiated, no resumption
   stat_t tlsResumptions{0}; // A TLS session has been resumed, either via session id or via a TLS ticket
@@ -753,6 +753,7 @@ struct ClientState
   /* in ms */
   pdns::stat_t_trait<double> tcpAvgConnectionDuration{0.0};
   size_t d_maxInFlightQueriesPerConn{1};
+  size_t d_tcpConcurrentConnectionsLimit{0};
   int udpFD{-1};
   int tcpFD{-1};
   int tcpListenQueueSize{SOMAXCONN};
@@ -921,6 +922,7 @@ struct DownstreamState
   pdns::stat_t_trait<double> tcpAvgConnectionDuration{0.0};
   size_t socketsOffset{0};
   size_t d_maxInFlightQueriesPerConn{1};
+  size_t d_tcpConcurrentConnectionsLimit{0};
   double queryLoad{0.0};
   double dropRate{0.0};
   double latencyUsec{0.0};
index 8a0b3e8ff41211c76a16b52ab81fa678c91d6f99..1c7bc4bc941f894b4499e435821b819d3082dccc 100644 (file)
@@ -72,7 +72,7 @@ Listen Sockets
     Added ``tcpListenQueueSize`` parameter.
 
   .. versionchanged:: 1.6.0
-    Added ``maxInFlight`` parameter.
+    Added ``maxInFlight`` and ``maxConcurrentTCPConnections`` parameters.
 
   Add to the list of listen addresses.
 
@@ -89,6 +89,7 @@ Listen Sockets
   * ``cpus={}``: table - Set the CPU affinity for this listener thread, asking the scheduler to run it on a single CPU id, or a set of CPU ids. This parameter is only available if the OS provides the pthread_setaffinity_np() function.
   * ``tcpListenQueueSize=SOMAXCONN``: int - Set the size of the listen queue. Default is ``SOMAXCONN``.
   * ``maxInFlight=0``: int - Maximum number of in-flight queries. The default is 0, which disables out-of-order processing.
+  * ``maxConcurrentTCPConnections=0``: int - Maximum number of concurrent incoming TCP connections. The default is 0 which means unlimited.
 
   .. code-block:: lua
 
@@ -105,7 +106,7 @@ Listen Sockets
     ``url`` now defaults to ``/dns-query`` instead of ``/``, and does exact matching instead of accepting sub-paths. Added ``tcpListenQueueSize`` parameter.
 
   .. versionchanged:: 1.6.0
-    ``exactPathMatching``, ``releaseBuffers`` and ``enableRenegotiation`` options added.
+    ``enableRenegotiation``, ``exactPathMatching``, ``maxConcurrentTCPConnections`` and ``releaseBuffers`` options added.
     ``internalPipeBufferSize`` now defaults to 1048576 on Linux.
 
   Listen on the specified address and TCP port for incoming DNS over HTTPS connections, presenting the specified X.509 certificate.
@@ -144,6 +145,7 @@ Listen Sockets
   * ``tcpListenQueueSize=SOMAXCONN``: int - Set the size of the listen queue. Default is ``SOMAXCONN``.
   * ``internalPipeBufferSize=0``: int - Set the size in bytes of the internal buffer of the pipes used internally to pass queries and responses between threads. Requires support for ``F_SETPIPE_SZ`` which is present in Linux since 2.6.35. The actual size might be rounded up to a multiple of a page size. 0 means that the OS default size is used. The default value is 0, except on Linux where it is 1048576 since 1.6.0.
   * ``exactPathMatching=true``: bool - Whether to do exact path matching of the query path against the paths configured in ``urls`` (true, the default since 1.5.0) or to accepts sub-paths (false, and was the default before 1.5.0).
+  * ``maxConcurrentTCPConnections=0``: int - Maximum number of concurrent incoming TCP connections. The default is 0 which means unlimited.  
   * ``releaseBuffers=true``: bool - Whether OpenSSL should release its I/O buffers when a connection goes idle, saving roughly 35 kB of memory per connection.
   * ``enableRenegotiation=false``: bool - Whether secure TLS renegotiation should be enabled. Disabled by default since it increases the attack surface and is seldom used for DNS.
 
@@ -154,7 +156,7 @@ Listen Sockets
   .. versionchanged:: 1.5.0
     ``sessionTimeout`` and ``tcpListenQueueSize`` options added.
   .. versionchanged:: 1.6.0
-    ``maxInFlight``, ``releaseBuffers`` and ``enableRenegotiation`` options added.
+    ``enableRenegotiation``, ``maxConcurrentTCPConnections``, ``maxInFlight`` and ``releaseBuffers`` options added.
 
   Listen on the specified address and TCP port for incoming DNS over TLS connections, presenting the specified X.509 certificate.
 
@@ -185,6 +187,7 @@ Listen Sockets
   * ``keyLogFile``: str - Write the TLS keys in the specified file so that an external program can decrypt TLS exchanges, in the format described in https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. Note that this feature requires OpenSSL >= 1.1.1.
   * ``tcpListenQueueSize=SOMAXCONN``: int - Set the size of the listen queue. Default is ``SOMAXCONN``.
   * ``maxInFlight=0``: int - Maximum number of in-flight queries. The default is 0, which disables out-of-order processing.
+  * ``maxConcurrentTCPConnections=0``: int - Maximum number of concurrent incoming TCP connections. The default is 0 which means unlimited.
   * ``releaseBuffers=true``: bool - Whether OpenSSL should release its I/O buffers when a connection goes idle, saving roughly 35 kB of memory per connection.
   * ``enableRenegotiation=false``: bool - Whether secure TLS renegotiation should be enabled (OpenSSL only, the GnuTLS provider does not support it). Disabled by default since it increases the attack surface and is seldom used for DNS.
 
index c2df70682d8067cee11c9ed9024f595703b6b94f..ee5ed9ede056be57878ff9102cb3e45d91ceb2c5 100644 (file)
@@ -7,6 +7,12 @@ DNSCrypt objects and functions
     Removed ``doTCP`` from the options. A listen socket on TCP is always created.
     ``certFile(s)`` and ``keyFile(s)`` now accept a list of files.
 
+  .. versionchanged:: 1.5.0
+    Added ``tcpListenQueueSize`` parameter.
+
+  .. versionchanged:: 1.6.0
+    Added ``maxInFlight`` and ``maxConcurrentTCPConnections`` parameters.
+
   Adds a DNSCrypt listen socket on ``address``.
 
   :param string address: The address and port to listen on
@@ -22,6 +28,9 @@ DNSCrypt objects and functions
   * ``tcpFastOpenQueueSize=0``: int - Set the TCP Fast Open queue size, enabling TCP Fast Open when available and the value is larger than 0
   * ``interface=""``: str - Sets the network interface to use
   * ``cpus={}``: table - Set the CPU affinity for this listener thread, asking the scheduler to run it on a single CPU id, or a set of CPU ids. This parameter is only available if the OS provides the pthread_setaffinity_np() function.
+  * ``tcpListenQueueSize=SOMAXCONN``: int - Set the size of the listen queue. Default is ``SOMAXCONN``.
+  * ``maxInFlight=0``: int - Maximum number of in-flight queries. The default is 0, which disables out-of-order processing.
+  * ``maxConcurrentTCPConnections=0``: int - Maximum number of concurrent incoming TCP connections. The default is 0 which means unlimited.
 
 .. function:: generateDNSCryptProviderKeys(publicKey, privateKey)
 
index cfcd23e0fb8469cb9f631dc6df28194b94339f6b..08bf9376fad0469de62071633ee5567bbbe42f70 100644 (file)
@@ -1169,20 +1169,28 @@ static void on_accept(h2o_socket_t *listener, const char *err)
   if (err != nullptr) {
     return;
   }
-  // do some dnsdist rules here to filter based on IP address
+
   if ((sock = h2o_evloop_socket_accept(listener)) == nullptr) {
     return;
   }
 
-  // ComboAddress remote;
-  // h2o_socket_getpeername(sock, reinterpret_cast<struct sockaddr*>(&remote));
-  //  cout<<"New HTTP accept for client "<<remote.toStringWithPort()<<": "<< listener->data << endl;
-
   const int descriptor = h2o_socket_get_fd(sock);
   if (descriptor == -1) {
+    h2o_socket_close(sock);
+    return;
+  }
+
+  auto concurrentConnections = ++dsc->cs->tcpCurrentConnections;
+  if (dsc->cs->d_tcpConcurrentConnectionsLimit > 0 && concurrentConnections > dsc->cs->d_tcpConcurrentConnectionsLimit) {
+    --dsc->cs->tcpCurrentConnections;
+    h2o_socket_close(sock);
     return;
   }
 
+  if (concurrentConnections > dsc->cs->tcpMaxConcurrentConnections) {
+    dsc->cs->tcpMaxConcurrentConnections = concurrentConnections;
+  }
+
   auto& conn = t_conns[descriptor];
 
   gettimeofday(&conn.d_connectionStartTime, nullptr);
@@ -1194,11 +1202,6 @@ static void on_accept(h2o_socket_t *listener, const char *err)
   sock->on_close.data = &conn;
   sock->data = dsc;
 
-  auto concurrentConnections = ++dsc->cs->tcpCurrentConnections;
-  if (concurrentConnections > dsc->cs->tcpMaxConcurrentConnections) {
-    dsc->cs->tcpMaxConcurrentConnections = concurrentConnections;
-  }
-
   ++dsc->df->d_httpconnects;
 
   h2o_accept(conn.d_acceptCtx->get(), sock);