Skip to content

PCAP-NG NRB nrb_record_ipv6 sized as if the address were 4 octets #343

Description

@JarryShaw

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssues reporting a defect (set by the bug report template; a default, not an assessment)

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions