- 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
59 lines
1.5 KiB
Go
59 lines
1.5 KiB
Go
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
|
|
}
|