mirror of
https://github.com/espressif/esp-idf.git
synced 2024-09-19 14:26:01 -04:00
simple_sniffer: can store packets in memory
This commit is contained in:
parent
f04f078825
commit
d6d0c37b30
@ -2,7 +2,7 @@
|
||||
# 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/advanced/components)
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/common_components/pcap")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(simple_sniffer)
|
||||
|
@ -5,6 +5,6 @@
|
||||
|
||||
PROJECT_NAME := simple_sniffer
|
||||
|
||||
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/system/console/advanced/components
|
||||
EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/common_components/pcap
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
@ -28,7 +28,8 @@ Open the project configuration menu (`idf.py menuconfig`). Then go into `Example
|
||||
- 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. The default SD card work mode is set to SDMMC for target ESP32 and ESP32S3, but SPI is the only choice for other targets.
|
||||
- `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), Component config -> Application Level Tracing -> Data Destination -> Trace memory should be enabled to choose `JTAG (App Trace)` as destination.
|
||||
- `Memory` means saving packets in memory and can parse packets in place.
|
||||
- `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), Component config -> Application Lelvel Tracing -> Data Destination -> Trace memory should be enabled to choose `JTAG (App Trace)` as destination.
|
||||
- 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.
|
||||
@ -52,23 +53,20 @@ idf.py -p PORT flash monitor
|
||||
|
||||
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 <wlan|eth0|eth1|...>] [-F <mgmt|data|ctrl|misc|mpdu|ampdu>]... [-c <channel>][--stop]
|
||||
> sniffer [-i <wlan|eth0|eth1|...>] [-F <mgmt|data|ctrl|misc|mpdu|ampdu|fcsfail>]... [-c <channel>] [--stop] [-n <num>]
|
||||
> Capture specific packet and store in pcap format
|
||||
> -f, --file=<file> name of the file storing the packets in pcap format
|
||||
> -i, --interface=<wlan|eth0|eth1|...> which interface to capture packet
|
||||
> -F, --filter=<mgmt|data|ctrl|misc|mpdu|ampdu> filter parameters
|
||||
> -F, --filter=<mgmt|data|ctrl|misc|mpdu|ampdu|fcsfail> filter parameters
|
||||
> -c, --channel=<channel> communication channel to use
|
||||
> --stop stop running sniffer
|
||||
> -n, --number=<num> the number of the packets to be captured
|
||||
|
||||
The `sniffer` command support some important options as follow:
|
||||
|
||||
* `-f`: Specify the name of file which 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 support `wlan` and `eth0`
|
||||
* `-c` :Specify the channel to sniffer packet at `wlan` interface
|
||||
* `-i`: Specify the interface to sniff packets, currently only support `wlan` and `eth0`
|
||||
* `-c`: Specify the channel to sniff packet at `wlan` interface
|
||||
* `-F`: Specify the filter condition at `wlan` interface, currently only support following filter conditions, you can select any number of them
|
||||
* mgmt: Management packets
|
||||
* data: Data packets
|
||||
@ -76,66 +74,239 @@ The `sniffer` command support some important options as follow:
|
||||
* misc: Other packets
|
||||
* mpdu: MPDU packets
|
||||
* ampdu: AMPDU packets
|
||||
* `-n`: Specify the number of packages to capture in this sniffer job. The sniffer job will stop automatically without using `sniffer --stop` command.
|
||||
* `--stop`: Stop sniffer job
|
||||
|
||||
### Mount SD Card
|
||||
### `pcap` Command Usage When the Destination is `SD Card`
|
||||
|
||||
> pcap -f <file> [--open] [--close] [--summary]
|
||||
> Save and parse pcap file
|
||||
> -f, --file=<file> name of the file storing the packets in pcap format
|
||||
> --open open .pcap file
|
||||
> --close close .pcap file
|
||||
> --summary option to parse and show the summary of .pcap file
|
||||
|
||||
The `pcap` command support some important options as follow:
|
||||
|
||||
* `-f`: Specify the name of file which will store the packets or show summary, default value is `sniffer`, and the resulting file name will be like “snifferX.pcap”, here ‘X’ shows the file’s order.
|
||||
* `--open`: Option to open a '.pcap' file
|
||||
* `--close`: Option to close the '.pcap' file
|
||||
* `--summary`: Show the summary of '.pcap' file
|
||||
|
||||
### `pcap` Command Usage When the Destination is `Memory`
|
||||
|
||||
> pcap -f <file> [--open] [--close] [--summary]
|
||||
> Save and parse pcap file
|
||||
> -f, --file=<file> name of the file storing the packets in pcap format
|
||||
> --open open .pcap file
|
||||
> --close close .pcap file
|
||||
> --summary option to parse and show the summary of .pcap file
|
||||
|
||||
The `pcap` command support some important options as follow:
|
||||
|
||||
* `-f`: Specify the file name to storage packet or show summary
|
||||
* `--open`: Option to open a '.pcap' file
|
||||
* `--close`: Option to close the '.pcap' file
|
||||
* `--summary`: Show the summary of '.pcap' file (needs to be called prior file closing)
|
||||
|
||||
### `pcap` Command Usage When the Destination is `JTAG`
|
||||
pcap command is not used when destination is JTAG. The pcap session is started automatically with the Sniffer start.
|
||||
|
||||
## Example Output
|
||||
### Steps for using **SD Card** to storage packages and watch summary
|
||||
#### Mount SD Card
|
||||
|
||||
```bash
|
||||
=======================================================
|
||||
| Steps to sniffer WiFi packets |
|
||||
| Steps to sniff network 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 |
|
||||
| 3. Enter 'pcap' to create pcap file |
|
||||
| 4. Enter 'sniffer' to start capture packets |
|
||||
| 5. Enter 'unmount <device>' to unmount filesystem |
|
||||
| |
|
||||
=======================================================
|
||||
|
||||
|
||||
Type 'help' to get the list of commands.
|
||||
Use UP/DOWN arrows to navigate through command history.
|
||||
Press TAB when typing command name to auto-complete.
|
||||
sniffer> 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
|
||||
I (12653) example: Initializing SD card
|
||||
I (12653) example: Using SDMMC peripheral
|
||||
I (12663) gpio: GPIO[13]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
Name: SC64G
|
||||
Type: SDHC/SDXC
|
||||
Speed: 20 MHz
|
||||
Size: 14832MB
|
||||
Size: 60906MB
|
||||
```
|
||||
|
||||
### Start Sniffer
|
||||
#### Create .pcap file
|
||||
|
||||
```bash
|
||||
sniffer> 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
|
||||
sniffer> 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
|
||||
sniffer> pcap --open -f simple-sniffer
|
||||
I (41383) cmd_pcap: open file successfully
|
||||
```
|
||||
|
||||
### Unmount SD Card
|
||||
#### Start Sniffer (with 10 packages)
|
||||
|
||||
```bash
|
||||
sniffer> sniffer -i wlan -c 2 -n 10
|
||||
I (58153) cmd_sniffer: 10 packages will be captured
|
||||
I (58163) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
|
||||
I (58263) wifi:ic_enable_sniffer
|
||||
I (58263) cmd_sniffer: start WiFi promiscuous ok
|
||||
I (58303) wifi:ic_disable_sniffer
|
||||
I (58303) wifi:flush txq
|
||||
I (58303) wifi:stop sw txq
|
||||
I (58303) wifi:lmac stop hw txq
|
||||
I (58303) cmd_sniffer: stop promiscuous ok
|
||||
```
|
||||
|
||||
#### Close .pcap file
|
||||
|
||||
```bash
|
||||
sniffer> pcap --close -f simple-sniffer
|
||||
I (80453) cmd_pcap: .pcap file close done
|
||||
```
|
||||
|
||||
#### Parse '.pcap' file and watch at bash with '--summary' option
|
||||
|
||||
```bash
|
||||
sniffer> pcap --summary -f simple-sniffer
|
||||
I (112833) cmd_pcap: /sdcard/simple-sniffer.pcap is to be parsed
|
||||
------------------------------------------------------------------------
|
||||
Pcap packet Head:
|
||||
------------------------------------------------------------------------
|
||||
Magic Number: a1b2c3d4
|
||||
Major Version: 2
|
||||
Minor Version: 4
|
||||
SnapLen: 262144
|
||||
LinkType: 105
|
||||
------------------------------------------------------------------------
|
||||
Packet 0:
|
||||
Timestamp (Seconds): 0
|
||||
Timestamp (Microseconds): 3670
|
||||
Capture Length: 303
|
||||
Packet Length: 303
|
||||
Packet Type: 0
|
||||
Packet Subtype: 5
|
||||
Destination: 0 0 0 0 a1 0
|
||||
Source: 2 84 56 e 0 0
|
||||
------------------------------------------------------------------------
|
||||
Packet 1:
|
||||
Timestamp (Seconds): 0
|
||||
Timestamp (Microseconds): 3670
|
||||
Capture Length: 294
|
||||
Packet Length: 294
|
||||
Packet Type: 0
|
||||
Packet Subtype: 5
|
||||
Destination: 0 0 0 0 a1 0
|
||||
Source: 2 84 56 e 0 0
|
||||
------------------------------------------------------------------------
|
||||
Packet 2:
|
||||
|
||||
...
|
||||
|
||||
------------------------------------------------------------------------
|
||||
Pcap packet Number: 10
|
||||
------------------------------------------------------------------------
|
||||
```
|
||||
|
||||
#### Unmount SD Card
|
||||
|
||||
```bash
|
||||
sniffer> unmount sd
|
||||
I (248800) example: Card unmounted
|
||||
I (183873) example: Card unmounted
|
||||
```
|
||||
|
||||
### Steps for sending packets to host via JTAG interface
|
||||
1. Select `JTAG (App Trace)` as the destination of pcap files.
|
||||
### Steps for using **memory** to storage packages and watch summary
|
||||
#### Open a memory for pcap
|
||||
|
||||
```bash
|
||||
sniffer> pcap --open -f simple-sniffer
|
||||
I (11816) cmd_pcap: open file successfully
|
||||
```
|
||||
|
||||
#### Sniff 10 packages
|
||||
|
||||
```bash
|
||||
sniffer> sniffer -i wlan -c 2 -n 10
|
||||
I (71086) cmd_sniffer: 10 packages will be captured
|
||||
I (71096) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
|
||||
I (71186) wifi:ic_enable_sniffer
|
||||
I (71186) cmd_sniffer: start WiFi promiscuous ok
|
||||
I (71246) wifi:ic_disable_sniffer
|
||||
I (71246) wifi:flush txq
|
||||
I (71256) wifi:stop sw txq
|
||||
I (71256) wifi:lmac stop hw txq
|
||||
I (71256) cmd_sniffer: stop promiscuous ok
|
||||
```
|
||||
|
||||
#### Watch the summary of the package captured above
|
||||
|
||||
```bash
|
||||
sniffer> pcap --summary -f simple-sniffer
|
||||
I (93396) cmd_pcap: Memory is to be parsed
|
||||
------------------------------------------------------------------------
|
||||
Pcap packet Head:
|
||||
------------------------------------------------------------------------
|
||||
Magic Number: a1b2c3d4
|
||||
Major Version: 2
|
||||
Minor Version: 4
|
||||
SnapLen: 262144
|
||||
LinkType: 105
|
||||
------------------------------------------------------------------------
|
||||
Packet 0:
|
||||
Timestamp (Seconds): 0
|
||||
Timestamp (Microseconds): 5481
|
||||
Capture Length: 266
|
||||
Packet Length: 266
|
||||
Packet Type: 0
|
||||
Packet Subtype: 2
|
||||
Destination: 0 0 0 0 a1 0
|
||||
Source: 2 8a 69 15 0 0
|
||||
------------------------------------------------------------------------
|
||||
Packet 1:
|
||||
Timestamp (Seconds): 0
|
||||
Timestamp (Microseconds): 24405
|
||||
Capture Length: 175
|
||||
Packet Length: 175
|
||||
Packet Type: 0
|
||||
Packet Subtype: f
|
||||
Destination: 0 0 0 0 a1 0
|
||||
Source: 2 84 55 5f 0 0
|
||||
------------------------------------------------------------------------
|
||||
Packet 2:
|
||||
|
||||
...
|
||||
|
||||
------------------------------------------------------------------------
|
||||
Pcap packet Number: 10
|
||||
------------------------------------------------------------------------
|
||||
```
|
||||
|
||||
|
||||
#### Close pcap file in memory
|
||||
|
||||
```bash
|
||||
sniffer> pcap --close -f simple-sniffer
|
||||
I (130566) cmd_pcap: free memory successfully
|
||||
I (130566) cmd_pcap: .pcap file close done
|
||||
```
|
||||
|
||||
### Steps for sending packets to host via **JTAG interface**
|
||||
|
||||
1. Select `JTAG (App Trace)` as the destination of pcap files in project configuration.
|
||||
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).
|
||||
6. Run the example, start sniffer with `sniffer` command.
|
||||
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)
|
||||
|
50
examples/network/simple_sniffer/example_test.py
Normal file
50
examples/network/simple_sniffer/example_test.py
Normal file
@ -0,0 +1,50 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI_Protocols')
|
||||
def test_examples_simple_sniffer(env, _): # type: (Any, Any) -> None
|
||||
|
||||
dut = env.get_dut('simple_sniffer', 'examples/network/simple_sniffer', app_config_name='mem')
|
||||
dut.start_app()
|
||||
|
||||
dut.expect('sniffer>')
|
||||
dut.write('pcap --open -f simple-sniffer')
|
||||
dut.expect('cmd_pcap: open file successfully')
|
||||
dut.write('sniffer -i wlan -c 2 -n 10')
|
||||
dut.expect('cmd_sniffer: 10 packages will be captured')
|
||||
dut.expect('cmd_sniffer: start WiFi promiscuous ok')
|
||||
dut.expect('cmd_sniffer: stop promiscuous ok')
|
||||
dut.write('pcap --summary -f simple-sniffer')
|
||||
dut.expect('cmd_pcap: Memory is to be parsed')
|
||||
dut.expect('Pcap packet Head:')
|
||||
dut.expect('Magic Number: a1b2c3d4')
|
||||
dut.expect(re.compile(r'Major Version: [0-9]*'))
|
||||
dut.expect(re.compile(r'Minor Version: [0-9]*'))
|
||||
dut.expect(re.compile(r'SnapLen: [0-9]*'))
|
||||
dut.expect(re.compile(r'LinkType: [0-9]*'))
|
||||
for i in range(0, 10):
|
||||
dut.expect('Packet ' + str(i) + ':')
|
||||
dut.expect(re.compile(r'Timestamp \(Seconds\): [0-9]*'))
|
||||
dut.expect(re.compile(r'Timestamp \(Microseconds\): [0-9]*'))
|
||||
dut.expect(re.compile(r'Capture Length: [0-9]*'))
|
||||
dut.expect(re.compile(r'Packet Length: [0-9]*'))
|
||||
dut.expect(re.compile(r'Packet Type: .*'))
|
||||
dut.expect(re.compile(r'Packet Subtype: .*'))
|
||||
dut.expect(re.compile(r'Destination: .*'))
|
||||
dut.expect(re.compile(r'Source: .*'))
|
||||
dut.expect('Pcap packet Number: 10')
|
||||
dut.write('pcap --close -f simple-sniffer')
|
||||
dut.expect('cmd_pcap: free memory successfully')
|
||||
dut.expect('cmd_pcap: .pcap file close done')
|
||||
|
||||
dut.write('')
|
||||
dut.expect('sniffer>')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_simple_sniffer()
|
@ -1,3 +1,4 @@
|
||||
idf_component_register(SRCS "simple_sniffer_example_main.c"
|
||||
"cmd_sniffer.c"
|
||||
"cmd_pcap.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
@ -22,6 +22,10 @@ menu "Example Configuration"
|
||||
depends on APPTRACE_DEST_TRAX
|
||||
help
|
||||
Store pcap file to host via JTAG interface.
|
||||
config SNIFFER_PCAP_DESTINATION_MEMORY
|
||||
bool "Memory"
|
||||
help
|
||||
Store pcap file to memory.
|
||||
endchoice
|
||||
|
||||
if SNIFFER_PCAP_DESTINATION_SD
|
||||
@ -53,6 +57,14 @@ menu "Example Configuration"
|
||||
Specify maximum name length of pcap file.
|
||||
endif
|
||||
|
||||
if SNIFFER_PCAP_DESTINATION_MEMORY
|
||||
config SNIFFER_PCAP_MEMORY_SIZE
|
||||
int "Memory size of the '.pcap' file in memory"
|
||||
default 4096
|
||||
help
|
||||
Max memory size to storage packet in memory.
|
||||
endif
|
||||
|
||||
config SNIFFER_WORK_QUEUE_LEN
|
||||
int "Length of sniffer work queue"
|
||||
default 128
|
||||
|
300
examples/network/simple_sniffer/main/cmd_pcap.c
Normal file
300
examples/network/simple_sniffer/main/cmd_pcap.c
Normal file
@ -0,0 +1,300 @@
|
||||
/* cmd_pcap 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 <stdlib.h>
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#ifdef CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
#include "freertos/timers.h"
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
#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 "esp_check.h"
|
||||
#include "cmd_sniffer.h"
|
||||
#include "cmd_pcap.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *CMD_PCAP_TAG = "cmd_pcap";
|
||||
|
||||
|
||||
#define PCAP_FILE_NAME_MAX_LEN CONFIG_SNIFFER_PCAP_FILE_NAME_MAX_LEN
|
||||
#define PCAP_MEMORY_BUFFER_SIZE CONFIG_SNIFFER_PCAP_MEMORY_SIZE
|
||||
#define SNIFFER_PROCESS_APPTRACE_TIMEOUT_US (100)
|
||||
#define SNIFFER_APPTRACE_RETRY (10)
|
||||
#define TRACE_TIMER_FLUSH_INT_MS (1000)
|
||||
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_MEMORY
|
||||
/**
|
||||
* @brief Pcap memory buffer Type Definition
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
char *buffer;
|
||||
uint32_t buffer_size;
|
||||
} pcap_memory_buffer_t;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
bool is_opened;
|
||||
bool is_writing;
|
||||
bool link_type_set;
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
char filename[PCAP_FILE_NAME_MAX_LEN];
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
pcap_file_handle_t pcap_handle;
|
||||
pcap_link_type_t link_type;
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_MEMORY
|
||||
pcap_memory_buffer_t pcap_buffer;
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_MEMORY
|
||||
#ifdef CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
TimerHandle_t trace_flush_timer; /*!< Timer handle for Trace buffer flush */
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
} pcap_cmd_runtime_t;
|
||||
|
||||
static pcap_cmd_runtime_t pcap_cmd_rt = {0};
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
void pcap_flush_apptrace_timer_cb(TimerHandle_t pxTimer)
|
||||
{
|
||||
esp_apptrace_flush(ESP_APPTRACE_DEST_TRAX, pdMS_TO_TICKS(10));
|
||||
}
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
|
||||
static esp_err_t pcap_close(pcap_cmd_runtime_t *pcap)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(pcap->is_opened, ESP_ERR_INVALID_STATE, err, CMD_PCAP_TAG, ".pcap file is already closed");
|
||||
ESP_GOTO_ON_ERROR(pcap_del_session(pcap->pcap_handle) != ESP_OK, err, CMD_PCAP_TAG, "stop pcap session failed");
|
||||
pcap->is_opened = false;
|
||||
pcap->link_type_set = false;
|
||||
pcap->pcap_handle = NULL;
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_MEMORY
|
||||
free(pcap->pcap_buffer.buffer);
|
||||
pcap->pcap_buffer.buffer_size = 0;
|
||||
ESP_LOGI(CMD_PCAP_TAG, "free memory successfully");
|
||||
#endif
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
if (pcap->trace_flush_timer != NULL) {
|
||||
xTimerDelete(pcap->trace_flush_timer, pdMS_TO_TICKS(100));
|
||||
pcap->trace_flush_timer = NULL;
|
||||
}
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t pcap_open(pcap_cmd_runtime_t *pcap)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
/* Create file to write, binary format */
|
||||
FILE *fp = NULL;
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
fp = funopen("trace", NULL, trace_writefun, NULL, trace_closefun);
|
||||
#elif CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
fp = fopen(pcap->filename, "wb+");
|
||||
#elif CONFIG_SNIFFER_PCAP_DESTINATION_MEMORY
|
||||
pcap->pcap_buffer.buffer = calloc(PCAP_MEMORY_BUFFER_SIZE, sizeof(char));
|
||||
ESP_GOTO_ON_FALSE(pcap->pcap_buffer.buffer, ESP_ERR_NO_MEM, err, CMD_PCAP_TAG, "pcap buffer calloc failed");
|
||||
fp = fmemopen(pcap->pcap_buffer.buffer, PCAP_MEMORY_BUFFER_SIZE, "wb+");
|
||||
#else
|
||||
#error "pcap file destination hasn't specified"
|
||||
#endif
|
||||
ESP_GOTO_ON_FALSE(fp, ESP_FAIL, err, CMD_PCAP_TAG, "open file failed");
|
||||
pcap_config_t pcap_config = {
|
||||
.fp = fp,
|
||||
.major_version = PCAP_DEFAULT_VERSION_MAJOR,
|
||||
.minor_version = PCAP_DEFAULT_VERSION_MINOR,
|
||||
.time_zone = PCAP_DEFAULT_TIME_ZONE_GMT,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(pcap_new_session(&pcap_config, &pcap_cmd_rt.pcap_handle), err, CMD_PCAP_TAG, "pcap init failed");
|
||||
pcap->is_opened = true;
|
||||
ESP_LOGI(CMD_PCAP_TAG, "open file successfully");
|
||||
return ret;
|
||||
err:
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t packet_capture(void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds)
|
||||
{
|
||||
return pcap_capture_packet(pcap_cmd_rt.pcap_handle, payload, length, seconds, microseconds);
|
||||
}
|
||||
|
||||
esp_err_t sniff_packet_start(pcap_link_type_t link_type)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
#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(CMD_PCAP_TAG, "waiting for apptrace established");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(retry < SNIFFER_APPTRACE_RETRY, ESP_ERR_TIMEOUT, err, CMD_PCAP_TAG, "waiting for apptrace established timeout");
|
||||
|
||||
pcap_open(&pcap_cmd_rt);
|
||||
#endif //CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
|
||||
ESP_GOTO_ON_FALSE(pcap_cmd_rt.is_opened, ESP_ERR_INVALID_STATE, err, CMD_PCAP_TAG, "no .pcap file stream is open");
|
||||
if (pcap_cmd_rt.link_type_set) {
|
||||
ESP_GOTO_ON_FALSE(link_type == pcap_cmd_rt.link_type, ESP_ERR_INVALID_STATE, err, CMD_PCAP_TAG, "link type error");
|
||||
ESP_GOTO_ON_FALSE(!pcap_cmd_rt.is_writing, ESP_ERR_INVALID_STATE, err, CMD_PCAP_TAG, "still sniffing");
|
||||
} else {
|
||||
pcap_cmd_rt.link_type = link_type;
|
||||
/* Create file to write, binary format */
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
/* Ethernet Link layer traffic amount may be much less than on Wi-Fi (no link management msgs.) and trace data is sent to listener
|
||||
only after filling trace buffer. Hence the trace buffer might not be filled prior listener's timeout. This condition is resolved by
|
||||
flushing the trace buffer periodically. */
|
||||
if (link_type == PCAP_LINK_TYPE_ETHERNET) {
|
||||
int timer_id = 0xFEED;
|
||||
pcap_cmd_rt.trace_flush_timer = xTimerCreate("flush_apptrace_timer",
|
||||
pdMS_TO_TICKS(TRACE_TIMER_FLUSH_INT_MS),
|
||||
pdTRUE, (void *) timer_id,
|
||||
pcap_flush_apptrace_timer_cb);
|
||||
ESP_GOTO_ON_FALSE(pcap_cmd_rt.trace_flush_timer, ESP_FAIL, err, CMD_PCAP_TAG, "pcap xTimerCreate failed");
|
||||
ESP_GOTO_ON_FALSE(xTimerStart(pcap_cmd_rt.trace_flush_timer, 0), ESP_FAIL, err_timer_start, CMD_PCAP_TAG, "pcap xTimerStart failed");
|
||||
}
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
pcap_write_header(pcap_cmd_rt.pcap_handle, link_type);
|
||||
pcap_cmd_rt.link_type_set = true;
|
||||
}
|
||||
pcap_cmd_rt.is_writing = true;
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
err_timer_start:
|
||||
xTimerDelete(pcap_cmd_rt.trace_flush_timer, pdMS_TO_TICKS(100));
|
||||
pcap_cmd_rt.trace_flush_timer = NULL;
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t sniff_packet_stop(void)
|
||||
{
|
||||
pcap_cmd_rt.is_writing = false;
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
pcap_close(&pcap_cmd_rt);
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if !CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
static struct {
|
||||
struct arg_str *file;
|
||||
struct arg_lit *open;
|
||||
struct arg_lit *close;
|
||||
struct arg_lit *summary;
|
||||
struct arg_end *end;
|
||||
} pcap_args;
|
||||
|
||||
static int do_pcap_cmd(int argc, char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
int nerrors = arg_parse(argc, argv, (void **)&pcap_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, pcap_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check whether or not to close pcap file: "--close" option */
|
||||
if (pcap_args.close->count) {
|
||||
/* close the pcap file */
|
||||
ESP_GOTO_ON_FALSE(!(pcap_cmd_rt.is_writing), ESP_FAIL, err, CMD_PCAP_TAG, "still sniffing, file will not close");
|
||||
pcap_close(&pcap_cmd_rt);
|
||||
ESP_LOGI(CMD_PCAP_TAG, ".pcap file close done");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
/* set pcap file name: "-f" option */
|
||||
int len = snprintf(pcap_cmd_rt.filename, sizeof(pcap_cmd_rt.filename), "%s/%s.pcap", CONFIG_SNIFFER_MOUNT_POINT, pcap_args.file->sval[0]);
|
||||
if (len >= sizeof(pcap_cmd_rt.filename)) {
|
||||
ESP_LOGW(CMD_PCAP_TAG, "pcap file name too long, try to enlarge memory in menuconfig");
|
||||
}
|
||||
|
||||
/* Check if needs to be parsed and shown: "--summary" option */
|
||||
if (pcap_args.summary->count) {
|
||||
ESP_LOGI(CMD_PCAP_TAG, "%s is to be parsed", pcap_cmd_rt.filename);
|
||||
if (pcap_cmd_rt.is_opened) {
|
||||
ESP_GOTO_ON_FALSE(!(pcap_cmd_rt.is_writing), ESP_FAIL, err, CMD_PCAP_TAG, "still writing");
|
||||
ESP_GOTO_ON_ERROR(pcap_print_summary(pcap_cmd_rt.pcap_handle, stdout), err, CMD_PCAP_TAG, "pcap print summary failed");
|
||||
} else {
|
||||
FILE *fp;
|
||||
fp = fopen(pcap_cmd_rt.filename, "rb");
|
||||
ESP_GOTO_ON_FALSE(fp, ESP_FAIL, err, CMD_PCAP_TAG, "open file failed");
|
||||
pcap_config_t pcap_config = {
|
||||
.fp = fp,
|
||||
.major_version = PCAP_DEFAULT_VERSION_MAJOR,
|
||||
.minor_version = PCAP_DEFAULT_VERSION_MINOR,
|
||||
.time_zone = PCAP_DEFAULT_TIME_ZONE_GMT,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(pcap_new_session(&pcap_config, &pcap_cmd_rt.pcap_handle), err, CMD_PCAP_TAG, "pcap init failed");
|
||||
ESP_GOTO_ON_ERROR(pcap_print_summary(pcap_cmd_rt.pcap_handle, stdout), err, CMD_PCAP_TAG, "pcap print summary failed");
|
||||
ESP_GOTO_ON_ERROR(pcap_del_session(pcap_cmd_rt.pcap_handle), err, CMD_PCAP_TAG, "stop pcap session failed");
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_MEMORY
|
||||
/* Check if needs to be parsed and shown: "--summary" option */
|
||||
if (pcap_args.summary->count) {
|
||||
ESP_LOGI(CMD_PCAP_TAG, "Memory is to be parsed");
|
||||
ESP_GOTO_ON_ERROR(pcap_print_summary(pcap_cmd_rt.pcap_handle, stdout), err, CMD_PCAP_TAG, "pcap print summary failed");
|
||||
}
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_MEMORY
|
||||
|
||||
if (pcap_args.open->count) {
|
||||
pcap_open(&pcap_cmd_rt);
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
|
||||
void register_pcap_cmd(void)
|
||||
{
|
||||
#if !CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
pcap_args.summary = arg_lit0(NULL, "summary", "option to parse and show the summary of .pcap file");
|
||||
pcap_args.file = arg_str1("f", "file", "<file>",
|
||||
"name of the file storing the packets in pcap format");
|
||||
pcap_args.close = arg_lit0(NULL, "close", "close .pcap file");
|
||||
pcap_args.open = arg_lit0(NULL, "open", "open .pcap file");
|
||||
pcap_args.end = arg_end(1);
|
||||
const esp_console_cmd_t pcap_cmd = {
|
||||
.command = "pcap",
|
||||
.help = "Save and parse pcap file",
|
||||
.hint = NULL,
|
||||
.func = &do_pcap_cmd,
|
||||
.argtable = &pcap_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&pcap_cmd));
|
||||
#endif // #if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
}
|
57
examples/network/simple_sniffer/main/cmd_pcap.h
Normal file
57
examples/network/simple_sniffer/main/cmd_pcap.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* cmd_pcap 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
|
||||
|
||||
#include "pcap.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Capture a pcap package with parameters
|
||||
*
|
||||
* @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 packet_capture(void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds);
|
||||
|
||||
/**
|
||||
* @brief Tell the pcap component to start sniff and write
|
||||
*
|
||||
* @param link_type link type of the captured package
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t sniff_packet_start(pcap_link_type_t link_type);
|
||||
|
||||
/**
|
||||
* @brief Tell the pcap component to stop sniff
|
||||
*
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t sniff_packet_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Register pcap command
|
||||
*
|
||||
*/
|
||||
void register_pcap_cmd(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -6,14 +6,12 @@
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#ifdef CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
#include "freertos/timers.h"
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include "esp_log.h"
|
||||
@ -21,32 +19,19 @@
|
||||
#include "esp_console.h"
|
||||
#include "esp_app_trace.h"
|
||||
#include "cmd_sniffer.h"
|
||||
#include "pcap.h"
|
||||
#include "cmd_pcap.h"
|
||||
#include "esp_check.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)
|
||||
#define SNIFFER_RX_FCS_ERR (0X41)
|
||||
#define SNIFFER_MAX_ETH_INTFS (3)
|
||||
#define SNIFFER_DECIMAL_NUM (10)
|
||||
|
||||
#define TRACE_TIMER_FLUSH_INT_MS (1000)
|
||||
|
||||
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;
|
||||
@ -59,13 +44,7 @@ typedef struct {
|
||||
uint32_t interf_num;
|
||||
uint32_t channel;
|
||||
uint32_t filter;
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
char filename[SNIFFER_FILE_NAME_MAX_LEN];
|
||||
#endif
|
||||
#ifdef CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
TimerHandle_t trace_flush_timer; /*!< Timer handle for Trace buffer flush */
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
pcap_handle_t pcap;
|
||||
int32_t packets_to_sniff;
|
||||
TaskHandle_t task;
|
||||
QueueHandle_t work_queue;
|
||||
SemaphoreHandle_t sem_task_over;
|
||||
@ -81,6 +60,7 @@ typedef struct {
|
||||
|
||||
static sniffer_runtime_t snf_rt = {0};
|
||||
static wlan_filter_table_t wifi_filter_hash_table[SNIFFER_WLAN_FILTER_MAX] = {0};
|
||||
static esp_err_t sniffer_stop(sniffer_runtime_t *sniffer);
|
||||
|
||||
static uint32_t hash_func(const char *str, uint32_t max_num)
|
||||
{
|
||||
@ -193,41 +173,52 @@ static void sniffer_task(void *parameters)
|
||||
sniffer_runtime_t *sniffer = (sniffer_runtime_t *)parameters;
|
||||
|
||||
while (sniffer->is_running) {
|
||||
if (sniffer->packets_to_sniff == 0) {
|
||||
sniffer_stop(sniffer);
|
||||
break;
|
||||
}
|
||||
/* receive packet 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) {
|
||||
if (packet_capture(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);
|
||||
if (sniffer->packets_to_sniff > 0) {
|
||||
sniffer->packets_to_sniff--;
|
||||
}
|
||||
|
||||
}
|
||||
/* notify that sniffer task is over */
|
||||
xSemaphoreGive(sniffer->sem_task_over);
|
||||
if (sniffer->packets_to_sniff != 0) {
|
||||
xSemaphoreGive(sniffer->sem_task_over);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static esp_err_t sniffer_stop(sniffer_runtime_t *sniffer)
|
||||
{
|
||||
bool eth_set_promiscuous;
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
SNIFFER_CHECK(sniffer->is_running, "sniffer is already stopped", err);
|
||||
ESP_GOTO_ON_FALSE(sniffer->is_running, ESP_ERR_INVALID_STATE, err, SNIFFER_TAG, "sniffer is already stopped");
|
||||
|
||||
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);
|
||||
ESP_GOTO_ON_ERROR(esp_wifi_set_promiscuous(false), err, SNIFFER_TAG, "stop wifi promiscuous failed");
|
||||
break;
|
||||
case SNIFFER_INTF_ETH:
|
||||
/* Disable Ethernet Promiscuous Mode */
|
||||
eth_set_promiscuous = false;
|
||||
SNIFFER_CHECK(esp_eth_ioctl(sniffer->eth_handles[sniffer->interf_num], ETH_CMD_S_PROMISCUOUS, ð_set_promiscuous) == ESP_OK,
|
||||
"stop Ethernet promiscuous failed", err);
|
||||
ESP_GOTO_ON_ERROR(esp_eth_ioctl(sniffer->eth_handles[sniffer->interf_num], ETH_CMD_S_PROMISCUOUS, ð_set_promiscuous),
|
||||
err, SNIFFER_TAG, "stop Ethernet promiscuous failed");
|
||||
esp_eth_update_input_path(sniffer->eth_handles[sniffer->interf_num], NULL, NULL);
|
||||
break;
|
||||
default:
|
||||
SNIFFER_CHECK(false, "unsupported interface", err);
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, SNIFFER_TAG, "unsupported interface");
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(SNIFFER_TAG, "stop promiscuous ok");
|
||||
@ -235,11 +226,15 @@ static esp_err_t sniffer_stop(sniffer_runtime_t *sniffer)
|
||||
/* stop sniffer local task */
|
||||
sniffer->is_running = false;
|
||||
/* wait for task over */
|
||||
xSemaphoreTake(sniffer->sem_task_over, portMAX_DELAY);
|
||||
if (sniffer->packets_to_sniff != 0) {
|
||||
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));
|
||||
@ -248,91 +243,44 @@ static esp_err_t sniffer_stop(sniffer_runtime_t *sniffer)
|
||||
vQueueDelete(sniffer->work_queue);
|
||||
sniffer->work_queue = NULL;
|
||||
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
if (sniffer->trace_flush_timer != NULL) {
|
||||
xTimerDelete(sniffer->trace_flush_timer, pdMS_TO_TICKS(100));
|
||||
sniffer->trace_flush_timer = NULL;
|
||||
}
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
/* stop pcap session */
|
||||
SNIFFER_CHECK(pcap_deinit(sniffer->pcap) == ESP_OK, "stop pcap session failed", err);
|
||||
return ESP_OK;
|
||||
sniff_packet_stop();
|
||||
err:
|
||||
return ESP_FAIL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
void pcap_flush_apptrace_timer_cb(TimerHandle_t pxTimer)
|
||||
{
|
||||
esp_apptrace_flush(ESP_APPTRACE_DEST_TRAX, pdMS_TO_TICKS(10));
|
||||
}
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
|
||||
static esp_err_t sniffer_start(sniffer_runtime_t *sniffer)
|
||||
{
|
||||
pcap_config_t pcap_config;
|
||||
esp_err_t ret = ESP_OK;
|
||||
pcap_link_type_t link_type;
|
||||
wifi_promiscuous_filter_t wifi_filter;
|
||||
bool eth_set_promiscuous;
|
||||
|
||||
SNIFFER_CHECK(sniffer->is_running == false, "sniffer is already running", err);
|
||||
ESP_GOTO_ON_FALSE(!(sniffer->is_running), ESP_ERR_INVALID_STATE, err, SNIFFER_TAG, "sniffer is already running");
|
||||
|
||||
switch (sniffer->interf) {
|
||||
case SNIFFER_INTF_WLAN:
|
||||
pcap_config.link_type = PCAP_LINK_TYPE_802_11;
|
||||
link_type = PCAP_LINK_TYPE_802_11;
|
||||
break;
|
||||
case SNIFFER_INTF_ETH:
|
||||
pcap_config.link_type = PCAP_LINK_TYPE_ETHERNET;
|
||||
link_type = PCAP_LINK_TYPE_ETHERNET;
|
||||
break;
|
||||
default:
|
||||
SNIFFER_CHECK(false, "unsupported interface", err);
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, SNIFFER_TAG, "unsupported interface");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Create file to write, binary format */
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
pcap_config.fp = funopen("trace", NULL, trace_writefun, NULL, trace_closefun);
|
||||
|
||||
/* Ethernet Link layer traffic amount may be much less than on Wi-Fi (no link management msgs.) and trace data is sent to listener
|
||||
only after filling trace buffer. Hence the trace buffer might not be filled prior listener's timeout. This condition is resolved by
|
||||
flushing the trace buffer periodically. */
|
||||
if(pcap_config.link_type == PCAP_LINK_TYPE_ETHERNET) {
|
||||
int timer_id = 0xFEED;
|
||||
sniffer->trace_flush_timer = xTimerCreate("flush_apptrace_timer",
|
||||
pdMS_TO_TICKS(TRACE_TIMER_FLUSH_INT_MS),
|
||||
pdTRUE, (void *) timer_id,
|
||||
pcap_flush_apptrace_timer_cb);
|
||||
SNIFFER_CHECK(sniffer->trace_flush_timer, "pcap xTimerCreate failed", err);
|
||||
SNIFFER_CHECK(xTimerStart(sniffer->trace_flush_timer, 0) == pdPASS, "pcap xTimerStart failed", err_timer_start);
|
||||
}
|
||||
#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);
|
||||
ESP_GOTO_ON_ERROR(sniff_packet_start(link_type), err, SNIFFER_TAG, "init pcap session failed");
|
||||
|
||||
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);
|
||||
ESP_GOTO_ON_FALSE(sniffer->work_queue, ESP_FAIL, err_queue, SNIFFER_TAG, "create work queue failed");
|
||||
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);
|
||||
ESP_GOTO_ON_FALSE(sniffer->sem_task_over, ESP_FAIL, err_sem, SNIFFER_TAG, "create work queue failed");
|
||||
ESP_GOTO_ON_FALSE(xTaskCreate(sniffer_task, "snifferT", CONFIG_SNIFFER_TASK_STACK_SIZE,
|
||||
sniffer, CONFIG_SNIFFER_TASK_PRIORITY, &sniffer->task), ESP_FAIL,
|
||||
err_task, SNIFFER_TAG, "create task failed");
|
||||
|
||||
switch (sniffer->interf) {
|
||||
case SNIFFER_INTF_WLAN:
|
||||
@ -340,22 +288,22 @@ static esp_err_t sniffer_start(sniffer_runtime_t *sniffer)
|
||||
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_GOTO_ON_ERROR(esp_wifi_set_promiscuous(true), err_start, SNIFFER_TAG, "create work queue failed");
|
||||
esp_wifi_set_channel(sniffer->channel, WIFI_SECOND_CHAN_NONE);
|
||||
ESP_LOGI(SNIFFER_TAG, "start WiFi promiscuous ok");
|
||||
break;
|
||||
case SNIFFER_INTF_ETH:
|
||||
/* Start Ethernet Promiscuous Mode */
|
||||
eth_set_promiscuous = true;
|
||||
SNIFFER_CHECK(esp_eth_ioctl(sniffer->eth_handles[sniffer->interf_num], ETH_CMD_S_PROMISCUOUS, ð_set_promiscuous) == ESP_OK,
|
||||
"start Ethernet promiscuous failed", err_start);
|
||||
ESP_GOTO_ON_ERROR(esp_eth_ioctl(sniffer->eth_handles[sniffer->interf_num], ETH_CMD_S_PROMISCUOUS, ð_set_promiscuous),
|
||||
err_start, SNIFFER_TAG, "start Ethernet promiscuous failed");
|
||||
esp_eth_update_input_path(sniffer->eth_handles[sniffer->interf_num], eth_sniffer_cb, NULL);
|
||||
ESP_LOGI(SNIFFER_TAG, "start Ethernet promiscuous ok");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
return ret;
|
||||
err_start:
|
||||
vTaskDelete(sniffer->task);
|
||||
sniffer->task = NULL;
|
||||
@ -367,37 +315,31 @@ err_sem:
|
||||
sniffer->work_queue = NULL;
|
||||
err_queue:
|
||||
sniffer->is_running = false;
|
||||
pcap_deinit(sniffer->pcap);
|
||||
#ifdef CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
err_timer_start:
|
||||
xTimerDelete(sniffer->trace_flush_timer, pdMS_TO_TICKS(100));
|
||||
sniffer->trace_flush_timer = NULL;
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
err:
|
||||
return ESP_FAIL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct {
|
||||
struct arg_str *file;
|
||||
struct arg_str *interface;
|
||||
struct arg_str *filter;
|
||||
struct arg_int *channel;
|
||||
struct arg_lit *stop;
|
||||
struct arg_int *number;
|
||||
struct arg_end *end;
|
||||
} sniffer_args;
|
||||
|
||||
esp_err_t sniffer_reg_eth_intf(esp_eth_handle_t eth_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
int32_t i = 0;
|
||||
while ((snf_rt.eth_handles[i] != NULL) && (i < SNIFFER_MAX_ETH_INTFS)) {
|
||||
i++;
|
||||
}
|
||||
SNIFFER_CHECK(i < SNIFFER_MAX_ETH_INTFS, "maximum num. of eth interfaces registered", err);
|
||||
ESP_GOTO_ON_FALSE(i < SNIFFER_MAX_ETH_INTFS, ESP_FAIL, err, SNIFFER_TAG, "maximum num. of eth interfaces registered");
|
||||
snf_rt.eth_handles[i] = eth_handle;
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
return ESP_FAIL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_sniffer_cmd(int argc, char **argv)
|
||||
@ -421,7 +363,7 @@ static int do_sniffer_cmd(int argc, char **argv)
|
||||
if (!strncmp(sniffer_args.interface->sval[0], "wlan", 4)) {
|
||||
snf_rt.interf = SNIFFER_INTF_WLAN;
|
||||
} else if (!strncmp(sniffer_args.interface->sval[0], "eth", 3)
|
||||
&& strlen(sniffer_args.interface->sval[0]) >= 4) {
|
||||
&& strlen(sniffer_args.interface->sval[0]) >= 4) {
|
||||
char *end_ptr = NULL;
|
||||
const char *eth_num_str_start = sniffer_args.interface->sval[0] + 3;
|
||||
int32_t eth_intf_num = strtol(eth_num_str_start, &end_ptr, SNIFFER_DECIMAL_NUM);
|
||||
@ -438,8 +380,7 @@ static int do_sniffer_cmd(int argc, char **argv)
|
||||
ESP_LOGE(SNIFFER_TAG, "unsupported interface %s", sniffer_args.interface->sval[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
snf_rt.interf = SNIFFER_INTF_WLAN;
|
||||
ESP_LOGW(SNIFFER_TAG, "sniffing interface set to wlan by default");
|
||||
}
|
||||
@ -461,30 +402,6 @@ static int do_sniffer_cmd(int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
|
||||
#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:
|
||||
@ -509,16 +426,22 @@ static int do_sniffer_cmd(int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check the number of captured packages: "-n" option */
|
||||
snf_rt.packets_to_sniff = -1;
|
||||
if (sniffer_args.number->count) {
|
||||
snf_rt.packets_to_sniff = sniffer_args.number->ival[0];
|
||||
ESP_LOGI(SNIFFER_TAG, "%d packages will be captured", snf_rt.packets_to_sniff);
|
||||
}
|
||||
|
||||
/* start sniffer */
|
||||
sniffer_start(&snf_rt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_sniffer(void)
|
||||
void register_sniffer_cmd(void)
|
||||
{
|
||||
sniffer_args.file = arg_str0("f", "file", "<file>",
|
||||
"name of the file storing the packets in pcap format");
|
||||
sniffer_args.number = arg_int0("n", "number", "<num>",
|
||||
"the number of the packets to be captured");
|
||||
sniffer_args.interface = arg_str0("i", "interface", "<wlan|eth0|eth1|...>",
|
||||
"which interface to capture packet");
|
||||
sniffer_args.filter = arg_strn("F", "filter", "<mgmt|data|ctrl|misc|mpdu|ampdu|fcsfail>", 0, 7, "filter parameters");
|
||||
|
@ -37,7 +37,7 @@ typedef enum {
|
||||
SNIFFER_WLAN_FILTER_MAX
|
||||
} sniffer_wlan_filter_t;
|
||||
|
||||
void register_sniffer(void);
|
||||
void register_sniffer_cmd(void);
|
||||
esp_err_t sniffer_reg_eth_intf(esp_eth_handle_t eth_handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -25,8 +25,8 @@
|
||||
#endif
|
||||
#include "nvs_flash.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "cmd_system.h"
|
||||
#include "cmd_sniffer.h"
|
||||
#include "cmd_pcap.h"
|
||||
#if CONFIG_ETH_USE_SPI_ETHERNET
|
||||
#include "driver/spi_master.h"
|
||||
#endif // CONFIG_ETH_USE_SPI_ETHERNET
|
||||
@ -397,18 +397,29 @@ void app_main(void)
|
||||
register_mount();
|
||||
register_unmount();
|
||||
#endif
|
||||
register_sniffer();
|
||||
register_system();
|
||||
|
||||
register_sniffer_cmd();
|
||||
register_pcap_cmd();
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
printf("\n =======================================================\n");
|
||||
printf(" | Steps to sniffer WiFi packets |\n");
|
||||
printf(" | Steps to sniff network 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(" | 3. Enter 'pcap' to create pcap file |\n");
|
||||
printf(" | 4. Enter 'sniffer' to start capture packets |\n");
|
||||
printf(" | 5. Enter 'unmount <device>' to unmount filesystem |\n");
|
||||
printf(" | |\n");
|
||||
printf(" =======================================================\n\n");
|
||||
#else
|
||||
printf("\n =======================================================\n");
|
||||
printf(" | Steps to sniff network packets |\n");
|
||||
printf(" | |\n");
|
||||
printf(" | 1. Enter 'help' to check all commands' usage |\n");
|
||||
printf(" | 2. Enter 'pcap' to create pcap file |\n");
|
||||
printf(" | 3. Enter 'sniffer' to start capture packets |\n");
|
||||
printf(" | |\n");
|
||||
printf(" =======================================================\n\n");
|
||||
#endif
|
||||
|
||||
// start console REPL
|
||||
ESP_ERROR_CHECK(esp_console_start_repl(repl));
|
||||
|
1
examples/network/simple_sniffer/sdkconfig.ci.mem
Normal file
1
examples/network/simple_sniffer/sdkconfig.ci.mem
Normal file
@ -0,0 +1 @@
|
||||
CONFIG_SNIFFER_PCAP_DESTINATION_MEMORY=y
|
Loading…
Reference in New Issue
Block a user