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
@github-project-automation github-project-automation Bot moved this from In review to Done in PyPCAPKit Oct 9, 2026
@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
@JarryShaw JarryShaw added this to the PyPCAPKit v1.5 milestone Oct 26, 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