You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(protocols): a cut standalone IPv6 extension header rebuilds with values its parse did not read #2761
Describe the bug
A standalone IPv6 extension header cut short parses with fewer octets in some fields than its rebuild holds. The rebuilt octets are exact, but from_data(info).info != info. The parse reads only the captured octets; the rebuild reads at the declared width (_uncaptured, #2703). Framed in IPv6, nothing differs.
Also: IPv6-Frag reserved_octet at cut 1 (b'' vs b'\x00'), HOPOPT/IPv6-Opts options gain a PadN the parse lacks (cuts 1, 2, 4–6), IPv6_Extlength at cut 1 (1 vs 2).
Expected behavior
The rebuild's info equals the parse's. test-coverage.rst (Round-Trip Invariants) requires a cycle to reproduce its input exactly; here the octets match, but the values do not.
Additional context
Likely site: Protocol.__post_init__ / replay_short_read: make a standalone short-read parse and its rebuild read the header the same way.
Decision needed: does a round trip whose octets match but whose parsed values differ count as a defect? The Round-Trip Invariants section speaks of octets, so it doesn't settle this case.
Defect, fix it. Make a standalone short-read parse and its rebuild read the header the same way, so from_data(info).info == info, and write the rule onto test-coverage.rst.
Accept it. Octets are the invariant. Document that a standalone cut header's rebuild may hold zero-filled values the parse lacked.
Recommendation: option 1. A rebuild that invents a PadN option, or zero-fills reserved, reports fields the capture never held. That is the same class of defect #2756 removed from lists.
This touches pcapkit/protocols/protocol.py, and #2490's member reorder is running across the protocol modules now. So a worker starts on option 1 once that reorder's PR lands, unless you rule option 2 first.
Option 1, reconciled: a round trip has to reproduce the parsed values as well as the octets.
A standalone cut header and its rebuild will read the header the same way. So from_data(info).info == info for IPv6-Route reserved, IPv6-Frag reserved_octet, HOPOPT/IPv6-Opts options (no invented PadN) and IPv6_Extlength.
test-coverage.rst (Round-Trip Invariants) gets that rule written into it.
#2767 fixes every field this issue lists except one; that one needs a decision.
The remaining gap:IPv6_Extlength at cut 1. The parse reads the header at 1 octet, while the rebuild reads it at the 2 octets that make() packs, and the data model does not record which width the parse was given. No shared rule fits all the cases. A cut Ethernet header differs only in its next layer. IPv6_Ext differs only in length. An IGMPv3 Report cut inside IPv4 needs IPv4's declared width.
(a) Record the parse's width next to __short_read__ and rebuild at that width. This adds one new Data metadata key, and that key could replace _uncaptured and _short_of_header.
Option (a), reconciled: a cut header's data model records the width its parse read the header at, alongside __short_read__, and from_data rebuilds at that width. This closes the IPv6_Extlength gap at cut 1 without a per-protocol clamp.
The new key is data-model metadata, kept out of to_dict() output in the same way as __short_read__. It must not change any other cut, framed or standalone.
Activity
JarryShaw commentedon Nov 4, 2026
Decision needed: does a round trip whose octets match but whose parsed values differ count as a defect? The Round-Trip Invariants section speaks of octets, so it doesn't settle this case.
from_data(info).info == info, and write the rule ontotest-coverage.rst.Recommendation: option 1. A rebuild that invents a PadN option, or zero-fills
reserved, reports fields the capture never held. That is the same class of defect #2756 removed from lists.This touches
pcapkit/protocols/protocol.py, and #2490's member reorder is running across the protocol modules now. So a worker starts on option 1 once that reorder's PR lands, unless you rule option 2 first.JarryShaw commentedon Nov 4, 2026
take 1
JarryShaw commentedon Nov 4, 2026
Option 1, reconciled: a round trip has to reproduce the parsed values as well as the octets.
from_data(info).info == infofor IPv6-Routereserved, IPv6-Fragreserved_octet, HOPOPT/IPv6-Optsoptions(no invented PadN) andIPv6_Extlength.test-coverage.rst(Round-Trip Invariants) gets that rule written into it.Protocol.__post_init__/replay_short_read). That is allowed alongside Reorder protocol class members to the member-order convention #2490's reorder, so a worker starts now rather than after Reorder protocol class members to the member-order convention #2490 lands.JarryShaw commentedon Nov 4, 2026
#2767 fixes every field this issue lists except one; that one needs a decision.
The remaining gap:
IPv6_Extlengthat cut 1. The parse reads the header at 1 octet, while the rebuild reads it at the 2 octets thatmake()packs, and the data model does not record which width the parse was given. No shared rule fits all the cases. A cut Ethernet header differs only in its next layer.IPv6_Extdiffers only inlength. An IGMPv3 Report cut inside IPv4 needs IPv4's declared width.__short_read__and rebuild at that width. This adds one newDatametadata key, and that key could replace_uncapturedand_short_of_header.lengthinsideIPv6_Ext.read. That is a one-line, per-protocol change, close to the length clamp ruled out under fix(transport): the SCTP schema on its own repacks a final chunk cut inside its padding at full width #2680.Recommendation: (a). It is the only shared fix. #2767 does not close this issue; the gap stays open here.
fix(protocols): rebuild a cut standalone header with the values its p…
JarryShaw commentedon Nov 4, 2026
take (a)
JarryShaw commentedon Nov 4, 2026
Option (a), reconciled: a cut header's data model records the width its parse read the header at, alongside
__short_read__, andfrom_datarebuilds at that width. This closes theIPv6_Extlengthgap at cut 1 without a per-protocol clamp.to_dict()output in the same way as__short_read__. It must not change any other cut, framed or standalone._uncaptured(fix(protocols): a layer rebuilt by from_data from a cut capture verifies before its short read is replayed #2703) and_short_of_header(fix(protocols): a cut header parses with a Raw next layer but rebuilds with NoPayload #2717) is checked in the same change. The swap is made only if every test of those issues stays green. Otherwise both stay, and the PR says why.test-coverage.rstthen holds without exception.A worker is starting now; the PR will close this issue.
docs(changelog): correct the #2770 and #2767 entries (#2449)
fix(protocols): rebuild a cut header at the width its parse read it (#…