feat: add mtu option for dynamic calculation of mss#70
Conversation
There was a problem hiding this comment.
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
IpMtuTCP 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.
| /// 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. |
There was a problem hiding this comment.
Mixed punctuation used in comment. Replace the full-width comma ',' with a standard comma ','.
| /// MTU option,helps 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. |
| 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 | ||
| }, | ||
| )); |
There was a problem hiding this comment.
Magic numbers 40 and 20 should be defined as named constants for better maintainability. Consider adding IPV6_HEADER_LEN and IPV4_HEADER_LEN constants.
| 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 | ||
| }, |
There was a problem hiding this comment.
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.
| 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, |
|
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 |
|
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). |
|
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. |
79df0a7 to
146bee5
Compare
cf434f0 to
651101d
Compare
|
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. |
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. |
|
Got it. I'll continue to try and improve it on my current branch, mainly by referencing smoltcp. |
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.