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 }