espilon-source/espilon_bot/components/mod_honeypot/hp_wifi_monitor.c
Eun0us 6d45770d98 epsilon: merge command system into core + add 5 new modules
Move command registry from components/command/ into components/core/.
New modules: mod_canbus, mod_honeypot, mod_fallback, mod_redteam, mod_ota.
Replace mod_proxy with tun_core (multiplexed SOCKS5 tunnel).
Kconfig extended with per-module settings and async worker config.
2026-02-28 20:07:59 +01:00

321 lines
10 KiB
C

/*
* hp_wifi_monitor.c
* WiFi promiscuous-mode monitor: probe requests, deauth frames,
* beacon flood, EAPOL capture detection.
*
* Sends EVT| events via event_send().
* Conflict guard: refuses to start if the fakeAP sniffer is active.
*/
#include "sdkconfig.h"
#ifdef CONFIG_MODULE_HONEYPOT
#include <stdio.h>
#include <string.h>
#include <stdatomic.h>
#include "esp_wifi.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "utils.h"
#include "event_format.h"
#include "hp_config.h"
#include "hp_wifi_monitor.h"
#define TAG "HP_WIFI"
#define WIFI_MON_STACK 4096
#define WIFI_MON_PRIO 4
#define WIFI_MON_CORE 1
/* Rate-limit counters (only report every N-th event) */
#define PROBE_RATE_LIMIT 10
#define DEAUTH_RATE_LIMIT 5
#define BEACON_RATE_LIMIT 20
#define EAPOL_RATE_LIMIT 3
/* Beacon flood detection: N beacons in BEACON_WINDOW_MS from same src */
#define BEACON_FLOOD_THRESHOLD 50
#define BEACON_WINDOW_MS 5000
/* ============================================================
* State
* ============================================================ */
static atomic_bool mon_running = false;
static atomic_bool mon_stop_req = false;
static TaskHandle_t mon_task = NULL;
static uint32_t cnt_probe = 0;
static uint32_t cnt_deauth = 0;
static uint32_t cnt_beacon = 0;
static uint32_t cnt_eapol = 0;
/* Multi-source beacon flood tracker */
#define BEACON_TRACK_MAX 4
typedef struct {
uint8_t mac[6];
uint32_t count;
uint32_t start;
bool alerted;
} beacon_tracker_t;
static beacon_tracker_t beacon_trackers[BEACON_TRACK_MAX];
static int beacon_tracker_count = 0;
/* ============================================================
* IEEE 802.11 helpers
* ============================================================ */
/* Frame control subtypes */
#define WLAN_FC_TYPE_MGMT 0x00
#define WLAN_FC_STYPE_PROBE 0x40 /* Probe Request */
#define WLAN_FC_STYPE_BEACON 0x80 /* Beacon */
#define WLAN_FC_STYPE_DEAUTH 0xC0 /* Deauthentication */
/* EAPOL: data frame with ethertype 0x888E */
#define ETHERTYPE_EAPOL 0x888E
static void mac_to_str(const uint8_t *mac, char *buf, size_t len)
{
snprintf(buf, len, "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
/* ============================================================
* Beacon flood helper — find or create tracker for MAC
* ============================================================ */
static beacon_tracker_t *beacon_find_or_create(const uint8_t *mac, uint32_t now)
{
/* Search existing */
for (int i = 0; i < beacon_tracker_count; i++) {
if (memcmp(beacon_trackers[i].mac, mac, 6) == 0)
return &beacon_trackers[i];
}
/* Evict oldest if full */
if (beacon_tracker_count >= BEACON_TRACK_MAX) {
int oldest = 0;
for (int i = 1; i < beacon_tracker_count; i++) {
if (beacon_trackers[i].start < beacon_trackers[oldest].start)
oldest = i;
}
if (oldest < beacon_tracker_count - 1)
beacon_trackers[oldest] = beacon_trackers[beacon_tracker_count - 1];
beacon_tracker_count--;
}
beacon_tracker_t *t = &beacon_trackers[beacon_tracker_count++];
memcpy(t->mac, mac, 6);
t->count = 0;
t->start = now;
t->alerted = false;
return t;
}
/* ============================================================
* Promiscuous RX callback
* ============================================================ */
static void wifi_monitor_rx_cb(void *buf, wifi_promiscuous_pkt_type_t type)
{
if (!mon_running)
return;
const wifi_promiscuous_pkt_t *pkt = (const wifi_promiscuous_pkt_t *)buf;
const uint8_t *frame = pkt->payload;
uint16_t frame_len = pkt->rx_ctrl.sig_len;
if (frame_len < 24)
return;
uint8_t fc0 = frame[0];
uint8_t fc_type = fc0 & 0x0C; /* bits 2-3 */
uint8_t fc_subtype = fc0 & 0xF0; /* bits 4-7 */
/* Source MAC (addr2 = transmitter) at offset 10 */
const uint8_t *src_mac = &frame[10];
char mac_str[18];
if (type == WIFI_PKT_MGMT) {
if (fc_type == WLAN_FC_TYPE_MGMT) {
/* --- Probe Request --- */
if (fc_subtype == WLAN_FC_STYPE_PROBE) {
cnt_probe++;
if ((cnt_probe % PROBE_RATE_LIMIT) == 1) {
mac_to_str(src_mac, mac_str, sizeof(mac_str));
char detail[64];
snprintf(detail, sizeof(detail), "count=%lu",
(unsigned long)cnt_probe);
event_send("WIFI_PROBE", "LOW",
mac_str, "0.0.0.0", 0, 0, detail, NULL);
}
return;
}
/* --- Deauthentication --- */
if (fc_subtype == WLAN_FC_STYPE_DEAUTH) {
cnt_deauth++;
if ((cnt_deauth % DEAUTH_RATE_LIMIT) == 1) {
mac_to_str(src_mac, mac_str, sizeof(mac_str));
char detail[64];
snprintf(detail, sizeof(detail), "reason=%d count=%lu",
(frame_len >= 26) ? (frame[24] | (frame[25] << 8)) : 0,
(unsigned long)cnt_deauth);
event_send("WIFI_DEAUTH", "HIGH",
mac_str, "0.0.0.0", 0, 0, detail, NULL);
}
return;
}
/* --- Beacon flood detection (multi-source) --- */
if (fc_subtype == WLAN_FC_STYPE_BEACON) {
uint32_t now = (uint32_t)(xTaskGetTickCount() *
portTICK_PERIOD_MS);
beacon_tracker_t *bt = beacon_find_or_create(src_mac, now);
if ((now - bt->start) >= BEACON_WINDOW_MS) {
/* Window expired, reset */
bt->start = now;
bt->count = 1;
bt->alerted = false;
} else {
bt->count++;
if (bt->count >= BEACON_FLOOD_THRESHOLD && !bt->alerted) {
bt->alerted = true;
cnt_beacon++;
mac_to_str(src_mac, mac_str, sizeof(mac_str));
char detail[64];
snprintf(detail, sizeof(detail),
"beacons=%lu window_ms=%d",
(unsigned long)bt->count,
BEACON_WINDOW_MS);
event_send("WIFI_BEACON_FLOOD", "HIGH",
mac_str, "0.0.0.0", 0, 0, detail, NULL);
}
}
return;
}
}
}
/* --- EAPOL detection (data frames with 802.1X ethertype) --- */
if (type == WIFI_PKT_DATA && frame_len >= 36) {
/* LLC/SNAP header starts at offset 24 for data frames:
* 24: AA AA 03 00 00 00 [ethertype_hi] [ethertype_lo] */
if (frame[24] == 0xAA && frame[25] == 0xAA && frame[26] == 0x03) {
uint16_t ethertype = (frame[30] << 8) | frame[31];
if (ethertype == ETHERTYPE_EAPOL) {
cnt_eapol++;
if ((cnt_eapol % EAPOL_RATE_LIMIT) == 1) {
mac_to_str(src_mac, mac_str, sizeof(mac_str));
char detail[64];
snprintf(detail, sizeof(detail), "count=%lu",
(unsigned long)cnt_eapol);
event_send("WIFI_EAPOL", "CRITICAL",
mac_str, "0.0.0.0", 0, 0, detail, NULL);
}
}
}
}
}
/* ============================================================
* Monitor task (just keeps alive, callback does the work)
* ============================================================ */
static void wifi_monitor_task(void *arg)
{
(void)arg;
esp_err_t err = esp_wifi_set_promiscuous_rx_cb(wifi_monitor_rx_cb);
if (err != ESP_OK) {
ESP_LOGE(TAG, "set_promiscuous_rx_cb failed: %s", esp_err_to_name(err));
goto done;
}
err = esp_wifi_set_promiscuous(true);
if (err != ESP_OK) {
ESP_LOGE(TAG, "set_promiscuous(true) failed: %s", esp_err_to_name(err));
goto done;
}
/* Filter: management + data frames only */
wifi_promiscuous_filter_t filter = {
.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT |
WIFI_PROMIS_FILTER_MASK_DATA
};
esp_wifi_set_promiscuous_filter(&filter);
ESP_LOGI(TAG, "WiFi monitor started");
mon_running = true;
/* Idle loop, checking for stop request */
while (!mon_stop_req) {
vTaskDelay(pdMS_TO_TICKS(500));
}
esp_wifi_set_promiscuous(false);
esp_wifi_set_promiscuous_rx_cb(NULL);
done:
mon_running = false;
mon_stop_req = false;
ESP_LOGI(TAG, "WiFi monitor stopped");
mon_task = NULL;
vTaskDelete(NULL);
}
/* ============================================================
* Public API
* ============================================================ */
void hp_wifi_monitor_start(void)
{
if (mon_running || mon_task) {
ESP_LOGW(TAG, "WiFi monitor already running");
return;
}
cnt_probe = cnt_deauth = cnt_beacon = cnt_eapol = 0;
memset(beacon_trackers, 0, sizeof(beacon_trackers));
beacon_tracker_count = 0;
mon_stop_req = false;
BaseType_t ret = xTaskCreatePinnedToCore(wifi_monitor_task, "hp_wifi",
WIFI_MON_STACK, NULL, WIFI_MON_PRIO, &mon_task, WIFI_MON_CORE);
if (ret != pdPASS) {
ESP_LOGE(TAG, "Failed to create WiFi monitor task");
mon_task = NULL;
}
}
void hp_wifi_monitor_stop(void)
{
if (!mon_running && !mon_task) {
ESP_LOGW(TAG, "WiFi monitor not running");
return;
}
mon_stop_req = true;
ESP_LOGI(TAG, "WiFi monitor stop requested");
}
bool hp_wifi_monitor_running(void)
{
return mon_running;
}
int hp_wifi_monitor_status(char *buf, size_t len)
{
return snprintf(buf, len,
"running=%s probes=%lu deauth=%lu beacon_flood=%lu eapol=%lu",
mon_running ? "yes" : "no",
(unsigned long)cnt_probe,
(unsigned long)cnt_deauth,
(unsigned long)cnt_beacon,
(unsigned long)cnt_eapol);
}
#endif /* CONFIG_MODULE_HONEYPOT */