package analyzer import ( "fmt" "sync" "time" "dip-ids/internal/config" "dip-ids/internal/storage" "github.com/google/gopacket" "github.com/google/gopacket/layers" ) // arpSpoofDetector обнаруживает ARP-спуфинг. // Алгоритм: ведёт таблицу соответствия IP→MAC; при обнаружении // нового MAC для уже известного IP генерирует алерт. type arpSpoofDetector struct { mu sync.Mutex cfg config.ARPSpoofConfig ipToMAC map[string]string // IP -> MAC alerted map[string]time.Time } func NewARPSpoofDetector(cfg config.ARPSpoofConfig) Detector { return &arpSpoofDetector{ cfg: cfg, ipToMAC: make(map[string]string), alerted: make(map[string]time.Time), } } func (d *arpSpoofDetector) Name() string { return "arp_spoof" } func (d *arpSpoofDetector) Analyze(pkt gopacket.Packet) []storage.Alert { if !d.cfg.Enabled { return nil } arpLayer := pkt.Layer(layers.LayerTypeARP) if arpLayer == nil { return nil } arp := arpLayer.(*layers.ARP) // Обрабатываем только ARP Reply (op=2) и Gratuitous ARP if arp.Operation != layers.ARPReply { return nil } srcIP := fmt.Sprintf("%d.%d.%d.%d", arp.SourceProtAddress[0], arp.SourceProtAddress[1], arp.SourceProtAddress[2], arp.SourceProtAddress[3]) srcMAC := fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", arp.SourceHwAddress[0], arp.SourceHwAddress[1], arp.SourceHwAddress[2], arp.SourceHwAddress[3], arp.SourceHwAddress[4], arp.SourceHwAddress[5]) now := pkt.Metadata().Timestamp if now.IsZero() { now = time.Now() } d.mu.Lock() defer d.mu.Unlock() knownMAC, exists := d.ipToMAC[srcIP] if !exists { d.ipToMAC[srcIP] = srcMAC return nil } if knownMAC == srcMAC { return nil } // MAC изменился — ARP спуфинг key := srcIP if last, ok := d.alerted[key]; ok && now.Sub(last) < 60*time.Second { return nil } d.alerted[key] = now // Обновляем таблицу d.ipToMAC[srcIP] = srcMAC return []storage.Alert{{ CreatedAt: now, Type: "arp_spoof", Severity: d.cfg.Severity, SrcIP: srcIP, Protocol: "ARP", Description: fmt.Sprintf("ARP spoofing: IP %s changed MAC from %s to %s", srcIP, knownMAC, srcMAC), Count: 1, }} } func (d *arpSpoofDetector) Reset() { d.mu.Lock() defer d.mu.Unlock() d.ipToMAC = make(map[string]string) d.alerted = make(map[string]time.Time) }