Try to trigger fast retransmit when we detect a missing TCP segment.

The changes in this commit affect the following scenario:
  * Remote end sends octets 1..2, they are delivered and buffered
    on local end;
  * Remote end sends octets 3..4, they are lost;
  * Remote end sends octets 5..6, they are delivered but cannot
    be buffered on local end because we don't perform reassembly.

Before this commit, we would silently drop the segment with octets
5..6, relying on retransmission timer on the remote end. This works,
but can result in severe decrease in throughput. After this commit,
we send a duplicate ACK, which may trigger fast retransmit, if
implemented by the congestion control algorithm on the remote end.
This commit is contained in:
whitequark 2017-06-26 08:09:27 +00:00
parent 62c9b7d6fa
commit cbf6e5cdbc
1 changed files with 59 additions and 6 deletions

View File

@ -1,3 +1,6 @@
// Heads up! Before working on this file you should read, at least, RFC 793 and
// the parts of RFC 1122 that discuss TCP.
use core::fmt;
use managed::Managed;
@ -697,8 +700,15 @@ impl<'a> TcpSocket<'a> {
// Every acknowledgement must be for transmitted but unacknowledged data.
(_, TcpRepr { ack_number: Some(ack_number), .. }) => {
let unacknowledged = self.tx_buffer.len() + control_len;
if !(ack_number >= self.local_seq_no &&
ack_number <= (self.local_seq_no + unacknowledged)) {
if ack_number < self.local_seq_no {
net_trace!("[{}]{}:{}: duplicate ACK ({} not in {}...{})",
self.debug_id, self.local_endpoint, self.remote_endpoint,
ack_number, self.local_seq_no, self.local_seq_no + unacknowledged);
// FIXME: instead of waiting for the retransmit timer to kick in,
// reset it here.
return Err(Error::Dropped)
}
if ack_number > self.local_seq_no + unacknowledged {
net_trace!("[{}]{}:{}: unacceptable ACK ({} not in {}...{})",
self.debug_id, self.local_endpoint, self.remote_endpoint,
ack_number, self.local_seq_no, self.local_seq_no + unacknowledged);
@ -715,20 +725,34 @@ impl<'a> TcpSocket<'a> {
// For now, do not try to reassemble out-of-order segments.
(_, TcpRepr { seq_number, .. }) => {
let next_remote_seq = self.remote_seq_no + self.rx_buffer.len();
let mut send_ack_again = false;
if seq_number > next_remote_seq {
net_trace!("[{}]{}:{}: unacceptable SEQ ({} not in {}..)",
net_trace!("[{}]{}:{}: unacceptable SEQ ({} not in {}..), \
will send duplicate ACK",
self.debug_id, self.local_endpoint, self.remote_endpoint,
seq_number, next_remote_seq);
return Err(Error::Dropped)
// Some segments between what we have last received and this segment
// went missing. Send a duplicate ACK; RFC 793 does not specify the behavior
// required when receiving a duplicate ACK, but in practice (see RFC 1122
// section 4.2.2.21) most congestion control algorithms implement what's called
// a "fast retransmit", where a threshold amount of duplicate ACKs triggers
// retransmission.
send_ack_again = true;
} else if seq_number != next_remote_seq {
net_trace!("[{}]{}:{}: duplicate SEQ ({} in ..{})",
net_trace!("[{}]{}:{}: duplicate SEQ ({} in ..{}), \
will re-send ACK",
self.debug_id, self.local_endpoint, self.remote_endpoint,
seq_number, next_remote_seq);
// If we've seen this sequence number already but the remote end is not aware
// of that, make sure we send the acknowledgement again.
send_ack_again = true;
}
if send_ack_again {
self.remote_last_ack = next_remote_seq - 1;
self.retransmit.reset();
// If we're in the TIME-WAIT state, restart the TIME-WAIT timeout.
// If we're in the TIME-WAIT state, restart the TIME-WAIT timeout, since
// the remote end may not realize we've closed the connection.
if self.state == State::TimeWait {
self.time_wait_since = timestamp;
}
@ -2355,6 +2379,35 @@ mod test {
}]);
}
#[test]
fn test_missing_segment() {
let mut s = socket_established();
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"abcdef"[..],
..SEND_TEMPL
});
recv!(s, [TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1 + 6),
window_len: 58,
..RECV_TEMPL
}]);
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1 + 6 + 6,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"mnopqr"[..],
..SEND_TEMPL
}, Err(Error::Dropped));
recv!(s, [TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1 + 6),
window_len: 58,
..RECV_TEMPL
}]);
}
#[test]
fn test_data_retransmit() {
let mut s = socket_established();