11#if PC_ENABLE_WIFI_SNIFFER
20bool pc_wifi_parse(
const uint8_t *frame,
size_t len, WifiFrame *out)
22 if (!frame || !out || len < 10)
27 memset(out, 0,
sizeof(*out));
29 uint8_t fc0 = frame[0];
30 uint8_t fc1 = frame[1];
31 out->version = fc0 & 0x03;
32 out->type = (fc0 >> 2) & 0x03;
33 out->subtype = (fc0 >> 4) & 0x0F;
34 out->to_ds = (fc1 & 0x01) != 0;
35 out->from_ds = (fc1 & 0x02) != 0;
36 out->retry = (fc1 & 0x08) != 0;
37 out->protected_frame = (fc1 & 0x40) != 0;
40 memcpy(out->addr1, frame + 4, 6);
44 memcpy(out->addr2, frame + 10, 6);
49 memcpy(out->addr3, frame + 16, 6);
55void pc_wifi_stats_reset(WifiStats *s)
59 memset(s, 0,
sizeof(*s));
63void pc_wifi_stats_add(WifiStats *s,
const WifiFrame *f)
71 case WifiType::WIFI_TYPE_MGMT:
74 case WifiType::WIFI_TYPE_CTRL:
77 case WifiType::WIFI_TYPE_DATA:
87bool pc_wifi_should_roam(int8_t cur_rssi, int8_t cand_rssi, uint8_t hysteresis_db)
91 int32_t margin = (int32_t)cand_rssi - (int32_t)cur_rssi;
92 return margin > (int32_t)hysteresis_db;
99uint8_t clamp_channel(uint8_t c)
113void pc_wifi_scan_init(WifiScan *s, uint8_t first, uint8_t last, uint16_t dwell_ms, uint32_t now_ms)
119 s->chan_first = clamp_channel(first);
120 s->chan_last = clamp_channel(last);
121 if (s->chan_last < s->chan_first)
123 s->chan_last = s->chan_first;
125 s->channel = s->chan_first;
126 s->dwell_ms = dwell_ms;
127 s->last_hop_ms = now_ms;
131bool pc_wifi_scan_due(
const WifiScan *s, uint32_t now_ms)
138 return (now_ms - s->last_hop_ms) >= s->dwell_ms;
141uint8_t pc_wifi_scan_next(WifiScan *s, uint32_t now_ms)
147 if (s->channel >= s->chan_last)
149 s->channel = s->chan_first;
156 s->last_hop_ms = now_ms;
162void pc_wifi_survey_reset(WifiSurvey *s, uint8_t first, uint8_t count)
168 memset(s, 0,
sizeof(*s));
169 s->first = clamp_channel(first);
173 s->ch[i].best_rssi = PC_WIFI_RSSI_NONE;
180int survey_index(
const WifiSurvey *s, uint8_t channel)
182 if (!s || channel < s->first)
186 int idx = (int)channel - (
int)s->first;
187 if (idx >= (
int)s->count)
195void pc_wifi_survey_add(WifiSurvey *s, uint8_t channel, int8_t rssi,
const WifiFrame *f)
197 int idx = survey_index(s, channel);
202 WifiChannelSurvey *e = &s->ch[idx];
204 if (e->best_rssi == PC_WIFI_RSSI_NONE || rssi > e->best_rssi)
208 if (f && f->naddr >= 2)
210 memcpy(e->best_bssid, f->addr2, 6);
215const WifiChannelSurvey *pc_wifi_survey_get(
const WifiSurvey *s, uint8_t channel)
217 int idx = survey_index(s, channel);
218 return (idx < 0) ? nullptr : &s->ch[idx];
221bool pc_wifi_survey_best(
const WifiSurvey *s, uint8_t exclude_channel, uint8_t *out_channel, int8_t *out_rssi)
229 int8_t best = PC_WIFI_RSSI_NONE;
230 for (uint8_t i = 0; i < s->count; i++)
232 uint8_t ch = (uint8_t)(s->first + i);
233 if (ch == exclude_channel || s->ch[i].best_rssi == PC_WIFI_RSSI_NONE)
237 if (!found || s->ch[i].best_rssi > best)
240 best = s->ch[i].best_rssi;
248 *out_channel = best_ch;
271WifiSnifferCtx s_sniffer = {};
275void sniffer_sink(
const uint8_t *frame, uint16_t len, int8_t rssi, uint8_t channel)
278 if (!pc_wifi_parse(frame, len, &f))
282 pc_wifi_stats_add(&s_sniffer.stats, &f);
283 pc_wifi_survey_add(&s_sniffer.survey, channel, rssi, &f);
287bool pc_wifi_sniffer_begin(uint8_t first_chan, uint8_t last_chan, uint16_t dwell_ms)
290 pc_wifi_stats_reset(&s_sniffer.stats);
291 pc_wifi_scan_init(&s_sniffer.scan, first_chan, last_chan, dwell_ms, now);
292 pc_wifi_survey_reset(&s_sniffer.survey, s_sniffer.scan.chan_first,
293 (uint8_t)(s_sniffer.scan.chan_last - s_sniffer.scan.chan_first + 1));
294 s_sniffer.running = pc_promisc_begin(s_sniffer.scan.channel, sniffer_sink);
295 return s_sniffer.running;
298void pc_wifi_sniffer_tick(
void)
300 if (!s_sniffer.running)
305 if (!pc_wifi_scan_due(&s_sniffer.scan, now))
309 pc_promisc_set_channel(pc_wifi_scan_next(&s_sniffer.scan, now));
312void pc_wifi_sniffer_end(
void)
314 if (!s_sniffer.running)
319 s_sniffer.running =
false;
322const WifiStats *pc_wifi_sniffer_stats(
void)
324 return &s_sniffer.stats;
327const WifiSurvey *pc_wifi_sniffer_survey(
void)
329 return &s_sniffer.survey;
332const WifiScan *pc_wifi_sniffer_scan(
void)
334 return &s_sniffer.scan;
Pluggable monotonic clock for all library timing.
uint32_t pc_millis(void)
The library's monotonic time at 1000 Hz (milliseconds).
Wi-Fi promiscuous (monitor) capture (PC_ENABLE_PROMISC) - passive 802.11 sniffing.
#define PC_WIFI_SNIFFER_MAX_CHANNELS
Channels tracked by the WiFi sniffer's per-channel survey (PC_WIFI_SNIFFER_MAX_CHANNELS).
802.11 frame decode + traffic tally + RSSI roaming decision (PC_ENABLE_WIFI_SNIFFER).