esp_radio/wifi/
sniffer.rs1use core::marker::PhantomData;
4
5use esp_sync::NonReentrantMutex;
6
7use super::{RxControlInfo, SNIFFER_BIT, release, try_acquire};
8use crate::{
9 WifiError,
10 sys::include::{
11 esp_wifi_80211_tx,
12 esp_wifi_set_promiscuous,
13 esp_wifi_set_promiscuous_rx_cb,
14 wifi_interface_t,
15 wifi_interface_t_WIFI_IF_AP,
16 wifi_interface_t_WIFI_IF_STA,
17 wifi_pkt_rx_ctrl_t,
18 wifi_promiscuous_pkt_t,
19 wifi_promiscuous_pkt_type_t,
20 },
21 wifi::esp_wifi_result,
22};
23
24#[derive(Debug)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27#[instability::unstable]
28pub struct PromiscuousPkt<'a> {
29 pub rx_cntl: RxControlInfo,
31 pub frame_type: wifi_promiscuous_pkt_type_t,
33 pub len: usize,
35 pub data: &'a [u8],
37}
38
39#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
40impl PromiscuousPkt<'_> {
41 pub(crate) unsafe fn from_raw(
46 buf: *const wifi_promiscuous_pkt_t,
47 frame_type: wifi_promiscuous_pkt_type_t,
48 ) -> Self {
49 let rx_cntl = unsafe { RxControlInfo::from_raw(&(*buf).rx_ctrl) };
50 let len = rx_cntl.sig_len as usize;
51 PromiscuousPkt {
52 rx_cntl,
53 frame_type,
54 len,
55 data: unsafe {
56 core::slice::from_raw_parts(
57 (buf as *const u8).add(core::mem::size_of::<wifi_pkt_rx_ctrl_t>()),
58 len,
59 )
60 },
61 }
62 }
63}
64
65static SNIFFER_CB: NonReentrantMutex<Option<fn(PromiscuousPkt<'_>)>> = NonReentrantMutex::new(None);
66
67unsafe extern "C" fn promiscuous_rx_cb(buf: *mut core::ffi::c_void, frame_type: u32) {
68 unsafe {
69 if let Some(sniffer_callback) = SNIFFER_CB.with(|callback| *callback) {
70 let promiscuous_pkt = PromiscuousPkt::from_raw(buf as *const _, frame_type);
71 sniffer_callback(promiscuous_pkt);
72 }
73 }
74}
75
76#[derive(Debug)]
78#[cfg_attr(feature = "defmt", derive(defmt::Format))]
79#[instability::unstable]
80#[non_exhaustive]
81pub struct Sniffer<'d> {
82 _phantom: PhantomData<&'d ()>,
83}
84
85impl Sniffer<'_> {
86 pub(crate) fn new() -> Self {
87 assert!(try_acquire(SNIFFER_BIT), "sniffer already in use");
88
89 let res =
92 esp_wifi_result!(unsafe { esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb)) });
93 if res.is_err() {
94 release(SNIFFER_BIT);
95 unwrap!(res);
96 }
97
98 Self {
99 _phantom: PhantomData,
100 }
101 }
102
103 #[instability::unstable]
105 pub fn set_promiscuous_mode(&self, enabled: bool) -> Result<(), WifiError> {
106 esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(enabled) })?;
107 Ok(())
108 }
109
110 #[instability::unstable]
112 pub fn send_raw_frame(
113 &mut self,
114 use_sta_interface: bool,
115 buffer: &[u8],
116 use_internal_seq_num: bool,
117 ) -> Result<(), WifiError> {
118 esp_wifi_result!(unsafe {
119 esp_wifi_80211_tx(
120 if use_sta_interface {
121 wifi_interface_t_WIFI_IF_STA
122 } else {
123 wifi_interface_t_WIFI_IF_AP
124 } as wifi_interface_t,
125 buffer.as_ptr() as *const _,
126 buffer.len() as i32,
127 use_internal_seq_num,
128 )
129 })
130 }
131
132 #[instability::unstable]
134 pub fn set_receive_cb(&mut self, cb: fn(PromiscuousPkt<'_>)) {
135 SNIFFER_CB.with(|callback| *callback = Some(cb));
136 }
137}
138
139impl Drop for Sniffer<'_> {
140 fn drop(&mut self) {
141 SNIFFER_CB.with(|callback| *callback = None);
144 if let Err(e) = esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(false) }) {
147 warn!(
148 "Failed to disable promiscuous mode on sniffer drop: {:?}",
149 e
150 );
151 }
152 if let Err(e) = esp_wifi_result!(unsafe { esp_wifi_set_promiscuous_rx_cb(None) }) {
153 warn!(
154 "Failed to unregister promiscuous rx cb on sniffer drop: {:?}",
155 e
156 );
157 }
158 release(SNIFFER_BIT);
159 }
160}