mirror of
https://github.com/buger/goreplay.git
synced 2024-04-21 12:32:02 +00:00
change package from `main -> goreplay` this will allow importing `goreplay` as a package
116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
//go:build linux
|
|
|
|
package capture
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/afpacket"
|
|
"github.com/google/gopacket/layers"
|
|
"github.com/google/gopacket/pcap"
|
|
"golang.org/x/net/bpf"
|
|
|
|
_ "github.com/google/gopacket/layers"
|
|
)
|
|
|
|
type afpacketHandle struct {
|
|
TPacket *afpacket.TPacket
|
|
}
|
|
|
|
func newAfpacketHandle(device string, snaplen int, block_size int, num_blocks int,
|
|
useVLAN bool, timeout time.Duration) (*afpacketHandle, error) {
|
|
|
|
h := &afpacketHandle{}
|
|
var err error
|
|
|
|
if device == "any" {
|
|
h.TPacket, err = afpacket.NewTPacket(
|
|
afpacket.OptFrameSize(snaplen),
|
|
afpacket.OptBlockSize(block_size),
|
|
afpacket.OptNumBlocks(num_blocks),
|
|
afpacket.OptAddVLANHeader(false),
|
|
afpacket.OptPollTimeout(timeout),
|
|
afpacket.SocketRaw,
|
|
afpacket.TPacketVersion3)
|
|
} else {
|
|
h.TPacket, err = afpacket.NewTPacket(
|
|
afpacket.OptInterface(device),
|
|
afpacket.OptFrameSize(snaplen),
|
|
afpacket.OptBlockSize(block_size),
|
|
afpacket.OptNumBlocks(num_blocks),
|
|
afpacket.OptAddVLANHeader(false),
|
|
afpacket.OptPollTimeout(timeout),
|
|
afpacket.SocketRaw,
|
|
afpacket.TPacketVersion3)
|
|
}
|
|
return h, err
|
|
}
|
|
|
|
// ReadPacketData satisfies PacketDataSource interface
|
|
func (h *afpacketHandle) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
|
return h.TPacket.ReadPacketData()
|
|
}
|
|
|
|
// SetBPFFilter translates a BPF filter string into BPF RawInstruction and applies them.
|
|
func (h *afpacketHandle) SetBPFFilter(filter string, snaplen int) (err error) {
|
|
pcapBPF, err := pcap.CompileBPFFilter(layers.LinkTypeEthernet, snaplen, filter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bpfIns := []bpf.RawInstruction{}
|
|
for _, ins := range pcapBPF {
|
|
bpfIns2 := bpf.RawInstruction{
|
|
Op: ins.Code,
|
|
Jt: ins.Jt,
|
|
Jf: ins.Jf,
|
|
K: ins.K,
|
|
}
|
|
bpfIns = append(bpfIns, bpfIns2)
|
|
}
|
|
if h.TPacket.SetBPF(bpfIns); err != nil {
|
|
return err
|
|
}
|
|
return h.TPacket.SetBPF(bpfIns)
|
|
}
|
|
|
|
// LinkType returns ethernet link type.
|
|
func (h *afpacketHandle) LinkType() layers.LinkType {
|
|
return layers.LinkTypeEthernet
|
|
}
|
|
|
|
// Close will close afpacket source.
|
|
func (h *afpacketHandle) Close() {
|
|
h.TPacket.Close()
|
|
}
|
|
|
|
// SocketStats prints received, dropped, queue-freeze packet stats.
|
|
func (h *afpacketHandle) SocketStats() (as afpacket.SocketStats, asv afpacket.SocketStatsV3, err error) {
|
|
return h.TPacket.SocketStats()
|
|
}
|
|
|
|
// afpacketComputeSize computes the block_size and the num_blocks in such a way that the
|
|
// allocated mmap buffer is close to but smaller than target_size_mb.
|
|
// The restriction is that the block_size must be divisible by both the
|
|
// frame size and page size.
|
|
func afpacketComputeSize(targetSizeMb int, snaplen int, pageSize int) (
|
|
frameSize int, blockSize int, numBlocks int, err error) {
|
|
|
|
if snaplen < pageSize {
|
|
frameSize = pageSize / (pageSize / snaplen)
|
|
} else {
|
|
frameSize = (snaplen/pageSize + 1) * pageSize
|
|
}
|
|
|
|
// 128 is the default from the gopacket library so just use that
|
|
blockSize = frameSize * 128
|
|
numBlocks = (targetSizeMb * 1024 * 1024) / blockSize
|
|
|
|
if numBlocks == 0 {
|
|
return 0, 0, 0, fmt.Errorf("Interface buffersize is too small")
|
|
}
|
|
|
|
return frameSize, blockSize, numBlocks, nil
|
|
}
|