Implement Sniffer API (#1935)
* Implemented queue_msg_waiting. * Fmt. * Adjusted changelog. * Fixed CI. * Fixed pointer mutability. * Implemented experimental sniffer api. * Fixed CI.. * Added safety comment. * Featured gated, PromiscuousPkt * Format. * Adjusted imports. * Added injection example. * Made RxControlInfo::from_raw public. * Format. * Added sniffer example.
This commit is contained in:
parent
59728c523f
commit
d1acacb757
@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- Implement `embedded_io::{ReadReady, WriteReady}` traits for `WifiStack` (#1882)
|
- Implement `embedded_io::{ReadReady, WriteReady}` traits for `WifiStack` (#1882)
|
||||||
- Implement `queue_msg_waiting` on the os_adapter (#1925)
|
- Implement `queue_msg_waiting` on the os_adapter (#1925)
|
||||||
|
- Added API for promiscuous mode (#1935)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@ -133,6 +133,7 @@ dhcpv4 = ["wifi", "utils", "smoltcp?/proto-dhcpv4", "smoltcp?/socket-dhcpv4"]
|
|||||||
wifi-default = ["ipv4", "tcp", "udp", "icmp", "igmp", "dns", "dhcpv4"]
|
wifi-default = ["ipv4", "tcp", "udp", "icmp", "igmp", "dns", "dhcpv4"]
|
||||||
defmt = ["dep:defmt", "smoltcp?/defmt", "esp-hal/defmt"]
|
defmt = ["dep:defmt", "smoltcp?/defmt", "esp-hal/defmt"]
|
||||||
log = ["dep:log", "esp-hal/log"]
|
log = ["dep:log", "esp-hal/log"]
|
||||||
|
sniffer = ["wifi"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = [
|
features = [
|
||||||
|
|||||||
@ -19,7 +19,7 @@ use crate::{
|
|||||||
binary::include::*,
|
binary::include::*,
|
||||||
compat::queue::SimpleQueue,
|
compat::queue::SimpleQueue,
|
||||||
hal::peripheral::{Peripheral, PeripheralRef},
|
hal::peripheral::{Peripheral, PeripheralRef},
|
||||||
wifi::Protocol,
|
wifi::{Protocol, RxControlInfo},
|
||||||
EspWifiInitialization,
|
EspWifiInitialization,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,57 +179,6 @@ pub struct PeerInfo {
|
|||||||
// we always use STA for now
|
// we always use STA for now
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(esp32c6)))]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct RxControlInfo {
|
|
||||||
pub rssi: i32,
|
|
||||||
pub rate: u32,
|
|
||||||
pub sig_mode: u32,
|
|
||||||
pub mcs: u32,
|
|
||||||
pub cwb: u32,
|
|
||||||
pub smoothing: u32,
|
|
||||||
pub not_sounding: u32,
|
|
||||||
pub aggregation: u32,
|
|
||||||
pub stbc: u32,
|
|
||||||
pub fec_coding: u32,
|
|
||||||
pub sgi: u32,
|
|
||||||
pub ampdu_cnt: u32,
|
|
||||||
pub channel: u32,
|
|
||||||
pub secondary_channel: u32,
|
|
||||||
pub timestamp: u32,
|
|
||||||
pub noise_floor: i32,
|
|
||||||
pub ant: u32,
|
|
||||||
pub sig_len: u32,
|
|
||||||
pub rx_state: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(esp32c6)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub struct RxControlInfo {
|
|
||||||
pub rssi: i32,
|
|
||||||
pub rate: u32,
|
|
||||||
pub sig_len: u32,
|
|
||||||
pub rx_state: u32,
|
|
||||||
pub dump_len: u32,
|
|
||||||
pub he_sigb_len: u32,
|
|
||||||
pub cur_single_mpdu: u32,
|
|
||||||
pub cur_bb_format: u32,
|
|
||||||
pub rx_channel_estimate_info_vld: u32,
|
|
||||||
pub rx_channel_estimate_len: u32,
|
|
||||||
pub second: u32,
|
|
||||||
pub channel: u32,
|
|
||||||
pub data_rssi: i32,
|
|
||||||
pub noise_floor: u32,
|
|
||||||
pub is_group: u32,
|
|
||||||
pub rxend_state: u32,
|
|
||||||
pub rxmatch3: u32,
|
|
||||||
pub rxmatch2: u32,
|
|
||||||
pub rxmatch1: u32,
|
|
||||||
pub rxmatch0: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct ReceiveInfo {
|
pub struct ReceiveInfo {
|
||||||
@ -813,52 +762,7 @@ unsafe extern "C" fn rcv_cb(
|
|||||||
];
|
];
|
||||||
|
|
||||||
let rx_cntl = (*esp_now_info).rx_ctrl;
|
let rx_cntl = (*esp_now_info).rx_ctrl;
|
||||||
#[cfg(not(any(esp32c6)))]
|
let rx_control = RxControlInfo::from_raw(rx_cntl);
|
||||||
let rx_control = RxControlInfo {
|
|
||||||
rssi: (*rx_cntl).rssi(),
|
|
||||||
rate: (*rx_cntl).rate(),
|
|
||||||
sig_mode: (*rx_cntl).sig_mode(),
|
|
||||||
mcs: (*rx_cntl).mcs(),
|
|
||||||
cwb: (*rx_cntl).cwb(),
|
|
||||||
smoothing: (*rx_cntl).smoothing(),
|
|
||||||
not_sounding: (*rx_cntl).not_sounding(),
|
|
||||||
aggregation: (*rx_cntl).aggregation(),
|
|
||||||
stbc: (*rx_cntl).stbc(),
|
|
||||||
fec_coding: (*rx_cntl).fec_coding(),
|
|
||||||
sgi: (*rx_cntl).sgi(),
|
|
||||||
ampdu_cnt: (*rx_cntl).ampdu_cnt(),
|
|
||||||
channel: (*rx_cntl).channel(),
|
|
||||||
secondary_channel: (*rx_cntl).secondary_channel(),
|
|
||||||
timestamp: (*rx_cntl).timestamp(),
|
|
||||||
noise_floor: (*rx_cntl).noise_floor(),
|
|
||||||
ant: (*rx_cntl).ant(),
|
|
||||||
sig_len: (*rx_cntl).sig_len(),
|
|
||||||
rx_state: (*rx_cntl).rx_state(),
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(esp32c6)]
|
|
||||||
let rx_control = RxControlInfo {
|
|
||||||
rssi: (*rx_cntl).rssi(),
|
|
||||||
rate: (*rx_cntl).rate(),
|
|
||||||
sig_len: (*rx_cntl).sig_len(),
|
|
||||||
rx_state: (*rx_cntl).rx_state(),
|
|
||||||
dump_len: (*rx_cntl).dump_len(),
|
|
||||||
he_sigb_len: (*rx_cntl).he_sigb_len(),
|
|
||||||
cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
|
|
||||||
cur_bb_format: (*rx_cntl).cur_bb_format(),
|
|
||||||
rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
|
|
||||||
rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
|
|
||||||
second: (*rx_cntl).second(),
|
|
||||||
channel: (*rx_cntl).channel(),
|
|
||||||
data_rssi: (*rx_cntl).data_rssi(),
|
|
||||||
noise_floor: (*rx_cntl).noise_floor(),
|
|
||||||
is_group: (*rx_cntl).is_group(),
|
|
||||||
rxend_state: (*rx_cntl).rxend_state(),
|
|
||||||
rxmatch3: (*rx_cntl).rxmatch3(),
|
|
||||||
rxmatch2: (*rx_cntl).rxmatch2(),
|
|
||||||
rxmatch1: (*rx_cntl).rxmatch1(),
|
|
||||||
rxmatch0: (*rx_cntl).rxmatch0(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let info = ReceiveInfo {
|
let info = ReceiveInfo {
|
||||||
src_address: src,
|
src_address: src,
|
||||||
|
|||||||
@ -6,15 +6,23 @@ pub(crate) mod state;
|
|||||||
use core::{
|
use core::{
|
||||||
cell::{RefCell, RefMut},
|
cell::{RefCell, RefMut},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
mem,
|
mem::{self, MaybeUninit},
|
||||||
mem::MaybeUninit,
|
|
||||||
ptr::addr_of,
|
ptr::addr_of,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use critical_section::{CriticalSection, Mutex};
|
use critical_section::{CriticalSection, Mutex};
|
||||||
use enumset::{EnumSet, EnumSetType};
|
use enumset::{EnumSet, EnumSetType};
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
use esp_wifi_sys::include::{
|
use esp_wifi_sys::include::{
|
||||||
|
esp_wifi_80211_tx,
|
||||||
|
esp_wifi_set_promiscuous,
|
||||||
|
esp_wifi_set_promiscuous_rx_cb,
|
||||||
|
wifi_promiscuous_pkt_t,
|
||||||
|
wifi_promiscuous_pkt_type_t,
|
||||||
|
};
|
||||||
|
use esp_wifi_sys::include::{
|
||||||
|
wifi_pkt_rx_ctrl_t,
|
||||||
WIFI_PROTOCOL_11AX,
|
WIFI_PROTOCOL_11AX,
|
||||||
WIFI_PROTOCOL_11B,
|
WIFI_PROTOCOL_11B,
|
||||||
WIFI_PROTOCOL_11G,
|
WIFI_PROTOCOL_11G,
|
||||||
@ -25,6 +33,8 @@ use num_derive::FromPrimitive;
|
|||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub(crate) use os_adapter::*;
|
pub(crate) use os_adapter::*;
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
use portable_atomic::AtomicBool;
|
||||||
use portable_atomic::{AtomicUsize, Ordering};
|
use portable_atomic::{AtomicUsize, Ordering};
|
||||||
#[cfg(feature = "smoltcp")]
|
#[cfg(feature = "smoltcp")]
|
||||||
use smoltcp::phy::{Device, DeviceCapabilities, RxToken, TxToken};
|
use smoltcp::phy::{Device, DeviceCapabilities, RxToken, TxToken};
|
||||||
@ -1770,10 +1780,208 @@ fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(esp32c6)))]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct RxControlInfo {
|
||||||
|
pub rssi: i32,
|
||||||
|
pub rate: u32,
|
||||||
|
pub sig_mode: u32,
|
||||||
|
pub mcs: u32,
|
||||||
|
pub cwb: u32,
|
||||||
|
pub smoothing: u32,
|
||||||
|
pub not_sounding: u32,
|
||||||
|
pub aggregation: u32,
|
||||||
|
pub stbc: u32,
|
||||||
|
pub fec_coding: u32,
|
||||||
|
pub sgi: u32,
|
||||||
|
pub ampdu_cnt: u32,
|
||||||
|
pub channel: u32,
|
||||||
|
pub secondary_channel: u32,
|
||||||
|
pub timestamp: u32,
|
||||||
|
pub noise_floor: i32,
|
||||||
|
pub ant: u32,
|
||||||
|
pub sig_len: u32,
|
||||||
|
pub rx_state: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(esp32c6)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct RxControlInfo {
|
||||||
|
pub rssi: i32,
|
||||||
|
pub rate: u32,
|
||||||
|
pub sig_len: u32,
|
||||||
|
pub rx_state: u32,
|
||||||
|
pub dump_len: u32,
|
||||||
|
pub he_sigb_len: u32,
|
||||||
|
pub cur_single_mpdu: u32,
|
||||||
|
pub cur_bb_format: u32,
|
||||||
|
pub rx_channel_estimate_info_vld: u32,
|
||||||
|
pub rx_channel_estimate_len: u32,
|
||||||
|
pub second: u32,
|
||||||
|
pub channel: u32,
|
||||||
|
pub data_rssi: i32,
|
||||||
|
pub noise_floor: u32,
|
||||||
|
pub is_group: u32,
|
||||||
|
pub rxend_state: u32,
|
||||||
|
pub rxmatch3: u32,
|
||||||
|
pub rxmatch2: u32,
|
||||||
|
pub rxmatch1: u32,
|
||||||
|
pub rxmatch0: u32,
|
||||||
|
}
|
||||||
|
impl RxControlInfo {
|
||||||
|
/// Create an instance from a raw pointer to [wifi_pkt_rx_ctrl_t].
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// When calling this, you must ensure, that `rx_cntl` points to a valid
|
||||||
|
/// instance of [wifi_pkt_rx_ctrl_t].
|
||||||
|
pub unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self {
|
||||||
|
#[cfg(not(esp32c6))]
|
||||||
|
let rx_control_info = RxControlInfo {
|
||||||
|
rssi: (*rx_cntl).rssi(),
|
||||||
|
rate: (*rx_cntl).rate(),
|
||||||
|
sig_mode: (*rx_cntl).sig_mode(),
|
||||||
|
mcs: (*rx_cntl).mcs(),
|
||||||
|
cwb: (*rx_cntl).cwb(),
|
||||||
|
smoothing: (*rx_cntl).smoothing(),
|
||||||
|
not_sounding: (*rx_cntl).not_sounding(),
|
||||||
|
aggregation: (*rx_cntl).aggregation(),
|
||||||
|
stbc: (*rx_cntl).stbc(),
|
||||||
|
fec_coding: (*rx_cntl).fec_coding(),
|
||||||
|
sgi: (*rx_cntl).sgi(),
|
||||||
|
ampdu_cnt: (*rx_cntl).ampdu_cnt(),
|
||||||
|
channel: (*rx_cntl).channel(),
|
||||||
|
secondary_channel: (*rx_cntl).secondary_channel(),
|
||||||
|
timestamp: (*rx_cntl).timestamp(),
|
||||||
|
noise_floor: (*rx_cntl).noise_floor(),
|
||||||
|
ant: (*rx_cntl).ant(),
|
||||||
|
sig_len: (*rx_cntl).sig_len(),
|
||||||
|
rx_state: (*rx_cntl).rx_state(),
|
||||||
|
};
|
||||||
|
#[cfg(esp32c6)]
|
||||||
|
let rx_control_info = RxControlInfo {
|
||||||
|
rssi: (*rx_cntl).rssi(),
|
||||||
|
rate: (*rx_cntl).rate(),
|
||||||
|
sig_len: (*rx_cntl).sig_len(),
|
||||||
|
rx_state: (*rx_cntl).rx_state(),
|
||||||
|
dump_len: (*rx_cntl).dump_len(),
|
||||||
|
he_sigb_len: (*rx_cntl).he_sigb_len(),
|
||||||
|
cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
|
||||||
|
cur_bb_format: (*rx_cntl).cur_bb_format(),
|
||||||
|
rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
|
||||||
|
rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
|
||||||
|
second: (*rx_cntl).second(),
|
||||||
|
channel: (*rx_cntl).channel(),
|
||||||
|
data_rssi: (*rx_cntl).data_rssi(),
|
||||||
|
noise_floor: (*rx_cntl).noise_floor(),
|
||||||
|
is_group: (*rx_cntl).is_group(),
|
||||||
|
rxend_state: (*rx_cntl).rxend_state(),
|
||||||
|
rxmatch3: (*rx_cntl).rxmatch3(),
|
||||||
|
rxmatch2: (*rx_cntl).rxmatch2(),
|
||||||
|
rxmatch1: (*rx_cntl).rxmatch1(),
|
||||||
|
rxmatch0: (*rx_cntl).rxmatch0(),
|
||||||
|
};
|
||||||
|
rx_control_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
pub struct PromiscuousPkt<'a> {
|
||||||
|
pub rx_cntl: RxControlInfo,
|
||||||
|
pub frame_type: wifi_promiscuous_pkt_type_t,
|
||||||
|
pub len: usize,
|
||||||
|
pub data: &'a [u8],
|
||||||
|
}
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
impl PromiscuousPkt<'_> {
|
||||||
|
/// # Safety
|
||||||
|
/// When calling this, you have to ensure, that `buf` points to a valid
|
||||||
|
/// [wifi_promiscuous_pkt_t].
|
||||||
|
pub(crate) unsafe fn from_raw(
|
||||||
|
buf: *const wifi_promiscuous_pkt_t,
|
||||||
|
frame_type: wifi_promiscuous_pkt_type_t,
|
||||||
|
) -> Self {
|
||||||
|
let rx_cntl = RxControlInfo::from_raw(&(*buf).rx_ctrl);
|
||||||
|
let len = rx_cntl.sig_len as usize;
|
||||||
|
PromiscuousPkt {
|
||||||
|
rx_cntl,
|
||||||
|
frame_type,
|
||||||
|
len,
|
||||||
|
data: core::slice::from_raw_parts(
|
||||||
|
(buf as *const u8).add(size_of::<wifi_pkt_rx_ctrl_t>()),
|
||||||
|
len,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
static SNIFFER_CB: Mutex<RefCell<Option<fn(PromiscuousPkt)>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
unsafe extern "C" fn promiscuous_rx_cb(buf: *mut core::ffi::c_void, frame_type: u32) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let Some(sniffer_callback) = *SNIFFER_CB.borrow_ref(cs) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let promiscuous_pkt = PromiscuousPkt::from_raw(buf as *const _, frame_type);
|
||||||
|
sniffer_callback(promiscuous_pkt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
/// A wifi sniffer.
|
||||||
|
pub struct Sniffer {
|
||||||
|
promiscuous_mode_enabled: AtomicBool,
|
||||||
|
}
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
impl Sniffer {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
// This shouldn't fail, since the way this is created, means that wifi will
|
||||||
|
// always be initialized.
|
||||||
|
esp_wifi_result!(unsafe { esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb)) })
|
||||||
|
.unwrap();
|
||||||
|
Self {
|
||||||
|
promiscuous_mode_enabled: AtomicBool::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Set promiscuous mode enabled or disabled.
|
||||||
|
pub fn set_promiscuous_mode(&self, enabled: bool) -> Result<(), WifiError> {
|
||||||
|
esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(enabled) })?;
|
||||||
|
self.promiscuous_mode_enabled
|
||||||
|
.store(enabled, Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Transmit a raw frame.
|
||||||
|
pub fn send_raw_frame(
|
||||||
|
&mut self,
|
||||||
|
use_sta_interface: bool,
|
||||||
|
buffer: &[u8],
|
||||||
|
use_internal_seq_num: bool,
|
||||||
|
) -> Result<(), WifiError> {
|
||||||
|
esp_wifi_result!(unsafe {
|
||||||
|
esp_wifi_80211_tx(
|
||||||
|
if use_sta_interface { 0 } else { 1 } as wifi_interface_t,
|
||||||
|
buffer.as_ptr() as *const _,
|
||||||
|
buffer.len() as i32,
|
||||||
|
use_internal_seq_num,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Set the callback for receiving a packet.
|
||||||
|
pub fn set_receive_cb(&mut self, cb: fn(PromiscuousPkt)) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
*SNIFFER_CB.borrow_ref_mut(cs) = Some(cb);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A wifi controller
|
/// A wifi controller
|
||||||
pub struct WifiController<'d> {
|
pub struct WifiController<'d> {
|
||||||
_device: PeripheralRef<'d, crate::hal::peripherals::WIFI>,
|
_device: PeripheralRef<'d, crate::hal::peripherals::WIFI>,
|
||||||
config: Configuration,
|
config: Configuration,
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
sniffer_taken: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> WifiController<'d> {
|
impl<'d> WifiController<'d> {
|
||||||
@ -1792,6 +2000,8 @@ impl<'d> WifiController<'d> {
|
|||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
_device,
|
_device,
|
||||||
config: Default::default(),
|
config: Default::default(),
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
sniffer_taken: AtomicBool::new(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mode = WifiMode::try_from(&config)?;
|
let mode = WifiMode::try_from(&config)?;
|
||||||
@ -1801,6 +2011,18 @@ impl<'d> WifiController<'d> {
|
|||||||
this.set_configuration(&config)?;
|
this.set_configuration(&config)?;
|
||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "sniffer")]
|
||||||
|
pub fn take_sniffer(&self) -> Option<Sniffer> {
|
||||||
|
if self
|
||||||
|
.sniffer_taken
|
||||||
|
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||||
|
== Ok(false)
|
||||||
|
{
|
||||||
|
Some(Sniffer::new())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the wifi protocol.
|
/// Set the wifi protocol.
|
||||||
///
|
///
|
||||||
|
|||||||
@ -42,6 +42,7 @@ fugit = "0.3.7"
|
|||||||
heapless = "0.8.0"
|
heapless = "0.8.0"
|
||||||
hex-literal = "0.4.1"
|
hex-literal = "0.4.1"
|
||||||
hmac = { version = "0.12.1", default-features = false }
|
hmac = { version = "0.12.1", default-features = false }
|
||||||
|
ieee80211 = { version = "0.4.0", default-features = false }
|
||||||
ieee802154 = "0.6.1"
|
ieee802154 = "0.6.1"
|
||||||
lis3dh-async = "0.9.3"
|
lis3dh-async = "0.9.3"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
|
|||||||
123
examples/src/bin/wifi_80211_tx.rs
Normal file
123
examples/src/bin/wifi_80211_tx.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
//! WiFi frame injection example
|
||||||
|
//!
|
||||||
|
//! Periodically transmits a beacon frame.
|
||||||
|
|
||||||
|
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer
|
||||||
|
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use esp_backtrace as _;
|
||||||
|
use esp_hal::{
|
||||||
|
clock::ClockControl,
|
||||||
|
delay::Delay,
|
||||||
|
peripherals::Peripherals,
|
||||||
|
prelude::*,
|
||||||
|
rng::Rng,
|
||||||
|
system::SystemControl,
|
||||||
|
timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer},
|
||||||
|
};
|
||||||
|
use esp_wifi::{initialize, wifi, EspWifiInitFor};
|
||||||
|
use ieee80211::{
|
||||||
|
common::{CapabilitiesInformation, FCFFlags},
|
||||||
|
element_chain,
|
||||||
|
elements::{DSSSParameterSetElement, RawIEEE80211Element, SSIDElement},
|
||||||
|
mgmt_frame::{body::BeaconBody, header::ManagementFrameHeader, BeaconFrame},
|
||||||
|
scroll::Pwrite,
|
||||||
|
supported_rates,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SSID: &str = "esp-wifi 802.11 injection";
|
||||||
|
/// This is an arbitrary MAC address, used for the fake beacon frames.
|
||||||
|
const MAC_ADDRESS: [u8; 6] = [0x00, 0x80, 0x41, 0x13, 0x37, 0x42];
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
esp_println::logger::init_logger_from_env();
|
||||||
|
|
||||||
|
let peripherals = Peripherals::take();
|
||||||
|
|
||||||
|
let system = SystemControl::new(peripherals.SYSTEM);
|
||||||
|
let clocks = ClockControl::max(system.clock_control).freeze();
|
||||||
|
|
||||||
|
let delay = Delay::new(&clocks);
|
||||||
|
|
||||||
|
let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||||
|
let timer0: ErasedTimer = timg0.timer0.into();
|
||||||
|
let timer = PeriodicTimer::new(timer0);
|
||||||
|
|
||||||
|
let init = initialize(
|
||||||
|
EspWifiInitFor::Wifi,
|
||||||
|
timer,
|
||||||
|
Rng::new(peripherals.RNG),
|
||||||
|
peripherals.RADIO_CLK,
|
||||||
|
&clocks,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let wifi = peripherals.WIFI;
|
||||||
|
|
||||||
|
// We must initialize some kind of interface and start it.
|
||||||
|
let (_, mut controller) = wifi::new_with_mode(&init, wifi, wifi::WifiApDevice).unwrap();
|
||||||
|
controller.start().unwrap();
|
||||||
|
|
||||||
|
let mut sniffer = controller.take_sniffer().unwrap();
|
||||||
|
|
||||||
|
// Create a buffer, which can hold the enitre serialized beacon frame.
|
||||||
|
let mut beacon = [0u8; 300];
|
||||||
|
let length = beacon
|
||||||
|
.pwrite(
|
||||||
|
BeaconFrame {
|
||||||
|
header: ManagementFrameHeader {
|
||||||
|
fcf_flags: FCFFlags::new(),
|
||||||
|
duration: 0,
|
||||||
|
receiver_address: [0xff; 6].into(),
|
||||||
|
transmitter_address: MAC_ADDRESS.into(),
|
||||||
|
bssid: MAC_ADDRESS.into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
body: BeaconBody {
|
||||||
|
timestamp: 0,
|
||||||
|
// We transmit a beacon every 100 ms/TUs
|
||||||
|
beacon_interval: 100,
|
||||||
|
capabilities_info: CapabilitiesInformation::new().with_is_ess(true),
|
||||||
|
elements: element_chain! {
|
||||||
|
SSIDElement::new(SSID).unwrap(),
|
||||||
|
// These are known good values.
|
||||||
|
supported_rates![
|
||||||
|
1 B,
|
||||||
|
2 B,
|
||||||
|
5.5 B,
|
||||||
|
11 B,
|
||||||
|
6,
|
||||||
|
9,
|
||||||
|
12,
|
||||||
|
18
|
||||||
|
],
|
||||||
|
DSSSParameterSetElement {
|
||||||
|
current_channel: 1,
|
||||||
|
},
|
||||||
|
// This contains the Traffic Indication Map(TIM), for which `ieee80211-rs` currently lacks support.
|
||||||
|
RawIEEE80211Element {
|
||||||
|
tlv_type: 5,
|
||||||
|
slice: [0x01, 0x02, 0x00, 0x00].as_slice(),
|
||||||
|
_phantom: PhantomData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_phantom: PhantomData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
// Only use the actually written bytes.
|
||||||
|
let beacon = &beacon[..length];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
sniffer.send_raw_frame(false, beacon, false).unwrap();
|
||||||
|
delay.delay(100.millis());
|
||||||
|
}
|
||||||
|
}
|
||||||
85
examples/src/bin/wifi_sniffer.rs
Normal file
85
examples/src/bin/wifi_sniffer.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//! WiFi sniffer example
|
||||||
|
//!
|
||||||
|
//! Sniffs for beacon frames.
|
||||||
|
|
||||||
|
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer
|
||||||
|
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::{
|
||||||
|
collections::btree_set::BTreeSet,
|
||||||
|
string::{String, ToString},
|
||||||
|
};
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use esp_alloc::heap_allocator;
|
||||||
|
use esp_backtrace as _;
|
||||||
|
use esp_hal::{
|
||||||
|
clock::ClockControl,
|
||||||
|
peripherals::Peripherals,
|
||||||
|
prelude::*,
|
||||||
|
rng::Rng,
|
||||||
|
system::SystemControl,
|
||||||
|
timer::{timg::TimerGroup, ErasedTimer, PeriodicTimer},
|
||||||
|
};
|
||||||
|
use esp_println::println;
|
||||||
|
use esp_wifi::{initialize, wifi, EspWifiInitFor};
|
||||||
|
use ieee80211::{match_frames, mgmt_frame::BeaconFrame};
|
||||||
|
|
||||||
|
static KNOWN_SSIDS: Mutex<RefCell<BTreeSet<String>>> = Mutex::new(RefCell::new(BTreeSet::new()));
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
esp_println::logger::init_logger_from_env();
|
||||||
|
|
||||||
|
let peripherals = Peripherals::take();
|
||||||
|
// Create a heap allocator, with 32kB of space.
|
||||||
|
heap_allocator!(32_168);
|
||||||
|
|
||||||
|
let system = SystemControl::new(peripherals.SYSTEM);
|
||||||
|
let clocks = ClockControl::max(system.clock_control).freeze();
|
||||||
|
|
||||||
|
let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||||
|
let timer0: ErasedTimer = timg0.timer0.into();
|
||||||
|
let timer = PeriodicTimer::new(timer0);
|
||||||
|
|
||||||
|
let init = initialize(
|
||||||
|
EspWifiInitFor::Wifi,
|
||||||
|
timer,
|
||||||
|
Rng::new(peripherals.RNG),
|
||||||
|
peripherals.RADIO_CLK,
|
||||||
|
&clocks,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let wifi = peripherals.WIFI;
|
||||||
|
|
||||||
|
// We must initialize some kind of interface and start it.
|
||||||
|
let (_, mut controller) = wifi::new_with_mode(&init, wifi, wifi::WifiApDevice).unwrap();
|
||||||
|
controller.start().unwrap();
|
||||||
|
|
||||||
|
let mut sniffer = controller.take_sniffer().unwrap();
|
||||||
|
sniffer.set_promiscuous_mode(true).unwrap();
|
||||||
|
sniffer.set_receive_cb(|packet| {
|
||||||
|
let _ = match_frames! {
|
||||||
|
packet.data,
|
||||||
|
beacon = BeaconFrame => {
|
||||||
|
let Some(ssid) = beacon.ssid() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if critical_section::with(|cs| {
|
||||||
|
KNOWN_SSIDS.borrow_ref_mut(cs).insert(ssid.to_string())
|
||||||
|
}) {
|
||||||
|
println!("Found new AP with SSID: {ssid}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user