From 6958e22088fe64d0e0d0308581cf417277f5b851 Mon Sep 17 00:00:00 2001 From: Yaro Kasear Date: Tue, 29 Apr 2025 12:53:53 -0500 Subject: [PATCH] Refactor ghost client detection in _count_unlinked_devices to improve clarity and accuracy --- enrichment/indexed_capture.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/enrichment/indexed_capture.py b/enrichment/indexed_capture.py index 5aa5b56..ed92587 100644 --- a/enrichment/indexed_capture.py +++ b/enrichment/indexed_capture.py @@ -247,27 +247,43 @@ class IndexedCapture: def _count_unlinked_devices(self, packets, ap_channel): aps = self.channel_to_aps.get(ap_channel, set()) - ghost_clients = set() - + ghost_candidates = set() + for packet in packets: try: if 'radiotap' not in packet or 'wlan' not in packet: continue - + radio = packet.radiotap wlan = packet.wlan - + + # Must be on our AP's channel + if hasattr(radio, 'channel') and hasattr(radio.channel, 'freq'): + freq = int(radio.channel.freq) + packet_channel = get_channel_from_freq(freq) + if packet_channel != ap_channel: + continue + else: + continue + sa = getattr(wlan, 'sa', '').lower() da = getattr(wlan, 'da', '').lower() - + bssid = getattr(wlan, 'bssid', '').lower() + + # If the packet is *talking to* any known AP, it's **linked**, not ghost + if sa in aps or da in aps or bssid in aps: + continue # Legit traffic, skip + + # Otherwise, these are "ghost candidates" for mac in (sa, da): - if mac and mac != 'ff:ff:ff:ff:ff:ff' and mac not in aps: - ghost_clients.add(mac) - + if mac and mac != 'ff:ff:ff:ff:ff:ff': + ghost_candidates.add(mac) + except Exception: continue + + return len(ghost_candidates) - return len(ghost_clients) def _cisco_avg_clients(self, ssid): if ssid in self.cisco_ssid_clients: