Initial commit
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user