DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
promisc.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file promisc.cpp
6 * @brief promisc implementation: the pure 802.11 header parser + PCAP framing, and the ESP32
7 * esp_wifi promiscuous binding. The parser / PCAP builders are host-identical.
8 */
9
11
12#if DETWS_ENABLE_PROMISC
13
14#include <string.h>
15
16bool wifi_frame_parse(const uint8_t *frame, uint16_t len, WifiFrameInfo *out)
17{
18 if (!frame || !out || len < 10) // FC(2) + Duration(2) + Addr1(6) - the shortest control frame
19 return false;
20 memset(out, 0, sizeof(*out));
21
22 const uint8_t fc0 = frame[0];
23 const uint8_t fc1 = frame[1];
24 out->type = (WifiFrameType)((fc0 >> 2) & 0x3);
25 out->subtype = (uint8_t)((fc0 >> 4) & 0xF);
26 out->to_ds = (fc1 & 0x01) != 0;
27 out->from_ds = (fc1 & 0x02) != 0;
28 out->protected_frame = (fc1 & 0x40) != 0;
29
30 if (out->type == WifiFrameType::WIFI_FT_CTRL)
31 {
32 // Control frames carry only Addr1 (the receiver); the rest vary by subtype.
33 out->dst = frame + 4;
34 out->hdr_len = 10;
35 return true;
36 }
37
38 // Management / data / extension frames carry the full 3-address header + sequence control.
39 if (len < 24)
40 return false;
41 out->seq = (uint16_t)(((uint16_t)frame[22] | ((uint16_t)frame[23] << 8)) >> 4);
42 out->is_qos = (out->type == WifiFrameType::WIFI_FT_DATA) && (out->subtype & 0x08) != 0;
43
44 const bool has_addr4 = out->to_ds && out->from_ds; // WDS
45 uint16_t hlen = 24;
46 if (has_addr4)
47 hlen += 6;
48 if (out->is_qos)
49 hlen += 2;
50 if (out->is_qos && (fc1 & 0x80)) // Order bit on a QoS data frame -> HT Control field
51 hlen += 4;
52 if (len < hlen)
53 return false;
54 out->hdr_len = hlen;
55
56 const uint8_t *a1 = frame + 4;
57 const uint8_t *a2 = frame + 10;
58 const uint8_t *a3 = frame + 16;
59 if (!out->to_ds && !out->from_ds) // IBSS / management
60 {
61 out->dst = a1;
62 out->src = a2;
63 out->bssid = a3;
64 }
65 else if (!out->to_ds && out->from_ds) // from the AP
66 {
67 out->dst = a1;
68 out->bssid = a2;
69 out->src = a3;
70 }
71 else if (out->to_ds && !out->from_ds) // to the AP
72 {
73 out->bssid = a1;
74 out->src = a2;
75 out->dst = a3;
76 }
77 else // WDS (4-address): A1=RA, A2=TA, A3=DA, A4=SA
78 {
79 out->dst = a3;
80 out->src = frame + 24;
81 out->bssid = nullptr;
82 }
83 return true;
84}
85
86// libpcap framing (det_pcap_global_header / det_pcap_record_header) is in
87// shared_primitives/pcap.h - shared with the other capture features.
88
89// --- ESP32 radio binding -----------------------------------------------------------------
90#ifdef ARDUINO
91
92#include <esp_wifi.h>
93
94namespace
95{
96// All promiscuous-capture state, owned by one instance (internal linkage): the frame sink.
97// One named owner, unreachable from any other translation unit.
98struct PromiscCtx
99{
100 promisc_sink_fn sink = nullptr;
101};
102PromiscCtx s_promisc;
103
104void promisc_rx(void *buf, wifi_promiscuous_pkt_type_t)
105{
106 if (!s_promisc.sink || !buf)
107 return;
108 const wifi_promiscuous_pkt_t *pkt = (const wifi_promiscuous_pkt_t *)buf;
109 uint16_t len = (uint16_t)pkt->rx_ctrl.sig_len; // includes the 4-byte FCS
110 if (len < 4)
111 return;
112 s_promisc.sink(pkt->payload, (uint16_t)(len - 4), (int8_t)pkt->rx_ctrl.rssi, (uint8_t)pkt->rx_ctrl.channel);
113}
114} // namespace
115
116bool promisc_begin(uint8_t channel, promisc_sink_fn sink)
117{
118 if (!sink)
119 return false;
120 s_promisc.sink = sink;
121 esp_wifi_set_promiscuous(true);
122 esp_wifi_set_promiscuous_rx_cb(&promisc_rx);
123 esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
124 return true;
125}
126
127void promisc_set_channel(uint8_t channel)
128{
129 esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
130}
131
132void promisc_end(void)
133{
134 esp_wifi_set_promiscuous(false);
135 s_promisc.sink = nullptr;
136}
137
138#else // host build - no radio
139
140bool promisc_begin(uint8_t, promisc_sink_fn)
141{
142 return false;
143}
144void promisc_set_channel(uint8_t)
145{
146 // host build: no radio, no channel to set
147}
148void promisc_end(void)
149{
150 // host build: no radio, nothing to stop
151}
152
153#endif // ARDUINO
154
155#endif // DETWS_ENABLE_PROMISC
Wi-Fi promiscuous (monitor) capture (DETWS_ENABLE_PROMISC) - passive 802.11 sniffing.