esp_radio/wifi/
sniffer.rs

1//! Wi-Fi sniffer.
2
3use core::marker::PhantomData;
4
5use esp_sync::NonReentrantMutex;
6
7use super::RxControlInfo;
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/// Represents a Wi-Fi packet in promiscuous mode.
25#[derive(Debug)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27#[instability::unstable]
28pub struct PromiscuousPkt<'a> {
29    /// Control information related to packet reception.
30    pub rx_cntl: RxControlInfo,
31    /// Frame type of the received packet.
32    pub frame_type: wifi_promiscuous_pkt_type_t,
33    /// Length of the received packet.
34    pub len: usize,
35    /// Data contained in the received packet.
36    pub data: &'a [u8],
37}
38
39#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
40impl PromiscuousPkt<'_> {
41    /// # Safety
42    ///
43    /// When calling this, you have to ensure, that `buf` points to a valid
44    /// [wifi_promiscuous_pkt_t].
45    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/// A Wi-Fi sniffer.
77#[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        // This shouldn't fail, since the way this is created, means that wifi will
88        // always be initialized.
89        unwrap!(esp_wifi_result!(unsafe {
90            esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb))
91        }));
92
93        Self {
94            _phantom: PhantomData,
95        }
96    }
97
98    /// Set promiscuous mode enabled or disabled.
99    #[instability::unstable]
100    pub fn set_promiscuous_mode(&self, enabled: bool) -> Result<(), WifiError> {
101        esp_wifi_result!(unsafe { esp_wifi_set_promiscuous(enabled) })?;
102        Ok(())
103    }
104
105    /// Transmit a raw frame.
106    #[instability::unstable]
107    pub fn send_raw_frame(
108        &mut self,
109        use_sta_interface: bool,
110        buffer: &[u8],
111        use_internal_seq_num: bool,
112    ) -> Result<(), WifiError> {
113        esp_wifi_result!(unsafe {
114            esp_wifi_80211_tx(
115                if use_sta_interface {
116                    wifi_interface_t_WIFI_IF_STA
117                } else {
118                    wifi_interface_t_WIFI_IF_AP
119                } as wifi_interface_t,
120                buffer.as_ptr() as *const _,
121                buffer.len() as i32,
122                use_internal_seq_num,
123            )
124        })
125    }
126
127    /// Set the callback for receiving a packet.
128    #[instability::unstable]
129    pub fn set_receive_cb(&mut self, cb: fn(PromiscuousPkt<'_>)) {
130        SNIFFER_CB.with(|callback| *callback = Some(cb));
131    }
132}