firmware&dashboard changes

This commit is contained in:
bot
2026-04-05 13:06:25 +03:00
parent 2636ea1e11
commit e01bbd9e56
11 changed files with 419 additions and 84 deletions
+92 -10
View File
@@ -50,13 +50,16 @@ def init_db(path: str) -> sqlite3.Connection:
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS heartbeat_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
received_at TEXT NOT NULL,
node_id TEXT,
node_ts INTEGER,
uptime_ms INTEGER,
free_heap INTEGER,
wifi_rssi INTEGER
id INTEGER PRIMARY KEY AUTOINCREMENT,
received_at TEXT NOT NULL,
node_id TEXT,
node_ts INTEGER,
uptime_ms INTEGER,
free_heap INTEGER,
wifi_rssi INTEGER,
probe_drops INTEGER,
deauth_drops INTEGER,
assoc_drops INTEGER
)
""")
conn.execute("""
@@ -73,6 +76,38 @@ def init_db(path: str) -> sqlite3.Connection:
rssi INTEGER
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS assoc_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
received_at TEXT NOT NULL,
node_id TEXT,
node_ts INTEGER,
subtype TEXT,
src TEXT,
bssid TEXT,
ssid TEXT,
rssi INTEGER
)
""")
# Indexes for common query patterns
conn.executescript("""
CREATE INDEX IF NOT EXISTS idx_beacon_received_at ON beacon_events (received_at);
CREATE INDEX IF NOT EXISTS idx_beacon_node_id ON beacon_events (node_id);
CREATE INDEX IF NOT EXISTS idx_beacon_bssid ON beacon_events (bssid);
CREATE INDEX IF NOT EXISTS idx_probe_received_at ON probe_events (received_at);
CREATE INDEX IF NOT EXISTS idx_probe_node_id ON probe_events (node_id);
CREATE INDEX IF NOT EXISTS idx_probe_src_mac ON probe_events (src_mac);
CREATE INDEX IF NOT EXISTS idx_heartbeat_node_id ON heartbeat_events (node_id);
CREATE INDEX IF NOT EXISTS idx_deauth_received_at ON deauth_events (received_at);
CREATE INDEX IF NOT EXISTS idx_deauth_node_id ON deauth_events (node_id);
CREATE INDEX IF NOT EXISTS idx_deauth_bssid ON deauth_events (bssid);
CREATE INDEX IF NOT EXISTS idx_deauth_src ON deauth_events (src);
CREATE INDEX IF NOT EXISTS idx_deauth_dst ON deauth_events (dst);
CREATE INDEX IF NOT EXISTS idx_assoc_received_at ON assoc_events (received_at);
CREATE INDEX IF NOT EXISTS idx_assoc_node_id ON assoc_events (node_id);
CREATE INDEX IF NOT EXISTS idx_assoc_src ON assoc_events (src);
CREATE INDEX IF NOT EXISTS idx_assoc_bssid ON assoc_events (bssid);
""")
conn.commit()
return conn
@@ -100,8 +135,9 @@ def store_beacon(conn: sqlite3.Connection, ev: dict, received_at: str):
def store_heartbeat(conn: sqlite3.Connection, ev: dict, received_at: str):
conn.execute("""
INSERT INTO heartbeat_events
(received_at, node_id, node_ts, uptime_ms, free_heap, wifi_rssi)
VALUES (?, ?, ?, ?, ?, ?)
(received_at, node_id, node_ts, uptime_ms, free_heap, wifi_rssi,
probe_drops, deauth_drops, assoc_drops)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
received_at,
ev.get("node_id"),
@@ -109,6 +145,9 @@ def store_heartbeat(conn: sqlite3.Connection, ev: dict, received_at: str):
ev.get("uptime_ms"),
ev.get("free_heap"),
ev.get("wifi_rssi"),
ev.get("probe_drops"),
ev.get("deauth_drops"),
ev.get("assoc_drops"),
))
conn.commit()
@@ -200,6 +239,37 @@ def validate_deauth(ev: dict) -> str | None:
return None
def store_assoc(conn: sqlite3.Connection, ev: dict, received_at: str):
conn.execute("""
INSERT INTO assoc_events
(received_at, node_id, node_ts, subtype, src, bssid, ssid, rssi)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""", (
received_at,
ev.get("node_id"),
ev.get("ts"),
ev.get("type"),
ev.get("src"),
ev.get("bssid"),
ev.get("ssid", ""),
ev.get("rssi"),
))
conn.commit()
def validate_assoc(ev: dict) -> str | None:
for field in ("node_id", "src", "bssid", "rssi"):
if ev.get(field) is None:
return f"missing field '{field}'"
if not _check_mac(ev["src"]):
return f"bad src format: {ev['src']}"
if not _check_mac(ev["bssid"]):
return f"bad bssid format: {ev['bssid']}"
if not _check_rssi(ev["rssi"]):
return f"rssi out of range: {ev['rssi']}"
return None
def validate_probe(ev: dict) -> str | None:
"""Returns an error string if invalid, else None."""
for field in ("node_id", "src_mac", "rssi"):
@@ -233,7 +303,19 @@ def handle_packet(data: bytes, addr: tuple, conn: sqlite3.Connection):
received_at = datetime.datetime.utcnow().isoformat(timespec="seconds")
pkt_type = ev.get("type", "beacon")
if pkt_type in ("deauth", "disassoc"):
if pkt_type in ("assoc", "reassoc"):
err = validate_assoc(ev)
if err:
print(f"[DROP] {addr[0]} {pkt_type}{err}")
return
store_assoc(conn, ev, received_at)
node = ev.get("node_id", "?")
src = ev.get("src", "?")
bssid = ev.get("bssid", "?")
ssid = ev.get("ssid") or "<hidden>"
rssi = ev.get("rssi", 0)
print(f"\033[94m[{received_at}] {node} {pkt_type.upper():<8} {src}{bssid} \"{ssid}\" {rssi:>4}dBm{RESET}")
elif pkt_type in ("deauth", "disassoc"):
err = validate_deauth(ev)
if err:
print(f"[DROP] {addr[0]} {pkt_type}{err}")