aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/gopacket/afpacket
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/google/gopacket/afpacket')
-rw-r--r--vendor/github.com/google/gopacket/afpacket/afpacket.go476
-rw-r--r--vendor/github.com/google/gopacket/afpacket/afpacket_test.go40
-rw-r--r--vendor/github.com/google/gopacket/afpacket/header.go158
-rw-r--r--vendor/github.com/google/gopacket/afpacket/options.go171
-rw-r--r--vendor/github.com/google/gopacket/afpacket/sockopt_linux.go58
-rw-r--r--vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.go57
-rw-r--r--vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.s8
7 files changed, 968 insertions, 0 deletions
diff --git a/vendor/github.com/google/gopacket/afpacket/afpacket.go b/vendor/github.com/google/gopacket/afpacket/afpacket.go
new file mode 100644
index 0000000..13937c1
--- /dev/null
+++ b/vendor/github.com/google/gopacket/afpacket/afpacket.go
@@ -0,0 +1,476 @@
+// 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 linux
+
+// Package afpacket provides Go bindings for MMap'd AF_PACKET socket reading.
+package afpacket
+
+// Couldn't have done this without:
+// http://lxr.free-electrons.com/source/Documentation/networking/packet_mmap.txt
+// http://codemonkeytips.blogspot.co.uk/2011/07/asynchronous-packet-socket-reading-with.html
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "runtime"
+ "sync"
+ "sync/atomic"
+ "time"
+ "unsafe"
+
+ "golang.org/x/net/bpf"
+ "golang.org/x/sys/unix"
+
+ "github.com/google/gopacket"
+)
+
+/*
+#include <linux/if_packet.h> // AF_PACKET, sockaddr_ll
+#include <linux/if_ether.h> // ETH_P_ALL
+#include <sys/socket.h> // socket()
+#include <unistd.h> // close()
+#include <arpa/inet.h> // htons()
+#include <sys/mman.h> // mmap(), munmap()
+#include <poll.h> // poll()
+*/
+import "C"
+
+var pageSize = unix.Getpagesize()
+var tpacketAlignment = uint(C.TPACKET_ALIGNMENT)
+
+// ErrPoll returned by poll
+var ErrPoll = errors.New("packet poll failed")
+
+// ErrTimeout returned on poll timeout
+var ErrTimeout = errors.New("packet poll timeout expired")
+
+func tpacketAlign(v int) int {
+ return int((uint(v) + tpacketAlignment - 1) & ((^tpacketAlignment) - 1))
+}
+
+// Stats is a set of counters detailing the work TPacket has done so far.
+type Stats struct {
+ // Packets is the total number of packets returned to the caller.
+ Packets int64
+ // Polls is the number of blocking syscalls made waiting for packets.
+ // This should always be <= Packets, since with TPacket one syscall
+ // can (and often does) return many results.
+ Polls int64
+}
+
+// SocketStats is a struct where socket stats are stored
+type SocketStats C.struct_tpacket_stats
+
+// SocketStatsV3 is a struct where socket stats for TPacketV3 are stored
+type SocketStatsV3 C.struct_tpacket_stats_v3
+
+// TPacket implements packet receiving for Linux AF_PACKET versions 1, 2, and 3.
+type TPacket struct {
+ // fd is the C file descriptor.
+ fd int
+ // ring points to the memory space of the ring buffer shared by tpacket and the kernel.
+ ring []byte
+ // rawring is the unsafe pointer that we use to poll for packets
+ rawring unsafe.Pointer
+ // opts contains read-only options for the TPacket object.
+ opts options
+ mu sync.Mutex // guards below
+ // offset is the offset into the ring of the current header.
+ offset int
+ // current is the current header.
+ current header
+ // pollset is used by TPacket for its poll() call.
+ pollset unix.PollFd
+ // shouldReleasePacket is set to true whenever we return packet data, to make sure we remember to release that data back to the kernel.
+ shouldReleasePacket bool
+ // headerNextNeeded is set to true when header need to move to the next packet. No need to move it case of poll error.
+ headerNextNeeded bool
+ // tpVersion is the version of TPacket actually in use, set by setRequestedTPacketVersion.
+ tpVersion OptTPacketVersion
+ // Hackity hack hack hack. We need to return a pointer to the header with
+ // getTPacketHeader, and we don't want to allocate a v3wrapper every time,
+ // so we leave it in the TPacket object and return a pointer to it.
+ v3 v3wrapper
+
+ statsMu sync.Mutex // guards stats below
+ // stats is simple statistics on TPacket's run.
+ stats Stats
+ // socketStats contains stats from the socket
+ socketStats SocketStats
+ // same as socketStats, but with an extra field freeze_q_cnt
+ socketStatsV3 SocketStatsV3
+}
+
+var _ gopacket.ZeroCopyPacketDataSource = &TPacket{}
+
+// bindToInterface binds the TPacket socket to a particular named interface.
+func (h *TPacket) bindToInterface(ifaceName string) error {
+ ifIndex := 0
+ // An empty string here means to listen to all interfaces
+ if ifaceName != "" {
+ iface, err := net.InterfaceByName(ifaceName)
+ if err != nil {
+ return fmt.Errorf("InterfaceByName: %v", err)
+ }
+ ifIndex = iface.Index
+ }
+ s := &unix.SockaddrLinklayer{
+ Protocol: htons(uint16(unix.ETH_P_ALL)),
+ Ifindex: ifIndex,
+ }
+ return unix.Bind(h.fd, s)
+}
+
+// setTPacketVersion asks the kernel to set TPacket to a particular version, and returns an error on failure.
+func (h *TPacket) setTPacketVersion(version OptTPacketVersion) error {
+ if err := unix.SetsockoptInt(h.fd, unix.SOL_PACKET, unix.PACKET_VERSION, int(version)); err != nil {
+ return fmt.Errorf("setsockopt packet_version: %v", err)
+ }
+ return nil
+}
+
+// setRequestedTPacketVersion tries to set TPacket to the requested version or versions.
+func (h *TPacket) setRequestedTPacketVersion() error {
+ switch {
+ case (h.opts.version == TPacketVersionHighestAvailable || h.opts.version == TPacketVersion3) && h.setTPacketVersion(TPacketVersion3) == nil:
+ h.tpVersion = TPacketVersion3
+ case (h.opts.version == TPacketVersionHighestAvailable || h.opts.version == TPacketVersion2) && h.setTPacketVersion(TPacketVersion2) == nil:
+ h.tpVersion = TPacketVersion2
+ case (h.opts.version == TPacketVersionHighestAvailable || h.opts.version == TPacketVersion1) && h.setTPacketVersion(TPacketVersion1) == nil:
+ h.tpVersion = TPacketVersion1
+ default:
+ return errors.New("no known tpacket versions work on this machine")
+ }
+ return nil
+}
+
+// setUpRing sets up the shared-memory ring buffer between the user process and the kernel.
+func (h *TPacket) setUpRing() (err error) {
+ totalSize := int(h.opts.framesPerBlock * h.opts.numBlocks * h.opts.frameSize)
+ switch h.tpVersion {
+ case TPacketVersion1, TPacketVersion2:
+ var tp C.struct_tpacket_req
+ tp.tp_block_size = C.uint(h.opts.blockSize)
+ tp.tp_block_nr = C.uint(h.opts.numBlocks)
+ tp.tp_frame_size = C.uint(h.opts.frameSize)
+ tp.tp_frame_nr = C.uint(h.opts.framesPerBlock * h.opts.numBlocks)
+ if err := setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_RX_RING, unsafe.Pointer(&tp), unsafe.Sizeof(tp)); err != nil {
+ return fmt.Errorf("setsockopt packet_rx_ring: %v", err)
+ }
+ case TPacketVersion3:
+ var tp C.struct_tpacket_req3
+ tp.tp_block_size = C.uint(h.opts.blockSize)
+ tp.tp_block_nr = C.uint(h.opts.numBlocks)
+ tp.tp_frame_size = C.uint(h.opts.frameSize)
+ tp.tp_frame_nr = C.uint(h.opts.framesPerBlock * h.opts.numBlocks)
+ tp.tp_retire_blk_tov = C.uint(h.opts.blockTimeout / time.Millisecond)
+ if err := setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_RX_RING, unsafe.Pointer(&tp), unsafe.Sizeof(tp)); err != nil {
+ return fmt.Errorf("setsockopt packet_rx_ring v3: %v", err)
+ }
+ default:
+ return errors.New("invalid tpVersion")
+ }
+ h.ring, err = unix.Mmap(h.fd, 0, totalSize, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
+ if err != nil {
+ return err
+ }
+ if h.ring == nil {
+ return errors.New("no ring")
+ }
+ h.rawring = unsafe.Pointer(&h.ring[0])
+ return nil
+}
+
+// Close cleans up the TPacket. It should not be used after the Close call.
+func (h *TPacket) Close() {
+ if h.fd == -1 {
+ return // already closed.
+ }
+ if h.ring != nil {
+ unix.Munmap(h.ring)
+ }
+ h.ring = nil
+ unix.Close(h.fd)
+ h.fd = -1
+ runtime.SetFinalizer(h, nil)
+}
+
+// NewTPacket returns a new TPacket object for reading packets off the wire.
+// Its behavior may be modified by passing in any/all of afpacket.Opt* to this
+// function.
+// If this function succeeds, the user should be sure to Close the returned
+// TPacket when finished with it.
+func NewTPacket(opts ...interface{}) (h *TPacket, err error) {
+ h = &TPacket{}
+ if h.opts, err = parseOptions(opts...); err != nil {
+ return nil, err
+ }
+ fd, err := unix.Socket(unix.AF_PACKET, int(h.opts.socktype), int(htons(unix.ETH_P_ALL)))
+ if err != nil {
+ return nil, err
+ }
+ h.fd = fd
+ if err = h.bindToInterface(h.opts.iface); err != nil {
+ goto errlbl
+ }
+ if err = h.setRequestedTPacketVersion(); err != nil {
+ goto errlbl
+ }
+ if err = h.setUpRing(); err != nil {
+ goto errlbl
+ }
+ // Clear stat counter from socket
+ if err = h.InitSocketStats(); err != nil {
+ goto errlbl
+ }
+ runtime.SetFinalizer(h, (*TPacket).Close)
+ return h, nil
+errlbl:
+ h.Close()
+ return nil, err
+}
+
+// SetBPF attaches a BPF filter to the underlying socket
+func (h *TPacket) SetBPF(filter []bpf.RawInstruction) error {
+ var p unix.SockFprog
+ if len(filter) > int(^uint16(0)) {
+ return errors.New("filter too large")
+ }
+ p.Len = uint16(len(filter))
+ p.Filter = (*unix.SockFilter)(unsafe.Pointer(&filter[0]))
+
+ return setsockopt(h.fd, unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, unsafe.Pointer(&p), unix.SizeofSockFprog)
+}
+
+func (h *TPacket) releaseCurrentPacket() error {
+ h.current.clearStatus()
+ h.offset++
+ h.shouldReleasePacket = false
+ return nil
+}
+
+// ZeroCopyReadPacketData reads the next packet off the wire, and returns its data.
+// The slice returned by ZeroCopyReadPacketData points to bytes owned by the
+// TPacket. 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.
+// tp, _ := NewTPacket(...)
+// data1, _, _ := tp.ZeroCopyReadPacketData()
+// // do everything you want with data1 here, copying bytes out of it if you'd like to keep them around.
+// data2, _, _ := tp.ZeroCopyReadPacketData() // invalidates bytes in data1
+func (h *TPacket) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
+ h.mu.Lock()
+retry:
+ if h.current == nil || !h.headerNextNeeded || !h.current.next() {
+ if h.shouldReleasePacket {
+ h.releaseCurrentPacket()
+ }
+ h.current = h.getTPacketHeader()
+ if err = h.pollForFirstPacket(h.current); err != nil {
+ h.headerNextNeeded = false
+ h.mu.Unlock()
+ return
+ }
+ // We received an empty block
+ if h.current.getLength() == 0 {
+ goto retry
+ }
+ }
+ data = h.current.getData()
+ ci.Timestamp = h.current.getTime()
+ ci.CaptureLength = len(data)
+ ci.Length = h.current.getLength()
+ ci.InterfaceIndex = h.current.getIfaceIndex()
+ atomic.AddInt64(&h.stats.Packets, 1)
+ h.headerNextNeeded = true
+ h.mu.Unlock()
+
+ return
+}
+
+// Stats returns statistics on the packets the TPacket has seen so far.
+func (h *TPacket) Stats() (Stats, error) {
+ return Stats{
+ Polls: atomic.LoadInt64(&h.stats.Polls),
+ Packets: atomic.LoadInt64(&h.stats.Packets),
+ }, nil
+}
+
+// InitSocketStats clears socket counters and return empty stats.
+func (h *TPacket) InitSocketStats() error {
+ if h.tpVersion == TPacketVersion3 {
+ socklen := unsafe.Sizeof(h.socketStatsV3)
+ slt := C.socklen_t(socklen)
+ var ssv3 SocketStatsV3
+
+ err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ssv3), uintptr(unsafe.Pointer(&slt)))
+ if err != nil {
+ return err
+ }
+ h.socketStatsV3 = SocketStatsV3{}
+ } else {
+ socklen := unsafe.Sizeof(h.socketStats)
+ slt := C.socklen_t(socklen)
+ var ss SocketStats
+
+ err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ss), uintptr(unsafe.Pointer(&slt)))
+ if err != nil {
+ return err
+ }
+ h.socketStats = SocketStats{}
+ }
+ return nil
+}
+
+// SocketStats saves stats from the socket to the TPacket instance.
+func (h *TPacket) SocketStats() (SocketStats, SocketStatsV3, error) {
+ h.statsMu.Lock()
+ defer h.statsMu.Unlock()
+ // We need to save the counters since asking for the stats will clear them
+ if h.tpVersion == TPacketVersion3 {
+ socklen := unsafe.Sizeof(h.socketStatsV3)
+ slt := C.socklen_t(socklen)
+ var ssv3 SocketStatsV3
+
+ err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ssv3), uintptr(unsafe.Pointer(&slt)))
+ if err != nil {
+ return SocketStats{}, SocketStatsV3{}, err
+ }
+
+ h.socketStatsV3.tp_packets += ssv3.tp_packets
+ h.socketStatsV3.tp_drops += ssv3.tp_drops
+ h.socketStatsV3.tp_freeze_q_cnt += ssv3.tp_freeze_q_cnt
+ return h.socketStats, h.socketStatsV3, nil
+ }
+ socklen := unsafe.Sizeof(h.socketStats)
+ slt := C.socklen_t(socklen)
+ var ss SocketStats
+
+ err := getsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS, unsafe.Pointer(&ss), uintptr(unsafe.Pointer(&slt)))
+ if err != nil {
+ return SocketStats{}, SocketStatsV3{}, err
+ }
+
+ h.socketStats.tp_packets += ss.tp_packets
+ h.socketStats.tp_drops += ss.tp_drops
+ return h.socketStats, h.socketStatsV3, nil
+}
+
+// ReadPacketDataTo reads packet data into a user-supplied buffer.
+// This function reads up to the length of the passed-in slice.
+// The number of bytes read into data will be returned in ci.CaptureLength,
+// which is the minimum of the size of the passed-in buffer and the size of
+// the captured packet.
+func (h *TPacket) ReadPacketDataTo(data []byte) (ci gopacket.CaptureInfo, err error) {
+ var d []byte
+ d, ci, err = h.ZeroCopyReadPacketData()
+ if err != nil {
+ return
+ }
+ ci.CaptureLength = copy(data, d)
+ return
+}
+
+// ReadPacketData reads the next packet, copies it into a new buffer, and returns
+// that buffer. Since the buffer is allocated by ReadPacketData, it is safe for long-term
+// use. This implements gopacket.PacketDataSource.
+func (h *TPacket) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
+ var d []byte
+ d, ci, err = h.ZeroCopyReadPacketData()
+ if err != nil {
+ return
+ }
+ data = make([]byte, len(d))
+ copy(data, d)
+ return
+}
+
+func (h *TPacket) getTPacketHeader() header {
+ switch h.tpVersion {
+ case TPacketVersion1:
+ if h.offset >= h.opts.framesPerBlock*h.opts.numBlocks {
+ h.offset = 0
+ }
+ position := uintptr(h.rawring) + uintptr(h.opts.frameSize*h.offset)
+ return (*v1header)(unsafe.Pointer(position))
+ case TPacketVersion2:
+ if h.offset >= h.opts.framesPerBlock*h.opts.numBlocks {
+ h.offset = 0
+ }
+ position := uintptr(h.rawring) + uintptr(h.opts.frameSize*h.offset)
+ return (*v2header)(unsafe.Pointer(position))
+ case TPacketVersion3:
+ // TPacket3 uses each block to return values, instead of each frame. Hence we need to rotate when we hit #blocks, not #frames.
+ if h.offset >= h.opts.numBlocks {
+ h.offset = 0
+ }
+ position := uintptr(h.rawring) + uintptr(h.opts.frameSize*h.offset*h.opts.framesPerBlock)
+ h.v3 = initV3Wrapper(unsafe.Pointer(position))
+ return &h.v3
+ }
+ panic("handle tpacket version is invalid")
+}
+
+func (h *TPacket) pollForFirstPacket(hdr header) error {
+ tm := int(h.opts.pollTimeout / time.Millisecond)
+ for hdr.getStatus()&C.TP_STATUS_USER == 0 {
+ h.pollset.Fd = int32(h.fd)
+ h.pollset.Events = unix.POLLIN
+ h.pollset.Revents = 0
+ n, err := unix.Poll([]unix.PollFd{h.pollset}, tm)
+ if n == 0 {
+ return ErrTimeout
+ }
+
+ atomic.AddInt64(&h.stats.Polls, 1)
+ if h.pollset.Revents&unix.POLLERR > 0 {
+ return ErrPoll
+ }
+ if err != nil {
+ return err
+ }
+ }
+
+ h.shouldReleasePacket = true
+ return nil
+}
+
+// FanoutType determines the type of fanout to use with a TPacket SetFanout call.
+type FanoutType int
+
+// FanoutType values.
+const (
+ FanoutHash FanoutType = 0
+ // It appears that defrag only works with FanoutHash, see:
+ // http://lxr.free-electrons.com/source/net/packet/af_packet.c#L1204
+ FanoutHashWithDefrag FanoutType = 0x8000
+ FanoutLoadBalance FanoutType = 1
+ FanoutCPU FanoutType = 2
+)
+
+// SetFanout activates TPacket's fanout ability.
+// Use of Fanout requires creating multiple TPacket objects and the same id/type to
+// a SetFanout call on each. Note that this can be done cross-process, so if two
+// different processes both call SetFanout with the same type/id, they'll share
+// packets between them. The same should work for multiple TPacket objects within
+// the same process.
+func (h *TPacket) SetFanout(t FanoutType, id uint16) error {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ arg := C.int(t) << 16
+ arg |= C.int(id)
+ return setsockopt(h.fd, unix.SOL_PACKET, unix.PACKET_FANOUT, unsafe.Pointer(&arg), unsafe.Sizeof(arg))
+}
+
+// WritePacketData transmits a raw packet.
+func (h *TPacket) WritePacketData(pkt []byte) error {
+ _, err := unix.Write(h.fd, pkt)
+ return err
+}
diff --git a/vendor/github.com/google/gopacket/afpacket/afpacket_test.go b/vendor/github.com/google/gopacket/afpacket/afpacket_test.go
new file mode 100644
index 0000000..57f6480
--- /dev/null
+++ b/vendor/github.com/google/gopacket/afpacket/afpacket_test.go
@@ -0,0 +1,40 @@
+// 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 linux
+
+package afpacket
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestParseOptions(t *testing.T) {
+ wanted1 := defaultOpts
+ wanted1.frameSize = 1 << 10
+ wanted1.framesPerBlock = wanted1.blockSize / wanted1.frameSize
+ for i, test := range []struct {
+ opts []interface{}
+ want options
+ err bool
+ }{
+ {opts: []interface{}{OptBlockSize(2)}, err: true},
+ {opts: []interface{}{OptFrameSize(333)}, err: true},
+ {opts: []interface{}{OptTPacketVersion(-3)}, err: true},
+ {opts: []interface{}{OptTPacketVersion(5)}, err: true},
+ {opts: []interface{}{OptFrameSize(1 << 10)}, want: wanted1},
+ } {
+ got, err := parseOptions(test.opts...)
+ t.Logf("got: %#v\nerr: %v", got, err)
+ if test.err && err == nil || !test.err && err != nil {
+ t.Errorf("%d error mismatch, want error? %v. error: %v", i, test.err, err)
+ }
+ if !test.err && !reflect.DeepEqual(test.want, got) {
+ t.Errorf("%d opts mismatch, want\n%#v", i, test.want)
+ }
+ }
+}
diff --git a/vendor/github.com/google/gopacket/afpacket/header.go b/vendor/github.com/google/gopacket/afpacket/header.go
new file mode 100644
index 0000000..0b9918e
--- /dev/null
+++ b/vendor/github.com/google/gopacket/afpacket/header.go
@@ -0,0 +1,158 @@
+// 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 linux
+
+package afpacket
+
+import (
+ "reflect"
+ "time"
+ "unsafe"
+)
+
+// #include <linux/if_packet.h>
+import "C"
+
+// Our model of handling all TPacket versions is a little hacky, to say the
+// least. We use the header interface to handle interactions with the
+// tpacket1/tpacket2 packet header AND the tpacket3 block header. The big
+// difference is that tpacket3's block header implements the next() call to get
+// the next packet within the block, while v1/v2 just always return false.
+
+type header interface {
+ // getStatus returns the TPacket status of the current header.
+ getStatus() int
+ // clearStatus clears the status of the current header, releasing its
+ // underlying data back to the kernel for future use with new packets.
+ // Using the header after calling clearStatus is an error. clearStatus
+ // should only be called after next() returns false.
+ clearStatus()
+ // getTime returns the timestamp for the current packet pointed to by
+ // the header.
+ getTime() time.Time
+ // getData returns the packet data pointed to by the current header.
+ getData() []byte
+ // getLength returns the total length of the packet.
+ getLength() int
+ // getIfaceIndex returns the index of the network interface
+ // where the packet was seen. The index can later be translated to a name.
+ getIfaceIndex() int
+ // next moves this header to point to the next packet it contains,
+ // returning true on success (in which case getTime and getData will
+ // return values for the new packet) or false if there are no more
+ // packets (in which case clearStatus should be called).
+ next() bool
+}
+
+func tpAlign(x int) int {
+ return int((uint(x) + tpacketAlignment - 1) &^ (tpacketAlignment - 1))
+}
+
+type v1header C.struct_tpacket_hdr
+type v2header C.struct_tpacket2_hdr
+
+func makeSlice(start uintptr, length int) (data []byte) {
+ slice := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ slice.Data = start
+ slice.Len = length
+ slice.Cap = length
+ return
+}
+
+func (h *v1header) getStatus() int {
+ return int(h.tp_status)
+}
+func (h *v1header) clearStatus() {
+ h.tp_status = 0
+}
+func (h *v1header) getTime() time.Time {
+ return time.Unix(int64(h.tp_sec), int64(h.tp_usec)*1000)
+}
+func (h *v1header) getData() []byte {
+ return makeSlice(uintptr(unsafe.Pointer(h))+uintptr(h.tp_mac), int(h.tp_snaplen))
+}
+func (h *v1header) getLength() int {
+ return int(h.tp_len)
+}
+func (h *v1header) getIfaceIndex() int {
+ ll := (*C.struct_sockaddr_ll)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(int(C.sizeof_struct_tpacket_hdr)))))
+ return int(ll.sll_ifindex)
+}
+func (h *v1header) next() bool {
+ return false
+}
+
+func (h *v2header) getStatus() int {
+ return int(h.tp_status)
+}
+func (h *v2header) clearStatus() {
+ h.tp_status = 0
+}
+func (h *v2header) getTime() time.Time {
+ return time.Unix(int64(h.tp_sec), int64(h.tp_nsec))
+}
+func (h *v2header) getData() []byte {
+ return makeSlice(uintptr(unsafe.Pointer(h))+uintptr(h.tp_mac), int(h.tp_snaplen))
+}
+func (h *v2header) getLength() int {
+ return int(h.tp_len)
+}
+func (h *v2header) getIfaceIndex() int {
+ ll := (*C.struct_sockaddr_ll)(unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(tpAlign(int(C.sizeof_struct_tpacket2_hdr)))))
+ return int(ll.sll_ifindex)
+}
+func (h *v2header) next() bool {
+ return false
+}
+
+type v3wrapper struct {
+ block *C.struct_tpacket_block_desc
+ blockhdr *C.struct_tpacket_hdr_v1
+ packet *C.struct_tpacket3_hdr
+ used C.__u32
+}
+
+func initV3Wrapper(block unsafe.Pointer) (w v3wrapper) {
+ w.block = (*C.struct_tpacket_block_desc)(block)
+ w.blockhdr = (*C.struct_tpacket_hdr_v1)(unsafe.Pointer(&w.block.hdr[0]))
+ w.packet = (*C.struct_tpacket3_hdr)(unsafe.Pointer(uintptr(block) + uintptr(w.blockhdr.offset_to_first_pkt)))
+ return
+}
+func (w *v3wrapper) getStatus() int {
+ return int(w.blockhdr.block_status)
+}
+func (w *v3wrapper) clearStatus() {
+ w.blockhdr.block_status = 0
+}
+func (w *v3wrapper) getTime() time.Time {
+ return time.Unix(int64(w.packet.tp_sec), int64(w.packet.tp_nsec))
+}
+func (w *v3wrapper) getData() []byte {
+ return makeSlice(uintptr(unsafe.Pointer(w.packet))+uintptr(w.packet.tp_mac), int(w.packet.tp_snaplen))
+}
+func (w *v3wrapper) getLength() int {
+ return int(w.packet.tp_len)
+}
+func (w *v3wrapper) getIfaceIndex() int {
+ ll := (*C.struct_sockaddr_ll)(unsafe.Pointer(uintptr(unsafe.Pointer(w.packet)) + uintptr(tpAlign(int(C.sizeof_struct_tpacket3_hdr)))))
+ return int(ll.sll_ifindex)
+}
+func (w *v3wrapper) next() bool {
+ w.used++
+ if w.used >= w.blockhdr.num_pkts {
+ return false
+ }
+
+ next := uintptr(unsafe.Pointer(w.packet))
+ if w.packet.tp_next_offset != 0 {
+ next += uintptr(w.packet.tp_next_offset)
+ } else {
+ next += uintptr(tpacketAlign(int(w.packet.tp_snaplen) + int(w.packet.tp_mac)))
+ }
+ w.packet = (*C.struct_tpacket3_hdr)(unsafe.Pointer(next))
+ return true
+}
diff --git a/vendor/github.com/google/gopacket/afpacket/options.go b/vendor/github.com/google/gopacket/afpacket/options.go
new file mode 100644
index 0000000..c5ab771
--- /dev/null
+++ b/vendor/github.com/google/gopacket/afpacket/options.go
@@ -0,0 +1,171 @@
+// 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 linux
+
+package afpacket
+
+import (
+ "errors"
+ "fmt"
+ "time"
+)
+
+// #include <linux/if_packet.h>
+// #include <sys/socket.h>
+import "C"
+
+// OptTPacketVersion is the version of TPacket to use.
+// It can be passed into NewTPacket.
+type OptTPacketVersion int
+
+// String returns a string representation of the version, generally of the form V#.
+func (t OptTPacketVersion) String() string {
+ switch t {
+ case TPacketVersion1:
+ return "V1"
+ case TPacketVersion2:
+ return "V2"
+ case TPacketVersion3:
+ return "V3"
+ case TPacketVersionHighestAvailable:
+ return "HighestAvailable"
+ }
+ return "InvalidVersion"
+}
+
+// OptSocketType is the socket type used to open the TPacket socket.
+type OptSocketType int
+
+func (t OptSocketType) String() string {
+ switch t {
+ case SocketRaw:
+ return "SOCK_RAW"
+ case SocketDgram:
+ return "SOCK_DGRAM"
+ }
+ return "UnknownSocketType"
+}
+
+// TPacket version numbers for use with NewHandle.
+const (
+ // TPacketVersionHighestAvailable tells NewHandle to use the highest available version of tpacket the kernel has available.
+ // This is the default, should a version number not be given in NewHandle's options.
+ TPacketVersionHighestAvailable = OptTPacketVersion(-1)
+ TPacketVersion1 = OptTPacketVersion(C.TPACKET_V1)
+ TPacketVersion2 = OptTPacketVersion(C.TPACKET_V2)
+ TPacketVersion3 = OptTPacketVersion(C.TPACKET_V3)
+ tpacketVersionMax = TPacketVersion3
+ tpacketVersionMin = -1
+ // SocketRaw is the default socket type. It returns packet data
+ // including the link layer (ethernet headers, etc).
+ SocketRaw = OptSocketType(C.SOCK_RAW)
+ // SocketDgram strips off the link layer when reading packets, and adds
+ // the link layer back automatically on packet writes (coming soon...)
+ SocketDgram = OptSocketType(C.SOCK_DGRAM)
+)
+
+// OptInterface is the specific interface to bind to.
+// It can be passed into NewTPacket.
+type OptInterface string
+
+// OptFrameSize is TPacket's tp_frame_size
+// It can be passed into NewTPacket.
+type OptFrameSize int
+
+// OptBlockSize is TPacket's tp_block_size
+// It can be passed into NewTPacket.
+type OptBlockSize int
+
+// OptNumBlocks is TPacket's tp_block_nr
+// It can be passed into NewTPacket.
+type OptNumBlocks int
+
+// OptBlockTimeout is TPacket v3's tp_retire_blk_tov. Note that it has only millisecond granularity, so must be >= 1 ms.
+// It can be passed into NewTPacket.
+type OptBlockTimeout time.Duration
+
+// OptPollTimeout is the number of milliseconds that poll() should block waiting for a file
+// descriptor to become ready. Specifying a negative value in time‐out means an infinite timeout.
+type OptPollTimeout time.Duration
+
+// Default constants used by options.
+const (
+ DefaultFrameSize = 4096 // Default value for OptFrameSize.
+ DefaultBlockSize = DefaultFrameSize * 128 // Default value for OptBlockSize.
+ DefaultNumBlocks = 128 // Default value for OptNumBlocks.
+ DefaultBlockTimeout = 64 * time.Millisecond // Default value for OptBlockTimeout.
+ DefaultPollTimeout = -1 * time.Millisecond // Default value for OptPollTimeout. This blocks forever.
+)
+
+type options struct {
+ frameSize int
+ framesPerBlock int
+ blockSize int
+ numBlocks int
+ blockTimeout time.Duration
+ pollTimeout time.Duration
+ version OptTPacketVersion
+ socktype OptSocketType
+ iface string
+}
+
+var defaultOpts = options{
+ frameSize: DefaultFrameSize,
+ blockSize: DefaultBlockSize,
+ numBlocks: DefaultNumBlocks,
+ blockTimeout: DefaultBlockTimeout,
+ pollTimeout: DefaultPollTimeout,
+ version: TPacketVersionHighestAvailable,
+ socktype: SocketRaw,
+}
+
+func parseOptions(opts ...interface{}) (ret options, err error) {
+ ret = defaultOpts
+ for _, opt := range opts {
+ switch v := opt.(type) {
+ case OptFrameSize:
+ ret.frameSize = int(v)
+ case OptBlockSize:
+ ret.blockSize = int(v)
+ case OptNumBlocks:
+ ret.numBlocks = int(v)
+ case OptBlockTimeout:
+ ret.blockTimeout = time.Duration(v)
+ case OptPollTimeout:
+ ret.pollTimeout = time.Duration(v)
+ case OptTPacketVersion:
+ ret.version = v
+ case OptInterface:
+ ret.iface = string(v)
+ case OptSocketType:
+ ret.socktype = v
+ default:
+ err = errors.New("unknown type in options")
+ return
+ }
+ }
+ if err = ret.check(); err != nil {
+ return
+ }
+ ret.framesPerBlock = ret.blockSize / ret.frameSize
+ return
+}
+func (o options) check() error {
+ switch {
+ case o.blockSize%pageSize != 0:
+ return fmt.Errorf("block size %d must be divisible by page size %d", o.blockSize, pageSize)
+ case o.blockSize%o.frameSize != 0:
+ return fmt.Errorf("block size %d must be divisible by frame size %d", o.blockSize, o.frameSize)
+ case o.numBlocks < 1:
+ return fmt.Errorf("num blocks %d must be >= 1", o.numBlocks)
+ case o.blockTimeout < time.Millisecond:
+ return fmt.Errorf("block timeout %v must be > %v", o.blockTimeout, time.Millisecond)
+ case o.version < tpacketVersionMin || o.version > tpacketVersionMax:
+ return fmt.Errorf("tpacket version %v is invalid", o.version)
+ }
+ return nil
+}
diff --git a/vendor/github.com/google/gopacket/afpacket/sockopt_linux.go b/vendor/github.com/google/gopacket/afpacket/sockopt_linux.go
new file mode 100644
index 0000000..c53e1cc
--- /dev/null
+++ b/vendor/github.com/google/gopacket/afpacket/sockopt_linux.go
@@ -0,0 +1,58 @@
+// 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 linux
+
+package afpacket
+
+import (
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+)
+
+// setsockopt provides access to the setsockopt syscall.
+func setsockopt(fd, level, name int, val unsafe.Pointer, vallen uintptr) error {
+ _, _, errno := unix.Syscall6(
+ unix.SYS_SETSOCKOPT,
+ uintptr(fd),
+ uintptr(level),
+ uintptr(name),
+ uintptr(val),
+ vallen,
+ 0,
+ )
+ if errno != 0 {
+ return error(errno)
+ }
+
+ return nil
+}
+
+// getsockopt provides access to the getsockopt syscall.
+func getsockopt(fd, level, name int, val unsafe.Pointer, vallen uintptr) error {
+ _, _, errno := unix.Syscall6(
+ unix.SYS_GETSOCKOPT,
+ uintptr(fd),
+ uintptr(level),
+ uintptr(name),
+ uintptr(val),
+ vallen,
+ 0,
+ )
+ if errno != 0 {
+ return error(errno)
+ }
+
+ return nil
+}
+
+// htons converts a short (uint16) from host-to-network byte order.
+// Thanks to mikioh for this neat trick:
+// https://github.com/mikioh/-stdyng/blob/master/afpacket.go
+func htons(i uint16) uint16 {
+ return (i<<8)&0xff00 | i>>8
+}
diff --git a/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.go b/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.go
new file mode 100644
index 0000000..8c3eb42
--- /dev/null
+++ b/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.go
@@ -0,0 +1,57 @@
+// 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 linux,386
+
+package afpacket
+
+import (
+ "unsafe"
+
+ "golang.org/x/sys/unix"
+)
+
+const (
+ sysSETSOCKOPT = 0xe
+ sysGETSOCKOPT = 0xf
+)
+
+func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, unix.Errno)
+
+// setsockopt provides access to the setsockopt syscall.
+func setsockopt(fd, level, name int, v unsafe.Pointer, l uintptr) error {
+ _, errno := socketcall(
+ sysSETSOCKOPT,
+ uintptr(fd),
+ uintptr(level),
+ uintptr(name),
+ uintptr(v),
+ l,
+ 0,
+ )
+ if errno != 0 {
+ return error(errno)
+ }
+
+ return nil
+}
+
+func getsockopt(fd, level, name int, v unsafe.Pointer, l uintptr) error {
+ _, errno := socketcall(
+ sysGETSOCKOPT,
+ uintptr(fd),
+ uintptr(level),
+ uintptr(name),
+ uintptr(v),
+ l,
+ 0,
+ )
+ if errno != 0 {
+ return error(errno)
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.s b/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.s
new file mode 100644
index 0000000..7d0336a
--- /dev/null
+++ b/vendor/github.com/google/gopacket/afpacket/sockopt_linux_386.s
@@ -0,0 +1,8 @@
+// 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.
+
+TEXT ·socketcall(SB),4,$0-36
+ JMP syscall·socketcall(SB) \ No newline at end of file