kern/93236: [PATCH] Received out of window SYN in ESTABLISHED connection isn't ACKed as required by RFC793

Grant Edwards grante at visi.com
Sun Feb 12 09:10:05 PST 2006


>Number:         93236
>Category:       kern
>Synopsis:       [PATCH] Received out of window SYN in ESTABLISHED connection isn't ACKed as required by RFC793
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Feb 12 17:10:03 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator:     Grant Edwards
>Release:        None
>Organization:
>Environment:
I'm using just the TCP/IP stack.
>Description:
Problem Summary

  When the FreeBSD stack receives an out-of-window SYN for an
  "ESTABLISHED" connection, the SYN is ignored rather that
  ACKed as required by RFC793. FWIW, Linux and NetBSD both
  follow the RFC.

Details
-------

After switching from the NetBSD TCP/IP stack to the FreeBSD
TCP/IP stack we've run into a problem which is causing
customers to complain.  


 Here's the scenario:

  1) Host opens TCP connection to device under test (DUT) which
     is running either FreeBSD or NetBSD TCP/IP stack.

  2) Somebody pushes the reset button on the host.

  3) The host reboots and attempts to open a TCP connection to
     the DUT _using_the_same_source_port_ (and the same
     destination port).

 4a) When the NetBSD stack received a SYN for an already-open
     connection, it sent an ACK with the sequence number from
     the old connection.  The host sees this and sent a RST
     (which drops the connection), then the HOST attempted to
     re-open the connection (which succeeds).  All is good.

 4b) When the FreeBSD stack receives a SYN for an already-open
     connection, it just ignores it.  So, after a timeout, the
     host sends it again.  Again it's ignored. This goes on
     until the Host times out the connection operation and the
     system fails.

RFC793 page 34 describes this scenario exactly and requires the
behavior of the Linux and NetBSD stacks.  According to the
RFC's sample implimentation on pages 58 though 84, when you
receive a SYN in the ESTABLISHED state you either


  1) Send a RST if the sequence number is inside the current
     receive window window (p71).

  2) Send an ACK if the sequence number is outside the current
     receive window (p69,71).

Ignoring the SYN isn't allowed.

Here's the relevent code from the current version of tcp_input.c:

At one point it is determined (correctly) that the sequence
number in the SYN is outside the receive window.  This means an
ACK is required, so the ACKNOW flag is set at line 1645:


  1629          /*
  1630           * Following if statement from Stevens, vol. 2, p. 960.
  1631           */
  1632          if (todrop > tlen
  1633              || (todrop == tlen && (thflags & TH_FIN) == 0)) {
  1634              /*
  1635               * Any valid FIN must be to the left of the window.
  1636               * At this point the FIN must be a duplicate or out
  1637               * of sequence; drop it.
  1638               */
  1639              thflags &= ~TH_FIN;
  1640  
  1641              /*
  1642               * Send an ACK to resynchronize and drop any data.
  1643               * But keep on processing for RST or ACK.
  1644               */
  1645              tp->t_flags |= TF_ACKNOW;
  1646              todrop = tlen;
  1647              tcpstat.tcps_rcvduppack++;
  1648              tcpstat.tcps_rcvdupbyte += todrop;
  1649          } else {
  1650              tcpstat.tcps_rcvpartduppack++;
  1651              tcpstat.tcps_rcvpartdupbyte += todrop;
  1652          }
  1653          drop_hdrlen += todrop;  /* drop from the top afterwards */
  1654          th->th_seq += todrop;
  1655          tlen -= todrop;
  1656          if (th->th_urp > todrop)
  1657              th->th_urp -= todrop;
  1658          else {
  1659              thflags &= ~TH_URG;
  1660              th->th_urp = 0;
  1661          }
  1662      }

Now we rattle on down through the tcp_input fuction for a while
and end up jumping to "drop" at line 1769:


  1759      /*
  1760       * If the ACK bit is off:  if in SYN-RECEIVED state or SENDSYN
  1761       * flag is on (half-synchronized state), then queue data for
  1762       * later processing; else drop segment and return.
  1763       */
  1764      if ((thflags & TH_ACK) == 0) {
  1765          if (tp->t_state == TCPS_SYN_RECEIVED ||
  1766              (tp->t_flags & TF_NEEDSYN))
  1767              goto step6;
  1768          else
  1769              goto drop;
  1770      }

The code at "drop:" cleans up a little and returns without ever
calling tcp_output(tp) to actually send the ACK that's required
by the RFC and was requested by the setting of the TF_ACKNOW bit.

  2549  drop:
  2550      /*
  2551       * Drop space held by incoming segment and return.
  2552       */
  2553  #ifdef TCPDEBUG
  2554      if (tp == 0 || (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
  2555          tcp_trace(TA_DROP, ostate, tp, (void *)tcp_saveipgen,
  2556                &tcp_savetcp, 0);
  2557  #endif
  2558      if (tp)
  2559          INP_UNLOCK(inp);
  2560      if (headlocked)
  2561          INP_INFO_WUNLOCK(&tcbinfo);
  2562      m_freem(m);
  2563      return;

So, the ACK that's required by the TCP RFC is never sent (the
SYN packet is just ignored): the host just sits there and
sends SYNs.




>How-To-Repeat:
1) Open TCP connection from host A to FreeBSD network stack on host B.

2) Press reset button on host A

3) After host A has rebooted, attempt to open TCP connection from host A
   to host B using same source/dest ports as step 1 above.

4) SYNs sent to attempt to "re-open" the connection will be ignored.
>Fix:
This bug was fixed in the NetBSD stack at tcp_input.c version 1.78 with the
CVS log entry below:

    "Ensure that out of window SYNs receive an ACK in responce,
     rather than being dropped.  This fixes a bug reported by
     Jason Thorpe."

Here's the NetBSD tcp_input.c diff that corresponds to that fix:

===================================================================
RCS file: /ftp/cvs/cvsroot/src/sys/netinet/tcp_input.c,v
retrieving revision 1.77
retrieving revision 1.78
diff -u -p -r1.77 -r1.78
--- src/sys/netinet/tcp_input.c 1999/02/05 22:37:24     1.77
+++ src/sys/netinet/tcp_input.c 1999/04/09 22:01:07     1.78
@@ -1,4 +1,4 @@
-/*     $NetBSD: tcp_input.c,v 1.77 1999/02/05 22:37:24 matt Exp $      */
+/*     $NetBSD: tcp_input.c,v 1.78 1999/04/09 22:01:07 kml Exp $       */
 
 /*-
  * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
@@ -1104,8 +1104,12 @@ after_listen:
        /*
         * If the ACK bit is off we drop the segment and return.
         */
-       if ((tiflags & TH_ACK) == 0)
-               goto drop;
+       if ((tiflags & TH_ACK) == 0) {
+               if (tp->t_flags & TF_ACKNOW)
+                       goto dropafterack;
+               else
+                       goto drop;
+       }
        
        /*
         * Ack processing.
===================================================================



I've got a patch for the current FreeBSD tcp_input.c, but I don't see any way to attach it as a file, so I'll paste it in here:

--- tcp_input.c-orig    2006-02-12 10:24:35.988707395 -0600
+++ tcp_input.c 2006-02-12 10:25:28.877515703 -0600
@@ -1758,20 +1758,22 @@
 
        /*
         * If the ACK bit is off:  if in SYN-RECEIVED state or SENDSYN
         * flag is on (half-synchronized state), then queue data for
         * later processing; else drop segment and return.
         */
        if ((thflags & TH_ACK) == 0) {
                if (tp->t_state == TCPS_SYN_RECEIVED ||
                    (tp->t_flags & TF_NEEDSYN))
                        goto step6;
+               else if (tp->t_flags & TF_ACKNOW)
+                       goto dropafterack;
                else
                        goto drop;
        }
 
        /*
         * Ack processing.
         */
        switch (tp->t_state) {
 
        /*


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list