From owner-freebsd-bugs@FreeBSD.ORG Sun Feb 12 17:10:05 2006 Return-Path: X-Original-To: freebsd-bugs@hub.freebsd.org Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 1488016A420 for ; Sun, 12 Feb 2006 17:10:05 +0000 (GMT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 6C39143D48 for ; Sun, 12 Feb 2006 17:10:04 +0000 (GMT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.13.4/8.13.4) with ESMTP id k1CHA4kh093322 for ; Sun, 12 Feb 2006 17:10:04 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.13.4/8.13.4/Submit) id k1CHA4SQ093321; Sun, 12 Feb 2006 17:10:04 GMT (envelope-from gnats) Resent-Date: Sun, 12 Feb 2006 17:10:04 GMT Resent-Message-Id: <200602121710.k1CHA4SQ093321@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Grant Edwards Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id A6E6016A420 for ; Sun, 12 Feb 2006 17:00:27 +0000 (GMT) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [216.136.204.117]) by mx1.FreeBSD.org (Postfix) with ESMTP id 602E243D46 for ; Sun, 12 Feb 2006 17:00:27 +0000 (GMT) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.13.1/8.13.1) with ESMTP id k1CH0Rbg044178 for ; Sun, 12 Feb 2006 17:00:27 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.13.1/8.13.1/Submit) id k1CH0Rhx044177; Sun, 12 Feb 2006 17:00:27 GMT (envelope-from nobody) Message-Id: <200602121700.k1CH0Rhx044177@www.freebsd.org> Date: Sun, 12 Feb 2006 17:00:27 GMT From: Grant Edwards To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-2.3 Cc: Subject: kern/93236: [PATCH] Received out of window SYN in ESTABLISHED connection isn't ACKed as required by RFC793 X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 12 Feb 2006 17:10:05 -0000 >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: