Skip to content

fix(ipv6)!: an extension header parsed with extension=True cannot rebuild from its own info (#1446) - #1449

Merged
JarryShaw merged 1 commit into
mainfrom
fix/1446-ipv6-ext-header-rebuild
Oct 9, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/1446-ipv6-ext-header-rebuild

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

What is the purpose of your pull request?

  • fix — corrects a defect
  • feat · [ ] perf · [ ] refactor · [ ] test · [ ] docs · [ ] ci · [ ] release · [ ] chore

Description of your pull request and other information

Closes #1446

An extension header parsed with extension=True is handed the rest of the datagram, but parses only itself: info has no payload and .payload raises UnsupportedCall. Internet.__post_init__ now cuts .data to the header's length in that mode, so type(ex).from_data(ex.info).data == ex.data. This keeps IPv6._make_data as it is, since it already joins from_data(info).data per header. Carrying the remainder in info would have emitted those octets twice.

Covers HOPOPT, IPv6-Route, IPv6-Frag, IPv6-Opts, MH, AH, HIP and IPv6_Ext. With HOPOPT + UDP, data/rebuilt goes from 28/8 to 8/8. On ipv6.pcap frame 16 (Fragment), 442/8 becomes 8/8. IPv6 parent rebuilds stay exact, 247/247 across examples/captures.

Breaking (!): data, bytes() and len() of an extension-mode header now cover the header only. A standalone parse is unchanged.

…uild from its own info (#1446)

IPv6 hands each extension header the rest of the datagram with
extension=True. The header parses only itself, and its info holds no
payload, but its data kept every octet after it, so
type(ex).from_data(ex.info).data came back short (8 of 442 octets on
ipv6.pcap frame 16).

Internet.__post_init__ now cuts data to the header's own length in
extension mode, which covers HOPOPT, IPv6-Route, IPv6-Frag, IPv6-Opts,
MH, AH, HIP and IPv6_Ext alike. The parent IPv6 rebuild already joins
from_data(info).data per header, so it is unchanged.
@JarryShaw JarryShaw added fix Pull requests that fix a defect (fix: subject prefix) breaking Breaks public-facing behaviour or API (apply alongside the type label) review: running A cross-review is in flight against the current head - no verdict yet labels Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Coverage: 89.51% (unit tier, Python 3.14, 0b9fc265c, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 4 20 4 93.55%
pcapkit/const 18791 968 2352 869 90.90%
pcapkit/corekit 2043 89 636 25 94.77%
pcapkit/dumpkit 234 2 84 2 98.74%
pcapkit/foundation 2648 98 930 48 94.80%
pcapkit/interface 112 7 40 5 92.11%
pcapkit/protocols 16805 219 4522 183 98.08%
pcapkit/toolkit 539 74 168 3 84.58%
pcapkit/utilities 429 4 122 4 98.55%
pcapkit/vendor 4409 2342 1006 157 43.25%

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

@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at 0b9fc265c (Sonnet cross-review; the author ran on Opus).

  • No other reader breaks. ipv6.py:448 is the only extension=True site, and toolkit/{pcap,pcapng}.py read only .info and .length.
  • Reassembly is unchanged. IPv6 fragment reassembly of ipv6.pcap frames 13–16 is byte-identical to main (4778 bytes, same sha1).
  • Only the extension header's .data changes. Across 1469 frames, per-frame info and the parent IPv6.data match main.
  • Every class goes through the hook. HOPOPT, Route, Frag, Opts, MH, AH, HIP, ESP and Shim6 all call super().__post_init__. On 13 synthetic chains, len(data) == length holds everywhere.
  • Rebuilds are exact. Every extension header in the 221 IPv6 frames rebuilds byte-exact from its own info.
  • test: capture-driven parse→rebuild and dumper round trips (#1202) #1445 interaction. Merged with test: capture-driven parse→rebuild and dumper round trips (#1202) #1445, the only failures are the stale-entry flags on its fix(ipv6): an extension header parsed with extension=True cannot rebuild from its own info #1446 LAYER_GAPS rows, as expected. Whichever lands second deletes them.
  • Tests, which I re-ran: the new module gives 4 passed with 14 subtests. With internet.py from main it gives 8 failed, 3 passed.
  • Neighbours. The internet leg runs 601 OK, and reassembly, traceflow and toolkit are clean.

Nits, no change needed:

@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 9, 2026
@JarryShaw
JarryShaw merged commit 2f7aadd into main Oct 9, 2026
42 checks passed
@JarryShaw
JarryShaw deleted the fix/1446-ipv6-ext-header-rebuild branch October 9, 2026 02:03
JarryShaw added a commit that referenced this pull request Oct 9, 2026
…ed them

#1445 and #1449 merged back to back. The IPv6_Frag rows in LAYER_GAPS and
DICT_GAPS now pass, and the stale-entry check fails on them: 8 failures on
2f7aadd. With the rows removed, the module passes (4 tests, 20501 subtests).
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaks public-facing behaviour or API (apply alongside the type label) fix Pull requests that fix a defect (fix: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(ipv6): an extension header parsed with extension=True cannot rebuild from its own info

1 participant