Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: b3e355437f68
Choose a base ref
...
head repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: ac6efbf99945
Choose a head ref
  • 3 commits
  • 3 files changed
  • 1 contributor

Commits on Jun 26, 2017

  1. In examples, print packet dumps with timestamps, too.

    This helps debugging retransmit issues.
    whitequark committed Jun 26, 2017
    Copy the full SHA
    86c1cba View commit details
  2. Copy the full SHA
    a2f233e View commit details
  3. 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.
    whitequark committed Jun 26, 2017
    Copy the full SHA
    ac6efbf View commit details
Showing with 75 additions and 13 deletions.
  1. +4 −0 README.md
  2. +12 −7 examples/utils.rs
  3. +59 −6 src/socket/tcp.rs
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -159,6 +159,10 @@ implement fault injection, available through command-line options:
A good starting value for `--drop-chance` and `--corrupt-chance` is 15%. A good starting
value for `--?x-rate-limit` is 4 and `--shaping-interval` is 50 ms.

Note that packets dropped by the fault injector still get traced;
the `rx: randomly dropping a packet` message indicates that the packet *above* it got dropped,
and the `tx: randomly dropping a packet` message indicates that the packet *below* it was.

### examples/tcpdump.rs

_examples/tcpdump.rs_ is a tiny clone of the _tcpdump_ utility.
19 changes: 12 additions & 7 deletions examples/utils.rs
Original file line number Diff line number Diff line change
@@ -15,13 +15,18 @@ pub fn setup_logging() {
LogBuilder::new()
.format(move |record: &LogRecord| {
let elapsed = Instant::now().duration_since(startup_time);
if record.target().starts_with("smoltcp::") {
format!("\x1b[0m[{:6}.{:03}s] ({}): {}\x1b[0m",
elapsed.as_secs(), elapsed.subsec_nanos() / 1000000,
let timestamp = format!("[{:6}.{:03}s]",
elapsed.as_secs(), elapsed.subsec_nanos() / 1000000);
if record.target().ends_with("::utils") {
let mut message = format!("{}", record.args());
message.pop();
format!("\x1b[37m{} {}\x1b[0m", timestamp,
message.replace("\n", "\n "))
} else if record.target().starts_with("smoltcp::") {
format!("\x1b[0m{} ({}): {}\x1b[0m", timestamp,
record.target().replace("smoltcp::", ""), record.args())
} else {
format!("\x1b[32m[{:6}.{:03}s] ({}): {}\x1b[0m",
elapsed.as_secs(), elapsed.subsec_nanos() / 1000000,
format!("\x1b[32m{} ({}): {}\x1b[0m", timestamp,
record.target(), record.args())
}
})
@@ -68,18 +73,18 @@ pub fn setup_device(more_args: &[&str])
let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();

fn trace_writer(printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
print!("\x1b[37m{}\x1b[0m", printer)
trace!("{}", printer)
}

let device = TapInterface::new(&matches.free[0]).unwrap();
let device = Tracer::<_, EthernetFrame<&'static [u8]>>::new(device, trace_writer);
let mut device = FaultInjector::new(device, seed);
device.set_drop_chance(drop_chance);
device.set_corrupt_chance(corrupt_chance);
device.set_max_packet_size(size_limit);
device.set_max_tx_rate(tx_rate_limit);
device.set_max_rx_rate(rx_rate_limit);
device.set_bucket_interval(Duration::from_millis(shaping_interval as u64));
let device = Tracer::<_, EthernetFrame<&'static [u8]>>::new(device, trace_writer);

(device, matches.free[1..].to_owned())
}
65 changes: 59 additions & 6 deletions src/socket/tcp.rs
Original file line number Diff line number Diff line change
@@ -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();