diff --git a/examples/wifi/simple_sniffer/README.md b/examples/wifi/simple_sniffer/README.md index e1385bcfe8..bf38632507 100644 --- a/examples/wifi/simple_sniffer/README.md +++ b/examples/wifi/simple_sniffer/README.md @@ -4,26 +4,32 @@ ## Overview -This example demonstrates basic usage of wifi sniffer mode by saving packets into SD card with pcap format. Go to wikipedia for more information about [pcap](https://en.wikipedia.org/wiki/Pcap). +This example demonstrates basic usage of WiFi sniffer mode by saving packets into SD card with pcap format. We can send pcap file to host via JTAG interface as well. -This example is based on esp-idf's console component. For more information about console you should read this [guide](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/console.html). +For more information about pcap, please go to [wikipedia](https://en.wikipedia.org/wiki/Pcap). + +This example is based on console component. For more information about console, please refer to [console guide](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/console.html). ## How to use example ### Hardware Required -To run this example, you should have one ESP32 dev board integrated with a SD card slot (e.g ESP32-WROVER Kit) or just connect ESP32-DevKitC to a SD card breakout board. +To run this example, you should have one ESP32 dev board integrated with a SD card slot (e.g [ESP-WROVER-KIT](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html#esp-wrover-kit-v4-1)) or just connect [ESP32-DevKitC](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html#esp32-devkitc-v4) to a SD card breakout board. +If you want to send packets to host, make sure to connect ESP32 to some kind of [JTAG adapter](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#jtag-debugging-selecting-jtag-adapter). ### Configure the project Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you are using CMake based build system. Then go into `Example Configuration` menu. - Check `Store command history in flash` if you want to save command history into flash (recommend). -- Set the mount point in your filesystem, for example, `/sdcard` if you want to store pcap file into SD card. -- Set the length of sniffer work queue. -- Set the stack size of the sniffer task. -- Set the priority of the sniffer task. -- Set the max number of packets to store in a single pcap file. The number of packets usually will be very large, so we just truncate them into multiple files. You should set a threshold value here. +- Select where to save the pcap file in `Select destination to store pcap file` menu item. + - `SD Card` means saving packets (pcap format) into the SD card you plug in. + - `JTAG (App Trace)` means sending packets (pcap format) to host via JTAG interface. This feature depends on [app trace component](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html). +- Set the mount point in your filesystem in `SD card mount point in the filesystem` menu item. This configuration only takes effect when you choose to save packets into SD card. +- Set max name length of pcap file in `Max name length of pcap file` menu item. +- Set the length of sniffer work queue in `Length of sniffer work queue` menu item. +- Set the stack size of the sniffer task in `Stack size of sniffer task` menu item. +- Set the priority of the sniffer task `Length of sniffer work queue` menu item. ### Build and Flash @@ -86,21 +92,17 @@ Size: 14832MB ```bash esp32> sniffer -f sniffer-example -i wlan -c 2 -I (36200) cmd_sniffer: Start WiFi Promicuous Mode -I (36270) phy: phy_version: 4000, b6198fa, Sep 3 2018, 15:11:06, 0, 0 -I (36270) wifi: ic_enable_sniffer -I (36290) pcap: Store packets to file: /sdcard/sniffer-example0.pcap -I (103810) pcap: Close Pcap file OK -I (103830) pcap: Store packets to file: /sdcard/sniffer-example1.pcap -I (177300) pcap: Close Pcap file OK -I (177320) pcap: Store packets to file: /sdcard/sniffer-example2.pcap +I (8946) cmd_sniffer: open file successfully +W (8966) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration +I (9176) phy: phy_version: 4100, 6fa5e27, Jan 25 2019, 17:02:06, 0, 2 +I (9186) wifi: ic_enable_sniffer +I (9196) cmd_sniffer: start WiFi promiscuous ok esp32> sniffer --stop -I (212250) wifi: ic_disable_sniffer -I (212250) wifi: flush txq -I (212250) wifi: stop sw txq -I (212260) wifi: lmac stop hw txq -I (212340) pcap: Close Pcap file OK -I (212340) cmd_sniffer: Sniffer Stopped +I (31456) wifi: ic_disable_sniffer +I (31456) wifi: flush txq +I (31456) wifi: stop sw txq +I (31456) wifi: lmac stop hw txq +I (31456) cmd_sniffer: stop WiFi promiscuous ok ``` ### Unmount SD Card @@ -110,15 +112,25 @@ esp32> unmount sd I (248800) example: Card unmounted ``` +### Steps for sending packets to host via JTAG interface +1. Select `JTAG (App Trace)` as the destination of pcap files. +2. Build & Flash with `idf.py build flash` or `make flash`. +3. Connect JTAG, run OpenOCD (for more information about how-to please refer to [JTAG Debugging](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html)). +4. Telnet to localhost with 4444 port: `telnet localhost 4444`. +5. In the telnet session, run command like `esp32 apptrace start file://sniffer-esp32.pcap 1 -1 20` (more information about this command, please refer to [apptrace command](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html#openocd-application-level-tracing-commands)). +6. Run the example, start sniffer with command `sniffer` (you don't need to specify the filename, because it has been set in step5). +7. Stop sniffer by entering command `sniffer --stop` in the example console. +8. Stop tracing by entering command `esp32 apptrace stop` in the telnet session. + + ### Open PCap File in Wireshark -![sniffer-example0.pcap](sniffer-example0-pcap.png) +![sniffer-example0.pcap](sniffer-esp32-pcap.png) ## Troubleshooting - Make sure you have pluged in your SD card and mount it into filesystem before doing sniffer work or you will get error message like “Create file /sdcard/sniffer0.pcap failed”. - To protect the SD card, we recommand you to execute command `unmount sd` before you plug out your SD card. +- Make sure to run `esp32 apptrace` command before or immediately after a new sniffer task started when you try this example with JTAG. Otherwise the console will issue warning message `waiting for apptrace established` every 1 second. If the apptrace communication doesn't be established within 10 seconds (can be altered by macro `SNIFFER_APPTRACE_RETRY`), this sniffer command will failed with an error message `waiting for apptrace established timeout`. - - -(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.) \ No newline at end of file +(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.) diff --git a/examples/wifi/simple_sniffer/components/pcap/pcap.c b/examples/wifi/simple_sniffer/components/pcap/pcap.c index 4032f2dcbc..a6c286372f 100644 --- a/examples/wifi/simple_sniffer/components/pcap/pcap.c +++ b/examples/wifi/simple_sniffer/components/pcap/pcap.c @@ -1,10 +1,16 @@ -/* pcap encoder. - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include #include #include @@ -15,19 +21,19 @@ #include "esp_log.h" #include "pcap.h" -static const char *TAG = "pcap"; -#define PCAP_CHECK(a, str, ret_val, ...) \ - do \ - { \ - if (!(a)) \ - { \ - ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ - return (ret_val); \ - } \ +static const char *PCAP_TAG = "pcap"; +#define PCAP_CHECK(a, str, goto_tag, ...) \ + do \ + { \ + if (!(a)) \ + { \ + ESP_LOGE(PCAP_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + goto goto_tag; \ + } \ } while (0) /** - * @brief Pcap File Header Type Definition + * @brief Pcap File Header * */ typedef struct { @@ -41,23 +47,29 @@ typedef struct { } pcap_file_header_t; /** - * @brief Pcap Packet Header Type Definition + * @brief Pcap Packet Header * */ typedef struct { uint32_t seconds; /*!< Number of seconds since January 1st, 1970, 00:00:00 GMT */ - uint32_t microseconds; /*!< Number of microseconds when the packet was captured(offset from seconds) */ - uint32_t capture_length; /*!< Number of bytes of captured data, not longer than packet_length */ + uint32_t microseconds; /*!< Number of microseconds when the packet was captured (offset from seconds) */ + uint32_t capture_length; /*!< Number of bytes of captured data, no longer than packet_length */ uint32_t packet_length; /*!< Actual length of current packet */ } pcap_packet_header_t; -static FILE *file = NULL; +/** + * @brief Pcap Runtime Handle + * + */ +typedef struct { + FILE *file; /*!< File handle */ +} pcap_runtime_t; -esp_err_t pcap_capture_packet(void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds) +esp_err_t pcap_capture_packet(pcap_handle_t handle, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds) { - if (!file) { - return ESP_FAIL; - } + PCAP_CHECK(handle, "pcap handle is NULL", err); + pcap_runtime_t *pcap_rt = (pcap_runtime_t *)handle; + PCAP_CHECK(pcap_rt->file, "pcap file is NULL", err); size_t real_write = 0; pcap_packet_header_t header = { .seconds = seconds, @@ -65,33 +77,39 @@ esp_err_t pcap_capture_packet(void *payload, uint32_t length, uint32_t seconds, .capture_length = length, .packet_length = length }; - real_write = fwrite(&header, sizeof(header), 1, file); - PCAP_CHECK(real_write == 1, "Write packet header error", ESP_FAIL); - real_write = fwrite(payload, sizeof(uint8_t), length, file); - PCAP_CHECK(real_write == length, "Write packet payload error", ESP_FAIL); + real_write = fwrite(&header, sizeof(header), 1, pcap_rt->file); + PCAP_CHECK(real_write == 1, "write packet header error", err); + real_write = fwrite(payload, sizeof(uint8_t), length, pcap_rt->file); + PCAP_CHECK(real_write == length, "write packet payload error", err); /* Flush content in the buffer into device */ - fflush(file); + fflush(pcap_rt->file); return ESP_OK; +err: + return ESP_FAIL; } -esp_err_t pcap_close(void) +esp_err_t pcap_deinit(pcap_handle_t handle) { - if (!file) { - return ESP_OK; - } - if (fclose(file)) { - ESP_LOGE(TAG, "Close pcap file failed"); - file = NULL; - return ESP_FAIL; - } - ESP_LOGI(TAG, "Close Pcap file OK"); - file = NULL; + PCAP_CHECK(handle, "pcap handle is NULL", err); + pcap_runtime_t *pcap_rt = (pcap_runtime_t *)handle; + PCAP_CHECK(pcap_rt->file, "pcap file is NULL", err); + PCAP_CHECK(fclose(pcap_rt->file) == 0, "close pcap file failed", err); + pcap_rt->file = NULL; + free(pcap_rt); + ESP_LOGD(PCAP_TAG, "pcap deinit OK"); return ESP_OK; +err: + ESP_LOGW(PCAP_TAG, "pcap deinit failed"); + return ESP_FAIL; } -esp_err_t pcap_new(pcap_config_t *config) +esp_err_t pcap_init(pcap_config_t *config, pcap_handle_t *handle) { - file = config->fp; + PCAP_CHECK(config, "config is NULL", err); + PCAP_CHECK(handle, "pcap handle is NULL", err); + pcap_runtime_t *pcap_rt = calloc(sizeof(pcap_runtime_t), 1); + PCAP_CHECK(pcap_rt, "calloc pcap runtime failed", err); + pcap_rt->file = config->fp; /* Write Pcap File header */ pcap_file_header_t header = { .magic = PCAP_MAGIC_BIG_ENDIAN, @@ -102,17 +120,19 @@ esp_err_t pcap_new(pcap_config_t *config) .snaplen = 0x40000, .link_type = config->link_type }; - size_t real_write = fwrite(&header, sizeof(header), 1, file); - if (real_write != 1) { - ESP_LOGE(TAG, "Write Pcap file header error"); - goto err_write; - } + size_t real_write = fwrite(&header, sizeof(header), 1, pcap_rt->file); + PCAP_CHECK(real_write == 1, "write pcap file header failed", err_write); /* Flush content in the buffer into device */ - fflush(file); + fflush(pcap_rt->file); + *handle = (pcap_handle_t)pcap_rt; + ESP_LOGD(PCAP_TAG, "pcap init OK"); return ESP_OK; - /* Error Handling */ err_write: - fclose(file); + fclose(pcap_rt->file); + pcap_rt->file = NULL; + free(pcap_rt); +err: + ESP_LOGW(PCAP_TAG, "pcap init failed"); return ESP_FAIL; } diff --git a/examples/wifi/simple_sniffer/components/pcap/pcap.h b/examples/wifi/simple_sniffer/components/pcap/pcap.h index ef2dfc78ab..af0abcfc19 100644 --- a/examples/wifi/simple_sniffer/components/pcap/pcap.h +++ b/examples/wifi/simple_sniffer/components/pcap/pcap.h @@ -1,10 +1,16 @@ -/* pcap encoder. - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #pragma once #ifdef __cplusplus @@ -12,6 +18,7 @@ extern "C" { #endif #include +#include "esp_err.h" #define PCAP_MAGIC_BIG_ENDIAN 0xA1B2C3D4 /*!< Big-Endian */ #define PCAP_MAGIC_LITTLE_ENDIAN 0xD4C3B2A1 /*!< Little-Endian */ @@ -21,8 +28,6 @@ extern "C" { #define PCAP_TIME_ZONE_GMT 0x00 /*!< Time Zone */ -#define PCAP_FILE_NAME_MAX_LEN 32 /*!< Max Name Length of Pcap File */ - /** * @brief Link layer Type Definition, used for Pcap reader to decode payload * @@ -55,30 +60,45 @@ typedef struct { } pcap_config_t; /** - * @brief Create a pcap object + * @brief Pcap Handle Type Definition + * + */ +typedef void *pcap_handle_t; + +/** + * @brief Initialize a pcap session * * @param config configuration of creating pcap object - * @return esp_err_t ESP_OK on success, ESP_FAIL on IO error + * @param handle pcap handle + * @return esp_err_t + * - ESP_OK on success + * - ESP_FAIL on error */ -esp_err_t pcap_new(pcap_config_t *config); +esp_err_t pcap_init(pcap_config_t *config, pcap_handle_t *handle); /** - * @brief Close pcap file and recyle related resources + * @brief De-initialize a pcap session * - * @return esp_err_t ESP_OK on success, ESP_FAIL on error + * @param handle pcap handle + * @return esp_err_t + * - ESP_OK on success + * - ESP_FAIL on error */ -esp_err_t pcap_close(void); +esp_err_t pcap_deinit(pcap_handle_t handle); /** - * @brief Capture one packet into file in pcap format + * @brief Capture one packet into pcap file * - * @param payload pointer to the captured data + * @param handle pcap handle + * @param payload pointer of the captured data * @param length length of captured data * @param seconds second of capture time * @param microseconds microsecond of capture time - * @return esp_err_t ESP_OK on success, ESP_FAIL on IO error + * @return esp_err_t + * - ESP_OK on success + * - ESP_FAIL on error */ -esp_err_t pcap_capture_packet(void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds); +esp_err_t pcap_capture_packet(pcap_handle_t handle, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds); #ifdef __cplusplus } diff --git a/examples/wifi/simple_sniffer/main/Kconfig.projbuild b/examples/wifi/simple_sniffer/main/Kconfig.projbuild index 319a6b5a60..096af2c66c 100644 --- a/examples/wifi/simple_sniffer/main/Kconfig.projbuild +++ b/examples/wifi/simple_sniffer/main/Kconfig.projbuild @@ -1,20 +1,43 @@ menu "Example Configuration" config STORE_HISTORY - bool "Store command history in flash" + bool "Store command history into flash" default y help - Linenoise line editing library provides functions to save and load - command history. If this option is enabled, initalizes a FAT filesystem - and uses it to store command history. + Linenoise line editing library provides functions to save and load command history. + If this option is enabled, initalizes a FAT filesystem and uses it to store command history. - config SNIFFER_MOUNT_POINT - string "Mount Point in your filesystem to store pcap files" - default "/sdcard" + choice SNIFFER_PCAP_DESTINATION + prompt "Select destination to store pcap file" + default SNIFFER_PCAP_DESTINATION_SD help - Here you need to specify the mount point in the VFS (Virtual File System) where the pcap would be saved. + Select where to store the pcap file. + Currently support storing files to SD card or to host via JTAG interface. + config SNIFFER_PCAP_DESTINATION_SD + bool "SD Card" + help + Store pcap file to SD card. + config SNIFFER_PCAP_DESTINATION_JTAG + bool "JTAG (App Trace)" + help + Store pcap file to host via JTAG interface. + endchoice - config SNIFFER_WORK_QUEUE_LENGTH + if SNIFFER_PCAP_DESTINATION_SD + config SNIFFER_MOUNT_POINT + string "SD card mount point in the filesystem" + default "/sdcard" + help + Specify the mount point in the VFS (Virtual File System) for SD card. + + config PCAP_FILE_NAME_MAX_LEN + int "Max name length of pcap file" + default 32 + help + Specify maximum name length of pcap file. + endif + + config SNIFFER_WORK_QUEUE_LEN int "Length of sniffer work queue" default 128 help @@ -24,9 +47,9 @@ menu "Example Configuration" config SNIFFER_TASK_STACK_SIZE int "Stack size of sniffer task" - default 2560 + default 4096 help - The stack size of sniffer task. + Stack size of sniffer task. config SNIFFER_TASK_PRIORITY int "Priority of sniffer task" @@ -34,11 +57,4 @@ menu "Example Configuration" help Priority of sniffer task. - config PCAP_FILE_MAX_PACKETS - int "Max packets in a pcap file" - default 2000 - help - To avoid the pcap file being very large, we should save packets into multiple fiiles. - Here you should specify the max number of packets that should be save in one pcap file. - endmenu diff --git a/examples/wifi/simple_sniffer/main/cmd_sniffer.c b/examples/wifi/simple_sniffer/main/cmd_sniffer.c index fdf7572c94..a756eba438 100644 --- a/examples/wifi/simple_sniffer/main/cmd_sniffer.c +++ b/examples/wifi/simple_sniffer/main/cmd_sniffer.c @@ -16,48 +16,58 @@ #include "esp_log.h" #include "esp_wifi.h" #include "esp_console.h" +#include "esp_app_trace.h" #include "cmd_sniffer.h" #include "pcap.h" #include "sdkconfig.h" -#define SNIFFER_DEFAULT_FILE_NAME "sniffer" -#define SNIFFER_DEFAULT_CHANNEL 1 +#define SNIFFER_DEFAULT_FILE_NAME "esp-sniffer" +#define SNIFFER_FILE_NAME_MAX_LEN CONFIG_PCAP_FILE_NAME_MAX_LEN +#define SNIFFER_DEFAULT_CHANNEL (1) +#define SNIFFER_PAYLOAD_FCS_LEN (4) +#define SNIFFER_PROCESS_PACKET_TIMEOUT_MS (100) +#define SNIFFER_PROCESS_APPTRACE_TIMEOUT_US (100) +#define SNIFFER_APPTRACE_RETRY (10) -static const char *TAG = "cmd_sniffer"; +static const char *SNIFFER_TAG = "cmd_sniffer"; +#define SNIFFER_CHECK(a, str, goto_tag, ...) \ + do \ + { \ + if (!(a)) \ + { \ + ESP_LOGE(SNIFFER_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + goto goto_tag; \ + } \ + } while (0) -static bool sniffer_running = false; -static pcap_config_t pcap_config; -static QueueHandle_t sniffer_work_queue = NULL; -static SemaphoreHandle_t sem_task_over = NULL; +typedef struct { + char *filter_name; + uint32_t filter_val; +} wlan_filter_table_t; -static wlan_filter_table_t wifi_filter_hash_table[SNIFFER_WLAN_FILTER_MAX] = {0}; -static char packet_filepath[PCAP_FILE_NAME_MAX_LEN]; +typedef struct { + bool is_running; + sniffer_intf_t interf; + uint32_t channel; + uint32_t filter; +#if CONFIG_SNIFFER_PCAP_DESTINATION_SD + char filename[SNIFFER_FILE_NAME_MAX_LEN]; +#endif + pcap_handle_t pcap; + TaskHandle_t task; + QueueHandle_t work_queue; + SemaphoreHandle_t sem_task_over; +} sniffer_runtime_t; typedef struct { void *payload; uint32_t length; uint32_t seconds; uint32_t microseconds; -} sniffer_packet_into_t; +} sniffer_packet_info_t; -static esp_err_t create_packet_file(void) -{ - uint32_t file_no = 0; - char filename[PCAP_FILE_NAME_MAX_LEN + 15]; - do { - snprintf(filename, sizeof(filename), "%s%d.pcap", packet_filepath, file_no); - file_no++; - } while (0 == access(filename, F_OK)); - /* Create file to write, binary format */ - pcap_config.fp = fopen(filename, "wb"); - if (!pcap_config.fp) { - ESP_LOGE(TAG, "Create file %s failed", filename); - return ESP_FAIL; - } - ESP_LOGI(TAG, "Store packets to file: %s", filename); - - return ESP_OK; -} +static sniffer_runtime_t snf_rt = {0}; +static wlan_filter_table_t wifi_filter_hash_table[SNIFFER_WLAN_FILTER_MAX] = {0}; static uint32_t hash_func(const char *str, uint32_t max_num) { @@ -73,10 +83,10 @@ static uint32_t hash_func(const char *str, uint32_t max_num) static void create_wifi_filter_hashtable() { char *wifi_filter_keys[SNIFFER_WLAN_FILTER_MAX] = {"mgmt", "data", "ctrl", "misc", "mpdu", "ampdu"}; - uint32_t wifi_filter_values[SNIFFER_WLAN_FILTER_MAX] = {WIFI_PROMIS_FILTER_MASK_MGMT, WIFI_PROMIS_FILTER_MASK_DATA, - WIFI_PROMIS_FILTER_MASK_CTRL, WIFI_PROMIS_FILTER_MASK_MISC, - WIFI_PROMIS_FILTER_MASK_DATA_MPDU, WIFI_PROMIS_FILTER_MASK_DATA_AMPDU - }; + uint32_t wifi_filter_values[SNIFFER_WLAN_FILTER_MAX] = {WIFI_PROMIS_FILTER_MASK_MGMT, WIFI_PROMIS_FILTER_MASK_DATA, + WIFI_PROMIS_FILTER_MASK_CTRL, WIFI_PROMIS_FILTER_MASK_MISC, + WIFI_PROMIS_FILTER_MASK_DATA_MPDU, WIFI_PROMIS_FILTER_MASK_DATA_AMPDU + }; for (int i = 0; i < SNIFFER_WLAN_FILTER_MAX; i++) { uint32_t idx = hash_func(wifi_filter_keys[i], SNIFFER_WLAN_FILTER_MAX); while (wifi_filter_hash_table[idx].filter_name) { @@ -110,123 +120,166 @@ static uint32_t search_wifi_filter_hashtable(const char *key) static void wifi_sniffer_cb(void *recv_buf, wifi_promiscuous_pkt_type_t type) { - if (sniffer_running) { - sniffer_packet_into_t packet_info; - wifi_promiscuous_pkt_t *sniffer = (wifi_promiscuous_pkt_t *)recv_buf; - /* prepare packet_info */ - packet_info.seconds = sniffer->rx_ctrl.timestamp / 1000000U; - packet_info.microseconds = sniffer->rx_ctrl.timestamp % 1000000U; - packet_info.length = sniffer->rx_ctrl.sig_len; - wifi_promiscuous_pkt_t *backup = malloc(sniffer->rx_ctrl.sig_len); + sniffer_packet_info_t packet_info; + wifi_promiscuous_pkt_t *sniffer = (wifi_promiscuous_pkt_t *)recv_buf; + /* prepare packet_info */ + packet_info.seconds = sniffer->rx_ctrl.timestamp / 1000000U; + packet_info.microseconds = sniffer->rx_ctrl.timestamp % 1000000U; + packet_info.length = sniffer->rx_ctrl.sig_len; + /* For now, the sniffer only dumps the length of the MISC type frame */ + if (type != WIFI_PKT_MISC && !sniffer->rx_ctrl.rx_state) { + packet_info.length -= SNIFFER_PAYLOAD_FCS_LEN; + void *backup = malloc(packet_info.length); if (backup) { - memcpy(backup, sniffer->payload, sniffer->rx_ctrl.sig_len); + memcpy(backup, sniffer->payload, packet_info.length); packet_info.payload = backup; - if (sniffer_work_queue) { + if (snf_rt.work_queue) { /* send packet_info */ - if (xQueueSend(sniffer_work_queue, &packet_info, 100 / portTICK_PERIOD_MS) != pdTRUE) { - ESP_LOGE(TAG, "sniffer work queue full"); + if (xQueueSend(snf_rt.work_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS)) != pdTRUE) { + ESP_LOGE(SNIFFER_TAG, "sniffer work queue full"); } } } else { - ESP_LOGE(TAG, "No enough memory for promiscuous packet"); + ESP_LOGE(SNIFFER_TAG, "No enough memory for promiscuous packet"); } } } static void sniffer_task(void *parameters) { - static uint32_t count = 0; - sniffer_packet_into_t packet_info; - BaseType_t ret = 0; + sniffer_packet_info_t packet_info; + sniffer_runtime_t *sniffer = (sniffer_runtime_t *)parameters; - while (sniffer_running) { + while (sniffer->is_running) { /* receive paclet info from queue */ - ret = xQueueReceive(sniffer_work_queue, &packet_info, 100 / portTICK_PERIOD_MS); - if (ret != pdTRUE) { + if (xQueueReceive(sniffer->work_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS)) != pdTRUE) { continue; } - if (pcap_capture_packet(packet_info.payload, packet_info.length, - packet_info.seconds, packet_info.microseconds) == ESP_OK) { - count++; - /* truncate, create another file */ - if (count >= CONFIG_PCAP_FILE_MAX_PACKETS) { - pcap_close(); - if (create_packet_file() != ESP_OK || pcap_new(&pcap_config) != ESP_OK) { - sniffer_running = false; - } else { - count = 0; - } - } + if (pcap_capture_packet(sniffer->pcap, packet_info.payload, packet_info.length, + packet_info.seconds, packet_info.microseconds) != ESP_OK) { + ESP_LOGW(SNIFFER_TAG, "save captured packet failed"); } free(packet_info.payload); } /* notify that sniffer task is over */ - xSemaphoreGive(sem_task_over); + xSemaphoreGive(sniffer->sem_task_over); vTaskDelete(NULL); } -static esp_err_t snifer_stop(sniffer_config_t *sniffer) +static esp_err_t sniffer_stop(sniffer_runtime_t *sniffer) { - /* Do interface specific work here */ + SNIFFER_CHECK(sniffer->is_running, "sniffer is already stopped", err); + switch (sniffer->interf) { case SNIFFER_INTF_WLAN: /* Disable wifi promiscuous mode */ - esp_wifi_set_promiscuous(false); + SNIFFER_CHECK(esp_wifi_set_promiscuous(false) == ESP_OK, "stop wifi promiscuous failed", err); break; default: + SNIFFER_CHECK(false, "unsupported interface", err); break; } + ESP_LOGI(SNIFFER_TAG, "stop WiFi promiscuous ok"); + /* stop sniffer local task */ - sniffer_running = false; + sniffer->is_running = false; /* wait for task over */ - xSemaphoreTake(sem_task_over, portMAX_DELAY); - vSemaphoreDelete(sem_task_over); - sem_task_over = NULL; + xSemaphoreTake(sniffer->sem_task_over, portMAX_DELAY); + vSemaphoreDelete(sniffer->sem_task_over); + sniffer->sem_task_over = NULL; /* make sure to free all resources in the left items */ - UBaseType_t left_items = uxQueueMessagesWaiting(sniffer_work_queue); - sniffer_packet_into_t packet_info; + UBaseType_t left_items = uxQueueMessagesWaiting(sniffer->work_queue); + sniffer_packet_info_t packet_info; while (left_items--) { - xQueueReceive(sniffer_work_queue, &packet_info, 100 / portTICK_PERIOD_MS); + xQueueReceive(sniffer->work_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS)); free(packet_info.payload); } - /* delete queue */ - vQueueDelete(sniffer_work_queue); - sniffer_work_queue = NULL; - /* Close the pcap file */ - pcap_close(); - - ESP_LOGI(TAG, "Sniffer Stopped"); + vQueueDelete(sniffer->work_queue); + sniffer->work_queue = NULL; + /* stop pcap session */ + SNIFFER_CHECK(pcap_deinit(sniffer->pcap) == ESP_OK, "stop pcap session failed", err); return ESP_OK; +err: + return ESP_FAIL; } -static esp_err_t sniffer_start(sniffer_config_t *sniffer) +#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG +static int trace_writefun(void *cookie, const char *buf, int len) { + return esp_apptrace_write(ESP_APPTRACE_DEST_TRAX, buf, len, SNIFFER_PROCESS_APPTRACE_TIMEOUT_US) == ESP_OK ? len : -1; +} + +static int trace_closefun(void *cookie) +{ + return esp_apptrace_flush(ESP_APPTRACE_DEST_TRAX, ESP_APPTRACE_TMO_INFINITE) == ESP_OK ? 0 : -1; +} +#endif + +static esp_err_t sniffer_start(sniffer_runtime_t *sniffer) +{ + pcap_config_t pcap_config; wifi_promiscuous_filter_t wifi_filter; - /* set sniffer running status before it starts to run */ - sniffer_running = true; - sniffer_work_queue = xQueueCreate(CONFIG_SNIFFER_WORK_QUEUE_LENGTH, sizeof(sniffer_packet_into_t)); - sem_task_over = xSemaphoreCreateBinary(); - /* sniffer task going to run*/ - xTaskCreate(sniffer_task, "sniffer", CONFIG_SNIFFER_TASK_STACK_SIZE, NULL, CONFIG_SNIFFER_TASK_PRIORITY, NULL); switch (sniffer->interf) { case SNIFFER_INTF_WLAN: - /* Set Promicuous Mode */ + pcap_config.link_type = PCAP_LINK_TYPE_802_11; + break; + default: + SNIFFER_CHECK(false, "unsupported interface", err); + break; + } + + /* Create file to write, binary format */ +#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG + pcap_config.fp = funopen("trace", NULL, trace_writefun, NULL, trace_closefun); +#elif CONFIG_SNIFFER_PCAP_DESTINATION_SD + pcap_config.fp = fopen(sniffer->filename, "wb"); +#else +#error "pcap file destination hasn't specified" +#endif + SNIFFER_CHECK(pcap_config.fp, "open file failed", err); + ESP_LOGI(SNIFFER_TAG, "open file successfully"); + + /* init a pcap session */ + SNIFFER_CHECK(pcap_init(&pcap_config, &sniffer->pcap) == ESP_OK, "init pcap session failed", err); + + sniffer->is_running = true; + sniffer->work_queue = xQueueCreate(CONFIG_SNIFFER_WORK_QUEUE_LEN, sizeof(sniffer_packet_info_t)); + SNIFFER_CHECK(sniffer->work_queue, "create work queue failed", err_queue); + sniffer->sem_task_over = xSemaphoreCreateBinary(); + SNIFFER_CHECK(sniffer->sem_task_over, "create sem failed", err_sem); + SNIFFER_CHECK(xTaskCreate(sniffer_task, "snifferT", CONFIG_SNIFFER_TASK_STACK_SIZE, + sniffer, CONFIG_SNIFFER_TASK_PRIORITY, &sniffer->task) == pdTRUE, + "create task failed", err_task); + + switch (sniffer->interf) { + case SNIFFER_INTF_WLAN: + /* Start WiFi Promicuous Mode */ wifi_filter.filter_mask = sniffer->filter; esp_wifi_set_promiscuous_filter(&wifi_filter); esp_wifi_set_promiscuous_rx_cb(wifi_sniffer_cb); - ESP_LOGI(TAG, "Start WiFi Promicuous Mode"); - ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); - /* Specify the channel */ + SNIFFER_CHECK(esp_wifi_set_promiscuous(true) == ESP_OK, "start wifi promiscuous failed", err_start); esp_wifi_set_channel(sniffer->channel, WIFI_SECOND_CHAN_NONE); - /* Create a new pcap object */ - pcap_config.link_type = PCAP_LINK_TYPE_802_11; - pcap_new(&pcap_config); + ESP_LOGI(SNIFFER_TAG, "start WiFi promiscuous ok"); break; default: break; } return ESP_OK; +err_start: + vTaskDelete(sniffer->task); + sniffer->task = NULL; +err_task: + vSemaphoreDelete(sniffer->sem_task_over); + sniffer->sem_task_over = NULL; +err_sem: + vQueueDelete(sniffer->work_queue); + sniffer->work_queue = NULL; +err_queue: + sniffer->is_running = false; + pcap_deinit(sniffer->pcap); +err: + return ESP_FAIL; } static struct { @@ -240,67 +293,73 @@ static struct { static int do_sniffer_cmd(int argc, char **argv) { - sniffer_config_t sniffer; - int nerrors = arg_parse(argc, argv, (void **)&sniffer_args); if (nerrors != 0) { arg_print_errors(stderr, sniffer_args.end, argv[0]); return 0; } - memset(&sniffer, 0, sizeof(sniffer)); - - /* Check interface: "-i" option */ - if (sniffer_args.interface->count) { - if (!strncmp(sniffer_args.interface->sval[0], "wlan", 4)) { - sniffer.interf = SNIFFER_INTF_WLAN; - } else { - ESP_LOGE(TAG, "Do not support interface %s", sniffer_args.interface->sval[0]); - return 1; - } - } else { - sniffer.interf = SNIFFER_INTF_WLAN; - } /* Check whether or not to stop sniffer: "--stop" option */ if (sniffer_args.stop->count) { /* stop sniffer */ - snifer_stop(&sniffer); + sniffer_stop(&snf_rt); return 0; } - /* Check channel: "-c" option */ - sniffer.channel = 0; - if (sniffer_args.channel->count) { - sniffer.channel = sniffer_args.channel->ival[0]; - } else { - sniffer.channel = SNIFFER_DEFAULT_CHANNEL; + + /* Check interface: "-i" option */ + snf_rt.interf = SNIFFER_INTF_WLAN; + if (sniffer_args.interface->count) { + if (!strncmp(sniffer_args.interface->sval[0], "wlan", 4)) { + snf_rt.interf = SNIFFER_INTF_WLAN; + } else { + ESP_LOGE(SNIFFER_TAG, "unsupported interface %s", sniffer_args.interface->sval[0]); + return 1; + } } - /* set pcap file name: "-f" option */ - if (sniffer_args.file->count) { - snprintf(packet_filepath, PCAP_FILE_NAME_MAX_LEN, "%s/%s", - CONFIG_SNIFFER_MOUNT_POINT, sniffer_args.file->sval[0]); - } else { - snprintf(packet_filepath, PCAP_FILE_NAME_MAX_LEN, "%s/%s", - CONFIG_SNIFFER_MOUNT_POINT, SNIFFER_DEFAULT_FILE_NAME); + /* Check channel: "-c" option */ + snf_rt.channel = SNIFFER_DEFAULT_CHANNEL; + if (sniffer_args.channel->count) { + snf_rt.channel = sniffer_args.channel->ival[0]; } - /* Determin file name */ - if (create_packet_file() != ESP_OK) { + +#if CONFIG_SNIFFER_PCAP_DESTINATION_SD + /* set pcap file name: "-f" option */ + int len = snprintf(snf_rt.filename, sizeof(snf_rt.filename), "%s/%s.pcap", CONFIG_SNIFFER_MOUNT_POINT, SNIFFER_DEFAULT_FILE_NAME); + if (sniffer_args.file->count) { + len = snprintf(snf_rt.filename, sizeof(snf_rt.filename), "%s/%s.pcap", CONFIG_SNIFFER_MOUNT_POINT, sniffer_args.file->sval[0]); + } + if (len >= sizeof(snf_rt.filename)) { + ESP_LOGW(SNIFFER_TAG, "pcap file name too long, try to enlarge memory in menuconfig"); + } +#endif +#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG + uint32_t retry = 0; + /* wait until apptrace communication established or timeout */ + while (!esp_apptrace_host_is_connected(ESP_APPTRACE_DEST_TRAX) && (retry < SNIFFER_APPTRACE_RETRY)) { + retry++; + ESP_LOGW(SNIFFER_TAG, "waiting for apptrace established"); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + if (retry >= SNIFFER_APPTRACE_RETRY) { + ESP_LOGE(SNIFFER_TAG, "waiting for apptrace established timeout"); return 1; } +#endif /* Check filter setting: "-F" option */ - switch (sniffer.interf) { + switch (snf_rt.interf) { case SNIFFER_INTF_WLAN: if (sniffer_args.filter->count) { for (int i = 0; i < sniffer_args.filter->count; i++) { - sniffer.filter += search_wifi_filter_hashtable(sniffer_args.filter->sval[i]); + snf_rt.filter += search_wifi_filter_hashtable(sniffer_args.filter->sval[i]); } /* When filter conditions are all wrong */ - if (sniffer.filter == 0) { - sniffer.filter = WIFI_PROMIS_FILTER_MASK_ALL; + if (snf_rt.filter == 0) { + snf_rt.filter = WIFI_PROMIS_FILTER_MASK_ALL; } } else { - sniffer.filter = WIFI_PROMIS_FILTER_MASK_ALL; + snf_rt.filter = WIFI_PROMIS_FILTER_MASK_ALL; } break; default: @@ -308,7 +367,7 @@ static int do_sniffer_cmd(int argc, char **argv) } /* start sniffer */ - sniffer_start(&sniffer); + sniffer_start(&snf_rt); return 0; } @@ -323,14 +382,14 @@ void register_sniffer() sniffer_args.channel = arg_int0("c", "channel", "", "communication channel to use"); sniffer_args.stop = arg_lit0(NULL, "stop", "stop running sniffer"); sniffer_args.end = arg_end(1); - const esp_console_cmd_t iperf_cmd = { + const esp_console_cmd_t sniffer_cmd = { .command = "sniffer", .help = "Capture specific packet and store in pcap format", .hint = NULL, .func = &do_sniffer_cmd, .argtable = &sniffer_args }; - ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd)); + ESP_ERROR_CHECK(esp_console_cmd_register(&sniffer_cmd)); create_wifi_filter_hashtable(); } diff --git a/examples/wifi/simple_sniffer/main/cmd_sniffer.h b/examples/wifi/simple_sniffer/main/cmd_sniffer.h index bf046380bf..c98f857d63 100644 --- a/examples/wifi/simple_sniffer/main/cmd_sniffer.h +++ b/examples/wifi/simple_sniffer/main/cmd_sniffer.h @@ -12,32 +12,28 @@ extern "C" { #endif +/** + * @brief Supported Sniffer Interface + * + */ typedef enum { - SNIFFER_INTF_WLAN = 0, + SNIFFER_INTF_WLAN = 0, /*!< WLAN interface */ } sniffer_intf_t; +/** + * @brief WLAN Sniffer Filter + * + */ typedef enum { - SNIFFER_WLAN_FILTER_MGMT = 0, - SNIFFER_WLAN_FILTER_CTRL, - SNIFFER_WLAN_FILTER_DATA, - SNIFFER_WLAN_FILTER_MISC, - SNIFFER_WLAN_FILTER_MPDU, - SNIFFER_WLAN_FILTER_AMPDU, + SNIFFER_WLAN_FILTER_MGMT = 0, /*!< MGMT */ + SNIFFER_WLAN_FILTER_CTRL, /*!< CTRL */ + SNIFFER_WLAN_FILTER_DATA, /*!< DATA */ + SNIFFER_WLAN_FILTER_MISC, /*!< MISC */ + SNIFFER_WLAN_FILTER_MPDU, /*!< MPDU */ + SNIFFER_WLAN_FILTER_AMPDU, /*!< AMPDU */ SNIFFER_WLAN_FILTER_MAX } sniffer_wlan_filter_t; -typedef struct { - char *filter_name; - uint32_t filter_val; -} wlan_filter_table_t; - -typedef struct { - sniffer_intf_t interf; - uint32_t channel; - uint32_t duration; - uint32_t filter; -} sniffer_config_t; - void register_sniffer(); #ifdef __cplusplus diff --git a/examples/wifi/simple_sniffer/main/simple_sniffer_example_main.c b/examples/wifi/simple_sniffer/main/simple_sniffer_example_main.c index 4048590563..fffc1cc375 100644 --- a/examples/wifi/simple_sniffer/main/simple_sniffer_example_main.c +++ b/examples/wifi/simple_sniffer/main/simple_sniffer_example_main.c @@ -120,6 +120,7 @@ static void initialize_console() #endif } +#if CONFIG_SNIFFER_PCAP_DESTINATION_SD static struct { struct arg_str *device; struct arg_end *end; @@ -155,7 +156,6 @@ static int mount(int argc, char **argv) // initialize SD card and mount FAT filesystem. sdmmc_card_t *card; esp_err_t ret = esp_vfs_fat_sdmmc_mount(CONFIG_SNIFFER_MOUNT_POINT, &host, &slot_config, &mount_config, &card); - if (ret != ESP_OK) { if (ret == ESP_FAIL) { ESP_LOGE(TAG, "Failed to mount filesystem. " @@ -194,7 +194,7 @@ static int unmount(int argc, char **argv) arg_print_errors(stderr, mount_args.end, argv[0]); return 1; } - /* mount sd card */ + /* unmount sd card */ if (!strncmp(mount_args.device->sval[0], "sd", 2)) { if (esp_vfs_fat_sdmmc_unmount() != ESP_OK) { ESP_LOGE(TAG, "Card unmount failed"); @@ -218,6 +218,7 @@ static void register_unmount() }; ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); } +#endif // CONFIG_SNIFFER_PCAP_DESTINATION_SD void app_main(void) { @@ -234,8 +235,10 @@ void app_main(void) /* Register commands */ esp_console_register_help_command(); +#if CONFIG_SNIFFER_PCAP_DESTINATION_SD register_mount(); register_unmount(); +#endif register_sniffer(); register_system(); diff --git a/examples/wifi/simple_sniffer/sdkconfig.defaults b/examples/wifi/simple_sniffer/sdkconfig.defaults index 23fd7b8517..3de6269e66 100644 --- a/examples/wifi/simple_sniffer/sdkconfig.defaults +++ b/examples/wifi/simple_sniffer/sdkconfig.defaults @@ -20,3 +20,7 @@ CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_FATFS_LFN_HEAP=y CONFIG_FATFS_MAX_LFN=31 +# App trace +CONFIG_ESP32_APPTRACE_DEST_TRAX=y +CONFIG_ESP32_APPTRACE_ENABLE=y + diff --git a/examples/wifi/simple_sniffer/sniffer-esp32-pcap.png b/examples/wifi/simple_sniffer/sniffer-esp32-pcap.png new file mode 100644 index 0000000000..17d579fef2 Binary files /dev/null and b/examples/wifi/simple_sniffer/sniffer-esp32-pcap.png differ diff --git a/examples/wifi/simple_sniffer/sniffer-example0-pcap.png b/examples/wifi/simple_sniffer/sniffer-example0-pcap.png deleted file mode 100644 index acff3c53a6..0000000000 Binary files a/examples/wifi/simple_sniffer/sniffer-example0-pcap.png and /dev/null differ