Skip to content

ipv6: a tunnel exit point survives a hook that drops the inner datagram - #1252

Open
adamgeorge309 wants to merge 1 commit into
masterfrom
topic/gy/ipv6-tunnel-exit-drop
Open

adamgeorge309 wants to merge 1 commit into
masterfrom
topic/gy/ipv6-tunnel-exit-drop

Conversation

@adamgeorge309

@adamgeorge309 adamgeorge309 commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

A tunnel exit point crashed when a netfilter PREROUTING hook dropped a decapsulated IPv6-in-IPv6 datagram. INET's own Mobile IPv6 (MIPv6) home agent drops every datagram tunneled to it from a source it has no binding for, so shipped modules reach the crash.

Closes #1251

The problem

The tunnel branch of Ipv6::localDeliverFinish() (src/inet/networklayer/ipv6/Ipv6.cc:852-856 on master) passes the decapsulated datagram through the PREROUTING hooks again:

        auto verdict = datagramPreRoutingHook(packet);
        if (verdict == INetfilter::IHook::ACCEPT)
            preroutingFinish(packet, fromIE, nullptr, Ipv6Address::UNSPECIFIED_ADDRESS);
        else if (verdict == INetfilter::IHook::DROP)
            delete packet;

The DROP case of Ipv6::datagramPreRoutingHook() has already deleted the datagram before it returns, as INetfilter::IHook::DROP documents ("will be deleted"). The branch deleted it a second time. The other caller, Ipv6::handleMessageWhenUp() (Ipv6.cc:256), continues only on ACCEPT and deletes nothing; the tunnel branch now does the same. The defect came with 95ecc59, which moved decapsulation from Ipv6Tunneling into Ipv6.

On a home agent, Mipv6::datagramPreRoutingHook() returns DROP for a decapsulated datagram whose tunnel source is not a known tunnel exit point. That is a looser form of the check in RFC 6275 Section 10.4.5, which requires the primary care-of address: "the home agent MUST verify that the Source Address in the tunnel IP header is the mobile node's primary care-of address".

Architectural surface

None changed. The fix follows the existing ownership contract of INetfilter::IHook::DROP.

Verification

tests/module/IPv6_tunnel_exit_drop.test, with shipped modules only: host A tunnels its traffic for H to a HomeAgent6 that has no binding for A. Release build, master 49e1fa0 with only the new test file added: cd tests/module && inet_run_module_tests -m release --no-build -l ERROR -f IPv6_tunnel_exit_drop reports FAIL. The runner's output is buffered and stops before the crash. Run the test program directly in tests/module/work/IPv6_tunnel_exit_drop, which the runner created: stdbuf -o0 -e0 ./IPv6_tunnel_exit_drop -u Cmdenv -f omnetpp.ini -n .:../../../../src, exit status 139. The last lines:

** Event #58  t=0.654227418073  on NeighbourSolicitation (inet::Packet)  in Ipv6TunnelExitDropTestNetwork.HA.ipv6.ipv6 (Ipv6, id=91)    13% completed  (13% total)
(Ipv6)Ipv6TunnelExitDropTestNetwork.HA.ipv6.ipv6: Routing datagram `' with dest=2001:db8:1:0:8aa:ff:fe00:2, requested nexthop is <unspec> on unspec interface: 
(Ipv6)Ipv6TunnelExitDropTestNetwork.HA.ipv6.ipv6: local delivery
(Ipv6)Ipv6TunnelExitDropTestNetwork.HA.ipv6.ipv6: Tunnelled IP datagram
(Ipv6)Ipv6TunnelExitDropTestNetwork.HA.ipv6.ipv6: Looking up tunnels...
(Ipv6)Ipv6TunnelExitDropTestNetwork.HA.ipv6.ipv6: found vIf=-1
(Ipv6)Ipv6TunnelExitDropTestNetwork.HA.ipv6.ipv6: Dropping packet: tunnel source is not a known tunnel exit point.
Segmentation fault (core dumped)

The first decapsulated datagram is the Neighbour Solicitation of the tunnel interface's own Duplicate Address Detection. After the commit the home agent drops all four decapsulated datagrams (that Neighbour Solicitation, a Router Solicitation and two User Datagram Protocol (UDP) datagrams: four Dropping packet: tunnel source is not a known tunnel exit point. lines in the test's test.out), H receives 0 packets, and the run reaches its 5 s time limit.

Commands, release build (make MODE=release), run on master 49e1fa0 and on this branch before its rebase onto 4eb3bb4 (the 83 commits between them change no file under src/):

cd tests/fingerprint && ./fingerprinttest -s -F tyf
cd tests/module && inet_run_module_tests -m release --no-build -l ERROR
cd tests/protocol/ipv6 && inet_run_protocol_tests -m release
  • Fingerprints, identical on both: 1774 rows, 0 failures, 62 errors. The 62 errors are rows whose optional features are disabled in this build (Voice over IP (VoIP) streaming, the lwIP Transmission Control Protocol (TCP) stack, OpenSceneGraph (OSG) visualization, Z3 gate scheduling).
  • Module tests: the same 46 fail on unmodified master and on this branch, so they are pre-existing (34 tcp_*, ConvolutionalCoder12/34, EtherHost_lifecycle, ExternalProcess_3, Ieee80211BitDomain/SymbolDomain, Ieee8021d-Rstp/Stp, IPv6_packet_too_big, MIPv6_tcp_handover, PacketGate_1, UDPSocket_1). The new test passes; 347 tests, 301 pass.
  • Protocol tests (tests/protocol/ipv6): 26 of 27 pass on both; Rfc8200OverlappingFragments fails on master too.
  • Focused, release: inet_run_module_tests -m release --no-build -l ERROR -f 'IPv6_tunnel|NetfilterHooks_Ipv6|IPsec_Ipv6|MIPv6_': 15 tests, 14 pass; MIPv6_tcp_handover fails on unmodified master too (one of the 46).
  • Focused, debug build (make MODE=debug): inet_run_module_tests -m debug --no-build -l ERROR -f 'IPv6_tunnel': both pass.
  • doc/project/enforcement/check-architecture.sh src/inet/networklayer/ipv6: PASS.

Landing order

This touches the same tunnel branch of localDeliverFinish() as #1247 (it inserts lines just above the ones changed here). git merge-tree merges Ipv6.cc cleanly in either order; the only conflict is WHATSNEW, where both add backward compatible item 6. #1236 merges without conflict. Either order works; the pull request that lands second renumbers its WHATSNEW item.


Devin Review

A tunnel exit point passes the decapsulated inner datagram through the
netfilter PREROUTING hooks again, in the tunnel branch of
Ipv6::localDeliverFinish(). When a hook returned DROP, that branch deleted
the datagram, which Ipv6::datagramPreRoutingHook() had already deleted
before returning.

INetfilter::IHook::DROP means "will be deleted", and the dispatcher
datagramPreRoutingHook() does the deleting. Its other caller,
Ipv6::handleMessageWhenUp(), continues only on ACCEPT and deletes
nothing; the tunnel branch does the same now. The defect came with
95ecc59, which moved IPv6-in-IPv6 decapsulation into Ipv6.

INET's own Mobile IPv6 (MIPv6) returns DROP there: on a home agent,
Mipv6::datagramPreRoutingHook() drops a decapsulated datagram whose tunnel
source is not a known tunnel exit point, INET's form of the check in RFC
6275 Section 10.4.5. tests/module/IPv6_tunnel_exit_drop.test reproduces it
with shipped modules only: host A tunnels its traffic to a HomeAgent6 that
has no binding for A. Before this commit the run ends with a segmentation
fault at t=0.654227418s, on the first decapsulated datagram (the Neighbour
Solicitation of the tunnel interface's own Duplicate Address Detection);
after it the home agent drops all four decapsulated datagrams and the run
reaches its time limit.

Change: src.networklayer.ipv6 | 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: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

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: a tunnel exit point deletes a hook-dropped decapsulated datagram twice

1 participant