Merge pull request #398 from smoltcp-rs/simultaneous-close
tcp: fix racey simultaneous close not sending FIN.
This commit is contained in:
commit
adce62a795
|
@ -1478,6 +1478,7 @@ impl<'a> TcpSocket<'a> {
|
|||
// Do we have to send a FIN?
|
||||
let want_fin = match self.state {
|
||||
State::FinWait1 => true,
|
||||
State::Closing => true,
|
||||
State::LastAck => true,
|
||||
_ => false,
|
||||
};
|
||||
|
@ -1627,9 +1628,8 @@ impl<'a> TcpSocket<'a> {
|
|||
}
|
||||
|
||||
// We transmit data in all states where we may have data in the buffer,
|
||||
// or the transmit half of the connection is still open:
|
||||
// the ESTABLISHED, FIN-WAIT-1, CLOSE-WAIT and LAST-ACK states.
|
||||
State::Established | State::FinWait1 | State::CloseWait | State::LastAck => {
|
||||
// or the transmit half of the connection is still open.
|
||||
State::Established | State::FinWait1 | State::Closing | State::CloseWait | State::LastAck => {
|
||||
// Extract as much data as the remote side can receive in this packet
|
||||
// from the transmit buffer.
|
||||
let offset = self.remote_last_seq - self.local_seq_no;
|
||||
|
@ -1641,7 +1641,7 @@ impl<'a> TcpSocket<'a> {
|
|||
// flags, depending on whether the transmit half of the connection is open.
|
||||
if offset + repr.payload.len() == self.tx_buffer.len() {
|
||||
match self.state {
|
||||
State::FinWait1 | State::LastAck =>
|
||||
State::FinWait1 | State::LastAck | State::Closing =>
|
||||
repr.control = TcpControl::Fin,
|
||||
State::Established | State::CloseWait if !repr.payload.is_empty() =>
|
||||
repr.control = TcpControl::Psh,
|
||||
|
@ -1650,13 +1650,8 @@ impl<'a> TcpSocket<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// We do not transmit data in the FIN-WAIT-2 state, but we may transmit
|
||||
// ACKs for incoming data.
|
||||
State::FinWait2 => {}
|
||||
|
||||
// We do not transmit data or control flags in the CLOSING or TIME-WAIT states,
|
||||
// but we may retransmit an ACK.
|
||||
State::Closing | State::TimeWait => ()
|
||||
// In FIN-WAIT-2 and TIME-WAIT states we may only transmit ACKs for incoming data or FIN
|
||||
State::FinWait2 | State::TimeWait => {}
|
||||
}
|
||||
|
||||
// There might be more than one reason to send a packet. E.g. the keep-alive timer
|
||||
|
@ -3483,6 +3478,74 @@ mod test {
|
|||
}]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simultaneous_close_raced() {
|
||||
let mut s = socket_established();
|
||||
s.close();
|
||||
assert_eq!(s.state, State::FinWait1);
|
||||
|
||||
// Socket receives FIN before it has a chance to send its own FIN
|
||||
send!(s, TcpRepr {
|
||||
control: TcpControl::Fin,
|
||||
seq_number: REMOTE_SEQ + 1,
|
||||
ack_number: Some(LOCAL_SEQ + 1),
|
||||
..SEND_TEMPL
|
||||
});
|
||||
assert_eq!(s.state, State::Closing);
|
||||
|
||||
// FIN + ack-of-FIN
|
||||
recv!(s, [TcpRepr {
|
||||
control: TcpControl::Fin,
|
||||
seq_number: LOCAL_SEQ + 1,
|
||||
ack_number: Some(REMOTE_SEQ + 1 + 1),
|
||||
..RECV_TEMPL
|
||||
}]);
|
||||
assert_eq!(s.state, State::Closing);
|
||||
|
||||
send!(s, TcpRepr {
|
||||
seq_number: REMOTE_SEQ + 1 + 1,
|
||||
ack_number: Some(LOCAL_SEQ + 1 + 1),
|
||||
..SEND_TEMPL
|
||||
});
|
||||
assert_eq!(s.state, State::TimeWait);
|
||||
recv!(s, []);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simultaneous_close_raced_with_data() {
|
||||
let mut s = socket_established();
|
||||
s.send_slice(b"abcdef").unwrap();
|
||||
s.close();
|
||||
assert_eq!(s.state, State::FinWait1);
|
||||
|
||||
// Socket receives FIN before it has a chance to send its own data+FIN
|
||||
send!(s, TcpRepr {
|
||||
control: TcpControl::Fin,
|
||||
seq_number: REMOTE_SEQ + 1,
|
||||
ack_number: Some(LOCAL_SEQ + 1),
|
||||
..SEND_TEMPL
|
||||
});
|
||||
assert_eq!(s.state, State::Closing);
|
||||
|
||||
// data + FIN + ack-of-FIN
|
||||
recv!(s, [TcpRepr {
|
||||
control: TcpControl::Fin,
|
||||
seq_number: LOCAL_SEQ + 1,
|
||||
ack_number: Some(REMOTE_SEQ + 1 + 1),
|
||||
payload: &b"abcdef"[..],
|
||||
..RECV_TEMPL
|
||||
}]);
|
||||
assert_eq!(s.state, State::Closing);
|
||||
|
||||
send!(s, TcpRepr {
|
||||
seq_number: REMOTE_SEQ + 1 + 1,
|
||||
ack_number: Some(LOCAL_SEQ + 1 + 6 + 1),
|
||||
..SEND_TEMPL
|
||||
});
|
||||
assert_eq!(s.state, State::TimeWait);
|
||||
recv!(s, []);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fin_with_data() {
|
||||
let mut s = socket_established();
|
||||
|
|
Loading…
Reference in New Issue