From e01bbd9e56d3b08a2e898a5516eab03eeb775c93 Mon Sep 17 00:00:00 2001 From: bot Date: Sun, 5 Apr 2026 13:06:25 +0300 Subject: [PATCH] firmware&dashboard changes --- README.md | 93 +++++++---- .../__pycache__/dashboard.cpython-314.pyc | Bin 20918 -> 44884 bytes .../__pycache__/udp_ingest.cpython-314.pyc | Bin 14108 -> 22375 bytes coordinator/dashboard.py | 31 ++++ coordinator/static/dashboard.css | 21 ++- coordinator/static/dashboard.js | 92 ++++++++--- coordinator/templates/dashboard.html | 7 +- coordinator/udp_ingest.py | 102 ++++++++++-- firmware/node/config.h | 6 + firmware/node/node.ino | 151 ++++++++++++++++-- heatmap.png | Bin 88628 -> 0 bytes 11 files changed, 419 insertions(+), 84 deletions(-) delete mode 100644 heatmap.png diff --git a/README.md b/README.md index 489e92c..ba909d0 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,12 @@ This is a learning/research project covering distributed systems, event-driven a ### Active nodes -| node_id | MAC | Location / Port | -|----------|-------------------|------------------------| -| F68D6E30 | 44:1b:f6:8d:6e:30 | desk /dev/ttyACM0 | -| A1D658D4 | e0:72:a1:d6:58:d4 | desk /dev/ttyACM1 | -| A1D700C4 | e0:72:a1:d7:00:c4 | desk /dev/ttyACM2 | -| A1D6F190 | e0:72:a1:d6:f1:90 | deployed (another room)| +| node_id | MAC | Location | +|----------|-------------------|-------------------------| +| F68D6E30 | 44:1b:f6:8d:6e:30 | room 1 (permanent) | +| A1D658D4 | e0:72:a1:d6:58:d4 | room 2 (permanent) | +| A1D700C4 | e0:72:a1:d7:00:c4 | dev machine /dev/ttyACM2| +| A1D6F190 | e0:72:a1:d6:f1:90 | dev machine /dev/ttyACM1| --- @@ -124,10 +124,15 @@ esp32_cluster/ Each node: - Connects to WiFi `sandbox` - Scans for nearby APs every 15 seconds (active scan, includes hidden SSIDs) +- Hops across all 13 2.4 GHz channels (300ms dwell per channel) while sniffing - Captures probe request frames passively (promiscuous mode) between scans -- Sends a heartbeat packet every 10 seconds (uptime, free heap, WiFi RSSI to router) -- Sends beacon, probe, and heartbeat events as UDP JSON packets to the coordinator +- Captures deauth and disassoc frames (0xC0 / 0xA0) +- Captures association and reassociation request frames (0x00 / 0x20) with SSID IE parsing +- Sends a heartbeat packet every 10 seconds (uptime, free heap, WiFi RSSI to router, queue drop counters) +- Sends all events as UDP JSON packets to the coordinator - `node_id` is derived from bytes 2–5 of the ESP32's base MAC address +- Events are buffered in FreeRTOS queues and flushed once per scan cycle when back on the home channel +- If a deauth with reason code 2 is seen at RSSI ≥ -60 dBm, an alert flag triggers an immediate flush on the home channel without waiting for the next scan ### Event formats @@ -179,17 +184,37 @@ Each node: - `reason` is the 802.11 reason code (7 = class 3 frame received from non-associated station, common in attack tools) - Captured passively in the same promiscuous callback as probe requests, flushed to coordinator after each scan cycle +**Assoc / Reassoc event** (client joining a network): +```json +{ + "node_id": "F68D6E30", + "ts": 12345, + "type": "assoc", + "src": "AA:BB:CC:DD:EE:FF", + "bssid": "11:22:33:44:55:66", + "ssid": "NetworkName", + "rssi": -55 +} +``` +- `type` is `assoc` (0x00) or `reassoc` (0x20) +- SSID is parsed from the first Information Element in the frame body (IE tag 0x00) +- Captures real client→AP join events, not just passive probe searches + **Heartbeat event** (node health, sent every 10 seconds): ```json { - "node_id": "F68D6E30", - "ts": 12345, - "type": "heartbeat", - "uptime_ms": 123456, - "free_heap": 245000, - "wifi_rssi": -62 + "node_id": "F68D6E30", + "ts": 12345, + "type": "heartbeat", + "uptime_ms": 123456, + "free_heap": 245000, + "wifi_rssi": -62, + "probe_drops": 0, + "deauth_drops": 0, + "assoc_drops": 0 } ``` +- `probe_drops`, `deauth_drops`, `assoc_drops` count events lost due to queue overflow since boot. Non-zero values indicate the node is seeing more traffic than the queue sizes can absorb. - `imp`: `high` if RSSI >= -50, `low` if <= -80, else `normal` - `ssid` in probe events is the network the device is searching for — empty means wildcard (any network) @@ -216,16 +241,21 @@ The same binary works on every node — `node_id` is auto-derived from the MAC, ### config.h reference -| Constant | Default | Purpose | -|--------------------|------------------|-------------------------------------------| -| `WIFI_SSID` | `sandbox` | WiFi network to connect to | -| `COORDINATOR_IP` | `192.168.1.133` | Orange Pi address | -| `COORDINATOR_PORT` | `5005` | UDP port | -| `SCAN_INTERVAL_MS` | `15000` | How often to run a beacon scan (ms) | -| `PROBE_SNIFF` | `1` | Enable/disable probe sniffing (1/0) | -| `PROBE_DEDUP_SECS` | `30` | Suppress duplicate probe MAC+SSID (secs) | -| `PROBE_QUEUE_SIZE` | `32` | Max probe events buffered between scans | -| `DEAUTH_QUEUE_SIZE`| `32` | Max deauth/disassoc events buffered | +| Constant | Default | Purpose | +|-------------------------|-----------|----------------------------------------------------------------| +| `WIFI_SSID` | `sandbox` | WiFi network to connect to | +| `COORDINATOR_IP` | `192.168.1.133` | Orange Pi address | +| `COORDINATOR_PORT` | `5005` | UDP port | +| `SCAN_INTERVAL_MS` | `15000` | How often to run a beacon scan (ms) | +| `HEARTBEAT_INTERVAL_MS` | `10000` | How often to send a heartbeat (ms) | +| `PROBE_SNIFF` | `1` | Enable/disable probe/deauth/assoc sniffing (1/0) | +| `PROBE_DEDUP_SECS` | `30` | Suppress duplicate probe MAC+SSID (secs) | +| `DEDUP_CACHE_SIZE` | `64` | Number of MAC+SSID pairs tracked for dedup | +| `PROBE_QUEUE_SIZE` | `128` | Max probe events buffered between flushes | +| `DEAUTH_QUEUE_SIZE` | `128` | Max deauth/disassoc events buffered | +| `ASSOC_QUEUE_SIZE` | `32` | Max assoc/reassoc events buffered | +| `HOP_DWELL_MS` | `300` | Ms to dwell on each channel while hopping (13ch × 300ms ≈ 4s) | +| `RSSI_ALERT_THRESHOLD` | `-60` | RSSI floor for immediate-flush deauth alert (dBm) | --- @@ -327,21 +357,28 @@ The `oui.txt` file is the IEEE public OUI database (39,171 entries as of downloa - [x] Node firmware: beacon scan + probe sniffing - [x] Four ESP32-S3 nodes flashed and running - [x] UDP ingestor (`udp_ingest.py`) — beacon + probe routing with field validation -- [x] Web dashboard: Feed, Networks, Cross-node, Clients, Node detail views -- [x] OUI vendor lookup in Clients view +- [x] Web dashboard: Feed, Networks, Cross-node, Clients, Alerts, Search, Presence, Node detail +- [x] OUI vendor lookup in Clients view (vendor grouping + randomized MAC detection) - [x] Dashboard split into HTML / CSS / JS (no monolithic file) - [x] Deployed to Orange Pi as systemd services (runs 24/7) - [x] All four nodes sending to Orange Pi, confirmed live - [x] Node heartbeat every 10s — uptime, free heap, AP signal, drives online/offline status - [x] RSSI history chart per network (canvas, per-node coloured lines, 1h/2h/6h/24h range) -- [x] Presence tab — Present Now, New Arrivals, Regulars +- [x] Presence tab — Present Now, New Arrivals, Regulars (fixed key mismatch bug) - [x] Deauth/disassoc frame detection — burst detection with multi-node confirmation, Alerts tab - [x] Alerts tab — most impersonated networks, most targeted devices, burst detection, raw feed - [x] Search tab — cross-table lookup by MAC, SSID, BSSID, or vendor name - [x] Node detail view — 2×5 stat cards with animated border, fixed alphabetical sidebar order +- [x] Channel hopping — 13 channels at 300ms dwell, flush on homeChannel pass +- [x] Assoc/reassoc frame capture — client join events with SSID IE parsing +- [x] RSSI-triggered alert flush — immediate homeChannel flush on close-range deauth attack signature +- [x] Queue drop counters — tracked per queue in firmware, reported in heartbeat, stored in DB +- [x] SQLite indexes — received_at, node_id, bssid, src_mac, src, dst across all event tables +- [x] Dashboard query caps — 300-row limits on Clients, Networks, Cross-node to keep UI responsive - [ ] Attack session reconstruction — group deauth events into discrete sessions by source/time -- [ ] Scan interval control from dashboard +- [ ] Surface assoc events in dashboard (Sessions tab or Search results) - [ ] DB pruning / retention policy (events.db grows indefinitely) +- [ ] Scan interval control from dashboard --- diff --git a/coordinator/__pycache__/dashboard.cpython-314.pyc b/coordinator/__pycache__/dashboard.cpython-314.pyc index b44fa1acd3582fdd8a81cc519329c1300b26495c..be954bc740f2a8bf7b67c5ff137879c9a26a1031 100644 GIT binary patch literal 44884 zcmd_Td2n3EnJ4&O-8ZTV_x%8Z1VMrT??VzvgCIbHAVKO8EJ`v-bOBW)N&-=(R|Ouh z-3=epU9{v8)RIGxO^x7bX_uO@r`a?1geE#VT6RzOcqe*y0TpRLC2m;Tt=J9kMofSr zWm?|(V}D=1vkriVQk@0 z+yKVZYXtmHpaMHvF3U><1kw z$3bVxdC-+|HFEGX_qh*xQl5j}l=q-7)NuXxbZ{## ze~Ff@CiyK@x=i{nswAsu+iF(ID*GLmWfvV?a;vYc936~E)MTv}^Yo@q!`or$EX zJE_-%hWRtRcoMEjdXDrdAj+@N^wOsP0}rc8{TDHoqTQz1TgrcxX`Qzf21(=3jkX%*vV zHi(Hco#OLnMnvh%IWc+W6*1N6>*iB6rvc@2d?P2iD>>0q$&L9_wW1gAKK5RBx~_>k zU8(lUR6XMJEPe^%_*t9=F@X0Vdv6pc#8P~f(bpKC5>8ib;l*wVz(!#BEiEA4YWhu`eqhh99y0v`5^qLm7=? zN2Qh`)v-*9P8DC`Mnmk*WnypAVsAFY-ndNcEn4iYhS-~yiM>sYeYzU$upJoLK^Ph1 z)mo=EFOzbono`_lC}+zuu^-c7KW>PtXt8%2VsBd}_8u+vUPJ8d%f$YY7W;zO zh5GKxtM86w(sgU;E{gk+t|u?u&Slc|YUvIbpxd=f?1NhDK11xsmWh2xi`@^14iiLL zyFI>4nj>180YeE-oZc-S-T4&9MKDX&5oO~0_N4Zl-gE2^4P9M%#V9!@J}HXgadB{m zMfL=R;8(G~wo8+zT_LNL+4f7m`uwLNqCag|XO$sBkelES%`;a1FGXv-aL_ zGSzjgFKZX0&rcvh)_ZVp@L12Kk!T_ni;p9U=iuOx!(uds_u*}(CX6b+w8?wXgU zH#Qbkzb2g;NLgnpdOk50PDPWGzK9Ba$7t&OSk{(Ig;TMSNw4ldl1xr|^v`FLllJ&T ztRrj@h3Wkku zTTJaWYFv)%G_B&=%)g=(S<6T~63yDAXlgJ1#%FD3!K}ryeM8gxQ(INDWWPBW*54a)BvYJwsQ`{|g^@6*8-d#TvnQQ6K zxDU@+4ohVS`huDlImwTXjbhC!xe+UCNrY3Qk{7SzW&wQ2P0DJ<+KV|| z6wnNZ8qLf5ZjkWa>-8Z}j8jwsd(hlyaM83w=?ahYL;Qqk!aStSrwLQYQ4;^4B_(VjYc5{;lc)O%irM4`;A(SDz(|=B&Nu4mOq=djK#(gln6^mZc9kfv$0EAn>?PA)EU+Y?2*T(b7(9c zjtqgIN}CbZg}>xa;7oCg)wNUmZn?cL?YOezr9D^n%(N`H8{c;~E|vt+&Rb4zIx=(e z+EWY8?Khp<-);J^tUSGcv7}-qa-*dA1Ao?pZ9anc4K+r(Qer z@|jHe>V@(R^W__^ja=W7Dc`YBzI(oWccy%Ax;Hmgy7!kQdFYiHhtG)0*SEq9*5_@$Xp?XMBgusZG(eC0={8)qClL9XSoE;?3mwcII%N?@cE zr8%NmUm+KxNupH`7j4F|dd9DganL&Ikn9Ozh1RY?hOw@^CrpDT4Sv8>$BE8W+#t6S zZ4vFp@)G7#G%K;E7*!cG>(iiU+Jd=W;7&Qz6gDmbeAZ!fi;f9XJ-3RBsUzGq`Db9c zh!02#YvlnpG#)-5Z5Lw6At{Ur^n7eG8foiriQ0#7q^mnAjE2E(rADK|SbQWrHg;e0ea4vI1s zO@i}_OM+f5E2_hlwJWoFuhfD{WjTpD)?))~z8iRK;8#@D0Zi~a@Y~yFn)KyDI|#C5 z9*d4kK7>o_$k_%b>k5U(CZat=$E5Y-VIKlyL;LqhGz!}+5>e}{i-A5wpl4k=Y|=&q zCTRc>HIhk@3q%Oih$<%SMF<-N-^U9Efrs;yyt-<7TUDY+pdqiyE#+6=UwS$^}hG^Wy%kvUAM}sXV%T8Zq}^N zl((l{_iUUaxLDFU=V)E@`d@nb%F{1=c_#Rsp{d?mp3;~4uk_E@W}O+&x@$di?#*+S z&A++p<~$X5Io{fg(%(nvX1xA3NlebwF6+J;?q@aLee29WYvbYOkQ$oAX)xW{$?CE) zV_Iy=@5H16ZiFUNnUw!h*I7Y;>M{5um?n@2(ut+WB`*d zhF%*#S$RzCI&h>*cs7o47akir4|aU_$*#j~HfbMn%G$9cB7|h^`}YkU>l!?mHH-0! zS+^9wICM5V0w(-&)*ihS9hpc)-{Fa!A0KCIoze>(;A4l#Q#y?77~W)F`j$KR(%x_H zy}BdgUNyD?XkO zQT!P94nF|AQ#XP&a0WbH!9rumXhnUXDO3o$}v|7U$u#@+n- z;0*ub{^`I!Kk)kCyt{eM(tOW~z?W+N>p!@gleCI?Md+%q{&iVbo8_k!?yfb<@IrFI z_v@SO)WFJv-l;XcXwer~tWR3yKIt5TC}AiWjgAjsAd z#k!}?KP61}NnK>srgqy(!drUr{6Yf))9f|Ar(6x&3Z{_6idcs$wk&Sks5&@AnQ#*Ix&W}13`== zrN;pfdOW09ob)uDtSN#;NhCU!3d?gj`m{WkdxpkAmxoB0Gm(@ch|8Mr9A2ilU%Jaa zD5+X9S*_K#yyXkthIwzpg13F%+kWlojCV)cbSqH35NMtcG%p0&<^ye+!1`;$Hv=0N z{Cns9do%v7v~{soxKXqcs7%z5xhSvxzu7%aCwa4R7>l&$cpm9`YONFoyH8@v1HiGjhj>kvzQ z5L>1UvP?HO`jNhY!p!Am08IiKAid(zBZ6GskerVebU-|M;+U}SX%%`c;HbF2M>GTg z?rs)1c~grcBSB5tE*L7RRdX~7@d(OJ<${j-O+dVTK00(h*$zQ^G&+QsiFV;4grGxG zG8xlKVbE*Y9oHmfuUkF0%kc!)R^=>&b*@O3_% z$eKon8Q;RXfK24w!xOQw2&42#2_iL+B~UdRIwT2o{Y#uP=(xinl-b}&$aEk+4|Uf!#8UB(p~A%7y3UY z-)r?ZTX(#-`9@8z?0LJA^97fxIFEm-|FchOIp0QxukQ!D_gZr18=Bx5CI17wJls+s zhxM{N(854r!mMMl^RbjPiXsQpg#k!DC$A13XwLeVh@Na?FyG@`Ja{cQSItZG^W#Xy4QDuAyyM?_uxoDIIgLr?>@owR8-;usSL!`+=TKoqo zamD;FG-)h?dziovYo#Rd)oTHi$z z8VeL876{H&%hka*(0TA$$O38L)gkF}ERZZF2>(B*Q(pUMpVasu<#}`BaG@8QCgE+E z`6w-$Er+2KWivwV41$SkVE@{#(1EoyTR_FSNlA<;GkoD(krY@lLSQkLn++5N(q*P- z&Gj#wie-RkVxxI0$<|rL((La$KG-+VJ*du$@|@3l|44jdJZ~Z_WDxXnwB?+J&=qEK zd5|&O3Vp|gfun=Mz=^|$wWQj#$D(N7+Gx=$#YUkPK^~1;Hg04-(nYzm(fnl;=J=Eu zDw?_&m!3;XUqv|UceFTACMiEnQhu7GmYNyy{ymS2|44OEmj83o5x0iS9_ZILn+<{)lbTv3`w0v5q0seVIRfPr>G6tbAVaoX zs5`XfNHLKjV&^d1YMG5(KbF~4SGN4YQa8b)ydql45Ok=;vKl_R9$k^N8-*v|DIx)k@Z?kW(ZJ z&RVG5vL04)L~cZB6Gox52Tq%Vabj7^v+>xtM0}8}vUNcr7m1z?PmHCqwlLK0;}Ln~ zM4GBLCtEqm3nmA~<`4KliqSaE@=3k;Bw1K!?Jh5~ zyz(SAG8Q9~F6l+YU~|N0c$wnvIv9gjLbF&2&0_ax7Gp~K57u>LN;}_anSF9P_S)g; z!|$|A9h@ueTr914ZRg87XS&~7H9Pue8*bKhW=c0s9lqr+U*b%)wI9{C&UE0d&AMc@ zR63Bqt0aBtg+}m{ftsnlB^OsyH+A?!Zv_@t*yYyvb9wcS0=Bmz8C0j$z59!)66*7 z7KWUaMcJfRlcje2eFb*^1^FdQS*G{6o&5IO&Zc8!vkzUZ*B=$MJr|`t1*sfgHMirUbMp}XO0vd zom{8&WDm_%q7$LcoE%*6apm~9;o~;?bed207{5h_@e8qYW>hMt|2cUsG}s|nR#F0y z?X>6>eX4a6xCI;V0!A-kH}Dv+e>aw_&c`9USi(w$Fpqcu(GO8wAY>!UKSkU|GjVY8 zeOE7(>{y6NmxX;a&yswJOaKH};%uOKOc3F%Ovo7VQL?+FUswS|lOP|D313lp@Z@8{ zmpeLO#4L;}94b7%A}>Vb6^fv)4us+>0>i|~GDVJGVScd)-t?(aUwasq319o#*G>yx zb4@;;Pp6d}iY%Z7jj$?l*jhFGukXWB40kNEx*F~vQkb*8!W2nE$YYO3<24%R>V|?RR-`vQ~x$=^LOkFc^>*;8~a6 zH?mfO6?3-M%2|inep#2^GEA#r9=ynL1WYwaXm?Qbt0OZ_GY8&S_iCI? zO@lB!G(Q)G-GfAg3&z?m))Z{rpLHnBlEj-rg`ndjaTJWP;}TXEupT5+pQH?=BkN=- zG!e1%FA!H|fZn9HX_gdfUgDL^$TLY6M$~rn$UKcdzdq92h{GmX7n?HgdbmKvT*dK> ze{ibrgVv21|E3>YhNyVgYjx9gKe!A=#=q%fk};PN<5R|#@9kVg)zm>*eC!WQw|>_J zMWNe&!!3N|ubhc4`YUF_v$i*4i~ef7INums^w;6V^G19T;loRATca<1@XFC8Kj*Ci z2K(RWSoBoj<ImbyJfR0?f6c~jQyrVm<`PCdb=*;=(t@D4&|=E`N|d@ z{+CLwlw=&$A9z+T`pfRxEk4^+?;RKCuSlD3)iutVXAivX$<%E~o70C84J?ws0@68e z<&0x?-Lp59Orr4ZRTFJ z$iIb*4k@k~zx6R@0Q7l$%FlzU(6Q!bPXfzsLu!lSZTM-z#wU?iw+`J;WiCPsr^dxX z{{DGaJ!0ObQ^c~f2MiXkOQsX%4wX6E4kpBUikL$75F1vE9x$Q6xsYAcECRE2Tz?Y; z1W{4eURsI@Bs6U(gcv&&wn>M|9x@rAasVo4$N|=9O4WD&g~r|1j0#FCH)457Ev3W^>06~+im zf3nt5SfC}b_?3+#;NxHu19nV={|0fi*+`jr)s?8`FVjPP8dSdJpBx4tmnT7l;)3br zxe_GN8509rLX3<$uN5yOM@dYeA%!M}C`hv0gpWk}jH4ldaUl;P0MO;eY`t$(=zyTf8dxT9|n8qt;wbBL6(jTFz+2TC>`a`s6aUl?Ju$<`U(OOjF zpQu^12gT_8$$$F>((rn}#wb9Nu_d)e(J`jC3RP9+5I3pff0EpU?-XgGBFhe>rr}et z?x3DsxbUFZ1@;UC!57|~1XRHShl(k>!cl6Wv#jc(2nlr0FQWa6D^TyZM9`X_wSd+u zl#y6~($CTO;j@K{&*Cy{;4F_ok^NhatqK$GM#7hi>Cp(~MY{q5wC4f3MFC0G65|Ew zH9}O-dd10pUUF^8Qjokj!N^Pg2*g9r0nmRjbYAN)T0dHmrfU-BA`QA{x3F2uP9rQ@ z8!L-Zz1qbH%{E!h7+xsS)gFe#=$K+HU6kDX58XwJ6Tv*RJsd&IOZ*7LQO{w8{{6ck zllSim57xOglCVPG&f2k6FbNFVj`LK(3>h0$U2~GRQ%~p^~d(ZGsGtS0UX$O z!w7*H1ZSO^Ik9YKMv4;YkKwfWAQgadu@hS`V3RzwX*BCnUN>(YCB+PbL$*QtHIlwm<<%5PWrcSl0Grpac;`9-bZ=fZC3s7K-}R(Ar0K;EyQ0#Mi?x!H82~Tu`fD; zGmb?^#f(RPgMh|kTjFe1n=FVxD{=Hk4+!%kD1PoM5cwYR*90O5O(YN@_tl8}TPP5r zzQzP1q^tOy3q-7sD71JGfe3U-ssQDoge^vah$fi89!y;z0gaPEAOVwcP2llk5_pik z#|f*!D&<73Hp*a?VoO*>M=>i8(RoU<+z4q#;6<%c>;}kOAv>&6+^|Z~EjYB)npKK} z#3v`527~(7b0G{l4F>fth+D{lWj~2qLat*0>aMvD1VTCbHiJREJLE2wLiDJj3Z(Ur zzpVcTgm?j ztpvgR^98LWdW>Q?v{|`~n*_BP>et(VHiH1DSPO)Fx%E{s1%K8a{Xzkr18ST^NvH(5 zHzRldDN-4*C*&7{MtIcNBo<5r62VXaF-lom2k}+|N7!bHkSF&%h$DO_C)S6& zVne7zYz&o(La3tCu4!1UF;&;<)ZTpB8~oauenoa=(39A)T^buWlhVJ&xAa4D-hnfD zSmX2+sheU2teirUx6MFX4dmMRs$zxk&q4k3GSohajVQwUCnttemlM(4wbybBFR+tE zY%lY;K1DslR;K#ms#pQqDhO*OSsZ`@R9hu!eV?AV zLJwmbb#mJ=uZOWxsl({qg?Z(5n7mXE)X9#YII_08>v#{LU_foj-K}!BXcY#@cazX_ z7%?^qJp=m{D3oqfWN<8(VJsO=CgUSTvqZHbN;r0Q!`4~D@FVrn@OSRh7z}yEFkYbM zCydJ*sm@}@cJvECcwQqt2=Q1>s0G-5*Mwl1j<1bjBX0DeOhZN$0JEl?Jf*cLh23W` z=fr)0(Heg9KJ6Vz=2_1#HyR1JhiP$bU@8gKCHK%oGMZ-~vs}BYDQP!uk>Rvl+bhW) zw*8f44>cKReE?|Gn&x49K-P@Zx`-hBCxuN0J1shbEO%T2;N==?#fH+-X>+dDR!FDJ zxy6WRVL||&KUt^35>BDWqXtO1Tx+c`QYqCkhQ&zrXbmMJ@SrABWlv6=KOe@aChk-`F{E^zY?=OnExwWHh1dw}nIIu!$MCW30@&z@4Y1hT+2+Kf1&_BCHB6*Z*FtH!o zIkQab$WlLLFI{*niUZQ58_0nPdH!FlvLN#d)+_{9%?DR41lP?6*JXn3kolEXT&-HD zUjKge`s-acs<+=P-M$d)nh$pUeAisCD-%2cnV%s_(~at_H%qrJ1ozGdN#+;an+YDD z>VwSBNBi!|Am}ptoKw9J*^!5@jEUDs2w3m)RZjK7e%s;uVBN+{`KEW)%sx3|f9=`n zXWv+Z@akmZJE+-@9dflPoMklp;`VedlzaPgw*RxmpRFAPt!xs3Ay>RrloeNgM zV2d^(I?Ek6jg84POPwW^QwQ!=aivv|bylO^o>J#j|GgT{QTjpqj!fmwclu|apJ{#5 zHRF1xAN8x;xm^7!>rkua-1;@-)~`yg-`3rk(%pZt>zY!(*)?xF-!E(X3s%Y99|r1B z$tvMZ&+DGqCuc9d{Y<86Bh~7KJ{VyI>w&PkCcxTEu+}-}ELC@EIA60Wj9vXBN2Y!g zGf$s(k?@pPP1Jd2q;5xCpnO@H(hVKe()#jGmU%Z9d&6-dVH|~ zIj1+wJE|5N+SzvIjH4E~^i?AhYEe50MeR%|TE;m8vo?rm$qD@Vxmo)){(o}4yLr)3 zKI>j|G|UNGAuOfWz^rGf#6faWht>AkC*5YQV)JL8Ks>ztvrqQ%T-7f2v4RX2A$y5>40(VT7z~Qg1w0RX#OUJ_Ck-o^3jo z=!p204A53%6eb~cK|%MplP20^OfDULN55D1%ip^E7g5<`hs&T`Yr}G>l;G545sYA(5!#PHvWu zwJ@Z}H3l>T-` zD!Vc4c_RrW?Rj(P(!Y=+w+uBEGgJHC$r;%Y?Bq3h4;$dr4BS+#K`NP-hYlw~a{j7}VAB+V&tDf=_YeFX0k6<2- z{!lM}McP@*&<1Gsgi3L4KHWgzc+mc=_EFGz%qH}p#6~t^)ZIL+k3ya-D6`Y*@W(~g zAB=G=CJoehbcNB>mXKJ4=!PT(%Lv_k2k|tWe0zauCXNh1gca>FHA68(RLvn)mQfZ! zvz){LIW8w=Ms*98ponge5)>{r5x&eS^ZphShx___guM{yG9fHvKJ=*}Ut2q|cG3mE zdJ8a-ks>PEEQ^SNl#R)%MHyI%taxfw6gXJe7WpgbzJQQrkuQhQVMB^qOL|Z3n~>t5 zCBnyxpje))^83=GBAE##LFp7p=5hkb-#eLF3CHrZupE_ALk)P&mSFhD&SN7l#Fccc z1`fx50;nI*)rR4-M%1IfsFX}4VEGL|QF0n#nL~Og9-c2~`k<^Ht!)?(`Bu)4G{;j; z4@Af(h9RF_t0Hhy`_495N!quJlJo(I(-vJPJQ*)&md`T*QOy)0x*Y8Lb77|xkPJV> z0TvWL>r)PuSB`!N|8$woCy#yBc}Swlh$B;57gQU${(gERb?h&IX(hD$lj+#S8CU`_ zr+3J$qxb6rxqai|*jPia&X%Zm4k+7p3w9%K0r9%?44fzUW}Iv~g=-Uf1!ajJJZ=t| zj-f;4KkD)+Z;5ZZ#7$WtFlv^U`E>B`Hgk-ZM5Kp7j&&@KqlVfnSyRWxEdN}VzmT;* zkJJ3&sUGt`CClHPPn?U6M=vF$J=sQi7jP1XagK}{b~LLI{sTz^Q6Tr5Ujg_z|AE*4 z=;hS(^VeJ7ee$nQ{)dx)`Skqe-q!+u8u0KI>L71EqSKN7i42 zkdO4w$oXS(m^pGaz5N3@MCY?s9I20U)4xR?WB{180MJxhne+?xF&29+N_(hjV{=ma zfMR9IVG50J)7uN=kZQxgSbj*Kx5)WF$RR0eHh}A)Mn;KalJ94MYQ~fpm3~PPJ|gG; zf|D$RgA_RKr#5h+jsxqc;+>zAY} z@020EgpKpU#_NIWyM9vlu9Ok>L3%%)VMfrwS{x7~v`xDqUI)-5g?Cgt zu$LP(tGp9%lm?-D2txP3v;c)gHMFpz8rG^Rje69hdBe1K-qElk5?DRu{GvZPm}%N3 zgIaoBF&YyWW1}<@-?k#AAt1K+^VWaoK>A4b#A&A!P^!uR8Eb4Xi$QcbCqi|fi~!z zs@JFKyrAZm^x+T78>V^ew_otL&ij?Key$H^{JR$XUz+!S>Ak>v+yA!u7yf>9hKk02 zEMGOv-&);yoxk4p6HjLK?sw0gkZoB0_aP{JwUwi#) zSN|yEKk)Ow9kXeTFMVM8$h@Z+ZR{zX8u;wab3EFBoyYKZHPwfGw!hnHgUd*?N^~L3 zm30<-@G|ufniU){ZATL2VR{z%{s(p%3)8d=+*j4|*F;=ZRZ9yWyv@P6np~ovw^G+4 zZ-0gP6slS@u&a=R@tg}0RWK+raPGJ&`csbC=U3qhtz45(C|Oi=;M(7u^hPC%i-`_# zWw24KOy;C?>DYf?CyuK)iD2@%&$;3D9l$U>5MZ){1&>r34_nMVh6$&J7sN-poP(LP zvN5%wY^@XJ$2+5)uX_Y%E>I|mPj%JMDYS?D;A|7O0ZQ2v9b(otZ}=l4w<`17IsB-y zBF`0}-ADM5lL1>S8O>3Rvc}wOHKtdcOi7BzgbuPvaIx5$#6jk%N${r{(}Ky)v(Cw5 zN*T|``0y2MfwWU%4L}u_`ZBBl4B1c!(E$rApo^R)t1lpxOetpgQjxREi*v6s=c+VK z&Y{Q}pQ;41rWly~q1GhIlFDe-i^7@kS39=6{R=%`y%?C9$STjKn}I3C&}*e<$$5^P zF>=PqiIejbIo~Dc5**yY2R1U5#LYsCcVx_BgaWJ*tVkp59OkUEC)6`AIMm&B4Ckkh zQrbb5HWE#RV`EZ+d?YyXr7zSdGW!;gd0$oqCGI^$V^rCPk)xbW>%+m#*nJgD*T6i) zQ;y^P8gL5U2PLJ8-oSLzcTVEIDzYNM!RnasSTwh_q)(_)h;ErG8bdppcG(!(p?u56(C)TaHioucip8LLHt)e@ zltxH3`mIM(Yme*dp!65*`khE3Hd?HFc_qbAF0XVR0!k7ItU4I4Q@xc7a(dxfjWFQ?uZ@i5eB>V9qtkOdf6e+ zJx}30YSMzybrZ_jrNY{rWRw(xSh3C%j){Foy2Pi2{+_1=+&p^}hgpj~M=>*HTl=c5pz@&U8tWsYcuQ^qv7RkkB9;R|) z8xT|d5QxzxpMNslET>k41MMyky$@-3dgxx--5#n}PCf?g%sm8lawO=*`wcTsnP>X0ocQQ0m;ahAyj0%R7td<;^-s84ZFWa=QtDIP<% zR+VEc3oPsWN=ZtjoC+Bl^)N+tQIp@rE!soz>oKX%^isk=ESdsHqwN-MjDFV9^;oiJzytwGLqwX1sjH8y!zhNVo)ZWHgEmI{zPn|8J7H zl7L}~`&hf))!})`bH#&mO)5NhIh)f95xmp;FkW6t=taUhEun|JJn*W4*Mb8VrPVBP zfjW2w;Mqd)+*jN$dhZ=Dv5R6~s`+-!Z<)0Em98E8%B(*tbMIU2oGkA-er(HTL8K!t zh5g~==x{tNMM&M3ilxS)S-unRK4`=e@$n=!7}B1u5q&W#i?_cCk2`DO$T~<}-mBlG zsjg~`Iz^L2%iFB;;NX$N>>}rPI3@*<#V6L-cqDpBGJz@Df+c;@kGP~H{McQ2)rl){ zaR=^aO!4>)e~_On@1z}1v60S*T8EC2JV{xL@iYQj$B z_zs_S;rydmc!&-Gz@6%>3XbmM$5{;|9^e`(;Y>!Q3qV^kDcKRSlPZD@30g%Q*PglI z-Z^L4`Pm%{yuT4F<0s1stMY6v&Oc90DsZ*Mn6f9Y^V#r)u-wNb3&2yWV?R{9HV(Nq z&n6`|V(+HPkcGF}D)zbd{Wsh_bC#aZBnKi)x;n#&SSOK(T=+@m&02z#6Zw5cm2Vh( z3T{+J%mU;hcjazEFN)ho@Q}T{3{S-iyRp4T!K>TLPr4a2^0b+wr6F*Tmb=pe=YCom zx>5poqMZ=zDmUzPAuIfqw!9ov5k*^GO#JHGIV!t`|y&F92|2@(6UFh zea5A*njnb|B3IA!Q93XG4le_0&YB@nmOe#dCfJk$K$`I|au?xK#*b8rumc2l9ilUY zdP=6PuU^i0nx=YgxpCO`4R_0&r9~Nn^^N?bS8i?9R6zz(b|0a^ze-@{jV2FkaHRmL z93UP6h<6D8a+_CY+_iI-+Rr4CuTFXxK#FP2$mfLT#Uk*y2_E&v$CmhWx2UM6FjvQLe9{RDF^@x?Nu0_W+1g0vu>3?VsJtO%;7PPcKTIRwI^rr+A4Wz! zGn-!V&PW+gGiblBV!_uq?`vG}t(o_&$@td(aOcAM-S4m8{a(+F^@ncy4lQ_3%zIDF z4UNuuPh`9?=&$G&2P@QE&|mq}jd$EuPYv#3#1`0!sb2Xq2VV(Y5u7$Jx_vj?b&EB1 z>0X?H=W-MEKg_synF*SEjB>D|y@?V79Y`GupGPBCP+Mg^yie3rBU=x^-X zv2T|;*UOeuIplr*N(( zNWO``I|7`1!lCbC&1Hp_5Emd2Kn1h6Vk<^xT&4&T8fo&>SUJPXvysebdVDI9%vq{)W=BoBk;^Y49ihom6ivErPoV^+ z$(qB^YeB%PWk**xF;!5<!iN+#SD~D>It;BW~E_5HjLUZvCuRN z=dCU5r*N2P>z!WYz5Fo2Eg<|^a5}Jip&r8?5f_i)o)BfW9z_hLmskd3uM#GF16ZD* zZ=`T{CRF~oNXJbp$q8%|LV)yZ2{mr zSCn9Ky+Z29bV%7eKo&z#gctHUS(_X*F!{$CrG)TSdz7s1d-uAUKxUaxl<@)pHIRNa zHVJW5?#LLPHN_&6Hk=d`eoUA&xm=2|PSzeyULGHb#k1DrSTvfzeV%j#ku@@LK9S7& zx=9s3HWrQaNK#yCvu5qgOeq$Txg&N0Halj<3>PP3hcA!C!x3h}!wQ!wdtRhh(1()$4tS?Qzhr_j^(e8@fi&JR9~+oIMLRb^Kh`G#&?;wSr)Rm>kWlJL_7 zZ=>9mF&1(mQV+tW3FDg-jDKP)vj*(GbHH`i zZl}{z>^MC|79iHv<7%HF98&~?yP-+1CPI7+va(70#t!x`Hr>c^y=~^KWi*bfUoB+s zK}R)FzsZ?731|Ss-TB|(6k{bJ~q9iMf) z(K%P%cGJJ^HU-??MDZXR<2&BxHoRE&;>fF8(xcyLnCCWpa@vG2##3N!cZfK79unDz zM8^-%WPf5jiTj*zOqu34S$_<-=*8j_xNdtQH45H97>kde0|$vq@^G)PEu2b)N1lUJ zxb(POQ(-I`zChP+UWlbG3!SiX8iSz~b_T{LQfiF4!FU{&o8y;d zg-wk1Ba)33Au_2(YVRhAyRcbcTW8_M&jXu?F$k{!in&3Y>aR)0d$N+ z6U^95&93qIM1qD9YM@wHC5y(Z?HgyP7h>bse~g_qz>p9fi=GDtq@!}3-ADSmg|jfo zghEF3tK7QbV)(Lv+aDvz(eQHxEl_&^W}XQF7bh+sr@pTT#R~a3MPfv`l4?n1Dum}) zjw7+eXjD=IE7ib6I3`_;C8I)axJM1|>OXQ!O;m-BsbNPvKF}l3#g3@B8UYg2pC%0m26bwI}jgC-z;7VICg0YdyYCQYl z_(h?syI*a)CiHb7C56Y6Y~)i%4xeK!f=qCUJ;<{>#3mb#MbQ<|u?f%P(R1+>x?mAN zy3nQLQfv~XQ&&N+AB#cjhU{q4P)F=!1d8}7-z$%Y$7HRHIry2U@M1gK=JQTwP{I-7B*~>V`?OcWbJHkTGk$k zN#oesM$^(+G#AS2r9iq3L9&tKqM{r}PxLXpQ`UWS;BenS&(QIn?&GqEpV4TNWJ;UK zd4e2>>^SK#IU#Z)E_Uz4FzWeQzFk{lJ??Uq5>7V5V-{^{-?~x~J^e zP`7M^x&4Egk`q(T72;fauQ5~d_qv}m!; zIrlA@@%oYcYQ-z|f>~|kZ>JB3^5LWpm-6AJ50CQUr4OI-Q9>VnAKFZif z?@~E^RB%>1l~YL{Rmw*-eblgz-lbals8c@b>7zl3)<_?M^3g;e%}TUY^wA=JELu>J z-Uo2^u{g3T$JcxS;=x4=0eX8cvYztYAvWRD{pcJ=_)lG4-gmo!t8JM*n5o$?WzTRM zKDGJzEuXHn@cw%~GrtOWdaQ@P$2s`wPpcYv|I&V*Yh3$o>K^CfOFyj%^8QcPwZa+X zaXb8{7kIi!_0wVVLEIejX$S&yL`qq(&FYh5d=Jl8+&*UF?7q3O^*3zoQ|1pi@3%_O zU?%IIRDQiOT{?4Wsxrf`yKUn6Rd>vsY5fwtp%LW>b50?{H&N&=3T>v)U53!c3@=b< zFHgZu6x_>e;pw^z%7-5A88NP}FYbmgr0%-%J3Uo~d(ogs`P-q>6HfW*gmGYf)0_6H%o1k%_qzy~-hR&gY zL?HiKEfw$@OHETC6lNZ61BNCJ`Yh#?6iJVN-y0M)o`c0bI9&F*r)Z+UfU zQk^=~fr4D@La;l1HW-niozC=}yN^T*sMBR~ z?)mOL_uO;O<9F}n?LSG+pQ6q4(i|xSex?350^3>$`2ar>KTDjMe#%6Mmy8j)BcEuC zA8cOHSw`+6vY{uX+pgKWQ?=A?hvq0F1E#*>#N?T_uWW`-a47lU;hK)jW*X~~jm=Vw z)3Uy{nywi&CfVFGfo#tw za%w&aW@v>2h2>-*FX2zEXaGXAgO(B50WxVq#wn)*pCR~Sxl_)BuPpouQmtelzlqA( z(32DIDV1~c$*7zs#LET>WZERj`5;gb7jVgi@Kq$n%Vj6<#e%PpOMovGe5G6lyi4#^ zayjJBoB?Y&kt^iNmU!s3>VY}sq&c1%kVVyf8{!{?j3zm`YQQefX^CfvT%DhsqRsu> zl$jYd32(@AXGyznAni^{*Um_fYPB~IpO+M`n-SOM-9WlNAw5t4Zf*dd%txODX=2}6 z-F0V<5_v&ls=RR4LiN`bCvsy_x@nel!wsaHlhTW3Nzb2=o}2V0F!JK0c*`vD1t`w9 zm|UZBD{d;erG+zkKBs1BQq8hiY8nTcViv}h!#=*GctIOwJ<1Nu~~f(nMJ z7_KJBJU*0ON3$m%N_R_Th0v=LRsct-Y75*t#WF&)>@|ZPX2ckj4;b**qY6 zO20Q4Rk|1pGv>k$i)X{_J)X{$y5Vj`a~qkP*Jl-1&4D2my;tpWxr7aR3Ty;+6y6by zFgOs%yvdQQbh-_fjco;ds_qO1G^I&T=?I5HicbrKLpR$^8?J)87$aASfo5DraCs3%cC7Y)vcxV5x47c$s%ZjahFLfnc>TPd5ew5bbneZ)SZL|0uVCX7il9 zYu{kYQP?P!)h)^%#TV5S-MU@Td_&$~Fs94IR)G{-jidvJZe-!zs!o{-$0(_uakU8& zlI>-^(A&km`R8Z~Ur^wz7YQT9>|6+AH#2T6-%|j_O+HbunJ#ZcVI%8{23i zBVum~21WuJ^FViRBKGLcm}9@v7WeZ1lsCc_7gW^J1!xxdCWtCfsfzefT^)C5%o1gn z64F!_fswuZfyxd@n{$<0R-;?l0Fax7@_ras^MPQMWMY*7Ic_|@=|qZPMU5<@@1UnA zU#&VzH?UAlCHiM}d8EbNqG-FrY=_EJ7#G?RLdM8Nd&U$=&$3RF^i_#36^DpfDitXKQnM}Ei+D^Bm87dac2fu1^$fCJL7i% zI9sHCDjVvGl2b?%Ob5d%y#Rl~eH;}<_d zP!*r@6uP@Mx65W6+q_Y>V7nG_&PGZ}KdA&&W$+g<1c=+pce#6|R*6sbmQH@?E|m=X z##8uvWd9YKB47pD13(_E>ymh2qxdY)>v6-}Qs7<6EDzy;wQ}pV&yrQUPKUne7#3exSRp z3-blC`pdxL1rSp)D=@hm>Eg>sjsy5Z!?Gvy=l@$WV^MiugY<6XQ;kMm-;v2LuE-U< zr!A8oYAoi5p?#^*83&pieqw16KU15_Z)s}a56^M%Xp{5Etu3^Uhf9l!2hpE(E`fCM zFzF`I$#(NMn(r(4cFgt#qS5IdQA1GBe0b5q{J%o4icma8j{3&S$5)@IJsO4@{K=x0 zQH-B%R>MqF{JMD;9dU3`vi<|8dmzJ7OaBf_76C;@xD%mLpw zL!K$Zf493DaPHIHuNz0V;cPFGK_pnlOhK|836u|Em}o2vL^sC}5As;#bP~076AMQ} zeu0F#t*gJQcax{1y$`?~qZ~tYvtQA?fgrmZdjdd2YE|L+7(b>m<3NUIRa_VoeTq!_ zzLiHIH@|=~7U%#BFiwBW8}e%TOZN=(;Ar-dL2C{?gN*nu@e;%DBMw)pY^)?p4J9N% z-KNP)qh}3my>J3`f*!U8h;H_UNAMI`2_Lf|)}e&dD1#H!H>8Ys*$EgffY7L0dDp?U z<@WE|4>+#Yla!o8MTd$WDEgO3B;IapFE{)y0}ou{vT zQ}+ZB=ovd!Oo5PJ*~3nQNDJlP|K73rLfYDK+igOaoq~~B@-|gryA+R4RoMiLETi0W z=;7uc0Sl-jrdrlqf2K7yb690hgLFIPb%&kO8Nz!GudWk@QgOik&w}1aAb$Qn1M^l< ze)jMVarY7jC1f2Qxi?F`b?CMPGDvjJWdh`QhiIVOc0TC8C5S2vaGMT<*f{VatJoxP ziHZeQPeD`S2k_6JV=d*5Biib?@hZ;yg11tI@rhIN=OD2WjY$LM5|sUi_QDHx=Xs;^ z8he)T-yG>_i0eYY{h}^A>PjH;FF>H5@;06;!OVWXt|vZ|UuR~Tm^oAHSm$?q-VWyl zd&zlY3EDBK9jl;r$k|VR{{Z^ms)(+|lCCINB{m;IQqN;Mpe02HBW+ z#t+ecc(E-qVEy&FsqX0Bw5hLak57rSxqy!?Xe}&E;`xy*b=(r2~Z2y zRK@F8mYT8gBat_KYk#^2rSw^J$R%hpZiW_o1ExA{+hL} zV5ub2Y8b1;G8W-$f-kp$FF0K~ibwC4#u4wT>RcePCzugmUB;_Wd$i##Qhy-lfbVG2 z{-J}VZ<6{?)>9Z0!3FD2;X*C!A~f)Z^!UR*kB7YqU#u0$?f4%wtO(iPBKb8ET#Vsf z^vrm?8sNZ-ghMJElekae8i7X)?sD)z8V(0Sy2T%0A@2yhr#znRfuI5lwW09iNS;LU z6bf7MA&W?<+c)&C>)~f7tfK-IYET3nBi^ks5ErM?9oRRFB!Wahfv90oj+j^iz;%N+ z5{W%LPGcV;s6a!8(8dr`7=i@D@@813qQ=-O1dewRO0*nz;PL2|=&pb-%tE?>MML6E z56(m{id48YmZH{WBVm6usH|XrgAuGG^#zHaJ>#NP{Lg33x~^CqH0^RJaaNt|zED&@ zX1PG>ub4Av)0MgKg1(x{4Nq4T!zSYww% diff --git a/coordinator/__pycache__/udp_ingest.cpython-314.pyc b/coordinator/__pycache__/udp_ingest.cpython-314.pyc index e8c0c4d4e3a470dc63a860aa20fc222e1eb0578e..91d86b47301e664df615f0d204b0d981223f8b92 100644 GIT binary patch delta 7291 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 delta 2759 zcmZ`*Yit|G5xymPr1+9V>OqO5#1r*0t%qYtc4S+UX-TvmmL+9ffHpr`1Fg{j2@Va4ra+Sx1uQo)l3zt< zmeC`CbHIH!JF~m<&Cc%Zef%?WD^uE5>UK!*e3JZAI@WN#w1&KTz4c%{HR)Dr)@^h0 zbUB*8`tZ90l@>HzUv8ap_K>2LT6#=HNq14%CsC_US}xb!)CO{ikV~l@WQUN;s1szD zkjv*KO2$08XU@YdBa=EmkC}WdqY$Qee_5pSXW#ACLi&?z^~PZ`Sc;1H2~dMkT(aKN+wwoHd^?4AnfP>Q4mRTcjyo| z0zuc7+vc!-W_QZAtr}i`IKWH$I(Q~{a1VEqD*Qh9IhiQV3R4)i981Pm5=mBvbB7SZ z0NcJm%pud3mKKJm!EomEEYsqvEW4nwgP=(jNV ztPp9Dp*eZm#A@NotYY{^;oFTL5NADV1aE~Wnqy;m??NJ_EWnmZDT`SK6pkveheX7C z`ogG)%$jj<6ai_l0R*H8o~rr7=6&)hXlxXMEmmS3MB|WfeYr5&dXEIeocmGThR_Z$ z$xfm;!s{aAZBMWR6XA2$T@?F|@z)|v@+63t`7a{x^8U_=J^oBLq{5ve%_I=RzE2@w zz}vpCJ<1;*4K@geJNAfqPka~^xXJeVYl^vh1{62o&1C>KrGI!1@V9##`OC-t2Pb5+ z??~4jG-6D+%{$3A$s>Wc!_Q*5o;jzd>|GeA$}!V8&h`4 zuDbV-Q@(~=gX)BN!>S9gQFYTMcadHl|L}yJf6?#zT1L%ZWkj0!^uRM;a%n*>?pEsF zUbxj#TuP$w`_V@CzC9evQ16cH=~<2%-+tP*GuCsyWt%--eB-Kzw(l6dyFsd62x9Nb zcc?Df2>~5YOTqG#T1F3wRx!*jwVYo%`8Mh1{*l3X!=kURYKFB~lMOrE9ZZLYp0}MF zrc={%%32w)Pyrb`8dO4L$u+)G!Y+Uj$lD8=3N^&W0>g&$SKuA#{ zmE1cP-w*`@YXifgZ&=U9CMKq44VlKy#8igoSQKKzGIaLDB*Tg&E-u49c6vHKG&M0r zBNc20)w2kD?mB21C9416G&yd9h`*1y2mI1ZJ-<2A5rAK_$IlTD@jl)MKN*jeQsVuz cNg|F994`L1nJ}-K?X+Q`{%?x#Q?tSU1Km7i^Z)<= diff --git a/coordinator/dashboard.py b/coordinator/dashboard.py index f10731e..db56b71 100644 --- a/coordinator/dashboard.py +++ b/coordinator/dashboard.py @@ -778,6 +778,37 @@ def ensure_schema(): 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 + ) + """) + 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() diff --git a/coordinator/static/dashboard.css b/coordinator/static/dashboard.css index 0672b7a..5134932 100644 --- a/coordinator/static/dashboard.css +++ b/coordinator/static/dashboard.css @@ -418,10 +418,9 @@ td.dim { color: var(--text-dim); } td.muted { color: var(--text-muted); } /* ── Badges ── */ -.badge { display: inline-block; padding: 1px 6px; font-size: 10px; letter-spacing: 0.04em; } -.badge.high { color: var(--green-live); border: 1px solid var(--green-dim); } -.badge.normal { color: var(--text-dim); border: 1px solid var(--border); } -.badge.low { color: var(--text-muted); border: 1px solid var(--text-muted); } +.imp-high { color: var(--green-live); } +.imp-normal { color: var(--text-dim); } +.imp-low { color: var(--text-muted); } /* ── RSSI colours ── */ .rssi-hi { color: var(--green-hi); } @@ -436,6 +435,20 @@ thead th.sort-active::after { content: ' ' attr(data-arrow); } /* ── Enc / node colours ── */ .enc-open { color: var(--orange); } +.enc-secure { color: var(--green-hi); } + +tr.group-hdr td { + background: var(--bg-panel); + color: var(--green-mid); + font-size: 11px; + font-weight: 500; + letter-spacing: 0.06em; + padding: 5px 8px; + border-top: 1px solid var(--border); +} +th.group-active { color: var(--green-hi); cursor: pointer; } +th.group-col { cursor: pointer; } +th.group-col:hover { color: var(--text); } .nodes-multi { color: var(--green-hi); } /* ── Cross-node cells ── */ diff --git a/coordinator/static/dashboard.js b/coordinator/static/dashboard.js index 21be0a3..49dd31b 100644 --- a/coordinator/static/dashboard.js +++ b/coordinator/static/dashboard.js @@ -8,6 +8,7 @@ let networksData = []; let netSort = { col: 'times_seen', dir: 'desc' }; let crossNodeData = { node_ids: [], networks: [] }; let clientsData = []; +let clientsGroupBy = null; // null | 'vendor' | 'ssid' let presenceData = {}; let alertsData = {}; let chartBssid = null; @@ -28,7 +29,13 @@ function rssiClass(rssi) { function impBadge(imp) { const cls = ['high', 'normal', 'low'].includes(imp) ? imp : 'normal'; - return `${fmt(imp)}`; + return `${fmt(imp)}`; +} + +function encClass(enc) { + if (!enc || enc === 'OPEN' || enc === 'None') return 'enc-open'; + if (enc === 'WPA3' || enc === 'WPA2/WPA3') return 'enc-secure'; + return 'muted'; } function shortTime(iso) { @@ -174,7 +181,7 @@ function renderNetworksTable() { count.textContent = `${sorted.length} unique networks`; tbody.innerHTML = sorted.map(r => { - const encCls = (!r.encryption || r.encryption === 'OPEN' || r.encryption === 'None') ? 'enc-open' : 'muted'; + const encCls = encClass(r.encryption); const nodeCls = r.node_count > 1 ? 'nodes-multi' : 'muted'; return ` ${fmt(r.ssid, '')} @@ -256,7 +263,7 @@ function renderCrossNodeTable() { tbody.innerHTML = networks.map(net => { const seenRssis = nodeIds.filter(id => net.nodes[id]).map(id => net.nodes[id].best_rssi); const globalBest = seenRssis.length ? Math.max(...seenRssis) : null; - const encCls = (!net.encryption || net.encryption === 'OPEN' || net.encryption === 'None') ? 'enc-open' : 'muted'; + const encCls = encClass(net.encryption); const nodeCls = net.node_count > 1 ? 'nodes-multi' : 'muted'; const nodeCells = nodeIds.map(id => { @@ -285,10 +292,38 @@ async function fetchClients() { renderClientsTable(); } +function clientsRow(r) { + const probing = r.probed_ssids.length + ? r.probed_ssids.map(s => `${s}`).join(', ') + : 'wildcard only'; + const vendorCls = r.randomized ? 'muted' : 'dim'; + const nodeCls = r.node_count > 1 ? 'nodes-multi' : 'muted'; + return ` + ${fmt(r.src_mac)} + ${r.vendor || '—'} + ${probing} + ${fmt(r.best_rssi)} dBm + ${fmt(r.avg_rssi)} dBm + ${fmt(r.node_count)} + ${fmt(r.times_seen)} + ${shortTime(r.last_seen)} + `; +} + +function toggleClientsGroup(by) { + clientsGroupBy = (clientsGroupBy === by) ? null : by; + renderClientsTable(); +} + function renderClientsTable() { const tbody = document.getElementById('clients-tbody'); const empty = document.getElementById('clients-empty'); const count = document.getElementById('clients-count'); + const thVendor = document.getElementById('clients-th-vendor'); + const thProbing = document.getElementById('clients-th-probing'); + + if (thVendor) thVendor.className = clientsGroupBy === 'vendor' ? 'group-active' : 'group-col'; + if (thProbing) thProbing.className = clientsGroupBy === 'ssid' ? 'group-active' : 'group-col'; if (!clientsData.length) { tbody.innerHTML = ''; @@ -299,22 +334,29 @@ function renderClientsTable() { empty.style.display = 'none'; count.textContent = `${clientsData.length} unique MACs`; - tbody.innerHTML = clientsData.map(r => { - const probing = r.probed_ssids.length - ? r.probed_ssids.map(s => `${s}`).join(', ') - : 'wildcard only'; - const vendorCls = r.randomized ? 'muted' : 'dim'; - const nodeCls = r.node_count > 1 ? 'nodes-multi' : 'muted'; - return ` - ${fmt(r.src_mac)} - ${r.vendor || '—'} - ${probing} - ${fmt(r.best_rssi)} dBm - ${fmt(r.avg_rssi)} dBm - ${fmt(r.node_count)} - ${fmt(r.times_seen)} - ${shortTime(r.last_seen)} - `; + if (!clientsGroupBy) { + tbody.innerHTML = clientsData.map(clientsRow).join(''); + return; + } + + // Build groups + const groups = {}; + clientsData.forEach(r => { + let key; + if (clientsGroupBy === 'vendor') { + key = r.vendor || 'Unknown'; + } else { + key = r.probed_ssids.length ? r.probed_ssids[0] : '(wildcard only)'; + } + if (!groups[key]) groups[key] = []; + groups[key].push(r); + }); + + const sorted = Object.keys(groups).sort((a, b) => a.localeCompare(b)); + tbody.innerHTML = sorted.map(key => { + const rows = groups[key]; + const hdr = `${key} — ${rows.length} device${rows.length > 1 ? 's' : ''}`; + return hdr + rows.map(clientsRow).join(''); }).join(''); } @@ -382,7 +424,7 @@ function renderPresenceView() { } else { arrNetsEmpty.style.display = 'none'; arrNetsTbody.innerHTML = newNets.map(r => { - const encCls = (!r.encryption || r.encryption === 'OPEN' || r.encryption === 'None') ? 'enc-open' : 'muted'; + const encCls = encClass(r.encryption); return ` ${fmt(r.ssid, '')} ${fmt(r.bssid)} @@ -398,8 +440,8 @@ function renderPresenceView() { const regMacsTbody = document.getElementById('regulars-macs-tbody'); const regMacsEmpty = document.getElementById('regulars-macs-empty'); const regCount = document.getElementById('regulars-count'); - const regMacs = p.regular_macs || []; - const regNets = p.regular_networks || []; + const regMacs = p.reg_macs || []; + const regNets = p.reg_networks || []; regCount.textContent = `${regMacs.length} devices · ${regNets.length} networks`; if (!regMacs.length) { regMacsTbody.innerHTML = ''; @@ -425,7 +467,7 @@ function renderPresenceView() { } else { regNetsEmpty.style.display = 'none'; regNetsTbody.innerHTML = regNets.map(r => { - const encCls = (!r.encryption || r.encryption === 'OPEN' || r.encryption === 'None') ? 'enc-open' : 'muted'; + const encCls = encClass(r.encryption); return ` ${fmt(r.ssid, '')} ${fmt(r.bssid)} @@ -841,7 +883,7 @@ function renderSearchResults(data, q) { // As a network if (data.as_network) { const rows = data.as_network.map(r => { - const encCls = (!r.encryption || r.encryption === 'OPEN') ? 'enc-open' : 'muted'; + const encCls = encClass(r.encryption); return ` ${fmt(r.ssid, '')} ${fmt(r.bssid)} @@ -936,7 +978,7 @@ function renderSearchResults(data, q) { if (data.vendor_networks) { const label = data.vendor_name || q; const rows = data.vendor_networks.map(r => { - const encCls = (!r.encryption || r.encryption === 'OPEN') ? 'enc-open' : 'muted'; + const encCls = encClass(r.encryption); return ` ${fmt(r.ssid, '')} ${fmt(r.bssid)} diff --git a/coordinator/templates/dashboard.html b/coordinator/templates/dashboard.html index 59a3156..26f1d36 100644 --- a/coordinator/templates/dashboard.html +++ b/coordinator/templates/dashboard.html @@ -3,7 +3,8 @@ - ESP32 Recon + ESP32 + @@ -135,8 +136,8 @@ MAC - Vendor - Probing for + Vendor ⊞ + Probing for ⊞ Best RSSI Avg RSSI Nodes diff --git a/coordinator/udp_ingest.py b/coordinator/udp_ingest.py index f989347..0af2bc6 100644 --- a/coordinator/udp_ingest.py +++ b/coordinator/udp_ingest.py @@ -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 "" + 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}") diff --git a/firmware/node/config.h b/firmware/node/config.h index 06399e8..cbf3417 100644 --- a/firmware/node/config.h +++ b/firmware/node/config.h @@ -21,5 +21,11 @@ #define PROBE_QUEUE_SIZE 128 // max probe events buffered between flushes #define DEAUTH_QUEUE_SIZE 128 // max deauth/disassoc events buffered between flushes +// ─── Association capture ───────────────────────────────────────────────────── +#define ASSOC_QUEUE_SIZE 32 // max assoc/reassoc events buffered between flushes + +// ─── RSSI alert threshold ──────────────────────────────────────────────────── +#define RSSI_ALERT_THRESHOLD -60 // flush immediately if attack-strength deauth seen (dBm) + // ─── Channel hopping ───────────────────────────────────────────────────────── #define HOP_DWELL_MS 300 // ms to dwell on each channel (13 ch × 300ms ≈ 4s/sweep) diff --git a/firmware/node/node.ino b/firmware/node/node.ino index 516de18..69eed41 100644 --- a/firmware/node/node.ino +++ b/firmware/node/node.ino @@ -28,16 +28,33 @@ struct ProbeEvent { }; struct DeauthEvent { - uint8_t src[6]; - uint8_t dst[6]; - uint8_t bssid[6]; - uint8_t subtype; // 0x0C = deauth, 0x0A = disassoc + uint8_t src[6]; + uint8_t dst[6]; + uint8_t bssid[6]; + uint8_t subtype; // 0xC0 = deauth, 0xA0 = disassoc uint16_t reason; + int8_t rssi; +}; + +struct AssocEvent { + uint8_t src[6]; // client MAC + uint8_t bssid[6]; // AP BSSID + char ssid[33]; // SSID the client is associating to + uint8_t subtype; // 0x00 = assoc req, 0x20 = reassoc req int8_t rssi; }; static QueueHandle_t probeQueue; static QueueHandle_t deauthQueue; +static QueueHandle_t assocQueue; + +static volatile uint32_t probeDrops = 0; +static volatile uint32_t deauthDrops = 0; +static volatile uint32_t assocDrops = 0; + +// Set in promiscuous callback when a close-range attack-signature deauth is seen. +// Checked in loop() to trigger an immediate flush rather than waiting for next hop cycle. +static volatile bool alertPending = false; // Dedup cache — suppress repeated MAC+SSID pairs within PROBE_DEDUP_SECS struct DedupEntry { uint8_t mac[6]; char ssid[33]; uint32_t last_ms; }; @@ -107,7 +124,7 @@ static void promiscuous_rx_cb(void* buf, wifi_promiscuous_pkt_type_t type) { memcpy(ev.src_mac, src, 6); memcpy(ev.ssid, ssid, 33); ev.rssi = (int8_t)pkt->rx_ctrl.rssi; - xQueueSend(probeQueue, &ev, 0); + if (xQueueSend(probeQueue, &ev, 0) != pdTRUE) probeDrops++; return; } @@ -124,7 +141,47 @@ static void promiscuous_rx_cb(void* buf, wifi_promiscuous_pkt_type_t type) { ev.subtype = subtype; ev.reason = (uint16_t)d[24] | ((uint16_t)d[25] << 8); ev.rssi = (int8_t)pkt->rx_ctrl.rssi; - xQueueSend(deauthQueue, &ev, 0); + if (xQueueSend(deauthQueue, &ev, 0) != pdTRUE) deauthDrops++; + + // Strong close-range attack signature — request an immediate flush + if (ev.reason == 2 && ev.rssi >= RSSI_ALERT_THRESHOLD) { + alertPending = true; + } + return; + } + + // ── Association request (0x00) and Reassociation request (0x20) ───────── + // Association req body: capability(2) + listen_interval(2) + IEs + // Reassociation req body: capability(2) + listen_interval(2) + current_AP(6) + IEs + // SSID IE is always first IE: tag(1) + length(1) + ssid(n) + if (subtype == 0x00 || subtype == 0x20) { + uint16_t ie_offset = (subtype == 0x00) ? 28 : 34; // body start + fixed fields + if (len < ie_offset + 2) return; + + AssocEvent ev; + memcpy(ev.src, d + 10, 6); + memcpy(ev.bssid, d + 16, 6); + ev.subtype = subtype; + ev.rssi = (int8_t)pkt->rx_ctrl.rssi; + ev.ssid[0] = '\0'; + + if (d[ie_offset] == 0x00) { // SSID IE tag + uint8_t slen = d[ie_offset + 1]; + if (slen > 0 && slen <= 32 && (ie_offset + 2 + slen) <= len) { + bool printable = true; + for (uint8_t i = 0; i < slen; i++) { + if (d[ie_offset + 2 + i] < 32 || d[ie_offset + 2 + i] > 126) { + printable = false; break; + } + } + if (printable) { + memcpy(ev.ssid, d + ie_offset + 2, slen); + ev.ssid[slen] = '\0'; + } + } + } + + if (xQueueSend(assocQueue, &ev, 0) != pdTRUE) assocDrops++; return; } } @@ -149,8 +206,9 @@ void setup() { #if PROBE_SNIFF memset(dedupCache, 0, sizeof(dedupCache)); - probeQueue = xQueueCreate(PROBE_QUEUE_SIZE, sizeof(ProbeEvent)); + probeQueue = xQueueCreate(PROBE_QUEUE_SIZE, sizeof(ProbeEvent)); deauthQueue = xQueueCreate(DEAUTH_QUEUE_SIZE, sizeof(DeauthEvent)); + assocQueue = xQueueCreate(ASSOC_QUEUE_SIZE, sizeof(AssocEvent)); esp_wifi_set_promiscuous_rx_cb(promiscuous_rx_cb); esp_wifi_set_promiscuous(true); Serial.println("[PROBE] Promiscuous mode enabled"); @@ -189,7 +247,18 @@ void loop() { } #if PROBE_SNIFF - advanceHop(); + // Alert path — close-range attack deauth detected; flush immediately + // rather than waiting up to 4s for the next natural homeChannel pass. + if (alertPending && WiFi.status() == WL_CONNECTED) { + alertPending = false; + esp_wifi_set_channel(homeChannel, WIFI_SECOND_CHAN_NONE); + flushDeauthQueue(); + flushProbeQueue(); + flushAssocQueue(); + lastHop = millis(); // avoid an immediate hop right after flushing + } else { + advanceHop(); + } #endif delay(10); @@ -261,6 +330,7 @@ void scanAndSend() { esp_wifi_set_promiscuous(true); flushProbeQueue(); flushDeauthQueue(); + flushAssocQueue(); // Reset hop state so next cycle starts cleanly from channel 1. hopIdx = 0; @@ -293,7 +363,8 @@ const char* importance(int rssi) { void sendBeaconEvent(int idx) { String ssid = WiFi.SSID(idx); - ssid.replace("\"", "\\\""); // escape quotes for JSON + ssid.replace("\\", "\\\\"); // escape backslashes first, then quotes + ssid.replace("\"", "\\\""); String bssid = WiFi.BSSIDstr(idx); int rssi = WiFi.RSSI(idx); @@ -325,22 +396,29 @@ void sendBeaconEvent(int idx) { void sendHeartbeat() { unsigned long now = millis(); - char buf[256]; + char buf[320]; snprintf(buf, sizeof(buf), "{\"node_id\":\"%s\",\"ts\":%lu,\"type\":\"heartbeat\"," - "\"uptime_ms\":%lu,\"free_heap\":%lu,\"wifi_rssi\":%d}", + "\"uptime_ms\":%lu,\"free_heap\":%lu,\"wifi_rssi\":%d," + "\"probe_drops\":%lu,\"deauth_drops\":%lu,\"assoc_drops\":%lu}", nodeId.c_str(), now, now, (unsigned long)ESP.getFreeHeap(), - WiFi.RSSI() + WiFi.RSSI(), + (unsigned long)probeDrops, + (unsigned long)deauthDrops, + (unsigned long)assocDrops ); udp.beginPacket(COORDINATOR_IP, COORDINATOR_PORT); udp.print(buf); udp.endPacket(); - Serial.printf("[HB] uptime=%lus heap=%luK ap=%ddBm\n", + Serial.printf("[HB] uptime=%lus heap=%luK ap=%ddBm drops(p/d/a)=%lu/%lu/%lu\n", now / 1000, (unsigned long)ESP.getFreeHeap() / 1024, - WiFi.RSSI() + WiFi.RSSI(), + (unsigned long)probeDrops, + (unsigned long)deauthDrops, + (unsigned long)assocDrops ); } @@ -426,6 +504,50 @@ void flushDeauthQueue() { } } +void sendAssocEvent(const AssocEvent& ev) { + auto macStr = [](const uint8_t* m, char* out) { + snprintf(out, 18, "%02X:%02X:%02X:%02X:%02X:%02X", + m[0], m[1], m[2], m[3], m[4], m[5]); + }; + char src[18], bssid[18]; + macStr(ev.src, src); + macStr(ev.bssid, bssid); + + const char* stype = (ev.subtype == 0x00) ? "assoc" : "reassoc"; + + // Escape quotes in SSID + char ssidEsc[66]; + int j = 0; + for (int i = 0; ev.ssid[i] && j < 64; i++) { + if (ev.ssid[i] == '"' || ev.ssid[i] == '\\') ssidEsc[j++] = '\\'; + ssidEsc[j++] = ev.ssid[i]; + } + ssidEsc[j] = '\0'; + + char buf[320]; + snprintf(buf, sizeof(buf), + "{\"node_id\":\"%s\",\"ts\":%lu,\"type\":\"%s\"," + "\"src\":\"%s\",\"bssid\":\"%s\",\"ssid\":\"%s\",\"rssi\":%d}", + nodeId.c_str(), millis(), stype, + src, bssid, ssidEsc, (int)ev.rssi + ); + + udp.beginPacket(COORDINATOR_IP, COORDINATOR_PORT); + udp.print(buf); + udp.endPacket(); + + Serial.printf(" [%s] %s → %s \"%s\" %ddBm\n", + stype, src, bssid, ev.ssid[0] ? ev.ssid : "", (int)ev.rssi); +} + +void flushAssocQueue() { + if (WiFi.status() != WL_CONNECTED) return; + AssocEvent ev; + while (xQueueReceive(assocQueue, &ev, 0) == pdTRUE) { + sendAssocEvent(ev); + } +} + // Advance to the next channel in the hop list. // Non-blocking — returns immediately if the dwell time hasn't elapsed. // Flushes queues each time we land back on homeChannel (WiFi is usable there). @@ -441,6 +563,7 @@ void advanceHop() { if (ch == homeChannel && WiFi.status() == WL_CONNECTED) { flushProbeQueue(); flushDeauthQueue(); + flushAssocQueue(); } } diff --git a/heatmap.png b/heatmap.png deleted file mode 100644 index 8df50a3669a0c1fec9a322bd9200a8101810ef03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88628 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

sVD8uqk?_Sr4HZQO7>kEKhcTB6VYd&km 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