From 4473d75651e6805b633349113ecea98a80491840 Mon Sep 17 00:00:00 2001 From: Tim Na Date: Tue, 12 Mar 2024 10:48:19 -0700 Subject: [PATCH] Add TCP keep-alive options to rtc::Socket Enabling Socket options on keep-alive related function that may enable clients to detect any stale connection early on. Bug: webrtc:15866 Change-Id: Ib4f15e0c933aeb6cf4fd18ff8cc708d118ea8645 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/342223 Reviewed-by: Harald Alvestrand Commit-Queue: Tim Na Cr-Commit-Position: refs/heads/main@{#41888} --- rtc_base/physical_socket_server.cc | 29 +++++++++++++++++++++++ rtc_base/socket.h | 7 +++++- rtc_base/socket_unittest.cc | 38 ++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/rtc_base/physical_socket_server.cc b/rtc_base/physical_socket_server.cc index 447e3da2ef..cd4f854aa8 100644 --- a/rtc_base/physical_socket_server.cc +++ b/rtc_base/physical_socket_server.cc @@ -782,6 +782,35 @@ int PhysicalSocket::TranslateOption(Option opt, int* slevel, int* sopt) { #endif case OPT_RTP_SENDTIME_EXTN_ID: return -1; // No logging is necessary as this not a OS socket option. + case OPT_KEEPALIVE: + *slevel = SOL_SOCKET; + *sopt = SO_KEEPALIVE; + break; + case OPT_TCP_KEEPCNT: + *slevel = IPPROTO_TCP; + *sopt = TCP_KEEPCNT; + break; + case OPT_TCP_KEEPIDLE: + *slevel = IPPROTO_TCP; +#if !defined(WEBRTC_MAC) + *sopt = TCP_KEEPIDLE; +#else + *sopt = TCP_KEEPALIVE; +#endif + break; + case OPT_TCP_KEEPINTVL: + *slevel = IPPROTO_TCP; + *sopt = TCP_KEEPINTVL; + break; + case OPT_TCP_USER_TIMEOUT: +#if defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) + *slevel = IPPROTO_TCP; + *sopt = TCP_USER_TIMEOUT; + break; +#else + RTC_LOG(LS_WARNING) << "Socket::OPT_TCP_USER_TIMEOUT not supported."; + return -1; +#endif default: RTC_DCHECK_NOTREACHED(); return -1; diff --git a/rtc_base/socket.h b/rtc_base/socket.h index 4e35406798..275294e6e3 100644 --- a/rtc_base/socket.h +++ b/rtc_base/socket.h @@ -147,7 +147,12 @@ class RTC_EXPORT Socket { // This is specific to libjingle and will be used // if SendTime option is needed at socket level. OPT_SEND_ECN, // 2-bit ECN - OPT_RECV_ECN + OPT_RECV_ECN, + OPT_KEEPALIVE, // Enable socket keep alive + OPT_TCP_KEEPCNT, // Set TCP keep alive count + OPT_TCP_KEEPIDLE, // Set TCP keep alive idle time in seconds + OPT_TCP_KEEPINTVL, // Set TCP keep alive interval in seconds + OPT_TCP_USER_TIMEOUT, // Set TCP user timeout }; virtual int GetOption(Option opt, int* value) = 0; virtual int SetOption(Option opt, int value) = 0; diff --git a/rtc_base/socket_unittest.cc b/rtc_base/socket_unittest.cc index c5e791b293..43dc1ae20e 100644 --- a/rtc_base/socket_unittest.cc +++ b/rtc_base/socket_unittest.cc @@ -1101,6 +1101,44 @@ void SocketTest::GetSetOptionsInternal(const IPAddress& loopback) { ASSERT_NE(-1, socket->GetOption(Socket::OPT_RECV_ECN, ¤t_recv_esn)); ASSERT_EQ(current_recv_esn, desired_recv_esn); #endif + + // Prepare on TCP specific options. + socket.reset(socket_factory_->CreateSocket(loopback.family(), SOCK_STREAM)); + socket->Bind(SocketAddress(loopback, 0)); + + // Check that we can set NODELAY on a TCP socket. + ASSERT_NE(-1, socket->SetOption(Socket::OPT_NODELAY, desired_nd)); + ASSERT_NE(-1, socket->GetOption(Socket::OPT_NODELAY, ¤t_nd)); + ASSERT_NE(0, current_nd); + + // Check TCP Keep Alive settings. + int current_kl, desired_kl = 1; + ASSERT_NE(-1, socket->SetOption(Socket::OPT_KEEPALIVE, desired_kl)); + ASSERT_NE(-1, socket->GetOption(Socket::OPT_KEEPALIVE, ¤t_kl)); + ASSERT_NE(0, current_kl); + + int current_kl_cnt, desired_kl_cnt = 3; + ASSERT_NE(-1, socket->SetOption(Socket::OPT_TCP_KEEPCNT, desired_kl_cnt)); + ASSERT_NE(-1, socket->GetOption(Socket::OPT_TCP_KEEPCNT, ¤t_kl_cnt)); + ASSERT_EQ(desired_kl_cnt, current_kl_cnt); + + int current_kl_idle, desired_kl_idle = 2; + ASSERT_NE(-1, socket->SetOption(Socket::OPT_TCP_KEEPIDLE, desired_kl_idle)); + ASSERT_NE(-1, socket->GetOption(Socket::OPT_TCP_KEEPIDLE, ¤t_kl_idle)); + ASSERT_EQ(desired_kl_idle, current_kl_idle); + + int current_kl_intvl, desired_kl_intvl = 2; + ASSERT_NE(-1, socket->SetOption(Socket::OPT_TCP_KEEPINTVL, desired_kl_intvl)); + ASSERT_NE(-1, + socket->GetOption(Socket::OPT_TCP_KEEPINTVL, ¤t_kl_intvl)); + ASSERT_EQ(desired_kl_intvl, current_kl_intvl); + +#if defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) + int current_ut, desired_ut = 10; + ASSERT_NE(-1, socket->SetOption(Socket::OPT_TCP_USER_TIMEOUT, desired_ut)); + ASSERT_NE(-1, socket->GetOption(Socket::OPT_TCP_USER_TIMEOUT, ¤t_ut)); + ASSERT_EQ(desired_ut, current_ut); +#endif } void SocketTest::SocketRecvTimestamp(const IPAddress& loopback) {