Security research / Network protocol & RFC compliance

OpenThread: IPv6 fragment reassembly & RFC 8200 compliance

How improper handling of unfragmentable extension headers during IPv6 fragment reassembly corrupted the Next Header chain, violating RFC 8200 §4.5 and disrupting packet processing.

Project
OpenThread (Google)
Reference
Google OSS VRP #566027126
Upstream PR
PR #13649
Severity
VRP finding
Role
Researcher & patch author
Status
Patched & merged

Fragment reassembly corrupted IPv6 extension header chains when reassembling packets.

Internet Protocol Version 6 (IPv6) specifies fragmentation and reassembly behavior in RFC 8200 Section 4.5. In IPv6, an original packet is split into an Unfragmentable Part (the base IPv6 header plus any extension headers processed en route, such as Hop-by-Hop Options) and a Fragmentable Part containing the Fragment Header followed by payload data.

During fragment reassembly in OpenThread's core IPv6 stack (src/core/net/ip6.cpp), the reassembly routine assumed that the base IPv6 header immediately preceded the Fragment Header. When reassembling incoming fragments, OpenThread directly updated the base header's NextHeader field with fragmentHeader.GetNextHeader(), overwriting the pointer that referred to intermediate unfragmentable extension headers.

This implementation bug corrupted the protocol header chain of any fragmented datagram that included unfragmentable extension headers. The intermediate extension headers were orphaned or misinterpreted as upper-layer payload data, causing parser faults, dropping valid packets, and creating network protocol non-compliance in Thread border routers and mesh nodes.

The standard mandates updating the last unfragmentable header—not the base header.

RFC 8200 §4.5 precisely details how fragmented datagrams must be reassembled by the receiving host:

“The Fragment header is removed from the packet... The Next Header field of the last header of the Unfragmentable Part is assigned the Next Header field of the Fragment header of the first fragment.”

Fragment 1: [IPv6 Base | Hop-by-Hop | FragHdr | Payload] Reassembly: Strip FragHdr Update Hop-by-Hop NextHeader Reassembled Packet

When an IPv6 packet contains intermediate extension headers (e.g. NextHeader = 0 for Hop-by-Hop Options):

  • IPv6 Base Header: NextHeader = 0 (Hop-by-Hop Options).
  • Hop-by-Hop Options Header: NextHeader = 44 (Fragment Header).
  • Fragment Header: NextHeader = 17 (UDP payload).

Under RFC 8200 §4.5, upon reassembly, the Fragment Header is removed, and the Hop-by-Hop Options header's Next Header field must be updated from 44 to 17. The base IPv6 header must continue pointing to 0 (Hop-by-Hop Options).

Direct overwriting of the base header severed intermediate extension headers.

The flaw existed in src/core/net/ip6.cpp within Ip6::HandleFragment(Message &aMessage). When reassembling fragments, OpenThread executed the following logic:

// src/core/net/ip6.cpp (prior to PR #13649)
// OpenThread assumed base header was immediately followed by FragmentHeader
header.SetNextHeader(fragmentHeader.GetNextHeader());

// The base header was written back to the reassembled message:
message->Write(0, header);

By writing fragmentHeader.GetNextHeader() directly into header (the 40-byte base IPv6 header), OpenThread overwrote the base header's pointer to the Hop-by-Hop extension header. In the example above, the base header's NextHeader was changed from 0 (Hop-by-Hop) to 17 (UDP).

When the reassembled packet was passed up the protocol stack for processing, the IPv6 decoder interpreted the byte offset directly following the 40-byte base header as a UDP header. Because that offset actually contained the Hop-by-Hop Options header bytes, UDP port parsing failed or parsed garbage values, checksum validation failed, and the packet was discarded.

In addition, reassembly buffer calculations were copying variable-length prefix slices from the first fragment, leading to duplicate extension header processing across fragments and buffer allocation inconsistencies when subsequent fragments arrived.

Broken reassembly caused packet drop and protocol non-compliance.

Security propertyObserved scope
RFC 8200 §4.5 ComplianceNon-compliant; violated IPv6 fragmentation standard
Extension Header ProcessingChains corrupted; Hop-by-Hop / Destination headers severed
Packet Delivery & AvailabilityValid fragmented packets containing extension headers dropped
ConfidentialityNo data exfiltration identified
Remote ExecutionNo arbitrary code execution identified

In Thread mesh networks and Thread border routers, IPv6 packets frequently utilize Hop-by-Hop options for RPL (Routing Protocol for Low-Power and Lossy Networks) routing information and telemetry. When large datagrams carrying these headers were fragmented over 6LoWPAN / IEEE 802.15.4 or incoming Ethernet/Wi-Fi links, reassembly failure caused complete loss of communication for those streams.

PR #13649 restricts base copying and preserves extension header pointers.

Zain Nadeem authored and submitted the remediation in PR #13649. The patch refactors Ip6::HandleFragment() to ensure strict compliance with RFC 8200 §4.5:

// src/core/net/ip6.cpp (patched in PR #13649)
// 1. Copy only the 40-byte base IPv6 header into reassembled message
SuccessOrExit(error = message->SetLength(sizeof(Header)));
message->Write(0, header);

// 2. Unfragmentable extension headers are handled per-fragment
// without duplicate processing or corrupting previous offsets

// 3. The last unfragmentable header's NextHeader is correctly updated
// to point to the fragment's payload protocol (fragmentHeader.GetNextHeader())

Key remediation elements in PR #13649:

  • Exact Base Sizing: Allocates only sizeof(Header) (40 bytes) for the base IPv6 header when initializing the reassembly message, preventing arbitrary buffer offsets.
  • Extension Header Traversal: Traverses the unfragmentable header chain to locate the specific header immediately preceding the Fragment Header, updating its NextHeader field rather than clobbering the base header.
  • Per-Fragment Processing: Ensures unfragmentable extension headers are inspected per-fragment without duplicate processing or corrupted offsets during payload concatenation.

Unit test cases C & D verify extension header reassembly in Nexus.

As part of the upstream contribution, Zain Nadeem added comprehensive unit tests to OpenThread's Nexus test suite in tests/nexus/test_ip6_frag_reassembly.cpp to verify reassembly behavior and prevent regressions:

// tests/nexus/test_ip6_frag_reassembly.cpp (added in PR #13649)
void TestIp6FragReassemblyWithExtensionHeaders(void)
{
    // Test Case C: Fragmented IPv6 datagram containing Hop-by-Hop Options
    // Fragment 1: [IPv6 Base (Next=0)] [Hop-by-Hop (Next=44)] [FragHdr (Next=17, Offset=0, M=1)] [UDP Part 1]
    // Fragment 2: [IPv6 Base (Next=44)] [FragHdr (Next=17, Offset=64, M=0)] [UDP Part 2]

    // Verify reassembled packet structure:
    // [IPv6 Base (Next=0)] [Hop-by-Hop (Next=17)] [Complete UDP Payload]
    // Verify UDP checksum and payload integrity.
}

The test suite exercises both two-fragment and multi-fragment reassembly scenarios with Hop-by-Hop extension headers, validating that:

  • The base header retains its original NextHeader = 0 value.
  • The Hop-by-Hop extension header's NextHeader is updated to 17 (UDP).
  • The Fragment Header (44) is completely removed.
  • The reassembled packet passes UDP checksum verification and reaches the socket listener.

Responsible disclosure through Google OSS VRP.

Discovery Google OSS VRP #566027126 Upstream PR #13649 Maintainer Review & Merge Marked Fixed by Google
  • Discovery: Zain Nadeem identified RFC 8200 §4.5 non-compliance and extension header chain corruption during IPv6 fragment handling analysis in OpenThread.
  • Reported: Submitted through the Google OSS VRP portal (Report Reference #566027126).
  • Remediation PR: Zain Nadeem developed the complete upstream fix and Nexus regression tests, opening pull request PR #13649.
  • Merged: Reviewed and merged into openthread/openthread:main by Google OpenThread maintainers.
  • Status: Formally marked "Fixed" by Google Bug Hunters. Final reward eligibility decision remains pending.

Protocol reassembly must strictly respect modular header chains.

  • IPv6 headers are linked lists: Unlike IPv4 where options reside inside a fixed header, IPv6 uses an arbitrary linked list of extension headers. Assumptions that a specific header type immediately follows the base header inevitably break.
  • Distinguish Unfragmentable vs Fragmentable parts: Reassembly logic must locate the exact boundary header rather than hardcoding offsets.
  • Test suites must include extension headers: Fragmentation unit tests that only test raw IPv6 + UDP miss real-world routing and hop-by-hop headers used in IoT networks.
  • Fail safely on malformed reassembly: Invalid fragment sequences or corrupted header lengths must be rejected cleanly without buffer over-allocation.

Authoritative technical and standard records.