package analyzer import ( "fmt" "sync" "time" "dip-ids/internal/config" "dip-ids/internal/storage" "github.com/google/gopacket" ) // dosDetector обнаруживает DoS/DDoS атаки по превышению порога PPS. // Алгоритм: считает количество пакетов от каждого источника // за скользящее временное окно; при превышении порога — алерт. type dosDetector struct { mu sync.Mutex cfg config.DoSConfig tracker map[string][]time.Time // srcIP -> timestamps alerted map[string]time.Time } func NewDoSDetector(cfg config.DoSConfig) Detector { return &dosDetector{ cfg: cfg, tracker: make(map[string][]time.Time), alerted: make(map[string]time.Time), } } func (d *dosDetector) Name() string { return "dos" } func (d *dosDetector) Analyze(pkt gopacket.Packet) []storage.Alert { if !d.cfg.Enabled { return nil } netLayer := pkt.NetworkLayer() if netLayer == nil { return nil } srcIP := netLayer.NetworkFlow().Src().String() dstIP := netLayer.NetworkFlow().Dst().String() now := pkt.Metadata().Timestamp if now.IsZero() { now = time.Now() } d.mu.Lock() defer d.mu.Unlock() window := time.Duration(d.cfg.WindowSeconds) * time.Second cutoff := now.Add(-window) d.tracker[srcIP] = append(d.tracker[srcIP], now) d.tracker[srcIP] = filterTimes(d.tracker[srcIP], cutoff) pps := len(d.tracker[srcIP]) if pps < d.cfg.PPSThreshold { return nil } if last, ok := d.alerted[srcIP]; ok && now.Sub(last) < 10*time.Second { return nil } d.alerted[srcIP] = now return []storage.Alert{{ CreatedAt: now, Type: "dos", Severity: d.cfg.Severity, SrcIP: srcIP, DstIP: dstIP, Protocol: "IP", Description: fmt.Sprintf("DoS attack detected from %s: %d pkt/s (threshold: %d)", srcIP, pps, d.cfg.PPSThreshold), Count: pps, }} } func (d *dosDetector) Reset() { d.mu.Lock() defer d.mu.Unlock() d.tracker = make(map[string][]time.Time) d.alerted = make(map[string]time.Time) }