[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Anti-idle in OpenSSH client?



On Fri, Apr 04, 2003 at 07:58:26PM -0600, mandar@webchat.chatsystems.com wrote:
> KeepAlive doesn't do the trick  unfortunately - it merely sets
> SO_KEEPALIVE on the socket, and is also not configurable. It's
> actually meant as a way for ssh to know when the underlying
> network connection to the remote server has gone away, when set.
> 
> I haven't yet been able to find the debian null packet patch
> that was previously reported on this list...I found some mention at:
> 
> http://lists.debian.org/debian-user/2002/debian-user-200207/msg00255.html
> 
> 
> but haven't been able to track down this patch...

Here it is. It was originally written by Richard Kettlewell, with
documentation by Matthew Vernon. I've hacked it out of the Debian patch,
I think correctly, but it would probably need review. :)

--- openssh-3.6.1p1.orig/clientloop.c
+++ openssh-3.6.1p1/clientloop.c
@@ -317,10 +317,14 @@
  * one of the file descriptors).
  */
 
-static void
+static int
 client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
     int *maxfdp, int *nallocp, int rekeying)
 {
+	struct timeval tv, *tvp;
+	int n;
+	extern Options options;
+
 	/* Add any selections by the channel mechanism. */
 	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
 
@@ -349,7 +353,7 @@
 			/* clear mask since we did not call select() */
 			memset(*readsetp, 0, *nallocp);
 			memset(*writesetp, 0, *nallocp);
-			return;
+			return 0;
 		} else {
 			FD_SET(connection_in, *readsetp);
 		}
@@ -368,7 +372,21 @@
 	 * SSH_MSG_IGNORE packet when the timeout expires.
 	 */
 
-	if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {
+	/*
+	 * We don't do the 'random' bit, but we want periodic ignored
+	 * message anyway, so as to notice when the other ends TCP
+	 * has given up during an outage.
+	 */
+
+	if (options.protocolkeepalives > 0) {
+	        tvp = &tv;
+		tv.tv_sec = options.protocolkeepalives;
+		tv.tv_usec = 0;
+	} else
+	        tvp = 0;
+
+	n = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
+	if (n < 0) {
 		char buf[100];
 
 		/*
@@ -380,12 +398,13 @@
 		memset(*writesetp, 0, *nallocp);
 
 		if (errno == EINTR)
-			return;
+			return 0;
 		/* Note: we might still have data in the buffers. */
 		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
 		buffer_append(&stderr_buffer, buf, strlen(buf));
 		quit_pending = 1;
 	}
+	return n == 0;
 }
 
 static void
@@ -846,6 +865,7 @@
 {
 	fd_set *readset = NULL, *writeset = NULL;
 	double start_time, total_time;
+	int timed_out;
 	int max_fd = 0, max_fd2 = 0, len, rekeying = 0, nalloc = 0;
 	char buf[100];
 
@@ -959,7 +979,7 @@
 		 * available on one of the descriptors).
 		 */
 		max_fd2 = max_fd;
-		client_wait_until_can_do_something(&readset, &writeset,
+		timed_out = client_wait_until_can_do_something(&readset, &writeset,
 		    &max_fd2, &nalloc, rekeying);
 
 		if (quit_pending)
@@ -983,6 +1003,21 @@
 		if (quit_pending)
 			break;
 
+		if(timed_out) {
+		        /*
+			 * Nothing is happening, so synthesize some
+			 * bogus activity
+			 */
+		        packet_start(compat20
+				     ? SSH2_MSG_IGNORE
+				     : SSH_MSG_IGNORE);
+			packet_put_cstring("");
+			packet_send();
+			if (FD_ISSET(connection_out, writeset))
+			        packet_write_poll();
+			continue;
+		}
+
 		if (!compat20) {
 			/* Buffer data from stdin */
 			client_process_input(readset);
--- openssh-3.6.1p1.orig/readconf.c
+++ openssh-3.6.1p1/readconf.c
@@ -81,6 +81,7 @@
      RhostsRSAAuthentication yes
      StrictHostKeyChecking yes
      KeepAlives no
+     ProtocolKeepAlives 0
      IdentityFile ~/.ssh/identity
      Port 22
      EscapeChar ~
@@ -115,6 +116,7 @@
 	oHostKeyAlgorithms, oBindAddress, oSmartcardDevice,
 	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
 	oEnableSSHKeysign,
+	oProtocolKeepAlives,
 	oDeprecated
 } OpCodes;
 
@@ -188,6 +190,7 @@
 	{ "clearallforwardings", oClearAllForwardings },
 	{ "enablesshkeysign", oEnableSSHKeysign },
 	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
+	{ "protocolkeepalives", oProtocolKeepAlives },
 	{ NULL, oBadOption }
 };
 
@@ -415,6 +418,10 @@
 		intptr = &options->no_host_authentication_for_localhost;
 		goto parse_flag;
 
+	case oProtocolKeepAlives:
+	        intptr = &options->protocolkeepalives;
+		goto parse_int;
+
 	case oNumberOfPasswordPrompts:
 		intptr = &options->number_of_password_prompts;
 		goto parse_int;
@@ -767,6 +774,7 @@
 	options->strict_host_key_checking = -1;
 	options->compression = -1;
 	options->keepalives = -1;
+	options->protocolkeepalives = -1;
 	options->compression_level = -1;
 	options->port = -1;
 	options->connection_attempts = -1;
@@ -855,6 +863,10 @@
 		options->compression = 0;
 	if (options->keepalives == -1)
 		options->keepalives = 1;
+	if (options->protocolkeepalives == -1){
+	  if (options->batch_mode == 1) /*in batch mode, default is 5mins */
+	        options->protocolkeepalives = 300;
+	  else  options->protocolkeepalives = 0;}
 	if (options->compression_level == -1)
 		options->compression_level = 6;
 	if (options->port == -1)
--- openssh-3.6.1p1.orig/readconf.h
+++ openssh-3.6.1p1/readconf.h
@@ -61,6 +61,7 @@
 	int     compression_level;	/* Compression level 1 (fast) to 9
 					 * (best). */
 	int     keepalives;	/* Set SO_KEEPALIVE. */
+        int     protocolkeepalives; /* ssh-level keepalives */
 	LogLevel log_level;	/* Level for logging. */
 
 	int     port;		/* Port to connect. */
--- openssh-3.6.1p1.orig/ssh_config.5
+++ openssh-3.6.1p1/ssh_config.5
@@ -126,8 +126,13 @@
 If set to
 .Dq yes ,
 passphrase/password querying will be disabled.
+In addition, the 
+.Cm ProtocolKeepAlives 
+option will both be set to 300 seconds by default.
 This option is useful in scripts and other batch jobs where no user
-is present to supply the password.
+is present to supply the password,
+and where it is desirable to detect a
+broken network swiftly.
 The argument must be
 .Dq yes
 or
@@ -357,7 +362,12 @@
 Specifies whether the system should send TCP keepalive messages to the
 other side.
 If they are sent, death of the connection or crash of one
-of the machines will be properly noticed.
+of the machines will be properly noticed.  This option only uses TCP
+keepalives (as opposed to using ssh level keepalives), so takes a long
+time to notice when the connection dies.  As such, you probably want
+the
+.Cm ProtocolKeepAlives
+option as well.
 However, this means that
 connections will die if the route is down temporarily, and some people
 find it annoying.
@@ -457,6 +467,13 @@
 .Nm ssh
 tries version 2 and falls back to version 1
 if version 2 is not available.
+.It Cm ProtocolKeepAlives
+Specifies the interval in seconds at which IGNORE packets will be sent to
+the server during idle periods. Use this option in scripts to detect
+when the network fails. The argument must be an integer. The default
+is 0 (disabled), or 300 if the 
+.Cm BatchMode
+option is set.
 .It Cm ProxyCommand
 Specifies the command to use to connect to the server.
 The command

-- 
Colin Watson                                  [cjwatson@flatline.org.uk]



Reply to: