From 3f1edad4e6ba0a7876750aea55507fae14d8badf Mon Sep 17 00:00:00 2001 From: Milan Lenco Date: Wed, 11 Oct 2017 16:40:58 +0200 Subject: ODPM 266: Go-libmemif + 2 examples. Change-Id: Icdb9b9eb2314eff6c96afe7996fcf2728291de4a Signed-off-by: Milan Lenco --- .../google/gopacket/bsdbpf/bsd_bpf_sniffer.go | 215 +++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 vendor/github.com/google/gopacket/bsdbpf/bsd_bpf_sniffer.go (limited to 'vendor/github.com/google/gopacket/bsdbpf') diff --git a/vendor/github.com/google/gopacket/bsdbpf/bsd_bpf_sniffer.go b/vendor/github.com/google/gopacket/bsdbpf/bsd_bpf_sniffer.go new file mode 100644 index 0000000..3e1da0b --- /dev/null +++ b/vendor/github.com/google/gopacket/bsdbpf/bsd_bpf_sniffer.go @@ -0,0 +1,215 @@ +// 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 darwin dragonfly freebsd netbsd openbsd + +package bsdbpf + +import ( + "errors" + "fmt" + "syscall" + "time" + "unsafe" + + "github.com/google/gopacket" + "golang.org/x/sys/unix" +) + +const wordSize = int(unsafe.Sizeof(uintptr(0))) + +func bpfWordAlign(x int) int { + return (((x) + (wordSize - 1)) &^ (wordSize - 1)) +} + +// Options is used to configure various properties of the BPF sniffer. +// Default values are used when a nil Options pointer is passed to NewBPFSniffer. +type Options struct { + // BPFDeviceName is name of the bpf device to use for sniffing + // the network device. The default value of BPFDeviceName is empty string + // which causes the first available BPF device file /dev/bpfX to be used. + BPFDeviceName string + // ReadBufLen specifies the size of the buffer used to read packets + // off the wire such that multiple packets are buffered with each read syscall. + // Note that an individual packet larger than the buffer size is necessarily truncated. + // A larger buffer should increase performance because fewer read syscalls would be made. + // If zero is used, the system's default buffer length will be used which depending on the + // system may default to 4096 bytes which is not big enough to accomodate some link layers + // such as WLAN (802.11). + // ReadBufLen defaults to 32767... however typical BSD manual pages for BPF indicate that + // if the requested buffer size cannot be accommodated, the closest allowable size will be + // set and returned... hence our GetReadBufLen method. + ReadBufLen int + // Timeout is the length of time to wait before timing out on a read request. + // Timeout defaults to nil which means no timeout is used. + Timeout *syscall.Timeval + // Promisc is set to true for promiscuous mode ethernet sniffing. + // Promisc defaults to true. + Promisc bool + // Immediate is set to true to make our read requests return as soon as a packet becomes available. + // Otherwise, a read will block until either the kernel buffer becomes full or a timeout occurs. + // The default is true. + Immediate bool + // PreserveLinkAddr is set to false if the link level source address should be filled in automatically + // by the interface output routine. Set to true if the link level source address will be written, + // as provided, to the wire. + // The default is true. + PreserveLinkAddr bool +} + +var defaultOptions = Options{ + BPFDeviceName: "", + ReadBufLen: 32767, + Timeout: nil, + Promisc: true, + Immediate: true, + PreserveLinkAddr: true, +} + +// BPFSniffer is a struct used to track state of a BSD BPF ethernet sniffer +// such that gopacket's PacketDataSource interface is implemented. +type BPFSniffer struct { + options *Options + sniffDeviceName string + fd int + readBuffer []byte + lastReadLen int + readBytesConsumed int +} + +// NewBPFSniffer is used to create BSD-only BPF ethernet sniffer +// iface is the network interface device name that you wish to sniff +// options can set to nil in order to utilize default values for everything. +// Each field of Options also have a default setting if left unspecified by +// the user's custome Options struct. +func NewBPFSniffer(iface string, options *Options) (*BPFSniffer, error) { + var err error + enable := 1 + sniffer := BPFSniffer{ + sniffDeviceName: iface, + } + if options == nil { + sniffer.options = &defaultOptions + } else { + sniffer.options = options + } + + if sniffer.options.BPFDeviceName == "" { + sniffer.pickBpfDevice() + } + + // setup our read buffer + if sniffer.options.ReadBufLen == 0 { + sniffer.options.ReadBufLen, err = syscall.BpfBuflen(sniffer.fd) + if err != nil { + return nil, err + } + } else { + sniffer.options.ReadBufLen, err = syscall.SetBpfBuflen(sniffer.fd, sniffer.options.ReadBufLen) + if err != nil { + return nil, err + } + } + sniffer.readBuffer = make([]byte, sniffer.options.ReadBufLen) + + err = syscall.SetBpfInterface(sniffer.fd, sniffer.sniffDeviceName) + if err != nil { + return nil, err + } + + if sniffer.options.Immediate { + // turn immediate mode on. This makes the snffer non-blocking. + err = syscall.SetBpfImmediate(sniffer.fd, enable) + if err != nil { + return nil, err + } + } + + // the above call to syscall.SetBpfImmediate needs to be made + // before setting a timer otherwise the reads will block for the + // entire timer duration even if there are packets to return. + if sniffer.options.Timeout != nil { + err = syscall.SetBpfTimeout(sniffer.fd, sniffer.options.Timeout) + if err != nil { + return nil, err + } + } + + if sniffer.options.PreserveLinkAddr { + // preserves the link level source address... + // higher level protocol analyzers will not need this + err = syscall.SetBpfHeadercmpl(sniffer.fd, enable) + if err != nil { + return nil, err + } + } + + if sniffer.options.Promisc { + // forces the interface into promiscuous mode + err = syscall.SetBpfPromisc(sniffer.fd, enable) + if err != nil { + return nil, err + } + } + + return &sniffer, nil +} + +// Close is used to close the file-descriptor of the BPF device file. +func (b *BPFSniffer) Close() error { + return syscall.Close(b.fd) +} + +func (b *BPFSniffer) pickBpfDevice() { + var err error + b.options.BPFDeviceName = "" + for i := 0; i < 99; i++ { + b.options.BPFDeviceName = fmt.Sprintf("/dev/bpf%d", i) + b.fd, err = syscall.Open(b.options.BPFDeviceName, syscall.O_RDWR, 0) + if err == nil { + return + } + } + panic("failed to acquire a BPF device for read-write access") +} + +func (b *BPFSniffer) ReadPacketData() ([]byte, gopacket.CaptureInfo, error) { + var err error + if b.readBytesConsumed >= b.lastReadLen { + b.readBytesConsumed = 0 + b.readBuffer = make([]byte, b.options.ReadBufLen) + b.lastReadLen, err = syscall.Read(b.fd, b.readBuffer) + if err != nil { + b.lastReadLen = 0 + return nil, gopacket.CaptureInfo{}, err + } + } + hdr := (*unix.BpfHdr)(unsafe.Pointer(&b.readBuffer[b.readBytesConsumed])) + frameStart := b.readBytesConsumed + int(hdr.Hdrlen) + b.readBytesConsumed += bpfWordAlign(int(hdr.Hdrlen) + int(hdr.Caplen)) + + if frameStart+int(hdr.Caplen) > len(b.readBuffer) { + captureInfo := gopacket.CaptureInfo{ + Timestamp: time.Unix(int64(hdr.Tstamp.Sec), int64(hdr.Tstamp.Usec)*1000), + CaptureLength: 0, + Length: 0, + } + return nil, captureInfo, errors.New("BPF captured frame received with corrupted BpfHdr struct.") + } + + rawFrame := b.readBuffer[frameStart : frameStart+int(hdr.Caplen)] + captureInfo := gopacket.CaptureInfo{ + Timestamp: time.Unix(int64(hdr.Tstamp.Sec), int64(hdr.Tstamp.Usec)*1000), + CaptureLength: len(rawFrame), + Length: len(rawFrame), + } + return rawFrame, captureInfo, nil +} + +// GetReadBufLen returns the BPF read buffer length +func (b *BPFSniffer) GetReadBufLen() int { + return b.options.ReadBufLen +} -- cgit 1.2.3-korg