Cygwin: tcp: Support TCP_KEEPIDLE, TCP_KEEPCNT, TCP_KEEPINTVL
Use WSAIoctl(SIO_KEEPALIVE_VALS) on older systems. Make sure that keep-alive timeout is equivalent to TCP_KEEPIDLE + TCP_KEEPCNT * TCP_KEEPINTVL on older systems, even with TCP_KEEPCNT being a fixed value on those systems. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
0feb77c260
commit
8ccffddc91
|
@ -720,6 +720,11 @@ class fhandler_socket_inet: public fhandler_socket_wsock
|
|||
private:
|
||||
bool oobinline; /* True if option SO_OOBINLINE is set */
|
||||
bool tcp_fastopen; /* True if TCP_FASTOPEN is set on older systems */
|
||||
int tcp_keepidle; /* TCP_KEEPIDLE value in secs on older systems */
|
||||
int tcp_keepcnt; /* TCP_KEEPCNT value on older systems */
|
||||
int tcp_keepintvl; /* TCP_KEEPINTVL value in secs on older systems */
|
||||
|
||||
int set_keepalive (int keepidle, int keepcnt, int keepintvl);
|
||||
protected:
|
||||
int af_local_connect () { return 0; }
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#endif
|
||||
#include <w32api/ws2tcpip.h>
|
||||
#include <w32api/mswsock.h>
|
||||
#include <w32api/mstcpip.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <unistd.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
@ -692,7 +693,10 @@ fhandler_socket_wsock::set_socket_handle (SOCKET sock, int af, int type,
|
|||
fhandler_socket_inet::fhandler_socket_inet () :
|
||||
fhandler_socket_wsock (),
|
||||
oobinline (false),
|
||||
tcp_fastopen (false)
|
||||
tcp_fastopen (false),
|
||||
tcp_keepidle (7200), /* WinSock default */
|
||||
tcp_keepcnt (10), /* WinSock default */
|
||||
tcp_keepintvl (1) /* WinSock default */
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1572,6 +1576,63 @@ fhandler_socket_wsock::writev (const struct iovec *const iov, const int iovcnt,
|
|||
return send_internal (&wsamsg, 0);
|
||||
}
|
||||
|
||||
#define MAX_TCP_KEEPIDLE 32767
|
||||
#define MAX_TCP_KEEPCNT 255
|
||||
#define MAX_TCP_KEEPINTVL 32767
|
||||
|
||||
#define FIXED_WSOCK_TCP_KEEPCNT 10
|
||||
|
||||
int
|
||||
fhandler_socket_inet::set_keepalive (int keepidle, int keepcnt, int keepintvl)
|
||||
{
|
||||
struct tcp_keepalive tka;
|
||||
int so_keepalive = 0;
|
||||
int len = sizeof so_keepalive;
|
||||
int ret;
|
||||
DWORD dummy;
|
||||
|
||||
/* Per MSDN,
|
||||
https://docs.microsoft.com/en-us/windows/win32/winsock/sio-keepalive-vals
|
||||
the subsequent keep-alive settings in struct tcp_keepalive are only used
|
||||
if the onoff member is != 0. Request the current state of SO_KEEPALIVE,
|
||||
then set the keep-alive options with onoff set to 1. On success, if
|
||||
SO_KEEPALIVE was 0, restore to the original SO_KEEPALIVE setting. Per
|
||||
the above MSDN doc, the SIO_KEEPALIVE_VALS settings are persistent
|
||||
across switching SO_KEEPALIVE. */
|
||||
ret = ::getsockopt (get_socket (), SOL_SOCKET, SO_KEEPALIVE,
|
||||
(char *) &so_keepalive, &len);
|
||||
if (ret == SOCKET_ERROR)
|
||||
debug_printf ("getsockopt (SO_KEEPALIVE) failed, %u\n", WSAGetLastError ());
|
||||
tka.onoff = 1;
|
||||
tka.keepalivetime = keepidle * MSPERSEC;
|
||||
/* WinSock TCP_KEEPCNT is fixed. But we still want that the keep-alive
|
||||
times out after TCP_KEEPIDLE + TCP_KEEPCNT * TCP_KEEPINTVL secs.
|
||||
To that end, we set keepaliveinterval so that
|
||||
|
||||
keepaliveinterval * FIXED_WSOCK_TCP_KEEPCNT == TCP_KEEPINTVL * TCP_KEEPCNT
|
||||
|
||||
FIXME? Does that make sense?
|
||||
|
||||
Sidenote: Given the max values, the entire operation fits into an int. */
|
||||
tka.keepaliveinterval = MSPERSEC / FIXED_WSOCK_TCP_KEEPCNT * keepcnt
|
||||
* keepintvl;
|
||||
if (WSAIoctl (get_socket (), SIO_KEEPALIVE_VALS, (LPVOID) &tka, sizeof tka,
|
||||
NULL, 0, &dummy, NULL, NULL) == SOCKET_ERROR)
|
||||
{
|
||||
set_winsock_errno ();
|
||||
return -1;
|
||||
}
|
||||
if (!so_keepalive)
|
||||
{
|
||||
ret = ::setsockopt (get_socket (), SOL_SOCKET, SO_KEEPALIVE,
|
||||
(const char *) &so_keepalive, sizeof so_keepalive);
|
||||
if (ret == SOCKET_ERROR)
|
||||
debug_printf ("setsockopt (SO_KEEPALIVE) failed, %u\n",
|
||||
WSAGetLastError ());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fhandler_socket_inet::setsockopt (int level, int optname, const void *optval,
|
||||
socklen_t optlen)
|
||||
|
@ -1686,6 +1747,14 @@ fhandler_socket_inet::setsockopt (int level, int optname, const void *optval,
|
|||
break;
|
||||
|
||||
case IPPROTO_TCP:
|
||||
/* Check for stream socket early on, so we don't have to do this for
|
||||
every option. Also, WinSock returns EINVAL. */
|
||||
if (type != SOCK_STREAM)
|
||||
{
|
||||
set_errno (EOPNOTSUPP);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (optname)
|
||||
{
|
||||
case TCP_MAXSEG:
|
||||
|
@ -1698,16 +1767,59 @@ fhandler_socket_inet::setsockopt (int level, int optname, const void *optval,
|
|||
/* Fake FastOpen on older systems. */
|
||||
if (!wincap.has_tcp_fastopen ())
|
||||
{
|
||||
if (type != SOCK_STREAM)
|
||||
{
|
||||
set_errno (EOPNOTSUPP);
|
||||
return -1;
|
||||
}
|
||||
ignore = true;
|
||||
tcp_fastopen = *(int *) optval ? true : false;
|
||||
}
|
||||
break;
|
||||
|
||||
case TCP_KEEPIDLE:
|
||||
/* Handle TCP_KEEPIDLE on older systems. */
|
||||
if (!wincap.has_linux_tcp_keepalive_sockopts ())
|
||||
{
|
||||
if (*(int *) optval < 1 || *(int *) optval > MAX_TCP_KEEPIDLE)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
if (set_keepalive (*(int *) optval, tcp_keepcnt, tcp_keepintvl))
|
||||
return -1;
|
||||
ignore = true;
|
||||
tcp_keepidle = *(int *) optval;
|
||||
}
|
||||
break;
|
||||
|
||||
case TCP_KEEPCNT:
|
||||
/* Fake TCP_KEEPCNT on older systems. */
|
||||
if (!wincap.has_linux_tcp_keepalive_sockopts ())
|
||||
{
|
||||
if (*(int *) optval < 1 || *(int *) optval > MAX_TCP_KEEPCNT)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
if (set_keepalive (tcp_keepidle, *(int *) optval, tcp_keepintvl))
|
||||
return -1;
|
||||
ignore = true;
|
||||
tcp_keepcnt = *(int *) optval;
|
||||
}
|
||||
break;
|
||||
|
||||
case TCP_KEEPINTVL:
|
||||
/* Handle TCP_KEEPINTVL on older systems. */
|
||||
if (!wincap.has_linux_tcp_keepalive_sockopts ())
|
||||
{
|
||||
if (*(int *) optval < 1 || *(int *) optval > MAX_TCP_KEEPINTVL)
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
if (set_keepalive (tcp_keepidle, tcp_keepcnt, *(int *) optval))
|
||||
return -1;
|
||||
ignore = true;
|
||||
tcp_keepintvl = *(int *) optval;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1841,23 +1953,56 @@ fhandler_socket_inet::getsockopt (int level, int optname, const void *optval,
|
|||
break;
|
||||
|
||||
case IPPROTO_TCP:
|
||||
/* Check for stream socket early on, so we don't have to do this for
|
||||
every option. Also, WinSock returns EINVAL. */
|
||||
if (type != SOCK_STREAM)
|
||||
{
|
||||
set_errno (EOPNOTSUPP);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (optname)
|
||||
{
|
||||
case TCP_FASTOPEN:
|
||||
/* Fake FastOpen on older systems */
|
||||
if (!wincap.has_tcp_fastopen ())
|
||||
{
|
||||
if (type != SOCK_STREAM)
|
||||
{
|
||||
set_errno (EOPNOTSUPP);
|
||||
return -1;
|
||||
}
|
||||
*(int *) optval = tcp_fastopen ? 1 : 0;
|
||||
*optlen = sizeof (int);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TCP_KEEPIDLE:
|
||||
/* Use stored value on older systems */
|
||||
if (!wincap.has_linux_tcp_keepalive_sockopts ())
|
||||
{
|
||||
*(int *) optval = tcp_keepidle;
|
||||
*optlen = sizeof (int);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TCP_KEEPCNT:
|
||||
/* Use stored value on older systems */
|
||||
if (!wincap.has_linux_tcp_keepalive_sockopts ())
|
||||
{
|
||||
*(int *) optval = tcp_keepcnt;
|
||||
*optlen = sizeof (int);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TCP_KEEPINTVL:
|
||||
/* Use stored value on older systems */
|
||||
if (!wincap.has_linux_tcp_keepalive_sockopts ())
|
||||
{
|
||||
*(int *) optval = tcp_keepintvl;
|
||||
*optlen = sizeof (int);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -124,7 +124,10 @@ struct tcphdr {
|
|||
* User-settable options (used with setsockopt).
|
||||
*/
|
||||
#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
|
||||
#define TCP_KEEPIDLE 0x03 /* start keepalives after this period */
|
||||
#define TCP_MAXSEG 0x04 /* get maximum segment size (r/o on windows) */
|
||||
#define TCP_FASTOPEN 0x0f /* enable FastOpen on listeners */
|
||||
#define TCP_KEEPCNT 0x10 /* number of keepalives before death */
|
||||
#define TCP_KEEPINTVL 0x11 /* interval between keepalives */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -47,6 +47,7 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = {
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:false,
|
||||
has_tcp_fastopen:false,
|
||||
has_linux_tcp_keepalive_sockopts:false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -79,6 +80,7 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:false,
|
||||
has_tcp_fastopen:false,
|
||||
has_linux_tcp_keepalive_sockopts:false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -111,6 +113,7 @@ wincaps wincap_8 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:false,
|
||||
has_tcp_fastopen:false,
|
||||
has_linux_tcp_keepalive_sockopts:false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -143,6 +146,7 @@ wincaps wincap_8_1 __attribute__((section (".cygwin_dll_common"), shared)) = {
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:false,
|
||||
has_tcp_fastopen:false,
|
||||
has_linux_tcp_keepalive_sockopts:false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -175,6 +179,7 @@ wincaps wincap_10_1507 __attribute__((section (".cygwin_dll_common"), shared))
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:false,
|
||||
has_tcp_fastopen:false,
|
||||
has_linux_tcp_keepalive_sockopts:false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -207,6 +212,7 @@ wincaps wincap_10_1607 __attribute__((section (".cygwin_dll_common"), shared))
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:false,
|
||||
has_tcp_fastopen:true,
|
||||
has_linux_tcp_keepalive_sockopts:false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -239,6 +245,7 @@ wincaps wincap_10_1703 __attribute__((section (".cygwin_dll_common"), shared)) =
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:false,
|
||||
has_tcp_fastopen:true,
|
||||
has_linux_tcp_keepalive_sockopts:false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -271,6 +278,7 @@ wincaps wincap_10_1709 __attribute__((section (".cygwin_dll_common"), shared)) =
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:false,
|
||||
has_tcp_fastopen:true,
|
||||
has_linux_tcp_keepalive_sockopts:true,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -303,6 +311,7 @@ wincaps wincap_10_1803 __attribute__((section (".cygwin_dll_common"), shared)) =
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:true,
|
||||
has_tcp_fastopen:true,
|
||||
has_linux_tcp_keepalive_sockopts:true,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -335,6 +344,7 @@ wincaps wincap_10_1809 __attribute__((section (".cygwin_dll_common"), shared)) =
|
|||
has_con_esc_rep:false,
|
||||
has_extended_mem_api:true,
|
||||
has_tcp_fastopen:true,
|
||||
has_linux_tcp_keepalive_sockopts:true,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -367,6 +377,7 @@ wincaps wincap_10_1903 __attribute__((section (".cygwin_dll_common"), shared)) =
|
|||
has_con_esc_rep:true,
|
||||
has_extended_mem_api:true,
|
||||
has_tcp_fastopen:true,
|
||||
has_linux_tcp_keepalive_sockopts:true,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ struct wincaps
|
|||
unsigned has_con_esc_rep : 1;
|
||||
unsigned has_extended_mem_api : 1;
|
||||
unsigned has_tcp_fastopen : 1;
|
||||
unsigned has_linux_tcp_keepalive_sockopts : 1;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -105,6 +106,7 @@ public:
|
|||
bool IMPLEMENT (has_con_esc_rep)
|
||||
bool IMPLEMENT (has_extended_mem_api)
|
||||
bool IMPLEMENT (has_tcp_fastopen)
|
||||
bool IMPLEMENT (has_linux_tcp_keepalive_sockopts)
|
||||
|
||||
void disable_case_sensitive_dirs ()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue