From: Remi Gacogne Date: Tue, 23 Mar 2021 17:58:54 +0000 (+0100) Subject: dnsdist: Add a parameter to limit the number of TCP conns per frontend X-Git-Tag: rec-4.6.0-alpha0~1^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=519a00725c3921ed1bc4bf350efb9d0a17901cc0;p=thirdparty%2Fpdns.git dnsdist: Add a parameter to limit the number of TCP conns per frontend --- diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index f234741d80..010478e9cf 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -99,7 +99,7 @@ void resetLuaSideEffect() typedef std::unordered_map >, std::vector >, std::map > > localbind_t; -static void parseLocalBindVars(boost::optional vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set& cpus, int& tcpListenQueueSize, size_t& maxInFlightQueriesPerConnection) +static void parseLocalBindVars(boost::optional vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set& cpus, int& tcpListenQueueSize, size_t& maxInFlightQueriesPerConnection, size_t& tcpMaxConcurrentConnections) { if (vars) { if (vars->count("reusePort")) { @@ -111,6 +111,9 @@ static void parseLocalBindVars(boost::optional vars, bool& reusePor if (vars->count("tcpListenQueueSize")) { tcpListenQueueSize = boost::get((*vars)["tcpListenQueueSize"]); } + if (vars->count("maxConcurrentTCPConnections")) { + tcpMaxConcurrentConnections = boost::get((*vars)["maxConcurrentTCPConnections"]); + } if (vars->count("maxInFlight")) { maxInFlightQueriesPerConnection = boost::get((*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 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 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 cpus; std::vector 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(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 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((*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 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((*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)); diff --git a/pdns/dnsdist-tcp.cc b/pdns/dnsdist-tcp.cc index c52015021b..32af86fcc9 100644 --- a/pdns/dnsdist-tcp.cc +++ b/pdns/dnsdist-tcp.cc @@ -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; } diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index a9ecda0a06..9fbca3861b 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -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 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 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}; diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index 8a0b3e8ff4..1c7bc4bc94 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -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. diff --git a/pdns/dnsdistdist/docs/reference/dnscrypt.rst b/pdns/dnsdistdist/docs/reference/dnscrypt.rst index c2df70682d..ee5ed9ede0 100644 --- a/pdns/dnsdistdist/docs/reference/dnscrypt.rst +++ b/pdns/dnsdistdist/docs/reference/dnscrypt.rst @@ -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) diff --git a/pdns/dnsdistdist/doh.cc b/pdns/dnsdistdist/doh.cc index cfcd23e0fb..08bf9376fa 100644 --- a/pdns/dnsdistdist/doh.cc +++ b/pdns/dnsdistdist/doh.cc @@ -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(&remote)); - // cout<<"New HTTP accept for client "<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);