Skip to content

Document the fragmented-traffic drop on the direct path (#779) - #780

Merged
kasnder merged 2 commits into
masterfrom
fix/ipv6-fragment-known-limitation
Aug 22, 2026
Merged

Document the fragmented-traffic drop on the direct path (#779)#780
kasnder merged 2 commits into
masterfrom
fix/ipv6-fragment-known-limitation

Conversation

@kasnder

@kasnder kasnder commented Aug 22, 2026

Copy link
Copy Markdown
Member

Closes #779.

Implements the issue's option B: fragmented traffic keeps being dropped on the direct path, and the limitation becomes stated, deliberate, and observable rather than silent.

Decision rationale

  • Option A (reassembly) rejected: per-packet fragment buffers are a memory + battery cost for state that legitimate traffic rarely produces — PMTUD keeps most flows unfragmented. This is exactly the trade the standing constraints rule out.
  • Option C (dispatch first fragments only) rejected as in the issue: continuations carry no L4 header, so sessions strand half-open.
  • ESP stays a documented limitation permanently (stated in code comment and docs).

What changed

  • ip.c:
    • Comment at the IPv4 IP_MF guard documenting that first fragments are dropped deliberately, that this fires before the WireGuard hijack (unlike IPv6 fragments), and why reassembly was rejected.
    • Comment at the ext-header walk call site explaining what a Fragment(44)/ESP(50)-stopped walk means downstream: no L4 dispatch branch matches, so an allowed packet is dropped on the direct path; WG-routed flows pass through raw before dispatch.
    • New trailing else on the L4 dispatch chain: a previously silent blackhole now logs Protocol %d allowed but not forwardable, dropping at WARN — one logcat line per affected packet, no DB writes.
  • ip6_ext.h: the Fragment bullet of the walk's contract now cross-references IPv6 fragmented traffic stays blackholed on the direct path (Fragment header stops the ext-header walk) #779.
  • agents/docs/codebase-map.md: new "Known limitations (deliberate)" section covering both families' behavior, the IPv4-before-WG-hijack asymmetry, and the ESP decision.

Verification

  • NDK clang (ndk;27.2.12479018, aarch64-linux-android24) -fsyntax-only -Wall -Wextra over ip.c: clean; all warnings pre-existing, none in changed regions.
  • Host tests with -Wall -Wextra -Werror: ip6_ext_test and dns_frame_test both pass.
  • No Java/Kotlin touched; behavior change limited to the one new WARN log line on the previously silent drop path.

The IPv6 extension-header walk stops at Fragment (44) and ESP (50), so
those packets match no L4 dispatch branch in handle_ip(): an allowed
packet is silently blackholed on the direct path. IPv4 fragments
(IP_MF, first fragments included) are dropped even earlier -- before
the WireGuard hijack, unlike their IPv6 counterparts.

Decide the issue's option B: keep dropping (reassembly rejected as a
battery/memory cost against traffic PMTUD keeps rare; dispatching first
fragments alone would strand mid-flow continuations), document it at
both drop sites and in the codebase map, state that ESP stays a
permanent limitation, and log the previously silent drop instead of
vanishing. See #779.
frag_off is network byte order but IP_MF and IP_OFFMASK are host-order
constants, and the test had no ntohs. On little-endian Android that never
looked at More Fragments at all: it tested bit 13 of the byte-swapped
value, i.e. bit 0x20 of the real field's low byte, so the drop fired on an
arbitrary slice of fragment offsets (256-511 bytes, 768-1023, ...). True
first fragments (MF set, offset 0) fell through and were parsed as whole
packets; last fragments (MF clear, offset non-zero) fell through too and
had their payload read as a TCP/UDP header, yielding garbage ports. The
ERROR log printed the same swapped value, which is why the offsets in it
never looked wrong enough to chase.

Convert once and test both halves, so first, middle and last fragments
alike drop, and the comment added alongside it is true of the code.

Also rate-limit the new undispatchable-protocol WARN the way the two
WireGuard drop paths in this function already do -- a single ESP flow hits
it every packet -- and keep HOPOPTS/IGMP/ESP out of that log, mirroring
the exemption at the "Unknown protocol" site above. The throttle counts on
its own counter: sharing one with the exempt protocols would let steady ESP
traffic eat every slot and silence the protocols worth seeing.
@kasnder
kasnder merged commit 8c119b0 into master Aug 22, 2026
2 checks passed
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 fragmented traffic stays blackholed on the direct path (Fragment header stops the ext-header walk)

1 participant