Initial commit: Linux wireless monitor for ESP32 verification
A C program using GNU Automake for capturing and parsing 802.11 WiFi frame headers in monitor mode. Designed to verify ESP32 monitor code by comparing Linux vs ESP32 frame parsing. Features: - Monitor mode setup using libnl3 - Packet capture using libpcap - 802.11 frame parsing (RA, TA, duration, retry flag) - MAC address filtering (matches ESP32 filter behavior) - Output format matches ESP32 debug logs for easy comparison Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
commit
38fbce9061
|
|
@ -0,0 +1,41 @@
|
|||
# Autotools generated files
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache/
|
||||
config.h
|
||||
config.h.in
|
||||
config.log
|
||||
config.status
|
||||
configure
|
||||
stamp-h1
|
||||
.deps/
|
||||
.dirstamp
|
||||
|
||||
# Compiled files
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.a
|
||||
*.so
|
||||
*.so.*
|
||||
|
||||
# Executables
|
||||
wireless_monitor
|
||||
src/wireless_monitor
|
||||
|
||||
# Distribution files
|
||||
*.tar.gz
|
||||
*.tar.bz2
|
||||
*.zip
|
||||
|
||||
# Editor files
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Build directories
|
||||
build/
|
||||
*.dSYM/
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
# Building on Raspberry Pi 5
|
||||
|
||||
This guide covers building the wireless monitor tool on Raspberry Pi 5 running Raspberry Pi OS (Debian-based).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Install Build Tools and Dependencies
|
||||
|
||||
```bash
|
||||
# Update package list
|
||||
sudo apt-get update
|
||||
|
||||
# Install build essentials and autotools
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
autoconf \
|
||||
automake \
|
||||
libtool \
|
||||
pkg-config
|
||||
|
||||
# Install WiFi monitoring libraries
|
||||
sudo apt-get install -y \
|
||||
libpcap-dev \
|
||||
libnl-genl-3-dev \
|
||||
libnl-3-dev
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
cd wireless-monitor-template
|
||||
|
||||
# Generate configure script
|
||||
./autogen.sh
|
||||
|
||||
# Configure build
|
||||
./configure
|
||||
|
||||
# Build
|
||||
make
|
||||
|
||||
# Test (as root - required for monitor mode)
|
||||
sudo ./src/wireless_monitor wlan0 11
|
||||
```
|
||||
|
||||
## Installation (Optional)
|
||||
|
||||
```bash
|
||||
# Install system-wide
|
||||
sudo make install
|
||||
|
||||
# Then run from anywhere
|
||||
sudo wireless_monitor wlan0 11
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Missing autotools
|
||||
|
||||
If `autogen.sh` fails:
|
||||
```bash
|
||||
sudo apt-get install autoconf automake libtool
|
||||
```
|
||||
|
||||
### Missing pkg-config
|
||||
|
||||
If configure fails to find libraries:
|
||||
```bash
|
||||
sudo apt-get install pkg-config
|
||||
```
|
||||
|
||||
### Library Not Found
|
||||
|
||||
If you get linker errors:
|
||||
```bash
|
||||
# Verify libraries are installed
|
||||
pkg-config --exists libpcap && echo "libpcap OK" || echo "libpcap missing"
|
||||
pkg-config --exists libnl-genl-3.0 && echo "libnl-genl OK" || echo "libnl-genl missing"
|
||||
pkg-config --exists libnl-3.0 && echo "libnl-3 OK" || echo "libnl-3 missing"
|
||||
```
|
||||
|
||||
### Monitor Mode Permission Denied
|
||||
|
||||
Must run as root:
|
||||
```bash
|
||||
sudo ./src/wireless_monitor wlan0 11
|
||||
```
|
||||
|
||||
## Cross-Compilation (from Linux PC)
|
||||
|
||||
If you want to cross-compile from a Linux PC:
|
||||
|
||||
```bash
|
||||
# Install cross-compiler
|
||||
sudo apt-get install gcc-aarch64-linux-gnu
|
||||
|
||||
# Configure for cross-compilation
|
||||
./autogen.sh
|
||||
./configure --host=aarch64-linux-gnu \
|
||||
PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
|
||||
|
||||
make
|
||||
```
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
SUBDIRS = src
|
||||
|
||||
dist_doc_DATA = README.md
|
||||
|
||||
EXTRA_DIST = autogen.sh
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
# Push to Umber Git Repository
|
||||
|
||||
## Current Status
|
||||
|
||||
✅ Git repository initialized
|
||||
✅ All files staged
|
||||
✅ Ready to commit and push
|
||||
|
||||
## Steps to Push
|
||||
|
||||
### 1. Create Repository on Umber Git Server
|
||||
|
||||
First, create a new repository on your Umber Git server (Gitea/GitLab/etc.):
|
||||
|
||||
- Repository name: `wireless-monitor` (or your preferred name)
|
||||
- Visibility: Private or Public (your choice)
|
||||
- **Do NOT** initialize with README (we already have one)
|
||||
|
||||
### 2. Commit Locally
|
||||
|
||||
```bash
|
||||
cd /home/rjmcmahon/Code/esp32/esp32-iperf-shell/wireless-monitor-template
|
||||
|
||||
# Create initial commit
|
||||
git commit -m "Initial commit: Linux wireless monitor for ESP32 verification
|
||||
|
||||
A C program using GNU Automake for capturing and parsing 802.11 WiFi
|
||||
frame headers in monitor mode. Designed to verify ESP32 monitor code
|
||||
by comparing Linux vs ESP32 frame parsing.
|
||||
|
||||
Features:
|
||||
- Monitor mode setup using libnl3
|
||||
- Packet capture using libpcap
|
||||
- 802.11 frame parsing (RA, TA, duration, retry flag)
|
||||
- MAC address filtering (matches ESP32 filter behavior)
|
||||
- Output format matches ESP32 debug logs for easy comparison"
|
||||
```
|
||||
|
||||
### 3. Add Remote Repository
|
||||
|
||||
Replace `YOUR_REPO_URL` with your actual Umber Git repository URL:
|
||||
|
||||
```bash
|
||||
# Example URLs:
|
||||
# https://git.umber.com/rjmcmahon/wireless-monitor.git
|
||||
# git@git.umber.com:rjmcmahon/wireless-monitor.git
|
||||
|
||||
git remote add origin YOUR_REPO_URL
|
||||
|
||||
# Verify remote
|
||||
git remote -v
|
||||
```
|
||||
|
||||
### 4. Push to Repository
|
||||
|
||||
```bash
|
||||
# Set default branch name (if needed)
|
||||
git branch -M main
|
||||
|
||||
# Push to repository
|
||||
git push -u origin main
|
||||
|
||||
# If your default branch is 'master' instead:
|
||||
# git branch -M master
|
||||
# git push -u origin master
|
||||
```
|
||||
|
||||
## Building on Raspberry Pi 5 After Clone
|
||||
|
||||
Once pushed, you can clone and build on Raspberry Pi 5:
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://git.umber.com/rjmcmahon/wireless-monitor.git
|
||||
cd wireless-monitor
|
||||
|
||||
# Build (automated)
|
||||
./build_pi5.sh
|
||||
|
||||
# Or manually
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
|
||||
# Run
|
||||
sudo ./src/wireless_monitor wlan0 11
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication Issues
|
||||
|
||||
If you get authentication errors:
|
||||
|
||||
```bash
|
||||
# For HTTPS (will prompt for username/password)
|
||||
git remote set-url origin https://git.umber.com/rjmcmahon/wireless-monitor.git
|
||||
|
||||
# For SSH (requires SSH key setup)
|
||||
git remote set-url origin git@git.umber.com:rjmcmahon/wireless-monitor.git
|
||||
```
|
||||
|
||||
### Branch Name Mismatch
|
||||
|
||||
If you get "branch name mismatch" error:
|
||||
|
||||
```bash
|
||||
# Check what branch you're on
|
||||
git branch
|
||||
|
||||
# Rename if needed
|
||||
git branch -M main # or 'master'
|
||||
```
|
||||
|
||||
### Push Rejected
|
||||
|
||||
If push is rejected because remote has content:
|
||||
|
||||
```bash
|
||||
# Pull first (if remote has initial commit)
|
||||
git pull origin main --allow-unrelated-histories
|
||||
|
||||
# Then push
|
||||
git push -u origin main
|
||||
```
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# Quick Start: Raspberry Pi 5
|
||||
|
||||
## 1. Clone Repository
|
||||
|
||||
```bash
|
||||
# On Raspberry Pi 5
|
||||
git clone https://git.umber.com/rjmcmahon/wireless-monitor.git
|
||||
cd wireless-monitor
|
||||
```
|
||||
|
||||
## 2. Build
|
||||
|
||||
**Option A: Use automated script**
|
||||
```bash
|
||||
./build_pi5.sh
|
||||
```
|
||||
|
||||
**Option B: Manual build**
|
||||
```bash
|
||||
# Install dependencies
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential autoconf automake libtool \
|
||||
pkg-config libpcap-dev libnl-genl-3-dev libnl-3-dev
|
||||
|
||||
# Build
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
```
|
||||
|
||||
## 3. Run
|
||||
|
||||
```bash
|
||||
# Run as root (required for monitor mode)
|
||||
sudo ./src/wireless_monitor wlan0 11
|
||||
|
||||
# With MAC filter (to match ESP32)
|
||||
sudo ./src/wireless_monitor wlan0 11 80:84:89:93:c4:b6
|
||||
```
|
||||
|
||||
## 4. Compare with ESP32
|
||||
|
||||
Run both simultaneously on the same channel and compare outputs:
|
||||
|
||||
**ESP32:**
|
||||
```
|
||||
monitor start -c 11
|
||||
monitor debug on
|
||||
monitor filter 80:84:89:93:c4:b6
|
||||
```
|
||||
|
||||
**Raspberry Pi 5:**
|
||||
```bash
|
||||
sudo ./src/wireless_monitor wlan0 11 80:84:89:93:c4:b6
|
||||
```
|
||||
|
||||
Compare:
|
||||
- Same TA/RA addresses?
|
||||
- Same frame counts?
|
||||
- Same durations?
|
||||
- Same retry flags?
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# Wireless Monitor
|
||||
|
||||
A Linux C program for capturing and parsing 802.11 WiFi frame headers in monitor mode. This tool is designed to **verify and cross-check** the ESP32 monitor code by comparing what a Linux machine sees vs what the ESP32 sees when monitoring the same WiFi traffic.
|
||||
|
||||
## Purpose
|
||||
|
||||
This program captures 802.11 frames and displays:
|
||||
- **RA (Receiver Address)** and **TA (Transmitter Address)** - same fields ESP32 extracts
|
||||
- Frame type, size, duration (NAV)
|
||||
- RSSI, MCS, spatial streams (when available from radiotap)
|
||||
- Retry flag
|
||||
|
||||
This allows you to verify that the ESP32's frame parsing matches what Linux sees, helping debug issues like:
|
||||
- Missing frames
|
||||
- Incorrect MAC address extraction
|
||||
- Duration/NAV mismatches
|
||||
- Frame type classification
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux kernel with nl80211 support
|
||||
- libpcap development files
|
||||
- libnl3 development files
|
||||
- GNU autotools (autoconf, automake, libtool)
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
**Ubuntu/Debian:**
|
||||
```bash
|
||||
sudo apt-get install build-essential autoconf automake libtool \
|
||||
libpcap-dev libnl-genl-3-dev libnl-3-dev pkg-config
|
||||
```
|
||||
|
||||
**Fedora/RHEL:**
|
||||
```bash
|
||||
sudo dnf install gcc autoconf automake libtool \
|
||||
libpcap-devel libnl3-devel pkgconfig
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
# Generate configure script
|
||||
./autogen.sh
|
||||
|
||||
# Configure build
|
||||
./configure
|
||||
|
||||
# Build
|
||||
make
|
||||
|
||||
# Install (optional)
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Run as root (required for monitor mode)
|
||||
sudo ./src/wireless_monitor wlan0 11
|
||||
|
||||
# Or after installation
|
||||
sudo wireless_monitor wlan1 36
|
||||
|
||||
# Example output (comparable to ESP32 debug logs):
|
||||
# [1770775602.813] DATA: TA=80:84:89:93:c4:b6, RA=e0:46:ee:07:df:e1, Size=228 bytes, Dur=25038 us, RSSI=-94 dBm, Retry=YES
|
||||
```
|
||||
|
||||
## Comparison with ESP32
|
||||
|
||||
The output format is designed to match ESP32's debug output format:
|
||||
- **TA** = Transmitter Address (same as ESP32's `addr2`)
|
||||
- **RA** = Receiver Address (same as ESP32's `addr1`)
|
||||
- Frame type, size, duration, RSSI, retry flag
|
||||
|
||||
You can run both simultaneously on the same channel and compare:
|
||||
1. Are the same frames seen?
|
||||
2. Do RA/TA match?
|
||||
3. Do durations match?
|
||||
4. Are retry flags consistent?
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
wireless-monitor/
|
||||
├── configure.ac # Autoconf configuration
|
||||
├── Makefile.am # Top-level automake file
|
||||
├── autogen.sh # Script to generate configure
|
||||
├── README.md
|
||||
└── src/
|
||||
├── Makefile.am # Source automake file
|
||||
├── main.c # Main program
|
||||
├── monitor.c # Monitor mode setup (libnl3)
|
||||
├── monitor.h
|
||||
├── capture.c # Packet capture (libpcap)
|
||||
├── capture.h
|
||||
├── frame_parser.c # 802.11 frame parsing
|
||||
└── frame_parser.h # Frame structures (matches ESP32)
|
||||
```
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
# Setting Up Git Repository
|
||||
|
||||
This guide helps you set up this project in the Umber git repository.
|
||||
|
||||
## Option 1: Create New Repository
|
||||
|
||||
### On Your Git Server (Gitea/GitHub/etc.)
|
||||
|
||||
1. Create a new repository named `wireless-monitor` (or your preferred name)
|
||||
2. Note the repository URL (e.g., `https://git.umber.com/rjmcmahon/wireless-monitor.git`)
|
||||
|
||||
### On Raspberry Pi 5
|
||||
|
||||
```bash
|
||||
cd wireless-monitor-template
|
||||
|
||||
# Initialize git (if not already done)
|
||||
git init
|
||||
|
||||
# Add all files
|
||||
git add -A
|
||||
|
||||
# Create initial commit
|
||||
git commit -m "Initial commit: Linux wireless monitor for ESP32 verification"
|
||||
|
||||
# Add remote repository
|
||||
git remote add origin https://git.umber.com/rjmcmahon/wireless-monitor.git
|
||||
|
||||
# Push to repository
|
||||
git push -u origin main
|
||||
# Or if your default branch is 'master':
|
||||
# git push -u origin master
|
||||
```
|
||||
|
||||
## Option 2: Add to Existing Repository
|
||||
|
||||
If you want to add this as a subdirectory to an existing repository:
|
||||
|
||||
```bash
|
||||
# From the parent repository
|
||||
cd /path/to/parent/repo
|
||||
|
||||
# Copy the template
|
||||
cp -r wireless-monitor-template wireless-monitor
|
||||
|
||||
# Add to git
|
||||
cd wireless-monitor
|
||||
git init
|
||||
git add -A
|
||||
git commit -m "Add wireless monitor tool for ESP32 verification"
|
||||
|
||||
# Add as submodule (if parent repo uses submodules)
|
||||
# Or just add files directly to parent repo
|
||||
```
|
||||
|
||||
## Option 3: Standalone Repository
|
||||
|
||||
If you want this as a completely separate repository:
|
||||
|
||||
```bash
|
||||
cd wireless-monitor-template
|
||||
|
||||
# Initialize git
|
||||
git init
|
||||
|
||||
# Create .gitignore (already included)
|
||||
# Add all files
|
||||
git add -A
|
||||
|
||||
# Initial commit
|
||||
git commit -m "Initial commit: Linux wireless monitor
|
||||
|
||||
A C program using GNU Automake for capturing and parsing 802.11 WiFi
|
||||
frame headers in monitor mode. Designed to verify ESP32 monitor code
|
||||
by comparing Linux vs ESP32 frame parsing."
|
||||
|
||||
# Add remote (replace with your actual repository URL)
|
||||
git remote add origin https://git.umber.com/rjmcmahon/wireless-monitor.git
|
||||
|
||||
# Push
|
||||
git branch -M main # Or 'master' if that's your default
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
## Building on Raspberry Pi 5
|
||||
|
||||
After cloning the repository:
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://git.umber.com/rjmcmahon/wireless-monitor.git
|
||||
cd wireless-monitor
|
||||
|
||||
# Run build script
|
||||
./build_pi5.sh
|
||||
|
||||
# Or manually:
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
```
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
wireless-monitor/
|
||||
├── .gitignore
|
||||
├── configure.ac
|
||||
├── Makefile.am
|
||||
├── autogen.sh
|
||||
├── build_pi5.sh # Automated build script for Pi 5
|
||||
├── README.md
|
||||
├── BUILD_PI5.md # Build instructions
|
||||
├── VERIFICATION_GUIDE.md # ESP32 verification guide
|
||||
└── src/
|
||||
├── Makefile.am
|
||||
├── main.c
|
||||
├── monitor.c
|
||||
├── monitor.h
|
||||
├── capture.c
|
||||
├── capture.h
|
||||
├── frame_parser.c
|
||||
└── frame_parser.h
|
||||
```
|
||||
|
||||
## Git Workflow
|
||||
|
||||
```bash
|
||||
# Make changes
|
||||
# ... edit files ...
|
||||
|
||||
# Stage changes
|
||||
git add -A
|
||||
|
||||
# Commit
|
||||
git commit -m "Description of changes"
|
||||
|
||||
# Push
|
||||
git push origin main
|
||||
```
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
# ESP32 Monitor Verification Guide
|
||||
|
||||
This Linux C program captures 802.11 frames and displays them in a format that matches ESP32's debug output, allowing you to verify that the ESP32 monitor code is correctly parsing frames.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Build
|
||||
cd wireless-monitor-template
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
|
||||
# Run (as root)
|
||||
sudo ./src/wireless_monitor wlan0 11
|
||||
|
||||
# With MAC filter (to match ESP32 filter)
|
||||
sudo ./src/wireless_monitor wlan0 11 80:84:89:93:c4:b6
|
||||
```
|
||||
|
||||
## Comparison Workflow
|
||||
|
||||
### 1. Setup ESP32 Monitor
|
||||
|
||||
```bash
|
||||
# On ESP32 console
|
||||
monitor start -c 11
|
||||
monitor debug on
|
||||
monitor filter 80:84:89:93:c4:b6
|
||||
```
|
||||
|
||||
### 2. Setup Linux Monitor (Same Channel)
|
||||
|
||||
```bash
|
||||
# On Linux machine (same channel!)
|
||||
sudo ./src/wireless_monitor wlan0 11 80:84:89:93:c4:b6
|
||||
```
|
||||
|
||||
### 3. Generate Test Traffic
|
||||
|
||||
```bash
|
||||
# On another device (e.g., Raspberry Pi)
|
||||
iperf -c <server_ip> -u -b 10M -t 60
|
||||
```
|
||||
|
||||
### 4. Compare Outputs
|
||||
|
||||
**ESP32 Output:**
|
||||
```
|
||||
[1770775602.813] I MONITOR: DATA: DATA, TA=80:84:89:93:c4:b6, Size=228 bytes, Rate=54 Mbps, MCS=0, SS=1, BW=20 MHz, RSSI=-94 dBm, Retry:YES
|
||||
```
|
||||
|
||||
**Linux Output:**
|
||||
```
|
||||
[1770775602.813] DATA: TA=80:84:89:93:c4:b6, RA=e0:46:ee:07:df:e1, Size=228 bytes, Dur=25038 us, RSSI=-94 dBm, Retry=YES
|
||||
```
|
||||
|
||||
## What to Verify
|
||||
|
||||
1. **Same Frames Seen**: Do both see the same number of frames?
|
||||
2. **TA Matches**: Transmitter Address should be identical
|
||||
3. **RA Present**: Linux shows RA, ESP32 extracts it as `addr1`
|
||||
4. **Duration/NAV**: Duration field should match (for collapse detection)
|
||||
5. **Retry Flag**: Should be consistent
|
||||
6. **Frame Types**: Both should classify frames the same way
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No Frames on Linux but ESP32 Sees Them
|
||||
|
||||
- **Channel Mismatch**: Ensure both are on the same channel
|
||||
- **Interface Issue**: Check `iw dev wlan0 info` shows monitor mode
|
||||
- **Permissions**: Must run as root
|
||||
|
||||
### Different TA/RA Values
|
||||
|
||||
- **Address Order**: ESP32 uses `addr2`=TA, `addr1`=RA (correct)
|
||||
- **To DS/From DS**: Address meanings change based on these bits
|
||||
- **Frame Direction**: Client→AP vs AP→Client have different addressing
|
||||
|
||||
### Duration Mismatches
|
||||
|
||||
- **NAV Field**: Both should read the same Duration/ID field
|
||||
- **Expected vs Actual**: ESP32 calculates expected duration, Linux shows actual
|
||||
- **Collapse Detection**: Large mismatches indicate potential collisions
|
||||
|
||||
## Example: Debugging Missing Frames
|
||||
|
||||
If ESP32 shows `Filter: 80:84:89:93:c4:b6 (0 frames, 0.0 fps)` but Linux sees frames:
|
||||
|
||||
1. **Check Channel**: `monitor status` on ESP32 vs `iw dev wlan0 info` on Linux
|
||||
2. **Check MAC**: Verify the MAC address is correct
|
||||
3. **Check Filter Logic**: ESP32 checks both TA and RA, Linux does the same
|
||||
4. **Check Frame Types**: ESP32 might be filtering by frame type
|
||||
|
||||
## Notes
|
||||
|
||||
- Linux output format matches ESP32 debug logs for easy comparison
|
||||
- Both tools filter on TA **and** RA to maximize matches
|
||||
- Radiotap header parsing (RSSI, MCS) is simplified - full parsing would require radiotap library
|
||||
- For accurate PHY info, consider using `tcpdump` or `wireshark` with radiotap support
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
# Run this script to generate the configure script and Makefile.in files
|
||||
|
||||
set -e
|
||||
|
||||
echo "Running aclocal..."
|
||||
aclocal
|
||||
|
||||
echo "Running autoconf..."
|
||||
autoconf
|
||||
|
||||
echo "Running automake..."
|
||||
automake --add-missing --copy
|
||||
|
||||
echo "Done. Now run ./configure && make"
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#!/bin/bash
|
||||
# Build script for Raspberry Pi 5
|
||||
# This script installs dependencies and builds the wireless monitor tool
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Wireless Monitor - Raspberry Pi 5 Build ==="
|
||||
echo ""
|
||||
|
||||
# Check if running on Raspberry Pi (optional check)
|
||||
if [ -f /proc/device-tree/model ] && grep -q "Raspberry Pi" /proc/device-tree/model 2>/dev/null; then
|
||||
echo "Detected Raspberry Pi"
|
||||
cat /proc/device-tree/model
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Check for root (needed for some package checks, but not for building)
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
echo "Warning: Running as root. Building as regular user is recommended."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Install dependencies
|
||||
echo "=== Installing Dependencies ==="
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
autoconf \
|
||||
automake \
|
||||
libtool \
|
||||
pkg-config \
|
||||
libpcap-dev \
|
||||
libnl-genl-3-dev \
|
||||
libnl-3-dev
|
||||
|
||||
echo ""
|
||||
echo "=== Building ==="
|
||||
|
||||
# Generate configure script
|
||||
if [ ! -f configure ]; then
|
||||
echo "Running autogen.sh..."
|
||||
./autogen.sh
|
||||
fi
|
||||
|
||||
# Configure
|
||||
if [ ! -f Makefile ]; then
|
||||
echo "Running configure..."
|
||||
./configure
|
||||
fi
|
||||
|
||||
# Build
|
||||
echo "Running make..."
|
||||
make
|
||||
|
||||
echo ""
|
||||
echo "=== Build Complete ==="
|
||||
echo ""
|
||||
echo "Binary location: ./src/wireless_monitor"
|
||||
echo ""
|
||||
echo "To test (requires root for monitor mode):"
|
||||
echo " sudo ./src/wireless_monitor wlan0 11"
|
||||
echo ""
|
||||
echo "To install system-wide:"
|
||||
echo " sudo make install"
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
AC_PREREQ([2.69])
|
||||
AC_INIT([wireless-monitor], [1.0.0], [your-email@example.com])
|
||||
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
|
||||
AC_CONFIG_SRCDIR([src/main.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
# Checks for programs
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC_C99
|
||||
AM_PROG_AR
|
||||
|
||||
# Checks for libraries
|
||||
PKG_CHECK_MODULES([LIBPCAP], [libpcap])
|
||||
PKG_CHECK_MODULES([LIBNL], [libnl-genl-3.0 libnl-3.0])
|
||||
|
||||
# Checks for header files
|
||||
AC_CHECK_HEADERS([sys/socket.h netinet/in.h linux/nl80211.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics
|
||||
AC_TYPE_UINT8_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
|
||||
# Checks for library functions
|
||||
AC_CHECK_FUNCS([memset memcpy strncpy])
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
src/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
bin_PROGRAMS = wireless_monitor
|
||||
|
||||
wireless_monitor_SOURCES = \
|
||||
main.c \
|
||||
monitor.c \
|
||||
capture.c \
|
||||
frame_parser.c
|
||||
|
||||
wireless_monitor_HEADERS = \
|
||||
monitor.h \
|
||||
capture.h \
|
||||
frame_parser.h
|
||||
|
||||
wireless_monitor_CFLAGS = \
|
||||
$(LIBPCAP_CFLAGS) \
|
||||
$(LIBNL_CFLAGS) \
|
||||
-I$(top_srcdir)/src
|
||||
|
||||
wireless_monitor_LDADD = \
|
||||
$(LIBPCAP_LIBS) \
|
||||
$(LIBNL_LIBS)
|
||||
|
||||
# Include headers in distribution
|
||||
include_HEADERS = monitor.h capture.h frame_parser.h
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
#include "config.h"
|
||||
#include "capture.h"
|
||||
#include "frame_parser.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pcap/pcap.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
static pcap_t *pcap_handle = NULL;
|
||||
static volatile int capture_running = 0;
|
||||
|
||||
static void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) {
|
||||
packet_callback_t callback = (packet_callback_t)user;
|
||||
if (callback && h->caplen >= 24) { // Minimum 802.11 header size
|
||||
callback(bytes, h->caplen, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int start_capture(const char *interface, packet_callback_t callback, void *user_data) {
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
if (capture_running) {
|
||||
fprintf(stderr, "Capture already running\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open interface for capture
|
||||
pcap_handle = pcap_open_live(interface, 65535, 1, 1000, errbuf);
|
||||
if (!pcap_handle) {
|
||||
fprintf(stderr, "Failed to open interface %s: %s\n", interface, errbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set non-blocking mode
|
||||
if (pcap_setnonblock(pcap_handle, 0, errbuf) < 0) {
|
||||
fprintf(stderr, "Warning: Failed to set blocking mode: %s\n", errbuf);
|
||||
}
|
||||
|
||||
capture_running = 1;
|
||||
|
||||
// Start capture loop
|
||||
printf("Starting capture loop...\n");
|
||||
while (capture_running) {
|
||||
int ret = pcap_dispatch(pcap_handle, 1, packet_handler, (u_char *)callback);
|
||||
if (ret < 0) {
|
||||
if (ret == PCAP_ERROR_BREAK) {
|
||||
// Normal break
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, "Capture error: %s\n", pcap_geterr(pcap_handle));
|
||||
break;
|
||||
} else if (ret == 0) {
|
||||
// Timeout - continue
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stop_capture(void) {
|
||||
capture_running = 0;
|
||||
if (pcap_handle) {
|
||||
pcap_breakloop(pcap_handle);
|
||||
pcap_close(pcap_handle);
|
||||
pcap_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef CAPTURE_H
|
||||
#define CAPTURE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* @brief Packet callback function type
|
||||
* @param packet Pointer to packet data
|
||||
* @param len Packet length
|
||||
* @param user_data User-provided data pointer
|
||||
*/
|
||||
typedef void (*packet_callback_t)(const uint8_t *packet, size_t len, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Start packet capture on interface
|
||||
* @param interface Interface name
|
||||
* @param callback Callback function for each packet
|
||||
* @param user_data User data passed to callback
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int start_capture(const char *interface, packet_callback_t callback, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Stop packet capture
|
||||
*/
|
||||
void stop_capture(void);
|
||||
|
||||
#endif /* CAPTURE_H */
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
#include "config.h"
|
||||
#include "frame_parser.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
// Radiotap header parsing (simplified)
|
||||
static int skip_radiotap_header(const uint8_t *packet, size_t len, size_t *offset) {
|
||||
if (len < 8) return -1;
|
||||
|
||||
// Check for radiotap header (starts with version 0)
|
||||
if (packet[0] == 0 && packet[1] == 0) {
|
||||
// Radiotap header present
|
||||
uint16_t rt_len = *(uint16_t *)&packet[2];
|
||||
if (rt_len > len || rt_len < 8) return -1;
|
||||
|
||||
// Extract RSSI and rate from radiotap (simplified - full parsing is complex)
|
||||
// For now, just skip the header
|
||||
*offset = rt_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// No radiotap header
|
||||
*offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_80211_frame(const uint8_t *packet, size_t len, wifi_frame_info_t *frame_info) {
|
||||
size_t offset = 0;
|
||||
|
||||
// Skip radiotap header if present
|
||||
if (skip_radiotap_header(packet, len, &offset) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len < offset + 24) {
|
||||
return -1; // Too short for 802.11 header
|
||||
}
|
||||
|
||||
const uint8_t *frame = packet + offset;
|
||||
|
||||
// Parse Frame Control (bytes 0-1)
|
||||
frame_info->frame_control = frame[0] | (frame[1] << 8);
|
||||
frame_info->type = (frame[0] >> 2) & 0x03;
|
||||
frame_info->subtype = (frame[0] >> 4) & 0x0F;
|
||||
frame_info->to_ds = (frame[1] >> 0) & 0x01;
|
||||
frame_info->from_ds = (frame[1] >> 1) & 0x01;
|
||||
frame_info->retry = (frame[1] >> 3) & 0x01;
|
||||
|
||||
// Parse Duration/ID (bytes 2-3)
|
||||
frame_info->duration_id = frame[2] | (frame[3] << 8);
|
||||
|
||||
// Parse Addresses based on To DS / From DS bits
|
||||
// addr1 = RA (Receiver Address)
|
||||
// addr2 = TA (Transmitter Address)
|
||||
// addr3 = BSSID/SA/DA
|
||||
memcpy(frame_info->addr1, &frame[4], 6); // RA
|
||||
memcpy(frame_info->addr2, &frame[10], 6); // TA
|
||||
memcpy(frame_info->addr3, &frame[16], 6); // BSSID/SA/DA
|
||||
|
||||
// Check for Address 4 (only if To DS and From DS both set)
|
||||
frame_info->has_addr4 = frame_info->to_ds && frame_info->from_ds;
|
||||
if (frame_info->has_addr4) {
|
||||
if (len < offset + 30) return -1;
|
||||
memcpy(frame_info->addr4, &frame[22], 6);
|
||||
}
|
||||
|
||||
// Parse Sequence Control (bytes 22-23 or 28-29)
|
||||
uint16_t seq_offset = frame_info->has_addr4 ? 28 : 22;
|
||||
if (len < offset + seq_offset + 2) return -1;
|
||||
|
||||
frame_info->seq_ctrl = frame[seq_offset] | (frame[seq_offset + 1] << 8);
|
||||
frame_info->fragment_num = frame_info->seq_ctrl & 0x0F;
|
||||
frame_info->sequence_num = (frame_info->seq_ctrl >> 4) & 0x0FFF;
|
||||
|
||||
// Initialize PHY info (would need radiotap parsing for accurate values)
|
||||
frame_info->rssi = -100; // Default
|
||||
frame_info->mcs = 0;
|
||||
frame_info->spatial_streams = 1;
|
||||
frame_info->bandwidth = 0;
|
||||
frame_info->sgi = false;
|
||||
frame_info->phy_rate_kbps = 0;
|
||||
|
||||
frame_info->frame_len = len - offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *get_frame_type_name(uint8_t type, uint8_t subtype) {
|
||||
switch (type) {
|
||||
case FRAME_TYPE_MANAGEMENT:
|
||||
switch (subtype) {
|
||||
case 0: return "ASSOC_REQ";
|
||||
case 1: return "ASSOC_RESP";
|
||||
case 2: return "REASSOC_REQ";
|
||||
case 3: return "REASSOC_RESP";
|
||||
case 4: return "PROBE_REQ";
|
||||
case 5: return "PROBE_RESP";
|
||||
case 8: return "BEACON";
|
||||
case 10: return "DISASSOC";
|
||||
case 11: return "AUTH";
|
||||
case 12: return "DEAUTH";
|
||||
default: return "MGMT_UNKNOWN";
|
||||
}
|
||||
case FRAME_TYPE_CONTROL:
|
||||
switch (subtype) {
|
||||
case 11: return "RTS";
|
||||
case 12: return "CTS";
|
||||
case 13: return "ACK";
|
||||
default: return "CTRL_UNKNOWN";
|
||||
}
|
||||
case FRAME_TYPE_DATA:
|
||||
switch (subtype) {
|
||||
case 0: return "DATA";
|
||||
case 1: return "DATA_CF_ACK";
|
||||
case 2: return "DATA_CF_POLL";
|
||||
case 3: return "DATA_CF_ACK_POLL";
|
||||
case 4: return "NULL";
|
||||
case 8: return "QOS_DATA";
|
||||
case 12: return "QOS_NULL";
|
||||
default: return "DATA_UNKNOWN";
|
||||
}
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void print_frame_info(const wifi_frame_info_t *frame_info, uint64_t timestamp_us) {
|
||||
const char *type_name = get_frame_type_name(frame_info->type, frame_info->subtype);
|
||||
|
||||
printf("[%llu.%03llu] %s: ",
|
||||
timestamp_us / 1000000, (timestamp_us / 1000) % 1000);
|
||||
|
||||
printf("TA=%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]);
|
||||
|
||||
printf("RA=%02x:%02x:%02x:%02x:%02x:%02x, ",
|
||||
frame_info->addr1[0], frame_info->addr1[1], frame_info->addr1[2],
|
||||
frame_info->addr1[3], frame_info->addr1[4], frame_info->addr1[5]);
|
||||
|
||||
printf("Size=%u bytes, ", frame_info->frame_len);
|
||||
printf("Dur=%u us, ", frame_info->duration_id);
|
||||
printf("RSSI=%d dBm", frame_info->rssi);
|
||||
|
||||
if (frame_info->retry) {
|
||||
printf(", Retry=YES");
|
||||
} else {
|
||||
printf(", Retry=no");
|
||||
}
|
||||
|
||||
if (frame_info->mcs > 0) {
|
||||
printf(", MCS=%u", frame_info->mcs);
|
||||
}
|
||||
|
||||
if (frame_info->spatial_streams > 1) {
|
||||
printf(", SS=%u", frame_info->spatial_streams);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef FRAME_PARSER_H
|
||||
#define FRAME_PARSER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* @brief 802.11 Frame types
|
||||
*/
|
||||
typedef enum {
|
||||
FRAME_TYPE_MANAGEMENT = 0,
|
||||
FRAME_TYPE_CONTROL = 1,
|
||||
FRAME_TYPE_DATA = 2,
|
||||
FRAME_TYPE_RESERVED = 3
|
||||
} wifi_frame_type_t;
|
||||
|
||||
/**
|
||||
* @brief Parsed 802.11 frame information (similar to ESP32 wifi_frame_info_t)
|
||||
*/
|
||||
typedef struct {
|
||||
// Frame Control
|
||||
uint16_t frame_control;
|
||||
uint8_t type;
|
||||
uint8_t subtype;
|
||||
bool to_ds;
|
||||
bool from_ds;
|
||||
bool retry;
|
||||
|
||||
// Duration/ID (NAV)
|
||||
uint16_t duration_id;
|
||||
|
||||
// MAC Addresses
|
||||
uint8_t addr1[6]; // Receiver Address (RA)
|
||||
uint8_t addr2[6]; // Transmitter Address (TA)
|
||||
uint8_t addr3[6]; // BSSID/SA/DA
|
||||
uint8_t addr4[6]; // Optional Address 4
|
||||
bool has_addr4;
|
||||
|
||||
// Sequence Control
|
||||
uint16_t seq_ctrl;
|
||||
uint16_t fragment_num;
|
||||
uint16_t sequence_num;
|
||||
|
||||
// PHY info (from radiotap header if available)
|
||||
int8_t rssi;
|
||||
uint8_t mcs;
|
||||
uint8_t spatial_streams; // NSS
|
||||
uint8_t bandwidth; // 0=20MHz, 1=40MHz, 2=80MHz, 3=160MHz
|
||||
bool sgi; // Short Guard Interval
|
||||
uint32_t phy_rate_kbps;
|
||||
|
||||
// Frame size
|
||||
uint16_t frame_len;
|
||||
} wifi_frame_info_t;
|
||||
|
||||
/**
|
||||
* @brief Parse 802.11 frame header
|
||||
* @param packet Raw packet data (including radiotap header if present)
|
||||
* @param len Packet length
|
||||
* @param frame_info Output: parsed frame information
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int parse_80211_frame(const uint8_t *packet, size_t len, wifi_frame_info_t *frame_info);
|
||||
|
||||
/**
|
||||
* @brief Get human-readable frame type name
|
||||
*/
|
||||
const char *get_frame_type_name(uint8_t type, uint8_t subtype);
|
||||
|
||||
/**
|
||||
* @brief Print frame information (for verification/comparison with ESP32)
|
||||
*/
|
||||
void print_frame_info(const wifi_frame_info_t *frame_info, uint64_t timestamp_us);
|
||||
|
||||
#endif /* FRAME_PARSER_H */
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "config.h"
|
||||
#include "monitor.h"
|
||||
#include "capture.h"
|
||||
#include "frame_parser.h"
|
||||
|
||||
static volatile int running = 1;
|
||||
static uint64_t packet_count = 0;
|
||||
static uint64_t data_frame_count = 0;
|
||||
static uint8_t filter_mac[6] = {0};
|
||||
static bool filter_enabled = false;
|
||||
|
||||
static void signal_handler(int sig) {
|
||||
(void)sig;
|
||||
running = 0;
|
||||
stop_capture();
|
||||
}
|
||||
|
||||
static uint64_t get_timestamp_us(void) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
|
||||
}
|
||||
|
||||
static bool mac_match(const uint8_t *mac1, const uint8_t *mac2) {
|
||||
return memcmp(mac1, mac2, 6) == 0;
|
||||
}
|
||||
|
||||
static void packet_callback(const uint8_t *packet, size_t len, void *user_data) {
|
||||
(void)user_data;
|
||||
wifi_frame_info_t frame_info;
|
||||
uint64_t timestamp_us = get_timestamp_us();
|
||||
|
||||
packet_count++;
|
||||
|
||||
// Parse 802.11 frame
|
||||
if (parse_80211_frame(packet, len, &frame_info) == 0) {
|
||||
// Only print data frames (for comparison with ESP32)
|
||||
if (frame_info.type == FRAME_TYPE_DATA) {
|
||||
// Apply MAC filter if enabled (check both TA and RA like ESP32)
|
||||
bool should_print = true;
|
||||
if (filter_enabled) {
|
||||
should_print = mac_match(frame_info.addr2, filter_mac) ||
|
||||
mac_match(frame_info.addr1, filter_mac);
|
||||
}
|
||||
|
||||
if (should_print) {
|
||||
data_frame_count++;
|
||||
print_frame_info(&frame_info, timestamp_us);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_mac(const char *mac_str, uint8_t *mac_out) {
|
||||
int values[6];
|
||||
if (sscanf(mac_str, "%x:%x:%x:%x:%x:%x",
|
||||
&values[0], &values[1], &values[2],
|
||||
&values[3], &values[4], &values[5]) != 6) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (values[i] < 0 || values[i] > 255) return -1;
|
||||
mac_out[i] = (uint8_t)values[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "Usage: %s <interface> <channel> [filter_mac]\n", argv[0]);
|
||||
fprintf(stderr, "Example: %s wlan0 11\n", argv[0]);
|
||||
fprintf(stderr, "Example: %s wlan0 11 80:84:89:93:c4:b6\n", argv[0]);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "This tool captures 802.11 frames to verify ESP32 monitor code.\n");
|
||||
fprintf(stderr, "Output format matches ESP32 debug logs for easy comparison.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *interface = argv[1];
|
||||
int channel = atoi(argv[2]);
|
||||
|
||||
if (channel < 1 || channel > 165) {
|
||||
fprintf(stderr, "Invalid channel: %d (must be 1-165)\n", channel);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parse optional MAC filter
|
||||
if (argc >= 4) {
|
||||
if (parse_mac(argv[3], filter_mac) == 0) {
|
||||
filter_enabled = true;
|
||||
printf("Filter enabled: %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 {
|
||||
fprintf(stderr, "Invalid MAC address format: %s\n", argv[3]);
|
||||
fprintf(stderr, "Expected format: XX:XX:XX:XX:XX:XX\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup signal handlers
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
printf("=== Wireless Monitor ===\n");
|
||||
printf("Interface: %s\n", interface);
|
||||
printf("Channel: %d\n", channel);
|
||||
printf("\n");
|
||||
|
||||
// Set monitor mode
|
||||
if (set_monitor_mode(interface, channel) != 0) {
|
||||
fprintf(stderr, "Failed to set monitor mode\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Monitor mode activated\n");
|
||||
printf("Capturing 802.11 frames... (Ctrl+C to stop)\n");
|
||||
printf("This output can be compared with ESP32 monitor debug logs\n");
|
||||
if (filter_enabled) {
|
||||
printf("Filter: %02x:%02x:%02x:%02x:%02x:%02x (showing frames with matching TA or RA)\n",
|
||||
filter_mac[0], filter_mac[1], filter_mac[2],
|
||||
filter_mac[3], filter_mac[4], filter_mac[5]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Start packet capture (blocks until stopped)
|
||||
if (start_capture(interface, packet_callback, NULL) != 0) {
|
||||
fprintf(stderr, "Failed to start capture\n");
|
||||
restore_managed_mode(interface);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("\n=== Capture Statistics ===\n");
|
||||
printf("Total packets: %llu\n", (unsigned long long)packet_count);
|
||||
printf("Data frames: %llu\n", (unsigned long long)data_frame_count);
|
||||
|
||||
restore_managed_mode(interface);
|
||||
printf("Done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
#include "config.h"
|
||||
#include "monitor.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/nl80211.h>
|
||||
#include <netlink/genl/genl.h>
|
||||
#include <netlink/genl/ctrl.h>
|
||||
#include <netlink/netlink.h>
|
||||
#include <net/if.h>
|
||||
#include <errno.h>
|
||||
|
||||
static struct nl_sock *nl_sock = NULL;
|
||||
static int nl80211_id = -1;
|
||||
|
||||
static int nl80211_init(void) {
|
||||
nl_sock = nl_socket_alloc();
|
||||
if (!nl_sock) {
|
||||
fprintf(stderr, "Failed to allocate netlink socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (genl_connect(nl_sock) < 0) {
|
||||
fprintf(stderr, "Failed to connect to netlink\n");
|
||||
nl_socket_free(nl_sock);
|
||||
nl_sock = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
nl80211_id = genl_ctrl_resolve(nl_sock, "nl80211");
|
||||
if (nl80211_id < 0) {
|
||||
fprintf(stderr, "nl80211 not found\n");
|
||||
nl_close(nl_sock);
|
||||
nl_socket_free(nl_sock);
|
||||
nl_sock = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl80211_cleanup(void) {
|
||||
if (nl_sock) {
|
||||
nl_close(nl_sock);
|
||||
nl_socket_free(nl_sock);
|
||||
nl_sock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int set_monitor_mode(const char *interface, int channel) {
|
||||
struct nl_msg *msg;
|
||||
struct nl_cb *cb;
|
||||
int ifindex;
|
||||
int err = 0;
|
||||
|
||||
if (nl80211_init() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifindex = if_nametoindex(interface);
|
||||
if (ifindex == 0) {
|
||||
fprintf(stderr, "Interface %s not found\n", interface);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create message to set monitor mode
|
||||
msg = nlmsg_alloc();
|
||||
if (!msg) {
|
||||
fprintf(stderr, "Failed to allocate netlink message\n");
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl80211_id, 0,
|
||||
NLM_F_REQUEST, NL80211_CMD_SET_INTERFACE, 0);
|
||||
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR);
|
||||
|
||||
// Send message
|
||||
cb = nl_cb_alloc(NL_CB_DEFAULT);
|
||||
if (!cb) {
|
||||
fprintf(stderr, "Failed to allocate netlink callback\n");
|
||||
nlmsg_free(msg);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = nl_send_auto_complete(nl_sock, msg);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "Failed to send netlink message: %s\n", nl_geterror(err));
|
||||
nl_cb_put(cb);
|
||||
nlmsg_free(msg);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
nl_cb_put(cb);
|
||||
nlmsg_free(msg);
|
||||
|
||||
// Set channel
|
||||
if (set_channel(interface, channel) < 0) {
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
nl80211_cleanup();
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
fprintf(stderr, "Failed to add netlink attribute\n");
|
||||
nlmsg_free(msg);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int restore_managed_mode(const char *interface) {
|
||||
struct nl_msg *msg;
|
||||
int ifindex;
|
||||
int err = 0;
|
||||
|
||||
if (nl80211_init() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifindex = if_nametoindex(interface);
|
||||
if (ifindex == 0) {
|
||||
fprintf(stderr, "Interface %s not found\n", interface);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = nlmsg_alloc();
|
||||
if (!msg) {
|
||||
fprintf(stderr, "Failed to allocate netlink message\n");
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl80211_id, 0,
|
||||
NLM_F_REQUEST, NL80211_CMD_SET_INTERFACE, 0);
|
||||
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION);
|
||||
|
||||
err = nl_send_auto_complete(nl_sock, msg);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "Failed to send netlink message: %s\n", nl_geterror(err));
|
||||
nlmsg_free(msg);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
nlmsg_free(msg);
|
||||
nl80211_cleanup();
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
fprintf(stderr, "Failed to add netlink attribute\n");
|
||||
nlmsg_free(msg);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert WiFi channel number to frequency in MHz
|
||||
static uint32_t channel_to_freq(int channel) {
|
||||
if (channel >= 1 && channel <= 14) {
|
||||
// 2.4 GHz: 2412 + (channel - 1) * 5
|
||||
return 2412 + (channel - 1) * 5;
|
||||
} else if (channel >= 36 && channel <= 165) {
|
||||
// 5 GHz: 5000 + channel * 5
|
||||
return 5000 + channel * 5;
|
||||
} else {
|
||||
// Invalid channel
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int set_channel(const char *interface, int channel) {
|
||||
struct nl_msg *msg;
|
||||
int ifindex;
|
||||
uint32_t freq_mhz;
|
||||
int err = 0;
|
||||
|
||||
// Convert channel to frequency
|
||||
freq_mhz = channel_to_freq(channel);
|
||||
if (freq_mhz == 0) {
|
||||
fprintf(stderr, "Invalid channel: %d\n", channel);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nl80211_init() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifindex = if_nametoindex(interface);
|
||||
if (ifindex == 0) {
|
||||
fprintf(stderr, "Interface %s not found\n", interface);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = nlmsg_alloc();
|
||||
if (!msg) {
|
||||
fprintf(stderr, "Failed to allocate netlink message\n");
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl80211_id, 0,
|
||||
NLM_F_REQUEST, NL80211_CMD_SET_CHANNEL, 0);
|
||||
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq_mhz);
|
||||
|
||||
err = nl_send_auto_complete(nl_sock, msg);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "Failed to set channel %d (freq %u MHz): %s\n",
|
||||
channel, freq_mhz, nl_geterror(err));
|
||||
nlmsg_free(msg);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
nlmsg_free(msg);
|
||||
nl80211_cleanup();
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
fprintf(stderr, "Failed to add netlink attribute\n");
|
||||
nlmsg_free(msg);
|
||||
nl80211_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef MONITOR_H
|
||||
#define MONITOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief Set WiFi interface to monitor mode
|
||||
* @param interface Interface name (e.g., "wlan0")
|
||||
* @param channel Channel number (1-165)
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int set_monitor_mode(const char *interface, int channel);
|
||||
|
||||
/**
|
||||
* @brief Restore WiFi interface to managed mode
|
||||
* @param interface Interface name
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int restore_managed_mode(const char *interface);
|
||||
|
||||
/**
|
||||
* @brief Set channel for monitor interface
|
||||
* @param interface Interface name
|
||||
* @param channel Channel number (1-165)
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int set_channel(const char *interface, int channel);
|
||||
|
||||
#endif /* MONITOR_H */
|
||||
Loading…
Reference in New Issue