[comp.protocols.tcp-ip] Questions on TCP shutdown and RFC-793

alp@hpindwa.cup.hp.com (Arnold Patton) (12/08/90)

I have two questions concerning the implementation of TCP and the
interpretation of RFC-793, specifically concerning connection
shutdown.

Question #1:

In step 8, on page 75, RFC-793 states that:

  "If the FIN bit is set, signal the user 'connection closing' and
   return any pending RECEIVEs with same message, advance RCV.NXT
   over the FIN, and send an acknowledgment for the FIN.  Note that
   FIN implies PUSH for any segment text not yet delivered to the
   user."

Notice that the RFC does not mention checking the current RCV.NXT
value against the FIN sequence number.  If one were to interpret this
literally, then it would seem that one would accept the FIN and change
states even if there were still holes in the TCP reassembly queue due
to lost or dropped packets (assuming that we are doing the right thing
and not accepting only in-order packets).  If that is the intent of
the RFC, then the statement about PUSHing any text not yet delivered
is ambiguous or at least not detailed enough, since it is possible to
have data both before and after a hole in the reassembly queue.  Do we
push only the data before the hole?  Or do we push all the data and
ignore the hole?

It seems to me that this was not the intent of the RFC, and that the
assumption was being made that there would not be any holes in the
reassembly queue (which is normally the case) when the FIN is
received.  In my understanding, the FIN handshake was intended to
insure that all data has been received by both sides.  The "literal
interpretation" above would defeat this attribute of the FIN handshake.
I thought that perhaps HOSTS RFC (RFC 1122) had clarified RFC-793's
statement, but it does not.  In fact, when I checked a copy of the BSD
4.3 TCP code, it appears that they indeed accept the FIN no matter
what!  (However, I haven't tested the actual behavior of the code, and
I may have overlooked something.  It would be difficult to believe
that the BSD code would behave this way.)

Also, if you do not accept the FIN right away, what do you do with it?
Options seem to be either set some state vector or to drop the FIN
packet and to pick it up in a retransmission after your reassembly
queue has been filled.  The second option would cause a delay in
waiting for the retranmitted FIN, and has a potential danger in that
if the retransmitted FIN is dropped in between (say at a gateway), the
remote connection half may time out.  However, the first option also
has the preformance penalty of always needing to check against this
new state vector to see if we should accept the FIN now.

Does the standard really intend some sort of bizarre behavior here?
Or does someone know of a reference in the standards which clarifies
things?  What behavior do BSD SOCKETS and other ULPs based on TCP
expect?  Or have I missed the boat entirely and overlooked something
in RFC-793?

Question #2:

This question also regards TCP connection shutdown implementations.

I thought that I read somewhere (I thought it was HOSTS RFC) that if
you get a FIN without an ACK of FIN while you are in the FINWAIT1
state that you should reply with a FIN, ACK of FIN packet (not just
ACK of FIN as RFC-793 states), but I can no longer find the reference.

I can see why you might want to do this, but I think it would work either
way.

Was I just imagining things?  Or does someone know of such a reference?
If you could pass it on to me, it'd be much appreciated.

-------------------------------------------------------------------------------
 Arnold Lee Patton    | MPE/XL Networking Engineer | I wonder how much time is
 Hewlett-Packard  Co. | alp@hpindwa.cup.hp.com     | spent figuring out clever
                      |                            | notes for this space.
-------------------------------------------------------------------------------

dls@mentor.cc.purdue.edu (David L Stevens) (12/08/90)

	If you check in the beginning of the "SEGMENT ARRIVES" section of
RFC 793, it says that they assume packets have already been sequenced in
proper order for the rest of it.
	Re: shutdown(2) in BSD implementations. It corresponds roughly to
the TCP ABORT function, but not quite. In particular, as the manual page
says, any buffered data pending will be dropped. It isn't really the
"half close" that RFC 793 wants, and it should only be used in programs
that are aborting or, because of mechanisms in the application protocol,
already know that the data has been delivered. It should *not* be used for
applications that have done writes and just want to mark the end of data for
the peer, since any data still pending in the local send buffers is discarded
and never delivered.
-- 
					+-DLS  (dls@mentor.cc.purdue.edu)

thomson@hub.toronto.edu (Brian Thomson) (12/11/90)

In article <2452@mentor.cc.purdue.edu> dls@mentor.cc.purdue.edu (David L Stevens) writes:
>	Re: shutdown(2) in BSD implementations. It corresponds roughly to
>the TCP ABORT function, but not quite. In particular, as the manual page
>says, any buffered data pending will be dropped. It isn't really the
>"half close" that RFC 793 wants, and it should only be used in programs
>that are aborting or, because of mechanisms in the application protocol,
>already know that the data has been delivered. It should *not* be used for
>applications that have done writes and just want to mark the end of data for
>the peer, since any data still pending in the local send buffers is discarded
>and never delivered.

I think the original question was about the TCP shutdown procedure, not the
BSD shutdown() system call, but this answer caught my attention because it
seems to contradict everything I thought I knew about the BSD routine.
My understanding is as follows:
	shutdown(s, 0)	- flushes data queued for receive, and shrinks
			  my receive window to zero.  Succeeding reads will
			  fail.
	shutdown(s, 1)	- data already queued will be delivered, followed
			  by a FIN.  Succeeding writes will fail.
	shutdown(s, 2)	- equivalent to { shutdown(s, 0); shutdown(s, 1); }

The second of these is precisely the orderly half close.
None of them performs the TCP ABORT function.

And, to top it off, my manual pages (Sun) say nothing about loss of
buffered data pending.
-- 
		    Brian Thomson,	    CSRI Univ. of Toronto
		    utcsri!uthub!thomson, thomson@hub.toronto.edu

dls@mentor.cc.purdue.edu (David L Stevens) (12/11/90)

In article <1990Dec10.131116.19393@jarvis.csri.toronto.edu>, thomson@hub.toronto.edu (Brian Thomson) writes:
> 
> I think the original question was about the TCP shutdown procedure, not the
> BSD shutdown() system call, but this answer caught my attention because it
> seems to contradict everything I thought I knew about the BSD routine.
> My understanding is as follows:
> 	shutdown(s, 0)	- flushes data queued for receive, and shrinks
> 			  my receive window to zero.  Succeeding reads will
> 			  fail.
> 	shutdown(s, 1)	- data already queued will be delivered, followed
> 			  by a FIN.  Succeeding writes will fail.
> 	shutdown(s, 2)	- equivalent to { shutdown(s, 0); shutdown(s, 1); }
> ...
> 
> And, to top it off, my manual pages (Sun) say nothing about loss of
> buffered data pending.

	Sorry, I had forgotten where I saw it. It isn't in the man page, and
I originally came across it in the code, but I knew there was a documentation
reference as well. The "IPC Primer" (section 2.6) says:

	"....Applying shutdown to a socket causes any data queued to be
immediately discarded."

Also, from the "Advanced 4.3BSD IPC Tutorial" (PS1:8-8):

	"Once a socket is no longer of interest, it may be discarded by
applying close to the descriptor, close(s);
	If data is associated with a socket which promises reliable delivery
(e.g., a stream socket) when a close takes place, the system will continue
to attempt to transfer the data. However, after a fairly long period of time,
if the data is still undelivered, it will be discarded. Should a user have no
use for a any pending data, it may perform a shutdown on the socket prior to
closing it."
-- 
					+-DLS  (dls@mentor.cc.purdue.edu)

dls@mentor.cc.purdue.edu (David L Stevens) (12/11/90)

	More information: I looked briefly through our (4.3 Tahoe) code and
it does in fact appear to do shutdown(2) as the "half-close" that you (and
I) want it to do. That is, it doesn't deallocate the send buffer until the
final close and it does appear to go into TIME_WAIT, instead of just
deallocating the TCB.
	So, if you trust my cursory reading of the current code and you don't
take the documentation I quoted too literally, then shutdown(2) appears to
be safe after a write, even if it's buffered, in at least 4.3 Tahoe. :-)
	I unfortunately don't have time to look at it in detail enough to turn
the "appears to be safe" to just "is safe" right now, but if anyone else does,
I'd like to see what you find.
-- 
					+-DLS  (dls@mentor.cc.purdue.edu)

george@na.excelan.com (George Powers) (12/29/90)

> In article <1990Dec10.131116.19393@jarvis.csri.toronto.edu>, thomson@hub.toronto.edu (Brian Thomson) writes:
> > 
> > I think the original question was about the TCP shutdown procedure, not the
> > BSD shutdown() system call, but this answer caught my attention because it
> > seems to contradict everything I thought I knew about the BSD routine.
> > My understanding is as follows:
> > 	shutdown(s, 0)	- flushes data queued for receive, and shrinks
> > 			  my receive window to zero.  Succeeding reads will
> > 			  fail.
> > 	shutdown(s, 1)	- data already queued will be delivered, followed
> > 			  by a FIN.  Succeeding writes will fail.
> > 	shutdown(s, 2)	- equivalent to { shutdown(s, 0); shutdown(s, 1); }
> > ...
> > 
> > And, to top it off, my manual pages (Sun) say nothing about loss of
> > buffered data pending.

Having read the 4.3BSD source and tested SUNOS 4.0, I agree with
Brian.  Shutdown is typically useful when applied to the send
direction.  The application program can then keep reading until it gets
an EOF, meaning that the peer application has seen the EOF which
shutdown caused, and has closed in response.  Therefore the application
which initiated the shutdown can be sure that all data has been
delivered in both directions.  If an application has no other way of
confirming delivery and closes without the shutdown method, it cannot
tell whether enqueued send data is lost after close detaches the socket
from its file descriptor.

--
UUCP: {ames,sun,apple,mtxinu,cae780,sco}!novell!george  George Powers
Internet: george@novell.com 
--