Implement the TCP TIME-WAIT state.

This commit is contained in:
whitequark 2016-12-28 05:33:12 +00:00
parent 94963faf12
commit 287affb447
4 changed files with 150 additions and 34 deletions

View File

@ -52,11 +52,12 @@ The TCP protocol is supported over IPv4.
* TCP header checksum is supported. * TCP header checksum is supported.
* Multiple packets will be transmitted without waiting for an acknowledgement. * Multiple packets will be transmitted without waiting for an acknowledgement.
* TCP urgent pointer is **not** supported; any urgent octets will be received alongside data. * TCP urgent pointer is **not** supported; any urgent octets will be received alongside
data octets.
* Reassembly of out-of-order segments is **not** supported. * Reassembly of out-of-order segments is **not** supported.
* TCP options are **not** supported, in particular: * TCP options are **not** supported, in particular:
* Maximum segment size is hardcoded at the default value, 536. * Maximum segment size is hardcoded at the default value, 536.
* Window scaling is **not** supported. * Window scaling is **not** supported, and the maximum buffer size is 65536.
* Keepalive is **not** supported. * Keepalive is **not** supported.
Installation Installation
@ -143,7 +144,9 @@ It responds to:
* pings (`ping 192.168.69.1`); * pings (`ping 192.168.69.1`);
* UDP packets on port 6969 (`socat stdio udp4-connect:192.168.69.1:6969 <<<"abcdefg"`), * UDP packets on port 6969 (`socat stdio udp4-connect:192.168.69.1:6969 <<<"abcdefg"`),
where it will respond "yo dawg" to any incoming packet; where it will respond "yo dawg" to any incoming packet;
* TCP packets on port 6969 (`socat stdio tcp4-connect:192.168.69.1:6969 <<<"abcdefg"`), * TCP packets on port 6969 (`socat stdio tcp4-connect:192.168.69.1:6969`),
where it will respond "yo dawg" to any incoming connection and immediately close it;
* TCP packets on port 6970 (`socat stdio tcp4-connect:192.168.69.1:6970 <<<"abcdefg"`),
where it will respond with reversed chunks of the input indefinitely. where it will respond with reversed chunks of the input indefinitely.
The buffers are only 64 bytes long, for convenience of testing resource exhaustion conditions. The buffers are only 64 bytes long, for convenience of testing resource exhaustion conditions.

View File

@ -53,23 +53,28 @@ fn main() {
let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 128])]); let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 128])]);
let udp_socket = UdpSocket::new(endpoint, udp_rx_buffer, udp_tx_buffer); let udp_socket = UdpSocket::new(endpoint, udp_rx_buffer, udp_tx_buffer);
let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 64]); let tcp1_rx_buffer = TcpSocketBuffer::new(vec![0; 64]);
let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 128]); let tcp1_tx_buffer = TcpSocketBuffer::new(vec![0; 128]);
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); let tcp1_socket = TcpSocket::new(tcp1_rx_buffer, tcp1_tx_buffer);
let tcp2_rx_buffer = TcpSocketBuffer::new(vec![0; 64]);
let tcp2_tx_buffer = TcpSocketBuffer::new(vec![0; 128]);
let tcp2_socket = TcpSocket::new(tcp2_rx_buffer, tcp2_tx_buffer);
let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let protocol_addrs = [IpAddress::v4(192, 168, 69, 1)]; let protocol_addrs = [IpAddress::v4(192, 168, 69, 1)];
let sockets = vec![udp_socket, tcp_socket]; let sockets = vec![udp_socket, tcp1_socket, tcp2_socket];
let mut iface = EthernetInterface::new(device, arp_cache, let mut iface = EthernetInterface::new(device, arp_cache,
hardware_addr, protocol_addrs, sockets); hardware_addr, protocol_addrs, sockets);
let mut tcp_6969_connected = false; let mut tcp_6969_connected = false;
loop { loop {
// udp:6969: respond "yo dawg"
{ {
let socket: &mut UdpSocket = iface.sockets()[0].as_socket(); let socket: &mut UdpSocket = iface.sockets()[0].as_socket();
let client = match socket.recv() { let client = match socket.recv() {
Ok((endpoint, data)) => { Ok((endpoint, data)) => {
debug!("udp recv data: {:?} from {}", debug!("udp:6969 recv data: {:?} from {}",
str::from_utf8(data.as_ref()).unwrap(), endpoint); str::from_utf8(data.as_ref()).unwrap(), endpoint);
Some(endpoint) Some(endpoint)
} }
@ -77,28 +82,46 @@ fn main() {
None None
} }
Err(e) => { Err(e) => {
debug!("udp recv error: {}", e); debug!("udp:6969 recv error: {}", e);
None None
} }
}; };
if let Some(endpoint) = client { if let Some(endpoint) = client {
let data = b"yo dawg"; let data = b"yo dawg\n";
debug!("udp send data: {:?}", debug!("udp:6969 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap()); str::from_utf8(data.as_ref()).unwrap());
socket.send_slice(endpoint, data).unwrap() socket.send_slice(endpoint, data).unwrap()
} }
} }
// tcp:6969: respond "yo dawg"
{ {
let socket: &mut TcpSocket = iface.sockets()[1].as_socket(); let socket: &mut TcpSocket = iface.sockets()[1].as_socket();
if !socket.is_open() { if !socket.is_open() {
socket.listen(endpoint).unwrap() socket.listen(6969).unwrap();
}
if socket.can_send() {
let data = b"yo dawg\n";
debug!("tcp:6969 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap());
socket.send_slice(data).unwrap();
debug!("tcp:6969 close");
socket.close();
}
}
// tcp:6970: echo with reverse
{
let socket: &mut TcpSocket = iface.sockets()[2].as_socket();
if !socket.is_open() {
socket.listen(6970).unwrap()
} }
if socket.is_connected() && !tcp_6969_connected { if socket.is_connected() && !tcp_6969_connected {
debug!("tcp connected"); debug!("tcp:6970 connected");
} else if !socket.is_connected() && tcp_6969_connected { } else if !socket.is_connected() && tcp_6969_connected {
debug!("tcp disconnected"); debug!("tcp:6970 disconnected");
} }
tcp_6969_connected = socket.is_connected(); tcp_6969_connected = socket.is_connected();
@ -106,7 +129,7 @@ fn main() {
let data = { let data = {
let mut data = socket.recv(128).unwrap().to_owned(); let mut data = socket.recv(128).unwrap().to_owned();
if data.len() > 0 { if data.len() > 0 {
debug!("tcp recv data: {:?}", debug!("tcp:6970 recv data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat(); data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
data.reverse(); data.reverse();
@ -115,13 +138,13 @@ fn main() {
data data
}; };
if socket.can_send() && data.len() > 0 { if socket.can_send() && data.len() > 0 {
debug!("tcp send data: {:?}", debug!("tcp:6970 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
socket.send_slice(&data[..]).unwrap(); socket.send_slice(&data[..]).unwrap();
} }
} else if socket.can_send() { } else if socket.can_send() {
debug!("tcp:6970 close");
socket.close(); socket.close();
debug!("tcp closed")
} }
} }

View File

@ -249,9 +249,10 @@ impl<'a> TcpSocket<'a> {
/// Start listening on the given endpoint. /// Start listening on the given endpoint.
/// ///
/// This function returns an error if the socket was open; see [is_open](#method.is_open). /// This function returns an error if the socket was open; see [is_open](#method.is_open).
pub fn listen(&mut self, endpoint: IpEndpoint) -> Result<(), ()> { pub fn listen<T: Into<IpEndpoint>>(&mut self, endpoint: T) -> Result<(), ()> {
if self.is_open() { return Err(()) } if self.is_open() { return Err(()) }
let endpoint = endpoint.into();
self.listen_address = endpoint.addr; self.listen_address = endpoint.addr;
self.local_endpoint = endpoint; self.local_endpoint = endpoint;
self.remote_endpoint = IpEndpoint::default(); self.remote_endpoint = IpEndpoint::default();
@ -507,8 +508,8 @@ impl<'a> TcpSocket<'a> {
let control_len = match state { let control_len = match state {
// In SYN-SENT or SYN-RECEIVED, we've just sent a SYN. // In SYN-SENT or SYN-RECEIVED, we've just sent a SYN.
State::SynSent | State::SynReceived => 1, State::SynSent | State::SynReceived => 1,
// In FIN-WAIT-1 or LAST-ACK, we've just sent a FIN. // In FIN-WAIT-1, LAST-ACK, or CLOSING, we've just sent a FIN.
State::FinWait1 | State::LastAck => 1, State::FinWait1 | State::LastAck | State::Closing => 1,
// In all other states we've already got acknowledgemetns for // In all other states we've already got acknowledgemetns for
// all of the control flags we sent. // all of the control flags we sent.
_ => 0 _ => 0
@ -625,9 +626,9 @@ impl<'a> TcpSocket<'a> {
// ACK packets in CLOSING state change it to TIME-WAIT. // ACK packets in CLOSING state change it to TIME-WAIT.
(State::Closing, TcpRepr { control: TcpControl::None, .. }) => { (State::Closing, TcpRepr { control: TcpControl::None, .. }) => {
// Clear the remote endpoint, or we'll send an ACK there. self.local_seq_no += 1;
self.remote_endpoint = IpEndpoint::default();
self.set_state(State::TimeWait); self.set_state(State::TimeWait);
self.retransmit.reset();
} }
// ACK packets in CLOSE-WAIT state do nothing. // ACK packets in CLOSE-WAIT state do nothing.
@ -696,8 +697,8 @@ impl<'a> TcpSocket<'a> {
let mut should_send = false; let mut should_send = false;
match self.state { match self.state {
// We never transmit anything in the CLOSED, LISTEN, TIME-WAIT or FIN-WAIT-2 states. // We never transmit anything in the CLOSED, LISTEN, or FIN-WAIT-2 states.
State::Closed | State::Listen | State::TimeWait | State::FinWait2 => { State::Closed | State::Listen | State::FinWait2 => {
return Err(Error::Exhausted) return Err(Error::Exhausted)
} }
@ -723,11 +724,11 @@ impl<'a> TcpSocket<'a> {
} }
// We transmit data in the ESTABLISHED state, // We transmit data in the ESTABLISHED state,
// ACK in CLOSE-WAIT and CLOSING states, // ACK in CLOSE-WAIT, CLOSING, and TIME-WAIT states,
// FIN in FIN-WAIT-1 and LAST-ACK states. // FIN in FIN-WAIT-1 and LAST-ACK states.
State::Established | State::Established |
State::CloseWait | State::LastAck | State::CloseWait | State::Closing | State::TimeWait |
State::FinWait1 | State::Closing => { State::FinWait1 | State::LastAck => {
// See if we should send data to the remote end because: // See if we should send data to the remote end because:
let mut may_send = false; let mut may_send = false;
// 1. the retransmit timer has expired or was reset, or... // 1. the retransmit timer has expired or was reset, or...
@ -1375,7 +1376,6 @@ mod test {
..SEND_TEMPL ..SEND_TEMPL
}]); }]);
assert_eq!(s.state, State::TimeWait); assert_eq!(s.state, State::TimeWait);
assert!(!s.remote_endpoint.is_unspecified());
} }
#[test] #[test]
@ -1391,7 +1391,7 @@ mod test {
fn socket_closing() -> TcpSocket<'static> { fn socket_closing() -> TcpSocket<'static> {
let mut s = socket_fin_wait_1(); let mut s = socket_fin_wait_1();
s.state = State::Closing; s.state = State::Closing;
s.local_seq_no = LOCAL_SEQ + 1 + 1; s.local_seq_no = LOCAL_SEQ + 1;
s.remote_seq_no = REMOTE_SEQ + 1 + 1; s.remote_seq_no = REMOTE_SEQ + 1 + 1;
s s
} }
@ -1400,7 +1400,7 @@ mod test {
fn test_closing_ack_fin() { fn test_closing_ack_fin() {
let mut s = socket_closing(); let mut s = socket_closing();
recv!(s, [TcpRepr { recv!(s, [TcpRepr {
seq_number: LOCAL_SEQ + 1 + 1, seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1 + 1), ack_number: Some(REMOTE_SEQ + 1 + 1),
..RECV_TEMPL ..RECV_TEMPL
}]); }]);
@ -1410,7 +1410,6 @@ mod test {
..SEND_TEMPL ..SEND_TEMPL
}]); }]);
assert_eq!(s.state, State::TimeWait); assert_eq!(s.state, State::TimeWait);
assert!(s.remote_endpoint.is_unspecified());
} }
#[test] #[test]
@ -1423,17 +1422,35 @@ mod test {
// =========================================================================================// // =========================================================================================//
// Tests for the TIME-WAIT state. // Tests for the TIME-WAIT state.
// =========================================================================================// // =========================================================================================//
fn socket_time_wait() -> TcpSocket<'static> { fn socket_time_wait(from_closing: bool) -> TcpSocket<'static> {
let mut s = socket_fin_wait_2(); let mut s = socket_fin_wait_2();
s.state = State::TimeWait; s.state = State::TimeWait;
s.remote_seq_no = REMOTE_SEQ + 1 + 1; s.remote_seq_no = REMOTE_SEQ + 1 + 1;
s.remote_last_ack = REMOTE_SEQ + 1 + 1; if from_closing {
s.remote_last_ack = REMOTE_SEQ + 1 + 1;
}
s s
} }
#[test]
fn test_time_wait_from_fin_wait_2_ack() {
let mut s = socket_time_wait(false);
recv!(s, [TcpRepr {
seq_number: LOCAL_SEQ + 1 + 1,
ack_number: Some(REMOTE_SEQ + 1 + 1),
..RECV_TEMPL
}]);
}
#[test]
fn test_time_wait_from_closing_no_ack() {
let mut s = socket_time_wait(true);
recv!(s, []);
}
#[test] #[test]
fn test_time_wait_close() { fn test_time_wait_close() {
let mut s = socket_time_wait(); let mut s = socket_time_wait(false);
s.close(); s.close();
assert_eq!(s.state, State::TimeWait); assert_eq!(s.state, State::TimeWait);
} }
@ -1573,5 +1590,70 @@ mod test {
ack_number: Some(LOCAL_SEQ + 1 + 1), ack_number: Some(LOCAL_SEQ + 1 + 1),
..SEND_TEMPL ..SEND_TEMPL
}]); }]);
assert_eq!(s.state, State::Closed);
}
#[test]
fn test_local_close() {
let mut s = socket_established();
s.close();
assert_eq!(s.state, State::FinWait1);
recv!(s, [TcpRepr {
control: TcpControl::Fin,
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1),
..RECV_TEMPL
}]);
send!(s, [TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1 + 1),
..SEND_TEMPL
}]);
assert_eq!(s.state, State::FinWait2);
send!(s, [TcpRepr {
control: TcpControl::Fin,
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1 + 1),
..SEND_TEMPL
}]);
assert_eq!(s.state, State::TimeWait);
recv!(s, [TcpRepr {
seq_number: LOCAL_SEQ + 1 + 1,
ack_number: Some(REMOTE_SEQ + 1 + 1),
..RECV_TEMPL
}]);
}
#[test]
fn test_simultaneous_close() {
let mut s = socket_established();
s.close();
assert_eq!(s.state, State::FinWait1);
recv!(s, [TcpRepr { // this is logically located...
control: TcpControl::Fin,
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1),
..RECV_TEMPL
}]);
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);
recv!(s, [TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1 + 1),
..RECV_TEMPL
}]);
// ... at this point
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, []);
} }
} }

View File

@ -78,6 +78,8 @@ impl fmt::Display for Address {
} }
/// An internet endpoint address. /// An internet endpoint address.
///
/// An endpoint can be constructed from a port, in which case the address is unspecified.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
pub struct Endpoint { pub struct Endpoint {
pub addr: Address, pub addr: Address,
@ -104,6 +106,12 @@ impl fmt::Display for Endpoint {
} }
} }
impl From<u16> for Endpoint {
fn from(port: u16) -> Endpoint {
Endpoint { addr: Address::Unspecified, port: port }
}
}
/// An IP packet representation. /// An IP packet representation.
/// ///
/// This enum abstracts the various versions of IP packets. It either contains a concrete /// This enum abstracts the various versions of IP packets. It either contains a concrete