sniffer: capture packets to host via JTAG

Based on app-trace component, it's able to send sniffer packets to host via JTAG interface.
This commit is contained in:
morris 2019-01-25 17:03:01 +08:00
parent a8b2e982e1
commit ae6d19b4fb
10 changed files with 399 additions and 269 deletions

View file

@ -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.)
(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.)

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -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;
}

View file

@ -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 <stdio.h>
#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
}

View file

@ -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

View file

@ -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", "<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();
}

View file

@ -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

View file

@ -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();

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 KiB