aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/gopacket/pcap
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/google/gopacket/pcap')
-rw-r--r--vendor/github.com/google/gopacket/pcap/doc.go106
-rw-r--r--vendor/github.com/google/gopacket/pcap/gopacket_benchmark/benchmark.go247
-rw-r--r--vendor/github.com/google/gopacket/pcap/pcap.go1005
-rw-r--r--vendor/github.com/google/gopacket/pcap/pcap_test.go308
-rw-r--r--vendor/github.com/google/gopacket/pcap/pcap_tester.go109
-rw-r--r--vendor/github.com/google/gopacket/pcap/pcap_unix.go71
-rw-r--r--vendor/github.com/google/gopacket/pcap/pcap_windows.go23
-rw-r--r--vendor/github.com/google/gopacket/pcap/pcapgo_test.go56
-rw-r--r--vendor/github.com/google/gopacket/pcap/test_dns.pcapbin0 -> 1001 bytes
-rw-r--r--vendor/github.com/google/gopacket/pcap/test_ethernet.pcapbin0 -> 7672 bytes
-rw-r--r--vendor/github.com/google/gopacket/pcap/test_loopback.pcapbin0 -> 58587 bytes
11 files changed, 1925 insertions, 0 deletions
diff --git a/vendor/github.com/google/gopacket/pcap/doc.go b/vendor/github.com/google/gopacket/pcap/doc.go
new file mode 100644
index 0000000..5bf8d86
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/doc.go
@@ -0,0 +1,106 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+/*
+Package pcap allows users of gopacket to read packets off the wire or from
+pcap files.
+
+This package is meant to be used with its parent,
+http://github.com/google/gopacket, although it can also be used independently
+if you just want to get packet data from the wire.
+
+Reading PCAP Files
+
+The following code can be used to read in data from a pcap file.
+
+ if handle, err := pcap.OpenOffline("/path/to/my/file"); err != nil {
+ panic(err)
+ } else {
+ packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
+ for packet := range packetSource.Packets() {
+ handlePacket(packet) // Do something with a packet here.
+ }
+ }
+
+Reading Live Packets
+
+The following code can be used to read in data from a live device, in this case
+"eth0".
+
+ if handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever); err != nil {
+ panic(err)
+ } else if err := handle.SetBPFFilter("tcp and port 80"); err != nil { // optional
+ panic(err)
+ } else {
+ packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
+ for packet := range packetSource.Packets() {
+ handlePacket(packet) // Do something with a packet here.
+ }
+ }
+
+Inactive Handles
+
+Newer PCAP functionality requires the concept of an 'inactive' PCAP handle.
+Instead of constantly adding new arguments to pcap_open_live, users now call
+pcap_create to create a handle, set it up with a bunch of optional function
+calls, then call pcap_activate to activate it. This library mirrors that
+mechanism, for those that want to expose/use these new features:
+
+ inactive, err := pcap.NewInactiveHandle(deviceName)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer inactive.CleanUp()
+
+ // Call various functions on inactive to set it up the way you'd like:
+ if err = inactive.SetTimeout(time.Minute); err != nil {
+ log.Fatal(err)
+ } else if err = inactive.SetTimestampSource("foo"); err != nil {
+ log.Fatal(err)
+ }
+
+ // Finally, create the actual handle by calling Activate:
+ handle, err := inactive.Activate() // after this, inactive is no longer valid
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer handle.Close()
+
+ // Now use your handle as you see fit.
+
+PCAP Timeouts
+
+pcap.OpenLive and pcap.SetTimeout both take timeouts.
+If you don't care about timeouts, just pass in BlockForever,
+which should do what you expect with minimal fuss.
+
+A timeout of 0 is not recommended. Some platforms, like Macs
+(http://www.manpages.info/macosx/pcap.3.html) say:
+ The read timeout is used to arrange that the read not necessarily return
+ immediately when a packet is seen, but that it wait for some amount of time
+ to allow more packets to arrive and to read multiple packets from the OS
+ kernel in one operation.
+This means that if you only capture one packet, the kernel might decide to wait
+'timeout' for more packets to batch with it before returning. A timeout of
+0, then, means 'wait forever for more packets', which is... not good.
+
+To get around this, we've introduced the following behavior: if a negative
+timeout is passed in, we set the positive timeout in the handle, then loop
+internally in ReadPacketData/ZeroCopyReadPacketData when we see timeout
+errors.
+
+PCAP File Writing
+
+This package does not implement PCAP file writing. However, gopacket/pcapgo
+does! Look there if you'd like to write PCAP files.
+
+Note For Windows 10 Users
+
+If you're trying to use 64-bit winpcap on Windows 10, you might have to do
+the crazy hijinks detailed at
+http://stackoverflow.com/questions/38047858/compile-gopacket-on-windows-64bit
+*/
+package pcap
diff --git a/vendor/github.com/google/gopacket/pcap/gopacket_benchmark/benchmark.go b/vendor/github.com/google/gopacket/pcap/gopacket_benchmark/benchmark.go
new file mode 100644
index 0000000..cbcae17
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/gopacket_benchmark/benchmark.go
@@ -0,0 +1,247 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+// This benchmark reads in file <tempdir>/gopacket_benchmark.pcap and measures
+// the time it takes to decode all packets from that file. If the file doesn't
+// exist, it's pulled down from a publicly available location. However, you can
+// feel free to substitute your own file at that location, in which case the
+// benchmark will run on your own data.
+//
+// It's also useful for figuring out which packets may be causing errors. Pass
+// in the --printErrors flag, and it'll print out error layers for each packet
+// that has them. This includes any packets that it's just unable to decode,
+// which is a great way to find new protocols to decode, and get test packets to
+// write tests for them.
+package main
+
+import (
+ "compress/gzip"
+ "encoding/hex"
+ "flag"
+ "fmt"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcap"
+ "github.com/google/gopacket/tcpassembly"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "time"
+)
+
+var decodeLazy *bool = flag.Bool("lazy", false, "If true, use lazy decoding")
+var decodeNoCopy *bool = flag.Bool("nocopy", true, "If true, avoid an extra copy when decoding packets")
+var printErrors *bool = flag.Bool("printErrors", false, "If true, check for and print error layers.")
+var printLayers *bool = flag.Bool("printLayers", false, "If true, print out the layers of each packet")
+var repeat *int = flag.Int("repeat", 5, "Read over the file N times")
+var cpuProfile *string = flag.String("cpuprofile", "", "If set, write CPU profile to filename")
+var url *string = flag.String("url", "http://www.ll.mit.edu/mission/communications/cyber/CSTcorpora/ideval/data/1999/training/week1/tuesday/inside.tcpdump.gz", "URL to gzip'd pcap file")
+
+type BufferPacketSource struct {
+ index int
+ data [][]byte
+ ci []gopacket.CaptureInfo
+}
+
+func NewBufferPacketSource(p gopacket.PacketDataSource) *BufferPacketSource {
+ start := time.Now()
+ b := &BufferPacketSource{}
+ for {
+ data, ci, err := p.ReadPacketData()
+ if err == io.EOF {
+ break
+ }
+ b.data = append(b.data, data)
+ b.ci = append(b.ci, ci)
+ }
+ duration := time.Since(start)
+ fmt.Printf("Reading packet data into memory: %d packets in %v, %v per packet\n", len(b.data), duration, duration/time.Duration(len(b.data)))
+ return b
+}
+
+func (b *BufferPacketSource) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
+ if b.index >= len(b.data) {
+ err = io.EOF
+ return
+ }
+ data = b.data[b.index]
+ ci = b.ci[b.index]
+ b.index++
+ return
+}
+
+func (b *BufferPacketSource) Reset() {
+ runtime.GC()
+ b.index = 0
+}
+
+func main() {
+ flag.Parse()
+ filename := os.TempDir() + string(os.PathSeparator) + "gopacket_benchmark.pcap"
+ if _, err := os.Stat(filename); err != nil {
+ // This URL points to a publicly available packet data set from a DARPA
+ // intrusion detection evaluation. See
+ // http://www.ll.mit.edu/mission/communications/cyber/CSTcorpora/ideval/data/1999/training/week1/index.html
+ // for more details.
+ fmt.Println("Local pcap file", filename, "doesn't exist, reading from", *url)
+ if resp, err := http.Get(*url); err != nil {
+ panic(err)
+ } else if out, err := os.Create(filename); err != nil {
+ panic(err)
+ } else if gz, err := gzip.NewReader(resp.Body); err != nil {
+ panic(err)
+ } else if n, err := io.Copy(out, gz); err != nil {
+ panic(err)
+ } else if err := gz.Close(); err != nil {
+ panic(err)
+ } else if err := out.Close(); err != nil {
+ panic(err)
+ } else {
+ fmt.Println("Successfully read", n, "bytes from url, unzipped to local storage")
+ }
+ }
+ fmt.Println("Reading file once through to hopefully cache most of it")
+ if f, err := os.Open(filename); err != nil {
+ panic(err)
+ } else if n, err := io.Copy(ioutil.Discard, f); err != nil {
+ panic(err)
+ } else if err := f.Close(); err != nil {
+ panic(err)
+ } else {
+ fmt.Println("Read in file", filename, ", total of", n, "bytes")
+ }
+ if *cpuProfile != "" {
+ if cpu, err := os.Create(*cpuProfile); err != nil {
+ panic(err)
+ } else if err := pprof.StartCPUProfile(cpu); err != nil {
+ panic(err)
+ } else {
+ defer func() {
+ pprof.StopCPUProfile()
+ cpu.Close()
+ }()
+ }
+ }
+ var packetDataSource *BufferPacketSource
+ var packetSource *gopacket.PacketSource
+ fmt.Printf("Opening file %q for read\n", filename)
+ if h, err := pcap.OpenOffline(filename); err != nil {
+ panic(err)
+ } else {
+ fmt.Println("Reading all packets into memory with BufferPacketSource.")
+ start := time.Now()
+ packetDataSource = NewBufferPacketSource(h)
+ duration := time.Since(start)
+ fmt.Printf("Time to read packet data into memory from file: %v\n", duration)
+ packetSource = gopacket.NewPacketSource(packetDataSource, h.LinkType())
+ packetSource.DecodeOptions.Lazy = *decodeLazy
+ packetSource.DecodeOptions.NoCopy = *decodeNoCopy
+ }
+ fmt.Println()
+ for i := 0; i < *repeat; i++ {
+ packetDataSource.Reset()
+ fmt.Printf("Benchmarking decode %d/%d\n", i+1, *repeat)
+ benchmarkPacketDecode(packetSource)
+ }
+ fmt.Println()
+ for i := 0; i < *repeat; i++ {
+ packetDataSource.Reset()
+ fmt.Printf("Benchmarking decoding layer parser %d/%d\n", i+1, *repeat)
+ benchmarkLayerDecode(packetDataSource, false)
+ }
+ fmt.Println()
+ for i := 0; i < *repeat; i++ {
+ packetDataSource.Reset()
+ fmt.Printf("Benchmarking decoding layer parser with assembly %d/%d\n", i+1, *repeat)
+ benchmarkLayerDecode(packetDataSource, true)
+ }
+}
+
+func benchmarkPacketDecode(packetSource *gopacket.PacketSource) {
+ count, errors := 0, 0
+ start := time.Now()
+ for packet, err := packetSource.NextPacket(); err != io.EOF; packet, err = packetSource.NextPacket() {
+ if err != nil {
+ fmt.Println("Error reading in packet:", err)
+ continue
+ }
+ count++
+ var hasError bool
+ if *printErrors && packet.ErrorLayer() != nil {
+ fmt.Println("\n\n\nError decoding packet:", packet.ErrorLayer().Error())
+ fmt.Println(hex.Dump(packet.Data()))
+ fmt.Printf("%#v\n", packet.Data())
+ errors++
+ hasError = true
+ }
+ if *printLayers || hasError {
+ fmt.Printf("\n=== PACKET %d ===\n", count)
+ for _, l := range packet.Layers() {
+ fmt.Printf("--- LAYER %v ---\n%#v\n\n", l.LayerType(), l)
+ }
+ fmt.Println()
+ }
+ }
+ duration := time.Since(start)
+ fmt.Printf("\tRead in %v packets in %v, %v per packet\n", count, duration, duration/time.Duration(count))
+ if *printErrors {
+ fmt.Printf("%v errors, successfully decoded %.02f%%\n", errors, float64(count-errors)*100.0/float64(count))
+ }
+}
+
+type streamFactory struct {
+}
+
+func (s *streamFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream {
+ return s
+}
+func (s *streamFactory) Reassembled([]tcpassembly.Reassembly) {
+}
+func (s *streamFactory) ReassemblyComplete() {
+}
+
+func benchmarkLayerDecode(source *BufferPacketSource, assemble bool) {
+ var tcp layers.TCP
+ var ip layers.IPv4
+ var eth layers.Ethernet
+ var udp layers.UDP
+ var icmp layers.ICMPv4
+ var payload gopacket.Payload
+ parser := gopacket.NewDecodingLayerParser(
+ layers.LayerTypeEthernet,
+ &eth, &ip, &icmp, &tcp, &udp, &payload)
+ pool := tcpassembly.NewStreamPool(&streamFactory{})
+ assembler := tcpassembly.NewAssembler(pool)
+ var decoded []gopacket.LayerType
+ start := time.Now()
+ packets, decodedlayers, assembled := 0, 0, 0
+ for {
+ packets++
+ data, ci, err := source.ReadPacketData()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ fmt.Println("Error reading packet: ", err)
+ continue
+ }
+ err = parser.DecodeLayers(data, &decoded)
+ for _, typ := range decoded {
+ decodedlayers++
+ if typ == layers.LayerTypeTCP && assemble {
+ assembled++
+ assembler.AssembleWithTimestamp(ip.NetworkFlow(), &tcp, ci.Timestamp)
+ }
+ }
+ }
+ if assemble {
+ assembler.FlushAll()
+ }
+ duration := time.Since(start)
+ fmt.Printf("\tRead in %d packets in %v, decoded %v layers, assembled %v packets: %v per packet\n", packets, duration, decodedlayers, assembled, duration/time.Duration(packets))
+}
diff --git a/vendor/github.com/google/gopacket/pcap/pcap.go b/vendor/github.com/google/gopacket/pcap/pcap.go
new file mode 100644
index 0000000..1ecdf03
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/pcap.go
@@ -0,0 +1,1005 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package pcap
+
+/*
+#cgo solaris LDFLAGS: -L /opt/local/lib -lpcap
+#cgo linux LDFLAGS: -lpcap
+#cgo dragonfly LDFLAGS: -lpcap
+#cgo freebsd LDFLAGS: -lpcap
+#cgo openbsd LDFLAGS: -lpcap
+#cgo netbsd LDFLAGS: -lpcap
+#cgo darwin LDFLAGS: -lpcap
+#cgo windows CFLAGS: -I C:/WpdPack/Include
+#cgo windows,386 LDFLAGS: -L C:/WpdPack/Lib -lwpcap
+#cgo windows,amd64 LDFLAGS: -L C:/WpdPack/Lib/x64 -lwpcap
+#include <stdlib.h>
+#include <pcap.h>
+
+// Some old versions of pcap don't define this constant.
+#ifndef PCAP_NETMASK_UNKNOWN
+#define PCAP_NETMASK_UNKNOWN 0xffffffff
+#endif
+
+// libpcap doesn't actually export its version in a #define-guardable way,
+// so we have to use other defined things to differentiate versions.
+// We assume at least libpcap v1.1 at the moment.
+// See http://upstream-tracker.org/versions/libpcap.html
+
+#ifndef PCAP_ERROR_TSTAMP_PRECISION_NOTSUP // < v1.5
+
+int pcap_set_immediate_mode(pcap_t *p, int mode) {
+ return PCAP_ERROR;
+}
+
+#ifndef PCAP_TSTAMP_HOST // < v1.2
+
+int pcap_set_tstamp_type(pcap_t* p, int t) { return -1; }
+int pcap_list_tstamp_types(pcap_t* p, int** t) { return 0; }
+void pcap_free_tstamp_types(int *tstamp_types) {}
+const char* pcap_tstamp_type_val_to_name(int t) {
+ return "pcap timestamp types not supported";
+}
+int pcap_tstamp_type_name_to_val(const char* t) {
+ return PCAP_ERROR;
+}
+
+#endif // < v1.2
+#endif // < v1.5
+
+#ifndef PCAP_ERROR_PROMISC_PERM_DENIED
+#define PCAP_ERROR_PROMISC_PERM_DENIED -11
+#endif
+
+// WinPcap doesn't export a pcap_statustostr, so use the less-specific
+// pcap_strerror. Note that linking against something like cygwin libpcap
+// may result is less-specific error messages.
+#ifdef WIN32
+#define pcap_statustostr pcap_strerror
+
+// WinPcap also doesn't export pcap_can_set_rfmon and pcap_set_rfmon,
+// as those are handled by separate libraries (airpcap).
+// https://www.winpcap.org/docs/docs_412/html/group__wpcapfunc.html
+// Stub out those functions here, returning values that indicate rfmon
+// setting is unavailable/unsuccessful.
+int pcap_can_set_rfmon(pcap_t *p) {
+ return 0;
+}
+
+int pcap_set_rfmon(pcap_t *p, int rfmon) {
+ return PCAP_ERROR;
+}
+#endif
+
+// Windows, Macs, and Linux all use different time types. Joy.
+#ifdef WIN32
+#define gopacket_time_secs_t long
+#define gopacket_time_usecs_t long
+#elif __APPLE__
+#define gopacket_time_secs_t __darwin_time_t
+#define gopacket_time_usecs_t __darwin_suseconds_t
+#elif __GLIBC__
+#define gopacket_time_secs_t __time_t
+#define gopacket_time_usecs_t __suseconds_t
+#else // Some form of linux/bsd/etc...
+#include <sys/param.h>
+#ifdef __OpenBSD__
+#define gopacket_time_secs_t u_int32_t
+#define gopacket_time_usecs_t u_int32_t
+#else
+#define gopacket_time_secs_t time_t
+#define gopacket_time_usecs_t suseconds_t
+#endif
+#endif
+*/
+import "C"
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "reflect"
+ "runtime"
+ "strconv"
+ "sync"
+ "sync/atomic"
+ "syscall"
+ "time"
+ "unsafe"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+const errorBufferSize = 256
+
+// MaxBpfInstructions is the maximum number of BPF instructions supported (BPF_MAXINSNS),
+// taken from Linux kernel: include/uapi/linux/bpf_common.h
+//
+// https://github.com/torvalds/linux/blob/master/include/uapi/linux/bpf_common.h
+const MaxBpfInstructions = 4096
+
+// 8 bytes per instruction, max 4096 instructions
+const bpfInstructionBufferSize = 8 * MaxBpfInstructions
+
+// Handle provides a connection to a pcap handle, allowing users to read packets
+// off the wire (Next), inject packets onto the wire (Inject), and
+// perform a number of other functions to affect and understand packet output.
+//
+// Handles are already pcap_activate'd
+type Handle struct {
+ // cptr is the handle for the actual pcap C object.
+ cptr *C.pcap_t
+ timeout time.Duration
+ device string
+ deviceIndex int
+ mu sync.Mutex
+ closeMu sync.Mutex
+ // stop is set to a non-zero value by Handle.Close to signal to
+ // getNextBufPtrLocked to stop trying to read packets
+ stop uint64
+
+ // Since pointers to these objects are passed into a C function, if
+ // they're declared locally then the Go compiler thinks they may have
+ // escaped into C-land, so it allocates them on the heap. This causes a
+ // huge memory hit, so to handle that we store them here instead.
+ pkthdr *C.struct_pcap_pkthdr
+ bufptr *C.u_char
+}
+
+// Stats contains statistics on how many packets were handled by a pcap handle,
+// and what was done with those packets.
+type Stats struct {
+ PacketsReceived int
+ PacketsDropped int
+ PacketsIfDropped int
+}
+
+// Interface describes a single network interface on a machine.
+type Interface struct {
+ Name string
+ Description string
+ Addresses []InterfaceAddress
+ // TODO: add more elements
+}
+
+// Datalink describes the datalink
+type Datalink struct {
+ Name string
+ Description string
+}
+
+// InterfaceAddress describes an address associated with an Interface.
+// Currently, it's IPv4/6 specific.
+type InterfaceAddress struct {
+ IP net.IP
+ Netmask net.IPMask // Netmask may be nil if we were unable to retrieve it.
+ // TODO: add broadcast + PtP dst ?
+}
+
+// BPF is a compiled filter program, useful for offline packet matching.
+type BPF struct {
+ orig string
+ bpf _Ctype_struct_bpf_program // takes a finalizer, not overriden by outsiders
+}
+
+// BPFInstruction is a byte encoded structure holding a BPF instruction
+type BPFInstruction struct {
+ Code uint16
+ Jt uint8
+ Jf uint8
+ K uint32
+}
+
+// BlockForever causes it to block forever waiting for packets, when passed
+// into SetTimeout or OpenLive, while still returning incoming packets to userland relatively
+// quickly.
+const BlockForever = -time.Millisecond * 10
+
+func timeoutMillis(timeout time.Duration) C.int {
+ // Flip sign if necessary. See package docs on timeout for reasoning behind this.
+ if timeout < 0 {
+ timeout *= -1
+ }
+ // Round up
+ if timeout != 0 && timeout < time.Millisecond {
+ timeout = time.Millisecond
+ }
+ return C.int(timeout / time.Millisecond)
+}
+
+// OpenLive opens a device and returns a *Handle.
+// It takes as arguments the name of the device ("eth0"), the maximum size to
+// read for each packet (snaplen), whether to put the interface in promiscuous
+// mode, and a timeout.
+//
+// See the package documentation for important details regarding 'timeout'.
+func OpenLive(device string, snaplen int32, promisc bool, timeout time.Duration) (handle *Handle, _ error) {
+ buf := (*C.char)(C.calloc(errorBufferSize, 1))
+ defer C.free(unsafe.Pointer(buf))
+
+ var pro C.int
+ if promisc {
+ pro = 1
+ }
+ p := &Handle{timeout: timeout, device: device}
+
+ ifc, err := net.InterfaceByName(device)
+ if err != nil {
+ // The device wasn't found in the OS, but could be "any"
+ // Set index to 0
+ p.deviceIndex = 0
+ } else {
+ p.deviceIndex = ifc.Index
+ }
+
+ dev := C.CString(device)
+ defer C.free(unsafe.Pointer(dev))
+
+ p.cptr = C.pcap_open_live(dev, C.int(snaplen), pro, timeoutMillis(timeout), buf)
+ if p.cptr == nil {
+ return nil, errors.New(C.GoString(buf))
+ }
+
+ if err := p.openLive(); err != nil {
+ C.pcap_close(p.cptr)
+ return nil, err
+ }
+
+ return p, nil
+}
+
+// OpenOffline opens a file and returns its contents as a *Handle.
+func OpenOffline(file string) (handle *Handle, err error) {
+ buf := (*C.char)(C.calloc(errorBufferSize, 1))
+ defer C.free(unsafe.Pointer(buf))
+ cf := C.CString(file)
+ defer C.free(unsafe.Pointer(cf))
+
+ cptr := C.pcap_open_offline(cf, buf)
+ if cptr == nil {
+ return nil, errors.New(C.GoString(buf))
+ }
+ return &Handle{cptr: cptr}, nil
+}
+
+// NextError is the return code from a call to Next.
+type NextError int32
+
+// NextError implements the error interface.
+func (n NextError) Error() string {
+ switch n {
+ case NextErrorOk:
+ return "OK"
+ case NextErrorTimeoutExpired:
+ return "Timeout Expired"
+ case NextErrorReadError:
+ return "Read Error"
+ case NextErrorNoMorePackets:
+ return "No More Packets In File"
+ case NextErrorNotActivated:
+ return "Not Activated"
+ }
+ return strconv.Itoa(int(n))
+}
+
+// NextError values.
+const (
+ NextErrorOk NextError = 1
+ NextErrorTimeoutExpired NextError = 0
+ NextErrorReadError NextError = -1
+ // NextErrorNoMorePackets is returned when reading from a file (OpenOffline) and
+ // EOF is reached. When this happens, Next() returns io.EOF instead of this.
+ NextErrorNoMorePackets NextError = -2
+ NextErrorNotActivated NextError = -3
+)
+
+// ReadPacketData returns the next packet read from the pcap handle, along with an error
+// code associated with that packet. If the packet is read successfully, the
+// returned error is nil.
+func (p *Handle) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
+ p.mu.Lock()
+ err = p.getNextBufPtrLocked(&ci)
+ if err == nil {
+ data = C.GoBytes(unsafe.Pointer(p.bufptr), C.int(ci.CaptureLength))
+ }
+ p.mu.Unlock()
+ if err == NextErrorTimeoutExpired {
+ runtime.Gosched()
+ }
+ return
+}
+
+type activateError C.int
+
+const (
+ aeNoError = 0
+ aeActivated = C.PCAP_ERROR_ACTIVATED
+ aePromisc = C.PCAP_WARNING_PROMISC_NOTSUP
+ aeNoSuchDevice = C.PCAP_ERROR_NO_SUCH_DEVICE
+ aeDenied = C.PCAP_ERROR_PERM_DENIED
+ aeNotUp = C.PCAP_ERROR_IFACE_NOT_UP
+)
+
+func (a activateError) Error() string {
+ switch a {
+ case aeNoError:
+ return "No Error"
+ case aeActivated:
+ return "Already Activated"
+ case aePromisc:
+ return "Cannot set as promisc"
+ case aeNoSuchDevice:
+ return "No Such Device"
+ case aeDenied:
+ return "Permission Denied"
+ case aeNotUp:
+ return "Interface Not Up"
+ default:
+ return fmt.Sprintf("unknown activated error: %d", a)
+ }
+}
+
+// getNextBufPtrLocked is shared code for ReadPacketData and
+// ZeroCopyReadPacketData.
+func (p *Handle) getNextBufPtrLocked(ci *gopacket.CaptureInfo) error {
+ if p.cptr == nil {
+ return io.EOF
+ }
+
+ for atomic.LoadUint64(&p.stop) == 0 {
+ // try to read a packet if one is immediately available
+ result := NextError(C.pcap_next_ex(p.cptr, &p.pkthdr, &p.bufptr))
+
+ switch result {
+ case NextErrorOk:
+ // got a packet, set capture info and return
+ sec := int64(p.pkthdr.ts.tv_sec)
+ // convert micros to nanos
+ nanos := int64(p.pkthdr.ts.tv_usec) * 1000
+
+ ci.Timestamp = time.Unix(sec, nanos)
+ ci.CaptureLength = int(p.pkthdr.caplen)
+ ci.Length = int(p.pkthdr.len)
+ ci.InterfaceIndex = p.deviceIndex
+
+ return nil
+ case NextErrorNoMorePackets:
+ // no more packets, return EOF rather than libpcap-specific error
+ return io.EOF
+ case NextErrorTimeoutExpired:
+ // Negative timeout means to loop forever, instead of actually returning
+ // the timeout error.
+ if p.timeout < 0 {
+ // must have had a timeout... wait before trying again
+ p.waitForPacket()
+ continue
+ }
+ default:
+ return result
+ }
+ }
+
+ // stop must be set
+ return io.EOF
+}
+
+// ZeroCopyReadPacketData reads the next packet off the wire, and returns its data.
+// The slice returned by ZeroCopyReadPacketData points to bytes owned by the
+// the Handle. Each call to ZeroCopyReadPacketData invalidates any data previously
+// returned by ZeroCopyReadPacketData. Care must be taken not to keep pointers
+// to old bytes when using ZeroCopyReadPacketData... if you need to keep data past
+// the next time you call ZeroCopyReadPacketData, use ReadPacketData, which copies
+// the bytes into a new buffer for you.
+// data1, _, _ := handle.ZeroCopyReadPacketData()
+// // do everything you want with data1 here, copying bytes out of it if you'd like to keep them around.
+// data2, _, _ := handle.ZeroCopyReadPacketData() // invalidates bytes in data1
+func (p *Handle) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
+ p.mu.Lock()
+ err = p.getNextBufPtrLocked(&ci)
+ if err == nil {
+ slice := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ slice.Data = uintptr(unsafe.Pointer(p.bufptr))
+ slice.Len = ci.CaptureLength
+ slice.Cap = ci.CaptureLength
+ }
+ p.mu.Unlock()
+ if err == NextErrorTimeoutExpired {
+ runtime.Gosched()
+ }
+ return
+}
+
+// Close closes the underlying pcap handle.
+func (p *Handle) Close() {
+ p.closeMu.Lock()
+ defer p.closeMu.Unlock()
+
+ if p.cptr == nil {
+ return
+ }
+
+ atomic.StoreUint64(&p.stop, 1)
+
+ // wait for packet reader to stop
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ C.pcap_close(p.cptr)
+ p.cptr = nil
+}
+
+// Error returns the current error associated with a pcap handle (pcap_geterr).
+func (p *Handle) Error() error {
+ return errors.New(C.GoString(C.pcap_geterr(p.cptr)))
+}
+
+// Stats returns statistics on the underlying pcap handle.
+func (p *Handle) Stats() (stat *Stats, err error) {
+ var cstats _Ctype_struct_pcap_stat
+ if -1 == C.pcap_stats(p.cptr, &cstats) {
+ return nil, p.Error()
+ }
+ return &Stats{
+ PacketsReceived: int(cstats.ps_recv),
+ PacketsDropped: int(cstats.ps_drop),
+ PacketsIfDropped: int(cstats.ps_ifdrop),
+ }, nil
+}
+
+// ListDataLinks obtains a list of all possible data link types supported for an interface.
+func (p *Handle) ListDataLinks() (datalinks []Datalink, err error) {
+ var dltbuf *C.int
+
+ n := int(C.pcap_list_datalinks(p.cptr, &dltbuf))
+ if -1 == n {
+ return nil, p.Error()
+ }
+
+ defer C.pcap_free_datalinks(dltbuf)
+
+ datalinks = make([]Datalink, n)
+
+ dltArray := (*[100]C.int)(unsafe.Pointer(dltbuf))
+
+ for i := 0; i < n; i++ {
+ expr := C.pcap_datalink_val_to_name((*dltArray)[i])
+ datalinks[i].Name = C.GoString(expr)
+
+ expr = C.pcap_datalink_val_to_description((*dltArray)[i])
+ datalinks[i].Description = C.GoString(expr)
+ }
+
+ return datalinks, nil
+}
+
+// pcap_compile is NOT thread-safe, so protect it.
+var pcapCompileMu sync.Mutex
+
+// compileBPFFilter always returns an allocated _Ctype_struct_bpf_program
+// It is the callers responsibility to free the memory again, e.g.
+//
+// C.pcap_freecode(&bpf)
+//
+func (p *Handle) compileBPFFilter(expr string) (_Ctype_struct_bpf_program, error) {
+ errorBuf := (*C.char)(C.calloc(errorBufferSize, 1))
+ defer C.free(unsafe.Pointer(errorBuf))
+
+ var netp uint32
+ var maskp uint32
+
+ // Only do the lookup on network interfaces.
+ // No device indicates we're handling a pcap file.
+ if len(p.device) > 0 {
+ dev := C.CString(p.device)
+ defer C.free(unsafe.Pointer(dev))
+ if -1 == C.pcap_lookupnet(
+ dev,
+ (*C.bpf_u_int32)(unsafe.Pointer(&netp)),
+ (*C.bpf_u_int32)(unsafe.Pointer(&maskp)),
+ errorBuf,
+ ) {
+ // We can't lookup the network, but that could be because the interface
+ // doesn't have an IPv4.
+ }
+ }
+
+ var bpf _Ctype_struct_bpf_program
+ cexpr := C.CString(expr)
+ defer C.free(unsafe.Pointer(cexpr))
+
+ pcapCompileMu.Lock()
+ defer pcapCompileMu.Unlock()
+ if -1 == C.pcap_compile(p.cptr, &bpf, cexpr, 1, C.bpf_u_int32(maskp)) {
+ return bpf, p.Error()
+ }
+
+ return bpf, nil
+}
+
+// CompileBPFFilter compiles and returns a BPF filter with given a link type and capture length.
+func CompileBPFFilter(linkType layers.LinkType, captureLength int, expr string) ([]BPFInstruction, error) {
+ cptr := C.pcap_open_dead(C.int(linkType), C.int(captureLength))
+ if cptr == nil {
+ return nil, errors.New("error opening dead capture")
+ }
+
+ h := Handle{cptr: cptr}
+ defer h.Close()
+ return h.CompileBPFFilter(expr)
+}
+
+// CompileBPFFilter compiles and returns a BPF filter for the pcap handle.
+func (p *Handle) CompileBPFFilter(expr string) ([]BPFInstruction, error) {
+ bpf, err := p.compileBPFFilter(expr)
+ defer C.pcap_freecode(&bpf)
+ if err != nil {
+ return nil, err
+ }
+
+ bpfInsn := (*[bpfInstructionBufferSize]_Ctype_struct_bpf_insn)(unsafe.Pointer(bpf.bf_insns))[0:bpf.bf_len:bpf.bf_len]
+ bpfInstruction := make([]BPFInstruction, len(bpfInsn), len(bpfInsn))
+
+ for i, v := range bpfInsn {
+ bpfInstruction[i].Code = uint16(v.code)
+ bpfInstruction[i].Jt = uint8(v.jt)
+ bpfInstruction[i].Jf = uint8(v.jf)
+ bpfInstruction[i].K = uint32(v.k)
+ }
+
+ return bpfInstruction, nil
+}
+
+// SetBPFFilter compiles and sets a BPF filter for the pcap handle.
+func (p *Handle) SetBPFFilter(expr string) (err error) {
+ bpf, err := p.compileBPFFilter(expr)
+ defer C.pcap_freecode(&bpf)
+ if err != nil {
+ return err
+ }
+
+ if -1 == C.pcap_setfilter(p.cptr, &bpf) {
+ return p.Error()
+ }
+
+ return nil
+}
+
+// SetBPFInstructionFilter may be used to apply a filter in BPF asm byte code format.
+//
+// Simplest way to generate BPF asm byte code is with tcpdump:
+// tcpdump -dd 'udp'
+//
+// The output may be used directly to add a filter, e.g.:
+// bpfInstructions := []pcap.BpfInstruction{
+// {0x28, 0, 0, 0x0000000c},
+// {0x15, 0, 9, 0x00000800},
+// {0x30, 0, 0, 0x00000017},
+// {0x15, 0, 7, 0x00000006},
+// {0x28, 0, 0, 0x00000014},
+// {0x45, 5, 0, 0x00001fff},
+// {0xb1, 0, 0, 0x0000000e},
+// {0x50, 0, 0, 0x0000001b},
+// {0x54, 0, 0, 0x00000012},
+// {0x15, 0, 1, 0x00000012},
+// {0x6, 0, 0, 0x0000ffff},
+// {0x6, 0, 0, 0x00000000},
+// }
+//
+// An other posibility is to write the bpf code in bpf asm.
+// Documentation: https://www.kernel.org/doc/Documentation/networking/filter.txt
+//
+// To compile the code use bpf_asm from
+// https://github.com/torvalds/linux/tree/master/tools/net
+//
+// The following command may be used to convert bpf_asm output to c/go struct, usable for SetBPFFilterByte:
+// bpf_asm -c tcp.bpf
+func (p *Handle) SetBPFInstructionFilter(bpfInstructions []BPFInstruction) (err error) {
+ bpf, err := bpfInstructionFilter(bpfInstructions)
+ if err != nil {
+ return err
+ }
+
+ if -1 == C.pcap_setfilter(p.cptr, &bpf) {
+ C.pcap_freecode(&bpf)
+ return p.Error()
+ }
+
+ C.pcap_freecode(&bpf)
+
+ return nil
+}
+func bpfInstructionFilter(bpfInstructions []BPFInstruction) (bpf _Ctype_struct_bpf_program, err error) {
+ if len(bpfInstructions) < 1 {
+ return bpf, errors.New("bpfInstructions must not be empty")
+ }
+
+ if len(bpfInstructions) > MaxBpfInstructions {
+ return bpf, fmt.Errorf("bpfInstructions must not be larger than %d", MaxBpfInstructions)
+ }
+
+ bpf.bf_len = C.u_int(len(bpfInstructions))
+ cbpfInsns := C.calloc(C.size_t(len(bpfInstructions)), C.size_t(unsafe.Sizeof(bpfInstructions[0])))
+
+ copy((*[bpfInstructionBufferSize]BPFInstruction)(cbpfInsns)[0:len(bpfInstructions)], bpfInstructions)
+ bpf.bf_insns = (*_Ctype_struct_bpf_insn)(cbpfInsns)
+
+ return
+}
+
+// NewBPF compiles the given string into a new filter program.
+//
+// BPF filters need to be created from activated handles, because they need to
+// know the underlying link type to correctly compile their offsets.
+func (p *Handle) NewBPF(expr string) (*BPF, error) {
+ bpf := &BPF{orig: expr}
+ cexpr := C.CString(expr)
+ defer C.free(unsafe.Pointer(cexpr))
+
+ pcapCompileMu.Lock()
+ defer pcapCompileMu.Unlock()
+ if C.pcap_compile(p.cptr, &bpf.bpf, cexpr /* optimize */, 1, C.PCAP_NETMASK_UNKNOWN) != 0 {
+ return nil, p.Error()
+ }
+
+ runtime.SetFinalizer(bpf, destroyBPF)
+ return bpf, nil
+}
+
+// NewBPFInstructionFilter sets the given BPFInstructions as new filter program.
+//
+// More details see func SetBPFInstructionFilter
+//
+// BPF filters need to be created from activated handles, because they need to
+// know the underlying link type to correctly compile their offsets.
+func (p *Handle) NewBPFInstructionFilter(bpfInstructions []BPFInstruction) (*BPF, error) {
+ var err error
+ bpf := &BPF{orig: "BPF Instruction Filter"}
+
+ bpf.bpf, err = bpfInstructionFilter(bpfInstructions)
+ if err != nil {
+ return nil, err
+ }
+
+ runtime.SetFinalizer(bpf, destroyBPF)
+ return bpf, nil
+}
+func destroyBPF(bpf *BPF) {
+ C.pcap_freecode(&bpf.bpf)
+}
+
+// String returns the original string this BPF filter was compiled from.
+func (b *BPF) String() string {
+ return b.orig
+}
+
+// Matches returns true if the given packet data matches this filter.
+func (b *BPF) Matches(ci gopacket.CaptureInfo, data []byte) bool {
+ var hdr C.struct_pcap_pkthdr
+ hdr.ts.tv_sec = C.gopacket_time_secs_t(ci.Timestamp.Unix())
+ hdr.ts.tv_usec = C.gopacket_time_usecs_t(ci.Timestamp.Nanosecond() / 1000)
+ hdr.caplen = C.bpf_u_int32(len(data)) // Trust actual length over ci.Length.
+ hdr.len = C.bpf_u_int32(ci.Length)
+ dataptr := (*C.u_char)(unsafe.Pointer(&data[0]))
+ return C.pcap_offline_filter(&b.bpf, &hdr, dataptr) != 0
+}
+
+// Version returns pcap_lib_version.
+func Version() string {
+ return C.GoString(C.pcap_lib_version())
+}
+
+// LinkType returns pcap_datalink, as a layers.LinkType.
+func (p *Handle) LinkType() layers.LinkType {
+ return layers.LinkType(C.pcap_datalink(p.cptr))
+}
+
+// SetLinkType calls pcap_set_datalink on the pcap handle.
+func (p *Handle) SetLinkType(dlt layers.LinkType) error {
+ if -1 == C.pcap_set_datalink(p.cptr, C.int(dlt)) {
+ return p.Error()
+ }
+ return nil
+}
+
+// FindAllDevs attempts to enumerate all interfaces on the current machine.
+func FindAllDevs() (ifs []Interface, err error) {
+ var buf *C.char
+ buf = (*C.char)(C.calloc(errorBufferSize, 1))
+ defer C.free(unsafe.Pointer(buf))
+ var alldevsp *C.pcap_if_t
+
+ if -1 == C.pcap_findalldevs((**C.pcap_if_t)(&alldevsp), buf) {
+ return nil, errors.New(C.GoString(buf))
+ }
+ defer C.pcap_freealldevs((*C.pcap_if_t)(alldevsp))
+ dev := alldevsp
+ var i uint32
+ for i = 0; dev != nil; dev = (*C.pcap_if_t)(dev.next) {
+ i++
+ }
+ ifs = make([]Interface, i)
+ dev = alldevsp
+ for j := uint32(0); dev != nil; dev = (*C.pcap_if_t)(dev.next) {
+ var iface Interface
+ iface.Name = C.GoString(dev.name)
+ iface.Description = C.GoString(dev.description)
+ iface.Addresses = findalladdresses(dev.addresses)
+ // TODO: add more elements
+ ifs[j] = iface
+ j++
+ }
+ return
+}
+
+func findalladdresses(addresses *_Ctype_struct_pcap_addr) (retval []InterfaceAddress) {
+ // TODO - make it support more than IPv4 and IPv6?
+ retval = make([]InterfaceAddress, 0, 1)
+ for curaddr := addresses; curaddr != nil; curaddr = (*_Ctype_struct_pcap_addr)(curaddr.next) {
+ // Strangely, it appears that in some cases, we get a pcap address back from
+ // pcap_findalldevs with a nil .addr. It appears that we can skip over
+ // these.
+ if curaddr.addr == nil {
+ continue
+ }
+ var a InterfaceAddress
+ var err error
+ if a.IP, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.addr))); err != nil {
+ continue
+ }
+ // To be safe, we'll also check for netmask.
+ if curaddr.netmask == nil {
+ continue
+ }
+ if a.Netmask, err = sockaddrToIP((*syscall.RawSockaddr)(unsafe.Pointer(curaddr.netmask))); err != nil {
+ // If we got an IP address but we can't get a netmask, just return the IP
+ // address.
+ a.Netmask = nil
+ }
+ retval = append(retval, a)
+ }
+ return
+}
+
+func sockaddrToIP(rsa *syscall.RawSockaddr) (IP []byte, err error) {
+ switch rsa.Family {
+ case syscall.AF_INET:
+ pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(rsa))
+ IP = make([]byte, 4)
+ for i := 0; i < len(IP); i++ {
+ IP[i] = pp.Addr[i]
+ }
+ return
+ case syscall.AF_INET6:
+ pp := (*syscall.RawSockaddrInet6)(unsafe.Pointer(rsa))
+ IP = make([]byte, 16)
+ for i := 0; i < len(IP); i++ {
+ IP[i] = pp.Addr[i]
+ }
+ return
+ }
+ err = errors.New("Unsupported address type")
+ return
+}
+
+// WritePacketData calls pcap_sendpacket, injecting the given data into the pcap handle.
+func (p *Handle) WritePacketData(data []byte) (err error) {
+ if -1 == C.pcap_sendpacket(p.cptr, (*C.u_char)(&data[0]), (C.int)(len(data))) {
+ err = p.Error()
+ }
+ return
+}
+
+// Direction is used by Handle.SetDirection.
+type Direction uint8
+
+// Direction values for Handle.SetDirection.
+const (
+ DirectionIn Direction = C.PCAP_D_IN
+ DirectionOut Direction = C.PCAP_D_OUT
+ DirectionInOut Direction = C.PCAP_D_INOUT
+)
+
+// SetDirection sets the direction for which packets will be captured.
+func (p *Handle) SetDirection(direction Direction) error {
+ if direction != DirectionIn && direction != DirectionOut && direction != DirectionInOut {
+ return fmt.Errorf("Invalid direction: %v", direction)
+ }
+ if status := C.pcap_setdirection(p.cptr, (C.pcap_direction_t)(direction)); status < 0 {
+ return statusError(status)
+ }
+ return nil
+}
+
+// TimestampSource tells PCAP which type of timestamp to use for packets.
+type TimestampSource C.int
+
+// String returns the timestamp type as a human-readable string.
+func (t TimestampSource) String() string {
+ return C.GoString(C.pcap_tstamp_type_val_to_name(C.int(t)))
+}
+
+// TimestampSourceFromString translates a string into a timestamp type, case
+// insensitive.
+func TimestampSourceFromString(s string) (TimestampSource, error) {
+ cs := C.CString(s)
+ defer C.free(unsafe.Pointer(cs))
+ t := C.pcap_tstamp_type_name_to_val(cs)
+ if t < 0 {
+ return 0, statusError(t)
+ }
+ return TimestampSource(t), nil
+}
+
+func statusError(status C.int) error {
+ return errors.New(C.GoString(C.pcap_statustostr(status)))
+}
+
+// InactiveHandle allows you to call pre-pcap_activate functions on your pcap
+// handle to set it up just the way you'd like.
+type InactiveHandle struct {
+ // cptr is the handle for the actual pcap C object.
+ cptr *C.pcap_t
+ device string
+ deviceIndex int
+ timeout time.Duration
+}
+
+// Activate activates the handle. The current InactiveHandle becomes invalid
+// and all future function calls on it will fail.
+func (p *InactiveHandle) Activate() (*Handle, error) {
+ err := activateError(C.pcap_activate(p.cptr))
+ if err != aeNoError {
+ return nil, err
+ }
+ h := &Handle{
+ cptr: p.cptr,
+ timeout: p.timeout,
+ device: p.device,
+ deviceIndex: p.deviceIndex,
+ }
+ p.cptr = nil
+ return h, nil
+}
+
+// CleanUp cleans up any stuff left over from a successful or failed building
+// of a handle.
+func (p *InactiveHandle) CleanUp() {
+ if p.cptr != nil {
+ C.pcap_close(p.cptr)
+ }
+}
+
+// NewInactiveHandle creates a new InactiveHandle, which wraps an un-activated PCAP handle.
+// Callers of NewInactiveHandle should immediately defer 'CleanUp', as in:
+// inactive := NewInactiveHandle("eth0")
+// defer inactive.CleanUp()
+func NewInactiveHandle(device string) (*InactiveHandle, error) {
+ buf := (*C.char)(C.calloc(errorBufferSize, 1))
+ defer C.free(unsafe.Pointer(buf))
+ dev := C.CString(device)
+ defer C.free(unsafe.Pointer(dev))
+
+ // Try to get the interface index, but iy could be something like "any"
+ // in which case use 0, which doesn't exist in nature
+ deviceIndex := 0
+ ifc, err := net.InterfaceByName(device)
+ if err == nil {
+ deviceIndex = ifc.Index
+ }
+
+ // This copies a bunch of the pcap_open_live implementation from pcap.c:
+ cptr := C.pcap_create(dev, buf)
+ if cptr == nil {
+ return nil, errors.New(C.GoString(buf))
+ }
+ return &InactiveHandle{cptr: cptr, device: device, deviceIndex: deviceIndex}, nil
+}
+
+// SetSnapLen sets the snap length (max bytes per packet to capture).
+func (p *InactiveHandle) SetSnapLen(snaplen int) error {
+ if status := C.pcap_set_snaplen(p.cptr, C.int(snaplen)); status < 0 {
+ return statusError(status)
+ }
+ return nil
+}
+
+// SetPromisc sets the handle to either be promiscuous (capture packets
+// unrelated to this host) or not.
+func (p *InactiveHandle) SetPromisc(promisc bool) error {
+ var pro C.int
+ if promisc {
+ pro = 1
+ }
+ if status := C.pcap_set_promisc(p.cptr, pro); status < 0 {
+ return statusError(status)
+ }
+ return nil
+}
+
+// SetTimeout sets the read timeout for the handle.
+//
+// See the package documentation for important details regarding 'timeout'.
+func (p *InactiveHandle) SetTimeout(timeout time.Duration) error {
+ if status := C.pcap_set_timeout(p.cptr, timeoutMillis(timeout)); status < 0 {
+ return statusError(status)
+ }
+ p.timeout = timeout
+ return nil
+}
+
+// SupportedTimestamps returns a list of supported timstamp types for this
+// handle.
+func (p *InactiveHandle) SupportedTimestamps() (out []TimestampSource) {
+ var types *C.int
+ n := int(C.pcap_list_tstamp_types(p.cptr, &types))
+ defer C.pcap_free_tstamp_types(types)
+ typesArray := (*[100]C.int)(unsafe.Pointer(types))
+ for i := 0; i < n; i++ {
+ out = append(out, TimestampSource((*typesArray)[i]))
+ }
+ return
+}
+
+// SetTimestampSource sets the type of timestamp generator PCAP uses when
+// attaching timestamps to packets.
+func (p *InactiveHandle) SetTimestampSource(t TimestampSource) error {
+ if status := C.pcap_set_tstamp_type(p.cptr, C.int(t)); status < 0 {
+ return statusError(status)
+ }
+ return nil
+}
+
+// CannotSetRFMon is returned by SetRFMon if the handle does not allow
+// setting RFMon because pcap_can_set_rfmon returns 0.
+var CannotSetRFMon = errors.New("Cannot set rfmon for this handle")
+
+// SetRFMon turns on radio monitoring mode, similar to promiscuous mode but for
+// wireless networks. If this mode is enabled, the interface will not need to
+// associate with an access point before it can receive traffic.
+func (p *InactiveHandle) SetRFMon(monitor bool) error {
+ var mon C.int
+ if monitor {
+ mon = 1
+ }
+ switch canset := C.pcap_can_set_rfmon(p.cptr); canset {
+ case 0:
+ return CannotSetRFMon
+ case 1:
+ // success
+ default:
+ return statusError(canset)
+ }
+ if status := C.pcap_set_rfmon(p.cptr, mon); status != 0 {
+ return statusError(status)
+ }
+ return nil
+}
+
+// SetBufferSize sets the buffer size (in bytes) of the handle.
+func (p *InactiveHandle) SetBufferSize(bufferSize int) error {
+ if status := C.pcap_set_buffer_size(p.cptr, C.int(bufferSize)); status < 0 {
+ return statusError(status)
+ }
+ return nil
+}
+
+// SetImmediateMode sets (or unsets) the immediate mode of the
+// handle. In immediate mode, packets are delivered to the application
+// as soon as they arrive. In other words, this overrides SetTimeout.
+func (p *InactiveHandle) SetImmediateMode(mode bool) error {
+ var md C.int
+ if mode {
+ md = 1
+ }
+ if status := C.pcap_set_immediate_mode(p.cptr, md); status < 0 {
+ return statusError(status)
+ }
+ return nil
+}
diff --git a/vendor/github.com/google/gopacket/pcap/pcap_test.go b/vendor/github.com/google/gopacket/pcap/pcap_test.go
new file mode 100644
index 0000000..04f1812
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/pcap_test.go
@@ -0,0 +1,308 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package pcap
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "testing"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+func TestPcapNonexistentFile(t *testing.T) {
+ handle, err := OpenOffline("/path/to/nonexistent/file")
+ if err == nil {
+ t.Error("No error returned for nonexistent file open")
+ } else {
+ t.Logf("Error returned for nonexistent file: %v", err)
+ }
+ if handle != nil {
+ t.Error("Non-nil handle returned for nonexistent file open")
+ }
+}
+
+func TestPcapFileRead(t *testing.T) {
+ invalidData := []byte{
+ 0xAB, 0xAD, 0x1D, 0xEA,
+ }
+
+ invalidPcap, err := ioutil.TempFile("", "invalid.pcap")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(invalidPcap.Name())
+
+ err = ioutil.WriteFile(invalidPcap.Name(), invalidData, 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer invalidPcap.Close()
+
+ for _, file := range []struct {
+ filename string
+ num int
+ expectedLayers []gopacket.LayerType
+ err string
+ }{
+ {filename: "test_loopback.pcap",
+ num: 24,
+ expectedLayers: []gopacket.LayerType{
+ layers.LayerTypeLoopback,
+ layers.LayerTypeIPv6,
+ layers.LayerTypeTCP,
+ },
+ },
+ {filename: "test_ethernet.pcap",
+ num: 16,
+ expectedLayers: []gopacket.LayerType{
+ layers.LayerTypeEthernet,
+ layers.LayerTypeIPv4,
+ layers.LayerTypeTCP,
+ },
+ },
+ {filename: "test_dns.pcap",
+ num: 10,
+ expectedLayers: []gopacket.LayerType{
+ layers.LayerTypeEthernet,
+ layers.LayerTypeIPv4,
+ layers.LayerTypeUDP,
+ layers.LayerTypeDNS,
+ },
+ },
+ {filename: invalidPcap.Name(),
+ num: 0,
+ err: "unknown file format",
+ },
+ } {
+ t.Logf("\n\n\n\nProcessing file %s\n\n\n\n", file.filename)
+
+ packets := []gopacket.Packet{}
+ if handle, err := OpenOffline(file.filename); err != nil {
+ if file.err != "" {
+ if err.Error() != file.err {
+ t.Errorf("expected message %q; got %q", file.err, err.Error())
+ }
+ } else {
+ t.Fatal(err)
+ }
+ } else {
+ if file.err != "" {
+ t.Fatalf("Expected error, got none")
+ }
+ packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
+ for packet := range packetSource.Packets() {
+ packets = append(packets, packet)
+ }
+ }
+ if len(packets) != file.num {
+ t.Fatal("Incorrect number of packets, want", file.num, "got", len(packets))
+ }
+ for i, p := range packets {
+ t.Log(p.Dump())
+ for _, layertype := range file.expectedLayers {
+ if p.Layer(layertype) == nil {
+ t.Fatal("Packet", i, "has no layer type\n%s", layertype, p.Dump())
+ }
+ }
+ }
+ }
+}
+
+func TestBPF(t *testing.T) {
+ handle, err := OpenOffline("test_ethernet.pcap")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, expected := range []struct {
+ expr string
+ Error bool
+ Result bool
+ }{
+ {"foobar", true, false},
+ {"tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)", false, true},
+ {"tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack", false, true},
+ {"udp", false, false},
+ } {
+ data, ci, err := handle.ReadPacketData()
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Log("Testing filter", expected.expr)
+ if bpf, err := handle.NewBPF(expected.expr); err != nil {
+ if !expected.Error {
+ t.Error(err, "while compiling filter was unexpected")
+ }
+ } else if expected.Error {
+ t.Error("expected error but didn't see one")
+ } else if matches := bpf.Matches(ci, data); matches != expected.Result {
+ t.Error("Filter result was", matches, "but should be", expected.Result)
+ }
+ }
+}
+
+func TestBPFInstruction(t *testing.T) {
+ handle, err := OpenOffline("test_ethernet.pcap")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cntr := 0
+ oversizedBpfInstructionBuffer := [MaxBpfInstructions + 1]BPFInstruction{}
+
+ for _, expected := range []struct {
+ Filter string
+ BpfInstruction []BPFInstruction
+ Error bool
+ Result bool
+ }{
+ // {"foobar", true, false},
+ {"foobar", []BPFInstruction{}, true, false},
+
+ // tcpdump -dd 'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)'
+ {"tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)",
+ []BPFInstruction{
+ {0x28, 0, 0, 0x0000000c},
+ {0x15, 0, 9, 0x00000800},
+ {0x30, 0, 0, 0x00000017},
+ {0x15, 0, 7, 0x00000006},
+ {0x28, 0, 0, 0x00000014},
+ {0x45, 5, 0, 0x00001fff},
+ {0xb1, 0, 0, 0x0000000e},
+ {0x50, 0, 0, 0x0000001b},
+ {0x54, 0, 0, 0x00000012},
+ {0x15, 0, 1, 0x00000012},
+ {0x6, 0, 0, 0x0000ffff},
+ {0x6, 0, 0, 0x00000000},
+ }, false, true},
+
+ // tcpdump -dd 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack'
+ {"tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-ack",
+ []BPFInstruction{
+ {0x28, 0, 0, 0x0000000c},
+ {0x15, 0, 9, 0x00000800},
+ {0x30, 0, 0, 0x00000017},
+ {0x15, 0, 7, 0x00000006},
+ {0x28, 0, 0, 0x00000014},
+ {0x45, 5, 0, 0x00001fff},
+ {0xb1, 0, 0, 0x0000000e},
+ {0x50, 0, 0, 0x0000001b},
+ {0x54, 0, 0, 0x00000012},
+ {0x15, 0, 1, 0x00000010},
+ {0x6, 0, 0, 0x0000ffff},
+ {0x6, 0, 0, 0x00000000},
+ }, false, true},
+
+ // tcpdump -dd 'udp'
+ {"udp",
+ []BPFInstruction{
+ {0x28, 0, 0, 0x0000000c},
+ {0x15, 0, 5, 0x000086dd},
+ {0x30, 0, 0, 0x00000014},
+ {0x15, 6, 0, 0x00000011},
+ {0x15, 0, 6, 0x0000002c},
+ {0x30, 0, 0, 0x00000036},
+ {0x15, 3, 4, 0x00000011},
+ {0x15, 0, 3, 0x00000800},
+ {0x30, 0, 0, 0x00000017},
+ {0x15, 0, 1, 0x00000011},
+ {0x6, 0, 0, 0x0000ffff},
+ {0x6, 0, 0, 0x00000000},
+ }, false, false},
+
+ {"", oversizedBpfInstructionBuffer[:], true, false},
+ } {
+ cntr++
+ data, ci, err := handle.ReadPacketData()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Log("Testing BpfInstruction filter", cntr)
+ if bpf, err := handle.NewBPFInstructionFilter(expected.BpfInstruction); err != nil {
+ if !expected.Error {
+ t.Error(err, "while compiling filter was unexpected")
+ }
+ } else if expected.Error {
+ t.Error("expected error but didn't see one")
+ } else if matches := bpf.Matches(ci, data); matches != expected.Result {
+ t.Error("Filter result was", matches, "but should be", expected.Result)
+ }
+
+ if expected.Filter != "" {
+ t.Log("Testing dead bpf filter", cntr)
+ if bpf, err := CompileBPFFilter(layers.LinkTypeEthernet, 65535, expected.Filter); err != nil {
+ if !expected.Error {
+ t.Error(err, "while compiling filter was unexpected")
+ }
+ } else if expected.Error {
+ t.Error("expected error but didn't see one")
+ } else {
+ if len(bpf) != len(expected.BpfInstruction) {
+ t.Errorf("expected %d instructions, got %d", len(expected.BpfInstruction), len(bpf))
+ }
+ for i := 0; i < len(bpf); i++ {
+ if bpf[i] != expected.BpfInstruction[i] {
+ t.Errorf("expected instruction %d = %d, got %d", i, expected.BpfInstruction[i], bpf[i])
+ }
+ }
+ }
+ }
+ }
+}
+
+func ExampleBPF() {
+ handle, err := OpenOffline("test_ethernet.pcap")
+ if err != nil {
+ log.Fatal(err)
+ }
+ synack, err := handle.NewBPF("tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)")
+ if err != nil {
+ log.Fatal(err)
+ }
+ syn, err := handle.NewBPF("tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn")
+ if err != nil {
+ log.Fatal(err)
+ }
+ for {
+ data, ci, err := handle.ReadPacketData()
+ switch {
+ case err == io.EOF:
+ return
+ case err != nil:
+ log.Fatal(err)
+ case synack.Matches(ci, data):
+ fmt.Println("SYN/ACK packet")
+ case syn.Matches(ci, data):
+ fmt.Println("SYN packet")
+ default:
+ fmt.Println("SYN flag not set")
+ }
+ }
+ // Output:
+ // SYN packet
+ // SYN/ACK packet
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+ // SYN flag not set
+}
diff --git a/vendor/github.com/google/gopacket/pcap/pcap_tester.go b/vendor/github.com/google/gopacket/pcap/pcap_tester.go
new file mode 100644
index 0000000..ee32690
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/pcap_tester.go
@@ -0,0 +1,109 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+// +build ignore
+
+// This binary tests that PCAP packet capture is working correctly by issuing
+// HTTP requests, then making sure we actually capture data off the wire.
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "time"
+
+ "github.com/google/gopacket/pcap"
+)
+
+var mode = flag.String("mode", "basic", "One of: basic,filtered,timestamp")
+
+func generatePackets() {
+ if resp, err := http.Get("http://code.google.com"); err != nil {
+ log.Printf("Could not get HTTP: %v", err)
+ } else {
+ resp.Body.Close()
+ }
+}
+
+func main() {
+ flag.Parse()
+ ifaces, err := net.Interfaces()
+ if err != nil {
+ log.Fatal(err)
+ }
+ for _, iface := range ifaces {
+ log.Printf("Trying capture on %q", iface.Name)
+ if err := tryCapture(iface); err != nil {
+ log.Printf("Error capturing on %q: %v", iface.Name, err)
+ } else {
+ log.Printf("Successfully captured on %q", iface.Name)
+ return
+ }
+ }
+ os.Exit(1)
+}
+
+func tryCapture(iface net.Interface) error {
+ if iface.Name[:2] == "lo" {
+ return errors.New("skipping loopback")
+ }
+ var h *pcap.Handle
+ var err error
+ switch *mode {
+ case "basic":
+ h, err = pcap.OpenLive(iface.Name, 65536, false, time.Second*3)
+ if err != nil {
+ return fmt.Errorf("openlive: %v", err)
+ }
+ defer h.Close()
+ case "filtered":
+ h, err = pcap.OpenLive(iface.Name, 65536, false, time.Second*3)
+ if err != nil {
+ return fmt.Errorf("openlive: %v", err)
+ }
+ defer h.Close()
+ if err := h.SetBPFFilter("port 80 or port 443"); err != nil {
+ return fmt.Errorf("setbpf: %v", err)
+ }
+ case "timestamp":
+ u, err := pcap.NewInactiveHandle(iface.Name)
+ if err != nil {
+ return err
+ }
+ defer u.CleanUp()
+ if err = u.SetSnapLen(65536); err != nil {
+ return err
+ } else if err = u.SetPromisc(false); err != nil {
+ return err
+ } else if err = u.SetTimeout(time.Second * 3); err != nil {
+ return err
+ }
+ sources := u.SupportedTimestamps()
+ if len(sources) == 0 {
+ return errors.New("no supported timestamp sources")
+ } else if err := u.SetTimestampSource(sources[0]); err != nil {
+ return fmt.Errorf("settimestampsource(%v): %v", sources[0], err)
+ } else if h, err = u.Activate(); err != nil {
+ return fmt.Errorf("could not activate: %v", err)
+ }
+ defer h.Close()
+ default:
+ panic("Invalid --mode: " + *mode)
+ }
+ go generatePackets()
+ h.ReadPacketData() // Do one dummy read to clear any timeouts.
+ data, ci, err := h.ReadPacketData()
+ if err != nil {
+ return fmt.Errorf("readpacketdata: %v", err)
+ }
+ log.Printf("Read packet, %v bytes, CI: %+v", len(data), ci)
+ return nil
+}
diff --git a/vendor/github.com/google/gopacket/pcap/pcap_unix.go b/vendor/github.com/google/gopacket/pcap/pcap_unix.go
new file mode 100644
index 0000000..b2a6dcd
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/pcap_unix.go
@@ -0,0 +1,71 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+//
+// +build !windows
+
+package pcap
+
+/*
+#include <stdlib.h>
+#include <pcap.h>
+
+// pcap_wait returns when the next packet is available or the timeout expires.
+// Since it uses pcap_get_selectable_fd, it will not work in Windows.
+int pcap_wait(pcap_t *p, int usec) {
+ fd_set fds;
+ int fd;
+ struct timeval tv;
+
+ fd = pcap_get_selectable_fd(p);
+ if(fd < 0) {
+ return fd;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = usec;
+
+ if(usec != 0) {
+ return select(fd+1, &fds, NULL, NULL, &tv);
+ }
+
+ // block indefinitely if no timeout provided
+ return select(fd+1, &fds, NULL, NULL, NULL);
+}
+*/
+import "C"
+
+import (
+ "errors"
+ "unsafe"
+)
+
+func (p *Handle) openLive() error {
+ buf := (*C.char)(C.calloc(errorBufferSize, 1))
+ defer C.free(unsafe.Pointer(buf))
+
+ // Change the device to non-blocking, we'll use pcap_wait to wait until the
+ // handle is ready to read.
+ if v := C.pcap_setnonblock(p.cptr, 1, buf); v == -1 {
+ return errors.New(C.GoString(buf))
+ }
+
+ return nil
+}
+
+// waitForPacket waits for a packet or for the timeout to expire.
+func (p *Handle) waitForPacket() {
+ // need to wait less than the read timeout according to pcap documentation.
+ // timeoutMillis rounds up to at least one millisecond so we can safely
+ // subtract up to a millisecond.
+ usec := timeoutMillis(p.timeout) * 1000
+ usec -= 100
+
+ C.pcap_wait(p.cptr, usec)
+}
diff --git a/vendor/github.com/google/gopacket/pcap/pcap_windows.go b/vendor/github.com/google/gopacket/pcap/pcap_windows.go
new file mode 100644
index 0000000..e3df123
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/pcap_windows.go
@@ -0,0 +1,23 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package pcap
+
+import (
+ "runtime"
+)
+
+func (p *Handle) openLive() error {
+ // do nothing
+ return nil
+}
+
+// waitForPacket waits for a packet or for the timeout to expire.
+func (p *Handle) waitForPacket() {
+ // can't use select() so instead just switch goroutines
+ runtime.Gosched()
+}
diff --git a/vendor/github.com/google/gopacket/pcap/pcapgo_test.go b/vendor/github.com/google/gopacket/pcap/pcapgo_test.go
new file mode 100644
index 0000000..4de018a
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/pcapgo_test.go
@@ -0,0 +1,56 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package pcap
+
+import (
+ "bytes"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcapgo"
+ "io/ioutil"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestPCAPGoWrite(t *testing.T) {
+ f, err := ioutil.TempFile("", "pcapgo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ data := []byte{0xab, 0xcd, 0xef, 0x01, 0x02, 0x03, 0x04}
+ ci := gopacket.CaptureInfo{
+ Timestamp: time.Unix(12345667, 1234567000),
+ Length: 700,
+ CaptureLength: len(data),
+ }
+ func() {
+ defer f.Close()
+ w := pcapgo.NewWriter(f)
+ if err := w.WriteFileHeader(65536, layers.LinkTypeEthernet); err != nil {
+ t.Fatal(err)
+ }
+ if err := w.WritePacket(ci, data); err != nil {
+ t.Fatal(err)
+ }
+ }()
+ h, err := OpenOffline(f.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer h.Close()
+ gotData, gotCI, err := h.ReadPacketData()
+ if err != nil {
+ t.Fatal("could not read first packet:", err)
+ }
+ if !bytes.Equal(gotData, data) {
+ t.Errorf("byte mismatch:\nwant: %v\n got: %v", data, gotData)
+ }
+ if !reflect.DeepEqual(ci, gotCI) {
+ t.Errorf("CI mismatch:\nwant: %v\n got: %v", ci, gotCI)
+ }
+}
diff --git a/vendor/github.com/google/gopacket/pcap/test_dns.pcap b/vendor/github.com/google/gopacket/pcap/test_dns.pcap
new file mode 100644
index 0000000..3a79f92
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/test_dns.pcap
Binary files differ
diff --git a/vendor/github.com/google/gopacket/pcap/test_ethernet.pcap b/vendor/github.com/google/gopacket/pcap/test_ethernet.pcap
new file mode 100644
index 0000000..1f8a87c
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/test_ethernet.pcap
Binary files differ
diff --git a/vendor/github.com/google/gopacket/pcap/test_loopback.pcap b/vendor/github.com/google/gopacket/pcap/test_loopback.pcap
new file mode 100644
index 0000000..ddeb82c
--- /dev/null
+++ b/vendor/github.com/google/gopacket/pcap/test_loopback.pcap
Binary files differ