example: add console command for wifi sniffer
1. add mount/unmount command 2. add sniffer command, supporting options 3. make pcap a component
This commit is contained in:
parent
c02e218da1
commit
900b69a33e
17 changed files with 1153 additions and 0 deletions
8
examples/wifi/simple_sniffer/CMakeLists.txt
Normal file
8
examples/wifi/simple_sniffer/CMakeLists.txt
Normal 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)
|
11
examples/wifi/simple_sniffer/Makefile
Normal file
11
examples/wifi/simple_sniffer/Makefile
Normal 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
|
||||||
|
|
124
examples/wifi/simple_sniffer/README.md
Normal file
124
examples/wifi/simple_sniffer/README.md
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
# 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. Go to wikipedia for more information about [pcap](https://en.wikipedia.org/wiki/Pcap).
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Enter `make -j4 flash monitor` if you are using GNU Make based build system or enter `idf.py build flash monitor` if you' are using CMake based build system.
|
||||||
|
|
||||||
|
(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 file’s 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 (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
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unmount SD Card
|
||||||
|
|
||||||
|
```bash
|
||||||
|
esp32> unmount sd
|
||||||
|
I (248800) example: Card unmounted
|
||||||
|
```
|
||||||
|
|
||||||
|
### Open PCap File in Wireshark
|
||||||
|
|
||||||
|
![sniffer-example0.pcap](sniffer-example0-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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(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.)
|
|
@ -0,0 +1,5 @@
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS .)
|
||||||
|
|
||||||
|
set(COMPONENT_SRCS "pcap.c")
|
||||||
|
|
||||||
|
register_component()
|
11
examples/wifi/simple_sniffer/components/pcap/component.mk
Normal file
11
examples/wifi/simple_sniffer/components/pcap/component.mk
Normal 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 := .
|
118
examples/wifi/simple_sniffer/components/pcap/pcap.c
Normal file
118
examples/wifi/simple_sniffer/components/pcap/pcap.c
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
#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 *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); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pcap File Header Type Definition
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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 Type Definition
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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 packet_length; /*!< Actual length of current packet */
|
||||||
|
} pcap_packet_header_t;
|
||||||
|
|
||||||
|
static FILE *file = NULL;
|
||||||
|
|
||||||
|
esp_err_t pcap_capture_packet(void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds)
|
||||||
|
{
|
||||||
|
if (!file) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
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, 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);
|
||||||
|
/* Flush content in the buffer into device */
|
||||||
|
fflush(file);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t pcap_close(void)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t pcap_new(pcap_config_t *config)
|
||||||
|
{
|
||||||
|
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, file);
|
||||||
|
if (real_write != 1) {
|
||||||
|
ESP_LOGE(TAG, "Write Pcap file header error");
|
||||||
|
goto err_write;
|
||||||
|
}
|
||||||
|
/* Flush content in the buffer into device */
|
||||||
|
fflush(file);
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
/* Error Handling */
|
||||||
|
err_write:
|
||||||
|
fclose(file);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
85
examples/wifi/simple_sniffer/components/pcap/pcap.h
Normal file
85
examples/wifi/simple_sniffer/components/pcap/pcap.h
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.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 */
|
||||||
|
|
||||||
|
#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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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 Create a pcap object
|
||||||
|
*
|
||||||
|
* @param config configuration of creating pcap object
|
||||||
|
* @return esp_err_t ESP_OK on success, ESP_FAIL on IO error
|
||||||
|
*/
|
||||||
|
esp_err_t pcap_new(pcap_config_t *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close pcap file and recyle related resources
|
||||||
|
*
|
||||||
|
* @return esp_err_t ESP_OK on success, ESP_FAIL on error
|
||||||
|
*/
|
||||||
|
esp_err_t pcap_close(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Capture one packet into file in pcap format
|
||||||
|
*
|
||||||
|
* @param payload pointer to 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
|
||||||
|
*/
|
||||||
|
esp_err_t pcap_capture_packet(void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
10
examples/wifi/simple_sniffer/main/CMakeLists.txt
Normal file
10
examples/wifi/simple_sniffer/main/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
# 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(COMPONENT_SRCS "simple_sniffer_example_main.c"
|
||||||
|
"cmd_sniffer.c")
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||||
|
|
||||||
|
register_component()
|
44
examples/wifi/simple_sniffer/main/Kconfig.projbuild
Normal file
44
examples/wifi/simple_sniffer/main/Kconfig.projbuild
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
config STORE_HISTORY
|
||||||
|
bool "Store command history in 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.
|
||||||
|
|
||||||
|
config SNIFFER_MOUNT_POINT
|
||||||
|
string "Mount Point in your filesystem to store pcap files"
|
||||||
|
default "/sdcard"
|
||||||
|
help
|
||||||
|
Here you need to specify the mount point in the VFS (Virtual File System) where the pcap would be saved.
|
||||||
|
|
||||||
|
config SNIFFER_WORK_QUEUE_LENGTH
|
||||||
|
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 2560
|
||||||
|
help
|
||||||
|
The stack size of sniffer task.
|
||||||
|
|
||||||
|
config SNIFFER_TASK_PRIORITY
|
||||||
|
int "Priority of sniffer task"
|
||||||
|
default 2
|
||||||
|
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
|
20
examples/wifi/simple_sniffer/main/cmd_decl.h
Normal file
20
examples/wifi/simple_sniffer/main/cmd_decl.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/* Iperf 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
|
||||||
|
|
||||||
|
#include "cmd_system.h"
|
||||||
|
#include "cmd_sniffer.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
336
examples/wifi/simple_sniffer/main/cmd_sniffer.c
Normal file
336
examples/wifi/simple_sniffer/main/cmd_sniffer.c
Normal file
|
@ -0,0 +1,336 @@
|
||||||
|
/* 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 "cmd_sniffer.h"
|
||||||
|
#include "pcap.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#define SNIFFER_DEFAULT_FILE_NAME "sniffer"
|
||||||
|
#define SNIFFER_DEFAULT_CHANNEL 1
|
||||||
|
|
||||||
|
static const char *TAG = "cmd_sniffer";
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
void *payload;
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t seconds;
|
||||||
|
uint32_t microseconds;
|
||||||
|
} sniffer_packet_into_t;
|
||||||
|
|
||||||
|
static esp_err_t create_packet_file(void)
|
||||||
|
{
|
||||||
|
uint32_t file_no = 0;
|
||||||
|
char filename[PCAP_FILE_NAME_MAX_LEN];
|
||||||
|
do {
|
||||||
|
snprintf(filename, PCAP_FILE_NAME_MAX_LEN, "%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 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()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
if (backup) {
|
||||||
|
memcpy(backup, sniffer->payload, sniffer->rx_ctrl.sig_len);
|
||||||
|
packet_info.payload = backup;
|
||||||
|
if (sniffer_work_queue) {
|
||||||
|
/* send packet_info */
|
||||||
|
if (xQueueSend(sniffer_work_queue, &packet_info, 100 / portTICK_PERIOD_MS) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "sniffer work queue full");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(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;
|
||||||
|
|
||||||
|
while (sniffer_running) {
|
||||||
|
/* receive paclet info from queue */
|
||||||
|
ret = xQueueReceive(sniffer_work_queue, &packet_info, 100 / portTICK_PERIOD_MS);
|
||||||
|
if (ret != 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(packet_info.payload);
|
||||||
|
}
|
||||||
|
/* notify that sniffer task is over */
|
||||||
|
xSemaphoreGive(sem_task_over);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t snifer_stop(sniffer_config_t *sniffer)
|
||||||
|
{
|
||||||
|
/* Do interface specific work here */
|
||||||
|
switch (sniffer->interf) {
|
||||||
|
case SNIFFER_INTF_WLAN:
|
||||||
|
/* Disable wifi promiscuous mode */
|
||||||
|
esp_wifi_set_promiscuous(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* stop sniffer local task */
|
||||||
|
sniffer_running = false;
|
||||||
|
/* wait for task over */
|
||||||
|
xSemaphoreTake(sem_task_over, portMAX_DELAY);
|
||||||
|
vSemaphoreDelete(sem_task_over);
|
||||||
|
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;
|
||||||
|
while (left_items--) {
|
||||||
|
xQueueReceive(sniffer_work_queue, &packet_info, 100 / portTICK_PERIOD_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");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t sniffer_start(sniffer_config_t *sniffer)
|
||||||
|
{
|
||||||
|
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 */
|
||||||
|
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 */
|
||||||
|
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);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
/* Determin file name */
|
||||||
|
if (create_packet_file() != ESP_OK) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check filter setting: "-F" option */
|
||||||
|
switch (sniffer.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]);
|
||||||
|
}
|
||||||
|
/* When filter conditions are all wrong */
|
||||||
|
if (sniffer.filter == 0) {
|
||||||
|
sniffer.filter = WIFI_PROMIS_FILTER_MASK_ALL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sniffer.filter = WIFI_PROMIS_FILTER_MASK_ALL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* start sniffer */
|
||||||
|
sniffer_start(&sniffer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_sniffer()
|
||||||
|
{
|
||||||
|
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 iperf_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));
|
||||||
|
|
||||||
|
create_wifi_filter_hashtable();
|
||||||
|
}
|
45
examples/wifi/simple_sniffer/main/cmd_sniffer.h
Normal file
45
examples/wifi/simple_sniffer/main/cmd_sniffer.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SNIFFER_INTF_WLAN = 0,
|
||||||
|
} sniffer_intf_t;
|
||||||
|
|
||||||
|
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_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
|
||||||
|
}
|
||||||
|
#endif
|
4
examples/wifi/simple_sniffer/main/component.mk
Normal file
4
examples/wifi/simple_sniffer/main/component.mk
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#
|
||||||
|
# "main" pseudo-component makefile.
|
||||||
|
#
|
||||||
|
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
304
examples/wifi/simple_sniffer/main/simple_sniffer_example_main.c
Normal file
304
examples/wifi/simple_sniffer/main/simple_sniffer_example_main.c
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
/* 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 "linenoise/linenoise.h"
|
||||||
|
#include "argtable3/argtable3.h"
|
||||||
|
#include "tcpip_adapter.h"
|
||||||
|
#include "esp_console.h"
|
||||||
|
#include "esp_event_loop.h"
|
||||||
|
#include "esp_vfs_dev.h"
|
||||||
|
#include "esp_vfs_fat.h"
|
||||||
|
#include "esp_wifi.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "driver/uart.h"
|
||||||
|
#include "driver/sdmmc_host.h"
|
||||||
|
#include "driver/sdspi_host.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "sdmmc_cmd.h"
|
||||||
|
#include "cmd_decl.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#if CONFIG_STORE_HISTORY
|
||||||
|
#define HISTORY_MOUNT_POINT "/data"
|
||||||
|
#define HISTORY_FILE_PATH HISTORY_MOUNT_POINT "/history.txt"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
#if CONFIG_STORE_HISTORY
|
||||||
|
/* Initialize filesystem for command history store */
|
||||||
|
static void initialize_filesystem()
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
tcpip_adapter_init();
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_init(NULL, NULL));
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize console component */
|
||||||
|
static void initialize_console()
|
||||||
|
{
|
||||||
|
/* Disable buffering on stdin and stdout */
|
||||||
|
setvbuf(stdin, NULL, _IONBF, 0);
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
|
||||||
|
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
|
||||||
|
esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
|
||||||
|
/* Move the caret to the beginning of the next line on '\n' */
|
||||||
|
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
|
||||||
|
|
||||||
|
/* Install UART driver for interrupt-driven reads and writes */
|
||||||
|
ESP_ERROR_CHECK(uart_driver_install(CONFIG_CONSOLE_UART_NUM,
|
||||||
|
256, 0, 0, NULL, 0));
|
||||||
|
|
||||||
|
/* Tell VFS to use UART driver */
|
||||||
|
esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
|
||||||
|
|
||||||
|
/* Initialize the console */
|
||||||
|
esp_console_config_t console_config = {
|
||||||
|
.max_cmdline_args = 8,
|
||||||
|
.max_cmdline_length = 256,
|
||||||
|
#if CONFIG_LOG_COLORS
|
||||||
|
.hint_color = atoi(LOG_COLOR_CYAN)
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_console_init(&console_config));
|
||||||
|
|
||||||
|
/* Configure linenoise line completion library */
|
||||||
|
/* Enable multiline editing. If not set, long commands will scroll within
|
||||||
|
* single line.
|
||||||
|
*/
|
||||||
|
linenoiseSetMultiLine(1);
|
||||||
|
|
||||||
|
/* Tell linenoise where to get command completions and hints */
|
||||||
|
linenoiseSetCompletionCallback(&esp_console_get_completion);
|
||||||
|
linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint);
|
||||||
|
|
||||||
|
/* Set command history size */
|
||||||
|
linenoiseHistorySetMaxLen(100);
|
||||||
|
|
||||||
|
#if CONFIG_STORE_HISTORY
|
||||||
|
/* Load command history from filesystem */
|
||||||
|
linenoiseHistoryLoad(HISTORY_FILE_PATH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/* mount 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()
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
initialize_nvs();
|
||||||
|
|
||||||
|
#if CONFIG_STORE_HISTORY
|
||||||
|
initialize_filesystem();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initialize WiFi */
|
||||||
|
initialize_wifi();
|
||||||
|
/* Initialize Console component */
|
||||||
|
initialize_console();
|
||||||
|
|
||||||
|
/* Register commands */
|
||||||
|
esp_console_register_help_command();
|
||||||
|
register_mount();
|
||||||
|
register_unmount();
|
||||||
|
register_sniffer();
|
||||||
|
register_system();
|
||||||
|
|
||||||
|
/* Prompt to be printed before each line.
|
||||||
|
* This can be customized, made dynamic, etc.
|
||||||
|
*/
|
||||||
|
const char *prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR;
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
/* Figure out if the terminal supports escape sequences */
|
||||||
|
int probe_status = linenoiseProbe();
|
||||||
|
if (probe_status) {
|
||||||
|
/* zero indicates success */
|
||||||
|
printf("\n"
|
||||||
|
"Your terminal application does not support escape sequences.\n"
|
||||||
|
"Line editing and history features are disabled.\n"
|
||||||
|
"On Windows, try using Putty instead.\n");
|
||||||
|
linenoiseSetDumbMode(1);
|
||||||
|
#if CONFIG_LOG_COLORS
|
||||||
|
/* Since the terminal doesn't support escape sequences,
|
||||||
|
* don't use color codes in the prompt.
|
||||||
|
*/
|
||||||
|
prompt = "esp32> ";
|
||||||
|
#endif //CONFIG_LOG_COLORS
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main loop */
|
||||||
|
while (true) {
|
||||||
|
/* Get a line using linenoise.
|
||||||
|
* The line is returned when ENTER is pressed.
|
||||||
|
*/
|
||||||
|
char *line = linenoise(prompt);
|
||||||
|
if (line == NULL) {
|
||||||
|
/* Ignore empty lines */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Add the command to the history */
|
||||||
|
linenoiseHistoryAdd(line);
|
||||||
|
|
||||||
|
#if CONFIG_STORE_HISTORY
|
||||||
|
/* Save command history to filesystem */
|
||||||
|
linenoiseHistorySave(HISTORY_FILE_PATH);
|
||||||
|
#endif
|
||||||
|
/* Try to run the command */
|
||||||
|
int ret;
|
||||||
|
esp_err_t err = esp_console_run(line, &ret);
|
||||||
|
if (err == ESP_ERR_NOT_FOUND) {
|
||||||
|
printf("Unrecognized command\n");
|
||||||
|
} else if (err == ESP_OK && ret != ESP_OK) {
|
||||||
|
printf("Command returned non-zero error code: 0x%x\n", ret);
|
||||||
|
} else if (err != ESP_OK) {
|
||||||
|
printf("Internal error: %s\n", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
/* linenoise allocates line buffer on the heap, so need to free it */
|
||||||
|
linenoiseFree(line);
|
||||||
|
}
|
||||||
|
}
|
6
examples/wifi/simple_sniffer/partitions_example.csv
Normal file
6
examples/wifi/simple_sniffer/partitions_example.csv
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
factory, app, factory, 0x10000, 1M,
|
||||||
|
storage, data, fat, , 1M,
|
|
22
examples/wifi/simple_sniffer/sdkconfig.defaults
Normal file
22
examples/wifi/simple_sniffer/sdkconfig.defaults
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Reduce bootloader log verbosity
|
||||||
|
CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y
|
||||||
|
CONFIG_LOG_BOOTLOADER_LEVEL=2
|
||||||
|
|
||||||
|
# Increase main task stack size
|
||||||
|
CONFIG_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
|
||||||
|
|
BIN
examples/wifi/simple_sniffer/sniffer-example0-pcap.png
Normal file
BIN
examples/wifi/simple_sniffer/sniffer-example0-pcap.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 504 KiB |
Loading…
Reference in a new issue