Exclude infrastructure MACs from personal device detection via NET_ALERTER_INFRA_MACS
Travel routers and other LAA-bit devices get misclassified as personal phones because the LAA heuristic fires on any MAC with bit 1 set (b6:cc is 0xb6). Add infrastructure_macs set checked before the LAA heuristic in is_personal_device(). NET_ALERTER_INFRA_MACS env var (comma-separated MACs) populates the set at startup.
This commit is contained in:
@@ -34,6 +34,7 @@ ENV_PATH = Path(os.getenv("NET_ALERTER_ENV", "/opt/net_alerter/.env"))
|
|||||||
known_devices = {} # {mac: {ip, hostname, vendor, first_seen, last_seen, infrastructure (opt)}}
|
known_devices = {} # {mac: {ip, hostname, vendor, first_seen, last_seen, infrastructure (opt)}}
|
||||||
known_lock = threading.Lock()
|
known_lock = threading.Lock()
|
||||||
infrastructure_ips = set() # IPs that should never trigger alerts (gateway, broadcast, self)
|
infrastructure_ips = set() # IPs that should never trigger alerts (gateway, broadcast, self)
|
||||||
|
infrastructure_macs = set() # MACs that should never be treated as personal devices
|
||||||
departure_timers = {} # {mac: threading.Timer} for 15-min departure debounce
|
departure_timers = {} # {mac: threading.Timer} for 15-min departure debounce
|
||||||
departure_lock = threading.Lock() # Protects departure_timers and last_departed_time
|
departure_lock = threading.Lock() # Protects departure_timers and last_departed_time
|
||||||
last_departed_time = {} # {mac: timestamp} when device was actually alerted as departed
|
last_departed_time = {} # {mac: timestamp} when device was actually alerted as departed
|
||||||
@@ -683,6 +684,19 @@ def load_personal_macs_from_env() -> None:
|
|||||||
logging.info(f"Loaded {len(personal_devices)} personal device MAC(s) and {len(personal_names)} personal name(s)")
|
logging.info(f"Loaded {len(personal_devices)} personal device MAC(s) and {len(personal_names)} personal name(s)")
|
||||||
|
|
||||||
|
|
||||||
|
def load_infrastructure_macs_from_env() -> None:
|
||||||
|
"""Parse NET_ALERTER_INFRA_MACS env var into infrastructure_macs set."""
|
||||||
|
global infrastructure_macs
|
||||||
|
macs_str = _read_env_value("NET_ALERTER_INFRA_MACS") or os.getenv("NET_ALERTER_INFRA_MACS", "")
|
||||||
|
for mac in (m.strip() for m in macs_str.split(",") if m.strip()):
|
||||||
|
normalized = _normalize_mac(mac)
|
||||||
|
if normalized:
|
||||||
|
infrastructure_macs.add(normalized)
|
||||||
|
logging.info(f"Seeded infrastructure MAC {mac} — excluded from personal device detection")
|
||||||
|
else:
|
||||||
|
logging.warning(f"Skipping invalid MAC from NET_ALERTER_INFRA_MACS: {mac}")
|
||||||
|
|
||||||
|
|
||||||
def _read_env_value(key: str) -> str:
|
def _read_env_value(key: str) -> str:
|
||||||
"""Read a single environment variable from .env file or system env."""
|
"""Read a single environment variable from .env file or system env."""
|
||||||
if ENV_PATH.exists():
|
if ENV_PATH.exists():
|
||||||
@@ -750,6 +764,10 @@ def is_personal_device(mac: str) -> bool:
|
|||||||
if oui in MOBILE_DEVICE_OUIS:
|
if oui in MOBILE_DEVICE_OUIS:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Infrastructure MACs are never personal, regardless of LAA bit
|
||||||
|
if mac_normalized in infrastructure_macs:
|
||||||
|
return False
|
||||||
|
|
||||||
# Locally-administered bit: modern phones randomize MACs; TVs/infra don't
|
# Locally-administered bit: modern phones randomize MACs; TVs/infra don't
|
||||||
try:
|
try:
|
||||||
return bool(int(mac_normalized[:2], 16) & 0x02)
|
return bool(int(mac_normalized[:2], 16) & 0x02)
|
||||||
@@ -1825,6 +1843,7 @@ def main() -> None:
|
|||||||
logging.info(f"Net alerter starting on interface {iface}")
|
logging.info(f"Net alerter starting on interface {iface}")
|
||||||
|
|
||||||
seed_infrastructure_ips(iface)
|
seed_infrastructure_ips(iface)
|
||||||
|
load_infrastructure_macs_from_env()
|
||||||
seed_from_arp_cache()
|
seed_from_arp_cache()
|
||||||
|
|
||||||
# Start monitor threads
|
# Start monitor threads
|
||||||
|
|||||||
Reference in New Issue
Block a user