Native Go Packet Capture, tcpdump-style cBPF Compilation, CGO-free Cross Builds
δΈζζζ‘£ Β· Documentation Β· Examples
go-pcap is a native Go packet-capture library and tcpdump-style cBPF filter
compiler. It provides a libpcap-like capture surface without CGO, making
CGO_ENABLED=0 builds and cross-compilation straightforward.
The following recording shows the pcap CLI capturing live loopback traffic
through an L3-aware cBPF filter, printing tcpdump-compatible packet summaries:
This project is derived from packetcap/go-pcap and remains licensed under Apache-2.0. The filter compiler has been substantially reworked by the HuaTuo team while retaining the pure-Go design:
- Compile the same filter AST for Ethernet (
EN10MB) or raw IP (RAW, L3) packet layouts. - Use a two-pass, label-based cBPF assembler instead of hand-calculated jump offsets.
- Correct
and/orprecedence, parenthesized negation, composite short-circuiting, and several L2/L3 edge cases. - Support IPv4, IPv6, ARP/RARP, TCP, UDP, ICMP, ICMP6, IGMP, PIM, ESP, AH, VRRP, host, network, port, multicast, and common logical combinations.
- Provide executable Examples, VM behavior tests, tcpdump decision-equivalence checks when tcpdump is installed, and repeatable benchmarks.
The implemented filter language is a useful tcpdump-style subset, not a claim of complete libpcap grammar compatibility.
go get github.com/huatuo-ai/go-pcap@latestUse the package-level compiler when the input packet layout is known. Do not
cast a pcap/link-layer numeric value to filter.LinkType: choose the semantic
layout explicitly.
package main
import (
"errors"
"log"
"github.com/huatuo-ai/go-pcap/filter"
"golang.org/x/net/bpf"
)
func main() {
insns, err := filter.Compile("ip6 and udp and port 53", filter.LinkTypeRaw)
if err != nil {
log.Fatal(err)
}
raw, err := bpf.Assemble(insns)
if err != nil {
log.Fatal(err)
}
_ = raw
_, err = filter.Compile("arp", filter.LinkTypeRaw)
if errors.Is(err, filter.ErrL2OnlyLinkType) {
// Choose Ethernet framing, or reject the L2-only expression.
}
}filter.Size(expr, linkType) returns the number of cBPF instructions that
filter.Compile would emit.
| Layout | Packet starts at | Supported predicates |
|---|---|---|
filter.LinkTypeEthernet |
Ethernet header | L2 and L3 predicates |
filter.LinkTypeRaw |
IPv4/IPv6 header | L3 predicates only |
On RAW, expressions that are entirely L2-only, such as arp, rarp, or
ether host aa:bb:cc:dd:ee:ff, return ErrL2OnlyLinkType rather than silently
producing a filter with the wrong meaning. Empty expressions return
ErrEmptyFilter; unsupported layouts return ErrUnsupportedLinkType.
Common supported expressions include:
tcp and port 443
src portrange 1000-2000
ip6 and udp and port 53
src and dst host 192.0.2.1
ip multicast
tcp[tcpflags] == tcp-syn
tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)
vlan 100 and tcp port 443
mpls and ip
tcp and port 80 or udp
not (tcp or udp)
and binds tighter than or; use parentheses when explicit grouping makes a
rule easier to read. Packet access supports 1-, 2-, and 4-byte network-order
loads with explicit bounds checks. TCP flag names follow tcpdump/libpcap, so
the SYN+ACK mask should be written as tcp-syn|tcp-ack rather than
syn|ack. Protocol-number literals, protochain, netmask-dependent broadcast
predicates, packet-context metadata, non-EN10MB/RAW layouts, and the full
tcpdump grammar are not currently implemented.
Run the full test suite with:
make testIt runs unit tests, Go Examples, cBPF VM behavior tests, RAW/L2 boundary tests,
and a tcpdump/libpcap decision-equivalence suite when tcpdump is available.
The equivalence suite compares packet accept/reject decisions, not instruction
bytes, because libpcap optimization output can vary by version.
Run repeatable filter benchmarks with:
make benchThis executes parser, compiler, size, and VM match/miss benchmarks ten times
with -benchmem, reporting ns/op, B/op, and allocs/op. Compare benchmark
results only on equivalent machines and Go versions.
Build the sample capture utility:
make build
./pcap --helpThe CLI prints tcpdump-style packet summaries for common Ethernet, ARP, IPv4,
IPv6, TCP, UDP, and ICMP traffic. Its common display switches are compatible
with tcpdump 4.99.x: -i, -c, -n/-nn, -q, -v, -e,
-X, -A, -s, and -p. Use -nn when scripting so host and service names
remain numeric and output is deterministic.
./pcap -nn -i eth0 -c 10 'tcp port 443'
./pcap -nn -i eth0 -c 10 'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)'This is a live-capture CLI; pcap file read/write modes and the less common tcpdump switches are not implemented.
Cross-compilation is supported through the OS and ARCH Makefile variables.
By default, the host executable is available as ./pcap in the project
root. Target-specific artifacts are also written to the project root; set
BINDIR to place them elsewhere. For 32-bit Linux ARM, select the ABI level
explicitly:
make build OS=linux ARCH=arm GOARM=6 # pcap-linux-armv6
make build OS=linux ARCH=arm GOARM=7 # pcap-linux-armv7An ARMv7 binary must not be deployed to an ARMv6 device. The release matrix does not publish a soft-float ARM artifact.
Capture support is available for Linux and macOS/Darwin. Packet capture usually
requires the appropriate operating-system privileges. On Linux the default
capture path uses an AF_PACKET TPACKET_V3 mmap ring, so it also needs kernel
support and CAP_NET_RAW. On older kernels or restricted containers,
./pcap --syscalls bypasses the mmap/TPACKET_V3 path; it does not remove the
capture-permission requirement. RAW is a compiler layout for packets
beginning at an IP header; it is not a substitute for an Ethernet capture
handle and cannot evaluate L2-only predicates.
Issues and pull requests are welcome, especially for new protocol support,
link types, compatibility cases, and performance work. Please run make test
and the relevant make bench cases before submitting a change.
See CONTRIBUTING.md for local checks and pull-request expectations. The documentation includes deeper guides for the architecture, compiler internals, new filter primitives, and testing.
The original capture library is derived from packetcap/go-pcap. The L3-aware filter compiler, label assembler, and reliability work were developed by the HuaTuo team. See LICENSE for the Apache-2.0 license text.

