Files
intercept/utils/wifi/constants.py
2026-02-07 17:31:17 +00:00

460 lines
14 KiB
Python

"""
WiFi-specific constants for the unified scanner.
"""
from __future__ import annotations
# =============================================================================
# SCANNER SETTINGS
# =============================================================================
# Default quick scan timeout in seconds
DEFAULT_QUICK_SCAN_TIMEOUT = 15
# Default deep scan channel dwell time (seconds)
DEFAULT_CHANNEL_DWELL_TIME = 2
# Maximum RSSI samples to keep per network
MAX_RSSI_SAMPLES = 300
# Network expiration time (seconds since last seen)
NETWORK_STALE_TIMEOUT = 300 # 5 minutes
# Client expiration time (seconds since last seen)
CLIENT_STALE_TIMEOUT = 180 # 3 minutes
# Probe request retention time (seconds)
PROBE_REQUEST_RETENTION = 600 # 10 minutes
# =============================================================================
# WIFI BANDS
# =============================================================================
BAND_2_4_GHZ = '2.4GHz'
BAND_5_GHZ = '5GHz'
BAND_6_GHZ = '6GHz'
BAND_UNKNOWN = 'unknown'
# =============================================================================
# WIFI BAND CHANNEL MAPPINGS
# =============================================================================
# 2.4 GHz channels (1-14)
CHANNELS_2_4_GHZ = list(range(1, 15))
# 5 GHz channels (UNII-1, UNII-2A, UNII-2C, UNII-3)
CHANNELS_5_GHZ = [
# UNII-1 (5150-5250 MHz)
36, 40, 44, 48,
# UNII-2A (5250-5350 MHz) - DFS
52, 56, 60, 64,
# UNII-2C (5470-5725 MHz) - DFS
100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
# UNII-3 (5725-5850 MHz)
149, 153, 157, 161, 165,
]
# 6 GHz channels (Wi-Fi 6E)
CHANNELS_6_GHZ = [
1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73,
77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137,
141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, 193, 197,
201, 205, 209, 213, 217, 221, 225, 229, 233
]
# Non-overlapping channels for recommendations
NON_OVERLAPPING_2_4_GHZ = [1, 6, 11]
NON_OVERLAPPING_5_GHZ = [36, 40, 44, 48, 149, 153, 157, 161, 165] # Non-DFS
# Channel to frequency mappings (MHz)
CHANNEL_FREQUENCIES = {
# 2.4 GHz
1: 2412, 2: 2417, 3: 2422, 4: 2427, 5: 2432, 6: 2437, 7: 2442,
8: 2447, 9: 2452, 10: 2457, 11: 2462, 12: 2467, 13: 2472, 14: 2484,
# 5 GHz
36: 5180, 40: 5200, 44: 5220, 48: 5240,
52: 5260, 56: 5280, 60: 5300, 64: 5320,
100: 5500, 104: 5520, 108: 5540, 112: 5560, 116: 5580,
120: 5600, 124: 5620, 128: 5640, 132: 5660, 136: 5680, 140: 5700, 144: 5720,
149: 5745, 153: 5765, 157: 5785, 161: 5805, 165: 5825,
}
# Frequency to channel reverse mapping
FREQUENCY_CHANNELS = {v: k for k, v in CHANNEL_FREQUENCIES.items()}
def get_band_from_channel(channel: int) -> str:
"""Get WiFi band from channel number."""
if 1 <= channel <= 14:
return BAND_2_4_GHZ
elif channel in CHANNELS_5_GHZ:
return BAND_5_GHZ
elif channel in CHANNELS_6_GHZ:
return BAND_6_GHZ
return BAND_UNKNOWN
def get_band_from_frequency(frequency_mhz: int) -> str:
"""Get WiFi band from frequency in MHz."""
if 2400 <= frequency_mhz <= 2500:
return BAND_2_4_GHZ
elif 5150 <= frequency_mhz <= 5850:
return BAND_5_GHZ
elif 5925 <= frequency_mhz <= 7125:
return BAND_6_GHZ
return BAND_UNKNOWN
def get_channel_from_frequency(frequency_mhz: int) -> int | None:
"""Get channel number from frequency in MHz."""
return FREQUENCY_CHANNELS.get(frequency_mhz)
# =============================================================================
# SECURITY TYPES
# =============================================================================
SECURITY_OPEN = 'Open'
SECURITY_WEP = 'WEP'
SECURITY_WPA = 'WPA'
SECURITY_WPA2 = 'WPA2'
SECURITY_WPA3 = 'WPA3'
SECURITY_WPA_WPA2 = 'WPA/WPA2'
SECURITY_WPA2_WPA3 = 'WPA2/WPA3'
SECURITY_ENTERPRISE = 'Enterprise'
SECURITY_UNKNOWN = 'Unknown'
# Security type priority (higher = more secure)
SECURITY_PRIORITY = {
SECURITY_OPEN: 0,
SECURITY_WEP: 1,
SECURITY_WPA: 2,
SECURITY_WPA_WPA2: 3,
SECURITY_WPA2: 4,
SECURITY_WPA2_WPA3: 5,
SECURITY_WPA3: 6,
SECURITY_ENTERPRISE: 7,
SECURITY_UNKNOWN: -1,
}
# =============================================================================
# CIPHER TYPES
# =============================================================================
CIPHER_NONE = 'None'
CIPHER_WEP = 'WEP'
CIPHER_TKIP = 'TKIP'
CIPHER_CCMP = 'CCMP'
CIPHER_GCMP = 'GCMP'
CIPHER_UNKNOWN = 'Unknown'
# =============================================================================
# AUTHENTICATION TYPES
# =============================================================================
AUTH_OPEN = 'Open'
AUTH_PSK = 'PSK'
AUTH_SAE = 'SAE'
AUTH_EAP = 'EAP'
AUTH_OWE = 'OWE'
AUTH_UNKNOWN = 'Unknown'
# =============================================================================
# CHANNEL WIDTH
# =============================================================================
WIDTH_20_MHZ = '20MHz'
WIDTH_40_MHZ = '40MHz'
WIDTH_80_MHZ = '80MHz'
WIDTH_160_MHZ = '160MHz'
WIDTH_320_MHZ = '320MHz'
WIDTH_UNKNOWN = 'Unknown'
# =============================================================================
# SIGNAL STRENGTH BANDS (for proximity radar)
# =============================================================================
SIGNAL_STRONG = 'strong' # >= -50 dBm
SIGNAL_MEDIUM = 'medium' # -50 to -70 dBm
SIGNAL_WEAK = 'weak' # -70 to -85 dBm
SIGNAL_VERY_WEAK = 'very_weak' # < -85 dBm
SIGNAL_UNKNOWN = 'unknown'
# RSSI thresholds for signal bands
RSSI_STRONG = -50
RSSI_MEDIUM = -70
RSSI_WEAK = -85
def get_signal_band(rssi: int | None) -> str:
"""Get signal band from RSSI value."""
if rssi is None:
return SIGNAL_UNKNOWN
if rssi >= RSSI_STRONG:
return SIGNAL_STRONG
elif rssi >= RSSI_MEDIUM:
return SIGNAL_MEDIUM
elif rssi >= RSSI_WEAK:
return SIGNAL_WEAK
return SIGNAL_VERY_WEAK
# =============================================================================
# PROXIMITY BANDS (consistent with Bluetooth)
# =============================================================================
PROXIMITY_IMMEDIATE = 'immediate' # < 3m
PROXIMITY_NEAR = 'near' # 3-10m
PROXIMITY_FAR = 'far' # > 10m
PROXIMITY_UNKNOWN = 'unknown'
# RSSI thresholds for proximity band classification
PROXIMITY_RSSI_IMMEDIATE = -55 # >= -55 dBm -> immediate
PROXIMITY_RSSI_NEAR = -70 # >= -70 dBm -> near
def get_proximity_band(rssi: int | None) -> str:
"""Get proximity band from RSSI value."""
if rssi is None:
return PROXIMITY_UNKNOWN
if rssi >= PROXIMITY_RSSI_IMMEDIATE:
return PROXIMITY_IMMEDIATE
elif rssi >= PROXIMITY_RSSI_NEAR:
return PROXIMITY_NEAR
return PROXIMITY_FAR
# =============================================================================
# DISTANCE ESTIMATION (WiFi-specific)
# =============================================================================
# Path-loss exponent for indoor WiFi (typically 2.5-4.0)
WIFI_PATH_LOSS_EXPONENT = 3.0
# Reference RSSI at 1 meter (typical WiFi AP)
WIFI_RSSI_AT_1M = -40
# EMA smoothing alpha for RSSI
WIFI_EMA_ALPHA = 0.3
# =============================================================================
# SCAN MODES
# =============================================================================
SCAN_MODE_QUICK = 'quick' # Uses system tools (no monitor mode)
SCAN_MODE_DEEP = 'deep' # Uses airodump-ng (monitor mode required)
# =============================================================================
# TOOL DETECTION
# =============================================================================
# Quick scan tools (by platform priority)
QUICK_SCAN_TOOLS_LINUX = ['nmcli', 'iw', 'iwlist']
QUICK_SCAN_TOOLS_DARWIN = ['airport']
# Deep scan tools
DEEP_SCAN_TOOLS = ['airodump-ng']
# Monitor mode tools
MONITOR_MODE_TOOLS = ['airmon-ng', 'iw']
# Tool command timeouts (seconds)
TOOL_TIMEOUT_QUICK = 30.0
TOOL_TIMEOUT_DETECT = 5.0
# =============================================================================
# AIRODUMP-NG SETTINGS
# =============================================================================
AIRODUMP_OUTPUT_PREFIX = 'airodump_wifi'
AIRODUMP_POLL_INTERVAL = 1.0 # seconds between CSV reads
# =============================================================================
# HEURISTIC FLAGS
# =============================================================================
HEURISTIC_HIDDEN = 'hidden'
HEURISTIC_ROGUE_AP = 'rogue_ap'
HEURISTIC_EVIL_TWIN = 'evil_twin'
HEURISTIC_BEACON_FLOOD = 'beacon_flood'
HEURISTIC_WEAK_SECURITY = 'weak_security'
HEURISTIC_DEAUTH_DETECTED = 'deauth_detected'
HEURISTIC_NEW = 'new'
HEURISTIC_PERSISTENT = 'persistent'
HEURISTIC_STRONG_STABLE = 'strong_stable'
# Thresholds
BEACON_FLOOD_THRESHOLD = 50 # Same BSSID seen > 50 times/minute
PERSISTENT_MIN_SEEN = 10
PERSISTENT_WINDOW_SECONDS = 300
STRONG_RSSI_THRESHOLD = -50
STABLE_VARIANCE_THRESHOLD = 5.0
# =============================================================================
# COMMON VENDOR OUI PREFIXES (first 3 bytes of MAC)
# =============================================================================
VENDOR_OUIS = {
'00:00:5E': 'IANA',
'00:03:93': 'Apple',
'00:0A:95': 'Apple',
'00:0D:93': 'Apple',
'00:11:24': 'Apple',
'00:14:51': 'Apple',
'00:16:CB': 'Apple',
'00:17:F2': 'Apple',
'00:19:E3': 'Apple',
'00:1B:63': 'Apple',
'00:1C:B3': 'Apple',
'00:1D:4F': 'Apple',
'00:1E:52': 'Apple',
'00:1E:C2': 'Apple',
'00:1F:5B': 'Apple',
'00:1F:F3': 'Apple',
'00:21:E9': 'Apple',
'00:22:41': 'Apple',
'00:23:12': 'Apple',
'00:23:32': 'Apple',
'00:23:6C': 'Apple',
'00:23:DF': 'Apple',
'00:24:36': 'Apple',
'00:25:00': 'Apple',
'00:25:4B': 'Apple',
'00:25:BC': 'Apple',
'00:26:08': 'Apple',
'00:26:4A': 'Apple',
'00:26:B0': 'Apple',
'00:26:BB': 'Apple',
'00:50:F2': 'Microsoft',
'00:15:5D': 'Microsoft',
'00:17:FA': 'Microsoft',
'00:1D:D8': 'Microsoft',
'00:50:56': 'VMware',
'00:0C:29': 'VMware',
'00:05:69': 'VMware',
'08:00:27': 'VirtualBox',
'00:1C:42': 'Parallels',
'00:16:3E': 'Xen',
'DC:A6:32': 'Raspberry Pi',
'B8:27:EB': 'Raspberry Pi',
'E4:5F:01': 'Raspberry Pi',
'28:CD:C1': 'Raspberry Pi',
'00:1A:11': 'Google',
'00:1A:22': 'Google',
'3C:5A:B4': 'Google',
'54:60:09': 'Google',
'94:EB:2C': 'Google',
'F4:F5:D8': 'Google',
'00:17:C4': 'Netgear',
'00:1B:2F': 'Netgear',
'00:1E:2A': 'Netgear',
'00:22:3F': 'Netgear',
'00:24:B2': 'Netgear',
'00:26:F2': 'Netgear',
'00:18:F8': 'Cisco',
'00:1A:A1': 'Cisco',
'00:1B:0C': 'Cisco',
'00:1B:D4': 'Cisco',
'00:1C:0E': 'Cisco',
'00:1C:57': 'Cisco',
'00:40:96': 'Cisco',
'00:50:54': 'Cisco',
'00:60:5C': 'Cisco',
'E8:65:D4': 'Ubiquiti',
'FC:EC:DA': 'Ubiquiti',
'00:27:22': 'Ubiquiti',
'04:18:D6': 'Ubiquiti',
'18:E8:29': 'Ubiquiti',
'24:A4:3C': 'Ubiquiti',
'44:D9:E7': 'Ubiquiti',
'68:72:51': 'Ubiquiti',
'74:83:C2': 'Ubiquiti',
'78:8A:20': 'Ubiquiti',
'B4:FB:E4': 'Ubiquiti',
'F0:9F:C2': 'Ubiquiti',
'00:0C:F1': 'Intel',
'00:13:02': 'Intel',
'00:13:20': 'Intel',
'00:13:CE': 'Intel',
'00:13:E8': 'Intel',
'00:15:00': 'Intel',
'00:15:17': 'Intel',
'00:16:6F': 'Intel',
'00:16:76': 'Intel',
'00:16:EA': 'Intel',
'00:16:EB': 'Intel',
'00:18:DE': 'Intel',
'00:19:D1': 'Intel',
'00:19:D2': 'Intel',
'00:1B:21': 'Intel',
'00:1B:77': 'Intel',
'00:1C:BF': 'Intel',
'00:1D:E0': 'Intel',
'00:1D:E1': 'Intel',
'00:1E:64': 'Intel',
'00:1E:65': 'Intel',
'00:1E:67': 'Intel',
'00:1F:3B': 'Intel',
'00:1F:3C': 'Intel',
'00:20:E0': 'TP-Link',
'00:23:CD': 'TP-Link',
'00:25:86': 'TP-Link',
'00:27:19': 'TP-Link',
'14:CC:20': 'TP-Link',
'14:CF:92': 'TP-Link',
'18:A6:F7': 'TP-Link',
'1C:3B:F3': 'TP-Link',
'30:B5:C2': 'TP-Link',
'50:C7:BF': 'TP-Link',
'54:C8:0F': 'TP-Link',
'60:E3:27': 'TP-Link',
'64:56:01': 'TP-Link',
'64:66:B3': 'TP-Link',
'64:70:02': 'TP-Link',
}
def get_vendor_from_mac(mac: str) -> str | None:
"""Get vendor name from MAC address OUI."""
if not mac:
return None
# Normalize MAC format
mac_upper = mac.upper().replace('-', ':')
oui = mac_upper[:8]
vendor = VENDOR_OUIS.get(oui)
if vendor:
return vendor
# Fallback to expanded OUI database if available
try:
from data.oui import get_manufacturer
manufacturer = get_manufacturer(mac_upper)
if manufacturer and manufacturer != 'Unknown':
return manufacturer
except Exception:
return None
return None
# =============================================================================
# HIDDEN SSID CORRELATION
# =============================================================================
# Time window for correlating probe requests with hidden AP associations
HIDDEN_CORRELATION_WINDOW_SECONDS = 60
# Minimum confidence for hidden SSID revelation
HIDDEN_MIN_CORRELATION_CONFIDENCE = 0.7
# =============================================================================
# CHANNEL ANALYSIS
# =============================================================================
# Weights for channel utilization scoring
CHANNEL_WEIGHT_AP_COUNT = 0.6
CHANNEL_WEIGHT_CLIENT_COUNT = 0.4
# RSSI adjustment factor (stronger signals = more interference)
CHANNEL_RSSI_INTERFERENCE_FACTOR = 0.1