summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2017-08-28 18:09:22 -0400
committerSara Golemon <sara.golemon@mongodb.com>2017-09-01 16:53:27 -0400
commitbd153cba11ab82ffbf8d2bc0b801eb808369eb9e (patch)
tree9e0f9cca88d27190ac0ba03019fdd97fddb8b75a
parentc30149da2ea0af52a1532017550431ac356f04f3 (diff)
downloadmongo-bd153cba11ab82ffbf8d2bc0b801eb808369eb9e.tar.gz
SERVER-29341 Set KeepAlive params on windows and mac
-rw-r--r--src/mongo/transport/session_asio.h30
-rw-r--r--src/mongo/util/SConscript6
-rw-r--r--src/mongo/util/net/SConscript1
-rw-r--r--src/mongo/util/net/sock.cpp139
-rw-r--r--src/mongo/util/net/sock.h3
-rw-r--r--src/mongo/util/ntservice.cpp6
-rw-r--r--src/mongo/util/winutil.cpp64
-rw-r--r--src/mongo/util/winutil.h29
8 files changed, 207 insertions, 71 deletions
diff --git a/src/mongo/transport/session_asio.h b/src/mongo/transport/session_asio.h
index 2a478dd966e..1a84f584419 100644
--- a/src/mongo/transport/session_asio.h
+++ b/src/mongo/transport/session_asio.h
@@ -34,6 +34,7 @@
#include "mongo/config.h"
#include "mongo/transport/asio_utils.h"
#include "mongo/transport/transport_layer_asio.h"
+#include "mongo/util/net/sock.h"
#ifdef MONGO_CONFIG_SSL
#include "mongo/util/net/ssl_manager.h"
#include "mongo/util/net/ssl_types.h"
@@ -114,34 +115,7 @@ public:
if (family == AF_INET || family == AF_INET6) {
_socket.set_option(asio::ip::tcp::no_delay(true));
_socket.set_option(asio::socket_base::keep_alive(true));
-#ifdef __linux__
- const auto sock = _socket.native_handle();
- // On linux the default keep alive value may be very high - say an hour.
- // Here we set it to a minimum of 5 minutes if the default value was over five minutes.
- // See SERVER-3604.
- int val = 1;
- socklen_t len = sizeof(val);
- if (getsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&val, &len))
- error() << "can't get TCP_KEEPIDLE: " << errnoWithDescription();
-
- if (val > 300) {
- val = 300;
- if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&val, sizeof(val))) {
- error() << "can't set TCP_KEEPIDLE: " << errnoWithDescription();
- }
- }
-
- len = sizeof(val); // just in case it changed
- if (getsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&val, &len))
- error() << "can't get TCP_KEEPINTVL: " << errnoWithDescription();
-
- if (val > 300) {
- val = 300;
- if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&val, sizeof(val))) {
- error() << "can't set TCP_KEEPINTVL: " << errnoWithDescription();
- }
- }
-#endif
+ setSocketKeepAliveParams(_socket.native_handle());
}
_local = endpointToHostAndPort(_socket.local_endpoint());
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript
index 856d15ae6e1..6e9416443ff 100644
--- a/src/mongo/util/SConscript
+++ b/src/mongo/util/SConscript
@@ -385,6 +385,12 @@ if env['MONGO_ALLOCATOR'] == 'tcmalloc':
],
)
+env.Library(
+ target='winutil',
+ source=[
+ 'winutil.cpp',
+ ],
+)
env.Library(
target='ntservice',
diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript
index 69c6a3074fe..a57710a3f1c 100644
--- a/src/mongo/util/net/SConscript
+++ b/src/mongo/util/net/SConscript
@@ -35,6 +35,7 @@ env.Library(
'$BUILD_DIR/mongo/util/background_job',
'$BUILD_DIR/mongo/util/fail_point',
'$BUILD_DIR/mongo/util/options_parser/options_parser',
+ '$BUILD_DIR/mongo/util/winutil',
],
)
diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp
index 35e614da95d..4b8665895ff 100644
--- a/src/mongo/util/net/sock.cpp
+++ b/src/mongo/util/net/sock.cpp
@@ -49,6 +49,10 @@
#if defined(__OpenBSD__)
#include <sys/uio.h>
#endif
+#else
+#include <mstcpip.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
#endif
#include "mongo/config.h"
@@ -65,6 +69,7 @@
#include "mongo/util/net/socket_exception.h"
#include "mongo/util/net/ssl_manager.h"
#include "mongo/util/quick_exit.h"
+#include "mongo/util/winutil.h"
namespace mongo {
@@ -105,6 +110,7 @@ void networkWarnWithDescription(const Socket& socket, StringData call, int error
const double kMaxConnectTimeoutMS = 5000;
+
} // namespace
static bool ipv6 = false;
@@ -140,23 +146,104 @@ void setSockTimeouts(int sock, double secs) {
#endif
}
-#if defined(_WIN32)
-void disableNagle(int sock) {
- int x = 1;
- if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&x, sizeof(x)))
- error() << "disableNagle failed: " << errnoWithDescription();
- if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&x, sizeof(x)))
- error() << "SO_KEEPALIVE failed: " << errnoWithDescription();
-}
+#ifdef _WIN32
+#ifdef _UNICODE
+#define X_STR_CONST(str) (L##str)
#else
+#define X_STR_CONST(str) (str)
+#endif
+const CString kKeepAliveGroup(
+ X_STR_CONST("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"));
+const CString kKeepAliveTime(X_STR_CONST("KeepAliveTime"));
+const CString kKeepAliveInterval(X_STR_CONST("KeepAliveInterval"));
+#undef X_STR_CONST
+#endif
-void disableNagle(int sock) {
- int x = 1;
+void setSocketKeepAliveParams(int sock,
+ unsigned int maxKeepIdleSecs,
+ unsigned int maxKeepIntvlSecs) {
+#ifdef _WIN32
+ // Defaults per MSDN when registry key does not exist.
+ // Expressed in seconds here to be consistent with posix,
+ // though Windows uses milliseconds.
+ const DWORD kWindowsKeepAliveTimeSecsDefault = 2 * 60 * 60;
+ const DWORD kWindowsKeepAliveIntervalSecsDefault = 1;
+
+ const auto getKey = [](const CString& key, DWORD default_value) {
+ auto withval = windows::getDWORDRegistryKey(kKeepAliveGroup, key);
+ if (withval.isOK()) {
+ auto val = withval.getValue();
+ // Return seconds
+ return val ? (val.get() / 1000) : default_value;
+ }
+ error() << "can't get KeepAlive parameter: " << withval.getStatus();
+ return default_value;
+ };
+
+ const auto keepIdleSecs = getKey(kKeepAliveTime, kWindowsKeepAliveTimeSecsDefault);
+ const auto keepIntvlSecs = getKey(kKeepAliveInterval, kWindowsKeepAliveIntervalSecsDefault);
+
+ if ((keepIdleSecs > maxKeepIdleSecs) || (keepIntvlSecs > maxKeepIntvlSecs)) {
+ DWORD sent = 0;
+ struct tcp_keepalive keepalive;
+ keepalive.onoff = TRUE;
+ keepalive.keepalivetime = std::min<DWORD>(keepIdleSecs, maxKeepIdleSecs) * 1000;
+ keepalive.keepaliveinterval = std::min<DWORD>(keepIntvlSecs, maxKeepIntvlSecs) * 1000;
+ if (WSAIoctl(sock,
+ SIO_KEEPALIVE_VALS,
+ &keepalive,
+ sizeof(keepalive),
+ nullptr,
+ 0,
+ &sent,
+ nullptr,
+ nullptr)) {
+ error() << "failed setting keepalive values: " << WSAGetLastError();
+ }
+ }
+#elif defined(__APPLE__) || defined(__linux__)
+ const auto updateSockOpt =
+ [sock](int level, int optnum, unsigned int maxval, StringData optname) {
+ unsigned int optval = 1;
+ socklen_t len = sizeof(optval);
+
+ if (getsockopt(sock, level, optnum, (char*)&optval, &len)) {
+ error() << "can't get " << optname << ": " << errnoWithDescription();
+ }
+
+ if (optval > maxval) {
+ optval = maxval;
+ if (setsockopt(sock, level, optnum, (char*)&optval, sizeof(optval))) {
+ error() << "can't set " << optname << ": " << errnoWithDescription();
+ }
+ }
+ };
+
+#ifdef __APPLE__
+ updateSockOpt(IPPROTO_TCP, TCP_KEEPALIVE, maxKeepIdleSecs, "TCP_KEEPALIVE");
+#endif
+#ifdef __linux__
#ifdef SOL_TCP
- int level = SOL_TCP;
+ const int level = SOL_TCP;
+#else
+ const int level = SOL_SOCKET;
+#endif
+ updateSockOpt(level, TCP_KEEPIDLE, maxKeepIdleSecs, "TCP_KEEPIDLE");
+ updateSockOpt(level, TCP_KEEPINTVL, maxKeepIntvlSecs, "TCP_KEEPINTVL");
+#endif
+
+#endif
+}
+
+void disableNagle(int sock) {
+ int x = 1;
+#ifdef _WIN32
+ const int level = IPPROTO_TCP;
+#elif defined(SOL_TCP)
+ const int level = SOL_TCP;
#else
- int level = SOL_SOCKET;
+ const int level = SOL_SOCKET;
#endif
if (setsockopt(sock, level, TCP_NODELAY, (char*)&x, sizeof(x)))
@@ -165,34 +252,10 @@ void disableNagle(int sock) {
#ifdef SO_KEEPALIVE
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&x, sizeof(x)))
error() << "SO_KEEPALIVE failed: " << errnoWithDescription();
-
-#ifdef __linux__
- socklen_t len = sizeof(x);
- if (getsockopt(sock, level, TCP_KEEPIDLE, (char*)&x, &len))
- error() << "can't get TCP_KEEPIDLE: " << errnoWithDescription();
-
- if (x > 300) {
- x = 300;
- if (setsockopt(sock, level, TCP_KEEPIDLE, (char*)&x, sizeof(x))) {
- error() << "can't set TCP_KEEPIDLE: " << errnoWithDescription();
- }
- }
-
- len = sizeof(x); // just in case it changed
- if (getsockopt(sock, level, TCP_KEEPINTVL, (char*)&x, &len))
- error() << "can't get TCP_KEEPINTVL: " << errnoWithDescription();
-
- if (x > 300) {
- x = 300;
- if (setsockopt(sock, level, TCP_KEEPINTVL, (char*)&x, sizeof(x))) {
- error() << "can't set TCP_KEEPINTVL: " << errnoWithDescription();
- }
- }
-#endif
#endif
-}
-#endif
+ setSocketKeepAliveParams(sock);
+}
// --- SockAddr
diff --git a/src/mongo/util/net/sock.h b/src/mongo/util/net/sock.h
index 18fba961c6d..c01c9bf3a18 100644
--- a/src/mongo/util/net/sock.h
+++ b/src/mongo/util/net/sock.h
@@ -68,6 +68,9 @@ struct SSLPeerInfo;
extern const int portSendFlags;
extern const int portRecvFlags;
+void setSocketKeepAliveParams(int sock,
+ unsigned int maxKeepIdleSecs = 300,
+ unsigned int maxKeepIntvlSecs = 300);
void disableNagle(int sock);
#if !defined(_WIN32)
diff --git a/src/mongo/util/ntservice.cpp b/src/mongo/util/ntservice.cpp
index edcf716e1a6..b0db46aecf3 100644
--- a/src/mongo/util/ntservice.cpp
+++ b/src/mongo/util/ntservice.cpp
@@ -287,7 +287,7 @@ void installServiceOrDie(const wstring& serviceName,
SC_HANDLE schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (schSCManager == NULL) {
DWORD err = ::GetLastError();
- log() << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err);
+ log() << "Error connecting to the Service Control Manager: " << windows::GetErrMsg(err);
quickExit(EXIT_NTSERVICE_ERROR);
}
@@ -336,7 +336,7 @@ void installServiceOrDie(const wstring& serviceName,
NULL); // user account password
if (schService == NULL) {
DWORD err = ::GetLastError();
- log() << "Error creating service: " << GetWinErrMsg(err);
+ log() << "Error creating service: " << windows::GetErrMsg(err);
::CloseServiceHandle(schSCManager);
quickExit(EXIT_NTSERVICE_ERROR);
}
@@ -441,7 +441,7 @@ void removeServiceOrDie(const wstring& serviceName) {
SC_HANDLE schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (schSCManager == NULL) {
DWORD err = ::GetLastError();
- log() << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err);
+ log() << "Error connecting to the Service Control Manager: " << windows::GetErrMsg(err);
quickExit(EXIT_NTSERVICE_ERROR);
}
diff --git a/src/mongo/util/winutil.cpp b/src/mongo/util/winutil.cpp
new file mode 100644
index 00000000000..9d82a029b1d
--- /dev/null
+++ b/src/mongo/util/winutil.cpp
@@ -0,0 +1,64 @@
+// winutil.cpp
+
+/* Copyright 2017 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#if defined(_WIN32)
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/util/winutil.h"
+
+#include "mongo/base/error_codes.h"
+#include "mongo/base/status.h"
+
+namespace mongo {
+
+StatusWith<boost::optional<DWORD>> windows::getDWORDRegistryKey(const CString& group,
+ const CString& key) {
+ CRegKey regkey;
+ if (ERROR_SUCCESS != regkey.Open(HKEY_LOCAL_MACHINE, group, KEY_READ)) {
+ return Status(ErrorCodes::InternalError, "Unable to access windows registry");
+ }
+
+ DWORD val;
+ const auto res = regkey.QueryDWORDValue(key, val);
+ if (ERROR_INVALID_DATA == res) {
+ return Status(ErrorCodes::TypeMismatch,
+ "Invalid data type in windows registry, expected DWORD");
+ }
+
+ if (ERROR_SUCCESS != res) {
+ return boost::none;
+ }
+
+ return val;
+}
+
+} // namespace mongo
+
+#endif
diff --git a/src/mongo/util/winutil.h b/src/mongo/util/winutil.h
index a49fcc2f406..a9138e77113 100644
--- a/src/mongo/util/winutil.h
+++ b/src/mongo/util/winutil.h
@@ -33,11 +33,19 @@
#if defined(_WIN32)
#include "text.h"
+#include <atlbase.h>
+#include <atlstr.h>
+#include <boost/optional.hpp>
+#include <sstream>
+#include <string>
#include <windows.h>
+#include <mongo/base/status_with.h>
+
namespace mongo {
+namespace windows {
-inline std::string GetWinErrMsg(DWORD err) {
+inline std::string GetErrMsg(DWORD err) {
LPTSTR errMsg;
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
@@ -56,6 +64,23 @@ inline std::string GetWinErrMsg(DWORD err) {
return output.str();
}
-}
+
+/**
+ * Retrieve a DWORD value from the Local Machine Windows Registry for element:
+ * group\key
+ * e.g. HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\KeepAliveTime
+ *
+ * On success, returns:
+ * boost::none if the key does not exist.
+ * The value read from the registry.
+ *
+ * On failure, returns:
+ * ErrorCodes::InternalError - Unable to access the registry group.
+ * ErrorCodes::TypeMismatch - Key exists, but is of the wrong type.
+ */
+StatusWith<boost::optional<DWORD>> getDWORDRegistryKey(const CString& group, const CString& key);
+
+} // namespace windows
+} // namespace mongo
#endif