From f04f078825a65f8370f7d4b7c3aa5ea7fd630f10 Mon Sep 17 00:00:00 2001 From: morris Date: Wed, 25 Aug 2021 20:25:38 +0800 Subject: [PATCH 1/2] pcap: refactor and move to common components --- .../common_components/pcap/CMakeLists.txt | 2 + examples/common_components/pcap/component.mk | 5 + .../common_components/pcap/include/pcap.h | 128 ++++++++++ examples/common_components/pcap/src/pcap.c | 224 ++++++++++++++++++ .../components/pcap/CMakeLists.txt | 2 - .../components/pcap/component.mk | 11 - .../simple_sniffer/components/pcap/pcap.c | 141 ----------- .../simple_sniffer/components/pcap/pcap.h | 105 -------- 8 files changed, 359 insertions(+), 259 deletions(-) create mode 100644 examples/common_components/pcap/CMakeLists.txt create mode 100644 examples/common_components/pcap/component.mk create mode 100644 examples/common_components/pcap/include/pcap.h create mode 100644 examples/common_components/pcap/src/pcap.c delete mode 100644 examples/network/simple_sniffer/components/pcap/CMakeLists.txt delete mode 100644 examples/network/simple_sniffer/components/pcap/component.mk delete mode 100644 examples/network/simple_sniffer/components/pcap/pcap.c delete mode 100644 examples/network/simple_sniffer/components/pcap/pcap.h diff --git a/examples/common_components/pcap/CMakeLists.txt b/examples/common_components/pcap/CMakeLists.txt new file mode 100644 index 0000000000..21b6fac3a0 --- /dev/null +++ b/examples/common_components/pcap/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "src/pcap.c" + INCLUDE_DIRS "include") diff --git a/examples/common_components/pcap/component.mk b/examples/common_components/pcap/component.mk new file mode 100644 index 0000000000..d1c2af0183 --- /dev/null +++ b/examples/common_components/pcap/component.mk @@ -0,0 +1,5 @@ +# +# Component Makefile +# +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_SRCDIRS := src diff --git a/examples/common_components/pcap/include/pcap.h b/examples/common_components/pcap/include/pcap.h new file mode 100644 index 0000000000..2bb9d5f775 --- /dev/null +++ b/examples/common_components/pcap/include/pcap.h @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PCAP_DEFAULT_VERSION_MAJOR 0x02 /*!< Major Version */ +#define PCAP_DEFAULT_VERSION_MINOR 0x04 /*!< Minor Version */ +#define PCAP_DEFAULT_TIME_ZONE_GMT 0x00 /*!< Time Zone */ + +/** + * @brief Type of pcap file handle + * + */ +typedef struct pcap_file_t *pcap_file_handle_t; + +/** +* @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 */ + unsigned int major_version; /*!< Pcap version: major */ + unsigned int minor_version; /*!< Pcap version: minor */ + unsigned int time_zone; /*!< Pcap timezone code */ + struct { + unsigned int little_endian: 1; /*!< Whether the pcap file is recored in little endian format */ + } flags; +} pcap_config_t; + +/** + * @brief Create a new pcap session, and returns pcap file handle + * + * @note This function won't create the low level FILE* object, the user should take care of the creation of the File Stream. + * + * @param[in] config pcap file configuration + * @param[out] ret_pcap Returned pcap file handle + * @return + * - ESP_OK: Create pcap file successfully + * - ESP_ERR_INVALID_ARG: Create pcap file failed because of invalid argument + * - ESP_ERR_NO_MEM: Create pcap file failed because out of memory + * - ESP_FAIL: Create pcap file failed + */ +esp_err_t pcap_new_session(const pcap_config_t *config, pcap_file_handle_t *ret_pcap); + +/** + * @brief Delete the pcap session, and close the File Stream + * + * @param[in] pcap pcap file handle created by `pcap_new_session()` + * @return + * - ESP_OK: Delete pcap session successfully + * - ESP_ERR_INVALID_ARG: Delete pcap session failed because of invalid argument + * - ESP_FAIL: Delete pcap session failed + */ +esp_err_t pcap_del_session(pcap_file_handle_t pcap); + +/** + * @brief Write pcap file header + * + * @param[in] pcap pcap file handle created by `pcap_new_session()` + * @param[in] link_type Network link layer type + * @return + * - ESP_OK: Write pcap file header successfully + * - ESP_ERR_INVALID_ARG: Write pcap file header failed because of invalid argument + * - ESP_FAIL: Write pcap file header failed + */ +esp_err_t pcap_write_header(pcap_file_handle_t pcap, pcap_link_type_t link_type); + +/** + * @brief Capture one packet into pcap file + * + * @param[in] pcap pcap file handle created by `pcap_new_session()` + * @param[in] payload pointer of the captured data buffer + * @param[in] length length of captured data buffer + * @param[in] seconds second of capture time + * @param[in] microseconds microsecond of capture time + * @return + * - ESP_OK: Write network packet into pcap file successfully + * - ESP_ERR_INVALID_ARG: Write network packet into pcap file failed because of invalid argument + * - ESP_FAIL: Write network packet into pcap file failed + */ +esp_err_t pcap_capture_packet(pcap_file_handle_t pcap, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds); + +/** + * @brief Print the summary of pcap file into stream + * + * @param[in] pcap pcap file handle created by `pcap_new_session()` + * @param[in] print_file the file stream to save the summary + * @return + * - ESP_OK: Print pcap file summary successfully + * - ESP_ERR_INVALID_ARG: Print pcap file summary failed because of invalid argument + * - ESP_FAIL: Print pcap file summary failed + */ +esp_err_t pcap_print_summary(pcap_file_handle_t pcap, FILE *print_file); + +#ifdef __cplusplus +} +#endif diff --git a/examples/common_components/pcap/src/pcap.c b/examples/common_components/pcap/src/pcap.c new file mode 100644 index 0000000000..ecf02f4ffd --- /dev/null +++ b/examples/common_components/pcap/src/pcap.c @@ -0,0 +1,224 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "esp_log.h" +#include "esp_check.h" +#include "pcap.h" + +static const char *TAG = "pcap"; + +#define PCAP_MAGIC_BIG_ENDIAN 0xA1B2C3D4 /*!< Big-Endian */ +#define PCAP_MAGIC_LITTLE_ENDIAN 0xD4C3B2A1 /*!< Little-Endian */ + +typedef struct pcap_file_t pcap_file_t; + +/** + * @brief Pcap File Header + * + */ +typedef struct { + uint32_t magic; /*!< Magic Number */ + uint16_t major; /*!< Major Version */ + uint16_t minor; /*!< Minor Version */ + uint32_t zone; /*!< Time Zone Offset */ + uint32_t sigfigs; /*!< Timestamp Accuracy */ + uint32_t snaplen; /*!< Max Length to Capture */ + uint32_t link_type; /*!< Link Layer Type */ +} pcap_file_header_t; + +/** + * @brief Pcap Packet Header + * + */ +typedef struct { + uint32_t seconds; /*!< Number of seconds since January 1st, 1970, 00:00:00 GMT */ + uint32_t microseconds; /*!< Number of microseconds when the packet was captured (offset from seconds) */ + uint32_t capture_length; /*!< Number of bytes of captured data, no longer than packet_length */ + uint32_t packet_length; /*!< Actual length of current packet */ +} pcap_packet_header_t; + +/** + * @brief Pcap Runtime Handle + * + */ +struct pcap_file_t { + FILE *file; /*!< File handle */ + pcap_link_type_t link_type; /*!< Pcap Link Type */ + unsigned int major_version; /*!< Pcap version: major */ + unsigned int minor_version; /*!< Pcap version: minor */ + unsigned int time_zone; /*!< Pcap timezone code */ + uint32_t endian_magic; /*!< Magic value related to endian format */ +}; + +esp_err_t pcap_new_session(const pcap_config_t *config, pcap_file_handle_t *ret_pcap) +{ + esp_err_t ret = ESP_OK; + pcap_file_t *pcap = NULL; + ESP_GOTO_ON_FALSE(config && ret_pcap, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + ESP_GOTO_ON_FALSE(config->fp, ESP_ERR_INVALID_ARG, err, TAG, "pcap file handle can't be NULL"); + pcap = calloc(1, sizeof(pcap_file_t)); + ESP_GOTO_ON_FALSE(pcap, ESP_ERR_NO_MEM, err, TAG, "no mem for pcap file object"); + pcap->file = config->fp; + pcap->major_version = config->major_version; + pcap->minor_version = config->minor_version; + pcap->endian_magic = config->flags.little_endian ? PCAP_MAGIC_LITTLE_ENDIAN : PCAP_MAGIC_BIG_ENDIAN; + pcap->time_zone = config->time_zone; + *ret_pcap = pcap; + return ret; +err: + if (pcap) { + free(pcap); + } + return ret; +} + +esp_err_t pcap_del_session(pcap_file_handle_t pcap) +{ + ESP_RETURN_ON_FALSE(pcap, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + if (pcap->file) { + fclose(pcap->file); + pcap->file = NULL; + } + free(pcap); + return ESP_OK; +} + +esp_err_t pcap_write_header(pcap_file_handle_t pcap, pcap_link_type_t link_type) +{ + ESP_RETURN_ON_FALSE(pcap, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + /* Write Pcap File header */ + pcap_file_header_t header = { + .magic = pcap->endian_magic, + .major = pcap->major_version, + .minor = pcap->minor_version, + .zone = pcap->time_zone, + .sigfigs = 0, + .snaplen = 0x40000, + .link_type = link_type, + }; + size_t real_write = fwrite(&header, sizeof(header), 1, pcap->file); + ESP_RETURN_ON_FALSE(real_write == 1, ESP_FAIL, TAG, "write pcap file header failed"); + /* Save the link type to pcap file object */ + pcap->link_type = link_type; + /* Flush content in the buffer into device */ + fflush(pcap->file); + return ESP_OK; +} + +esp_err_t pcap_capture_packet(pcap_file_handle_t pcap, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds) +{ + ESP_RETURN_ON_FALSE(pcap && payload, ESP_ERR_INVALID_ARG, TAG, "invalid argumnet"); + size_t real_write = 0; + pcap_packet_header_t header = { + .seconds = seconds, + .microseconds = microseconds, + .capture_length = length, + .packet_length = length + }; + real_write = fwrite(&header, sizeof(header), 1, pcap->file); + ESP_RETURN_ON_FALSE(real_write == 1, ESP_FAIL, TAG, "write packet header failed"); + real_write = fwrite(payload, sizeof(uint8_t), length, pcap->file); + ESP_RETURN_ON_FALSE(real_write == length, ESP_FAIL, TAG, "write packet payload failed"); + /* Flush content in the buffer into device */ + fflush(pcap->file); + return ESP_OK; +} + +esp_err_t pcap_print_summary(pcap_file_handle_t pcap, FILE *print_file) +{ + esp_err_t ret = ESP_OK; + long size = 0; + char *packet_payload = NULL; + ESP_RETURN_ON_FALSE(pcap && print_file, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + // get file size + fseek(pcap->file, 0L, SEEK_END); + size = ftell(pcap->file); + fseek(pcap->file, 0L, SEEK_SET); + // file empty is allowed, so return ESP_OK + ESP_RETURN_ON_FALSE(size, ESP_OK, TAG, "pcap file is empty"); + // packet index (by bytes) + uint32_t index = 0; + pcap_file_header_t file_header; + size_t real_read = fread(&file_header, sizeof(pcap_file_header_t), 1, pcap->file); + ESP_RETURN_ON_FALSE(real_read == 1, ESP_FAIL, TAG, "read pcap file header failed"); + index += sizeof(pcap_file_header_t); + //print pcap header information + fprintf(print_file, "------------------------------------------------------------------------\n"); + fprintf(print_file, "Pcap packet Head:\n"); + fprintf(print_file, "------------------------------------------------------------------------\n"); + fprintf(print_file, "Magic Number: %x\n", file_header.magic); + fprintf(print_file, "Major Version: %d\n", file_header.major); + fprintf(print_file, "Minor Version: %d\n", file_header.minor); + fprintf(print_file, "SnapLen: %d\n", file_header.snaplen); + fprintf(print_file, "LinkType: %d\n", file_header.link_type); + fprintf(print_file, "------------------------------------------------------------------------\n"); + uint32_t packet_num = 0; + pcap_packet_header_t packet_header; + while (index < size) { + real_read = fread(&packet_header, sizeof(pcap_packet_header_t), 1, pcap->file); + ESP_GOTO_ON_FALSE(real_read == 1, ESP_FAIL, err, TAG, "read pcap packet header failed"); + // print packet header information + fprintf(print_file, "Packet %d:\n", packet_num); + fprintf(print_file, "Timestamp (Seconds): %d\n", packet_header.seconds); + fprintf(print_file, "Timestamp (Microseconds): %d\n", packet_header.microseconds); + fprintf(print_file, "Capture Length: %d\n", packet_header.capture_length); + fprintf(print_file, "Packet Length: %d\n", packet_header.packet_length); + size_t payload_length = packet_header.capture_length; + packet_payload = malloc(payload_length); + ESP_GOTO_ON_FALSE(packet_payload, ESP_ERR_NO_MEM, err, TAG, "no mem to save packet payload"); + real_read = fread(packet_payload, payload_length, 1, pcap->file); + ESP_GOTO_ON_FALSE(real_read == 1, ESP_FAIL, err, TAG, "read payload error"); + // print packet information + // currently only print info for 802.11 + if (file_header.link_type == PCAP_LINK_TYPE_802_11) { + fprintf(print_file, "Packet Type: %2x\n", (packet_payload[0] >> 4) & 0x03); + fprintf(print_file, "Packet Subtype: %2x\n", packet_payload[0] & 0x0F); + fprintf(print_file, "Destination: "); + for (int j = 0; j < 5; j++) { + fprintf(print_file, "%2x ", packet_payload[4 + j]); + } + fprintf(print_file, "%2x\n", packet_payload[9]); + fprintf(print_file, "Source: "); + for (int j = 0; j < 5; j++) { + fprintf(print_file, "%2x ", packet_payload[10 + j]); + } + fprintf(print_file, "%2x\n", packet_payload[15]); + fprintf(print_file, "------------------------------------------------------------------------\n"); + } else if (file_header.link_type == PCAP_LINK_TYPE_ETHERNET){ + fprintf(print_file, "Destination: "); + for (int j = 0; j < 5; j++) { + fprintf(print_file, "%2x ", packet_payload[j]); + } + fprintf(print_file, "%2x\n", packet_payload[5]); + fprintf(print_file, "Source: "); + for (int j = 0; j < 5; j++) { + fprintf(print_file, "%2x ", packet_payload[6 + j]); + } + fprintf(print_file, "%2x\n", packet_payload[11]); + fprintf(print_file, "Type: 0x%x\n", packet_payload[13] | (packet_payload[12] << 8)); + fprintf(print_file, "------------------------------------------------------------------------\n"); + } + else { + fprintf(print_file, "Unknown link type:%d\n", file_header.link_type); + fprintf(print_file, "------------------------------------------------------------------------\n"); + } + free(packet_payload); + packet_payload = NULL; + index += packet_header.capture_length + sizeof(pcap_packet_header_t); + packet_num ++; + } + fprintf(print_file, "Pcap packet Number: %d\n", packet_num); + fprintf(print_file, "------------------------------------------------------------------------\n"); + return ret; +err: + if (packet_payload) { + free(packet_payload); + } + return ret; +} diff --git a/examples/network/simple_sniffer/components/pcap/CMakeLists.txt b/examples/network/simple_sniffer/components/pcap/CMakeLists.txt deleted file mode 100644 index f12edf2304..0000000000 --- a/examples/network/simple_sniffer/components/pcap/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "pcap.c" - INCLUDE_DIRS .) diff --git a/examples/network/simple_sniffer/components/pcap/component.mk b/examples/network/simple_sniffer/components/pcap/component.mk deleted file mode 100644 index e5a56dfd56..0000000000 --- a/examples/network/simple_sniffer/components/pcap/component.mk +++ /dev/null @@ -1,11 +0,0 @@ -# -# 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/network/simple_sniffer/components/pcap/pcap.c b/examples/network/simple_sniffer/components/pcap/pcap.c deleted file mode 100644 index 1a2b8a832f..0000000000 --- a/examples/network/simple_sniffer/components/pcap/pcap.c +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include -#include -#include -#include -#include -#include "esp_types.h" -#include "esp_err.h" -#include "esp_log.h" -#include "pcap.h" - -static const char *PCAP_TAG = "pcap"; - -#define PCAP_CHECK(a, str, goto_tag, ...) \ - do \ - { \ - if (!(a)) \ - { \ - ESP_LOGE(PCAP_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ - goto goto_tag; \ - } \ - } while (0) - -/** - * @brief Pcap File Header - * - */ -typedef struct { - uint32_t magic; /*!< Magic Number */ - uint16_t major; /*!< Major Version */ - uint16_t minor; /*!< Minor Version */ - uint32_t zone; /*!< Time Zone Offset */ - uint32_t sigfigs; /*!< Timestamp Accuracy */ - uint32_t snaplen; /*!< Max Length to Capture */ - uint32_t link_type; /*!< Link Layer Type */ -} pcap_file_header_t; - -/** - * @brief Pcap Packet Header - * - */ -typedef struct { - uint32_t seconds; /*!< Number of seconds since January 1st, 1970, 00:00:00 GMT */ - uint32_t microseconds; /*!< Number of microseconds when the packet was captured (offset from seconds) */ - uint32_t capture_length; /*!< Number of bytes of captured data, no longer than packet_length */ - uint32_t packet_length; /*!< Actual length of current packet */ -} pcap_packet_header_t; - -/** - * @brief Pcap Runtime Handle - * - */ -typedef struct { - FILE *file; /*!< File handle */ -} pcap_runtime_t; - -esp_err_t pcap_capture_packet(pcap_handle_t handle, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds) -{ - PCAP_CHECK(handle, "pcap handle is NULL", err); - pcap_runtime_t *pcap_rt = (pcap_runtime_t *)handle; - PCAP_CHECK(pcap_rt->file, "pcap file is NULL", err); - size_t real_write = 0; - pcap_packet_header_t header = { - .seconds = seconds, - .microseconds = microseconds, - .capture_length = length, - .packet_length = length - }; - real_write = fwrite(&header, sizeof(header), 1, pcap_rt->file); - PCAP_CHECK(real_write == 1, "write packet header error", err); - real_write = fwrite(payload, sizeof(uint8_t), length, pcap_rt->file); - PCAP_CHECK(real_write == length, "write packet payload error", err); - /* Flush content in the buffer into device */ - fflush(pcap_rt->file); - return ESP_OK; -err: - return ESP_FAIL; -} - -esp_err_t pcap_deinit(pcap_handle_t handle) -{ - PCAP_CHECK(handle, "pcap handle is NULL", err); - pcap_runtime_t *pcap_rt = (pcap_runtime_t *)handle; - - if (pcap_rt->file != NULL) { - fclose(pcap_rt->file); - pcap_rt->file = NULL; - } - free(pcap_rt); - ESP_LOGD(PCAP_TAG, "pcap deinit OK"); - return ESP_OK; -err: - ESP_LOGW(PCAP_TAG, "pcap deinit failed"); - return ESP_FAIL; -} - -esp_err_t pcap_init(pcap_config_t *config, pcap_handle_t *handle) -{ - PCAP_CHECK(config, "config is NULL", err); - PCAP_CHECK(handle, "pcap handle is NULL", err); - pcap_runtime_t *pcap_rt = calloc(1, sizeof(pcap_runtime_t)); - PCAP_CHECK(pcap_rt, "calloc pcap runtime failed", err); - pcap_rt->file = config->fp; - /* Write Pcap File header */ - pcap_file_header_t header = { - .magic = PCAP_MAGIC_BIG_ENDIAN, - .major = PCAP_VERSION_MAJOR, - .minor = PCAP_VERSION_MINOR, - .zone = PCAP_TIME_ZONE_GMT, - .sigfigs = 0, - .snaplen = 0x40000, - .link_type = config->link_type - }; - size_t real_write = fwrite(&header, sizeof(header), 1, pcap_rt->file); - PCAP_CHECK(real_write == 1, "write pcap file header failed", err_write); - /* Flush content in the buffer into device */ - fflush(pcap_rt->file); - *handle = (pcap_handle_t)pcap_rt; - ESP_LOGD(PCAP_TAG, "pcap init OK"); - return ESP_OK; - /* Error Handling */ -err_write: - fclose(pcap_rt->file); - pcap_rt->file = NULL; - free(pcap_rt); -err: - ESP_LOGW(PCAP_TAG, "pcap init failed"); - return ESP_FAIL; -} diff --git a/examples/network/simple_sniffer/components/pcap/pcap.h b/examples/network/simple_sniffer/components/pcap/pcap.h deleted file mode 100644 index af0abcfc19..0000000000 --- a/examples/network/simple_sniffer/components/pcap/pcap.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include "esp_err.h" - -#define PCAP_MAGIC_BIG_ENDIAN 0xA1B2C3D4 /*!< Big-Endian */ -#define PCAP_MAGIC_LITTLE_ENDIAN 0xD4C3B2A1 /*!< Little-Endian */ - -#define PCAP_VERSION_MAJOR 0x02 /*!< Major Version */ -#define PCAP_VERSION_MINOR 0x04 /*!< Minor Version */ - -#define PCAP_TIME_ZONE_GMT 0x00 /*!< Time Zone */ - -/** -* @brief Link layer Type Definition, used for Pcap reader to decode payload -* -*/ -typedef enum { - PCAP_LINK_TYPE_LOOPBACK = 0, /*!< Loopback devices, except for later OpenBSD */ - PCAP_LINK_TYPE_ETHERNET = 1, /*!< Ethernet, and Linux loopback devices */ - PCAP_LINK_TYPE_TOKEN_RING = 6, /*!< 802.5 Token Ring */ - PCAP_LINK_TYPE_ARCNET = 7, /*!< ARCnet */ - PCAP_LINK_TYPE_SLIP = 8, /*!< SLIP */ - PCAP_LINK_TYPE_PPP = 9, /*!< PPP */ - PCAP_LINK_TYPE_FDDI = 10, /*!< FDDI */ - PCAP_LINK_TYPE_ATM = 100, /*!< LLC/SNAP encapsulated ATM */ - PCAP_LINK_TYPE_RAW_IP = 101, /*!< Raw IP, without link */ - PCAP_LINK_TYPE_BSD_SLIP = 102, /*!< BSD/OS SLIP */ - PCAP_LINK_TYPE_BSD_PPP = 103, /*!< BSD/OS PPP */ - PCAP_LINK_TYPE_CISCO_HDLC = 104, /*!< Cisco HDLC */ - PCAP_LINK_TYPE_802_11 = 105, /*!< 802.11 */ - PCAP_LINK_TYPE_BSD_LOOPBACK = 108, /*!< OpenBSD loopback devices(with AF_value in network byte order) */ - PCAP_LINK_TYPE_LOCAL_TALK = 114 /*!< LocalTalk */ -} pcap_link_type_t; - -/** -* @brief Pcap configuration Type Definition -* -*/ -typedef struct { - FILE *fp; /* Pointer to a standard file handle */ - pcap_link_type_t link_type; /* Pcap Link Type */ -} pcap_config_t; - -/** - * @brief Pcap Handle Type Definition - * - */ -typedef void *pcap_handle_t; - -/** - * @brief Initialize a pcap session - * - * @param config configuration of creating pcap object - * @param handle pcap handle - * @return esp_err_t - * - ESP_OK on success - * - ESP_FAIL on error - */ -esp_err_t pcap_init(pcap_config_t *config, pcap_handle_t *handle); - -/** - * @brief De-initialize a pcap session - * - * @param handle pcap handle - * @return esp_err_t - * - ESP_OK on success - * - ESP_FAIL on error - */ -esp_err_t pcap_deinit(pcap_handle_t handle); - -/** - * @brief Capture one packet into pcap file - * - * @param handle pcap handle - * @param payload pointer of the captured data - * @param length length of captured data - * @param seconds second of capture time - * @param microseconds microsecond of capture time - * @return esp_err_t - * - ESP_OK on success - * - ESP_FAIL on error - */ -esp_err_t pcap_capture_packet(pcap_handle_t handle, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds); - -#ifdef __cplusplus -} -#endif From d6d0c37b30c4ba95b4bdc580ac0283a392bde93d Mon Sep 17 00:00:00 2001 From: bizhuangyang Date: Thu, 13 May 2021 15:59:03 +0800 Subject: [PATCH 2/2] simple_sniffer: can store packets in memory --- .../network/simple_sniffer/CMakeLists.txt | 2 +- examples/network/simple_sniffer/Makefile | 2 +- examples/network/simple_sniffer/README.md | 245 +++++++++++--- .../network/simple_sniffer/example_test.py | 50 +++ .../simple_sniffer/main/CMakeLists.txt | 1 + .../simple_sniffer/main/Kconfig.projbuild | 12 + .../network/simple_sniffer/main/cmd_pcap.c | 300 ++++++++++++++++++ .../network/simple_sniffer/main/cmd_pcap.h | 57 ++++ .../network/simple_sniffer/main/cmd_sniffer.c | 205 ++++-------- .../network/simple_sniffer/main/cmd_sniffer.h | 2 +- .../main/simple_sniffer_example_main.c | 25 +- .../network/simple_sniffer/sdkconfig.ci.mem | 1 + 12 files changed, 714 insertions(+), 188 deletions(-) create mode 100644 examples/network/simple_sniffer/example_test.py create mode 100644 examples/network/simple_sniffer/main/cmd_pcap.c create mode 100644 examples/network/simple_sniffer/main/cmd_pcap.h create mode 100644 examples/network/simple_sniffer/sdkconfig.ci.mem diff --git a/examples/network/simple_sniffer/CMakeLists.txt b/examples/network/simple_sniffer/CMakeLists.txt index 6bdba03eea..130c6c262e 100644 --- a/examples/network/simple_sniffer/CMakeLists.txt +++ b/examples/network/simple_sniffer/CMakeLists.txt @@ -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) diff --git a/examples/network/simple_sniffer/Makefile b/examples/network/simple_sniffer/Makefile index 520cfededf..51e1bca362 100644 --- a/examples/network/simple_sniffer/Makefile +++ b/examples/network/simple_sniffer/Makefile @@ -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 diff --git a/examples/network/simple_sniffer/README.md b/examples/network/simple_sniffer/README.md index 23d3b16b0b..154a644348 100644 --- a/examples/network/simple_sniffer/README.md +++ b/examples/network/simple_sniffer/README.md @@ -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 ][-i ] [-F ]... [-c ][--stop] +> sniffer [-i ] [-F ]... [-c ] [--stop] [-n ] > Capture specific packet and store in pcap format -> -f, --file= name of the file storing the packets in pcap format > -i, --interface= which interface to capture packet -> -F, --filter= filter parameters +> -F, --filter= filter parameters > -c, --channel= communication channel to use > --stop stop running sniffer +> -n, --number= 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 [--open] [--close] [--summary] +> Save and parse pcap file +> -f, --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 [--open] [--close] [--summary] +> Save and parse pcap file +> -f, --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 ' to mount filesystem | - | 3. Enter 'sniffer' to start capture packets | - | 4. Enter 'unmount ' to unmount filesystem | + | 3. Enter 'pcap' to create pcap file | + | 4. Enter 'sniffer' to start capture packets | + | 5. Enter 'unmount ' 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) diff --git a/examples/network/simple_sniffer/example_test.py b/examples/network/simple_sniffer/example_test.py new file mode 100644 index 0000000000..4df9a18b56 --- /dev/null +++ b/examples/network/simple_sniffer/example_test.py @@ -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() diff --git a/examples/network/simple_sniffer/main/CMakeLists.txt b/examples/network/simple_sniffer/main/CMakeLists.txt index 944e8b301b..5d4205471d 100644 --- a/examples/network/simple_sniffer/main/CMakeLists.txt +++ b/examples/network/simple_sniffer/main/CMakeLists.txt @@ -1,3 +1,4 @@ idf_component_register(SRCS "simple_sniffer_example_main.c" "cmd_sniffer.c" + "cmd_pcap.c" INCLUDE_DIRS ".") diff --git a/examples/network/simple_sniffer/main/Kconfig.projbuild b/examples/network/simple_sniffer/main/Kconfig.projbuild index 0137d2e33a..b40b5d4d23 100644 --- a/examples/network/simple_sniffer/main/Kconfig.projbuild +++ b/examples/network/simple_sniffer/main/Kconfig.projbuild @@ -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 diff --git a/examples/network/simple_sniffer/main/cmd_pcap.c b/examples/network/simple_sniffer/main/cmd_pcap.c new file mode 100644 index 0000000000..bf6d0631ef --- /dev/null +++ b/examples/network/simple_sniffer/main/cmd_pcap.c @@ -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 +#include +#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 +#include +#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", "", + "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 +} diff --git a/examples/network/simple_sniffer/main/cmd_pcap.h b/examples/network/simple_sniffer/main/cmd_pcap.h new file mode 100644 index 0000000000..6dbc3ceea2 --- /dev/null +++ b/examples/network/simple_sniffer/main/cmd_pcap.h @@ -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 diff --git a/examples/network/simple_sniffer/main/cmd_sniffer.c b/examples/network/simple_sniffer/main/cmd_sniffer.c index 3c2939b97b..34b31c3b6d 100644 --- a/examples/network/simple_sniffer/main/cmd_sniffer.c +++ b/examples/network/simple_sniffer/main/cmd_sniffer.c @@ -6,14 +6,12 @@ CONDITIONS OF ANY KIND, either express or implied. */ #include +#include #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 #include #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", "", - "name of the file storing the packets in pcap format"); + sniffer_args.number = arg_int0("n", "number", "", + "the number of the packets to be captured"); sniffer_args.interface = arg_str0("i", "interface", "", "which interface to capture packet"); sniffer_args.filter = arg_strn("F", "filter", "", 0, 7, "filter parameters"); diff --git a/examples/network/simple_sniffer/main/cmd_sniffer.h b/examples/network/simple_sniffer/main/cmd_sniffer.h index 7f76e4a3ea..d20085a127 100644 --- a/examples/network/simple_sniffer/main/cmd_sniffer.h +++ b/examples/network/simple_sniffer/main/cmd_sniffer.h @@ -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 diff --git a/examples/network/simple_sniffer/main/simple_sniffer_example_main.c b/examples/network/simple_sniffer/main/simple_sniffer_example_main.c index 54172ebe3e..5249589e7c 100644 --- a/examples/network/simple_sniffer/main/simple_sniffer_example_main.c +++ b/examples/network/simple_sniffer/main/simple_sniffer_example_main.c @@ -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 ' to mount filesystem |\n"); - printf(" | 3. Enter 'sniffer' to start capture packets |\n"); - printf(" | 4. Enter 'unmount ' 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 ' 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)); diff --git a/examples/network/simple_sniffer/sdkconfig.ci.mem b/examples/network/simple_sniffer/sdkconfig.ci.mem new file mode 100644 index 0000000000..5e21d301f7 --- /dev/null +++ b/examples/network/simple_sniffer/sdkconfig.ci.mem @@ -0,0 +1 @@ +CONFIG_SNIFFER_PCAP_DESTINATION_MEMORY=y