Files
dip-ids/internal/capture/capture.go
T
2026-04-12 22:26:26 +04:00

123 lines
2.6 KiB
Go

package capture
import (
"context"
"fmt"
"io"
"log"
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
)
// Mode определяет режим захвата трафика
type Mode string
const (
ModeLive Mode = "live"
ModePCAP Mode = "pcap"
)
// Config параметры захвата
type Config struct {
Interface string
PCAPFile string
Mode Mode
Snaplen int32
Promiscuous bool
}
// Capturer захватывает пакеты и отправляет их в канал
type Capturer struct {
cfg Config
handle *pcap.Handle
}
// New создаёт новый захватчик трафика
func New(cfg Config) (*Capturer, error) {
return &Capturer{cfg: cfg}, nil
}
// Start открывает источник пакетов и запускает захват.
// Пакеты отправляются в возвращаемый канал до отмены ctx.
func (c *Capturer) Start(ctx context.Context) (<-chan gopacket.Packet, error) {
var handle *pcap.Handle
var err error
switch c.cfg.Mode {
case ModeLive:
handle, err = pcap.OpenLive(
c.cfg.Interface,
c.cfg.Snaplen,
c.cfg.Promiscuous,
pcap.BlockForever,
)
if err != nil {
return nil, fmt.Errorf("open live capture on %s: %w", c.cfg.Interface, err)
}
case ModePCAP:
handle, err = pcap.OpenOffline(c.cfg.PCAPFile)
if err != nil {
return nil, fmt.Errorf("open pcap file %s: %w", c.cfg.PCAPFile, err)
}
default:
return nil, fmt.Errorf("unknown capture mode: %s", c.cfg.Mode)
}
c.handle = handle
pkts := make(chan gopacket.Packet, 1024)
go func() {
defer close(pkts)
defer handle.Close()
src := gopacket.NewPacketSource(handle, handle.LinkType())
src.NoCopy = true
for {
select {
case <-ctx.Done():
return
default:
}
pkt, err := src.NextPacket()
if err != nil {
if err == io.EOF {
return
}
// pcap timeout или другая recoverable ошибка
log.Printf("[capture] packet read error: %v", err)
continue
}
select {
case pkts <- pkt:
case <-ctx.Done():
return
}
}
}()
return pkts, nil
}
// Stop принудительно закрывает handle (используется при остановке из API)
func (c *Capturer) Stop() {
if c.handle != nil {
c.handle.Close()
}
}
// ListInterfaces возвращает доступные сетевые интерфейсы
func ListInterfaces() ([]string, error) {
devs, err := pcap.FindAllDevs()
if err != nil {
return nil, err
}
names := make([]string, 0, len(devs))
for _, d := range devs {
names = append(names, d.Name)
}
return names, nil
}