From 78ffed74eb07c831bc12c6634b13014e23960f72 Mon Sep 17 00:00:00 2001 From: Orvaxis <198349614+Orvaxis@users.noreply.github.com> Date: Thu, 9 Oct 2025 14:21:26 +0800 Subject: [PATCH 1/4] feat: add mtu option for dynamic calculation of mss --- src/stream/tcp.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/stream/tcp.rs b/src/stream/tcp.rs index 4ece70d..994f280 100644 --- a/src/stream/tcp.rs +++ b/src/stream/tcp.rs @@ -28,6 +28,7 @@ const CLOSE_WAIT_TIMEOUT: Duration = Duration::from_secs(5); const LAST_ACK_MAX_RETRIES: usize = 3; const LAST_ACK_TIMEOUT: Duration = Duration::from_millis(500); const TIMEOUT: Duration = Duration::from_secs(60); +const TCP_HEADER_LEN: u16 = 20; #[non_exhaustive] #[derive(Debug, Clone)] @@ -52,6 +53,9 @@ pub struct TcpConfig { pub enum TcpOptions { /// Maximum segment size (MSS) for TCP connections. Default is 1460 bytes. MaximumSegmentSize(u16), + + /// MTU option,helps to set the MSS (Maximum Segment Size) option in the TCP header. + IpMtu(u16), } impl Default for TcpConfig { @@ -913,7 +917,17 @@ pub(crate) fn create_raw_packet( let mut tcp_options = Vec::new(); for opt in opts { match opt { - TcpOptions::MaximumSegmentSize(mss) => tcp_options.push(TcpOptionElement::MaximumSegmentSize(*mss)), + TcpOptions::MaximumSegmentSize(mss) => tcp_options.push(etherparse::TcpOptionElement::MaximumSegmentSize(*mss)), + TcpOptions::IpMtu(mtu) => { + tcp_options.push(etherparse::TcpOptionElement::MaximumSegmentSize( + mtu - TCP_HEADER_LEN + - if src_addr.is_ipv6() || dst_addr.is_ipv6() { + 40 + } else { + 20 // minimum IPv4 header size + }, + )); + } } } tcp_header From 146bee5be99ba07ef39949e9d9439bfa00b94218 Mon Sep 17 00:00:00 2001 From: Orvaxis <198349614+Orvaxis@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:16:03 +0800 Subject: [PATCH 2/4] refactor: remove redundant IpMtu option, use TCB MTU value instead. --- src/stream/tcp.rs | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/stream/tcp.rs b/src/stream/tcp.rs index 994f280..f788373 100644 --- a/src/stream/tcp.rs +++ b/src/stream/tcp.rs @@ -53,9 +53,6 @@ pub struct TcpConfig { pub enum TcpOptions { /// Maximum segment size (MSS) for TCP connections. Default is 1460 bytes. MaximumSegmentSize(u16), - - /// MTU option,helps to set the MSS (Maximum Segment Size) option in the TCP header. - IpMtu(u16), } impl Default for TcpConfig { @@ -884,6 +881,7 @@ pub(crate) fn write_packet_to_device( seq, ack, window_size, + tcb.get_mtu(), payload.unwrap_or_default(), options, )?; @@ -902,6 +900,7 @@ pub(crate) fn create_raw_packet( seq: u32, ack: u32, win: u16, + mtu: u16, mut payload: Vec, options: Option<&Vec>, ) -> std::io::Result { @@ -912,28 +911,24 @@ pub(crate) fn create_raw_packet( tcp_header.rst = flags & RST != 0; tcp_header.fin = flags & FIN != 0; tcp_header.psh = flags & PSH != 0; - + let mut actual_mss = if src_addr.is_ipv6() || dst_addr.is_ipv6() { + mtu - 40 - TCP_HEADER_LEN + } else { + mtu - 20 - TCP_HEADER_LEN + }; + let mut tcp_options = Vec::new(); if let Some(opts) = options { - let mut tcp_options = Vec::new(); for opt in opts { match opt { - TcpOptions::MaximumSegmentSize(mss) => tcp_options.push(etherparse::TcpOptionElement::MaximumSegmentSize(*mss)), - TcpOptions::IpMtu(mtu) => { - tcp_options.push(etherparse::TcpOptionElement::MaximumSegmentSize( - mtu - TCP_HEADER_LEN - - if src_addr.is_ipv6() || dst_addr.is_ipv6() { - 40 - } else { - 20 // minimum IPv4 header size - }, - )); - } + TcpOptions::MaximumSegmentSize(mss) => actual_mss = *mss, } } - tcp_header - .set_options(&tcp_options) - .map_err(|e| std::io::Error::new(InvalidInput, e))?; } + tcp_options.push(etherparse::TcpOptionElement::MaximumSegmentSize(actual_mss)); + tcp_header + .set_options(&tcp_options) + .map_err(|e| std::io::Error::new(InvalidInput, e))?; + let ip_header = match (src_addr.ip(), dst_addr.ip()) { (std::net::IpAddr::V4(src), std::net::IpAddr::V4(dst)) => { let mut ip_h = From 651101dc860a6a68a0aa7084d27b3dab9072db48 Mon Sep 17 00:00:00 2001 From: Orvaxis <198349614+Orvaxis@users.noreply.github.com> Date: Thu, 9 Oct 2025 22:22:54 +0800 Subject: [PATCH 3/4] fix: calculate mss only during handshake --- src/stream/tcp.rs | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/stream/tcp.rs b/src/stream/tcp.rs index f788373..a3f9e24 100644 --- a/src/stream/tcp.rs +++ b/src/stream/tcp.rs @@ -452,7 +452,16 @@ async fn tcp_main_logic_loop( &up_packet_sender, network_tuple, &tcb, - config.options.as_ref(), + config.options.as_ref().as_deref().map(|o| { + ( + o, + if network_tuple.src.is_ipv4() && network_tuple.dst.is_ipv4() { + tcb.get_mtu() - 20 - TCP_HEADER_LEN + } else { + tcb.get_mtu() - 40 - TCP_HEADER_LEN + }, + ) + }), ACK | SYN, None, None, @@ -862,7 +871,7 @@ pub(crate) fn write_packet_to_device( up_packet_sender: &PacketSender, tuple: NetworkTuple, tcb: &Tcb, - options: Option<&Vec>, + options: Option<(&Vec, u16)>, flags: u8, seq: Option, payload: Option>, @@ -881,7 +890,6 @@ pub(crate) fn write_packet_to_device( seq, ack, window_size, - tcb.get_mtu(), payload.unwrap_or_default(), options, )?; @@ -900,9 +908,8 @@ pub(crate) fn create_raw_packet( seq: u32, ack: u32, win: u16, - mtu: u16, mut payload: Vec, - options: Option<&Vec>, + options: Option<(&Vec, u16)>, ) -> std::io::Result { let mut tcp_header = etherparse::TcpHeader::new(src_addr.port(), dst_addr.port(), seq, win); tcp_header.acknowledgment_number = ack; @@ -911,23 +918,19 @@ pub(crate) fn create_raw_packet( tcp_header.rst = flags & RST != 0; tcp_header.fin = flags & FIN != 0; tcp_header.psh = flags & PSH != 0; - let mut actual_mss = if src_addr.is_ipv6() || dst_addr.is_ipv6() { - mtu - 40 - TCP_HEADER_LEN - } else { - mtu - 20 - TCP_HEADER_LEN - }; - let mut tcp_options = Vec::new(); - if let Some(opts) = options { + + if let Some((opts, mut mss)) = options { + let mut tcp_options = Vec::new(); for opt in opts { match opt { - TcpOptions::MaximumSegmentSize(mss) => actual_mss = *mss, + TcpOptions::MaximumSegmentSize(fixed_mss) => mss = *fixed_mss, } } + tcp_options.push(etherparse::TcpOptionElement::MaximumSegmentSize(mss)); + tcp_header + .set_options(&tcp_options) + .map_err(|e| std::io::Error::new(InvalidInput, e))?; } - tcp_options.push(etherparse::TcpOptionElement::MaximumSegmentSize(actual_mss)); - tcp_header - .set_options(&tcp_options) - .map_err(|e| std::io::Error::new(InvalidInput, e))?; let ip_header = match (src_addr.ip(), dst_addr.ip()) { (std::net::IpAddr::V4(src), std::net::IpAddr::V4(dst)) => { From a29c1f1d887827f768a48edfd938cc7f7528d310 Mon Sep 17 00:00:00 2001 From: Orvaxis <198349614+Orvaxis@users.noreply.github.com> Date: Thu, 9 Oct 2025 23:46:51 +0800 Subject: [PATCH 4/4] chore: clean code --- src/stream/tcp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/tcp.rs b/src/stream/tcp.rs index a3f9e24..6bf154a 100644 --- a/src/stream/tcp.rs +++ b/src/stream/tcp.rs @@ -9,7 +9,7 @@ use crate::{ }, stream::tcb::{PacketType, Tcb, TcpState}, }; -use etherparse::{IpNumber, Ipv4Header, Ipv6FlowLabel, TcpHeader, TcpOptionElement}; +use etherparse::{IpNumber, Ipv4Header, Ipv6FlowLabel, TcpHeader}; use std::{ future::Future, io::ErrorKind::{BrokenPipe, ConnectionRefused, InvalidInput, UnexpectedEof}, @@ -452,7 +452,7 @@ async fn tcp_main_logic_loop( &up_packet_sender, network_tuple, &tcb, - config.options.as_ref().as_deref().map(|o| { + config.options.as_ref().map(|o| { ( o, if network_tuple.src.is_ipv4() && network_tuple.dst.is_ipv4() {