Skip to content

Commit

Permalink
Merge pull request #1001 from lrh2000/sim-open
Browse files Browse the repository at this point in the history
Support TCP simultaneous open
  • Loading branch information
whitequark authored Oct 20, 2024
2 parents 45fa984 + 06eb8b7 commit 032094e
Showing 1 changed file with 140 additions and 8 deletions.
148 changes: 140 additions & 8 deletions src/socket/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1508,18 +1508,17 @@ impl<'a> Socket<'a> {
(State::Listen, _, None) => (),
// This case is handled in `accepts()`.
(State::Listen, _, Some(_)) => unreachable!(),
// Every packet after the initial SYN must be an acknowledgement.
(_, _, None) => {
net_debug!("expecting an ACK");
return None;
}
// SYN|ACK in the SYN-SENT state must have the exact ACK number.
(State::SynSent, TcpControl::Syn, Some(ack_number)) => {
if ack_number != self.local_seq_no + 1 {
net_debug!("unacceptable SYN|ACK in response to initial SYN");
return Some(Self::rst_reply(ip_repr, repr));
}
}
// TCP simultaneous open.
// This is required by RFC 9293, which states "A TCP implementation MUST support
// simultaneous open attempts (MUST-10)."
(State::SynSent, TcpControl::Syn, None) => (),
// ACKs in the SYN-SENT state are invalid.
(State::SynSent, TcpControl::None, Some(ack_number)) => {
// If the sequence number matches, ignore it instead of RSTing.
Expand All @@ -1543,6 +1542,11 @@ impl<'a> Socket<'a> {
net_debug!("expecting a SYN|ACK");
return None;
}
// Every packet after the initial SYN must be an acknowledgement.
(_, _, None) => {
net_debug!("expecting an ACK");
return None;
}
// ACK in the SYN-RECEIVED state must have the exact ACK number, or we RST it.
(State::SynReceived, _, Some(ack_number)) => {
if ack_number != self.local_seq_no + 1 {
Expand Down Expand Up @@ -1722,7 +1726,10 @@ impl<'a> Socket<'a> {
(State::Listen, TcpControl::Rst) => return None,

// RSTs in SYN-RECEIVED flip the socket back to the LISTEN state.
(State::SynReceived, TcpControl::Rst) => {
// Here we need to additionally check `listen_endpoint`, because we want to make sure
// that SYN-RECEIVED was actually converted from the LISTEN state (another possible
// reason is TCP simultaneous open).
(State::SynReceived, TcpControl::Rst) if self.listen_endpoint.port != 0 => {
tcp_trace!("received RST");
self.tuple = None;
self.set_state(State::Listen);
Expand Down Expand Up @@ -1789,8 +1796,13 @@ impl<'a> Socket<'a> {
}

// SYN|ACK packets in the SYN-SENT state change it to ESTABLISHED.
// SYN packets in the SYN-SENT state change it to SYN-RECEIVED.
(State::SynSent, TcpControl::Syn) => {
tcp_trace!("received SYN|ACK");
if repr.ack_number.is_some() {
tcp_trace!("received SYN|ACK");
} else {
tcp_trace!("received SYN");
}
if let Some(max_seg_size) = repr.max_seg_size {
if max_seg_size == 0 {
tcp_trace!("received SYNACK with zero MSS, ignoring");
Expand All @@ -1816,7 +1828,11 @@ impl<'a> Socket<'a> {
self.tsval_generator = None;
}

self.set_state(State::Established);
if repr.ack_number.is_some() {
self.set_state(State::Established);
} else {
self.set_state(State::SynReceived);
}
self.timer.set_for_idle(cx.now(), self.keep_alive);
}

Expand Down Expand Up @@ -2333,6 +2349,7 @@ impl<'a> Socket<'a> {
// We transmit a SYN|ACK in the SYN-RECEIVED state.
State::SynSent | State::SynReceived => {
repr.control = TcpControl::Syn;
repr.seq_number = self.local_seq_no;
// window len must NOT be scaled in SYNs.
repr.window_len = u16::try_from(self.rx_buffer.window()).unwrap_or(u16::MAX);
if self.state == State::SynSent {
Expand Down Expand Up @@ -3537,6 +3554,78 @@ mod test {
sanity!(s, socket_established());
}

#[test]
fn test_syn_sent_syn_received_ack() {
let mut s = socket_syn_sent();
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
}]
);

// A SYN packet changes the SYN-SENT state to SYN-RECEIVED.
send!(
s,
TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: None,
max_seg_size: Some(BASE_MSS - 80),
window_scale: Some(0),
..SEND_TEMPL
}
);
assert_eq!(s.state, State::SynReceived);

// The socket will then send a SYN|ACK packet.
recv!(
s,
[TcpRepr {
control: TcpControl::Syn,
seq_number: LOCAL_SEQ,
ack_number: Some(REMOTE_SEQ + 1),
max_seg_size: Some(BASE_MSS),
window_scale: Some(0),
..RECV_TEMPL
}]
);
recv_nothing!(s);

// The socket may retransmit the SYN|ACK packet.
recv!(
s,
time 1001,
Ok(TcpRepr {
control: TcpControl::Syn,
seq_number: LOCAL_SEQ,
ack_number: Some(REMOTE_SEQ + 1),
max_seg_size: Some(BASE_MSS),
window_scale: Some(0),
..RECV_TEMPL
})
);

// An ACK packet changes the SYN-RECEIVED state to ESTABLISHED.
send!(
s,
TcpRepr {
control: TcpControl::None,
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
..SEND_TEMPL
}
);
assert_eq!(s.state, State::Established);
sanity!(s, socket_established());
}

#[test]
fn test_syn_sent_syn_ack_not_incremented() {
let mut s = socket_syn_sent();
Expand Down Expand Up @@ -3573,6 +3662,49 @@ mod test {
assert_eq!(s.state, State::SynSent);
}

#[test]
fn test_syn_sent_syn_received_rst() {
let mut s = socket_syn_sent();
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
}]
);

// A SYN packet changes the SYN-SENT state to SYN-RECEIVED.
send!(
s,
TcpRepr {
control: TcpControl::Syn,
seq_number: REMOTE_SEQ,
ack_number: None,
max_seg_size: Some(BASE_MSS - 80),
window_scale: Some(0),
..SEND_TEMPL
}
);
assert_eq!(s.state, State::SynReceived);

// A RST packet changes the SYN-RECEIVED state to CLOSED.
send!(
s,
TcpRepr {
control: TcpControl::Rst,
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ),
..SEND_TEMPL
}
);
assert_eq!(s.state, State::Closed);
}

#[test]
fn test_syn_sent_rst() {
let mut s = socket_syn_sent();
Expand Down

0 comments on commit 032094e

Please # to comment.