TCP: Fix Zero Window Probes.

I did some tcpdump recording, and I believe we confuse the receiver
with our Zero Window Probe, because we don't resent even though it only
ACKs the previous segment, and we keep sending things after it:

 * we send the last segment before window is closed, next seg = N,
 * we get ACK for N, with window=0
 * we send Zero Window Probe with 1 byte, next seg = N+1,
 * we eventually get a window>0 ACK still at N,
 * we start sending stuff again, but starting from N+1,
 * receiver keeps ACKing N because it never accepted it.

It seems sending either 1 or 0 byte is valid for a ZWP, although I'm not
entirely sure. At least it's easier to handle 0 than 1, and it seems to work.
Wireshark shows them as duplicate ACKs, but they get the thing going.

References:
 * RFC 793: https://tools.ietf.org/html/rfc793#section-3.7
 * RFC 6429: https://tools.ietf.org/html/rfc6429
 * Wireshark wiki: https://www.wireshark.org/docs/wsug_html_chunked/ChAdvTCPAnalysis.html

Should fix #13769.

Change-Id: I95264ebbbb8c66c23411dfce6fc41871e0427166
Reviewed-on: https://review.haiku-os.org/c/haiku/+/1188
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
François Revol 2019-03-11 00:31:10 +01:00 committed by waddlesplash
parent 934ad06cf1
commit f0ba8f6aca

View File

@ -2084,12 +2084,6 @@ TCPEndpoint::_SendQueued(bool force, uint32 sendWindow)
} else
sendWindow -= consumedWindow;
if (force && sendWindow == 0 && fSendNext <= fSendQueue.LastSequence()) {
// send one byte of data to ask for a window update
// (triggered by the persist timer)
sendWindow = 1;
}
uint32 length = min_c(fSendQueue.Available(fSendNext), sendWindow);
bool shouldStartRetransmitTimer = fSendNext == fSendUnacknowledged;
bool retransmit = fSendNext < fSendMax;
@ -2104,7 +2098,7 @@ TCPEndpoint::_SendQueued(bool force, uint32 sendWindow)
- tcp_options_length(segment);
uint32 segmentLength = min_c(length, segmentMaxSize);
if (fSendNext + segmentLength == fSendQueue.LastSequence()) {
if (fSendNext + segmentLength == fSendQueue.LastSequence() && !force) {
if (state_needs_finish(fState))
segment.flags |= TCP_FLAG_FINISH;
if (length > 0)