Skip to content

[inetstack] Compute Default MSS from MTU - #1638

Open
SWARAJ SINGH (dedsec-terminal) wants to merge 1 commit into
microsoft:devfrom
dedsec-terminal:feature/221-compute-default-mss-from-mtu
Open

SWARAJ SINGH (dedsec-terminal) wants to merge 1 commit into
microsoft:devfrom
dedsec-terminal:feature/221-compute-default-mss-from-mtu

Conversation

@dedsec-terminal

Copy link
Copy Markdown

Which issue does this PR fix?

Closes #221.
Related to #178 (MTU Discovery): this does not implement discovery. It only stops the stack from ignoring the MTU that is already configured.

What does this PR do?

DEFAULT_MSS was hard-coded to 1450, regardless of the MTU the runtime was configured with. It is now derived from the MTU as RFC 793 and RFC 6691 prescribe:

mss = mtu - sizeof(min IPv4 header) - sizeof(min TCP header)

The Ethernet header is deliberately excluded (an MTU bounds the IP datagram), and the minimum header sizes are used because that is what the stack actually emits on data segments. MSS_HEADER_OVERHEAD carries a comment noting that this must grow if the RFC 7323 timestamp option is ever added.
Details:

  • src/inetstack/consts.rs: new MSS_HEADER_OVERHEAD, DEFAULT_MTU, and const fn default_mss_from_mtu(). The helper uses checked_sub and clamps to MIN_MSS..=MAX_MSS, so a degenerate MTU can neither underflow nor produce a value that does not fit the 16-bit MSS option. DEFAULT_MSS is now derived from DEFAULT_MTU (1500) instead of being an unrelated hard-coded 1450, so the two defaults cannot disagree.
  • src/inetstack/config/tcp.rs: TcpConfig::new() derives the advertised MSS from the configured MTU when no mss is given. An explicitly configured mss still wins, but it now returns Fail(ERANGE) instead of panicking on an out-of-range value, and logs a warning when it is larger than the MTU can carry.
  • New TcpConfig::get_effective_mss(), used by the active and passive open paths.

Three latent MSS bugs fixed along the way

  1. The MSS advertised by a peer was used verbatim. active_open.rs and passive_open.rs stored the received MSS option straight into Sender::mss, which is what caps segment size in calculate_open_window_bytes(). A peer sitting on a jumbo link could therefore make our sender build segments larger than our own interface can carry. RFC 1122, Section 4.2.2.6 requires min(our MSS, offered MSS), which is what get_effective_mss() now implements (falling back to FALLBACK_MSS = 536 when the peer sends no MSS option, as before).
  2. Catpowder (Windows) double-counted the headers. It computed max_body_size = config.mss()? - MAX_HEADER_SIZE, but the MSS is already a payload size, so the headers were subtracted twice (1500 -> 1366 instead of 1460). It also failed outright at startup when no mss was configured. It now derives max_body_size from the MTU with a saturating subtraction, matching what Catpowder (Linux) already does.
  3. The shipped config templates advertised mss: 1500 next to mtu: 1500, i.e. a segment that can never fit in a frame. That line is now a comment showing the option is optional.

Behaviour change

With only mtu: 1500 configured and no explicit mss, the advertised MSS becomes 1460 (was 1450). That is the point of the issue, but it is wire-visible, so calling it out.
Nothing existing changes behaviour: src/inetstack/test_helpers/*.yaml and tests/rust/common/*.yaml all pin mss: explicitly, so no current test is affected. tests/rust/common/*.yaml still carries the same odd mss: 1500 / mtu: 1500 pair as the templates did - it now only produces a warning. Happy to clean that up in a follow-up if you would rather keep this diff focused.

Tests

Three new unit tests in src/inetstack/config/tcp.rs:

  • test_tcp_config_mss_derived_from_mtu - 1500 -> 1460, 9000 -> 8960, 4000 -> 3960, and clamping to 536 for MTUs of 576 and 20.
  • test_tcp_config_mss_overrides_mtu - explicit MSS precedence, the no-MTU/no-MSS fallback, and ERANGE for mss: 535 and mss: 65536.
  • test_tcp_config_effective_mss - no option -> 536, smaller option honoured, larger option clamped.
    An end-to-end handshake test for the clamping would need new config fixtures (a peer advertising a larger MSS than our MTU), which I left out to keep this reviewable. Let me know if you want it and I will add it.

Not covered here

The real interface MTU is still not discoverable from the inetstack: catnip only asserts that DPDK's MTU matches the config value, and sender.rs still carries TODO: Revisit this once we support path MTU discovery. That is #178.

Copilot AI lite review requested due to automatic review settings September 14, 2026 20:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@dedsec-terminal

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[inetstack] Compute Default MSS from MTU

2 participants