feat(04-01): add BPF validation, pcap reading, Timestamp field, and filter support
- Add Timestamp time.Time field to ClassifiedPacket (classify/types.go) - Create capture/bpf.go: ValidateBPFFilter and CompileSoftwareBPF - Create capture/pcap_reader.go: ReadPcapFile with optional software BPF filter - Update OpenCapture and StartCapture to accept filter string param - Update cmd/netsynth/main.go to pass empty filter to StartCapture - All new tests pass; existing tests unaffected
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package capture
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gopacket/gopacket"
|
||||
"github.com/gopacket/gopacket/layers"
|
||||
"github.com/gopacket/gopacket/pcapgo"
|
||||
"golang.org/x/net/bpf"
|
||||
)
|
||||
|
||||
// ReadPcapFile opens a pcap file and returns a channel of packets.
|
||||
// If filter is non-empty, applies software BPF filtering (kernel BPF unavailable for files).
|
||||
// Per D-03: replaces -i as packet source. Per D-04: --filter works with --read.
|
||||
// The returned channel is closed when all packets have been emitted.
|
||||
func ReadPcapFile(path string, filter string) (<-chan gopacket.Packet, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot open pcap file %q: %w", path, err)
|
||||
}
|
||||
r, err := pcapgo.NewReader(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, fmt.Errorf("not a valid pcap file %q: %w", path, err)
|
||||
}
|
||||
|
||||
// Compile software BPF filter if provided.
|
||||
var vm *bpf.VM
|
||||
if filter != "" {
|
||||
vm, err = CompileSoftwareBPF(filter)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
lt := layers.LinkType(r.LinkType())
|
||||
packetSource := gopacket.NewPacketSource(r, lt)
|
||||
packetSource.NoCopy = false // CRITICAL: do NOT use NoCopy=true with pcapgo (Pitfall 3)
|
||||
|
||||
packets := make(chan gopacket.Packet, 512)
|
||||
go func() {
|
||||
defer close(packets)
|
||||
defer f.Close()
|
||||
for pkt := range packetSource.Packets() {
|
||||
// Apply software BPF filter if active.
|
||||
if vm != nil {
|
||||
result, err := vm.Run(pkt.Data())
|
||||
if err != nil || result == 0 {
|
||||
continue // packet does not match filter
|
||||
}
|
||||
}
|
||||
packets <- pkt
|
||||
}
|
||||
}()
|
||||
return packets, nil
|
||||
}
|
||||
Reference in New Issue
Block a user