Skip to content

fix(ipv4,ipv6)!: keep the octets past Total Length or Payload Length as captured (#1209) - #1431

Merged
JarryShaw merged 1 commit into
mainfrom
fix/1209-link-trailer-kept
Oct 8, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/1209-link-trailer-kept

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner
  • Searched for similar pull requests

  • Followed the coding style — pylint/mypy/isort run on the 6 files: isort clean, mypy 4 errors same as main, pylint only adds unused-argument on the TYPE_CHECKING __init__ stub, as every sibling argument already has

  • make test passes, and a test case covers the change — new module plus run_unittest_leg.py protocols --exclude protocols/internet (1081 OK), protocols/internet (576 OK), tests/project (397 passed)

  • Changelog entry — N/A — centralised in docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620) #657

  • fix — corrects a defect


Closes #1209

IP read only its own length, so octets past it (Ethernet padding or junk) were dropped. IPv4/IPv6 now keep them as info.trailer, present only when non-empty, and make(trailer=...) writes them outside the declared length. Breaking: for these packets the data shape gains trailer. IP owns the octets because its length field is what excludes them. That makes the bare packet exact too, and Ethernet, C-Tag and Q-in-Q follow unchanged.

case before after
Ethernet/IPv4, 18×00 pad (also junk pad) 60 → 42 60 → 60
C-Tag / Q-in-Q 60 → 46 / 50 60 → 60
bare IPv4, TL 24 + 6 30 → 24 30 → 30
bare IPv6, PL 0 + 2 (and behind Ethernet) 42 → 40 (56 → 54) exact

tests/protocols/link/test_link_trailer_kept_unit.py: 5 passed, 14 subtests; 12 fail without the fix. examples/captures/ has no padded IP frame, so per-frame digests match main. Left for follow-up: IPX behind Ethernet still drops its pad (60 → 44), in internet/ipx.py, which this PR does not own.

…as captured (#1209)

Ethernet and the VLAN tags hand the rest of the frame to IP, which read only
its own length, so the octets after it (most often minimum-frame padding)
were kept by no info and dropped on rebuild: a 60-octet frame rebuilt as 42.

- IPv4 and IPv6 schemas gain a trailing `trailer` payload field, which takes
  what the payload leaves on unpack and writes it verbatim on pack.
- `read()` records it as `info.trailer` when non-empty; `make()` takes a
  `trailer=` keyword written outside the declared length; `_make_data`
  passes it through.

The IP layer owns the octets because its length field is what excludes
them, so the bare IP packet rebuilds exactly too, and every link carrier
(Ethernet, C-Tag, Q-in-Q) follows without change.
@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 8, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

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

  • Re-derived by me with from_data(Proto(raw).info).data: Ethernet + IPv4 (60-octet frame) goes 60 → 42 on main and stays 60 here; bare IPv4 with 6 extra octets goes 34 → 28 on main and stays 34 here.
  • Reviewer: 802.1Q (60 → 46) and QinQ (60 → 50) are now exact. So are bare IPv6 (42 → 40), Ethernet + IPv6 (56 → 54), IPv4 with options plus a trailer, and IPv6 with a hop-by-hop header plus a trailer.
  • No-trailer cases are unchanged, with no trailer key: exact-length, truncated (captured < declared), and Total Length below the header length. The trailer field never reads past the end.
  • Upper layers are unaffected. On main the next layer was already cut to the declared length (the excess was just dropped). TCP payload and reassembly are identical, because reassembly/ip.py reads info.payload.
  • info digests are identical for all 23 examples/captures/ files (1614 frames). json, plist and tree dumps of a padded frame write trailer without error.
  • ! is justified: padded frames' info gains a key, and the make() output changes.
  • make(trailer=) takes bytes. Total Length and Payload Length exclude the trailer. A bytearray is rejected (ProtocolUnbound), which is a nit.
  • Tests: 12 fail with the six source files reverted. Link (75 passed), test_ipv4_unit (26), test_ipv6_unit (10) and 15 reassembly, toolkit and dumpkit modules all pass.

Follow-ups:

  • IPX behind Ethernet still drops its padding. I measured a well-formed 30-octet IPX header in a 60-octet frame rebuilding to 44 on both main and this head; I'll file it separately.
  • An IPv6 jumbogram (Payload Length 0 with a Jumbo option) now round-trips exactly, but its payload is reported as trailer. On main it was lossy (72 → 40) and parsed as NoPayload. I'll file this separately too.

@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 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Coverage: 89.45% (unit tier, Python 3.14, cb3706558, 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 2038 89 634 25 94.76%
pcapkit/dumpkit 234 2 84 2 98.74%
pcapkit/foundation 2637 98 928 48 94.78%
pcapkit/interface 112 7 40 5 92.11%
pcapkit/protocols 16621 221 4478 185 98.04%
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.

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(link): Ethernet and VLAN drop trailing bytes beyond an IPv4 payload on rebuild

1 participant