[patch] workaround for solaris 2.5.1 and 2.6 FIN bug (ID 4083814)

Andrea Arcangeli (andrea@e-mind.com)
Thu, 25 Feb 1999 20:53:01 +0100 (CET)


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 majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/