[net.bugs.4bsd] Problems with 4.2 BSD TCP and half-open connections

thomson@uthub.UUCP (Brian Thomson) (07/08/85)

Index: /sys/netinet/tcp_input.c 4.2BSD Fix

Description:
    I have been having problems with half-open connections remaining after
    one of two connected hosts crashes.  When that host reboots, it
    attempts to reopen the connection that the other end thinks is
    still in ESTABLISHED state.  The TCP specification (RFC 793)
    argues convincingly that the two TCPs should be able to sort
    this out, but between a pair of 4.2BSD TCPs this doesn't happen.
    Instead, the reconnection attempt eventually times out, and the
    other end continues to think that the old connection exists.

Repeat-By:
    rlogin from machine A to machine B, such that A's TCP port number
    is the first available privileged port (1023).  Now crash machine A,
    reboot it, and attempt to rlogin to B again.  (this is a rather
    disruptive demonstration ...)

Description:
    It appears that the problem lies in the file netinet/tcp_input.c,
    where we find the code sequence:

    >dropafterack:
    >	/*
    >	 * Generate an ACK dropping incoming segment if it occupies
    >	 * sequence space, where the ACK reflects our state.
    >	 */
    >	if ((tiflags&TH_RST) ||
    >	    tlen == 0 && (tiflags&(TH_SYN|TH_FIN)) == 0)
    >		goto drop;

    This is branched to under several conditions, the most interesting
    being when an established connection receives a segment (eg. a
    connection request) that is entirely outside its window.
    The effect of the tests is that an ACK packet will be returned to
    the originator of the funny segment, UNLESS that funny segment
    contained a reset (TH_RST) OR contained no data and no SYN or FIN flags
    (which do "occupy sequence space").
    These ACKs have to be sent to recover from half-open connections, but
    they aren't being sent.  What seems to happen here is the connection request
    contains no data (so tlen == 0) but does, ORIGINALLY, contain a SYN.
    The receiving TCP trims the segment to its receive window, discarding
    the SYN, realizes there's nothing left, and branches to this point
    to send an ACK.  Which doesn't get sent, because the modified segment
    no longer appears to "occupy sequence space".
    
Fix:
    The "occupy sequence space" requirement is not part of the TCP definition.
    I have no idea why it was added.  I have deleted the test, leaving
    only the requirement that the original segment not contain an RST.
    And, now, we appear to be recovering nicely from half-open connections.
    The new code is:

    >dropafterack:
    >	/*
    >	 * Generate an ACK dropping incoming segment if it does not
    >	 * contain an RST, where the ACK reflects our state.
    >	 */
    >	if (tiflags&TH_RST)
    >		goto drop;
-- 
		    Brian Thomson,	    CSRI Univ. of Toronto
		    {linus,ihnp4,uw-beaver,floyd,utzoo}!utcsrgv!uthub!thomson