添加智能灯固件代码

This commit is contained in:
kerwincui
2021-07-13 17:14:51 +08:00
parent 332f74dd17
commit ecc0b91b8b
2568 changed files with 229441 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/components)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(simple_sniffer)

View File

@@ -0,0 +1,11 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := simple_sniffer
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/system/console/components
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,140 @@
# Simple Sniffer Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
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.
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 [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
Open the project configuration menu (`idf.py menuconfig`). Then go into `Example Configuration` menu.
- Check `Store command history in flash` if you want to save command history into flash (recommend).
- 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
```
idf.py -p PORT flash monitor
```
(Replace PORT with name of the serial port.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
### `sniffer` Command Usage
> sniffer [-f <file>][-i ] [-F <mgmt|data|ctrl|misc|mpdu|ampdu>]... [-c <channel>][--stop]
> Capture specific packet and store in pcap format
> -f, --file=<file> name of the file storing the packets in pcap format
> -i, --interface=<wlan> which interface to capture packet
> -F, --filter=<mgmt|data|ctrl|misc|mpdu|ampdu> filter parameters
> -c, --channel=<channel> communication channel to use
> --stop stop running sniffer
The `sniffer` command support some important options as follow:
* `-f`: Specify the name of file who will store the packets, default value is `sniffer`, and the resulting file name will be like “snifferX.pcap”, here X shows the files order.
* `-i`: Specify the interface to sniffer packets, currently only support `wlan`
* `-c` :Specify the channel to sniffer packet
* `-F`: Specify the filter condition, currently only support following filter conditions, you can select any number of them
* mgmt: Management packets
* data: Data packets
* ctrl: Control packets
* misc: Other packets
* mpdu: MPDU packets
* ampdu: AMPDU packets
* `--stop`: Stop sniffer job
### Mount SD Card
```bash
=======================================================
| Steps to sniffer WiFi packets |
| |
| 1. Enter 'help' to check all commands' usage |
| 2. Enter 'mount <device>' to mount filesystem |
| 3. Enter 'sniffer' to start capture packets |
| 4. Enter 'unmount <device>' to unmount filesystem |
| |
=======================================================
esp32> mount sd
I (158912) example: Initializing SD card
I (158912) example: Using SDMMC peripheral
I (158912) gpio: GPIO[13]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
Name: SA16G
Type: SDHC/SDXC
Speed: 20 MHz
Size: 14832MB
```
### Start Sniffer
```bash
esp32> sniffer -f sniffer-example -i wlan -c 2
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 (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
```bash
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 -p PORT 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-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.)

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "pcap.c"
INCLUDE_DIRS .)

View File

@@ -0,0 +1,11 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
#include $(IDF_PATH)/make/component_common.mk
COMPONENT_ADD_INCLUDEDIRS := .

View File

@@ -0,0 +1,138 @@
// 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>
#include <sys/unistd.h>
#include <sys/fcntl.h>
#include "esp_types.h"
#include "esp_err.h"
#include "esp_log.h"
#include "pcap.h"
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
*
*/
typedef struct {
uint32_t magic; /*!< Magic Number */
uint16_t major; /*!< Major Version */
uint16_t minor; /*!< Minor Version */
uint32_t zone; /*!< Time Zone Offset */
uint32_t sigfigs; /*!< Timestamp Accuracy */
uint32_t snaplen; /*!< Max Length to Capture */
uint32_t link_type; /*!< Link Layer Type */
} pcap_file_header_t;
/**
* @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, no longer than packet_length */
uint32_t packet_length; /*!< Actual length of current packet */
} pcap_packet_header_t;
/**
* @brief Pcap Runtime Handle
*
*/
typedef struct {
FILE *file; /*!< File handle */
} pcap_runtime_t;
esp_err_t pcap_capture_packet(pcap_handle_t handle, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds)
{
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,
.microseconds = microseconds,
.capture_length = length,
.packet_length = length
};
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(pcap_rt->file);
return ESP_OK;
err:
return ESP_FAIL;
}
esp_err_t pcap_deinit(pcap_handle_t handle)
{
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_init(pcap_config_t *config, pcap_handle_t *handle)
{
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,
.major = PCAP_VERSION_MAJOR,
.minor = PCAP_VERSION_MINOR,
.zone = PCAP_TIME_ZONE_GMT,
.sigfigs = 0,
.snaplen = 0x40000,
.link_type = config->link_type
};
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(pcap_rt->file);
*handle = (pcap_handle_t)pcap_rt;
ESP_LOGD(PCAP_TAG, "pcap init OK");
return ESP_OK;
/* Error Handling */
err_write:
fclose(pcap_rt->file);
pcap_rt->file = NULL;
free(pcap_rt);
err:
ESP_LOGW(PCAP_TAG, "pcap init failed");
return ESP_FAIL;
}

View File

@@ -0,0 +1,105 @@
// 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
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 */
#define PCAP_VERSION_MAJOR 0x02 /*!< Major Version */
#define PCAP_VERSION_MINOR 0x04 /*!< Minor Version */
#define PCAP_TIME_ZONE_GMT 0x00 /*!< Time Zone */
/**
* @brief Link layer Type Definition, used for Pcap reader to decode payload
*
*/
typedef enum {
PCAP_LINK_TYPE_LOOPBACK = 0, /*!< Loopback devices, except for later OpenBSD */
PCAP_LINK_TYPE_ETHERNET = 1, /*!< Ethernet, and Linux loopback devices */
PCAP_LINK_TYPE_TOKEN_RING = 6, /*!< 802.5 Token Ring */
PCAP_LINK_TYPE_ARCNET = 7, /*!< ARCnet */
PCAP_LINK_TYPE_SLIP = 8, /*!< SLIP */
PCAP_LINK_TYPE_PPP = 9, /*!< PPP */
PCAP_LINK_TYPE_FDDI = 10, /*!< FDDI */
PCAP_LINK_TYPE_ATM = 100, /*!< LLC/SNAP encapsulated ATM */
PCAP_LINK_TYPE_RAW_IP = 101, /*!< Raw IP, without link */
PCAP_LINK_TYPE_BSD_SLIP = 102, /*!< BSD/OS SLIP */
PCAP_LINK_TYPE_BSD_PPP = 103, /*!< BSD/OS PPP */
PCAP_LINK_TYPE_CISCO_HDLC = 104, /*!< Cisco HDLC */
PCAP_LINK_TYPE_802_11 = 105, /*!< 802.11 */
PCAP_LINK_TYPE_BSD_LOOPBACK = 108, /*!< OpenBSD loopback devices(with AF_value in network byte order) */
PCAP_LINK_TYPE_LOCAL_TALK = 114 /*!< LocalTalk */
} pcap_link_type_t;
/**
* @brief Pcap configuration Type Definition
*
*/
typedef struct {
FILE *fp; /* Pointer to a standard file handle */
pcap_link_type_t link_type; /* Pcap Link Type */
} pcap_config_t;
/**
* @brief Pcap Handle Type Definition
*
*/
typedef void *pcap_handle_t;
/**
* @brief Initialize a pcap session
*
* @param config configuration of creating pcap object
* @param handle pcap handle
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t pcap_init(pcap_config_t *config, pcap_handle_t *handle);
/**
* @brief De-initialize a pcap session
*
* @param handle pcap handle
* @return esp_err_t
* - ESP_OK on success
* - ESP_FAIL on error
*/
esp_err_t pcap_deinit(pcap_handle_t handle);
/**
* @brief Capture one packet into pcap file
*
* @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 error
*/
esp_err_t pcap_capture_packet(pcap_handle_t handle, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "simple_sniffer_example_main.c"
"cmd_sniffer.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,61 @@
menu "Example Configuration"
config SNIFFER_STORE_HISTORY
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.
choice SNIFFER_PCAP_DESTINATION
prompt "Select destination to store pcap file"
default SNIFFER_PCAP_DESTINATION_SD if IDF_TARGET_ESP32
default SNIFFER_PCAP_DESTINATION_JTAG if IDF_TARGET_ESP32S2
help
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
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 SNIFFER_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
The sniffer callback function should not do heavy work, so we put all heavy IO operation to another task.
The task gets some basic info of sniffer packet via queue.
Here you should specify the length of queue.
config SNIFFER_TASK_STACK_SIZE
int "Stack size of sniffer task"
default 4096
help
Stack size of sniffer task.
config SNIFFER_TASK_PRIORITY
int "Priority of sniffer task"
default 2
help
Priority of sniffer task.
endmenu

View File

@@ -0,0 +1,395 @@
/* cmd_sniffer example.
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.
*/
#include <string.h>
#include "argtable3/argtable3.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include <sys/unistd.h>
#include <sys/fcntl.h>
#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 "esp-sniffer"
#define SNIFFER_FILE_NAME_MAX_LEN CONFIG_SNIFFER_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 *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)
typedef struct {
char *filter_name;
uint32_t filter_val;
} wlan_filter_table_t;
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_info_t;
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)
{
uint32_t ret = 0;
char *p = (char *)str;
while (*p) {
ret += *p;
p++;
}
return ret % max_num;
}
static void create_wifi_filter_hashtable(void)
{
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
};
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) {
idx++;
if (idx >= SNIFFER_WLAN_FILTER_MAX) {
idx = 0;
}
}
wifi_filter_hash_table[idx].filter_name = wifi_filter_keys[i];
wifi_filter_hash_table[idx].filter_val = wifi_filter_values[i];
}
}
static uint32_t search_wifi_filter_hashtable(const char *key)
{
uint32_t len = strlen(key);
uint32_t start_idx = hash_func(key, SNIFFER_WLAN_FILTER_MAX);
uint32_t idx = start_idx;
while (strncmp(wifi_filter_hash_table[idx].filter_name, key, len)) {
idx++;
if (idx >= SNIFFER_WLAN_FILTER_MAX) {
idx = 0;
}
/* wrong key */
if (idx == start_idx) {
return 0;
}
}
return wifi_filter_hash_table[idx].filter_val;
}
static void wifi_sniffer_cb(void *recv_buf, wifi_promiscuous_pkt_type_t type)
{
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, packet_info.length);
packet_info.payload = backup;
if (snf_rt.work_queue) {
/* send packet_info */
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(SNIFFER_TAG, "No enough memory for promiscuous packet");
}
}
}
static void sniffer_task(void *parameters)
{
sniffer_packet_info_t packet_info;
sniffer_runtime_t *sniffer = (sniffer_runtime_t *)parameters;
while (sniffer->is_running) {
/* receive paclet info from queue */
if (xQueueReceive(sniffer->work_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS)) != pdTRUE) {
continue;
}
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(sniffer->sem_task_over);
vTaskDelete(NULL);
}
static esp_err_t sniffer_stop(sniffer_runtime_t *sniffer)
{
SNIFFER_CHECK(sniffer->is_running, "sniffer is already stopped", err);
switch (sniffer->interf) {
case SNIFFER_INTF_WLAN:
/* Disable wifi promiscuous mode */
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->is_running = false;
/* wait for task over */
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_info_t packet_info;
while (left_items--) {
xQueueReceive(sniffer->work_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS));
free(packet_info.payload);
}
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;
}
#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;
switch (sniffer->interf) {
case SNIFFER_INTF_WLAN:
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);
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);
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 {
struct arg_str *file;
struct arg_str *interface;
struct arg_str *filter;
struct arg_int *channel;
struct arg_lit *stop;
struct arg_end *end;
} sniffer_args;
static int do_sniffer_cmd(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&sniffer_args);
if (nerrors != 0) {
arg_print_errors(stderr, sniffer_args.end, argv[0]);
return 0;
}
/* Check whether or not to stop sniffer: "--stop" option */
if (sniffer_args.stop->count) {
/* stop sniffer */
sniffer_stop(&snf_rt);
return 0;
}
/* 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;
}
}
/* Check channel: "-c" option */
snf_rt.channel = SNIFFER_DEFAULT_CHANNEL;
if (sniffer_args.channel->count) {
snf_rt.channel = sniffer_args.channel->ival[0];
}
#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 (snf_rt.interf) {
case SNIFFER_INTF_WLAN:
if (sniffer_args.filter->count) {
for (int i = 0; i < sniffer_args.filter->count; i++) {
snf_rt.filter += search_wifi_filter_hashtable(sniffer_args.filter->sval[i]);
}
/* When filter conditions are all wrong */
if (snf_rt.filter == 0) {
snf_rt.filter = WIFI_PROMIS_FILTER_MASK_ALL;
}
} else {
snf_rt.filter = WIFI_PROMIS_FILTER_MASK_ALL;
}
break;
default:
break;
}
/* start sniffer */
sniffer_start(&snf_rt);
return 0;
}
void register_sniffer(void)
{
sniffer_args.file = arg_str0("f", "file", "<file>",
"name of the file storing the packets in pcap format");
sniffer_args.interface = arg_str0("i", "interface", "<wlan>",
"which interface to capture packet");
sniffer_args.filter = arg_strn("F", "filter", "<mgmt|data|ctrl|misc|mpdu|ampdu>", 0, 6, "filter parameters");
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 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(&sniffer_cmd));
create_wifi_filter_hashtable();
}

View File

@@ -0,0 +1,41 @@
/* cmd_sniffer example — declarations of command registration functions.
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.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Supported Sniffer Interface
*
*/
typedef enum {
SNIFFER_INTF_WLAN = 0, /*!< WLAN interface */
} sniffer_intf_t;
/**
* @brief WLAN Sniffer Filter
*
*/
typedef enum {
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;
void register_sniffer(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,213 @@
/* Sniffer example.
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.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sdkconfig.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "esp_netif.h"
#include "esp_console.h"
#include "esp_event.h"
#include "esp_vfs_fat.h"
#include "esp_wifi.h"
#include "esp_err.h"
#include "esp_log.h"
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
#include "driver/sdmmc_host.h"
#include "driver/sdspi_host.h"
#endif
#include "nvs_flash.h"
#include "sdmmc_cmd.h"
#include "cmd_system.h"
#include "cmd_sniffer.h"
#if CONFIG_SNIFFER_STORE_HISTORY
#define HISTORY_MOUNT_POINT "/data"
#define HISTORY_FILE_PATH HISTORY_MOUNT_POINT "/history.txt"
#endif
static const char *TAG = "example";
#if CONFIG_SNIFFER_STORE_HISTORY
/* Initialize filesystem for command history store */
static void initialize_filesystem(void)
{
static wl_handle_t wl_handle;
const esp_vfs_fat_mount_config_t mount_config = {
.max_files = 4,
.format_if_mount_failed = true
};
esp_err_t err = esp_vfs_fat_spiflash_mount(HISTORY_MOUNT_POINT, "storage", &mount_config, &wl_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
return;
}
}
#endif
static void initialize_nvs(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
}
/* Initialize wifi with tcp/ip adapter */
static void initialize_wifi(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
}
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
static struct {
struct arg_str *device;
struct arg_end *end;
} mount_args;
/** 'mount' command */
static int mount(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&mount_args);
if (nerrors != 0) {
arg_print_errors(stderr, mount_args.end, argv[0]);
return 1;
}
/* mount sd card */
if (!strncmp(mount_args.device->sval[0], "sd", 2)) {
ESP_LOGI(TAG, "Initializing SD card");
ESP_LOGI(TAG, "Using SDMMC peripheral");
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1-line modes
gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes
gpio_set_pull_mode(4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
gpio_set_pull_mode(12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 4,
.allocation_unit_size = 16 * 1024
};
// 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. "
"If you want the card to be formatted, set format_if_mount_failed = true.");
} else {
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
"Make sure SD card lines have pull-up resistors in place.",
esp_err_to_name(ret));
}
return 1;
}
/* print card info if mount successfully */
sdmmc_card_print_info(stdout, card);
}
return 0;
}
static void register_mount(void)
{
mount_args.device = arg_str1(NULL, NULL, "<sd>", "choose a proper device to mount/unmount");
mount_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "mount",
.help = "mount the filesystem",
.hint = NULL,
.func = &mount,
.argtable = &mount_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
static int unmount(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&mount_args);
if (nerrors != 0) {
arg_print_errors(stderr, mount_args.end, argv[0]);
return 1;
}
/* 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");
return -1;
}
ESP_LOGI(TAG, "Card unmounted");
}
return 0;
}
static void register_unmount(void)
{
mount_args.device = arg_str1(NULL, NULL, "<sd>", "choose a proper device to mount/unmount");
mount_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "unmount",
.help = "unmount the filesystem",
.hint = NULL,
.func = &unmount,
.argtable = &mount_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_SD
void app_main(void)
{
initialize_nvs();
/* Initialize WiFi */
initialize_wifi();
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
#if CONFIG_SNIFFER_STORE_HISTORY
initialize_filesystem();
repl_config.history_save_path = HISTORY_FILE_PATH;
#endif
repl_config.prompt = "sniffer>";
// init console REPL environment
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
/* Register commands */
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
register_mount();
register_unmount();
#endif
register_sniffer();
register_system();
printf("\n =======================================================\n");
printf(" | Steps to sniffer WiFi packets |\n");
printf(" | |\n");
printf(" | 1. Enter 'help' to check all commands' usage |\n");
printf(" | 2. Enter 'mount <device>' to mount filesystem |\n");
printf(" | 3. Enter 'sniffer' to start capture packets |\n");
printf(" | 4. Enter 'unmount <device>' to unmount filesystem |\n");
printf(" | |\n");
printf(" =======================================================\n\n");
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 1M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, fat, , 1M,

View File

@@ -0,0 +1,26 @@
# Reduce bootloader log verbosity
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_BOOTLOADER_LOG_LEVEL=2
# Increase main task stack size
CONFIG_ESP_MAIN_TASK_STACK_SIZE=7168
# Enable filesystem
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
# Enable FreeRTOS stats formatting functions, needed for 'tasks' command
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
# FatFS
CONFIG_FATFS_LFN_HEAP=y
CONFIG_FATFS_MAX_LFN=31
# App trace
CONFIG_APPTRACE_DEST_TRAX=y
CONFIG_APPTRACE_ENABLE=y

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB