# pcap-frame-parser
A small, dependency-light Rust parser for network capture files and the
frames inside them — no `libpcap`/`npcap` linkage, no `unsafe`.
It handles two things:
1. **Capture containers** — legacy PCAP and PCAPng, extracting the raw
captured frames and their timestamps.
2. **Frame dissection** — Ethernet II, an optional single 802.1Q VLAN
tag, optional double-tagged 802.1ad "Q-in-Q" framing, IPv4, and
UDP/TCP, down to the transport-layer payload.
It stops at the transport payload on purpose: this crate doesn't know or
care whether that payload is DNS, DHCP, OSPF, or something else — that's
for a protocol-specific parser built on top.
Extracted from a series of protocol-forensics tools
([`dns-postmortem`](https://github.com/casablanque-code/dns-postmortem),
[`ospf-postmortem`](https://github.com/casablanque-code/ospf-postmortem),
[`dhcp-postmortem`](https://github.com/casablanque-code/dhcp-postmortem),
[`stp-postmortem`](https://github.com/casablanque-code/stp-postmortem))
where this exact parsing logic had been duplicated four times.
## Scope
| Legacy PCAP | Live capture |
| PCAPng (SHB/IDB/EPB/OPB/SPB blocks) | Writing capture files |
| Ethernet II | IPv6 |
| Untagged / 802.1Q / 802.1ad Q-in-Q | Other link-layer types (only `LINKTYPE_ETHERNET` is handled) |
| IPv4, UDP, TCP | IP fragmentation reassembly |
If you need any of the "not supported" column, this crate isn't the
right layer for it — pair it with something like `pnet` or `etherparse`
instead, or reassemble fragments before handing frames to this crate.
## Example
```rust
use pcap_frame_parser::{pcap, ethernet};
let capture_bytes: &[u8] = /* contents of a .pcap file */
# &[0xd4,0xc3,0xb2,0xa1,2,0,4,0,0,0,0,0,0,0,0,0,0xff,0xff,0,0,1,0,0,0];
let (_header, packets) = pcap::iter_packets(capture_bytes)?;
for packet in &packets {
let Some((ip_header, ip_payload)) = ethernet::extract_ip(packet.data) else { continue };
if ip_header.protocol == ethernet::PROTO_UDP {
if let Some((src_port, dst_port, payload)) = ethernet::extract_udp(ip_payload) {
println!("{} -> {}: {} bytes", src_port, dst_port, payload.len());
}
}
}
# Ok::<(), String>(())
```
For PCAPng files, use `pcapng::parse_pcapng(bytes)` instead, which
returns `Vec<(ts_sec, ts_usec, frame_bytes)>`.
## Why not `pcap-parser` / `pnet` / etc.?
Those are fine, more complete crates if you need broader protocol
coverage or live capture. This crate is intentionally narrow: it's the
exact slice of parsing — PCAP/PCAPng container plus
Ethernet/VLAN/IP/UDP/TCP dissection — that a forensics or analysis tool
typically needs before handing off to its own protocol-specific logic,
with no unnecessary dependencies pulled in.
## License
MIT