Subject: Re: FIN_WAIT_2
To: None <mouse@Rodents.Montreal.QC.CA>
From: Arne Henrik Juul <arnej@stud.math.ntnu.no>
List: tech-net
Date: 09/01/1998 19:14:09
> As I read it, FIN_WAIT_2 is the state for a socket whose send side has
> been shut down but whose receive side is still up.  (Well, one of the
> states; FIN_WAIT_1 is similar, but before our FIN has been ACKed.)
> 
> As such, FIN_WAIT_2 is a legitimate long-term state; a client that
> connects to a server, sends a request, and then shuts down its send
> side should still be able to receive data from the server.
> 
> The problem is, that's not the way we work.  If the peer is slow about
> sending data, we will unilaterally terminate the connection without
> making any attempt to find out whether the peer TCP thinks the
> connection is still alive: tcp_usrclosed() [tcp_usrreq.c] sets the 2MSL
> timer going, and if it expires before the peer decides to send us
> something (or if, after it expires, the peer lets the connection idle
> for more than tcp_keepintvl), we will tcp_close() the connection (see
> tcp_timers() [tcp_timer.c], case TCPT_2MSL).

No, FIN_WAIT_2 is used
  -  while the send side is shut down but the receive side is up
  -  *and* when the receive side is shut as well.

This is tcp_usrclosed() after all, which means the user did close(),
and both directions are now shut, so we want to time out and kill the
connection if the peer doesn't answer.  It's logically entering
the same state as it does in tcp_input around line 1287:

                case TCPS_FIN_WAIT_1:
                        if (ourfinisacked) {
                                /*
                                 * If we can't receive any more
                                 * data, then closing user can proceed.
                                 * Starting the timer is contrary to the
                                 * specification, but if we don't get a FIN
                                 * we'll hang forever.
                                 */
                                if (so->so_state & SS_CANTRCVMORE) {
                                        soisdisconnected(so);
                                        TCP_TIMER_ARM(tp, TCPT_2MSL,
                                            tcp_maxidle);
                                }
                                tp->t_state = TCPS_FIN_WAIT_2;
                        }
                        break;

I don't know TCP well enough to say whether it's possible to do
better, but without starting the timer in these two cases you may
get lots and lots of connections from windows hosts hanging in
FIN_WAIT_2 (with no possibility of data transfer either way).

 -  Arne H. J.