Skip to content

Commit 87a34c9

Browse files
Improve configuration API to adjust internal TCP parameters (#80)
* Improve configuration API * Update README example
1 parent cc7200f commit 87a34c9

File tree

7 files changed

+113
-34
lines changed

7 files changed

+113
-34
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
3838
});
3939
4040
let mut ipstack_config = ipstack::IpStackConfig::default();
41-
ipstack_config.mtu(MTU);
41+
ipstack_config.mtu(MTU)?;
4242
let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun::create_as_async(&config)?);
4343
4444
while let Ok(stream) = ip_stack.accept().await {

examples/tun.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
9292
});
9393

9494
let mut ipstack_config = ipstack::IpStackConfig::default();
95-
ipstack_config.mtu(MTU);
95+
ipstack_config.mtu(MTU)?;
9696
let mut tcp_config = ipstack::TcpConfig::default();
9797
tcp_config.timeout = std::time::Duration::from_secs(args.tcp_timeout);
9898
tcp_config.options = Some(vec![ipstack::TcpOptions::MaximumSegmentSize(1460)]);

examples/tun_wintun.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4040
// });
4141

4242
let mut ipstack_config = ipstack::IpStackConfig::default();
43-
ipstack_config.mtu(MTU);
43+
ipstack_config.mtu(MTU)?;
4444
// ipstack_config.packet_information(cfg!(target_family = "unix"));
4545

4646
#[cfg(not(target_os = "windows"))]

src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ pub enum IpStackError {
3434
/// Error sending data through a channel.
3535
#[error("Send Error {0}")]
3636
SendError(#[from] Box<tokio::sync::mpsc::error::SendError<crate::stream::IpStackStream>>),
37+
38+
/// Invalid MTU size. The minimum MTU is 1280 bytes to comply with IPv6 standards.
39+
#[error("Invalid MTU size: {0} (bytes). Minimum MTU is 1280 bytes.")]
40+
InvalidMtuSize(u16),
3741
}
3842

3943
impl From<tokio::sync::mpsc::error::SendError<crate::stream::IpStackStream>> for IpStackError {

src/lib.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const MIN_MTU: u16 = 1280;
5858
/// use std::time::Duration;
5959
///
6060
/// let mut config = IpStackConfig::default();
61-
/// config.mtu(1500)
61+
/// config.mtu(1500).expect("Failed to set MTU")
6262
/// .udp_timeout(Duration::from_secs(60))
6363
/// .packet_information(false);
6464
/// ```
@@ -140,9 +140,18 @@ impl IpStackConfig {
140140
/// use ipstack::IpStackConfig;
141141
///
142142
/// let mut config = IpStackConfig::default();
143-
/// config.mtu(1500);
143+
/// config.mtu(1500).expect("Failed to set MTU");
144144
/// ```
145-
pub fn mtu(&mut self, mtu: u16) -> &mut Self {
145+
pub fn mtu(&mut self, mtu: u16) -> Result<&mut Self, IpStackError> {
146+
if mtu < MIN_MTU {
147+
return Err(IpStackError::InvalidMtuSize(mtu));
148+
}
149+
self.mtu = mtu;
150+
Ok(self)
151+
}
152+
153+
/// Set the Maximum Transmission Unit (MTU) size without validation.
154+
pub fn mtu_unchecked(&mut self, mtu: u16) -> &mut Self {
146155
self.mtu = mtu;
147156
self
148157
}
@@ -307,13 +316,6 @@ fn run<Device: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
307316
let mut buffer = vec![0_u8; config.mtu as usize + offset];
308317
let (up_pkt_sender, mut up_pkt_receiver) = mpsc::unbounded_channel::<NetworkPacket>();
309318

310-
if config.mtu < MIN_MTU {
311-
log::warn!(
312-
"the MTU in the configuration ({}) below the MIN_MTU (1280) can cause problems.",
313-
config.mtu
314-
);
315-
}
316-
317319
tokio::spawn(async move {
318320
loop {
319321
select! {

src/stream/tcb.rs

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use super::seqnum::SeqNum;
22
use etherparse::TcpHeader;
3-
use std::collections::BTreeMap;
3+
use std::{collections::BTreeMap, time::Duration};
44

5-
const MAX_UNACK: u32 = 1024 * 16; // 16KB
6-
const READ_BUFFER_SIZE: usize = 1024 * 16; // 16KB
7-
const MAX_COUNT_FOR_DUP_ACK: usize = 3; // Maximum number of duplicate ACKs before retransmission
5+
pub(super) const MAX_UNACK: u32 = 1024 * 16; // 16KB
6+
pub(super) const READ_BUFFER_SIZE: usize = 1024 * 16; // 16KB
7+
pub(super) const MAX_COUNT_FOR_DUP_ACK: usize = 3; // Maximum number of duplicate ACKs before retransmission
88

99
/// Retransmission timeout
10-
const RTO: std::time::Duration = std::time::Duration::from_secs(1);
10+
pub(super) const RTO: std::time::Duration = std::time::Duration::from_secs(1);
1111

1212
/// Maximum count of retransmissions before dropping the packet
13-
pub(crate) const MAX_RETRANSMIT_COUNT: usize = 3;
13+
pub(super) const MAX_RETRANSMIT_COUNT: usize = 3;
1414

1515
#[derive(Debug, PartialEq, Clone, Copy)]
1616
pub(crate) enum TcpState {
@@ -55,10 +55,23 @@ pub(crate) struct Tcb {
5555
unordered_packets: BTreeMap<SeqNum, Vec<u8>>,
5656
duplicate_ack_count: usize,
5757
duplicate_ack_count_helper: SeqNum,
58+
max_unacked_bytes: u32,
59+
read_buffer_size: usize,
60+
max_count_for_dup_ack: usize,
61+
rto: std::time::Duration,
62+
max_retransmit_count: usize,
5863
}
5964

6065
impl Tcb {
61-
pub(super) fn new(ack: SeqNum, mtu: u16) -> Tcb {
66+
pub(super) fn new(
67+
ack: SeqNum,
68+
mtu: u16,
69+
max_unacked_bytes: u32,
70+
read_buffer_size: usize,
71+
max_count_for_dup_ack: usize,
72+
rto: std::time::Duration,
73+
max_retransmit_count: usize,
74+
) -> Tcb {
6275
#[cfg(debug_assertions)]
6376
let seq = 100;
6477
#[cfg(not(debug_assertions))]
@@ -74,6 +87,11 @@ impl Tcb {
7487
unordered_packets: BTreeMap::new(),
7588
duplicate_ack_count: 0,
7689
duplicate_ack_count_helper: seq.into(),
90+
max_unacked_bytes,
91+
read_buffer_size,
92+
max_count_for_dup_ack,
93+
rto,
94+
max_retransmit_count,
7795
}
7896
}
7997

@@ -94,7 +112,7 @@ impl Tcb {
94112
}
95113

96114
pub fn is_duplicate_ack_count_exceeded(&self) -> bool {
97-
self.duplicate_ack_count >= MAX_COUNT_FOR_DUP_ACK
115+
self.duplicate_ack_count >= self.max_count_for_dup_ack
98116
}
99117

100118
pub(super) fn add_unordered_packet(&mut self, seq: SeqNum, buf: Vec<u8>) {
@@ -106,7 +124,7 @@ impl Tcb {
106124
self.unordered_packets.insert(seq, buf);
107125
}
108126
pub(super) fn get_available_read_buffer_size(&self) -> usize {
109-
READ_BUFFER_SIZE.saturating_sub(self.get_unordered_packets_total_len())
127+
self.read_buffer_size.saturating_sub(self.get_unordered_packets_total_len())
110128
}
111129
#[inline]
112130
pub(crate) fn get_unordered_packets_total_len(&self) -> usize {
@@ -234,7 +252,7 @@ impl Tcb {
234252
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Empty payload"));
235253
}
236254
let buf_len = buf.len() as u32;
237-
self.inflight_packets.insert(self.seq, InflightPacket::new(self.seq, buf));
255+
self.inflight_packets.insert(self.seq, InflightPacket::new(self.seq, buf, self.rto));
238256
self.seq += buf_len;
239257
Ok(())
240258
}
@@ -275,7 +293,7 @@ impl Tcb {
275293
let mut retransmit_list = Vec::new();
276294

277295
self.inflight_packets.retain(|_, packet| {
278-
if packet.retransmit_count >= MAX_RETRANSMIT_COUNT {
296+
if packet.retransmit_count >= self.max_retransmit_count {
279297
log::warn!("Packet with seq {:?} reached max retransmit count, dropping packet", packet.seq);
280298
return false; // remove this packet
281299
}
@@ -302,7 +320,7 @@ impl Tcb {
302320
pub fn is_send_buffer_full(&self) -> bool {
303321
// To respect the receiver's window (remote_window) size and avoid sending too many unacknowledged packets, which may cause packet loss
304322
// Simplified version: min(cwnd, rwnd)
305-
self.seq.distance(self.get_last_received_ack()) >= MAX_UNACK.min(self.get_send_window() as u32)
323+
self.seq.distance(self.get_last_received_ack()) >= self.max_unacked_bytes.min(self.get_send_window() as u32)
306324
}
307325
}
308326

@@ -316,13 +334,13 @@ pub struct InflightPacket {
316334
}
317335

318336
impl InflightPacket {
319-
fn new(seq: SeqNum, payload: Vec<u8>) -> Self {
337+
fn new(seq: SeqNum, payload: Vec<u8>, rto: Duration) -> Self {
320338
Self {
321339
seq,
322340
payload,
323341
send_time: std::time::Instant::now(),
324342
retransmit_count: 0,
325-
retransmit_timeout: RTO,
343+
retransmit_timeout: rto,
326344
}
327345
}
328346
pub(crate) fn contains_seq_num(&self, seq: SeqNum) -> bool {
@@ -339,7 +357,7 @@ mod tests {
339357

340358
#[test]
341359
fn test_in_flight_packet() {
342-
let p = InflightPacket::new((u32::MAX - 1).into(), vec![10, 20, 30, 40, 50]);
360+
let p = InflightPacket::new((u32::MAX - 1).into(), vec![10, 20, 30, 40, 50], RTO);
343361

344362
assert!(p.contains_seq_num((u32::MAX - 1).into()));
345363
assert!(p.contains_seq_num(u32::MAX.into()));
@@ -352,7 +370,15 @@ mod tests {
352370

353371
#[test]
354372
fn test_get_unordered_packets_with_max_bytes() {
355-
let mut tcb = Tcb::new(SeqNum(1000), 1500);
373+
let mut tcb = Tcb::new(
374+
SeqNum(1000),
375+
1500,
376+
MAX_UNACK,
377+
READ_BUFFER_SIZE,
378+
MAX_COUNT_FOR_DUP_ACK,
379+
RTO,
380+
MAX_RETRANSMIT_COUNT,
381+
);
356382

357383
// insert 3 consecutive packets
358384
tcb.add_unordered_packet(SeqNum(1000), vec![1; 500]); // seq=1000, len=500
@@ -384,7 +410,15 @@ mod tests {
384410

385411
#[test]
386412
fn test_update_inflight_packet_queue() {
387-
let mut tcb = Tcb::new(SeqNum(1000), 1500);
413+
let mut tcb = Tcb::new(
414+
SeqNum(1000),
415+
1500,
416+
MAX_UNACK,
417+
READ_BUFFER_SIZE,
418+
MAX_COUNT_FOR_DUP_ACK,
419+
RTO,
420+
MAX_RETRANSMIT_COUNT,
421+
);
388422
tcb.seq = SeqNum(100); // setting the initial seq
389423

390424
// insert 3 consecutive packets
@@ -408,7 +442,15 @@ mod tests {
408442

409443
#[test]
410444
fn test_update_inflight_packet_queue_cumulative_ack() {
411-
let mut tcb = Tcb::new(SeqNum(1000), 1500);
445+
let mut tcb = Tcb::new(
446+
SeqNum(1000),
447+
1500,
448+
MAX_UNACK,
449+
READ_BUFFER_SIZE,
450+
MAX_COUNT_FOR_DUP_ACK,
451+
RTO,
452+
MAX_RETRANSMIT_COUNT,
453+
);
412454
tcb.seq = SeqNum(1000);
413455

414456
// Insert 3 consecutive packets
@@ -423,7 +465,15 @@ mod tests {
423465

424466
#[test]
425467
fn test_retransmit_with_exponential_backoff() {
426-
let mut tcb = Tcb::new(SeqNum(1000), 1500);
468+
let mut tcb = Tcb::new(
469+
SeqNum(1000),
470+
1500,
471+
MAX_UNACK,
472+
READ_BUFFER_SIZE,
473+
MAX_COUNT_FOR_DUP_ACK,
474+
RTO,
475+
MAX_RETRANSMIT_COUNT,
476+
);
427477

428478
tcb.add_inflight_packet(vec![1; 500]).unwrap();
429479

src/stream/tcp.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
tcp_flags::{ACK, FIN, PSH, RST, SYN},
88
tcp_header_flags, tcp_header_fmt,
99
},
10-
stream::tcb::{PacketType, Tcb, TcpState},
10+
stream::tcb::{MAX_COUNT_FOR_DUP_ACK, MAX_RETRANSMIT_COUNT, MAX_UNACK, PacketType, READ_BUFFER_SIZE, RTO, Tcb, TcpState},
1111
};
1212
use etherparse::{IpNumber, Ipv4Header, Ipv6FlowLabel, TcpHeader, TcpOptionElement};
1313
use std::{
@@ -43,6 +43,16 @@ pub struct TcpConfig {
4343
pub timeout: Duration,
4444
/// Timeout for the TIME_WAIT state. Default is 2 seconds.
4545
pub two_msl: Duration,
46+
/// Maximum number of unacknowledged bytes allowed in the send buffer.
47+
pub max_unacked_bytes: u32,
48+
/// Size of the read buffer for incoming data.
49+
pub read_buffer_size: usize,
50+
/// Maximum number of duplicate ACKs before triggering fast retransmission.
51+
pub max_count_for_dup_ack: usize,
52+
/// Retransmission timeout duration.
53+
pub rto: std::time::Duration,
54+
/// Maximum number of retransmissions before giving up.
55+
pub max_retransmit_count: usize,
4656
/// TCP options
4757
pub options: Option<Vec<TcpOptions>>,
4858
}
@@ -62,6 +72,11 @@ impl Default for TcpConfig {
6272
close_wait_timeout: CLOSE_WAIT_TIMEOUT,
6373
timeout: TIMEOUT,
6474
two_msl: TWO_MSL,
75+
max_unacked_bytes: MAX_UNACK,
76+
read_buffer_size: READ_BUFFER_SIZE,
77+
max_count_for_dup_ack: MAX_COUNT_FOR_DUP_ACK,
78+
rto: RTO,
79+
max_retransmit_count: MAX_RETRANSMIT_COUNT,
6580
options: Default::default(),
6681
}
6782
}
@@ -169,7 +184,15 @@ impl IpStackTcpStream {
169184
destroy_messenger: Option<::tokio::sync::oneshot::Sender<()>>,
170185
config: Arc<TcpConfig>,
171186
) -> Result<IpStackTcpStream, IpStackError> {
172-
let tcb = Tcb::new(SeqNum(tcp.sequence_number), mtu);
187+
let tcb = Tcb::new(
188+
SeqNum(tcp.sequence_number),
189+
mtu,
190+
config.max_unacked_bytes,
191+
config.read_buffer_size,
192+
config.max_count_for_dup_ack,
193+
config.rto,
194+
config.max_retransmit_count,
195+
);
173196
let tuple = NetworkTuple::new(src_addr, dst_addr, true);
174197
if !tcp.syn {
175198
if !tcp.rst

0 commit comments

Comments
 (0)