This bug was fixed in the 2.0.3x series about a year ago. The approach 
there didn't have any adverse side effects -- since the bug only
happens when the FIN/Data segment arrives out of order, and the
data will get ack'ed by Solaris, the approach was (on the retransmit
from the linux end) to *not* retransmit the ack'ed data. This leaves
a packet with just a FIN in it. Arguably that was what should have
been happening anyway. Once I understood what was happening,
I seem to recall that the fix was pretty trivial.

This is the bit of code from tcp_do_retransmit in 2.0.36

        /* See if we need to shrink the leading packet on
         * the retransmit queue. Strictly speaking, we
         * should never need to do this, but some buggy TCP
         * implementations get confused if you send them
         * a packet that contains both old and new data. (Feh!)
         * Soooo, we have this uglyness here.
         *
         * Is the && test needed here? If so, then it implies that
         * we might be retransmitting an acked packet. This code is
         * needed here to talk to Solaris 2.6 stack.
         */
        if (after(sk->rcv_ack_seq,skb->seq+th->syn) &&
before(sk->rcv_ack_seq, skb->end_seq))
                tcp_shrink_skb(sk,skb,sk->rcv_ack_seq);

I wonder how many other bugs have been fixed in 2.0.36ff and not yet
fixed
in 2.2.x?

Philip


Andrea Arcangeli wrote:
> 
> Solaris has a stupid bug in its TCP stack that cause it to hang the
> connection between linux-2.2.x and Solaris (with linux as server) once the
> connection closes.
> 
> The reason is that linux-2.2.x is smart enough to save some good packet on
> the network by setting the FIN flag in the latest packet of data if there
> is pending data in write queue. But if such last packet gets reordered by
> the network it seems that Solaris ignores the FIN flag and as first it
> will think that the last octect is full of data while it's the FIN advance
> (if I am thinking right I think this can cause some troubles), and as
> second Solaris will not send back to Linux the FIN to allow Linux to go to
> FINWAIT-2 from FINWAIT-1. So the connection will hang and the end of
> the data has no way to be transferred correctly.
> 
> Sun just released a fix for Solaris but since I think there are still many
> buggy Solaris 2.6 and 2.5.1 TCP stacks on the Internet, I taken the time
> to implement a workaround for Linux.
> 
> The workaround is implemented as a sysctl. As default there is no
> workaround. But if you:
> 
> echo 1 >//proc/sys/net/ipv4/tcp_solaris_fin_bug_4083814_workaround
> 
> you'll enable the buggy-solaris-compatibilty-mode on the Linux TCP stack.
> 
> Note that this make a difference only when Linux is the server. So large
> FTP sites are likely to want to apply my patch and enable the workaround
> in the meantime the bogus Solaris tcp stack will be fixed properly
> everywhere.
> 
> This is probably useful also in Unversity like mine where there are many
> old slowww Sun machines that are not likely to be patched until it will be
> discovered the next root exploit ;).
> 
> Once the workaround is enabled you risk to generate 1 more packet on the
> network every time you close a TCP socket.
> 
> Here the patch:
> 
> Index: include/linux/sysctl.h
> ===================================================================
> RCS file: /var/cvs/linux/include/linux/sysctl.h,v
> retrieving revision 1.1.2.2
> diff -u -r1.1.2.2 sysctl.h
> --- sysctl.h    1999/02/06 16:42:46     1.1.2.2
> +++ linux/include/linux/sysctl.h        1999/02/25 14:59:34
> @@ -209,7 +209,8 @@
>         NET_IPV4_ICMP_PARAMPROB_RATE=62,
>         NET_IPV4_ICMP_ECHOREPLY_RATE=63,
>         NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES=64,
> -       NET_IPV4_IGMP_MAX_MEMBERSHIPS=65
> +       NET_IPV4_IGMP_MAX_MEMBERSHIPS=65,
> +       NET_IPV4_TCP_SOLARIS_FIN_BUG_4083814_WORKAROUND=66,
>  };
> 
>  enum {
> Index: net/ipv4/sysctl_net_ipv4.c
> ===================================================================
> RCS file: /var/cvs/linux/net/ipv4/sysctl_net_ipv4.c,v
> retrieving revision 1.1.2.1
> diff -u -r1.1.2.1 sysctl_net_ipv4.c
> --- sysctl_net_ipv4.c   1999/01/18 01:34:50     1.1.2.1
> +++ linux/net/ipv4/sysctl_net_ipv4.c    1999/02/25 14:58:19
> @@ -48,6 +48,7 @@
>  extern int sysctl_tcp_window_scaling;
>  extern int sysctl_tcp_sack;
>  extern int sysctl_tcp_retrans_collapse;
> +extern int sysctl_tcp_solaris_fin_bug_4083814_workaround;
>  extern int sysctl_tcp_keepalive_time;
>  extern int sysctl_tcp_keepalive_probes;
>  extern int sysctl_tcp_max_ka_probes;
> @@ -107,6 +108,10 @@
>          {NET_IPV4_TCP_RETRANS_COLLAPSE, "tcp_retrans_collapse",
>           &sysctl_tcp_retrans_collapse, sizeof(int), 0644, NULL,
>           &proc_dointvec},
> +        {NET_IPV4_TCP_SOLARIS_FIN_BUG_4083814_WORKAROUND,
> +        "tcp_solaris_fin_bug_4083814_workaround",
> +        &sysctl_tcp_solaris_fin_bug_4083814_workaround, sizeof(int), 0644,
> +        NULL, &proc_dointvec},
>          {NET_IPV4_FORWARD, "ip_forward",
>           &ipv4_devconf.forwarding, sizeof(int), 0644, NULL,
>           &ipv4_sysctl_forward},
> Index: net/ipv4/tcp_output.c
> ===================================================================
> RCS file: /var/cvs/linux/net/ipv4/tcp_output.c,v
> retrieving revision 1.1.2.6
> diff -u -r1.1.2.6 tcp_output.c
> --- tcp_output.c        1999/02/23 17:08:43     1.1.2.6
> +++ linux/net/ipv4/tcp_output.c 1999/02/25 17:11:18
> @@ -31,6 +31,7 @@
>   *                                     during syn/ack processing.
>   *             David S. Miller :       Output engine completely rewritten.
>   *             Andrea Arcangeli:       SYNACK carry ts_recent in tsecr.
> + *             Andrea Arcangeli:       solaris_fin_bug_4083814_workaround.
>   *
>   */
> 
> @@ -42,6 +43,17 @@
> 
>  /* People can turn this off for buggy TCP's found in printers etc. */
>  int sysctl_tcp_retrans_collapse = 1;
> +/*
> + * People can turn this on to avoid Solaris 2.5.1 and 2.6 TCP stacks to stall
> + * upon connection termination. NOTE: you should really apply Solaris 2.6 105529
> + * patch from Sun on the client side instead of enable this sysctl on the
> + * Linux server side! This is _only_ intended as last resort and will disappear
> + * shortly beacuse if enabled doesn't allow the Linux TCP stack to be
> + * clever in decresing the network traffic while sending the FIN from the
> + * FIN_WAIT1 state.
> + *                                                     -arca
> + */
> +int sysctl_tcp_solaris_fin_bug_4083814_workaround = 0;
> 
>  /* Get rid of any delayed acks, we sent one already.. */
>  static __inline__ void clear_delayed_acks(struct sock * sk)
> @@ -693,7 +705,8 @@
>          */
>         mss_now = tcp_current_mss(sk);
> 
> -       if((tp->send_head != NULL) && (skb->len < mss_now)) {
> +       if((tp->send_head != NULL) && (skb->len < mss_now) &&
> +          !sysctl_tcp_solaris_fin_bug_4083814_workaround) {
>                 /* tcp_write_xmit() takes care of the rest. */
>                 TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;
>                 TCP_SKB_CB(skb)->end_seq++;
> 
> Just to avoid confusion I repeat: this is a silly Solaris bug and not a
> Linux bug.
> 
> Andrea Arcangeli
> 
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [EMAIL PROTECTED]
> Please read the FAQ at http://www.tux.org/lkml/

-- 
Philip Gladstone                           +1 781 530 2461
Axent Technologies, Waltham, MA

S/MIME Cryptographic Signature

Reply via email to