Reapply "Merge pull request #132 from eddc005/feat-pcap"
This reverts commit 2ac8783eb6.
This commit is contained in:
@@ -48,6 +48,9 @@ type PacketIO interface {
|
||||
ProtectedDialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||
// Close closes the packet IO.
|
||||
Close() error
|
||||
// SetCancelFunc gives packet IO access to context cancel function, enabling it to
|
||||
// trigger a shutdown
|
||||
SetCancelFunc(cancelFunc context.CancelFunc) error
|
||||
}
|
||||
|
||||
type ErrInvalidPacket struct {
|
||||
|
||||
@@ -281,6 +281,11 @@ func (n *nfqueuePacketIO) Close() error {
|
||||
return n.n.Close()
|
||||
}
|
||||
|
||||
// nfqueue IO does not issue shutdown
|
||||
func (n *nfqueuePacketIO) SetCancelFunc(cancelFunc context.CancelFunc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *nfqueuePacketIO) setupNft(local, rst, remove bool) error {
|
||||
rules, err := generateNftRules(local, rst)
|
||||
if err != nil {
|
||||
|
||||
130
io/pcap.go
Normal file
130
io/pcap.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hash/crc32"
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/pcap"
|
||||
)
|
||||
|
||||
var _ PacketIO = (*pcapPacketIO)(nil)
|
||||
|
||||
type pcapPacketIO struct {
|
||||
pcap *pcap.Handle
|
||||
lastTime *time.Time
|
||||
ioCancel context.CancelFunc
|
||||
config PcapPacketIOConfig
|
||||
|
||||
dialer *net.Dialer
|
||||
}
|
||||
|
||||
type PcapPacketIOConfig struct {
|
||||
PcapFile string
|
||||
Realtime bool
|
||||
}
|
||||
|
||||
func NewPcapPacketIO(config PcapPacketIOConfig) (PacketIO, error) {
|
||||
handle, err := pcap.OpenOffline(config.PcapFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pcapPacketIO{
|
||||
pcap: handle,
|
||||
lastTime: nil,
|
||||
ioCancel: nil,
|
||||
config: config,
|
||||
dialer: &net.Dialer{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *pcapPacketIO) Register(ctx context.Context, cb PacketCallback) error {
|
||||
go func() {
|
||||
packetSource := gopacket.NewPacketSource(p.pcap, p.pcap.LinkType())
|
||||
for packet := range packetSource.Packets() {
|
||||
p.wait(packet)
|
||||
|
||||
networkLayer := packet.NetworkLayer()
|
||||
if networkLayer != nil {
|
||||
src, dst := networkLayer.NetworkFlow().Endpoints()
|
||||
endpoints := []string{src.String(), dst.String()}
|
||||
sort.Strings(endpoints)
|
||||
id := crc32.Checksum([]byte(strings.Join(endpoints, ",")), crc32.IEEETable)
|
||||
|
||||
cb(&pcapPacket{
|
||||
streamID: id,
|
||||
timestamp: packet.Metadata().Timestamp,
|
||||
data: packet.LinkLayer().LayerPayload(),
|
||||
}, nil)
|
||||
}
|
||||
}
|
||||
// Give the workers a chance to finish everything
|
||||
time.Sleep(time.Second)
|
||||
// Stop the engine when all packets are finished
|
||||
p.ioCancel()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// A normal dialer is sufficient as pcap IO does not mess up with the networking
|
||||
func (p *pcapPacketIO) ProtectedDialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return p.dialer.DialContext(ctx, network, address)
|
||||
}
|
||||
|
||||
func (p *pcapPacketIO) SetVerdict(pkt Packet, v Verdict, newPacket []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pcapPacketIO) SetCancelFunc(cancelFunc context.CancelFunc) error {
|
||||
p.ioCancel = cancelFunc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pcapPacketIO) Close() error {
|
||||
p.pcap.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Intentionally slow down the replay
|
||||
// In realtime mode, this is to match the timestamps in the capture
|
||||
func (p *pcapPacketIO) wait(packet gopacket.Packet) error {
|
||||
if !p.config.Realtime {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.lastTime == nil {
|
||||
p.lastTime = &packet.Metadata().Timestamp
|
||||
} else {
|
||||
t := packet.Metadata().Timestamp.Sub(*p.lastTime)
|
||||
time.Sleep(t)
|
||||
p.lastTime = &packet.Metadata().Timestamp
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ Packet = (*pcapPacket)(nil)
|
||||
|
||||
type pcapPacket struct {
|
||||
streamID uint32
|
||||
timestamp time.Time
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (p *pcapPacket) StreamID() uint32 {
|
||||
return p.streamID
|
||||
}
|
||||
|
||||
func (p *pcapPacket) Timestamp() time.Time {
|
||||
return p.timestamp
|
||||
}
|
||||
|
||||
func (p *pcapPacket) Data() []byte {
|
||||
return p.data
|
||||
}
|
||||
Reference in New Issue
Block a user