Browse Source

tcp: expose advertised MSS via --tcp-mss

ipstack sends its SYN-ACK without a MSS option, so the peer OS falls back to
its own default (512 on macOS) and fragments large first segments such as a
TLS ClientHello. Downstream consumers that sniff the SNI from the first read
then miss the domain.

Wire a new optional Args.tcp_mss into ipstack::TcpConfig.options (mirrors how
--tcp-timeout sets TcpConfig.timeout). Default None keeps the previous
behaviour, so this is a runtime no-op unless the flag is set.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
pull/260/head
hypnosis 2 days ago
parent
commit
c97ca83a86
  1. 1
      README.md
  2. 9
      src/args.rs
  3. 3
      src/lib.rs

1
README.md

@ -187,6 +187,7 @@ Options:
-b, --bypass <IP/CIDR> IPs used in routing setup which should bypass the tunnel, in the form of IP or IP/CIDR. -b, --bypass <IP/CIDR> IPs used in routing setup which should bypass the tunnel, in the form of IP or IP/CIDR.
Multiple IPs can be specified, e.g. --bypass 3.4.5.0/24 --bypass 5.6.7.8 Multiple IPs can be specified, e.g. --bypass 3.4.5.0/24 --bypass 5.6.7.8
--tcp-timeout <seconds> TCP timeout in seconds [default: 600] --tcp-timeout <seconds> TCP timeout in seconds [default: 600]
--tcp-mss <bytes> MSS advertised in the SYN-ACK to the kernel side [default: none]
--udp-timeout <seconds> UDP timeout in seconds [default: 10] --udp-timeout <seconds> UDP timeout in seconds [default: 10]
-v, --verbosity <level> Verbosity level [default: info] [possible values: off, error, warn, info, debug, trace] -v, --verbosity <level> Verbosity level [default: info] [possible values: off, error, warn, info, debug, trace]
--daemonize Daemonize for unix family or run as Windows service --daemonize Daemonize for unix family or run as Windows service

9
src/args.rs

@ -116,6 +116,14 @@ pub struct Args {
#[arg(long, value_name = "seconds", default_value = "600")] #[arg(long, value_name = "seconds", default_value = "600")]
pub tcp_timeout: u64, pub tcp_timeout: u64,
/// MSS advertised in the SYN-ACK to the kernel side. By default the userspace
/// stack sends no MSS option, so the peer OS falls back to its own default
/// (e.g. 512 on macOS), fragmenting large first segments such as a TLS
/// ClientHello. Set this to the tunnel's (MTU - 40) to let the peer send big
/// segments. `None` keeps the previous behaviour (no option advertised).
#[arg(long, value_name = "bytes")]
pub tcp_mss: Option<u16>,
/// UDP timeout in seconds /// UDP timeout in seconds
#[arg(long, value_name = "seconds", default_value = "10")] #[arg(long, value_name = "seconds", default_value = "10")]
pub udp_timeout: u64, pub udp_timeout: u64,
@ -191,6 +199,7 @@ impl Default for Args {
bypass: vec![], bypass: vec![],
mtu: tun::DEFAULT_MTU, mtu: tun::DEFAULT_MTU,
tcp_timeout: 600, tcp_timeout: 600,
tcp_mss: None,
udp_timeout: 10, udp_timeout: 10,
verbosity: ArgVerbosity::Info, verbosity: ArgVerbosity::Info,
virtual_dns_pool: IpCidr::from_str("198.18.0.0/15").unwrap(), virtual_dns_pool: IpCidr::from_str("198.18.0.0/15").unwrap(),

3
src/lib.rs

@ -243,6 +243,9 @@ where
ipstack_config.mtu(mtu)?; ipstack_config.mtu(mtu)?;
let mut tcp_cfg = ipstack::TcpConfig::default(); let mut tcp_cfg = ipstack::TcpConfig::default();
tcp_cfg.timeout = std::time::Duration::from_secs(args.tcp_timeout); tcp_cfg.timeout = std::time::Duration::from_secs(args.tcp_timeout);
if let Some(mss) = args.tcp_mss {
tcp_cfg.options = Some(vec![ipstack::TcpOptions::MaximumSegmentSize(mss)]);
}
ipstack_config.with_tcp_config(tcp_cfg); ipstack_config.with_tcp_config(tcp_cfg);
ipstack_config.udp_timeout(std::time::Duration::from_secs(args.udp_timeout)); ipstack_config.udp_timeout(std::time::Duration::from_secs(args.udp_timeout));

Loading…
Cancel
Save