Compare commits

..

No commits in common. "c73e694b9a812decda1cc92a7aa8e1b587eb3af9" and "bb6bd568ce558d0e8f717d83eb7bc4283e2b0ec4" have entirely different histories.

11 changed files with 37 additions and 1227 deletions

View File

@ -1,40 +0,0 @@
# Installing Scapy on Raspberry Pi 5
## Option 1: Install via apt (Recommended)
```bash
sudo apt-get install python3-scapy
```
This is the safest method and integrates with the system package manager.
## Option 2: Use --break-system-packages (For system tools)
Since this is a system-level monitoring tool that runs as root, you can use:
```bash
sudo pip3 install --break-system-packages scapy
```
## Option 3: Virtual Environment (Not recommended for root tools)
If you prefer a virtual environment (though less convenient for root tools):
```bash
python3 -m venv ~/scapy-env
source ~/scapy-env/bin/activate
pip install scapy
```
Then run the script with:
```bash
sudo ~/scapy-env/bin/python3 rpi_capture_ra_ta_python.py 11
```
## Quick Install Command
For this use case (system monitoring tool), Option 2 is acceptable:
```bash
sudo pip3 install --break-system-packages scapy
```

View File

@ -1,216 +0,0 @@
# Raspberry Pi 5 WiFi Monitor Mode Guide
## Quick Setup
1. **Copy the setup script to your Raspberry Pi:**
```bash
scp rpi_monitor_setup.sh pi@your-pi-ip:~/
```
2. **SSH into your Raspberry Pi and run:**
```bash
sudo ./rpi_monitor_setup.sh [channel]
```
Example for channel 11:
```bash
sudo ./rpi_monitor_setup.sh 11
```
## Manual Setup (Alternative)
If you prefer to run commands manually:
```bash
# 1. Check available interfaces
iw dev
# 2. Bring down the interface
sudo ip link set wlan0 down
# 3. Set to monitor mode
sudo iw dev wlan0 set type monitor
# 4. Bring up the interface
sudo ip link set wlan0 up
# 5. Set channel (e.g., channel 11)
sudo iw dev wlan0 set channel 11
# 6. Verify monitor mode
iw dev wlan0 info
```
## Capturing Packets
Once monitor mode is active, you can capture packets:
### Using tcpdump (simple)
```bash
# View packets in real-time
sudo tcpdump -i wlan0 -n
# Save to file
sudo tcpdump -i wlan0 -w capture.pcap
# Filter by MAC address (e.g., your Pi's MAC)
sudo tcpdump -i wlan0 -n ether host 80:84:89:93:c4:b6
# Filter by channel (if using multiple interfaces)
sudo tcpdump -i wlan0 -n -c 100 # Capture 100 packets
```
### Using airodump-ng (advanced, requires aircrack-ng)
```bash
# Install aircrack-ng if needed
sudo apt-get update
sudo apt-get install aircrack-ng
# Capture on specific channel
sudo airodump-ng wlan0 -c 11
# Save to file
sudo airodump-ng wlan0 -c 11 -w capture
```
### Using Wireshark (GUI)
```bash
# Install wireshark if needed
sudo apt-get install wireshark
# Run wireshark (may need to add user to wireshark group)
sudo wireshark -i wlan0
```
## Capturing RA/TA Addresses
### Quick Capture Script (Recommended)
Use the provided Python script for best results:
```bash
# Install scapy if needed
sudo apt-get install python3-pip
sudo pip3 install scapy
# Capture on channel 11 (shows all frames with RA/TA)
sudo python3 rpi_capture_ra_ta_python.py 11
# Capture and filter by specific MAC address
sudo python3 rpi_capture_ra_ta_python.py 11 80:84:89:93:c4:b6
```
The script will:
- Automatically set monitor mode
- Parse 802.11 frames correctly
- Display RA (Receiver Address) and TA (Transmitter Address)
- Show frame type, RSSI, length, and QoS info
- Provide statistics when stopped (Ctrl+C)
### Alternative: Bash Script
For a simpler bash-based solution:
```bash
# Capture on channel 11
sudo ./rpi_capture_ra_ta.sh 11
# Capture and filter by MAC
sudo ./rpi_capture_ra_ta.sh 11 80:84:89:93:c4:b6
```
## Monitoring Specific Traffic
### Filter by MAC address (TA/RA)
```bash
# Capture frames from specific transmitter (TA)
sudo tcpdump -i wlan0 -n ether src 80:84:89:93:c4:b6
# Capture frames to specific receiver (RA)
sudo tcpdump -i wlan0 -n ether dst e0:46:ee:07:df:e1
# Capture frames involving either address
sudo tcpdump -i wlan0 -n "ether host 80:84:89:93:c4:b6 or ether host e0:46:ee:07:df:e1"
```
### Filter by frame type
```bash
# Data frames only
sudo tcpdump -i wlan0 -n "type wlan type data"
# Management frames (beacons, probes, etc.)
sudo tcpdump -i wlan0 -n "type wlan type mgt"
# Control frames (RTS, CTS, ACK)
sudo tcpdump -i wlan0 -n "type wlan type ctl"
```
## Restoring Normal WiFi
To restore normal WiFi operation:
```bash
# Bring down interface
sudo ip link set wlan0 down
# Set back to managed mode
sudo iw dev wlan0 set type managed
# Bring up interface
sudo ip link set wlan0 up
# Reconnect to your network (use NetworkManager, wpa_supplicant, etc.)
sudo nmcli device wifi connect "YourSSID" password "YourPassword"
# OR
sudo wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf &
sudo dhclient wlan0
```
## Troubleshooting
### Interface not found
```bash
# List all network interfaces
ip link show
# Check WiFi interfaces specifically
iw dev
```
### Permission denied
- Make sure you're using `sudo` for all monitor mode commands
- Some distributions require adding your user to specific groups
### Can't set monitor mode
- Some WiFi adapters don't support monitor mode
- Check adapter capabilities: `iw phy | grep -A 10 "Supported interface modes"`
- Raspberry Pi 5 built-in WiFi should support monitor mode
### Channel not changing
- Make sure the interface is up: `sudo ip link set wlan0 up`
- Try bringing it down first, then setting channel, then bringing it up
## Useful Commands
```bash
# Check current interface status
iw dev wlan0 info
# Scan for networks (won't work in monitor mode, but useful before switching)
iw dev wlan0 scan
# Check signal strength and link info (before switching to monitor mode)
iw dev wlan0 link
# Monitor channel activity
watch -n 1 "iw dev wlan0 info | grep channel"
```
## Comparing with ESP32 Monitor
When comparing captures between your ESP32 and Raspberry Pi:
1. **Ensure same channel**: Both devices must monitor the same channel
2. **Time sync**: Consider using NTP for accurate timestamp comparison
3. **MAC filtering**: Use tcpdump filters to match your ESP32's filter settings
4. **Frame types**: Both should capture the same frame types (data, management, control)

View File

@ -54,11 +54,6 @@ static struct {
struct arg_end *end;
} debug_args;
static struct {
struct arg_str *mac;
struct arg_end *end;
} filter_args;
static void print_monitor_usage(void) {
printf("Usage: monitor <subcommand> [args]\n");
printf("Subcommands:\n");
@ -67,7 +62,6 @@ static void print_monitor_usage(void) {
printf(" status Show current status\n");
printf(" channel <n> Switch channel (while running)\n");
printf(" debug [on|off] Enable/disable debug logging (default: show status)\n");
printf(" filter [mac] Set MAC address filter for debug (e.g., 80:84:89:93:c4:b6), or 'clear' to disable\n");
printf(" save Save current config to NVS\n");
printf(" reload Reload config from NVS\n");
printf(" clear Clear NVS config\n");
@ -145,73 +139,6 @@ static int do_monitor_debug(int argc, char **argv) {
return 0;
}
static int do_monitor_filter(int argc, char **argv) {
filter_args.mac = arg_str0(NULL, NULL, "<mac|clear>", "MAC address (XX:XX:XX:XX:XX:XX) or 'clear' to disable filter");
filter_args.end = arg_end(1);
int nerrors = arg_parse(argc, argv, (void **)&filter_args);
if (nerrors > 0) {
arg_print_errors(stderr, filter_args.end, argv[0]);
return 1;
}
if (filter_args.mac->count > 0) {
const char *mac_str = filter_args.mac->sval[0];
if (strcmp(mac_str, "clear") == 0) {
wifi_ctl_set_monitor_debug_filter(NULL);
printf("Debug filter cleared\n");
} else {
/* Parse MAC address string (format: XX:XX:XX:XX:XX:XX) */
uint8_t mac[6];
int values[6];
int count = sscanf(mac_str, "%x:%x:%x:%x:%x:%x",
&values[0], &values[1], &values[2],
&values[3], &values[4], &values[5]);
if (count == 6) {
/* Validate values are in range */
bool valid = true;
for (int i = 0; i < 6; i++) {
if (values[i] < 0 || values[i] > 255) {
valid = false;
break;
}
mac[i] = (uint8_t)values[i];
}
if (valid) {
if (wifi_ctl_set_monitor_debug_filter(mac) == ESP_OK) {
printf("Debug filter set to: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
} else {
printf("Failed to set debug filter\n");
return 1;
}
} else {
printf("Invalid MAC address: values must be 0-255\n");
return 1;
}
} else {
printf("Invalid MAC address format. Use XX:XX:XX:XX:XX:XX or 'clear'\n");
return 1;
}
}
} else {
/* No argument: show current filter */
uint8_t mac[6];
bool enabled = wifi_ctl_get_monitor_debug_filter(mac);
if (enabled) {
printf("Debug filter: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
} else {
printf("Debug filter: disabled\n");
}
}
return 0;
}
static int cmd_monitor(int argc, char **argv) {
if (argc < 2) {
print_monitor_usage();
@ -227,7 +154,6 @@ static int cmd_monitor(int argc, char **argv) {
if (strcmp(argv[1], "channel") == 0) return do_monitor_channel(argc - 1, &argv[1]);
if (strcmp(argv[1], "debug") == 0) return do_monitor_debug(argc - 1, &argv[1]);
if (strcmp(argv[1], "filter") == 0) return do_monitor_filter(argc - 1, &argv[1]);
if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0) {
print_monitor_usage();
@ -249,9 +175,6 @@ void register_monitor_cmd(void) {
debug_args.enable = arg_str0(NULL, NULL, "<on|off>", "Enable or disable debug logging");
debug_args.end = arg_end(1);
filter_args.mac = arg_str0(NULL, NULL, "<mac|clear>", "MAC address filter or 'clear'");
filter_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "monitor",
.help = "Monitor Mode: start, stop, channel, status",

View File

@ -673,85 +673,6 @@ void wifi_ctl_status(void) {
}
printf(" Frames: %lu, %.1f fps\n", (unsigned long)s_monitor_frame_count, frame_rate_fps);
/* Show frame type breakdown */
wifi_collapse_stats_t monitor_stats;
if (wifi_monitor_get_stats(&monitor_stats) == ESP_OK) {
printf(" Frame Types: Data=%lu, Mgmt=%lu, Ctrl=%lu (RTS=%lu, CTS=%lu, ACK=%lu)\n",
(unsigned long)monitor_stats.data_frames,
(unsigned long)monitor_stats.mgmt_frames,
(unsigned long)(monitor_stats.rts_frames + monitor_stats.cts_frames + monitor_stats.ack_frames),
(unsigned long)monitor_stats.rts_frames,
(unsigned long)monitor_stats.cts_frames,
(unsigned long)monitor_stats.ack_frames);
/* Show histograms */
// AMPDU histogram
uint32_t total_ampdu = 0;
for (int i = 0; i < 8; i++) {
total_ampdu += monitor_stats.ampdu_hist[i];
}
if (total_ampdu > 0) {
printf(" AMPDU Aggregation: ");
const char *ampdu_labels[] = {"1", "2-4", "5-8", "9-16", "17-32", "33-48", "49-64", "65+"};
for (int i = 0; i < 8; i++) {
if (monitor_stats.ampdu_hist[i] > 0) {
printf("%s=%lu ", ampdu_labels[i], (unsigned long)monitor_stats.ampdu_hist[i]);
}
}
printf("\n");
}
// MCS histogram (show non-zero entries)
uint32_t total_mcs = 0;
for (int i = 0; i < 32; i++) {
total_mcs += monitor_stats.mcs_hist[i];
}
if (total_mcs > 0) {
printf(" MCS Distribution: ");
int printed = 0;
for (int i = 0; i < 32; i++) {
if (monitor_stats.mcs_hist[i] > 0) {
if (printed > 0) printf(", ");
printf("MCS%d=%lu", i, (unsigned long)monitor_stats.mcs_hist[i]);
printed++;
if (printed >= 10) { // Limit output length
printf(" ...");
break;
}
}
}
printf("\n");
}
// Spatial streams histogram
uint32_t total_ss = 0;
for (int i = 0; i < 8; i++) {
total_ss += monitor_stats.ss_hist[i];
}
if (total_ss > 0) {
printf(" Spatial Streams: ");
for (int i = 0; i < 8; i++) {
if (monitor_stats.ss_hist[i] > 0) {
printf("%dSS=%lu ", i + 1, (unsigned long)monitor_stats.ss_hist[i]);
}
}
printf("\n");
}
}
/* Show debug and filter status */
bool debug_enabled = wifi_ctl_get_monitor_debug();
uint8_t filter_mac[6];
bool filter_enabled = wifi_ctl_get_monitor_debug_filter(filter_mac);
printf(" Debug: %s\n", debug_enabled ? "enabled" : "disabled");
if (filter_enabled) {
printf(" Filter: %02x:%02x:%02x:%02x:%02x:%02x\n",
filter_mac[0], filter_mac[1], filter_mac[2],
filter_mac[3], filter_mac[4], filter_mac[5]);
} else {
printf(" Filter: disabled (showing all frames)\n");
}
/* Show telemetry rate statistics */
uint64_t gen_bytes = 0, write_bytes = 0;
double gen_rate = 0.0, write_rate = 0.0;
@ -903,14 +824,6 @@ bool wifi_ctl_get_monitor_debug(void) {
return s_monitor_debug;
}
esp_err_t wifi_ctl_set_monitor_debug_filter(const uint8_t *mac) {
return wifi_monitor_set_debug_filter(mac);
}
bool wifi_ctl_get_monitor_debug_filter(uint8_t *mac_out) {
return wifi_monitor_get_debug_filter(mac_out);
}
// --- Deprecated ---
static void auto_monitor_task_func(void *arg) {
uint8_t channel = (uint8_t)(uintptr_t)arg;

View File

@ -82,10 +82,6 @@ esp_err_t wifi_ctl_get_sd_write_stats(uint64_t *total_bytes, double *rate_bps);
void wifi_ctl_set_monitor_debug(bool enable);
bool wifi_ctl_get_monitor_debug(void);
// Debug MAC filter control
esp_err_t wifi_ctl_set_monitor_debug_filter(const uint8_t *mac);
bool wifi_ctl_get_monitor_debug_filter(uint8_t *mac_out);
// Deprecated / Compatibility
void wifi_ctl_auto_monitor_start(uint8_t channel);

View File

@ -1,5 +1,5 @@
idf_component_register(
SRCS "wifi_monitor.c"
INCLUDE_DIRS "."
REQUIRES esp_wifi nvs_flash esp_timer
REQUIRES esp_wifi nvs_flash
)

View File

@ -34,7 +34,6 @@
#include "wifi_monitor.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_timer.h"
#include "string.h"
static const char *TAG = "WiFi_Monitor";
@ -58,12 +57,9 @@ uint32_t threshold_duration_multiplier = 2; // NAV > expected * this = mism
uint32_t log_every_n_mismatches = 1; // Log every Nth mismatch (1 = all, 10 = every 10th)
static uint32_t s_mismatch_log_counter = 0;
static bool s_monitor_debug = false; // Debug mode: enable serial logging
static uint8_t s_monitor_debug_filter_mac[6] = {0}; // MAC address filter (all zeros = no filter)
static bool s_monitor_debug_filter_enabled = false; // Whether MAC filtering is enabled
// Forward declarations
static void wifi_promiscuous_rx_cb(void *buf, wifi_promiscuous_pkt_type_t type);
static bool wifi_monitor_debug_filter_match(const uint8_t *mac);
/**
* @brief Parse HT/VHT/HE headers to extract PHY parameters
@ -107,11 +103,10 @@ static void wifi_parse_phy_headers(const uint8_t *payload, uint16_t len, uint16_
uint8_t ctrl_id = ht_ctrl & 0x0F;
if (ctrl_id == 0) {
// HT Control (802.11n) - MCS info is typically in PLCP header (HT-SIG)
// which ESP-IDF provides in rx_ctrl->rate, not in HT Control field
// HT Control (802.11n) - MCS info might be elsewhere
frame_info->sig_mode = 1; // HT
// Note: MCS will be extracted from rx_ctrl->rate in the callback
// For HT, spatial streams can be inferred from MCS index (MCS 0-7 = 1 SS, 8-15 = 2 SS, etc.)
// Note: For HT, spatial streams are typically in HT Capabilities element
// or can be inferred from MCS index (MCS 0-7 = 1 SS, 8-15 = 2 SS, etc.)
} else if (ctrl_id >= 1 && ctrl_id <= 3) {
// VHT Control (802.11ac) - bits layout varies by variant
frame_info->sig_mode = 3; // VHT
@ -152,101 +147,6 @@ static void wifi_parse_phy_headers(const uint8_t *payload, uint16_t len, uint16_
}
}
/**
* @brief Parse A-MPDU aggregation count from frame payload
* @param payload Raw frame payload
* @param len Total frame length
* @param mac_hdr_len Length of MAC header (24 or 30 bytes)
* @return Number of aggregated MPDUs (1 if not aggregated)
*/
static uint8_t wifi_parse_ampdu_count(const uint8_t *payload, uint16_t len, uint16_t mac_hdr_len) {
uint8_t count = 1; // Default: not aggregated
uint16_t offset = mac_hdr_len;
// Check if this is a QoS data frame (required for A-MPDU)
uint8_t frame_type = (payload[0] >> 2) & 0x03;
uint8_t frame_subtype = (payload[0] >> 4) & 0x0F;
if (frame_type != FRAME_TYPE_DATA) {
return count; // Not a data frame
}
// Check if it's a QoS subtype
bool is_qos = (frame_subtype == DATA_QOS_DATA ||
frame_subtype == DATA_QOS_DATA_CF_ACK ||
frame_subtype == DATA_QOS_DATA_CF_POLL ||
frame_subtype == DATA_QOS_DATA_CF_ACK_POLL ||
frame_subtype == DATA_QOS_NULL ||
frame_subtype == DATA_QOS_CF_POLL ||
frame_subtype == DATA_QOS_CF_ACK_POLL);
if (!is_qos || len < offset + 4) {
return count; // Not QoS or too short
}
// Skip QoS Control field (4 bytes)
offset += 4;
// Check for HT Control field (4 bytes) - present if Order bit set
// ORDER bit is 0x8000 (bit 15), which is in payload[1], bit 7
bool has_ht_ctrl = (payload[1] & 0x80) != 0;
if (has_ht_ctrl && len >= offset + 4) {
offset += 4; // Skip HT Control field
}
// A-MPDU delimiter format (IEEE 802.11-2016):
// Each delimiter is 4 bytes: [Reserved(4) | MPDU Length(12) | CRC(8) | Delimiter Signature(8)]
// Delimiter signature is 0x4E (ASCII 'N')
// We count delimiters until we find the end delimiter (length = 0) or run out of data
uint16_t remaining = len - offset;
if (remaining < 4) {
return count; // Not enough data for even one delimiter
}
// Count A-MPDU subframes by parsing delimiters
uint16_t pos = offset;
uint16_t delimiter_count = 0; // Use uint16_t to support up to 256 delimiters
while (pos + 4 <= len && delimiter_count < 256) { // Support up to 256 for HE (though typically 64)
// Read delimiter (little-endian)
uint16_t delimiter = payload[pos] | (payload[pos + 1] << 8);
uint8_t sig = payload[pos + 3];
// Check delimiter signature (should be 0x4E)
if (sig != 0x4E) {
break; // Not a valid delimiter
}
// Extract MPDU length (bits 4-15)
uint16_t mpdu_len = (delimiter >> 4) & 0x0FFF;
if (mpdu_len == 0) {
break; // End delimiter
}
delimiter_count++;
// Move to next delimiter (skip this MPDU)
pos += 4 + mpdu_len; // Delimiter (4) + MPDU length
// Align to 4-byte boundary
pos = (pos + 3) & ~3;
if (pos >= len) {
break; // Reached end of frame
}
}
// If we found multiple delimiters, this is an A-MPDU
// Cap count at 255 (max value for uint8_t return type)
if (delimiter_count > 1) {
count = (delimiter_count > 255) ? 255 : (uint8_t)delimiter_count;
}
return count;
}
/**
* @brief Parse 802.11 MAC header
*/
@ -305,9 +205,6 @@ esp_err_t wifi_parse_frame(const uint8_t *payload, uint16_t len, wifi_frame_info
// Parse HT/VHT/HE headers to extract PHY parameters
wifi_parse_phy_headers(payload, len, mac_hdr_len, frame_info);
// Parse A-MPDU aggregation count
frame_info->ampdu_count = wifi_parse_ampdu_count(payload, len, mac_hdr_len);
frame_info->frame_len = len;
return ESP_OK;
@ -339,29 +236,11 @@ static void wifi_promiscuous_rx_cb(void *buf, wifi_promiscuous_pkt_type_t type)
frame_info.rate = rx_ctrl->rate; // This is the rate index
// If MCS wasn't parsed from headers but we have HT/VHT/HE mode, try to extract from rate
// ESP-IDF encoding: rate >= 128 encodes MCS for HT/VHT/HE frames
// HT: MCS 0-31, VHT: MCS 0-9, HE: MCS 0-11
if (frame_info.sig_mode > 0 && frame_info.mcs == 0 && rx_ctrl->rate >= 128) {
uint8_t extracted_mcs = rx_ctrl->rate - 128;
// Cap MCS based on signal mode
if (frame_info.sig_mode == 1) {
// HT (802.11n): MCS 0-31
if (extracted_mcs > 31) extracted_mcs = 31;
frame_info.mcs = extracted_mcs;
} else if (frame_info.sig_mode == 3) {
// VHT (802.11ac): MCS 0-9
if (extracted_mcs > 9) extracted_mcs = 9;
frame_info.mcs = extracted_mcs;
} else if (frame_info.sig_mode == 4) {
// HE (802.11ax): MCS 0-11
if (extracted_mcs > 11) extracted_mcs = 11;
frame_info.mcs = extracted_mcs;
} else {
// Unknown mode, use extracted value but cap at 31 (max HT)
if (extracted_mcs > 31) extracted_mcs = 31;
frame_info.mcs = extracted_mcs;
}
// For HT/VHT/HE, rate >= 128 might encode MCS
// ESP-IDF encoding: rate 128+ might be MCS index
frame_info.mcs = rx_ctrl->rate - 128;
if (frame_info.mcs > 11) frame_info.mcs = 11; // Cap at max MCS
}
// Calculate PHY rate using parsed MCS/spatial streams if available
@ -438,23 +317,20 @@ static void wifi_promiscuous_rx_cb(void *buf, wifi_promiscuous_pkt_type_t type)
if (frame_info.duration_id > threshold_duration_mismatch_us) {
s_mismatch_log_counter++;
if (s_monitor_debug && (s_mismatch_log_counter % log_every_n_mismatches) == 0) {
/* Check MAC filter before logging */
if (wifi_monitor_debug_filter_match(frame_info.addr2)) {
ESP_LOGW("MONITOR", "Duration mismatch: %s frame, %u bytes @ %u Mbps",
wifi_frame_type_str(frame_info.type, frame_info.subtype),
frame_info.frame_len, phy_rate_mbps);
ESP_LOGW("MONITOR", "Duration mismatch: %s frame, %u bytes @ %u Mbps",
wifi_frame_type_str(frame_info.type, frame_info.subtype),
frame_info.frame_len, phy_rate_mbps);
// NEW: Log the Source MAC (Addr2)
ESP_LOGW("MONITOR", " Source MAC: %02x:%02x:%02x:%02x:%02x:%02x",
frame_info.addr2[0], frame_info.addr2[1], frame_info.addr2[2],
frame_info.addr2[3], frame_info.addr2[4], frame_info.addr2[5]);
// NEW: Log the Source MAC (Addr2)
ESP_LOGW("MONITOR", " Source MAC: %02x:%02x:%02x:%02x:%02x:%02x",
frame_info.addr2[0], frame_info.addr2[1], frame_info.addr2[2],
frame_info.addr2[3], frame_info.addr2[4], frame_info.addr2[5]);
ESP_LOGW("MONITOR", " Expected: %lu us, Actual NAV: %u us (+%ld us)",
expected_duration, frame_info.duration_id,
frame_info.duration_id - expected_duration);
ESP_LOGW("MONITOR", " Retry: %s, RSSI: %d dBm",
frame_info.retry ? "YES" : "no", frame_info.rssi);
}
ESP_LOGW("MONITOR", " Expected: %lu us, Actual NAV: %u us (+%ld us)",
expected_duration, frame_info.duration_id,
frame_info.duration_id - expected_duration);
ESP_LOGW("MONITOR", " Retry: %s, RSSI: %d dBm",
frame_info.retry ? "YES" : "no", frame_info.rssi);
}
}
}
@ -465,21 +341,18 @@ static void wifi_promiscuous_rx_cb(void *buf, wifi_promiscuous_pkt_type_t type)
if (frame_info.retry && frame_info.duration_id > threshold_high_nav_us &&
phy_rate_mbps < threshold_phy_rate_fallback_mbps) {
if (s_monitor_debug) {
/* Check MAC filter before logging */
if (wifi_monitor_debug_filter_match(frame_info.addr2)) {
ESP_LOGW("MONITOR", "⚠⚠⚠ COLLISION DETECTED!");
ESP_LOGW("MONITOR", "⚠⚠⚠ COLLISION DETECTED!");
// NEW: Log the Attacker MAC
ESP_LOGW("MONITOR", " Attacker MAC: %02x:%02x:%02x:%02x:%02x:%02x",
frame_info.addr2[0], frame_info.addr2[1], frame_info.addr2[2],
frame_info.addr2[3], frame_info.addr2[4], frame_info.addr2[5]);
// NEW: Log the Attacker MAC
ESP_LOGW("MONITOR", " Attacker MAC: %02x:%02x:%02x:%02x:%02x:%02x",
frame_info.addr2[0], frame_info.addr2[1], frame_info.addr2[2],
frame_info.addr2[3], frame_info.addr2[4], frame_info.addr2[5]);
ESP_LOGW("MONITOR", " Type: %s, Size: %u bytes, Rate: %u Mbps",
wifi_frame_type_str(frame_info.type, frame_info.subtype),
frame_info.frame_len, phy_rate_mbps);
ESP_LOGW("MONITOR", " NAV: %u us (expected %lu us), Retry: YES",
frame_info.duration_id, expected_duration);
}
ESP_LOGW("MONITOR", " Type: %s, Size: %u bytes, Rate: %u Mbps",
wifi_frame_type_str(frame_info.type, frame_info.subtype),
frame_info.frame_len, phy_rate_mbps);
ESP_LOGW("MONITOR", " NAV: %u us (expected %lu us), Retry: YES",
frame_info.duration_id, expected_duration);
}
}
}
@ -502,94 +375,6 @@ static void wifi_promiscuous_rx_cb(void *buf, wifi_promiscuous_pkt_type_t type)
case FRAME_TYPE_DATA:
stats.data_frames++;
/* Periodic diagnostic summary (even when debug is off) */
static uint32_t data_frame_diag_counter = 0;
static uint64_t last_diag_log_ms = 0;
data_frame_diag_counter++;
uint64_t now_ms = esp_timer_get_time() / 1000;
if (now_ms - last_diag_log_ms > 10000) { /* Log every 10 seconds */
ESP_LOGI("MONITOR", "Data frames: %lu total, last TA=%02x:%02x:%02x:%02x:%02x:%02x, "
"debug=%s, filter=%s",
(unsigned long)stats.data_frames,
frame_info.addr2[0], frame_info.addr2[1], frame_info.addr2[2],
frame_info.addr2[3], frame_info.addr2[4], frame_info.addr2[5],
s_monitor_debug ? "on" : "off",
s_monitor_debug_filter_enabled ? "on" : "off");
data_frame_diag_counter = 0;
last_diag_log_ms = now_ms;
}
/* Update histograms for data frames */
// AMPDU histogram: [1, 2-4, 5-8, 9-16, 17-32, 33-48, 49-64, 65+]
uint8_t ampdu_count = frame_info.ampdu_count;
if (ampdu_count == 1) {
stats.ampdu_hist[0]++;
} else if (ampdu_count >= 2 && ampdu_count <= 4) {
stats.ampdu_hist[1]++;
} else if (ampdu_count >= 5 && ampdu_count <= 8) {
stats.ampdu_hist[2]++;
} else if (ampdu_count >= 9 && ampdu_count <= 16) {
stats.ampdu_hist[3]++;
} else if (ampdu_count >= 17 && ampdu_count <= 32) {
stats.ampdu_hist[4]++;
} else if (ampdu_count >= 33 && ampdu_count <= 48) {
stats.ampdu_hist[5]++;
} else if (ampdu_count >= 49 && ampdu_count <= 64) {
stats.ampdu_hist[6]++;
} else if (ampdu_count >= 65) {
stats.ampdu_hist[7]++;
}
// MCS histogram: [0-31]
if (frame_info.mcs < 32) {
stats.mcs_hist[frame_info.mcs]++;
}
// Spatial streams histogram: [1-8]
uint8_t ss = frame_info.spatial_streams;
if (ss >= 1 && ss <= 8) {
stats.ss_hist[ss - 1]++; // Convert to 0-indexed
}
/* Debug logging for data frames (especially QoS data used by iperf) */
if (s_monitor_debug) {
bool filter_match = wifi_monitor_debug_filter_match(frame_info.addr2);
uint16_t phy_rate_mbps = frame_info.phy_rate_kbps / 1000;
const char *frame_type_name = wifi_frame_type_str(frame_info.type, frame_info.subtype);
if (filter_match) {
/* Log all data frames (QoS and non-QoS) when filter matches or filter is disabled */
ESP_LOGI("MONITOR", "DATA: %s, TA=%02x:%02x:%02x:%02x:%02x:%02x, RA=%02x:%02x:%02x:%02x:%02x:%02x, "
"Size=%u bytes, Rate=%u Mbps, MCS=%u, SS=%u, BW=%u MHz, RSSI=%d dBm, Retry=%s",
frame_type_name,
frame_info.addr2[0], frame_info.addr2[1], frame_info.addr2[2],
frame_info.addr2[3], frame_info.addr2[4], frame_info.addr2[5],
frame_info.addr1[0], frame_info.addr1[1], frame_info.addr1[2],
frame_info.addr1[3], frame_info.addr1[4], frame_info.addr1[5],
frame_info.frame_len, phy_rate_mbps, frame_info.mcs,
frame_info.spatial_streams,
(frame_info.bandwidth == 0) ? 20 : (frame_info.bandwidth == 1) ? 40 : 80,
frame_info.rssi, frame_info.retry ? "YES" : "no");
} else {
/* Diagnostic: log when data frames are filtered out (throttled, but more visible) */
static uint32_t filtered_data_count = 0;
static uint64_t last_filtered_log_ms = 0;
static uint8_t last_filtered_ta[6] = {0};
filtered_data_count++;
uint64_t now_ms = esp_timer_get_time() / 1000;
memcpy(last_filtered_ta, frame_info.addr2, 6);
if (now_ms - last_filtered_log_ms > 5000) { /* Log every 5 seconds */
ESP_LOGW("MONITOR", "Filtered out %lu data frames (last TA=%02x:%02x:%02x:%02x:%02x:%02x, filter=%s)",
(unsigned long)filtered_data_count,
last_filtered_ta[0], last_filtered_ta[1], last_filtered_ta[2],
last_filtered_ta[3], last_filtered_ta[4], last_filtered_ta[5],
s_monitor_debug_filter_enabled ? "enabled" : "disabled");
filtered_data_count = 0;
last_filtered_log_ms = now_ms;
}
}
}
break;
}
@ -823,51 +608,3 @@ void wifi_monitor_set_debug(bool enable) {
bool wifi_monitor_get_debug(void) {
return s_monitor_debug;
}
/**
* @brief Check if a MAC address matches the debug filter
* @param mac MAC address to check (6 bytes)
* @return true if filter is disabled or MAC matches filter, false otherwise
*/
static bool wifi_monitor_debug_filter_match(const uint8_t *mac) {
if (!s_monitor_debug_filter_enabled || mac == NULL) {
return true; /* No filter or invalid MAC - allow all */
}
/* Compare MAC addresses byte by byte */
for (int i = 0; i < 6; i++) {
if (mac[i] != s_monitor_debug_filter_mac[i]) {
return false; /* MAC doesn't match filter */
}
}
return true; /* MAC matches filter */
}
/**
* @brief Set MAC address filter for debug logging
* @param mac MAC address to filter on (6 bytes), or NULL to disable filter
* @return ESP_OK on success, ESP_ERR_INVALID_ARG if mac is invalid
*/
esp_err_t wifi_monitor_set_debug_filter(const uint8_t *mac) {
if (mac == NULL) {
/* Disable filter */
s_monitor_debug_filter_enabled = false;
memset(s_monitor_debug_filter_mac, 0, 6);
return ESP_OK;
}
/* Enable filter and copy MAC address */
memcpy(s_monitor_debug_filter_mac, mac, 6);
s_monitor_debug_filter_enabled = true;
return ESP_OK;
}
/**
* @brief Get current MAC address filter for debug logging
* @param mac_out Buffer to store MAC address (6 bytes), or NULL to just check if filter is enabled
* @return true if filter is enabled, false otherwise
*/
bool wifi_monitor_get_debug_filter(uint8_t *mac_out) {
if (mac_out != NULL && s_monitor_debug_filter_enabled) {
memcpy(mac_out, s_monitor_debug_filter_mac, 6);
}
return s_monitor_debug_filter_enabled;
}

View File

@ -176,9 +176,6 @@ typedef struct {
uint8_t bandwidth; // 0=20MHz, 1=40MHz, 2=80MHz, 3=160MHz
uint32_t phy_rate_kbps; // Calculated PHY rate in Kbps (uint32_t to handle >65 Mbps)
// Aggregation info
uint8_t ampdu_count; // Number of aggregated MPDUs in A-MPDU (1 = not aggregated)
// Frame size
uint16_t frame_len;
} wifi_frame_info_t;
@ -208,11 +205,6 @@ typedef struct {
uint16_t avg_phy_rate_mbps; // Average PHY rate
uint16_t min_phy_rate_mbps; // Minimum PHY rate seen
uint16_t max_phy_rate_mbps; // Maximum PHY rate seen
// Histograms
uint32_t ampdu_hist[8]; // AMPDU aggregation: [1, 2-4, 5-8, 9-16, 17-32, 33-48, 49-64, 65+]
uint32_t mcs_hist[32]; // MCS index: [0-31]
uint32_t ss_hist[8]; // Spatial streams: [1-8]
} wifi_collapse_stats_t;
/**
@ -308,20 +300,6 @@ void wifi_monitor_set_debug(bool enable);
*/
bool wifi_monitor_get_debug(void);
/**
* @brief Set MAC address filter for debug logging
* @param mac MAC address to filter on (6 bytes), or NULL to disable filter
* @return ESP_OK on success, ESP_ERR_INVALID_ARG if mac is invalid
*/
esp_err_t wifi_monitor_set_debug_filter(const uint8_t *mac);
/**
* @brief Get current MAC address filter for debug logging
* @param mac_out Buffer to store MAC address (6 bytes), or NULL to just check if filter is enabled
* @return true if filter is enabled, false otherwise
*/
bool wifi_monitor_get_debug_filter(uint8_t *mac_out);
#ifdef __cplusplus
}
#endif

View File

@ -1,101 +0,0 @@
#!/bin/bash
# Raspberry Pi 5 WiFi Monitor Mode - Capture RA/TA Addresses
# This script sets up monitor mode and captures 802.11 frames showing RA and TA
set -e
WIFI_INTERFACE="wlan0"
CHANNEL="${1:-11}" # Default to channel 11, or pass as argument
FILTER_MAC="${2:-}" # Optional: filter by MAC address (e.g., 80:84:89:93:c4:b6)
echo "=== Raspberry Pi 5 WiFi Monitor - RA/TA Capture ==="
echo "Interface: $WIFI_INTERFACE"
echo "Channel: $CHANNEL"
if [ -n "$FILTER_MAC" ]; then
echo "Filter: $FILTER_MAC"
fi
echo ""
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root (use sudo)"
exit 1
fi
# Check if interface exists
if ! ip link show "$WIFI_INTERFACE" &>/dev/null; then
echo "Error: Interface $WIFI_INTERFACE not found"
exit 1
fi
# Check current mode
CURRENT_MODE=$(iw dev "$WIFI_INTERFACE" info 2>/dev/null | grep "type" | awk '{print $2}' || echo "unknown")
if [ "$CURRENT_MODE" != "monitor" ]; then
echo "Setting $WIFI_INTERFACE to monitor mode..."
ip link set "$WIFI_INTERFACE" down
iw dev "$WIFI_INTERFACE" set type monitor
ip link set "$WIFI_INTERFACE" up
iw dev "$WIFI_INTERFACE" set channel "$CHANNEL"
echo "Monitor mode activated on channel $CHANNEL"
else
echo "Already in monitor mode, setting channel to $CHANNEL..."
iw dev "$WIFI_INTERFACE" set channel "$CHANNEL"
fi
echo ""
echo "=== Starting Capture (showing RA/TA addresses) ==="
echo "Press Ctrl+C to stop"
echo ""
# Build tcpdump filter
TCPDUMP_FILTER=""
if [ -n "$FILTER_MAC" ]; then
# Remove colons from MAC for tcpdump
MAC_CLEAN=$(echo "$FILTER_MAC" | tr -d ':')
TCPDUMP_FILTER="ether host $FILTER_MAC"
fi
# Use tcpdump with verbose output to show MAC addresses
# -e shows link-level headers (includes MAC addresses)
# -n prevents DNS resolution
# -v increases verbosity
if [ -n "$TCPDUMP_FILTER" ]; then
tcpdump -i "$WIFI_INTERFACE" -e -n -v "$TCPDUMP_FILTER" 2>&1 | \
grep -E "(ether|RA|TA|SA|DA|BSSID)" | \
awk '
{
# Extract MAC addresses from tcpdump output
# tcpdump shows: ether src/dst MAC
if (match($0, /ether (src|dst) ([0-9a-f:]{17})/, arr)) {
direction = arr[1]
mac = arr[2]
print "[" direction "] " mac
}
# Also show full frame info
print $0
}'
else
# Show all frames with MAC address extraction
tcpdump -i "$WIFI_INTERFACE" -e -n -v 2>&1 | \
while IFS= read -r line; do
# Extract and highlight MAC addresses
if echo "$line" | grep -q "ether"; then
# Extract source MAC (TA in 802.11)
if echo "$line" | grep -q "ether src"; then
TA=$(echo "$line" | grep -oP 'ether src \K[0-9a-f:]{17}' || echo "")
if [ -n "$TA" ]; then
echo "TA (Transmitter): $TA"
fi
fi
# Extract destination MAC (RA in 802.11)
if echo "$line" | grep -q "ether dst"; then
RA=$(echo "$line" | grep -oP 'ether dst \K[0-9a-f:]{17}' || echo "")
if [ -n "$RA" ]; then
echo "RA (Receiver): $RA"
fi
fi
fi
echo "$line"
done
fi

View File

@ -1,307 +0,0 @@
#!/usr/bin/env python3
"""
Raspberry Pi 5 WiFi Monitor - Capture RA/TA Addresses
Uses scapy to parse 802.11 frames and display RA/TA addresses clearly
Requirements:
sudo apt-get install python3-pip
sudo pip3 install scapy
Usage:
sudo python3 rpi_capture_ra_ta_python.py [channel] [filter_mac]
Example:
sudo python3 rpi_capture_ra_ta_python.py 11
sudo python3 rpi_capture_ra_ta_python.py 11 80:84:89:93:c4:b6
"""
import sys
import os
import subprocess
import signal
from datetime import datetime
from scapy.all import *
from scapy.layers.dot11 import Dot11, Dot11QoS
# Configuration
WIFI_INTERFACE = "wlan0"
CHANNEL = int(sys.argv[1]) if len(sys.argv) > 1 else 11
FILTER_MAC = sys.argv[2] if len(sys.argv) > 2 else None
def setup_monitor_mode():
"""Set WiFi interface to monitor mode"""
print(f"=== Setting up monitor mode on {WIFI_INTERFACE} ===")
# Check current mode
try:
result = subprocess.run(
["iw", "dev", WIFI_INTERFACE, "info"],
capture_output=True,
text=True,
check=True
)
if "type monitor" in result.stdout:
print(f"Already in monitor mode")
else:
print(f"Setting {WIFI_INTERFACE} to monitor mode...")
subprocess.run(["ip", "link", "set", WIFI_INTERFACE, "down"], check=True)
subprocess.run(["iw", "dev", WIFI_INTERFACE, "set", "type", "monitor"], check=True)
subprocess.run(["ip", "link", "set", WIFI_INTERFACE, "up"], check=True)
print("Monitor mode activated")
except subprocess.CalledProcessError as e:
print(f"Error setting monitor mode: {e}")
sys.exit(1)
# Set channel
try:
subprocess.run(["iw", "dev", WIFI_INTERFACE, "set", "channel", str(CHANNEL)], check=True)
print(f"Channel set to {CHANNEL}")
except subprocess.CalledProcessError as e:
print(f"Error setting channel: {e}")
sys.exit(1)
def get_frame_type_name(dot11):
"""Get human-readable frame type name"""
type_names = {
0: { # Management
0: "Association Request",
1: "Association Response",
2: "Reassociation Request",
3: "Reassociation Response",
4: "Probe Request",
5: "Probe Response",
8: "Beacon",
10: "Disassociation",
11: "Authentication",
12: "Deauthentication"
},
1: { # Control
10: "RTS",
11: "CTS",
12: "ACK",
13: "CF-End",
14: "CF-End+CF-Ack"
},
2: { # Data
0: "Data",
1: "Data+CF-Ack",
2: "Data+CF-Poll",
3: "Data+CF-Ack+CF-Poll",
4: "Null",
8: "QoS Data",
9: "QoS Data+CF-Ack",
10: "QoS Data+CF-Poll",
11: "QoS Data+CF-Ack+CF-Poll",
12: "QoS Null"
}
}
return type_names.get(dot11.type, {}).get(dot11.subtype,
f"Type{dot11.type}/Subtype{dot11.subtype}")
def parse_80211_frame(pkt):
"""Parse 802.11 frame and extract RA/TA addresses"""
if not pkt.haslayer(Dot11):
return None
dot11 = pkt[Dot11]
# 802.11 addressing:
# For Data frames (To DS=1, From DS=1):
# addr1 = RA (Receiver Address) = Next hop destination
# addr2 = TA (Transmitter Address) = Transmitting station
# addr3 = DA (Destination Address) = Final destination
# addr4 = SA (Source Address) = Original source
# For Data frames (To DS=0, From DS=1): AP to STA
# addr1 = RA = DA (Destination STA)
# addr2 = TA = SA (Source AP)
# addr3 = BSSID
# For Data frames (To DS=1, From DS=0): STA to AP
# addr1 = RA = BSSID (AP)
# addr2 = TA = SA (Source STA)
# addr3 = DA (Destination)
frame_type = dot11.type
frame_subtype = dot11.subtype
# Get addresses
addr1 = dot11.addr1 if dot11.addr1 else "N/A"
addr2 = dot11.addr2 if dot11.addr2 else "N/A"
addr3 = dot11.addr3 if dot11.addr3 else "N/A"
addr4 = dot11.addr4 if hasattr(dot11, 'addr4') and dot11.addr4 else None
# Extract To DS and From DS flags
to_ds = dot11.FCfield & 0x1
from_ds = (dot11.FCfield >> 1) & 0x1
# Determine RA and TA based on frame type
if frame_type == 2: # Data frame
# For data frames:
# addr1 is always RA (receiver)
# addr2 is always TA (transmitter)
ra = addr1
ta = addr2
# Additional context based on To DS / From DS
if to_ds and from_ds:
# WDS frame: addr3=DA, addr4=SA
context = f"WDS: DA={addr3}, SA={addr4 if addr4 else 'N/A'}"
elif to_ds and not from_ds:
# STA to AP: addr3=DA
context = f"STA→AP: DA={addr3}"
elif not to_ds and from_ds:
# AP to STA: addr3=BSSID
context = f"AP→STA: BSSID={addr3}"
else:
# Ad-hoc: addr3=BSSID
context = f"Ad-hoc: BSSID={addr3}"
else:
# For management/control frames, addr1=DA, addr2=SA
ra = addr1 # Receiver/Destination
ta = addr2 # Transmitter/Source
context = f"BSSID={addr3}" if addr3 != "N/A" else ""
# Get RSSI if available
rssi = "N/A"
if hasattr(pkt, "dBm_AntSignal"):
rssi = f"{pkt.dBm_AntSignal} dBm"
elif hasattr(pkt, "notdecoded"):
# Try to extract from radiotap header if present
pass
# Check for QoS data
is_qos = pkt.haslayer(Dot11QoS)
qos_info = ""
if is_qos:
qos = pkt[Dot11QoS]
tid = qos.TID if hasattr(qos, 'TID') else "N/A"
qos_info = f", TID={tid}"
return {
"type": frame_type,
"subtype": frame_subtype,
"name": get_frame_type_name(dot11),
"ra": ra,
"ta": ta,
"bssid": addr3,
"context": context,
"rssi": rssi,
"len": len(pkt),
"qos": is_qos,
"qos_info": qos_info,
"retry": bool(dot11.FCfield & 0x8) if hasattr(dot11, 'FCfield') else False
}
# Statistics
stats = {
"total": 0,
"by_ta": {},
"by_ra": {},
"by_type": {}
}
def packet_handler(pkt):
"""Handle captured packets"""
frame_info = parse_80211_frame(pkt)
if not frame_info:
return
# Apply MAC filter if specified
if FILTER_MAC:
filter_mac_clean = FILTER_MAC.lower().replace(":", "").replace("-", "")
ta_clean = frame_info["ta"].replace(":", "").replace("-", "").lower() if frame_info["ta"] != "N/A" else ""
ra_clean = frame_info["ra"].replace(":", "").replace("-", "").lower() if frame_info["ra"] != "N/A" else ""
if filter_mac_clean.lower() not in ta_clean and filter_mac_clean.lower() not in ra_clean:
return # Skip this frame
# Update statistics
stats["total"] += 1
if frame_info["ta"] != "N/A":
stats["by_ta"][frame_info["ta"]] = stats["by_ta"].get(frame_info["ta"], 0) + 1
if frame_info["ra"] != "N/A":
stats["by_ra"][frame_info["ra"]] = stats["by_ra"].get(frame_info["ra"], 0) + 1
frame_type_key = f"{frame_info['name']}"
stats["by_type"][frame_type_key] = stats["by_type"].get(frame_type_key, 0) + 1
# Print frame information
timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
retry_str = " [RETRY]" if frame_info["retry"] else ""
print(f"\n[{timestamp}] {frame_info['name']}{retry_str}")
print(f" RA (Receiver): {frame_info['ra']}")
print(f" TA (Transmitter): {frame_info['ta']}")
if frame_info['bssid'] != "N/A":
print(f" BSSID: {frame_info['bssid']}")
if frame_info['context']:
print(f" Context: {frame_info['context']}")
print(f" RSSI: {frame_info['rssi']}")
print(f" Length: {frame_info['len']} bytes{frame_info['qos_info']}")
def signal_handler(sig, frame):
"""Handle Ctrl+C gracefully"""
print("\n\nStopping capture...")
sys.exit(0)
def main():
"""Main function"""
print(f"=== Raspberry Pi 5 WiFi Monitor - RA/TA Capture ===")
print(f"Interface: {WIFI_INTERFACE}")
print(f"Channel: {CHANNEL}")
if FILTER_MAC:
print(f"Filter: {FILTER_MAC}")
print("")
# Check if running as root
if os.geteuid() != 0:
print("Error: This script must be run as root (use sudo)")
sys.exit(1)
# Setup monitor mode
setup_monitor_mode()
# Register signal handler
signal.signal(signal.SIGINT, signal_handler)
print("\n=== Starting Capture (showing RA/TA addresses) ===")
print("Press Ctrl+C to stop\n")
# Build filter
bpf_filter = None
if FILTER_MAC:
bpf_filter = f"ether host {FILTER_MAC}"
# Start capturing
try:
sniff(
iface=WIFI_INTERFACE,
prn=packet_handler,
filter=bpf_filter,
store=False
)
except KeyboardInterrupt:
print("\n\n" + "="*60)
print("Capture stopped by user")
print("="*60)
print(f"\nStatistics:")
print(f" Total frames captured: {stats['total']}")
print(f"\n Top 5 TAs (Transmitters):")
sorted_tas = sorted(stats['by_ta'].items(), key=lambda x: x[1], reverse=True)[:5]
for ta, count in sorted_tas:
print(f" {ta}: {count} frames")
print(f"\n Top 5 RAs (Receivers):")
sorted_ras = sorted(stats['by_ra'].items(), key=lambda x: x[1], reverse=True)[:5]
for ra, count in sorted_ras:
print(f" {ra}: {count} frames")
print(f"\n Frame types:")
for ftype, count in sorted(stats['by_type'].items(), key=lambda x: x[1], reverse=True):
print(f" {ftype}: {count}")
except Exception as e:
print(f"\nError during capture: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -1,73 +0,0 @@
#!/bin/bash
# Raspberry Pi 5 WiFi Monitor Mode Setup Script
# Run this script with sudo on your Raspberry Pi
set -e
# Configuration
WIFI_INTERFACE="wlan0" # Change if your interface is different
MONITOR_INTERFACE="mon0" # Monitor mode interface name
CHANNEL="${1:-11}" # Default to channel 11, or pass as argument: ./rpi_monitor_setup.sh 36
echo "=== Raspberry Pi 5 WiFi Monitor Mode Setup ==="
echo "Interface: $WIFI_INTERFACE"
echo "Monitor interface: $MONITOR_INTERFACE"
echo "Channel: $CHANNEL"
echo ""
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root (use sudo)"
exit 1
fi
# Check if interface exists
if ! ip link show "$WIFI_INTERFACE" &>/dev/null; then
echo "Error: Interface $WIFI_INTERFACE not found"
echo "Available interfaces:"
ip link show | grep -E "^[0-9]+:" | awk '{print $2}' | sed 's/://'
exit 1
fi
# Check if monitor interface already exists
if ip link show "$MONITOR_INTERFACE" &>/dev/null; then
echo "Monitor interface $MONITOR_INTERFACE already exists. Removing..."
iw dev "$MONITOR_INTERFACE" del
fi
# Bring down the interface
echo "Bringing down $WIFI_INTERFACE..."
ip link set "$WIFI_INTERFACE" down
# Set monitor mode
echo "Setting $WIFI_INTERFACE to monitor mode..."
iw dev "$WIFI_INTERFACE" set type monitor
# Bring up the monitor interface
echo "Bringing up monitor interface..."
ip link set "$WIFI_INTERFACE" up
# Set channel
echo "Setting channel to $CHANNEL..."
iw dev "$WIFI_INTERFACE" set channel "$CHANNEL"
# Verify monitor mode
echo ""
echo "=== Verification ==="
iw dev "$WIFI_INTERFACE" info
echo ""
echo "=== Monitor mode is now active! ==="
echo "Interface: $WIFI_INTERFACE"
echo "Channel: $CHANNEL"
echo ""
echo "To capture packets, you can use:"
echo " sudo tcpdump -i $WIFI_INTERFACE -n"
echo " sudo tcpdump -i $WIFI_INTERFACE -w capture.pcap"
echo " sudo wireshark -i $WIFI_INTERFACE"
echo ""
echo "To stop monitor mode and restore normal WiFi:"
echo " sudo ip link set $WIFI_INTERFACE down"
echo " sudo iw dev $WIFI_INTERFACE set type managed"
echo " sudo ip link set $WIFI_INTERFACE up"
echo " # Then reconnect to your network"