go-pcap
This is a native go packet processing library. It performs a function very similar to libpcap, or, for that matter, github.com/google/gopacket/pcap, except that it is 100% native go. This means that:
- you can build it with
CGO_ENABLED=0
- cross-compiling is simple
It also include a simple binary called pcap
that exercises the library by capturing packets and outputting some of the data.
Operating Systems
The current support is for Linux and macOS/Darwin. Eventually, we will port to other OSes (and happily will take Pull Requests).
How To Use It
Library
The library has single primary entrypoint, pcap.OpenLive. This will return a pcap.Handle that can be used to loop and read packets.
if handle, err = pcap.OpenLive(iface, 1600, true, 0); err != nil {
log.Fatal(err)
}
for {
data, captureInfo, error := handle.ReadPacketData()
}
ReadPacketData()
will block until there is packet information available, or until timeout
is reached. You can set an infinite timeout with 0
.
The returned information will be the packet bytes themselves, excluding the system-defined headers, i.e. the Ethernet frame and all contents.
Handle
is 100% compatible with gopacket.Handle; you can use it to process packets, analyze layers,
and anything else you would want.
import (
pcap "github.com/packetcap/go-pcap"
"github.com/google/gopacket"
)
if handle, err = pcap.OpenLive(iface, 1600, true, 0); err != nil {
log.Fatal(err)
}
packetSource := gopacket.NewPacketSource(handle, layers.LinkTypeEthernet)
for packet := range packetSource.Packets() {
processPacket(packet)
}
If you know you don't want all of that overhead, you can use the Listen interface, which returns a chan
to which it will send packets.
if handle, err = pcap.OpenLive(iface, 1600, true, 0); err != nil {
log.Fatal(err)
}
for packet := range handle.Listen() {
processPacket(packet.B)
}
pcap.Listen
will start a separate goroutine, so you do not have to. pcap.Listen
is a one-shot, "open a socket, listen for packets, send
them down my channel" convenience.
Filters
The library (and CLI below) support using libpcap-style filters. You simply need to set the filter
on the handle returned by pcap.OpenLive()
if handle, err = pcap.OpenLive(iface, 1600, true, 0); err != nil {
log.Fatal(err)
}
err = handle.SetBPFFilter(filter)
if err != nil {
// handle error
}
packetSource := gopacket.NewPacketSource(handle, layers.LinkTypeEthernet)
for packet := range packetSource.Packets() {
processPacket(packet)
}
If you are using the simple channel method pcap.Listen()
, you also can filter it:
if handle, err = pcap.OpenLive(iface, 1600, true, 0); err != nil {
log.Fatal(err)
}
err = handle.SetBPFFilter(filter)
if err != nil {
// handle error
}
for packet := range pcap.Listen() {
processPacket(packet.B)
}
The filter
is a string that matches the tcpdump syntax from libcap.
Efficiency
The Linux implementation supports both syscall-based packet reads and mmap-based packet reads. The syscall read is fine for just a few packets, or a lightly loaded system. However, making a syscall to retrieve each packet can get very slow, very quickly. For faster purposes, you can use a shared mmap buffer with the kernel.
The OpenLive()
call uses mmap by default.
CLI
There is a sample command-line utility included. To build it:
$ make build
Or for an alternate OS:
$ make build OS=linux
The binaries will be output as dist/pcap-<os>-<arch>
. Additionally, if you are building for your local OS+arch, a binary named pcap
will be
deposited in the current directory, so you can just do ./pcap
.
For options, run ./pcap --help
. It also supports using filters.