-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ESP32S2: implement API for Promiscuous mode #4914
Comments
Any clues from the debug serial? |
For the API, I'd suggest looking at how we do |
Just a note that promiscuous mode can be run concurrently with STA, AP, or APSTA modes, but Espressif recommends not using promiscuous mode along with heavy non-promiscuous traffic. |
Hi everyone - thank you for the quick response and insight. In the past week I have tried various different approaches with changing the order of initializing the ESP32 module etc but no luck. One thing that I did find is that setting Moving on from here, after trying to start in From many dozen attempts at changing order of commands and other values, the most recent attempt of the circuitpython build is based on the below code and seems like the correct approach but still no luck. Including or excluding the @anecdata - you mentioned the output from the debug serial - could you elaborate on this? right now the only output that I am using is from
|
|
There may be an issue having some of these lines in the
Most of that is already done at system start up time, I'm not sure what happens if they are executed again: https://github.com/adafruit/circuitpython/blob/main/ports/esp32s2/common-hal/wifi/__init__.c |
tried removing these lines and few variations (STA / AP modes and including/removing start/stop wifi) but no luck - still freezing on the line attempt 1 :
attempt 2 :
attempt 3 :
attempt 4 :
|
The |
I think it's worth discussing some design goals. Monitor will potentially be subjected to a firehose of wifi frames, even with filtering by channel, Espressif's esp-idf example allows filtering by channel and In any case, there are a variety of ways the firehose could be throttled down, and we should think about what's the best CircuitPython operation and API. How many frames to return, whether they should be the most recent or a more random sampling, whether duplicates should be weeded out in the core code (e.g., Beacon frames at a rate of 10/sec per AP per SSID is typical), whether we simply drop new incoming frames when some buffer is full or drop the older frames in favor of the newer frames, etc. One problem with a fixed size buffer that drops frames from one end or the other is that it will not necessarily be representative of the traffic over a longer period than the timeframe of the buffer; in other words at the CircuitPython level the results will be chunks taken from the stream rather than some smoother sampling. We could attempt to enforce enough filters to keep the data volume manageable, but that's very difficult to predict across wifi environments. Any thoughts, or specific use cases? |
I'd suggest modeling the API after the BLE scanning API. It has similar issues of getting a lot of inputs and needing to filter early. |
Providing a means to generate a simple list of "device X was present for Y seconds" is probably enough to get started. By passing in filters of Example : What do you think? |
Makes sense to me. I'm still trying to get my arms around the idf example, and how to translate that to common-hal. Seems to me that:
This is different and separate from the existing station and AP operation where there is a single event handler to catch asynchronous wifi events. I think we need the callback and the continuous (while started) sniffer task, but they don't get exposed to the API, just like the wifi event handler isn't exposed to the API. Probably initially we should start with a simple set of filters similar to what you suggest. I think longer-term we might want: channel hopping to get frames from a list of (not-necessaily-contiguous) channels, time interval as an option in addition to frame count, and maybe a way to just return frame headers instead of whole frame bodies. I don't know how much call there would be to handle non-mgmt frames or specific SSIDs or specific SA/DA/BSSID MACs. There's also the issue of whether to try to scrub out duplicates. I would like to pop the frame we capture up a level to also capture especially the RSSI and the channel (in case of channel hopping): https://github.com/espressif/esp-idf/blob/e493a4c30e1b2f7c54a376327388b9e12262cbbf/components/esp_wifi/include/esp_wifi_types.h#L378 Or they could be passed in like the pcap parameters are passed in along with the packet buffer in the idf example. Maybe that's the better approach to reduce the size of each buffer returned. Another consideration: memory available to esp-idf is not large. The way I understand it, devices that have extra PSRAM have that RAM devoted to CircuitPython code use (heap). So the number of frames we can accumulate may be very limited. WiFi devices come and go all the time, and traffic is bursty. It will be interesting to see how much practical control we're able to achieve over the smoothness (ability to continuously pass a even sampling up to CircuitPython) or chunkiness (e.g., we got got a burst of 10 frames from the 100 or 1000 or 10_000 that hit the antenna over some interval that includes the overhead of passing the frames up to user code and doing something with them). The API, as tannewt suggests, would be pretty simple: @matthewjerome Did you get the debug console working, and are you working toward a PR? I'm far from expert at CircuitPython core development, but I'm happy to help in any way I can. |
One more trade-off to consider. Processing is faster in C than Python, which argues for doing more packet parsing in C and returning more fields as opposed to a raw packet. But that reduces the duty cycle of packet capture. On the other hand, doing more packet parsing in Python will make the capture more "chunky" in that we'd be grabbing some packets quickly, then processing that chunk more slowly while we miss capturing more packets. I don't know, maybe it's a parameter and the code can do either. I don't have an intuition for the performance trade-off, or code size if that's an issue. I think it's safe to say we don't want to fully parse packets. The variations are complex, would require a lot of code, and a lot of the information is pretty esoteric and would probably have little value to most users. In between, there are degrees of parsing we could think about. Parsing the frame header is pretty simple, it's all fixed fields... some bits, some bytes, some multi-byte sequences. The frame body is a little more involved. Management type frames have different numbers of fixed-length fields (from 0 to 12 bytes total) by subtype, followed by the variable-length Information Elements, each of which has a code, a length, and a body. So to parse the frame body, wherever it's done, the code has to step through the IEs one by one to see what's there and extract anything interesting. Regardless of where on the spectrum of core <--> user the parsing is done, there is at least one particularly interesting variable element that's probably worth extracting in the core: SSID (when present), and it shouldn't be too demanding as it's usually the first IE. |
Still playing around with heavily-modified example code that does management frames only, single-channel or channel-hopping, parses frame headers, and does a coarse parse and dump of the fixed and variable components of the frame body. The test environment has a relatively limited set of devices in range. Let it run a few hours last night, and do get interspersed errors from the callback:
So I think it's safe to say that filters should be set relatively tightly to stay within good operating conditions. It seems the errors are caught without incident, and packet capture continues, but some packets are missed. |
WiFi and BLE scanning both seek to discover a relatively fixed list of devices in range, and those devices are explicitly sending frequent beacons and advertisements in order to be discovered within a fixed length of time. Advertisements from a connected BLE device are conceptually infinite, but I believe the typical use case makes occasional scanning for new data a practical approach. WiFi monitor mode, on the other hand, is conceptually an infinite process with no clear end point. Occasional scanning may miss relatively ephemeral but useful events. I wonder if an explicit queue mechanism would be more appropriate, something more akin to The API could get even more interesting on processors with more memory (e.g., esp32-s3), faster processors, or on something like a Raspberry Pi Zero W with CircuitPython. |
I'm featuring CircuitPython on the esp32s2 on the Hak5 YouTube channel and was pleased to see this is being worked on. Just wanted to mention I'd be happy to feature this on Hak5 with a shout-out and $100 gift card to whoever manages to pull it off, thank you all for your work! |
There's a very rough and rudimentary POC here, basically the Espressif sniffer example (minus the SD Card, JTAG, and GUI), shoehorned into CP and tweaked to parse the more interesting RX_CTRL header and frame header fields and split up the frame body into its fixed and variable (Information Element) components, just enough to extract an SSID if available. Management frame type only at the moment, any subtype. Channel changes after each frame. CircuitPython code just starts and stops monitor mode, no parameters are currently sent, and no data is currently returned:
CP console:
debug console:
Build with There are a ton of things left to do to improve code, add parameters and return data, and conform to the CircuitPython idioms. Significant open questions remain on the API (see above). If someone else wants to run with this or start from scratch, that's totally fine. |
There's a new branch https://github.com/anecdata/circuitpython/tree/monitor_min with a rough, simple There's a thread now on the Adafruit Discord, in the #circuitpython-dev channel to discuss some design and implementation issues. |
Has anyone taken a look at implementing promiscuous mode with Circuit Python + ESP32S2 ? I have tried a few approaches and no success.
Here is the sample code that I have been working on - after building using the
make BOARD=unexpectedmaker_feathers2
command, theuf2
file is generated but freezes the chip when theesp_wifi_set_promiscuous(true)
line is enabled.Is there some other options that need to be set before setting
esp_wifi_set_promiscuous
?Similar to the AP Mode recently implemented : #4246
Example in Micropython + ESP8266 : https://github.com/mzakharo/micropython-promiscuous-esp8266
Example Reference ESP-IDF Implementation : https://github.com/adafruit/esp-idf/tree/circuitpython/examples/wifi/simple_sniffer
Target Python Implementation :
ports/esp32s2/common-hal/wifi/Radio.h
shared-bindings/wifi/Radio.h
shared-bindings/wifi/Radio.c
ports/esp32s2/common-hal/wifi/Radio.c
The text was updated successfully, but these errors were encountered: