This is my another example of simple and inexpensive WiFi packet analyzer (also known as a WiFi sniffer). The heart of this project is ESP8266 WiFi module which is able to work in a promiscusous mode. This module allows IEEE802.11 network packets capturing for further analyzing. Because of the fact that ESP modules don’t listening on all channels at a time, additional code has been added in the main loop to switch channels in 2s intervals. Presented sniffer requires a callback function that will process all received promiscusous packets. Example callback function displays few basic information like packet channel, length, RSSI or MAC addresses. The code is using ESP-IDF and can be found on GitHub, click here.
Parts Required
- ESP8266 Development Module (for example based on ESP-12x)
Software
This code is written in C and can be compiled using xtensa-lx106-elf-gcc. Don’t know how to start? Please read about building ESP8266 toolchain for Linux.
/**
* Copyright (c) 2019, Łukasz Marcin Podkalicki <lpodkalicki@gmail.com>
* ESP8266/016
* Example of WiFi sniffer.
*/
#include <stdint.h>
#include <esp/gpio.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
typedef struct {
signed rssi: 8;
unsigned rate: 4;
unsigned is_group: 1;
unsigned: 1;
unsigned sig_mode: 2;
unsigned legacy_length: 12;
unsigned damatch0: 1;
unsigned damatch1: 1;
unsigned bssidmatch0: 1;
unsigned bssidmatch1: 1;
unsigned MCS: 7;
unsigned CWB: 1;
unsigned HT_length: 16;
unsigned Smoothing: 1;
unsigned Not_Sounding: 1;
unsigned: 1;
unsigned Aggregation: 1;
unsigned STBC: 2;
unsigned FEC_CODING: 1;
unsigned SGI: 1;
unsigned rxend_state: 8;
unsigned ampdu_cnt: 8;
unsigned channel: 4;
unsigned: 12;
} wifi_pkt_rx_ctrl_t;
typedef struct {
wifi_pkt_rx_ctrl_t rx_ctrl;
uint8_t payload[0]; /* ieee80211 packet buff */
} wifi_promiscuous_pkt_t;
typedef struct {
unsigned frame_ctrl:16;
unsigned duration_id:16;
uint8_t addr1[6]; /* receiver address */
uint8_t addr2[6]; /* sender address */
uint8_t addr3[6]; /* filtering address */
unsigned sequence_ctrl:16;
uint8_t addr4[6]; /* optional */
} wifi_ieee80211_mac_hdr_t;
typedef struct {
wifi_ieee80211_mac_hdr_t hdr;
uint8_t payload[0]; /* network data ended with 4 bytes csum (CRC32) */
} wifi_ieee80211_packet_t;
extern sdk_wifi_promiscuous_cb_t sdk_promiscuous_cb;
static void sniffer_init(void);
static void sniffer_task(void *prv);
static void sniffer_packet_handler(uint8_t *buf, uint16_t len);
void
user_init(void)
{
uart_set_baud(0, 115200);
sniffer_init();
printf("SDK version:%s\n", sdk_system_get_sdk_version());
xTaskCreate(sniffer_task, "sniffer_task", 1024, NULL, 1, NULL);
}
void
sniffer_init(void)
{
sdk_wifi_set_opmode(NULL_MODE); // reset AP mode
sdk_wifi_set_channel(1); // set start channel
sdk_promiscuous_cb = sniffer_packet_handler; // set promiscuous callback
sdk_wifi_promiscuous_enable(true); // enable promiscuous mode
}
void
sniffer_task(void *prv)
{
while (1) {
vTaskDelay(2000 / portTICK_PERIOD_MS);
sdk_wifi_set_channel(sdk_wifi_get_channel() % 13 + 1);
}
}
void
sniffer_packet_handler(uint8_t *buff, uint16_t len)
{
const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff;
const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload;
const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr;
printf("PACKET LEN=%u CHAN=%02d, RSSI=%02d,"
" ADDR1=%02x:%02x:%02x:%02x:%02x:%02x,"
" ADDR2=%02x:%02x:%02x:%02x:%02x:%02x,"
" ADDR3=%02x:%02x:%02x:%02x:%02x:%02x\n",
len,
sdk_wifi_get_channel(),
(int32_t)ppkt->rx_ctrl.rssi,
/* ADDR1 */
hdr->addr1[0],hdr->addr1[1],hdr->addr1[2],
hdr->addr1[3],hdr->addr1[4],hdr->addr1[5],
/* ADDR2 */
hdr->addr2[0],hdr->addr2[1],hdr->addr2[2],
hdr->addr2[3],hdr->addr2[4],hdr->addr2[5],
/* ADDR3 */
hdr->addr3[0],hdr->addr3[1],hdr->addr3[2],
hdr->addr3[3],hdr->addr3[4],hdr->addr3[5]
);
}
