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

88 lines
2.0 KiB
Go

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)
}