What is wrong
pcapkit/protocols/schema/misc/pcapng.py:1102 sizes the name field of a Name Resolution Block IPv6 record as if the address were four octets wide — the expression is copied verbatim from IPv4Record twelve lines above:
@schema_final
class IPv6Record(NameResolutionRecord, code=Enum_RecordType.nrb_record_ipv6):
"""Header schema for PCAP-NG NRB ``nrb_record_ipv4`` records."""
#: IPv4 address.
ip: 'IPv6Address' = IPv6AddressField() # 16 octets
#: Name resolution data.
resol: 'str' = StringField(length=lambda pkt: pkt['length'] - 4) # <-- should be 16
pkt['length'] is the record value length, and the record value is address followed by names, so with a 16-octet address the name field is length - 16. § 4.5 of draft-ietf-opsawg-pcapng is explicit:
nrb_record_ipv6: The nrb_record_ipv6 record specifies an IPv6 address (contained in the first 16 octets), followed by one or more zero-terminated strings containing the DNS entries for that address. The minimum valid Record Length for this Record Type is thus 18: 16 for the IP octets, 1 character, and a zero-value octet terminator.
As written the name field over-reads by 12 octets, which swallows the record terminator (nrb_record_end) and runs into whatever follows — the block's options, or the next block. Note the copy-paste also left the class docstring and the ip comment saying "ipv4".
Symptom
An NRB containing an IPv6 record warns, and the record's names and the block's ns_* options come out wrong or missing. On the minimal fixture below:
[WARNING] packet length < 0: -6
The generators' docstring records packet length < 0: -29273 on a larger fixture, and the block's ns_* options vanishing. The magnitude just scales with what follows the record.
Reproduction
import struct, ipaddress
def pad4(b): return b + b'\x00' * ((4 - len(b) % 4) % 4)
def block(t, body):
body = pad4(body); n = 12 + len(body)
return struct.pack('<II', t, n) + body + struct.pack('<I', n)
def record(t, v): return struct.pack('<HH', t, len(v)) + pad4(v)
shb = lambda: block(0x0A0D0D0A, struct.pack('<IHHq', 0x1A2B3C4D, 1, 0, -1))
idb = lambda: block(0x00000001, struct.pack('<HHI', 1, 0, 0x40000))
epb = lambda d: block(0x00000006, struct.pack('<IIIII', 0, 0, 0, len(d), len(d)) + pad4(d))
ETH = bytes.fromhex('ffffffffffff001122334455') + b'\x08\x06' + b'\x00' * 28
v6 = record(2, ipaddress.IPv6Address('2001:db8::1').packed + b'host6.example\x00')
v4 = record(1, ipaddress.IPv4Address('192.0.2.1').packed + b'host4.example\x00')
end = record(0, b'')
import pcapkit
# the IPv4 record parses cleanly ...
open('/tmp/nrb4.pcapng', 'wb').write(shb() + idb() + block(0x00000004, v4 + end) + epb(ETH))
pcapkit.extract(fin='/tmp/nrb4.pcapng', store=True, nofile=True)
# ... the IPv6 record does not
open('/tmp/nrb6.pcapng', 'wb').write(shb() + idb() + block(0x00000004, v6 + end) + epb(ETH))
pcapkit.extract(fin='/tmp/nrb6.pcapng', store=True, nofile=True)
What a fix would need to touch
Changing the constant from 4 to 16 at line 1102 is sufficient: patching that callback at runtime and re-parsing the fixture above clears the warning, with nothing else changed. The stale docstring and #: IPv4 address. comment on the same class are worth correcting while there.
How it was found
While building deterministic sample captures for the test suite; the generators' module docstrings record it. examples/samples/pcapng.py exercises the NRB on purpose, because it warns rather than raising and a fixture covering the code path is what will catch a future crash there.
Line numbers are from main at 72f950d.
What is wrong
pcapkit/protocols/schema/misc/pcapng.py:1102sizes the name field of a Name Resolution Block IPv6 record as if the address were four octets wide — the expression is copied verbatim fromIPv4Recordtwelve lines above:pkt['length']is the record value length, and the record value is address followed by names, so with a 16-octet address the name field islength - 16. § 4.5 of draft-ietf-opsawg-pcapng is explicit:As written the name field over-reads by 12 octets, which swallows the record terminator (
nrb_record_end) and runs into whatever follows — the block's options, or the next block. Note the copy-paste also left the class docstring and theipcomment saying "ipv4".Symptom
An NRB containing an IPv6 record warns, and the record's names and the block's
ns_*options come out wrong or missing. On the minimal fixture below:The generators' docstring records
packet length < 0: -29273on a larger fixture, and the block'sns_*options vanishing. The magnitude just scales with what follows the record.Reproduction
What a fix would need to touch
Changing the constant from 4 to 16 at line 1102 is sufficient: patching that callback at runtime and re-parsing the fixture above clears the warning, with nothing else changed. The stale docstring and
#: IPv4 address.comment on the same class are worth correcting while there.How it was found
While building deterministic sample captures for the test suite; the generators' module docstrings record it.
examples/samples/pcapng.pyexercises the NRB on purpose, because it warns rather than raising and a fixture covering the code path is what will catch a future crash there.Line numbers are from
mainat 72f950d.