Browse Source

Implement fast retransmit for TcpSocket

Implement a simple way to discover and respond to three duplicate ACKs
from the reciver of the data. This leads to a fast retransmit of the
last unacknowledged seqment. This is feature is described in [RFC 5681].

Closes: #104

Closes: #214
Approved by: whitequark
Ole Martin Ruud 6 years ago
parent
commit
375b0266fe
1 changed files with 116 additions and 4 deletions
  1. 116 4
      src/socket/tcp.rs

+ 116 - 4
src/socket/tcp.rs

@@ -59,6 +59,7 @@ enum Timer {
         expires_at: Instant,
         delay:      Duration
     },
+    FastRetransmit,
     Close {
         expires_at: Instant
     }
@@ -89,7 +90,8 @@ impl Timer {
             Timer::Retransmit { expires_at, delay }
                     if timestamp >= expires_at => {
                 Some(timestamp - expires_at + delay)
-            }
+            },
+            Timer::FastRetransmit => Some(Duration::from_millis(0)),
             _ => None
         }
     }
@@ -109,6 +111,7 @@ impl Timer {
             Timer::Idle { keep_alive_at: Some(keep_alive_at) } => PollAt::Time(keep_alive_at),
             Timer::Idle { keep_alive_at: None } => PollAt::Ingress,
             Timer::Retransmit { expires_at, .. } => PollAt::Time(expires_at),
+            Timer::FastRetransmit => PollAt::Now,
             Timer::Close { expires_at } => PollAt::Time(expires_at),
         }
     }
@@ -154,10 +157,15 @@ impl Timer {
                 }
             }
             Timer::Retransmit { .. } => (),
+            Timer::FastRetransmit { .. } => (),
             Timer::Close { .. } => ()
         }
     }
 
+    fn set_for_fast_retransmit(&mut self) {
+        *self = Timer::FastRetransmit
+    }
+
     fn set_for_close(&mut self, timestamp: Instant) {
         *self = Timer::Close {
             expires_at: timestamp + CLOSE_DELAY
@@ -166,7 +174,7 @@ impl Timer {
 
     fn is_retransmit(&self) -> bool {
         match *self {
-            Timer::Retransmit {..} => true,
+            Timer::Retransmit {..} | Timer::FastRetransmit => true,
             _ => false,
         }
     }
@@ -226,6 +234,11 @@ pub struct TcpSocket<'a> {
     remote_mss:      usize,
     /// The timestamp of the last packet received.
     remote_last_ts:  Option<Instant>,
+    /// The ACK number of the last packet recived. 
+    local_rx_last_ack: Option<TcpSeqNumber>,
+    /// The number of packets recived directly after 
+    /// each other which have the same ACK number.
+    local_rx_dup_acks: u8,
 }
 
 const DEFAULT_MSS: usize = 536;
@@ -261,6 +274,8 @@ impl<'a> TcpSocket<'a> {
             remote_win_len:  0,
             remote_mss:      DEFAULT_MSS,
             remote_last_ts:  None,
+            local_rx_last_ack: None,
+            local_rx_dup_acks: 0,
         }
     }
 
@@ -872,12 +887,10 @@ 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 {
                     net_debug!("{}:{}:{}: duplicate ACK ({} not in {}...{})",
                                self.meta.handle, self.local_endpoint, self.remote_endpoint,
                                ack_number, self.local_seq_no, self.local_seq_no + unacknowledged);
-                    // FIXME: implement fast retransmit
                     return Err(Error::Dropped)
                 }
 
@@ -887,6 +900,29 @@ impl<'a> TcpSocket<'a> {
                                ack_number, self.local_seq_no, self.local_seq_no + unacknowledged);
                     return Ok(Some(self.ack_reply(ip_repr, &repr)))
                 }
+
+                // 1. Check if duplicate ACK, and change self.local_rx_dup_acks accordingly
+                // 2. Update the last received ACK (self.local_rx_last_ack)
+                match self.local_rx_last_ack {
+                    // We have a duplicate ACK -> Increment dup ACKs count and 
+                    // set for retransmit if we just recived the third dup ACK
+                    Some(ref mut last_rx_ack) if *last_rx_ack == ack_number => {
+                        net_debug!("{}:{}:{}: duplicate ACK number {} for seq {}",
+                                 self.meta.handle, self.local_endpoint, self.remote_endpoint, 
+                                 self.local_rx_dup_acks, ack_number);
+                        self.local_rx_dup_acks += 1;
+                        if self.local_rx_dup_acks == 3 {
+                            self.timer.set_for_fast_retransmit();
+                            net_debug!("{}:{}:{}: set a fast retransmit for seq {}",
+                                     self.meta.handle, self.local_endpoint, self.remote_endpoint, ack_number);
+                        }
+                    },
+                    // We do not have a duplicate ACK -> Reset state
+                    _ => {
+                        self.local_rx_last_ack = Some(ack_number);
+                        self.local_rx_dup_acks = 1;
+                    }
+                };
             }
         }
 
@@ -3167,6 +3203,82 @@ mod test {
         }));
     }
 
+    #[test]
+    fn test_fast_retransmit_after_triple_duplicate_ack() {
+        let mut s = socket_established();
+        s.remote_win_len = 6;
+        
+        s.send_slice(b"xxxxxxabcdef123456ABCDEFZYXWVU").unwrap();
+
+        recv!(s, time 1000, Ok(TcpRepr {
+            seq_number: LOCAL_SEQ + 1,
+            ack_number: Some(REMOTE_SEQ + 1),
+            payload:    &b"xxxxxx"[..],
+            ..RECV_TEMPL
+        }));
+        // This packet is lost
+        recv!(s, time 1005, Ok(TcpRepr {
+            seq_number: LOCAL_SEQ + 1 + 6,
+            ack_number: Some(REMOTE_SEQ + 1),
+            payload:    &b"abcdef"[..],
+            ..RECV_TEMPL
+        }));
+        recv!(s, time 1010, Ok(TcpRepr {
+            seq_number: LOCAL_SEQ + 1 + 6 + 6,
+            ack_number: Some(REMOTE_SEQ + 1),
+            payload:    &b"123456"[..],
+            ..RECV_TEMPL
+        }));
+        recv!(s, time 1015, Ok(TcpRepr {
+            seq_number: LOCAL_SEQ + 1 + 6 + 6 + 6,
+            ack_number: Some(REMOTE_SEQ + 1),
+            payload:    &b"ABCDEF"[..],
+            ..RECV_TEMPL
+        }));
+        recv!(s, time 1020, Ok(TcpRepr {
+            seq_number: LOCAL_SEQ + 1 + 6 + 6 + 6 + 6,
+            ack_number: Some(REMOTE_SEQ + 1),
+            payload:    &b"ZYXWVU"[..],
+            ..RECV_TEMPL
+        }));
+        // First ACK
+        send!(s, time 1025, TcpRepr {
+            seq_number: REMOTE_SEQ + 1,
+            ack_number: Some(LOCAL_SEQ + 1 + 6),
+            window_len: 6,
+            ..SEND_TEMPL
+        });
+        // Second (duplicate) ACK
+        send!(s, time 1030, TcpRepr {
+            seq_number: REMOTE_SEQ + 1,
+            ack_number: Some(LOCAL_SEQ + 1 + 6),
+            window_len: 6,
+            ..SEND_TEMPL
+        });
+        // Third (duplicate) ACK
+        // Should trigger a fast retransmit of dropped packet
+        send!(s, time 1035, TcpRepr {
+            seq_number: REMOTE_SEQ + 1,
+            ack_number: Some(LOCAL_SEQ + 1 + 6),
+            window_len: 6,
+            ..SEND_TEMPL
+        });
+        // Fast retransmit packet
+        recv!(s, time 1040, Ok(TcpRepr {
+            seq_number: LOCAL_SEQ + 1 + 6,
+            ack_number: Some(REMOTE_SEQ + 1),
+            payload:    &b"abcdef"[..],
+            ..RECV_TEMPL
+        }));
+        // ACK all recived segments
+        send!(s, time 1045, TcpRepr {
+            seq_number: REMOTE_SEQ + 1,
+            ack_number: Some(LOCAL_SEQ + 1 + (6 * 5)),
+            window_len: 6,
+            ..SEND_TEMPL
+        });
+    }
+
     // =========================================================================================//
     // Tests for window management.
     // =========================================================================================//