Repository navigation
PCAP-NG obsolete Packet Block: 32-bit fields vs 32-octet arithmetic #345
Description
What is wrong
In PacketBlock — the obsolete Packet Block, 0x00000002 — the declared field widths and the option-area arithmetic describe two different layouts, so neither a spec-conformant block nor a block matching the declared widths parses.
pcapkit/protocols/schema/misc/pcapng.py:1648:
length: 'int' = UInt32Field(callback=byteorder_callback)
interface_id: 'int' = UInt32Field(callback=byteorder_callback) # line 1657
drop_count: 'int' = UInt32Field(callback=byteorder_callback, default=0xFFFF) # line 1659
timestamp_high: 'int' = UInt32Field(callback=byteorder_callback)
timestamp_low: 'int' = UInt32Field(callback=byteorder_callback)
captured_length: 'int' = UInt32Field(callback=byteorder_callback)
original_length: 'int' = UInt32Field(callback=byteorder_callback)
packet_data: 'bytes' = PayloadField(length=lambda pkt: pkt['captured_length'])
padding_data: 'bytes' = BytesField(length=lambda pkt: (4 - pkt['captured_length'] % 4) % 4)
options: 'list[Option]' = OptionField(
length=lambda pkt: pkt['length'] - 32 - pkt['captured_length'] - len(pkt['padding_data']), # line 1674
...
)Both fields are 16-bit in the format. Appendix A of draft-ietf-opsawg-pcapng (Figure 19) puts them in one 32-bit word:
8 | Interface ID | Drops Count |
with Packet Data starting at octet 28, so the fixed overhead including the trailing Block Total Length is 32 octets — which is precisely the constant line 1674 subtracts. With interface_id and drop_count declared as UInt32Field the overhead is 36, so the two disagree by four octets and every field from timestamp_high onward is read from the wrong offset.
drop_count's own default=0xFFFF is a further sign the 16-bit layout was intended: the spec reserves exactly that value ("The value xFFFF (in hexadecimal) is reserved for those systems in which this information is not available"), which is a 16-bit sentinel, not a 32-bit one.
Symptom
Both layouts fail, and confusingly — the warnings come first and the failure surfaces as an AttributeError from the protocol object rather than as a format error:
[WARNING] packet length < 0: -4
[WARNING] PCAP-NG: [Block 2] block length mismatch: 76 != 0
[WARNING] PCAP-NG: Packet Block has been obsolete! ...
AttributeError: 'PCAPNG' object has no attribute '_info'. Did you mean: 'info'?
(76 != 0 for the spec layout, 80 != 0 for the 32-bit layout; the AttributeError is identical.)
Reproduction
import struct
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)
shb = lambda: block(0x0A0D0D0A, struct.pack('<IHHq', 0x1A2B3C4D, 1, 0, -1))
idb = lambda: block(0x00000001, struct.pack('<HHI', 1, 0, 0x40000))
ETH = bytes.fromhex('ffffffffffff001122334455') + b'\x08\x06' + b'\x00' * 28
# Appendix A layout: 16-bit Interface ID and Drops Count
pb16 = block(0x00000002, struct.pack('<HHIIII', 0, 0, 0, 0, len(ETH), len(ETH)) + pad4(ETH))
# the layout the schema declares: 32-bit Interface ID and Drops Count
pb32 = block(0x00000002, struct.pack('<IIIIII', 0, 0, 0, 0, len(ETH), len(ETH)) + pad4(ETH))
import pcapkit
for name, pb in (('pb16', pb16), ('pb32', pb32)):
open(f'/tmp/{name}.pcapng', 'wb').write(shb() + idb() + pb)
try:
pcapkit.extract(fin=f'/tmp/{name}.pcapng', store=True, nofile=True)
except Exception as exc:
print(name, '->', type(exc).__name__, exc)What a fix would need to touch
interface_id and drop_count at lines 1657 and 1659 become UInt16Field, which makes the existing - 32 at line 1674 correct. The corresponding _read_block_packet / _make_block_packet in pcapkit/protocols/misc/pcapng.py and the Data_PacketBlock model should be checked for the same assumption. The AttributeError: 'PCAPNG' object has no attribute '_info' is worth a look on its own — a malformed block ought to raise a format error rather than leaving a half-constructed protocol object (compare #289, which is the same symptom from a different cause).
The block is obsolete and MUST NOT appear in new files, so this is low-priority — but pcapkit claims to read it, and files containing it exist.
How it was found
While building deterministic sample captures for the test suite. The generators' module docstrings record it: examples/samples/pcapng.py lists the obsolete Packet Block among the block types deliberately left out of the fixtures, because neither layout parses.
Line numbers are from main at 72f950d.
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Activity
JarryShaw commentedon Sep 14, 2026
Fixed in #371, merged as 19d7962. (GitHub only auto-linked the first issue in that PR's
Closeslist, so the rest needed closing by hand.)