net: tcp: Don't respond to FIN-less ACKs during TIME-WAIT
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 14 Dec 2018 19:46:50 +0000 (14:46 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 14 Dec 2018 22:23:48 +0000 (17:23 -0500)
commita13aa901a37a407407771f0f45dd79e28fced28b
tree04b7b34089ebe5b8490fe69116556f04a53141e2
parentf89b6d306593e2ab3e6ea7d6e73ece090c44b176
net: tcp: Don't respond to FIN-less ACKs during TIME-WAIT

Under the normal close sequence, when we receive a FIN|ACK, we enter
TIME-WAIT and respond to that LAST-ACK with an ACK.  Our TCP stack would
send an ACK in response to *any* ACK, which included FIN|ACK but also
included regular ACKs.  (Or PSH|ACKs, which is what we were actually
getting/sending).

That was more ACKs than is necessary and results in an endless ACK storm
if we were under the simultaneous close sequence.  In that scenario,
both sides of a connection are in TIME-WAIT.  Both sides receive
FIN|ACK, and both respond with an ACK.  Then both sides receive *those*
ACKs, and respond again.  This continues until the TIME-WAIT wait period
elapses and each side's TCP timers (in the Plan 9 / Akaros case) shut
down.

The fix for this is to only respond to a FIN|ACK when we are in
TIME-WAIT.

The way I noticed this was when a Go test simultaneously closed both
ends of a loopback connection.  All of the packets went through the
looback interface and were handled by the loopbackread() ktask.  On
occasion, the tcpackproc() ktask would get scheduled on the same core as
loopbackread(), so the timers would not fire until loopbackread()
yielded (Akaros is non-preemptive).  However, loopbackread() would
always have packets, since for every ACK received, it sent another.
loopbackread() would then receive that packet, since it receives every
packet on both sides of a connection on the loopback interface.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/src/net/tcp.c