diff options
author | Milan Lenco <milan.lenco@pantheon.tech> | 2017-10-11 16:40:58 +0200 |
---|---|---|
committer | Milan Lenco <milan.lenco@pantheon.tech> | 2017-10-13 08:40:37 +0200 |
commit | 3f1edad4e6ba0a7876750aea55507fae14d8badf (patch) | |
tree | a473997249d9ba7deb70b1076d14e4c4ed029a43 /vendor/github.com/google/gopacket/pcap | |
parent | 8b66677c2382a8e739d437621de4473d5ec0b9f1 (diff) |
ODPM 266: Go-libmemif + 2 examples.
Change-Id: Icdb9b9eb2314eff6c96afe7996fcf2728291de4a
Signed-off-by: Milan Lenco <milan.lenco@pantheon.tech>
Diffstat (limited to 'vendor/github.com/google/gopacket/pcap')
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/doc.go | 106 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/gopacket_benchmark/benchmark.go | 247 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/pcap.go | 1005 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/pcap_test.go | 308 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/pcap_tester.go | 109 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/pcap_unix.go | 71 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/pcap_windows.go | 23 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/pcapgo_test.go | 56 | ||||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/test_dns.pcap | bin | 0 -> 1001 bytes | |||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/test_ethernet.pcap | bin | 0 -> 7672 bytes | |||
-rw-r--r-- | vendor/github.com/google/gopacket/pcap/test_loopback.pcap | bin | 0 -> 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, + ð, &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 Binary files differnew file mode 100644 index 0000000..3a79f92 --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/test_dns.pcap diff --git a/vendor/github.com/google/gopacket/pcap/test_ethernet.pcap b/vendor/github.com/google/gopacket/pcap/test_ethernet.pcap Binary files differnew file mode 100644 index 0000000..1f8a87c --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/test_ethernet.pcap diff --git a/vendor/github.com/google/gopacket/pcap/test_loopback.pcap b/vendor/github.com/google/gopacket/pcap/test_loopback.pcap Binary files differnew file mode 100644 index 0000000..ddeb82c --- /dev/null +++ b/vendor/github.com/google/gopacket/pcap/test_loopback.pcap |