Skip to content

Commit 7756839

Browse files
authored
Fix bug in rust tcp sequence number (#3134)
I missed this before. It was previously skipping sequence number ranges and somehow still worked.
2 parents aecb55a + c2846ce commit 7756839

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

src/lib/tcp/src/connection.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,16 @@ impl<I: Instant> Connection<I> {
240240
#[cfg(debug_assertions)]
241241
debug_assert!(wants_to_send);
242242

243-
break 'packet (self.send.buffer.next_seq(), TcpFlags::empty(), Bytes::new());
243+
// use the sequence number of the next unsent message if we have one buffered,
244+
// otherwise get the next sequence number from the buffer
245+
let seq = self
246+
.send
247+
.buffer
248+
.next_not_transmitted()
249+
.map(|x| x.0)
250+
.unwrap_or(self.send.buffer.next_seq());
251+
252+
break 'packet (seq, TcpFlags::empty(), Bytes::new());
244253
}
245254

246255
#[cfg(debug_assertions)]

src/lib/tcp/src/tests/send_recv.rs

+78
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,81 @@ fn test_send_recv() {
7171
TcpSocket::recvmsg(&tcp, &mut recv_buf[..], 10).unwrap();
7272
assert_eq!(recv_buf, b"helloworld");
7373
}
74+
75+
/// This test tries to make sure that an acknowledgement sent while the socket's usable send window
76+
/// (send window excluding in-flight not-acked data) is empty uses the correct sequence number.
77+
/// (This test doesn't require that the usable send window is actually empty, just that it's empty
78+
/// enough that TCP decides it can't/shouldn't send more payload packets.)
79+
#[test]
80+
// this test causes miri to time-out
81+
#[cfg_attr(miri, ignore)]
82+
fn test_ack_with_empty_usable_send_window() {
83+
let scheduler = Scheduler::new();
84+
let mut host = Host::new();
85+
86+
/// Helper to get the state from a socket.
87+
fn s(tcp: &Rc<RefCell<TcpSocket>>) -> Ref<TcpState<TestEnvState>> {
88+
Ref::map(tcp.borrow(), |x| x.tcp_state())
89+
}
90+
91+
// get an established tcp socket
92+
let tcp = establish_helper(&scheduler, &mut host);
93+
94+
// send on the socket until the send buffer has more data than the usable send window allows
95+
let max_in_flight = s(&tcp)
96+
.as_established()
97+
.unwrap()
98+
.connection
99+
.send
100+
.window_range()
101+
.len();
102+
103+
let mut buffered = 0;
104+
while buffered <= max_in_flight as usize {
105+
buffered += TcpSocket::sendmsg(&tcp, &b"hello"[..], 5).unwrap();
106+
}
107+
108+
// read all of the packets it sent and make sure the sequence number is consistent
109+
let mut next_seq = None;
110+
while let Some((header, payload)) = scheduler.pop_packet() {
111+
if next_seq.is_none() {
112+
next_seq = Some(header.seq as usize);
113+
}
114+
115+
let next_seq = next_seq.as_mut().unwrap();
116+
assert_eq!(*next_seq, header.seq as usize);
117+
*next_seq += payload.len();
118+
}
119+
120+
// send a packet with a payload to trigger an acknowledgement
121+
let header = TcpHeader {
122+
ip: Ipv4Header {
123+
src: "5.6.7.8".parse().unwrap(),
124+
dst: host.ip_addr,
125+
},
126+
flags: TcpFlags::empty(),
127+
src_port: 20,
128+
dst_port: 10,
129+
seq: 1,
130+
ack: 1,
131+
window_size: 10000,
132+
selective_acks: None,
133+
window_scale: None,
134+
timestamp: None,
135+
timestamp_echo: None,
136+
};
137+
tcp.borrow_mut()
138+
.push_in_packet(&header, Bytes::from(&b"world"[..]));
139+
140+
// check the packet sent by the socket
141+
let (header, payload) = scheduler.pop_packet().unwrap();
142+
143+
// should have acked the packet we sent above using the correct sequence number
144+
assert!(header.flags.contains(TcpFlags::ACK));
145+
assert_eq!(header.ack, 6);
146+
assert_eq!(header.seq as usize, next_seq.unwrap());
147+
148+
// we haven't acked any of the data it sent, so its usable send window should still be empty and
149+
// should not have sent any data
150+
assert!(payload.is_empty());
151+
}

0 commit comments

Comments
 (0)