Subject: kern/2335: 4.4BSD-Lite2 TCP persist timeout patches not in -current
To: None <gnats-bugs@NetBSD.ORG>
From: None <pete@demon.net>
List: netbsd-bugs
Date: 04/16/1996 13:15:53
>Number:         2335
>Category:       kern
>Synopsis:       4.4BSD-Lite2 TCP persist timeout patches not in -current
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue Apr 16 08:35:01 1996
>Last-Modified:
>Originator:     Pete Bentley
>Organization:
	Demon Internet
>Release:        16 April 1996
>Environment:
	
System: NetBSD chico.eng.demon.net 1.1 NetBSD 1.1 (PETE) #68: Sat Apr 13 14:18:08 BST 1996 pete@chico.eng.demon.net:/src/NetBSD/1.1/src/sys/arch/i386/compile/PETE i386


>Description:
Not all the 4.4BSD-Lite2 TCP patches, in particular the persist timeout 
patches seem to have made it into NetBSD-Current.  This means TCP sockets
can get into CLOSE_WAIT or FIN_WAIT_1 with data queued on them and neither
persist nor keepalive timers running, tieing up mbus forever and eventually
filling up the mbuf map
>How-To-Repeat:
Run a machine with many TCP connections from dial-up clients which
may disappear from the net before closing down the TCP sockets (eg a web 
server or web cache).  Note that netstat -m shows network memory in use
monotonically increaing until mb_map fills up after some period.
>Fix:
This patch makes sure that TCP send window reaches zero that the persist
timer is running, and that if enough persist probes fail the socket will be
dropped (Stevens, "TCP/IP Illustrated, Volume 3", pp196--200).


--- /src/NetBSD/current/src/sys/netinet/tcp_var.h	Wed Feb 14 12:37:03 1996
+++ tcp_var.h	Tue Apr 16 11:32:59 1996
@@ -180,6 +180,7 @@
 	u_long	tcps_timeoutdrop;	/* conn. dropped in rxmt timeout */
 	u_long	tcps_rexmttimeo;	/* retransmit timeouts */
 	u_long	tcps_persisttimeo;	/* persist timeouts */
+	u_long	tcps_persistdrop;	/* connections dropped in persist */
 	u_long	tcps_keeptimeo;		/* keepalive timeouts */
 	u_long	tcps_keepprobe;		/* keepalive probes sent */
 	u_long	tcps_keepdrops;		/* connections dropped in keepalive */
--- /src/NetBSD/current/src/sys/netinet/tcp_output.c	Wed Feb 14 12:37:02 1996
+++ tcp_output.c	Tue Apr 16 11:31:38 1996
@@ -153,14 +153,18 @@
 		 * but we haven't been called to retransmit,
 		 * len will be -1.  Otherwise, window shrank
 		 * after we sent into it.  If window shrank to 0,
-		 * cancel pending retransmit and pull snd_nxt
-		 * back to (closed) window.  We will enter persist
-		 * state below.  If the window didn't close completely,
-		 * just wait for an ACK.
+ 		 * cancel pending retransmit, pull snd_nxt back
+ 		 * to (closed) window, and set the persist timer
+ 		 * if it isn't already going.  If the window didn't
+ 		 * close completely, just wait for an ACK.
 		 */
 		len = 0;
 		if (win == 0) {
 			tp->t_timer[TCPT_REXMT] = 0;
+ 			tp->t_rxtshift = 0;
+  			tp->snd_nxt = tp->snd_una;
+ 			if (tp->t_timer[TCPT_PERSIST] == 0)
+ 				tcp_setpersist(tp);
 			tp->snd_nxt = tp->snd_una;
 		}
 	}
--- /src/NetBSD/current/src/sys/netinet/tcp_timer.c	Wed Feb 14 12:37:03 1996
+++ tcp_timer.c	Tue Apr 16 11:28:46 1996
@@ -62,6 +62,7 @@
 
 int	tcp_keepidle = TCPTV_KEEP_IDLE;
 int	tcp_keepintvl = TCPTV_KEEPINTVL;
+int	tcp_maxpersistidle = TCPTV_KEEP_IDLE;	/* max idle time in persist */
 int	tcp_maxidle;
 #endif /* TUBA_INCLUDE */
 /*
@@ -162,6 +163,8 @@
 int	tcp_backoff[TCP_MAXRXTSHIFT + 1] =
     { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
 
+int tcp_totbackoff = 511;	/* sum of tcp_backoff[] */
+
 /*
  * TCP timer processing.
  */
@@ -265,6 +268,20 @@
 	 */
 	case TCPT_PERSIST:
 		tcpstat.tcps_persisttimeo++;
+		/*
+		 * Hack: if the peer is dead/unreachable, we do not
+		 * time out if the window is closed.  After a full
+		 * backoff, drop the connection if the idle time
+		 * (no responses to probes) reaches the maximum
+		 * backoff that we would use if retransmitting.
+		 */
+		if (tp->t_rxtshift == TCP_MAXRXTSHIFT &&
+		    (tp->t_idle >= tcp_maxpersistidle ||
+		    tp->t_idle >= TCP_REXMTVAL(tp) * tcp_totbackoff)) {
+			tcpstat.tcps_persistdrop++;
+			tp = tcp_drop(tp, ETIMEDOUT);
+			break;
+		}
 		tcp_setpersist(tp);
 		tp->t_force = 1;
 		(void) tcp_output(tp);
>Audit-Trail:
>Unformatted: