Skip to content

Commit

Permalink
Merge pull request #939 from tomDev5/tcp_timestamp
Browse files Browse the repository at this point in the history
Support TCP Timestamp option
  • Loading branch information
whitequark authored Jun 26, 2024
2 parents 57b1dd4 + 89e8265 commit 23697fa
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/iface/interface/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ pub fn tcp_not_accepted() {
max_seg_size: None,
sack_permitted: false,
sack_ranges: [None, None, None],
timestamp: None,
payload: &[],
};

Expand Down Expand Up @@ -212,6 +213,7 @@ pub fn tcp_not_accepted() {
max_seg_size: None,
sack_permitted: false,
sack_ranges: [None, None, None],
timestamp: None,
payload: &[],
})
))
Expand Down
282 changes: 279 additions & 3 deletions src/socket/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +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. Consult RFC 7414 when implementing
// a new feature.
// the parts of RFC 1122 that discuss TCP, as well as RFC 7323 for some of the TCP options.
// Consult RFC 7414 when implementing a new feature.

use core::fmt::Display;
#[cfg(feature = "async")]
Expand All @@ -14,7 +14,7 @@ use crate::storage::{Assembler, RingBuffer};
use crate::time::{Duration, Instant};
use crate::wire::{
IpAddress, IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, TcpControl, TcpRepr, TcpSeqNumber,
TCP_HEADER_LEN,
TcpTimestampGenerator, TcpTimestampRepr, TCP_HEADER_LEN,
};

mod congestion;
Expand Down Expand Up @@ -482,6 +482,12 @@ pub struct Socket<'a> {
/// The congestion control algorithm.
congestion_controller: congestion::AnyController,

/// tsval generator - if some, tcp timestamp is enabled
tsval_generator: Option<TcpTimestampGenerator>,

/// 0 if not seen or timestamp not enabled
last_remote_tsval: u32,

#[cfg(feature = "async")]
rx_waker: WakerRegistration,
#[cfg(feature = "async")]
Expand Down Expand Up @@ -540,6 +546,8 @@ impl<'a> Socket<'a> {
ack_delay_timer: AckDelayTimer::Idle,
challenge_ack_timer: Instant::from_secs(0),
nagle: true,
tsval_generator: None,
last_remote_tsval: 0,
congestion_controller: congestion::AnyController::new(),

#[cfg(feature = "async")]
Expand All @@ -549,6 +557,16 @@ impl<'a> Socket<'a> {
}
}

/// Enable or disable TCP Timestamp.
pub fn set_tsval_generator(&mut self, generator: Option<TcpTimestampGenerator>) {
self.tsval_generator = generator;
}

/// Return whether TCP Timestamp is enabled.
pub fn timestamp_enabled(&self) -> bool {
self.tsval_generator.is_some()
}

/// Set an algorithm for congestion control.
///
/// `CongestionControl::None` indicates that no congestion control is applied.
Expand Down Expand Up @@ -1300,6 +1318,7 @@ impl<'a> Socket<'a> {
max_seg_size: None,
sack_permitted: false,
sack_ranges: [None, None, None],
timestamp: None,
payload: &[],
};
let ip_reply_repr = IpRepr::new(
Expand Down Expand Up @@ -1330,6 +1349,9 @@ impl<'a> Socket<'a> {

fn ack_reply(&mut self, ip_repr: &IpRepr, repr: &TcpRepr) -> (IpRepr, TcpRepr<'static>) {
let (mut ip_reply_repr, mut reply_repr) = Self::reply(ip_repr, repr);
reply_repr.timestamp = repr
.timestamp
.and_then(|tcp_ts| tcp_ts.generate_reply(self.tsval_generator));

// From RFC 793:
// [...] an empty acknowledgment segment containing the current send-sequence number
Expand Down Expand Up @@ -1725,6 +1747,10 @@ impl<'a> Socket<'a> {
if self.remote_win_scale.is_none() {
self.remote_win_shift = 0;
}
// Remote doesn't support timestamping, don't do it.
if repr.timestamp.is_none() {
self.tsval_generator = None;
}
self.set_state(State::SynReceived);
self.timer.set_for_idle(cx.now(), self.keep_alive);
}
Expand Down Expand Up @@ -1767,6 +1793,10 @@ impl<'a> Socket<'a> {
if self.remote_win_scale.is_none() {
self.remote_win_shift = 0;
}
// Remote doesn't support timestamping, don't do it.
if repr.timestamp.is_none() {
self.tsval_generator = None;
}

self.set_state(State::Established);
self.timer.set_for_idle(cx.now(), self.keep_alive);
Expand Down Expand Up @@ -1954,6 +1984,11 @@ impl<'a> Socket<'a> {
}
}

// update last remote tsval
if let Some(timestamp) = repr.timestamp {
self.last_remote_tsval = timestamp.tsval;
}

let payload_len = payload.len();
if payload_len == 0 {
return None;
Expand Down Expand Up @@ -2246,6 +2281,10 @@ impl<'a> Socket<'a> {
max_seg_size: None,
sack_permitted: false,
sack_ranges: [None, None, None],
timestamp: TcpTimestampRepr::generate_reply_with_tsval(
self.tsval_generator,
self.last_remote_tsval,
),
payload: &[],
};

Expand Down Expand Up @@ -2574,6 +2613,7 @@ mod test {
max_seg_size: None,
sack_permitted: false,
sack_ranges: [None, None, None],
timestamp: None,
payload: &[],
};
const _RECV_IP_TEMPL: IpRepr = IpReprIpvX(IpvXRepr {
Expand All @@ -2594,6 +2634,7 @@ mod test {
max_seg_size: None,
sack_permitted: false,
sack_ranges: [None, None, None],
timestamp: None,
payload: &[],
};

Expand Down Expand Up @@ -7429,4 +7470,239 @@ mod test {
s.set_congestion_control(CongestionControl::None);
assert_eq!(s.congestion_control(), CongestionControl::None);
}

// =========================================================================================//
// Timestamp tests
// =========================================================================================//

#[test]
fn test_tsval_established_connection() {
let mut s = socket_established();
s.set_tsval_generator(Some(|| 1));

assert!(s.timestamp_enabled());

// First roundtrip after establishing.
s.send_slice(b"abcdef").unwrap();
recv!(
s,
[TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1),
payload: &b"abcdef"[..],
timestamp: Some(TcpTimestampRepr::new(1, 0)),
..RECV_TEMPL
}]
);
assert_eq!(s.tx_buffer.len(), 6);
send!(
s,
TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1 + 6),
timestamp: Some(TcpTimestampRepr::new(500, 1)),
..SEND_TEMPL
}
);
assert_eq!(s.tx_buffer.len(), 0);
// Second roundtrip.
s.send_slice(b"foobar").unwrap();
recv!(
s,
[TcpRepr {
seq_number: LOCAL_SEQ + 1 + 6,
ack_number: Some(REMOTE_SEQ + 1),
payload: &b"foobar"[..],
timestamp: Some(TcpTimestampRepr::new(1, 500)),
..RECV_TEMPL
}]
);
send!(
s,
TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1 + 6 + 6),
..SEND_TEMPL
}
);
assert_eq!(s.tx_buffer.len(), 0);
}

#[test]
fn test_tsval_disabled_in_remote_client() {
let mut s = socket_listen();
s.set_tsval_generator(Some(|| 1));
assert!(s.timestamp_enabled());
send!(
s,
TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: None,
..SEND_TEMPL
}
);
assert_eq!(s.state(), State::SynReceived);
assert_eq!(s.tuple, Some(TUPLE));
assert!(!s.timestamp_enabled());
recv!(
s,
[TcpRepr {
control: TcpControl::Syn,
seq_number: LOCAL_SEQ,
ack_number: Some(REMOTE_SEQ + 1),
max_seg_size: Some(BASE_MSS),
..RECV_TEMPL
}]
);
send!(
s,
TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
..SEND_TEMPL
}
);
assert_eq!(s.state(), State::Established);
assert_eq!(s.local_seq_no, LOCAL_SEQ + 1);
assert_eq!(s.remote_seq_no, REMOTE_SEQ + 1);
}

#[test]
fn test_tsval_disabled_in_local_server() {
let mut s = socket_listen();
// s.set_timestamp(false); // commented to alert if the default state changes
assert!(!s.timestamp_enabled());
send!(
s,
TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: None,
timestamp: Some(TcpTimestampRepr::new(500, 0)),
..SEND_TEMPL
}
);
assert_eq!(s.state(), State::SynReceived);
assert_eq!(s.tuple, Some(TUPLE));
assert!(!s.timestamp_enabled());
recv!(
s,
[TcpRepr {
control: TcpControl::Syn,
seq_number: LOCAL_SEQ,
ack_number: Some(REMOTE_SEQ + 1),
max_seg_size: Some(BASE_MSS),
..RECV_TEMPL
}]
);
send!(
s,
TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
..SEND_TEMPL
}
);
assert_eq!(s.state(), State::Established);
assert_eq!(s.local_seq_no, LOCAL_SEQ + 1);
assert_eq!(s.remote_seq_no, REMOTE_SEQ + 1);
}

#[test]
fn test_tsval_disabled_in_remote_server() {
let mut s = socket();
s.set_tsval_generator(Some(|| 1));
assert!(s.timestamp_enabled());
s.local_seq_no = LOCAL_SEQ;
s.socket
.connect(&mut s.cx, REMOTE_END, LOCAL_END.port)
.unwrap();
assert_eq!(s.tuple, Some(TUPLE));
recv!(
s,
[TcpRepr {
control: TcpControl::Syn,
seq_number: LOCAL_SEQ,
ack_number: None,
max_seg_size: Some(BASE_MSS),
window_scale: Some(0),
sack_permitted: true,
timestamp: Some(TcpTimestampRepr::new(1, 0)),
..RECV_TEMPL
}]
);
send!(
s,
TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: Some(LOCAL_SEQ + 1),
max_seg_size: Some(BASE_MSS - 80),
window_scale: Some(0),
timestamp: None,
..SEND_TEMPL
}
);
assert!(!s.timestamp_enabled());
s.send_slice(b"abcdef").unwrap();
recv!(
s,
[TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1),
payload: &b"abcdef"[..],
timestamp: None,
..RECV_TEMPL
}]
);
}

#[test]
fn test_tsval_disabled_in_local_client() {
let mut s = socket();
// s.set_timestamp(false); // commented to alert if the default state changes
assert!(!s.timestamp_enabled());
s.local_seq_no = LOCAL_SEQ;
s.socket
.connect(&mut s.cx, REMOTE_END, LOCAL_END.port)
.unwrap();
assert_eq!(s.tuple, Some(TUPLE));
recv!(
s,
[TcpRepr {
control: TcpControl::Syn,
seq_number: LOCAL_SEQ,
ack_number: None,
max_seg_size: Some(BASE_MSS),
window_scale: Some(0),
sack_permitted: true,
..RECV_TEMPL
}]
);
send!(
s,
TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: Some(LOCAL_SEQ + 1),
max_seg_size: Some(BASE_MSS - 80),
window_scale: Some(0),
timestamp: Some(TcpTimestampRepr::new(500, 0)),
..SEND_TEMPL
}
);
assert!(!s.timestamp_enabled());
s.send_slice(b"abcdef").unwrap();
recv!(
s,
[TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1),
payload: &b"abcdef"[..],
timestamp: None,
..RECV_TEMPL
}]
);
}
}
Loading

0 comments on commit 23697fa

Please sign in to comment.