Skip to content

feat: add mtu option for dynamic calculation of mss#70

Closed
Orvaxis wants to merge 4 commits into
narrowlink:mainfrom
Orvaxis:feat/better-mss-configuration
Closed

feat: add mtu option for dynamic calculation of mss#70
Orvaxis wants to merge 4 commits into
narrowlink:mainfrom
Orvaxis:feat/better-mss-configuration

Conversation

@Orvaxis
Copy link
Copy Markdown
Contributor

@Orvaxis Orvaxis commented Oct 9, 2025

Due to the different header lengths of IPv4 and IPv6, the MSS (Maximum Segment Size) calculated using a fixed value should not be the same. An option should be added to pass the MTU (Maximum Transmission Unit) for dynamic calculation.

Todo, since MSS is negotiated, the minimum value between the local and remote MSS should be used. However, the current API lacks a mechanism to retrieve the remote MSS, so functionality may need to be added to record the remote MSS during the SYN handshake.

@SajjadPourali SajjadPourali requested a review from Copilot October 9, 2025 06:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds support for dynamic MSS calculation based on MTU values to account for different header lengths in IPv4 and IPv6. The change introduces a new IpMtu option that automatically calculates the appropriate MSS by subtracting header sizes from the provided MTU.

  • Adds a new IpMtu TCP option for dynamic MSS calculation
  • Introduces logic to differentiate between IPv4 and IPv6 header sizes when calculating MSS
  • Adds a constant for TCP header length to support the calculation

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread src/stream/tcp.rs Outdated
/// 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.
Copy link

Copilot AI Oct 9, 2025

Choose a reason for hiding this comment

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

Mixed punctuation used in comment. Replace the full-width comma ',' with a standard comma ','.

Suggested change
/// MTU optionhelps to set the MSS (Maximum Segment Size) option in the TCP header.
/// MTU option, helps to set the MSS (Maximum Segment Size) option in the TCP header.

Copilot uses AI. Check for mistakes.
Comment thread src/stream/tcp.rs Outdated
Comment on lines +922 to +929
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
},
));
Copy link

Copilot AI Oct 9, 2025

Choose a reason for hiding this comment

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

Magic numbers 40 and 20 should be defined as named constants for better maintainability. Consider adding IPV6_HEADER_LEN and IPV4_HEADER_LEN constants.

Copilot uses AI. Check for mistakes.
Comment thread src/stream/tcp.rs Outdated
Comment on lines +922 to +928
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
},
Copy link

Copilot AI Oct 9, 2025

Choose a reason for hiding this comment

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

The MSS calculation may result in underflow if the MTU is smaller than the combined header lengths. Add validation to ensure MTU is large enough before performing subtraction.

Suggested change
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
},
let ip_header_len = if src_addr.is_ipv6() || dst_addr.is_ipv6() {
40
} else {
20 // minimum IPv4 header size
};
let total_header_len = TCP_HEADER_LEN + ip_header_len;
if mtu < total_header_len {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!(
"MTU ({}) is smaller than combined header lengths ({})",
mtu, total_header_len
),
));
}
tcp_options.push(etherparse::TcpOptionElement::MaximumSegmentSize(
mtu - total_header_len,

Copilot uses AI. Check for mistakes.
@SajjadPourali
Copy link
Copy Markdown
Collaborator

I think it's better to keep the MMS calculation on the user side for now.

@Orvaxis
Copy link
Copy Markdown
Contributor Author

Orvaxis commented Oct 9, 2025

I think it's better to keep the MMS calculation on the user side for now.

However, the current API can only set one MSS. If dual stack is required, it cannot be processed, so you can only choose the smallest MSS

@Orvaxis
Copy link
Copy Markdown
Contributor Author

Orvaxis commented Oct 9, 2025

However, placing the MTU in a TCP option doesn't seem appropriate; it should be handled at the IP layer. In the future, this could also help with UDP packet fragmentation (for MDDS calculation).

@Orvaxis
Copy link
Copy Markdown
Contributor Author

Orvaxis commented Oct 9, 2025

This option should be deleted, and then the default calculation is based on the mtu passed in by Tcb, and then overriding it through options seems to be in line with the semantics of the current API and meets the possibility of automatic calculation.

@Orvaxis Orvaxis force-pushed the feat/better-mss-configuration branch from 79df0a7 to 146bee5 Compare October 9, 2025 11:16
@Orvaxis Orvaxis force-pushed the feat/better-mss-configuration branch from cf434f0 to 651101d Compare October 9, 2025 14:23
@SajjadPourali
Copy link
Copy Markdown
Collaborator

The MSS can be set by either the client or the server. We may have a feature to receive the MSS value from the client during negotiation, which obtained from the SYN packet. However, I am not positive about having an automatic MSS calculation on the server/IpStack side, as other low-level APIs also leave this part to be handled manually by users. This calculation can be performed during IpStack initialization since they also have access to the MTU.

@Orvaxis
Copy link
Copy Markdown
Contributor Author

Orvaxis commented Oct 9, 2025

The MSS can be set by either the client or the server. We may have a feature to receive the MSS value from the client during negotiation, which obtained from the SYN packet. However, I am not positive about having an automatic MSS calculation on the server/IpStack side, as other low-level APIs also leave this part to be handled manually by users. This calculation can be performed during IpStack initialization since they also have access to the MTU.

My previous expression might have been inaccurate. I meant that the dynamic calculation is only performed during the handshake. It calculates a default value.
This API is more like setting a preference value rather than obtaining it via MTU, because we usually don't set it, but it still gives us the correct MSS.
I have corrected the code 651101d, but the current API is very ugly and is just a demo.

@SajjadPourali
Copy link
Copy Markdown
Collaborator

The MSS can be set by either the client or the server. We may have a feature to receive the MSS value from the client during negotiation, which obtained from the SYN packet. However, I am not positive about having an automatic MSS calculation on the server/IpStack side, as other low-level APIs also leave this part to be handled manually by users. This calculation can be performed during IpStack initialization since they also have access to the MTU.

My previous expression might have been inaccurate. I meant that the dynamic calculation is only performed during the handshake. It calculates a default value. This API is more like setting a preference value rather than obtaining it via MTU, because we usually don't set it, but it still gives us the correct MSS. I have corrected the code 651101d, but the current API is very ugly and is just a demo.

I am still against this mechanism. Users might not use MSS, but they can take over its calculation during IpStack initialization. Additionally, we do not need to handle it separately for both IPv6 and IPv4! https://github.com/narrowlink/ipstack/blob/main/examples/tun.rs#L98

I understand that IPv6 has a smaller payload, and hard-coding this value sacrifices a few bytes, but I can't come up with a good API pattern.

@Orvaxis
Copy link
Copy Markdown
Contributor Author

Orvaxis commented Oct 10, 2025

Got it. I'll continue to try and improve it on my current branch, mainly by referencing smoltcp.

@Orvaxis Orvaxis closed this Oct 10, 2025
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.

3 participants