]> granicus.if.org Git - esp-idf/commitdiff
example: add console command for wifi sniffer
authormorris <maoshengrong@espressif.com>
Thu, 27 Sep 2018 03:24:12 +0000 (11:24 +0800)
committermorris <maoshengrong@espressif.com>
Fri, 19 Oct 2018 03:51:56 +0000 (11:51 +0800)
1. add mount/unmount command
2. add sniffer command, supporting options
3. make pcap a component

17 files changed:
examples/wifi/simple_sniffer/CMakeLists.txt [new file with mode: 0644]
examples/wifi/simple_sniffer/Makefile [new file with mode: 0644]
examples/wifi/simple_sniffer/README.md [new file with mode: 0644]
examples/wifi/simple_sniffer/components/pcap/CMakeLists.txt [new file with mode: 0644]
examples/wifi/simple_sniffer/components/pcap/component.mk [new file with mode: 0644]
examples/wifi/simple_sniffer/components/pcap/pcap.c [new file with mode: 0644]
examples/wifi/simple_sniffer/components/pcap/pcap.h [new file with mode: 0644]
examples/wifi/simple_sniffer/main/CMakeLists.txt [new file with mode: 0644]
examples/wifi/simple_sniffer/main/Kconfig.projbuild [new file with mode: 0644]
examples/wifi/simple_sniffer/main/cmd_decl.h [new file with mode: 0644]
examples/wifi/simple_sniffer/main/cmd_sniffer.c [new file with mode: 0644]
examples/wifi/simple_sniffer/main/cmd_sniffer.h [new file with mode: 0644]
examples/wifi/simple_sniffer/main/component.mk [new file with mode: 0644]
examples/wifi/simple_sniffer/main/simple_sniffer_example_main.c [new file with mode: 0644]
examples/wifi/simple_sniffer/partitions_example.csv [new file with mode: 0644]
examples/wifi/simple_sniffer/sdkconfig.defaults [new file with mode: 0644]
examples/wifi/simple_sniffer/sniffer-example0-pcap.png [new file with mode: 0644]

diff --git a/examples/wifi/simple_sniffer/CMakeLists.txt b/examples/wifi/simple_sniffer/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2ec8c4d
--- /dev/null
@@ -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)
diff --git a/examples/wifi/simple_sniffer/Makefile b/examples/wifi/simple_sniffer/Makefile
new file mode 100644 (file)
index 0000000..13aa6f8
--- /dev/null
@@ -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
+
diff --git a/examples/wifi/simple_sniffer/README.md b/examples/wifi/simple_sniffer/README.md
new file mode 100644 (file)
index 0000000..e1385bc
--- /dev/null
@@ -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.)
\ No newline at end of file
diff --git a/examples/wifi/simple_sniffer/components/pcap/CMakeLists.txt b/examples/wifi/simple_sniffer/components/pcap/CMakeLists.txt
new file mode 100644 (file)
index 0000000..15f5b5d
--- /dev/null
@@ -0,0 +1,5 @@
+set(COMPONENT_ADD_INCLUDEDIRS .)
+
+set(COMPONENT_SRCS "pcap.c")
+
+register_component()
diff --git a/examples/wifi/simple_sniffer/components/pcap/component.mk b/examples/wifi/simple_sniffer/components/pcap/component.mk
new file mode 100644 (file)
index 0000000..da115f7
--- /dev/null
@@ -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 := .
diff --git a/examples/wifi/simple_sniffer/components/pcap/pcap.c b/examples/wifi/simple_sniffer/components/pcap/pcap.c
new file mode 100644 (file)
index 0000000..4032f2d
--- /dev/null
@@ -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;
+}
diff --git a/examples/wifi/simple_sniffer/components/pcap/pcap.h b/examples/wifi/simple_sniffer/components/pcap/pcap.h
new file mode 100644 (file)
index 0000000..ef2dfc7
--- /dev/null
@@ -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
diff --git a/examples/wifi/simple_sniffer/main/CMakeLists.txt b/examples/wifi/simple_sniffer/main/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8e95d42
--- /dev/null
@@ -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()
diff --git a/examples/wifi/simple_sniffer/main/Kconfig.projbuild b/examples/wifi/simple_sniffer/main/Kconfig.projbuild
new file mode 100644 (file)
index 0000000..61d2276
--- /dev/null
@@ -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
diff --git a/examples/wifi/simple_sniffer/main/cmd_decl.h b/examples/wifi/simple_sniffer/main/cmd_decl.h
new file mode 100644 (file)
index 0000000..908f6f0
--- /dev/null
@@ -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
diff --git a/examples/wifi/simple_sniffer/main/cmd_sniffer.c b/examples/wifi/simple_sniffer/main/cmd_sniffer.c
new file mode 100644 (file)
index 0000000..fb5aea6
--- /dev/null
@@ -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();
+}
diff --git a/examples/wifi/simple_sniffer/main/cmd_sniffer.h b/examples/wifi/simple_sniffer/main/cmd_sniffer.h
new file mode 100644 (file)
index 0000000..bf04638
--- /dev/null
@@ -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
diff --git a/examples/wifi/simple_sniffer/main/component.mk b/examples/wifi/simple_sniffer/main/component.mk
new file mode 100644 (file)
index 0000000..a98f634
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
diff --git a/examples/wifi/simple_sniffer/main/simple_sniffer_example_main.c b/examples/wifi/simple_sniffer/main/simple_sniffer_example_main.c
new file mode 100644 (file)
index 0000000..4048590
--- /dev/null
@@ -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);
+    }
+}
diff --git a/examples/wifi/simple_sniffer/partitions_example.csv b/examples/wifi/simple_sniffer/partitions_example.csv
new file mode 100644 (file)
index 0000000..7e28b56
--- /dev/null
@@ -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, 
diff --git a/examples/wifi/simple_sniffer/sdkconfig.defaults b/examples/wifi/simple_sniffer/sdkconfig.defaults
new file mode 100644 (file)
index 0000000..23fd7b8
--- /dev/null
@@ -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
+
diff --git a/examples/wifi/simple_sniffer/sniffer-example0-pcap.png b/examples/wifi/simple_sniffer/sniffer-example0-pcap.png
new file mode 100644 (file)
index 0000000..acff3c5
Binary files /dev/null and b/examples/wifi/simple_sniffer/sniffer-example0-pcap.png differ