[newlib-cygwin] Cygwin: tcp: Support TCP_KEEPIDLE, TCP_KEEPCNT, TCP_KEEPINTVL

Corinna Vinschen corinna@sourceware.org
Wed Jul 1 18:32:33 GMT 2020


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=8ccffddc91942dcb4a6417c13b46c5ab5d81a5d9

commit 8ccffddc91942dcb4a6417c13b46c5ab5d81a5d9
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Wed Jul 1 20:27:10 2020 +0200

    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>

Diff:
---
 winsup/cygwin/fhandler.h              |   5 ++
 winsup/cygwin/fhandler_socket_inet.cc | 163 ++++++++++++++++++++++++++++++++--
 winsup/cygwin/include/netinet/tcp.h   |   9 +-
 winsup/cygwin/wincap.cc               |  11 +++
 winsup/cygwin/wincap.h                |   2 +
 5 files changed, 178 insertions(+), 12 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 57f282105..24184dacc 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -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; }
 
diff --git a/winsup/cygwin/fhandler_socket_inet.cc b/winsup/cygwin/fhandler_socket_inet.cc
index 1e837d72c..9bdaece5a 100644
--- a/winsup/cygwin/fhandler_socket_inet.cc
+++ b/winsup/cygwin/fhandler_socket_inet.cc
@@ -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,13 +1767,56 @@ 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)
+	      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 (EOPNOTSUPP);
+		  set_errno (EINVAL);
 		  return -1;
 		}
+	      if (set_keepalive (*(int *) optval, tcp_keepcnt, tcp_keepintvl))
+		return -1;
 	      ignore = true;
-	      tcp_fastopen = *(int *) optval ? true : false;
+	      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;
 
@@ -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;
 	}
diff --git a/winsup/cygwin/include/netinet/tcp.h b/winsup/cygwin/include/netinet/tcp.h
index ab1cd12bf..9c2e90eaa 100644
--- a/winsup/cygwin/include/netinet/tcp.h
+++ b/winsup/cygwin/include/netinet/tcp.h
@@ -123,8 +123,11 @@ struct tcphdr {
 /*
  * User-settable options (used with setsockopt).
  */
-#define TCP_NODELAY     0x01    /* don't delay send to coalesce packets */
-#define TCP_MAXSEG      0x04    /* get maximum segment size (r/o on windows) */
-#define TCP_FASTOPEN    0x0f    /* enable FastOpen on listeners */
+#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
diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc
index be6d71d12..323c5a368 100644
--- a/winsup/cygwin/wincap.cc
+++ b/winsup/cygwin/wincap.cc
@@ -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,
   },
 };
 
diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h
index 54a880af7..26c4c01ef 100644
--- a/winsup/cygwin/wincap.h
+++ b/winsup/cygwin/wincap.h
@@ -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 ()
   {


More information about the Cygwin-cvs mailing list