Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
8250521: Configure initial RTO to use minimal retry for loopback conn…
…ections on Windows

Reviewed-by: alanb
  • Loading branch information
Nikola Grcevski authored and Alan Bateman committed Aug 10, 2020
1 parent 14bc361 commit ff76801f488896cad344d1854a3a65e954c0f680
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 4 deletions.
@@ -770,6 +770,57 @@ NET_EnableFastTcpLoopback(int fd) {
return result == SOCKET_ERROR ? WSAGetLastError() : 0;
}

int
IsWindows10RS3OrGreater() {
OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
DWORDLONG const cond_mask = VerSetConditionMask(
VerSetConditionMask(
VerSetConditionMask(
0, VER_MAJORVERSION, VER_GREATER_EQUAL),
VER_MINORVERSION, VER_GREATER_EQUAL),
VER_BUILDNUMBER, VER_GREATER_EQUAL);

osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_WIN10);
osvi.dwMinorVersion = LOBYTE(_WIN32_WINNT_WIN10);
osvi.dwBuildNumber = 16299; // RS3 (Redstone 3)

return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, cond_mask) != 0;
}

/**
* Shortens the default Windows socket
* connect timeout. Recommended for usage
* on the loopback adapter only.
*/
JNIEXPORT jint JNICALL
NET_EnableFastTcpLoopbackConnect(int fd) {
TCP_INITIAL_RTO_PARAMETERS rto = {
TCP_INITIAL_RTO_UNSPECIFIED_RTT, // Use the default or overriden by the Administrator
1 // Minimum possible value before Windows 10 RS3
};

/**
* In Windows 10 RS3+ we can use the no retransmissions flag to
* completely remove the timeout delay, which is fixed to 500ms
* if Windows receives RST when the destination port is not open.
*/
if (IsWindows10RS3OrGreater()) {
rto.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
}

DWORD result_byte_count = -1;
int result = WSAIoctl(fd, // descriptor identifying a socket
SIO_TCP_INITIAL_RTO, // dwIoControlCode
&rto, // pointer to TCP_INITIAL_RTO_PARAMETERS structure
sizeof(rto), // size, in bytes, of the input buffer
NULL, // pointer to output buffer
0, // size of output buffer
&result_byte_count, // number of bytes returned
NULL, // OVERLAPPED structure
NULL); // completion routine
return (result == SOCKET_ERROR) ? WSAGetLastError() : 0;
}

/**
* See net_util.h for documentation
*/
@@ -26,6 +26,7 @@
#include <WS2tcpip.h>
#include <iphlpapi.h>
#include <icmpapi.h>
#include <mstcpip.h>

/* used to disable connection reset messages on Windows XP */
#ifndef SIO_UDP_CONNRESET
@@ -86,10 +87,29 @@ struct ipv6bind {

#define GET_PORT(X) ((X)->sa.sa_family == AF_INET ? (X)->sa4.sin_port : (X)->sa6.sin6_port)

/**
* With dual socket implementation the
* IPv4 addresseses might be mapped as IPv6.
* The IPv4 loopback adapter address will
* be mapped as the following IPv6 ::ffff:127.0.0.1.
* For example, this is done by NET_InetAddressToSockaddr.
*/
#define IN6_IS_ADDR_V4MAPPED_LOOPBACK(x) ( \
(((x)->s6_words[0] == 0) && \
((x)->s6_words[1] == 0) && \
((x)->s6_words[2] == 0) && \
((x)->s6_words[3] == 0) && \
((x)->s6_words[4] == 0) && \
((x)->s6_words[5] == 0xFFFF) && \
((x)->s6_words[6] == 0x007F) && \
((x)->s6_words[7] == 0x0100)) \
)

#define IS_LOOPBACK_ADDRESS(x) ( \
((x)->sa.sa_family == AF_INET) ? \
(ntohl((x)->sa4.sin_addr.s_addr) == INADDR_LOOPBACK) : \
(IN6ADDR_ISLOOPBACK(x)) \
((IN6_IS_ADDR_LOOPBACK(&(x)->sa6.sin6_addr)) || \
(IN6_IS_ADDR_V4MAPPED_LOOPBACK(&(x)->sa6.sin6_addr))) \
)

JNIEXPORT int JNICALL NET_SocketClose(int fd);
@@ -119,6 +139,8 @@ JNIEXPORT int JNICALL NET_BindV6(struct ipv6bind *b, jboolean exclBind);
JNIEXPORT int JNICALL NET_WinBind(int s, SOCKETADDRESS *sa, int len,
jboolean exclBind);

JNIEXPORT jint JNICALL NET_EnableFastTcpLoopbackConnect(int fd);

/* XP versions of the native routines */

JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0_XP
@@ -226,13 +226,25 @@ Java_sun_nio_ch_Net_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, job
{
SOCKETADDRESS sa;
int rv;
int so_rv;
int sa_len = 0;
SOCKET s = (SOCKET)fdval(env, fdo);
int type = 0, optlen = sizeof(type);

if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len, preferIPv6) != 0) {
return IOS_THROWN;
}

so_rv = getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&type, &optlen);

/**
* Windows has a very long socket connect timeout of 2 seconds.
* If it's the loopback adapter we can shorten the wait interval.
*/
if (so_rv == 0 && type == SOCK_STREAM && IS_LOOPBACK_ADDRESS(&sa)) {
NET_EnableFastTcpLoopbackConnect((jint)s);
}

rv = connect(s, &sa.sa, sa_len);
if (rv != 0) {
int err = WSAGetLastError();
@@ -243,9 +255,7 @@ Java_sun_nio_ch_Net_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, job
return IOS_THROWN;
} else {
/* Enable WSAECONNRESET errors when a UDP socket is connected */
int type = 0, optlen = sizeof(type);
rv = getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&type, &optlen);
if (rv == 0 && type == SOCK_DGRAM) {
if (so_rv == 0 && type == SOCK_DGRAM) {
setConnectionReset(s, TRUE);
}
}

0 comments on commit ff76801

Please sign in to comment.