Coders for the classic libpcap (.pcap) capture file format.
Round-trip a complete little-endian capture file
Round-trip a complete little-endian capture file
import { assertEquals } from "@std/assert"; import { pcapFile, PCAP_MAGIC_MICROS, LINKTYPE, } from "@binstruct/pcap"; const coder = pcapFile("le"); const value = { header: { magic: PCAP_MAGIC_MICROS, versionMajor: 2, versionMinor: 4, thisZone: 0, sigFigs: 0, snapLen: 65535, network: LINKTYPE.ETHERNET, }, records: [ { tsSec: 1_700_000_000, tsUsec: 250_000, inclLen: 4, origLen: 4, data: new Uint8Array([0xde, 0xad, 0xbe, 0xef]), }, { tsSec: 1_700_000_001, tsUsec: 0, inclLen: 2, origLen: 1500, data: new Uint8Array([0x12, 0x34]), }, ], }; const buffer = new Uint8Array(128); const written = coder.encode(value, buffer); const [decoded] = coder.decode(buffer.subarray(0, written)); assertEquals(written, 24 + 16 + 4 + 16 + 2); assertEquals(decoded.records.length, 2); assertEquals(decoded.records[0].data, value.records[0].data); assertEquals(decoded.records[1].origLen, 1500);
Detect endianness, then decode
Detect endianness, then decode
import { assertEquals } from "@std/assert"; import { detectPcapMagic, pcapFile, PCAP_MAGIC_MICROS, LINKTYPE, } from "@binstruct/pcap"; const original = pcapFile("be"); const buffer = new Uint8Array(64); original.encode({ header: { magic: PCAP_MAGIC_MICROS, versionMajor: 2, versionMinor: 4, thisZone: 0, sigFigs: 0, snapLen: 1500, network: LINKTYPE.RAW, }, records: [], }, buffer); const info = detectPcapMagic(buffer); assertEquals(info, { endianness: "be", nanos: false }); const reader = pcapFile(info!.endianness); const [decoded] = reader.decode(buffer); assertEquals(decoded.header.network, LINKTYPE.RAW);
Composition with the rest of @binstruct/*
Pcap stores raw link-layer payloads. The natural use is to read a capture,
then hand each record.data to a sibling coder for the link type advertised
in the global header.
Walk an inet stack: pcap → IPv4 → UDP (LINKTYPE.RAW)
Walk an inet stack: pcap → IPv4 → UDP (LINKTYPE.RAW)
import { assertEquals } from "@std/assert"; import { LINKTYPE, PCAP_MAGIC_MICROS, pcapFile } from "@binstruct/pcap"; import { ipv4Packet } from "@binstruct/ipv4"; import { udpPacket } from "@binstruct/udp"; import { parseAddressv4 } from "@hertzg/ip/addressv4"; const ip = ipv4Packet(); const udp = udpPacket(); // Synth a UDP-over-IPv4 packet to put in the capture. const udpBytes = new Uint8Array(12); udp.encode({ srcPort: 53, dstPort: 49152, length: 12, checksum: 0, payload: new Uint8Array([0xde, 0xad, 0xbe, 0xef]), }, udpBytes); const packet = new Uint8Array(32); ip.encode({ versionIhl: { version: 4, ihl: 5 }, typeOfService: 0, totalLength: 32, identification: 0, flagsFragmentOffset: { reserved: 0, dontFragment: 0, moreFragments: 0, fragmentOffset: 0, }, timeToLive: 64, protocol: 17, headerChecksum: 0, sourceAddress: parseAddressv4("192.0.2.1").address, destinationAddress: parseAddressv4("192.0.2.2").address, options: new Uint8Array(0), payload: udpBytes, }, packet); const cap = pcapFile("le"); const buf = new Uint8Array(24 + 16 + packet.length); const written = cap.encode({ header: { magic: PCAP_MAGIC_MICROS, versionMajor: 2, versionMinor: 4, thisZone: 0, sigFigs: 0, snapLen: 65535, network: LINKTYPE.RAW, }, records: [{ tsSec: 0, tsUsec: 0, inclLen: packet.length, origLen: packet.length, data: packet, }], }, buf); // Walk the stack on read. const [{ records }] = cap.decode(buf.subarray(0, written)); const [parsedIp] = ip.decode(records[0].data); const [parsedUdp] = udp.decode(parsedIp.payload); assertEquals(parsedIp.sourceAddress, parseAddressv4("192.0.2.1").address); assertEquals(parsedUdp.srcPort, 53); assertEquals(parsedUdp.payload, new Uint8Array([0xde, 0xad, 0xbe, 0xef]));
Walk the full inet stack (LINKTYPE.ETHERNET via @binstruct/inet)
Walk the full inet stack (LINKTYPE.ETHERNET via @binstruct/inet)
import { assert, assertEquals } from "@std/assert"; import { LINKTYPE, PCAP_MAGIC_MICROS, pcapFile } from "@binstruct/pcap"; import { inetFrame } from "@binstruct/inet"; import { ETHERTYPE_IPV4 } from "@binstruct/ipv4"; import { IP_PROTOCOL_UDP } from "@binstruct/udp"; import { parseAddressv4 } from "@hertzg/ip/addressv4"; const inet = inetFrame(); // Build an Ethernet → IPv4 → UDP frame in one pass. const frame = new Uint8Array(14 + 32); inet.encode({ dstMac: new Uint8Array([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]), srcMac: new Uint8Array([0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb]), etherType: ETHERTYPE_IPV4, payload: { versionIhl: { version: 4, ihl: 5 }, typeOfService: 0, totalLength: 32, identification: 0, flagsFragmentOffset: { reserved: 0, dontFragment: 0, moreFragments: 0, fragmentOffset: 0 }, timeToLive: 64, protocol: IP_PROTOCOL_UDP, headerChecksum: 0, sourceAddress: parseAddressv4("192.0.2.1").address, destinationAddress: parseAddressv4("192.0.2.2").address, options: new Uint8Array(0), payload: { srcPort: 53, dstPort: 49152, length: 12, checksum: 0, payload: new Uint8Array([0xde, 0xad, 0xbe, 0xef]), }, }, }, frame); const cap = pcapFile("le"); const buf = new Uint8Array(24 + 16 + frame.length); const written = cap.encode({ header: { magic: PCAP_MAGIC_MICROS, versionMajor: 2, versionMinor: 4, thisZone: 0, sigFigs: 0, snapLen: 65535, network: LINKTYPE.ETHERNET, }, records: [{ tsSec: 0, tsUsec: 0, inclLen: frame.length, origLen: frame.length, data: frame, }], }, buf); // Read back and walk the stack in one shot. const [{ records }] = cap.decode(buf.subarray(0, written)); const [decoded] = inet.decode(records[0].data); assert(!(decoded.payload instanceof Uint8Array)); assert("protocol" in decoded.payload); assertEquals(decoded.payload.sourceAddress, parseAddressv4("192.0.2.1").address); assert(!(decoded.payload.payload instanceof Uint8Array)); assert("srcPort" in decoded.payload.payload); assertEquals(decoded.payload.payload.srcPort, 53); assertEquals(decoded.payload.payload.payload, new Uint8Array([0xde, 0xad, 0xbe, 0xef]));
Inspects the first four bytes of a buffer to identify the pcap magic.
Creates a coder for a complete pcap capture file.
Creates a coder for a complete pcap capture file fixed to big-endian byte
order. Exactly pcapFile("be"), spelled so it can be called with no
arguments.
Creates a coder for a complete pcap capture file fixed to little-endian byte
order. Exactly pcapFile("le"), spelled so it can be called with no
arguments.
Creates a coder for a complete pcap capture file using the supplied header and record coders.
Creates a coder for the pcap global header in the requested byte order.
Creates a coder for a single pcap record (16-byte header plus payload).
Decoded representation of a complete pcap capture file.
-
header: THeader
Global header parsed from the file's first 24 bytes.
-
records: TRecord[]
All records present in the remainder of the buffer.
Decoded representation of the 24-byte pcap global header.
-
magic: number
Magic number identifying byte order and timestamp resolution.
-
network: number
Link-layer header type identifier (see LINKTYPE).
-
sigFigs: number
Timestamp accuracy; conventionally 0.
-
snapLen: number
Maximum captured length per packet, in bytes.
-
thisZone: number
GMT-to-local-time correction in seconds; almost always 0 in modern files.
-
versionMajor: number
Major version of the file format (currently 2).
-
versionMinor: number
Minor version of the file format (currently 4).
Result of probing a buffer's first four bytes for a pcap magic number.
-
endianness: PcapEndianness
Byte order implied by the magic.
-
nanos: boolean
Whether the file uses nanosecond-resolution timestamps.
Decoded representation of a single pcap record.
-
data: Uint8Array
Captured packet payload, exactly
inclLenbytes long. -
inclLen: number
Number of bytes of packet data actually present in
data. -
origLen: number
Original packet length on the wire (may exceed
inclLen). -
tsSec: number
Timestamp seconds since the Unix epoch.
-
tsUsec: number
Sub-second portion of the timestamp.
Numeric link-layer header type, as stored in the pcap global header.
Byte order to use for all multi-byte integers in the pcap stream.
Common link-layer header type values used in the pcap global header.
-
ARCNET_BSD: number
ARCNET, with BSD-style header.
-
AX25: number
AX.25 packet, with no link-layer pseudo-header.
-
BLUETOOTH_HCI_H4: number
Bluetooth HCI UART transport layer.
-
ETHERNET: number
IEEE 802.3 Ethernet.
-
FDDI: number
FDDI.
-
IEEE802_11: number
IEEE 802.11 wireless LAN.
-
IEEE802_11_RADIOTAP: number
IEEE 802.11 plus radiotap radio header.
-
IEEE802_5: number
IEEE 802.5 Token Ring.
-
LINUX_SLL: number
Linux "cooked" capture encapsulation (SLL).
-
LINUX_SLL2: number
Linux "cooked" capture encapsulation v2.
-
NULL: number
No link-layer header (BSD loopback).
-
PPP: number
PPP, as per RFC 1661 and RFC 1662.
-
PPP_HDLC: number
Apple PPP-over-HDLC.
-
RAW: number
Raw IP packet (IPv4 or IPv6) with no link layer.
-
SLIP: number
SLIP, with no direction indication.
-
USB_LINUX: number
USB packets, beginning with a Linux USB header.
Byte order assumed by the pcap coder factories when none is supplied.
Logical magic number for microsecond-resolution captures.
Logical magic number for nanosecond-resolution captures.