]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Fix TCP keepalive (#853)
authorAmos Jeffries <yadij@users.noreply.github.com>
Sun, 1 Aug 2021 12:58:25 +0000 (12:58 +0000)
committerSquid Anubis <squid-anubis@squid-cache.org>
Sun, 1 Aug 2021 14:24:30 +0000 (14:24 +0000)
Setting TCP keep-alive flags at accept(2) time resolves issues with
client sockets timing out while waiting for the ::Server handler to run.

Also resolves a bug with FTP DATA connections not having keep-alive set.
These connections would truncate objects if the data transfer connection
paused for too long and became timed out by the network routing system.

16 files changed:
acinclude/ax_cxx_0x_types.m4
configure.ac
src/anyp/PortCfg.cc
src/anyp/PortCfg.h
src/client_side.cc
src/comm.cc
src/comm.h
src/comm/Makefile.am
src/comm/Tcp.cc [new file with mode: 0644]
src/comm/Tcp.h [new file with mode: 0644]
src/comm/TcpAcceptor.cc
src/comm/forward.h
src/ipc/TypedMsgHdr.h
src/servers/FtpServer.cc
src/tests/stub_comm.cc
src/tests/stub_libcomm.cc

index 52ab37b21f8a9c07855162fbff4f912c54d6f615..289b49f53f01a6b9a99f26d265f747f7fa9a3629 100644 (file)
@@ -57,3 +57,22 @@ enum class testEnum { one, two, three };
      [$squid_cv_have_std_underlying_type],
      [Define if stdlibc support std::underlying_type for enums])
 ])
+
+## SQUID_CXX_STD_IS_TRIVIALLY_COPYABLE
+## checks whether the std::is_trivially_copyable<> trait exists
+## (known to be missing in GCC until version 5.1)
+AC_DEFUN([SQUID_CXX_STD_IS_TRIVIALLY_COPYABLE],[
+  AC_CACHE_CHECK([whether compiler supports std::is_trivially_copyable],
+    [squid_cv_have_std_is_trivially_copyable],[
+      AC_REQUIRE([AC_PROG_CXX])
+      AC_LANG_PUSH([C++])
+      AC_TRY_COMPILE([#include <type_traits>],
+        [return std::is_trivially_copyable<int>::value ? 1 : 0;],
+        [squid_cv_have_std_is_trivially_copyable=yes],
+        [squid_cv_have_std_is_trivially_copyable=no])
+      AC_LANG_POP
+  ])
+  SQUID_DEFINE_BOOL([HAVE_STD_IS_TRIVIALLY_COPYABLE],
+     [$squid_cv_have_std_is_trivially_copyable],
+     [Define if stdlibc support std::is_trivially_copyable])
+])
index 3ce45e1f3699cee9c83c7b3744a0a1a71ccd2381..dcdfa45cd767e877bfe40f4dabd1ea0138ab2df6 100644 (file)
@@ -2900,6 +2900,7 @@ AC_CHECK_SIZEOF(size_t)
 dnl Some C++11 types we try to use
 AX_CXX_TYPE_UNIFORM_DISTRIBUTIONS
 SQUID_CXX_STD_UNDERLYING_TYPE
+SQUID_CXX_STD_IS_TRIVIALLY_COPYABLE
 
 dnl On Solaris 9 x86, gcc may includes a "fixed" set of old system include files
 dnl that is incompatible with the updated Solaris header files.
index 95d12be7397228bd9470b623fbce442b7a875ae8..6a86396b5c5c18ff5e13e7463f50ae86ac10038b 100644 (file)
@@ -42,7 +42,6 @@ AnyP::PortCfg::PortCfg() :
     workerQueues(false),
     listenConn()
 {
-    memset(&tcp_keepalive, 0, sizeof(tcp_keepalive));
 }
 
 AnyP::PortCfg::~PortCfg()
index 7c46a85b56caffd0e88921c65c6b8dc1091786b0..5cffac33a59c40deb561ec6d6db4609ba6775a06 100644 (file)
@@ -14,6 +14,7 @@
 #include "anyp/TrafficMode.h"
 #include "base/CodeContext.h"
 #include "comm/Connection.h"
+#include "comm/Tcp.h"
 #include "sbuf/SBuf.h"
 #include "security/ServerOptions.h"
 
@@ -53,12 +54,7 @@ public:
     int disable_pmtu_discovery;
     bool workerQueues; ///< whether listening queues should be worker-specific
 
-    struct {
-        unsigned int idle;
-        unsigned int interval;
-        unsigned int timeout;
-        bool enabled;
-    } tcp_keepalive;
+    Comm::TcpKeepAlive tcp_keepalive;
 
     /**
      * The listening socket details.
index 8e1659a028fd28cc3a2b610babe3da785d35854a..7da4c82925c61cd9b7ab07e042925e5885f063d4 100644 (file)
@@ -2328,9 +2328,6 @@ httpAccept(const CommAcceptCbParams &params)
     debugs(33, 4, params.conn << ": accepted");
     fd_note(params.conn->fd, "client http connect");
 
-    if (s->tcp_keepalive.enabled)
-        commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
-
     // Socket is ready, setup the connection manager to start using it
     auto *srv = Http::NewServer(xact);
     AsyncJob::Start(srv); // usually async-calls readSomeData()
@@ -2531,10 +2528,6 @@ httpsAccept(const CommAcceptCbParams &params)
     debugs(33, 4, HERE << params.conn << " accepted, starting SSL negotiation.");
     fd_note(params.conn->fd, "client https connect");
 
-    if (s->tcp_keepalive.enabled) {
-        commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
-    }
-
     // Socket is ready, setup the connection manager to start using it
     auto *srv = Https::NewServer(xact);
     AsyncJob::Start(srv); // usually async-calls postHttpsAccept()
index 60d4f1b39f6bd5764df4ce27d45b6e43364b4fd7..dba7c60e9cabef24ba74988ec6268750242bc934 100644 (file)
@@ -1142,41 +1142,6 @@ commSetTcpNoDelay(int fd)
 
 #endif
 
-void
-commSetTcpKeepalive(int fd, int idle, int interval, int timeout)
-{
-    int on = 1;
-#ifdef TCP_KEEPCNT
-    if (timeout && interval) {
-        int count = (timeout + interval - 1) / interval;
-        if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(on)) < 0) {
-            int xerrno = errno;
-            debugs(5, DBG_IMPORTANT, MYNAME << "FD " << fd << ": " << xstrerr(xerrno));
-        }
-    }
-#endif
-#ifdef TCP_KEEPIDLE
-    if (idle) {
-        if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(on)) < 0) {
-            int xerrno = errno;
-            debugs(5, DBG_IMPORTANT, MYNAME << "FD " << fd << ": " << xstrerr(xerrno));
-        }
-    }
-#endif
-#ifdef TCP_KEEPINTVL
-    if (interval) {
-        if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(on)) < 0) {
-            int xerrno = errno;
-            debugs(5, DBG_IMPORTANT, MYNAME << "FD " << fd << ": " << xstrerr(xerrno));
-        }
-    }
-#endif
-    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) {
-        int xerrno = errno;
-        debugs(5, DBG_IMPORTANT, MYNAME << "FD " << fd << ": " << xstrerr(xerrno));
-    }
-}
-
 void
 comm_init(void)
 {
index 2e9dea96faef217f75346cf93c9874e0a987856e..c27fe0104542b78a2f27385ca35cce18f8edab84 100644 (file)
@@ -23,7 +23,6 @@ bool comm_iocallbackpending(void); /* inline candidate */
 int commSetNonBlocking(int fd);
 int commUnsetNonBlocking(int fd);
 void commSetCloseOnExec(int fd);
-void commSetTcpKeepalive(int fd, int idle, int interval, int timeout);
 void _comm_close(int fd, char const *file, int line);
 #define comm_close(x) (_comm_close((x), __FILE__, __LINE__))
 void old_comm_reset_close(int fd);
index 0c5d24e9048a039a6957aac2981f482c3e4747e3..a5c933d6082f32b504a57aa5dcf7339f72e0c095 100644 (file)
@@ -30,6 +30,8 @@ libcomm_la_SOURCES = \
        ModSelectWin32.cc \
        Read.cc \
        Read.h \
+       Tcp.cc \
+       Tcp.h \
        TcpAcceptor.cc \
        TcpAcceptor.h \
        UdpOpenDialer.h \
diff --git a/src/comm/Tcp.cc b/src/comm/Tcp.cc
new file mode 100644 (file)
index 0000000..59769a3
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+/* DEBUG: section 05    TCP Socket Functions */
+
+#include "squid.h"
+#include "comm/Tcp.h"
+#include "Debug.h"
+
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <type_traits>
+
+/// setsockopt(2) wrapper
+template <typename Option>
+static bool
+SetSocketOption(const int fd, const int level, const int optName, const Option &optValue)
+{
+#if HAVE_STD_IS_TRIVIALLY_COPYABLE
+    static_assert(std::is_trivially_copyable<Option>::value, "setsockopt() expects POD-like options");
+#endif
+    static_assert(!std::is_same<Option, bool>::value, "setsockopt() uses int to represent boolean options");
+    if (setsockopt(fd, level, optName, &optValue, sizeof(optValue)) < 0) {
+        const auto xerrno = errno;
+        debugs(5, DBG_IMPORTANT, "ERROR: setsockopt(2) failure: " << xstrerr(xerrno));
+        // TODO: Generalize to throw on errors when some callers need that.
+        return false;
+    }
+    return true;
+}
+
+/// setsockopt(2) wrapper for setting typical on/off options
+static bool
+SetBooleanSocketOption(const int fd, const int level, const int optName, const bool enable)
+{
+    const int optValue = enable ? 1 : 0;
+    return SetSocketOption(fd, level, optName, optValue);
+}
+
+void
+Comm::ApplyTcpKeepAlive(int fd, const TcpKeepAlive &cfg)
+{
+    if (!cfg.enabled)
+        return;
+
+#if defined(TCP_KEEPCNT)
+    if (cfg.timeout && cfg.interval) {
+        const int count = (cfg.timeout + cfg.interval - 1) / cfg.interval; // XXX: unsigned-to-signed conversion
+        (void)SetSocketOption(fd, IPPROTO_TCP, TCP_KEEPCNT, count);
+    }
+#endif
+#if defined(TCP_KEEPIDLE)
+    if (cfg.idle) {
+        // XXX: TCP_KEEPIDLE expects an int; cfg.idle is unsigned
+        (void)SetSocketOption(fd, IPPROTO_TCP, TCP_KEEPIDLE, cfg.idle);
+    }
+#endif
+#if defined(TCP_KEEPINTVL)
+    if (cfg.interval) {
+        // XXX: TCP_KEEPINTVL expects an int; cfg.interval is unsigned
+        (void)SetSocketOption(fd, IPPROTO_TCP, TCP_KEEPINTVL, cfg.interval);
+    }
+#endif
+    (void)SetBooleanSocketOption(fd, SOL_SOCKET, SO_KEEPALIVE, true);
+}
diff --git a/src/comm/Tcp.h b/src/comm/Tcp.h
new file mode 100644 (file)
index 0000000..94508d2
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID__SRC_COMM_TCP_H
+#define SQUID__SRC_COMM_TCP_H
+
+namespace Comm
+{
+
+/// Configuration settings for the TCP keep-alive feature
+class TcpKeepAlive
+{
+public:
+    unsigned int idle = 0;
+    unsigned int interval = 0;
+    unsigned int timeout = 0;
+    bool enabled = false;
+};
+
+/// apply configured TCP keep-alive settings to the given FD socket
+void ApplyTcpKeepAlive(int fd, const TcpKeepAlive &);
+
+} // namespace Comm
+
+#endif /* SQUID__SRC_COMM_TCP_H */
index 17639607f72b0d74d34b85cc4339f47a73172d26..0c3addfa5b2522820aa963343b59bbfbe57b531f 100644 (file)
@@ -430,6 +430,7 @@ Comm::TcpAcceptor::oldAccept(Comm::ConnectionPointer &details)
     // set socket flags
     commSetCloseOnExec(sock);
     commSetNonBlocking(sock);
+    Comm::ApplyTcpKeepAlive(sock, listenPort_->tcp_keepalive);
 
     /* IFF the socket is (tproxy) transparent, pass the flag down to allow spoofing */
     F->flags.transparent = fd_table[conn->fd].flags.transparent; // XXX: can we remove this line yet?
index 126a935067273fa46e60f0c4a8b0cc32337b4927..51ffb5a3fd4618ee2d589f018eaa236548ad5fdd 100644 (file)
@@ -23,6 +23,7 @@ namespace Comm
 
 class Connection;
 class ConnOpener;
+class TcpKeepAlive;
 
 typedef RefCount<Comm::Connection> ConnectionPointer;
 
index f97fe828f204183d0378b37dc8372a12643d398c..9a60cd415ae99925e7a20ee36d63578b961ba16a 100644 (file)
@@ -115,8 +115,10 @@ template <class Pod>
 void
 Ipc::TypedMsgHdr::getPod(Pod &pod) const
 {
+#if HAVE_STD_IS_TRIVIALLY_COPYABLE
     // TODO: Enable after fixing Ipc::SharedListenRequest::SharedListenRequest()
     //static_assert(std::is_trivially_copyable<Pod>::value, "getPod() used for a POD");
+#endif
     getFixed(&pod, sizeof(pod));
 }
 
@@ -124,8 +126,10 @@ template <class Pod>
 void
 Ipc::TypedMsgHdr::putPod(const Pod &pod)
 {
+#if HAVE_STD_IS_TRIVIALLY_COPYABLE
     // TODO: Enable after fixing Ipc::SharedListenRequest::pack()
     //static_assert(std::is_trivially_copyable<Pod>::value, "putPod() used for a POD");
+#endif
     putFixed(&pod, sizeof(pod));
 }
 
index 8b015f72d10c89838d782e9e451863ef4d88efc7..5203d8354eac40b855bee3ce268dd04790c0fbea 100644 (file)
@@ -254,9 +254,6 @@ Ftp::Server::AcceptCtrlConnection(const CommAcceptCbParams &params)
     debugs(33, 4, params.conn << ": accepted");
     fd_note(params.conn->fd, "client ftp connect");
 
-    if (s->tcp_keepalive.enabled)
-        commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
-
     AsyncJob::Start(new Server(xact));
 }
 
index 9c99de7b73ba52df66c9271466785da6e2dffa47..d2b15eb45661000dffdaaa2b3a969793a4c36e3b 100644 (file)
@@ -33,7 +33,6 @@ bool comm_iocallbackpending(void) STUB_RETVAL(false)
 int commSetNonBlocking(int fd) STUB_RETVAL(Comm::COMM_ERROR)
 int commUnsetNonBlocking(int fd) STUB_RETVAL(-1)
 void commSetCloseOnExec(int fd) STUB_NOP
-void commSetTcpKeepalive(int fd, int idle, int interval, int timeout) STUB
 void _comm_close(int fd, char const *file, int line) STUB
 void old_comm_reset_close(int fd) STUB
 void comm_reset_close(const Comm::ConnectionPointer &conn) STUB
index 7f3fe834b710424838a49fbfb24ccd830adbf1d6..cf1c2d2fc39a6e375d9e7a52c44d4c5b79225d81 100644 (file)
@@ -76,6 +76,9 @@ void Comm::TcpAcceptor::unsubscribe(const char *) STUB
 void Comm::TcpAcceptor::acceptNext() STUB
 void Comm::TcpAcceptor::notify(const Comm::Flag flag, const Comm::ConnectionPointer &) const STUB
 
+#include "comm/Tcp.h"
+void Comm::ApplyTcpKeepAlive(int, const TcpKeepAlive &) STUB
+
 #include "comm/Write.h"
 void Comm::Write(const Comm::ConnectionPointer &, const char *, int, AsyncCall::Pointer &, FREE *) STUB
 void Comm::Write(const Comm::ConnectionPointer &conn, MemBuf *mb, AsyncCall::Pointer &callback) STUB