Skip to content

fix(protocols): a later IPv6 fragment's data is raw, not an upper-layer header (#1545) - #1552

Merged
JarryShaw merged 1 commit into
mainfrom
fix/1545-ipv6-nonfirst-fragment
Oct 10, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/1545-ipv6-nonfirst-fragment

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Please follow the guide below

What is the purpose of your pull request?

Tick the commit type your subject line carries.

  • fix — corrects a defect
  • feat — adds a feature
  • perf — changes performance, not behaviour
  • refactor — changes neither behaviour nor performance
  • test — tests only
  • docs — documentation only
  • ci — workflows or build tooling
  • release — bumps the version or rolls up a distribution
  • chore — anything else

Description of your pull request and other information

Closes #1545.

After #1540 a Fragment header with a non-zero offset ends the extension-header walk, but IPv6._decode_next_layer still dispatched the rest on its Next Header. So ipv6.pcap frames 14–16 read as IPv6-Frag:UDP:Raw with garbage ports. Now no code is dispatched for a known non-zero offset, and the rest is a Raw leaf with no protocol alias. Frames 14–16 read Ethernet:IPv6:IPv6-Frag:Raw, which matches tshark (ipv6.fraghdr:data), and 'UDP' in frame is false.

  • protocol still names the first fragment's upper layer, and fragment/hdr_len/raw_len are unchanged. Reassembled datagrams over every capture under examples/captures/ are identical before and after (IPv6: one 4778-octet datagram from frames 13–16).
  • A Fragment header parsed with no offset (IPv6_Ext stand-in) still dispatches as before, since it is not known to be a later fragment.
  • Tests: the new test_ipv6_later_fragment_raw_unit.py covers UDP and TCP at four offsets, M set and clear, make→parse, and a first/atomic-fragment guard. test_ipv6_extension_runtime.py covers frames 14–16; its frame-16 chain pin had encoded the bug. Round trips stay byte-exact.
  • The dpkt and scapy engines already read these frames as raw (bytes and Raw). No Gap rows cite fix(protocols): a non-first IPv6 fragment's data is dissected as an upper-layer header #1545.

…er header (#1545)

A Fragment header with a non-zero offset ended the extension header walk
(#1539), but the octets after it were still dispatched on its Next Header,
so a slice from the middle of a UDP or TCP datagram read as a UDP or TCP
header. RFC 8200 section 4.5 puts that header in the first fragment only,
so a later fragment's data is now a Raw leaf with no protocol alias.
``protocol`` still names the first fragment's upper layer, and the split
that reassembly takes is unchanged.
@JarryShaw JarryShaw added fix Pull requests that fix a defect (fix: subject prefix) review: running A cross-review is in flight against the current head - no verdict yet labels Oct 10, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at 50cbc5f21. Cross-reviewed on Sonnet; the author ran Opus.

  • Behaviour: ipv6.pcap frames 14–16 now chain Ethernet:IPv6:IPv6-Frag:Raw, which matches tshark (data). Frame 13 is unchanged. Across 1,590 frames in every capture, no other chain changes, and reassembly gives 1,571 datagrams hash-identical to main.
  • 24 hand-built cases:
    • A later fragment stays Raw behind HBH, Routing, Dest-Opts, AH and combinations of them, whatever its Next Header.
    • First and atomic fragments still dissect.
  • Round trips: all 16 ipv6.pcap frames are byte-exact through from_data(info), from_data(info.to_dict()) and re-parse.
  • Fail-before: the PR's tests give 21 failed on main's ipv6.py.
  • With fix(toolkit): the pypcapfile engine warns that it leaves TCP over IPv6 out (#1513) #1535: merged together, test_pypcapfile_unit.py gives 35 passed in 3.11.

One reviewer finding I refuted: the review asked for a test pinning the "offset unknown" guard (ipv6.py:625), saying an opaque = True mutant survives. I ran that mutant. It is killed by the existing test_ipv6_unit.py::test_ipv6_decode_next_layer_handles_extension_headers_and_fragments (None != <TransType.UDP: 17>). The review had not run that file, so no change is needed.

Not blocking:

@JarryShaw JarryShaw added review: good-to-go Cross-review at the current head says ready; CI state is separate and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Coverage: 90.02% (unit tier, Python 3.14, 50cbc5f21, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 4 20 4 93.55%
pcapkit/const 18795 842 2356 756 92.09%
pcapkit/corekit 2204 67 712 34 96.26%
pcapkit/dumpkit 258 2 90 2 98.85%
pcapkit/foundation 2797 132 986 48 94.18%
pcapkit/interface 108 7 36 5 91.67%
pcapkit/protocols 16994 229 4546 190 98.03%
pcapkit/toolkit 635 98 198 5 83.31%
pcapkit/utilities 431 4 124 4 98.56%
pcapkit/vendor 4409 2342 1006 157 43.25%

Per-file detail: the coverage-html artifact of this run.

@JarryShaw
JarryShaw merged commit b0bf4b9 into main Oct 10, 2026
41 checks passed
@JarryShaw
JarryShaw deleted the fix/1545-ipv6-nonfirst-fragment branch October 10, 2026 03:37
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 10, 2026
JarryShaw added a commit that referenced this pull request Oct 10, 2026
…er header (#1553) (#1562)

A datagram with a non-zero Fragment Offset now passes no next-layer code to
_decode_next_layer, so its data is a Raw leaf with no protocol name, as
Wireshark shows it, instead of a bogus UDP/TCP header read from mid-datagram
octets. info.protocol, the lengths and packet.payload are unchanged, so
reassembly and every round trip are too. First fragments and unfragmented
datagrams are still dissected. Mirrors #1552's IPv6 fix for #1545.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Pull requests that fix a defect (fix: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(protocols): a non-first IPv6 fragment's data is dissected as an upper-layer header

1 participant