diff --git a/get-started/csi_recv_router/main/app_main.c b/get-started/csi_recv_router/main/app_main.c index 6d3d232..97c84c9 100644 --- a/get-started/csi_recv_router/main/app_main.c +++ b/get-started/csi_recv_router/main/app_main.c @@ -689,7 +689,7 @@ static void ota_task(void *arg) vTaskDelete(NULL); } -/* --- Promiscuous mode: deauth/disassoc detection --- */ +/* --- Promiscuous mode: deauth/disassoc detection + probe request capture --- */ typedef struct { uint16_t frame_ctrl; @@ -700,6 +700,41 @@ typedef struct { uint16_t seq_ctrl; } __attribute__((packed)) wifi_ieee80211_mac_hdr_t; +/* Probe request deduplication: report each MAC at most once per 10 seconds */ +#define PROBE_DEDUP_SIZE 32 +#define PROBE_DEDUP_US 10000000LL + +static struct { + uint8_t mac[6]; + int64_t ts; +} s_probe_seen[PROBE_DEDUP_SIZE]; + +static bool probe_dedup_check(const uint8_t *mac) +{ + int64_t now = esp_timer_get_time(); + int oldest_idx = 0; + int64_t oldest_ts = INT64_MAX; + + for (int i = 0; i < PROBE_DEDUP_SIZE; i++) { + if (memcmp(s_probe_seen[i].mac, mac, 6) == 0) { + if (now - s_probe_seen[i].ts < PROBE_DEDUP_US) { + return true; /* seen recently, skip */ + } + s_probe_seen[i].ts = now; + return false; /* cooldown expired */ + } + if (s_probe_seen[i].ts < oldest_ts) { + oldest_ts = s_probe_seen[i].ts; + oldest_idx = i; + } + } + + /* New MAC — replace oldest entry */ + memcpy(s_probe_seen[oldest_idx].mac, mac, 6); + s_probe_seen[oldest_idx].ts = now; + return false; +} + static void wifi_promiscuous_cb(void *buf, wifi_promiscuous_pkt_type_t type) { if (type != WIFI_PKT_MGMT) return; @@ -708,41 +743,71 @@ static void wifi_promiscuous_cb(void *buf, wifi_promiscuous_pkt_type_t type) const wifi_ieee80211_mac_hdr_t *hdr = (wifi_ieee80211_mac_hdr_t *)pkt->payload; uint8_t subtype = (hdr->frame_ctrl >> 4) & 0x0F; - const char *type_str = NULL; - if (subtype == 0x0C) { - type_str = "deauth"; - } else if (subtype == 0x0A) { - type_str = "disassoc"; - } else { + /* Deauth (0x0C) / Disassoc (0x0A) */ + if (subtype == 0x0C || subtype == 0x0A) { + const char *type_str = (subtype == 0x0C) ? "deauth" : "disassoc"; + + char alert[160]; + int len = snprintf(alert, sizeof(alert), + "ALERT_DATA,%s,%s," + "%02x:%02x:%02x:%02x:%02x:%02x," + "%02x:%02x:%02x:%02x:%02x:%02x," + "%d\n", + s_hostname, type_str, + hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], + hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], + hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], + hdr->addr1[3], hdr->addr1[4], hdr->addr1[5], + pkt->rx_ctrl.rssi); + + if (s_udp_socket >= 0) { + sendto(s_udp_socket, alert, len, 0, + (struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr)); + } + + ESP_LOGW(TAG, "ALERT: %s from " MACSTR " -> " MACSTR " rssi=%d", + type_str, + hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], + hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], + hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], + hdr->addr1[3], hdr->addr1[4], hdr->addr1[5], + pkt->rx_ctrl.rssi); return; } - char alert[160]; - int len = snprintf(alert, sizeof(alert), - "ALERT_DATA,%s,%s," - "%02x:%02x:%02x:%02x:%02x:%02x," - "%02x:%02x:%02x:%02x:%02x:%02x," - "%d\n", - s_hostname, type_str, - hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], - hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], - hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], - hdr->addr1[3], hdr->addr1[4], hdr->addr1[5], - pkt->rx_ctrl.rssi); + /* Probe request (0x04) */ + if (subtype == 0x04) { + /* Dedup: skip if this MAC was reported recently */ + if (probe_dedup_check(hdr->addr2)) return; - if (s_udp_socket >= 0) { - sendto(s_udp_socket, alert, len, 0, - (struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr)); + /* Parse SSID from tagged parameters after MAC header */ + const uint8_t *body = pkt->payload + sizeof(wifi_ieee80211_mac_hdr_t); + int body_len = pkt->rx_ctrl.sig_len - sizeof(wifi_ieee80211_mac_hdr_t); + + char ssid[33] = ""; + if (body_len >= 2 && body[0] == 0) { /* Tag 0 = SSID */ + int ssid_len = body[1]; + if (ssid_len > 0 && ssid_len <= 32 && ssid_len + 2 <= body_len) { + memcpy(ssid, &body[2], ssid_len); + ssid[ssid_len] = '\0'; + } + } + + char probe[192]; + int len = snprintf(probe, sizeof(probe), + "PROBE_DATA,%s,%02x:%02x:%02x:%02x:%02x:%02x,%d,%s\n", + s_hostname, + hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], + hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], + pkt->rx_ctrl.rssi, + ssid); + + if (s_udp_socket >= 0) { + sendto(s_udp_socket, probe, len, 0, + (struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr)); + } } - - ESP_LOGW(TAG, "ALERT: %s from " MACSTR " -> " MACSTR " rssi=%d", - type_str, - hdr->addr2[0], hdr->addr2[1], hdr->addr2[2], - hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], - hdr->addr1[0], hdr->addr1[1], hdr->addr1[2], - hdr->addr1[3], hdr->addr1[4], hdr->addr1[5], - pkt->rx_ctrl.rssi); } static void wifi_promiscuous_init(void) @@ -753,7 +818,7 @@ static void wifi_promiscuous_init(void) ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filt)); ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb)); ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); - ESP_LOGI(TAG, "Promiscuous mode: deauth/disassoc detection enabled"); + ESP_LOGI(TAG, "Promiscuous mode: deauth/disassoc/probe detection enabled"); } /* --- Command handler --- */ @@ -1125,11 +1190,12 @@ void app_main() } #endif - /* mDNS: announce as .local */ + /* mDNS: announce as .local with _esp-csi._udp service */ ESP_ERROR_CHECK(mdns_init()); mdns_hostname_set(s_hostname); mdns_instance_name_set("ESP32 CSI Sensor"); - ESP_LOGI(TAG, "mDNS hostname: %s.local", s_hostname); + mdns_service_add(NULL, "_esp-csi", "_udp", s_target_port, NULL, 0); + ESP_LOGI(TAG, "mDNS hostname: %s.local (_esp-csi._udp:%d)", s_hostname, s_target_port); /* Watchdog: 30s timeout, auto-reboot on hang */ esp_task_wdt_config_t wdt_cfg = {