Skip to content

ipv6tunneling: carry packets of up to 1280 bytes through a tunnel with a smaller mtu - #1292

Open
adamgeorge309 wants to merge 1 commit into
masterfrom
topic/gy/ipv6-tunnel-minimum-mtu
Open

adamgeorge309 wants to merge 1 commit into
masterfrom
topic/gy/ipv6-tunnel-minimum-mtu

Conversation

@adamgeorge309

@adamgeorge309 adamgeorge309 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

An Ipv6TunnelInterface whose mtu parameter is below 1280 bytes refused every forwarded packet larger than the mtu value with an ICMPv6 (Internet Control Message Protocol for Internet Protocol version 6) Packet Too Big that reported the tunnel mtu. RFC (Request for Comments) 2473 (Generic Packet Tunneling in IPv6) Section 7.1 (b) requires such a tunnel to encapsulate packets of up to 1280 bytes and fragment the tunnel packet, and RFC 8201 (Path MTU Discovery for IPv6) does not let a source lower its path MTU (maximum transmission unit) below 1280 bytes, so these packets never got through. The interface MTU is now max(mtu, 1280). Results change only for tunnels configured with an mtu below 1280 bytes: there a node that sends its own packets into the tunnel now fragments them to 1280 bytes instead of to the tunnel mtu. The tunnels that Mobile IPv6 (MIPv6) and Proxy Mobile IPv6 (PMIPv6) create use the default mtu of 1500 bytes and do not change.

Closes #1291

The problem

Ipv6Tunnel::configureNetworkInterface() (src/inet/networklayer/ipv6tunneling/Ipv6Tunnel.cc:35) set the interface MTU to the mtu parameter. Ipv6::fragmentAndSend() then refused a forwarded packet above that size with a Packet Too Big that reported that size (Ipv6.cc:1059-1073).

tests/module/IPv6_tunnel_mtu_below_minimum.test: hostA - borderA - transit - borderB - hostB with static routes. borderA routes hostB's prefix into an Ipv6TunnelInterface with mtu = 1200B, and hostA sends four User Datagram Protocol (UDP) packets of 1248 bytes. On master 93210f4 hostA receives four Packet Too Big messages, each reporting 1200, and hostB receives 0 of 4 packets; on this branch hostA receives none and hostB receives 4 of 4.

The fix

With the interface MTU at max(mtu, 1280), both cases of RFC 2473 Section 7.1 follow: Ipv6 accepts a packet of up to 1280 bytes into the tunnel, and the Packet Too Big for a larger packet reports at least 1280, which is case (a)'s max(tunnel MTU, 1280). Ipv6 originates the tunnel packet, so it fragments it if the tunnel packet is larger than the MTU of the outgoing interface.

A tunnel MTU below 1280 bytes is a valid state in RFC 2473: Section 6.7 derives it from the path MTU to the exit point, and Section 7.1 says what to do with it. So the value is accepted rather than rejected.

The obvious alternative, fragmenting the tunnel packet to mtu plus the 40-byte tunnel header, needs a way for Ipv6Tunnel to ask Ipv6 for a fragment size below the MTU of the outgoing interface. Ipv6 has none (FragmentationReq carries only the Don't Fragment bit), and adding one changes a contract between the modules, which a defect fix should not.

Architectural surface

The change alters the behavior of Ipv6TunnelInterface for an mtu below 1280 bytes, and the NED (Network Description) comment of the mtu parameter now states the 1280-byte floor. Ipv6Tunnel.cc now includes networklayer/ipv6/Ipv6InterfaceData.h for IPv6_MIN_MTU; both directories belong to the Ipv6 feature, and check-architecture.sh src/inet/networklayer/ipv6tunneling passes. No parameter, packet format, C++ interface or feature descriptor changes.

Verification

Release build (make MODE=release):

cd tests/module && inet_run_module_tests -m release --no-build -l ERROR -f IPv6_tunnel

IPv6_tunnel_mtu_below_minimum fails on master (hostB receives 0 packets) and passes on this branch; IPv6_tunnel passes on both.

Full suites, release, on master 93210f4 and on this branch:

cd tests/fingerprint && ./fingerprinttest -s -F tyf -t 6
cd tests/module && inet_run_module_tests -m release --no-build -l ERROR
cd tests/protocol/ipv6 && inet_run_protocol_tests -m release
cd tests/protocol/nd && inet_run_protocol_tests -m release
  • Fingerprints: 1774 rows, 2 failures and 62 errors on both, with the same actual values. The failures are examples/ipv6/nclients ETH and PPP in mipv6-refactoring.csv; the errors are rows whose optional features are disabled in this build. No fingerprint moves: no shipped example, showcase, tutorial or fingerprint configuration names Ipv6TunnelInterface, and the tunnels that MIPv6 and PMIPv6 create keep the default mtu.
  • Module tests: master runs 349 tests, 303 pass; this branch runs 350, 304 pass, the new one included. The same 46 fail on both (34 tcp_*, ConvolutionalCoder12/34, EtherHost_lifecycle, ExternalProcess_3, Ieee80211BitDomain/SymbolDomain, Ieee8021d-Rstp/Stp, IPv6_packet_too_big, MIPv6_tcp_handover, PacketGate_1, UDPSocket_1).
  • Protocol tests: tests/protocol/ipv6 26 of 27 pass on both (Rfc8200OverlappingFragments fails on both); tests/protocol/nd 22 of 37 pass on both, with the same 15 failures.
  • Focused tests in a debug build (make MODE=debug, inet_run_module_tests -m debug --no-build -l ERROR -f IPv6_tunnel): all pass on this branch, and the new test fails on master in debug too.
  • doc/project/enforcement/check-commits.sh, check-classification.sh and check-architecture.sh on src/inet/networklayer/ipv6tunneling and src/inet/networklayer/ipv6: PASS. Statistical tests and check-cpp.sh were not run.

Not addressed here

RFC 2473 Section 7.1 (b) fragments the tunnel packet to the Path MTU to the exit point, and Section 6.7 derives the tunnel MTU from that Path MTU. The entry point fragments to the MTU of its outgoing interface instead (see The fix), and the mtu parameter stays a fixed value. If a link inside the tunnel is smaller than the outgoing interface, a tunnel packet that carries a packet of 1280 bytes or less is still lost there.

Landing order

git merge-tree against the heads of the open pull requests #1228, #1232, #1252 and #1256 conflicts only in WHATSNEW, where each adds an item with the same number. #1264 also conflicts in the include block of Ipv6Tunnel.cc, where both add a line at the same place; keeping both lines resolves it. The pull request that lands later renumbers its WHATSNEW item.


Devin Review

An Ipv6TunnelInterface whose mtu parameter is below 1280 bytes refused
every forwarded packet larger than that value in Ipv6::fragmentAndSend()
(Ipv6.cc:1059-1073). It sent an ICMPv6
(Internet Control Message Protocol for Internet Protocol version 6)
Packet Too Big that reported the tunnel mtu, for example 1200, because
Ipv6Tunnel::configureNetworkInterface() (Ipv6Tunnel.cc:35) copied the
parameter into the interface MTU (maximum transmission unit) unchanged.
RFC (Request for Comments) 8201 (Path MTU Discovery for IPv6) Section 4
does not let a source lower its path MTU below 1280 bytes, so packets
between the mtu parameter and 1280 bytes never got through.

RFC 2473 (Generic Packet Tunneling in IPv6) Section 7.1 separates two
cases for an original packet that exceeds the tunnel MTU. One larger
than the IPv6 minimum link MTU of 1280 bytes is refused with a Packet
Too Big that reports max(tunnel MTU, 1280). One of 1280 bytes or less is
encapsulated, and the tunnel packet is fragmented. The interface MTU is
now max(mtu, 1280), which gives both cases. A node that sends its own
packets into such a tunnel fragmented them to the tunnel mtu, and now
fragments them to 1280 bytes. A tunnel MTU below 1280 bytes is a valid
state in RFC 2473, which derives it from the path MTU to the exit point
(Section 6.7), so the value is accepted, not rejected.

Ipv6 originates the tunnel packet and fragments it to the MTU of its
outgoing interface, not to mtu plus the 40-byte tunnel header: Ipv6 has
no way for a sender to ask for a smaller fragment size, and adding one
would change a contract between the modules. So if a link inside the
tunnel is smaller than the outgoing interface, a tunnel packet that
carries a packet of 1280 bytes or less is still lost there.

The tunnels that Mobile IPv6 and Proxy Mobile IPv6 create keep the
default mtu of 1500 bytes and do not change.

tests/module/IPv6_tunnel_mtu_below_minimum.test reproduces it: a router
tunnels 1248-byte packets into a tunnel with an mtu of 1200 bytes.
Before this commit hostB receives 0 of 4 packets and hostA receives four
Packet Too Big messages; after it, hostB receives 4 of 4.

Change: src.networklayer.ipv6tunneling | behavior.change.fix | test whatsnew

@devin-ai-integration devin-ai-integration Bot 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.

Devin Review found 1 potential issue.

Devin Review

// fragmented. So the interface accepts packets of up to that size. Ipv6
// originates the tunnel packet and fragments it to the MTU of its outgoing
// interface.
networkInterface->setMtu(std::max(par("mtu").intValue(), (intval_t)IPv6_MIN_MTU));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Transit links drop admitted tunnel packets

With mtu below 1280 and a smaller transit link, the tunnel admits inner packets that its outer packet cannot carry. fragmentAndSend uses the entry's outgoing MTU, so a 1280-byte transit link drops a 1320-byte packet sent over a 1500-byte entry link.

Learn more

The tunnel interface MTU controls the size of the inner packet accepted for encapsulation. The tunnel's configured MTU represents the path toward its exit, but handleUpperPacket only submits the inner packet to IPv6. fragmentAndSend fragments the resulting outer packet only when it exceeds the outgoing interface MTU. Thus admitting an inner packet does not guarantee that the outer packet fits later links. A transit router cannot fragment that forwarded outer packet, and it sends Packet Too Big instead.

Example: Configure tunnel mtu = 1200B, an entry link with MTU 1500B, and a transit link with MTU 1280B. An inner 1280-byte packet becomes a 1320-byte outer packet. The entry sends it unfragmented; the transit router rejects it, rather than delivering the original packet through the tunnel.

Recommended fix: Arrange for the tunnel entry to fragment the outer packet to the tunnel path's usable size when the inner packet is at most 1280 bytes. Preserve the 1280-byte acceptance floor for the inner packet and the Packet Too Big behavior above it. Extend the module test with a constrained downstream link and verify fragment transmission and delivery.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

This branch has not been deployed

No deployments
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.

ipv6tunneling: a tunnel whose mtu is below 1280 bytes refuses packets of up to 1280 bytes (RFC 2473 Section 7.1 (b))

1 participant