// 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 #include // 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 #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 }