SMFIdentificationBasedDPDOption in pcapkit/protocols/schema/internet/ipv6_opts.py declares a test ForwardMatchField that its HOPOPT twin does not. The two modules are otherwise line-for-line equivalents, and this one extra field makes the same octets parse under HOPOPT and fail under IPv6-Opts.
The difference
Enumerating the class body from origin/main (c28ffc287):
hopopt.py SMFIdentificationBasedDPDOption fields = ['info', 'tid', 'id']
ipv6_opts.py SMFIdentificationBasedDPDOption fields = ['test', 'info', 'tid', 'id']
^^^^^^ line 434
# pcapkit/protocols/schema/internet/ipv6_opts.py:434
test: 'SMFDPDTestFlag' = ForwardMatchField(BitField(length=1, namespace={
'mode': (0, 1),
}))
Normalising both modules for their protocol names leaves ten differing lines: a module docstring, a five-line explanatory comment, and these three.
Why it is a stray rather than intentional
- The enclosing
_SMFDPDOption already declares a test ForwardMatchField (length=3), in both modules.
- Nothing reads the nested one.
smf_dpd_data_selector reads pkt['test']['mode'] and pkt['test']['len'], and that pkt is the enclosing schema's.
- It is the only field in the class carrying no
#: comment, unlike info, tid and id around it.
It predates #432, which neither added nor removed it.
Effect
ForwardMatchField consumes nothing from the stream but still occupies a slot in __buffer__, so it counts toward len(schema). The nested schema therefore over-reports its length by one octet, and OptionField accumulates that over-report against the declared option area until the area check fails.
Same wire layout, same octets, different outcomes — built through HOPOPT as 1100080100010100, option area 080100010100:
len(schema)
hopopt 3 <- correct
ipv6_opts 4 <- one too many
HOPOPT(built, 8, extension=True) -> options=[SMF_DPD, PadN]
IPv6_Opts(built, 8, extension=True) -> ProtocolError: IPv6-Opts: invalid format
The error is raised at pcapkit/protocols/internet/ipv6_opts.py:497, the option-area threshold check in _read_ipv6_opts, reached from read at :315 via options=self._read_ipv6_opts(schema.len * 8 + 6) and unpack → __post_init__ at protocols/protocol.py:678.
Byte-for-byte identical on Python 3.10.20 and 3.14.7, so unlike the PCAP-NG failures in #439 this is not interpreter-dependent.
Provenance
Surfaced by the option round-trip harness in #440, which records ipv6-opts-option/SMF_DPD as a PARSE gap. Two corrections happened on the way to it and are worth recording, since both were wrong in ways that looked right:
Not fixed in #440 deliberately: that PR keeps git diff origin/main -- pcapkit/ empty, which is what makes a 258-case harness reviewable.
SMFIdentificationBasedDPDOptioninpcapkit/protocols/schema/internet/ipv6_opts.pydeclares atestForwardMatchFieldthat its HOPOPT twin does not. The two modules are otherwise line-for-line equivalents, and this one extra field makes the same octets parse under HOPOPT and fail under IPv6-Opts.The difference
Enumerating the class body from
origin/main(c28ffc287):Normalising both modules for their protocol names leaves ten differing lines: a module docstring, a five-line explanatory comment, and these three.
Why it is a stray rather than intentional
_SMFDPDOptionalready declares atestForwardMatchField(length=3), in both modules.smf_dpd_data_selectorreadspkt['test']['mode']andpkt['test']['len'], and thatpktis the enclosing schema's.#:comment, unlikeinfo,tidandidaround it.It predates #432, which neither added nor removed it.
Effect
ForwardMatchFieldconsumes nothing from the stream but still occupies a slot in__buffer__, so it counts towardlen(schema). The nested schema therefore over-reports its length by one octet, andOptionFieldaccumulates that over-report against the declared option area until the area check fails.Same wire layout, same octets, different outcomes — built through HOPOPT as
1100080100010100, option area080100010100:The error is raised at
pcapkit/protocols/internet/ipv6_opts.py:497, the option-area threshold check in_read_ipv6_opts, reached fromreadat :315 viaoptions=self._read_ipv6_opts(schema.len * 8 + 6)andunpack→__post_init__atprotocols/protocol.py:678.Byte-for-byte identical on Python 3.10.20 and 3.14.7, so unlike the PCAP-NG failures in #439 this is not interpreter-dependent.
Provenance
Surfaced by the option round-trip harness in #440, which records
ipv6-opts-option/SMF_DPDas aPARSEgap. Two corrections happened on the way to it and are worth recording, since both were wrong in ways that looked right:84233da3a) touched both schema modules symmetrically, 26 changed lines each, and both now carry'len': (8, 8)with zero(1, 8)remaining.'len': (1, 8)at line 374. Also false — I was reading a working tree two commits behindorigin/main. Onorigin/mainline 374 is unrelated and the namespace reads(8, 8)in both.Not fixed in #440 deliberately: that PR keeps
git diff origin/main -- pcapkit/empty, which is what makes a 258-case harness reviewable.