Skip to content

ipv6: relay ICMPv6 errors about tunnel packets to the original source - #1294

Open
adamgeorge309 wants to merge 2 commits into
masterfrom
topic/gy/ipv6-tunnel-icmp-error-relay
Open

adamgeorge309 wants to merge 2 commits into
masterfrom
topic/gy/ipv6-tunnel-icmp-error-relay

Conversation

@adamgeorge309

@adamgeorge309 adamgeorge309 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

A router that forwards a User Datagram Protocol (UDP) or Transmission Control Protocol (TCP) packet into an Ipv6TunnelInterface stopped the simulation when an ICMPv6 (Internet Control Message Protocol for Internet Protocol version 6) error about the tunnel packet came back. The tunnel entry point now reports the error to the source of the original packet, as RFC (Request for Comments) 2473 (Generic Packet Tunneling in IPv6) Section 8 requires. A node that is itself the entry point and the source of the original packet gets the reported MTU (maximum transmission unit) minus the 40-byte tunnel header, for example 1360 instead of 1400, so its TCP no longer stalls. Results change only where an ICMPv6 error about a tunnel packet arrives: on a router, where the simulation stopped before, and on a node that is entry point and source, where the transport protocol now sees the smaller MTU. The tunnels that Mobile IPv6 (MIPv6) and Proxy Mobile IPv6 (PMIPv6) create take the same code path; no configuration with them was run with a tunnel error.

Closes #1293

The problem

Ipv6::handleIcmpErrorIndication() (src/inet/networklayer/ipv6/Ipv6.cc:306-339 on master) pops the quoted IPv6 header and dispatches the indication by its next header. For a tunnel packet the next header is IPv6, so the indication came back to Ipv6, which popped the inner header and dispatched it again, to the inner transport protocol.

test (tests/module) master 93210f4 this branch
IPv6_tunnel_packet_too_big: router entry point, a Packet Too Big reporting 1400 about a 1488-byte tunnel packet stops: Unknown protocol: protocolId = 69, protocolName = udp at borderA hostA gets a Packet Too Big reporting 1360
IPv6_tunnel_packet_too_big_small: the same with a 1248-byte original packet and a 1280-byte link stops the same way nothing is reported to hostA
IPv6_tunnel_unreachable: the tunnel exit point does not exist stops the same way hostA's UDP gets 4 Destination Unreachable messages
IPv6_tunnel_packet_too_big_tcp: host entry point, TCP with Path MTU Discovery (pmtudEnabled) and a maximum segment size (MSS) of 1380 MSS lowered to 1340 from the reported 1400; every full segment refused again; no data arrives in 10 s MSS lowered to 1300; all 100000 bytes arrive

The commits

  1. ipv6: fix: relay a tunnel packet's ICMPv6 error to the original source: the entry point reports the errors that RFC 2473 Section 8.1 lists to the source of the original packet, as Section 8.2 says, and drops the others; a local source keeps master's path. Tests: IPv6_tunnel_packet_too_big, IPv6_tunnel_packet_too_big_small, IPv6_tunnel_unreachable.
  2. ipv6: fix: give a local tunnel source the MTU left after the tunnel header: the indication for a local source carries the translated MTU as well. Test: IPv6_tunnel_packet_too_big_tcp.

Architectural surface

A new protected virtual method Ipv6::handleTunnelIcmpErrorIndication(Indication *) in the INET_API class Ipv6. It is virtual like handleIcmpErrorIndication() next to it, so that a subclass can change which errors reach the source of the original packet, for example when it sends the Tunnel Encapsulation Limit option. Ipv6 now handles an ICMPv6 error about an IPv6-in-IPv6 packet itself instead of dispatching it to a transport protocol. It sends its reports through the existing Icmpv6::sendErrorMessage(). No NED (Network Description) parameter, packet format 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

The four new tests fail on master and pass on this branch; the existing IPv6_tunnel.test passes on both. At the first commit, its three tests and IPv6_tunnel.test pass.

Full suites, release, on master 93210f4 and at both commits of 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 master and at both commits, 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.
  • Module tests: master runs 349 tests, 303 pass; the first commit runs 352, 306 pass; the tip runs 353, 307 pass, the four new ones 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). IPv6_packet_too_big is about a router's Packet Too Big for a forwarded packet without a tunnel; it fails on master in release builds only and passes in debug.
  • 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 at the tip of this branch, and the new tests fail on master in debug too. The first commit was built in release only.
  • 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

  • An original packet of 1280 bytes or less whose tunnel packet draws a Packet Too Big inside the tunnel is not reported to its source (RFC 2473 Section 8.2). Section 7.1 (b) then has the entry point fragment the tunnel packet to the path MTU to the exit point, but the IPv6 layer keeps no path MTU estimate, so later tunnel packets of that size are lost at the same link.
  • An ICMPv6 error about a tunnel packet that the entry point fragmented quotes a Fragment header after the outer IPv6 header. Icmpv6 discards it before it reaches Ipv6 ("Transport protocol 44 not registered, packet dropped"), so this fix does not see it.

Landing order

git merge-tree against the heads of the open pull requests #1228, #1232, #1252, #1256 and #1264 merges Ipv6.cc and Ipv6.h cleanly, including #1264, which also edits both files. The only conflict is WHATSNEW, where each adds an item with the same number; the pull request that lands later renumbers its item.


Devin Review

A router that forwards a User Datagram Protocol (UDP) or Transmission
Control Protocol (TCP) packet into an Ipv6TunnelInterface stopped the
simulation when an ICMPv6 (Internet Control Message Protocol for
Internet Protocol version 6) error about the tunnel packet came back:

  Unknown protocol: protocolId = 69, protocolName = udp, servicePrimitive =
  INDICATION, pathStartGate = ...borderA.ipv6.ipv6.transportOut

handleIcmpErrorIndication() pops one quoted IPv6 header and dispatches
the indication by its next header. For a tunnel packet the next header
is IPv6. The indication therefore came back to Ipv6 itself, which popped
the inner header and dispatched it to the inner transport protocol. A
router has no such module. Now the tunnel entry point reports the error
to the source of the original packet.

RFC (Request for Comments) 2473 (Generic Packet Tunneling in IPv6)
Section 8.1 lists the problems that the entry point also reports to the
source of the original packet: hop limit exceeded, unreachable node, a
Parameter Problem about the Tunnel Encapsulation Limit option, which
INET does not send, and packet too big. Section 8.2 reports the first three as a Destination Unreachable
with code 3 (address unreachable), and a packet too big as a Packet Too
Big with the reported MTU (maximum transmission unit) minus the tunnel
headers, sent only if the original packet is larger than the IPv6
minimum link MTU of 1280 bytes. Section 7.1 (a) keeps the reported MTU
at 1280 bytes or more.

handleTunnelIcmpErrorIndication() does that for Destination
Unreachable, Packet Too Big and Time Exceeded with code 0 (hop limit
exceeded), and drops every other error about a tunnel packet with a
warning. Every Destination Unreachable code counts as an unreachable
node: about a forwarded packet INET sends only code 0 (no route) and
code 3 (address resolution failed). The ICMPv6 message quotes at most 1280 bytes, so the size of
the original packet comes from the Payload Length of the quoted inner
header. If this node is itself the source of the original packet, the
indication is processed again for the inner header, as before.

New tests in tests/module, each failing before this commit:
IPv6_tunnel_packet_too_big.test, IPv6_tunnel_packet_too_big_small.test
and IPv6_tunnel_unreachable.test.

Change: src.networklayer.ipv6.Ipv6 | behavior.change.fix | test whatsnew | ipv6-tunnel-icmp-errors
…eader

A node that was both the entry point of an Ipv6TunnelInterface and the
source of the original packet passed an ICMPv6 (Internet Control
Message Protocol for Internet Protocol version 6) Packet Too Big about
its tunnel packet to the transport protocol with the MTU (maximum
transmission unit) of the tunnel packet, which leaves no room for the
40-byte tunnel header. A Transmission Control Protocol (TCP) connection
with Path MTU Discovery (pmtudEnabled) then stalled: on a Packet Too Big
reporting 1400, TCP lowered its maximum segment size (MSS) to 1340,
every full segment made a 1440-byte tunnel packet that the 1400-byte
link refused again, and no data arrived in 10 s.

RFC (Request for Comments) 2473 (Generic Packet Tunneling in IPv6)
Section 8.2 gives the source of the original packet the reported MTU
minus the tunnel headers, and Section 7.1 (a) keeps it at 1280 bytes or
more. handleTunnelIcmpErrorIndication() already applies that
translation when it relays the message to another node; it now applies
it to the indication that it processes again for a local source. The
1280-byte condition of Section 8.2 is not applied here: it decides
whether a message is sent to another node, while a local transport
protocol only receives the MTU.

tests/module/IPv6_tunnel_packet_too_big_tcp.test reproduces it; with
this commit the MSS is 1300 and all 100000 bytes arrive.

Change: src.networklayer.ipv6.Ipv6 | behavior.change.fix | test whatsnew | ipv6-tunnel-icmp-errors

@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 2 potential issues.

Devin Review

Comment on lines +378 to +383
if (rt->isLocalAddress(innerHeader->getSrcAddress())) {
// this node is also the source of the original packet
if (packetTooBig)
errorInd->setMtu(innerMtu);
handleIcmpErrorIndication(indication);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Tunnel errors abort local TCP connections

For a locally sourced tunnel packet, handleTunnelIcmpErrorIndication forwards Destination Unreachable with its outer code unchanged. TcpConnection::processIcmpv6Error aborts connection setup for port-unreachable or administratively prohibited codes. The tunnel error must reach TCP as address unreachable, not a port or policy failure.

Learn more

A tunnel error describes the outer route rather than the transport endpoint in the inner packet. The local-source branch sends the original indication directly into IPv6 again, leaving a Destination Unreachable error's code unchanged. TcpConnection::processIcmpv6Error treats port-unreachable and administratively prohibited codes as hard errors during connection setup. For a remote original source, this method already creates a new Destination Unreachable with address-unreachable code 3.

Example: Host A opens TCP over its own IPv6 tunnel. An outer router sends Destination Unreachable, code 4 (port unreachable), quoting its tunnel packet. Host A passes code 4 to TCP, which closes the pending connection rather than reporting address unreachable for the tunnel path.

Recommended fix: Translate allowed non-PTB errors to address-unreachable code 3 for the local-source branch as well, before calling handleIcmpErrorIndication. Cover both local and remote sources with a code-4 or code-1 outer error.

Devin Review


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

Comment on lines +378 to +383
if (rt->isLocalAddress(innerHeader->getSrcAddress())) {
// this node is also the source of the original packet
if (packetTooBig)
errorInd->setMtu(innerMtu);
handleIcmpErrorIndication(indication);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Spurious size errors for local tunnel sources

For a locally sourced inner packet of at most 1280 bytes, handleTunnelIcmpErrorIndication forwards Packet Too Big. The remote-source path suppresses that error, but the local-source return bypasses its size check. Local transports receive an error for a packet the tunnel must fragment instead.

Learn more

Packet Too Big on the outer tunnel path must not be reported for an inner IPv6 packet no larger than the minimum link MTU of 1280 bytes. The remote-source path checks the original packet's IPv6 payload length before generating a new error, but the local-source branch forwards the original indication immediately. The MTU adjustment does not suppress the indication.

Example: A host sends a 1248-byte IPv6 packet through its own tunnel across a 1280-byte link. The 1288-byte outer packet triggers Packet Too Big. The host's UDP module receives the translated error even though the entry point must handle fragmentation without reporting this error to UDP.

Recommended fix: Apply the inner-packet size guard before the local-versus-remote split for Packet Too Big, and add a local-source counterpart to the existing small-packet tunnel test.

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.

ipv6: an ICMPv6 error about a tunnel packet stops a router entry point and gives a local source the tunnel packet's MTU

1 participant