From owner-freebsd-net@FreeBSD.ORG Fri Mar 30 16:06:09 2012 Return-Path: Delivered-To: freebsd-net@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id ACEB81065673 for ; Fri, 30 Mar 2012 16:06:09 +0000 (UTC) (envelope-from andre@freebsd.org) Received: from c00l3r.networx.ch (c00l3r.networx.ch [62.48.2.2]) by mx1.freebsd.org (Postfix) with ESMTP id 08DFE8FC16 for ; Fri, 30 Mar 2012 16:06:08 +0000 (UTC) Received: (qmail 52906 invoked from network); 30 Mar 2012 16:05:09 -0000 Received: from c00l3r.networx.ch (HELO [127.0.0.1]) ([62.48.2.2]) (envelope-sender ) by c00l3r.networx.ch (qmail-ldap-1.03) with SMTP for ; 30 Mar 2012 16:05:09 -0000 Message-ID: <4F75D9ED.7080707@freebsd.org> Date: Fri, 30 Mar 2012 18:06:05 +0200 From: Andre Oppermann User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 MIME-Version: 1.0 To: darrenr@freebsd.org References: <4F75C1A3.4030401@freebsd.org> In-Reply-To: <4F75C1A3.4030401@freebsd.org> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Cc: freebsd-net@freebsd.org Subject: Re: FreeBSD TCP ignores zero window size X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 30 Mar 2012 16:06:09 -0000 On 30.03.2012 16:22, Darren Reed wrote: > I've been tracking down some problems with FreeBSD's sending > of TCP packets and seem to have come to the conclusion that > in FreeBSD 8.2-RELEASE, when the system is working with a > TCP connection that has a moderate delay in it, FreeBSD's > TCP ignores the other end telling it that the window size > is now 0 and continues to send data. I suspect that this is > meant to make sense because it is expecting that the ACK > that will open up the window is already in transit. But that > only accounts for the condition where the TCP on FreeBSD can > compute and decide that the remote TCP will have its buffer > full. What I find harder to accept is that when FreeBSD's > TCP receives a TCP packet from the remote end advertising > a window of 0, FreeBSD's response is to send more data and > not a window probe or is that now the expected behaviour? > And whilst you might say "ok" for a packet of data, I'm > somewhat hard pressed to explain why FreeBSD's TCP sends > multiple packets with data in them after receiving a TCP > packet from the other end advertising a zero window size. > > However this causes a problem with firewalls (;_) that are > close to the FreeBSD end because for them, it appears that > FreeBSD is sending data outside of its window. > > Is this a known problem? > If so, has it been fixed in a later version of FreeBSD? > (No, I haven't tested anything other than 8.2) The window update acceptance test is too restrictive. In your case the last updated seq# tracking gets it wrong and prevents the update. The code hasn't changed for a long time and newer versions behave the same. The concept patch below simplifies the logic, better tracks the seq# and is a bit more permissive. -- Andre $ svn diff netinet/tcp_input.c Index: netinet/tcp_input.c =================================================================== --- netinet/tcp_input.c (revision 233227) +++ netinet/tcp_input.c (working copy) @@ -1717,7 +1730,7 @@ * Pull snd_wl1 up to prevent seq wrap relative to * th_seq. */ - tp->snd_wl1 = th->th_seq; + tp->snd_wl1 = th->th_seq + tlen; /* * Pull rcv_up up to prevent seq wrap relative to * rcv_nxt. @@ -2710,15 +2723,16 @@ * Don't look at window if no ACK: TAC's send garbage on first SYN. */ if ((thflags & TH_ACK) && - (SEQ_LT(tp->snd_wl1, th->th_seq) || - (tp->snd_wl1 == th->th_seq && (SEQ_LT(tp->snd_wl2, th->th_ack) || - (tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd))))) { + (BYTES_THIS_ACK(tp, th) > 0 || /* new data acked */ + SEQ_GT(th->th_seq, tp->snd_wl1) || /* new data received */ + (th->th_seq == tp->snd_wl1 && tiwin > tp->snd_wnd))) { /* pure win update */ + /* keep track of pure window updates */ if (tlen == 0 && tp->snd_wl2 == th->th_ack && tiwin > tp->snd_wnd) TCPSTAT_INC(tcps_rcvwinupd); tp->snd_wnd = tiwin; - tp->snd_wl1 = th->th_seq; + tp->snd_wl1 = th->th_seq + tlen; tp->snd_wl2 = th->th_ack; if (tp->snd_wnd > tp->max_sndwnd) tp->max_sndwnd = tp->snd_wnd;