1003 lines
27 KiB
Rust
1003 lines
27 KiB
Rust
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
|
use std::collections::HashMap;
|
|
use std::ffi::c_void;
|
|
use std::ptr;
|
|
use std::sync::atomic::{AtomicI32, Ordering};
|
|
use std::sync::Mutex;
|
|
|
|
use super::net::{netif_carrier_off, netif_carrier_on, NetDevice};
|
|
|
|
#[derive(Clone, Default)]
|
|
struct WirelessEventState {
|
|
new_sta: Option<[u8; 6]>,
|
|
mgmt_rx_freq: u32,
|
|
mgmt_rx_signal: i32,
|
|
mgmt_rx_len: usize,
|
|
mgmt_rx_data: Vec<u8>,
|
|
mgmt_tx_cookie: u64,
|
|
mgmt_tx_len: usize,
|
|
mgmt_tx_ack: bool,
|
|
mgmt_tx_data: Vec<u8>,
|
|
sched_scan_reqid: u64,
|
|
roc_cookie: u64,
|
|
roc_chan_freq: u16,
|
|
roc_band: u32,
|
|
roc_duration: u32,
|
|
roc_active: bool,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct WiphyBands {
|
|
bands: [usize; 3],
|
|
}
|
|
|
|
unsafe impl Send for WiphyBands {}
|
|
|
|
lazy_static::lazy_static! {
|
|
static ref WIRELESS_EVENTS: Mutex<HashMap<usize, WirelessEventState>> = Mutex::new(HashMap::new());
|
|
static ref BSS_REGISTRY: Mutex<Vec<Box<Cfg80211Bss>>> = Mutex::new(Vec::new());
|
|
static ref BSS_IES: Mutex<HashMap<usize, Vec<u8>>> = Mutex::new(HashMap::new());
|
|
static ref WIPY_BANDS_MAP: Mutex<HashMap<usize, WiphyBands>> = Mutex::new(HashMap::new());
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct Wiphy {
|
|
pub priv_data: *mut c_void,
|
|
pub registered: AtomicI32,
|
|
pub interface_modes: u32,
|
|
pub max_scan_ssids: i32,
|
|
pub max_scan_ie_len: i32,
|
|
priv_alloc_size: usize,
|
|
priv_alloc_align: usize,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct WirelessDev {
|
|
pub wiphy: *mut Wiphy,
|
|
pub netdev: *mut c_void,
|
|
pub iftype: u32,
|
|
pub scan_in_flight: bool,
|
|
pub scan_aborted: bool,
|
|
pub connecting: bool,
|
|
pub connected: bool,
|
|
pub locally_generated: bool,
|
|
pub last_status: u16,
|
|
pub last_reason: u16,
|
|
pub has_bssid: bool,
|
|
pub last_bssid: [u8; 6],
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct Cfg80211ScanInfo {
|
|
pub aborted: bool,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct Cfg80211ScanRequest {
|
|
pub wiphy: *mut Wiphy,
|
|
pub wdev: *mut WirelessDev,
|
|
pub n_ssids: u32,
|
|
pub n_channels: u32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct Cfg80211Ssid {
|
|
pub ssid: [u8; 32],
|
|
pub ssid_len: u8,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct KeyParams {
|
|
pub key: *const u8,
|
|
pub key_len: u8,
|
|
pub cipher: u32,
|
|
pub key_idx: u8,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct Cfg80211ConnectParams {
|
|
pub ssid: *const u8,
|
|
pub ssid_len: usize,
|
|
pub bssid: *const u8,
|
|
pub ie: *const u8,
|
|
pub ie_len: usize,
|
|
pub key: KeyParams,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct StationParameters {
|
|
pub supported_rates: *const u8,
|
|
pub supported_rates_len: usize,
|
|
pub sta_flags_mask: u32,
|
|
pub sta_flags_set: u32,
|
|
}
|
|
|
|
fn update_event_state<F>(key: usize, update: F)
|
|
where
|
|
F: FnOnce(&mut WirelessEventState),
|
|
{
|
|
if let Ok(mut events) = WIRELESS_EVENTS.lock() {
|
|
update(events.entry(key).or_default());
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn wiphy_new_nm(
|
|
_ops: *const c_void,
|
|
sizeof_priv: usize,
|
|
_requested_name: *const u8,
|
|
) -> *mut Wiphy {
|
|
let mut wiphy = Box::new(Wiphy {
|
|
priv_data: ptr::null_mut(),
|
|
registered: AtomicI32::new(0),
|
|
interface_modes: 0,
|
|
max_scan_ssids: 4,
|
|
max_scan_ie_len: 512,
|
|
priv_alloc_size: 0,
|
|
priv_alloc_align: 0,
|
|
});
|
|
|
|
if sizeof_priv != 0 {
|
|
let layout = match Layout::from_size_align(sizeof_priv, 16) {
|
|
Ok(layout) => layout,
|
|
Err(_) => return ptr::null_mut(),
|
|
};
|
|
let ptr = unsafe { alloc_zeroed(layout) } as *mut c_void;
|
|
if ptr.is_null() {
|
|
return ptr::null_mut();
|
|
}
|
|
wiphy.priv_data = ptr;
|
|
wiphy.priv_alloc_size = sizeof_priv;
|
|
wiphy.priv_alloc_align = 16;
|
|
}
|
|
|
|
Box::into_raw(wiphy)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn wiphy_free(wiphy: *mut Wiphy) {
|
|
if wiphy.is_null() {
|
|
return;
|
|
}
|
|
let wiphy_key = wiphy as usize;
|
|
if let Ok(mut events) = WIRELESS_EVENTS.lock() {
|
|
events.remove(&wiphy_key);
|
|
}
|
|
if let Ok(mut registry) = BSS_REGISTRY.lock() {
|
|
if let Ok(mut ies_map) = BSS_IES.lock() {
|
|
for entry in registry.iter() {
|
|
if entry.wiphy == wiphy_key {
|
|
let ptr = entry.as_ref() as *const Cfg80211Bss as usize;
|
|
ies_map.remove(&ptr);
|
|
}
|
|
}
|
|
}
|
|
registry.retain(|e| e.wiphy != wiphy_key);
|
|
}
|
|
if let Ok(mut bands_map) = WIPY_BANDS_MAP.lock() {
|
|
bands_map.remove(&wiphy_key);
|
|
}
|
|
unsafe {
|
|
let wiphy_box = Box::from_raw(wiphy);
|
|
if !wiphy_box.priv_data.is_null() {
|
|
if let Ok(layout) = Layout::from_size_align(
|
|
wiphy_box.priv_alloc_size.max(1),
|
|
wiphy_box.priv_alloc_align.max(1),
|
|
) {
|
|
dealloc(wiphy_box.priv_data.cast::<u8>(), layout);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn wiphy_register(wiphy: *mut Wiphy) -> i32 {
|
|
if wiphy.is_null() {
|
|
return -22;
|
|
}
|
|
if unsafe { &*wiphy }.registered.load(Ordering::Acquire) != 0 {
|
|
return -16;
|
|
}
|
|
unsafe { &*wiphy }.registered.store(1, Ordering::Release);
|
|
0
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn wiphy_unregister(wiphy: *mut Wiphy) {
|
|
if wiphy.is_null() {
|
|
return;
|
|
}
|
|
unsafe { &*wiphy }.registered.store(0, Ordering::Release);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_scan_done(
|
|
request: *mut Cfg80211ScanRequest,
|
|
info: *const Cfg80211ScanInfo,
|
|
) {
|
|
if request.is_null() {
|
|
return;
|
|
}
|
|
|
|
let wdev = unsafe { (*request).wdev };
|
|
if wdev.is_null() {
|
|
return;
|
|
}
|
|
|
|
unsafe {
|
|
(*wdev).scan_in_flight = false;
|
|
(*wdev).scan_aborted = if info.is_null() {
|
|
false
|
|
} else {
|
|
(*info).aborted
|
|
};
|
|
}
|
|
}
|
|
|
|
fn netdev_to_wireless_dev(dev: *mut c_void) -> *mut WirelessDev {
|
|
if dev.is_null() {
|
|
return ptr::null_mut();
|
|
}
|
|
let dev = dev.cast::<NetDevice>();
|
|
unsafe { (*dev).ieee80211_ptr.cast::<WirelessDev>() }
|
|
}
|
|
|
|
fn copy_bssid(dst: &mut WirelessDev, bssid: *const u8) {
|
|
if bssid.is_null() {
|
|
dst.has_bssid = false;
|
|
dst.last_bssid = [0; 6];
|
|
return;
|
|
}
|
|
|
|
unsafe {
|
|
ptr::copy_nonoverlapping(bssid, dst.last_bssid.as_mut_ptr(), dst.last_bssid.len());
|
|
}
|
|
dst.has_bssid = true;
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_connect_result(
|
|
dev: *mut c_void,
|
|
bssid: *const u8,
|
|
_req_ie: *const u8,
|
|
_req_ie_len: usize,
|
|
_resp_ie: *const u8,
|
|
_resp_ie_len: usize,
|
|
status: u16,
|
|
_gfp: u32,
|
|
) {
|
|
let wdev = netdev_to_wireless_dev(dev);
|
|
if wdev.is_null() {
|
|
return;
|
|
}
|
|
|
|
unsafe {
|
|
let wdev_ref = &mut *wdev;
|
|
wdev_ref.connecting = false;
|
|
wdev_ref.connected = status == 0;
|
|
wdev_ref.last_status = status;
|
|
wdev_ref.locally_generated = false;
|
|
copy_bssid(wdev_ref, bssid);
|
|
}
|
|
|
|
if status == 0 {
|
|
netif_carrier_on(dev.cast::<NetDevice>());
|
|
} else {
|
|
netif_carrier_off(dev.cast::<NetDevice>());
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_disconnected(
|
|
dev: *mut c_void,
|
|
reason: u16,
|
|
_ie: *const u8,
|
|
_ie_len: usize,
|
|
locally_generated: bool,
|
|
_gfp: u32,
|
|
) {
|
|
let wdev = netdev_to_wireless_dev(dev);
|
|
if !wdev.is_null() {
|
|
unsafe {
|
|
let wdev_ref = &mut *wdev;
|
|
wdev_ref.connecting = false;
|
|
wdev_ref.connected = false;
|
|
wdev_ref.last_reason = reason;
|
|
wdev_ref.locally_generated = locally_generated;
|
|
wdev_ref.has_bssid = false;
|
|
wdev_ref.last_bssid = [0; 6];
|
|
}
|
|
}
|
|
|
|
netif_carrier_off(dev.cast::<NetDevice>());
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_connect_bss(
|
|
dev: *mut c_void,
|
|
bssid: *const u8,
|
|
req_ie: *const u8,
|
|
req_ie_len: usize,
|
|
resp_ie: *const u8,
|
|
resp_ie_len: usize,
|
|
status: u16,
|
|
gfp: u32,
|
|
) {
|
|
cfg80211_connect_result(
|
|
dev,
|
|
bssid,
|
|
req_ie,
|
|
req_ie_len,
|
|
resp_ie,
|
|
resp_ie_len,
|
|
status,
|
|
gfp,
|
|
)
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_ready_on_channel(
|
|
wdev: *mut WirelessDev,
|
|
cookie: u64,
|
|
chan: *mut c_void,
|
|
_chan_type: u32,
|
|
duration: u32,
|
|
_gfp: u32,
|
|
) {
|
|
if wdev.is_null() {
|
|
return;
|
|
}
|
|
let key = wdev as usize;
|
|
let (freq, band) = if chan.is_null() {
|
|
(0u16, 0u32)
|
|
} else {
|
|
let ch = chan.cast::<Ieee80211Channel>();
|
|
unsafe { ((*ch).center_freq, (*ch).band) }
|
|
};
|
|
update_event_state(key, |state| {
|
|
state.roc_cookie = cookie;
|
|
state.roc_chan_freq = freq;
|
|
state.roc_band = band;
|
|
state.roc_duration = duration;
|
|
state.roc_active = true;
|
|
});
|
|
log::trace!(
|
|
"cfg80211_ready_on_channel: wdev={:#x} cookie={} freq={} duration={}",
|
|
key,
|
|
cookie,
|
|
freq,
|
|
duration
|
|
);
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct Ieee80211Channel {
|
|
pub band: u32,
|
|
pub center_freq: u16,
|
|
pub hw_value: u16,
|
|
pub flags: u32,
|
|
pub max_power: i8,
|
|
pub max_reg_power: i8,
|
|
pub max_antenna_gain: i8,
|
|
pub beacon_found: bool,
|
|
}
|
|
|
|
pub const NL80211_BAND_2GHZ: u32 = 0;
|
|
pub const NL80211_BAND_5GHZ: u32 = 1;
|
|
pub const NL80211_BAND_6GHZ: u32 = 2;
|
|
|
|
pub const IEEE80211_CHAN_DISABLED: u32 = 1 << 0;
|
|
pub const IEEE80211_CHAN_NO_IR: u32 = 1 << 1;
|
|
pub const IEEE80211_CHAN_RADAR: u32 = 1 << 2;
|
|
pub const IEEE80211_CHAN_NO_HT40PLUS: u32 = 1 << 3;
|
|
pub const IEEE80211_CHAN_NO_HT40MINUS: u32 = 1 << 4;
|
|
pub const IEEE80211_CHAN_NO_OFDM: u32 = 1 << 5;
|
|
pub const IEEE80211_CHAN_NO_80MHZ: u32 = 1 << 6;
|
|
pub const IEEE80211_CHAN_NO_160MHZ: u32 = 1 << 7;
|
|
|
|
#[repr(C)]
|
|
pub struct Ieee80211Rate {
|
|
pub flags: u32,
|
|
pub bitrate: u16,
|
|
pub hw_value: u16,
|
|
pub hw_value_short: u16,
|
|
}
|
|
|
|
pub const IEEE80211_RATE_SHORT_PREAMBLE: u32 = 1 << 0;
|
|
pub const IEEE80211_RATE_MANDATORY: u32 = 1 << 1;
|
|
pub const IEEE80211_RATE_ERP_G: u32 = 1 << 2;
|
|
|
|
#[repr(C)]
|
|
pub struct Ieee80211SupportedBand {
|
|
pub channels: *mut Ieee80211Channel,
|
|
pub n_channels: usize,
|
|
pub bitrates: *mut Ieee80211Rate,
|
|
pub n_bitrates: usize,
|
|
pub ht_cap: *mut c_void,
|
|
pub vht_cap: *mut c_void,
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn wiphy_bands_append(
|
|
wiphy: *mut Wiphy,
|
|
band_idx: u32,
|
|
band: *mut Ieee80211SupportedBand,
|
|
) -> i32 {
|
|
if wiphy.is_null() || band.is_null() {
|
|
return -22;
|
|
}
|
|
|
|
if band_idx > NL80211_BAND_6GHZ {
|
|
return -22;
|
|
}
|
|
|
|
let band_ref = unsafe { &*band };
|
|
if band_ref.n_channels == 0 || band_ref.channels.is_null() {
|
|
return -22;
|
|
}
|
|
|
|
let key = wiphy as usize;
|
|
if let Ok(mut map) = WIPY_BANDS_MAP.lock() {
|
|
let entry = map
|
|
.entry(key)
|
|
.or_insert_with(|| WiphyBands { bands: [0; 3] });
|
|
entry.bands[band_idx as usize] = band as usize;
|
|
}
|
|
|
|
0
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct Cfg80211Bss {
|
|
pub bssid: [u8; 6],
|
|
pub channel: *mut Ieee80211Channel,
|
|
pub signal: i16,
|
|
pub capability: u16,
|
|
pub beacon_interval: u16,
|
|
pub ies: *const u8,
|
|
pub ies_len: usize,
|
|
wiphy: usize,
|
|
}
|
|
|
|
unsafe impl Send for Cfg80211Bss {}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_inform_bss(
|
|
wiphy: *mut Wiphy,
|
|
wdev: *mut WirelessDev,
|
|
_freq: u32,
|
|
bssid: *const u8,
|
|
_tsf: u64,
|
|
capability: u16,
|
|
beacon_interval: u16,
|
|
ies: *const u8,
|
|
ies_len: usize,
|
|
signal: i32,
|
|
_gfp: u32,
|
|
) -> *mut Cfg80211Bss {
|
|
if wiphy.is_null() || wdev.is_null() || bssid.is_null() {
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let mut bssid_bytes = [0; 6];
|
|
unsafe {
|
|
ptr::copy_nonoverlapping(bssid, bssid_bytes.as_mut_ptr(), bssid_bytes.len());
|
|
}
|
|
|
|
let Ok(mut registry) = BSS_REGISTRY.lock() else {
|
|
log::warn!("cfg80211_inform_bss: registry lock failed");
|
|
return ptr::null_mut();
|
|
};
|
|
|
|
let clamped_signal = signal.clamp(i16::MIN as i32, i16::MAX as i32) as i16;
|
|
|
|
let ies_owned: Vec<u8> = if !ies.is_null() && ies_len > 0 {
|
|
unsafe { std::slice::from_raw_parts(ies, ies_len) }.to_vec()
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
for entry in registry.iter_mut() {
|
|
if entry.bssid == bssid_bytes && entry.wiphy == wiphy as usize {
|
|
entry.signal = clamped_signal;
|
|
entry.capability = capability;
|
|
entry.beacon_interval = beacon_interval;
|
|
let entry_ptr = entry.as_ref() as *const Cfg80211Bss as usize;
|
|
if let Ok(mut ies_map) = BSS_IES.lock() {
|
|
entry.ies_len = ies_owned.len();
|
|
if ies_owned.is_empty() {
|
|
entry.ies = ptr::null();
|
|
ies_map.remove(&entry_ptr);
|
|
} else {
|
|
let stored = ies_map.entry(entry_ptr).or_default();
|
|
stored.clear();
|
|
stored.extend_from_slice(&ies_owned);
|
|
entry.ies = stored.as_ptr();
|
|
}
|
|
}
|
|
return entry.as_ref() as *const Cfg80211Bss as *mut Cfg80211Bss;
|
|
}
|
|
}
|
|
|
|
let bss = Box::new(Cfg80211Bss {
|
|
bssid: bssid_bytes,
|
|
channel: ptr::null_mut(),
|
|
signal: clamped_signal,
|
|
capability,
|
|
beacon_interval,
|
|
ies: ptr::null(),
|
|
ies_len: 0,
|
|
wiphy: wiphy as usize,
|
|
});
|
|
registry.push(bss);
|
|
let entry = match registry.last_mut() {
|
|
Some(e) => e,
|
|
None => return ptr::null_mut(),
|
|
};
|
|
let entry_ptr = entry.as_ref() as *const Cfg80211Bss as usize;
|
|
if let Ok(mut ies_map) = BSS_IES.lock() {
|
|
if !ies_owned.is_empty() {
|
|
ies_map.insert(entry_ptr, ies_owned);
|
|
entry.ies = ies_map[&entry_ptr].as_ptr();
|
|
entry.ies_len = ies_map[&entry_ptr].len();
|
|
}
|
|
}
|
|
entry.as_ref() as *const Cfg80211Bss as *mut Cfg80211Bss
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_put_bss(bss: *mut Cfg80211Bss) {
|
|
if bss.is_null() {
|
|
return;
|
|
}
|
|
let bss_addr = bss as usize;
|
|
let Ok(mut registry) = BSS_REGISTRY.lock() else {
|
|
log::warn!("cfg80211_put_bss: registry lock failed");
|
|
return;
|
|
};
|
|
let before = registry.len();
|
|
registry.retain(|entry| entry.as_ref() as *const Cfg80211Bss as usize != bss_addr);
|
|
let removed = before != registry.len();
|
|
if removed {
|
|
if let Ok(mut ies_map) = BSS_IES.lock() {
|
|
ies_map.remove(&bss_addr);
|
|
}
|
|
}
|
|
log::trace!(
|
|
"cfg80211_put_bss: released reference bss={:#x} (removed={})",
|
|
bss_addr,
|
|
removed
|
|
);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_get_bss(
|
|
wiphy: *mut Wiphy,
|
|
_band: u32,
|
|
bssid: *const u8,
|
|
ssid: *const u8,
|
|
ssid_len: usize,
|
|
_bss_type: u32,
|
|
_privacy: u32,
|
|
) -> *mut Cfg80211Bss {
|
|
if wiphy.is_null() {
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let Ok(registry) = BSS_REGISTRY.lock() else {
|
|
return ptr::null_mut();
|
|
};
|
|
|
|
let want_bssid = if bssid.is_null() {
|
|
None
|
|
} else {
|
|
let mut bytes = [0u8; 6];
|
|
unsafe { ptr::copy_nonoverlapping(bssid, bytes.as_mut_ptr(), 6) };
|
|
Some(bytes)
|
|
};
|
|
|
|
let want_ssid = if ssid.is_null() || ssid_len == 0 {
|
|
None
|
|
} else {
|
|
let slice = unsafe { std::slice::from_raw_parts(ssid, ssid_len) };
|
|
Some(slice.to_vec())
|
|
};
|
|
|
|
for entry in registry.iter() {
|
|
if entry.wiphy != wiphy as usize {
|
|
continue;
|
|
}
|
|
if let Some(ref wb) = want_bssid {
|
|
if entry.bssid != *wb {
|
|
continue;
|
|
}
|
|
}
|
|
if let Some(ref ws) = want_ssid {
|
|
if entry.ies.is_null() || entry.ies_len < ws.len() + 2 {
|
|
continue;
|
|
}
|
|
unsafe {
|
|
let ies_slice = std::slice::from_raw_parts(entry.ies, entry.ies_len);
|
|
let mut offset = 0;
|
|
let mut found = false;
|
|
while offset + 2 <= ies_slice.len() {
|
|
let tag_id = ies_slice[offset];
|
|
let tag_len = ies_slice[offset + 1] as usize;
|
|
if offset + 2 + tag_len > ies_slice.len() {
|
|
break;
|
|
}
|
|
if tag_id == 0 && tag_len == ws.len() {
|
|
if &ies_slice[offset + 2..offset + 2 + tag_len] == ws.as_slice() {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
offset += 2 + tag_len;
|
|
}
|
|
if !found {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
return entry.as_ref() as *const Cfg80211Bss as *mut Cfg80211Bss;
|
|
}
|
|
|
|
ptr::null_mut()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_new_sta(
|
|
dev: *mut c_void,
|
|
mac_addr: *const u8,
|
|
_params: *const StationParameters,
|
|
_gfp: u32,
|
|
) {
|
|
if dev.is_null() || mac_addr.is_null() {
|
|
return;
|
|
}
|
|
|
|
let wdev = netdev_to_wireless_dev(dev);
|
|
if wdev.is_null() || unsafe { (*wdev).wiphy }.is_null() {
|
|
return;
|
|
}
|
|
|
|
let mut addr = [0u8; 6];
|
|
unsafe { ptr::copy_nonoverlapping(mac_addr, addr.as_mut_ptr(), addr.len()) };
|
|
update_event_state(unsafe { (*wdev).wiphy as usize }, |state| {
|
|
state.new_sta = Some(addr)
|
|
});
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_rx_mgmt(
|
|
wdev: *mut WirelessDev,
|
|
freq: u32,
|
|
sig_dbm: i32,
|
|
buf: *const u8,
|
|
len: usize,
|
|
_gfp: u32,
|
|
) {
|
|
if wdev.is_null() || (buf.is_null() && len != 0) {
|
|
return;
|
|
}
|
|
|
|
let frame_data = if !buf.is_null() && len > 0 {
|
|
unsafe { std::slice::from_raw_parts(buf, len) }.to_vec()
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
update_event_state(wdev as usize, |state| {
|
|
state.mgmt_rx_freq = freq;
|
|
state.mgmt_rx_signal = sig_dbm;
|
|
state.mgmt_rx_len = len;
|
|
state.mgmt_rx_data = frame_data;
|
|
});
|
|
|
|
log::debug!(
|
|
"cfg80211_rx_mgmt: wdev={:#x} freq={} sig={} len={}",
|
|
wdev as usize,
|
|
freq,
|
|
sig_dbm,
|
|
len
|
|
);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_mgmt_tx_status(
|
|
wdev: *mut WirelessDev,
|
|
cookie: u64,
|
|
buf: *const u8,
|
|
len: usize,
|
|
ack: bool,
|
|
_gfp: u32,
|
|
) {
|
|
if wdev.is_null() || (buf.is_null() && len != 0) {
|
|
return;
|
|
}
|
|
|
|
let frame_data = if !buf.is_null() && len > 0 {
|
|
unsafe { std::slice::from_raw_parts(buf, len) }.to_vec()
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
update_event_state(wdev as usize, |state| {
|
|
state.mgmt_tx_cookie = cookie;
|
|
state.mgmt_tx_len = len;
|
|
state.mgmt_tx_ack = ack;
|
|
state.mgmt_tx_data = frame_data;
|
|
});
|
|
|
|
log::debug!(
|
|
"cfg80211_mgmt_tx_status: wdev={:#x} cookie={} len={} ack={}",
|
|
wdev as usize,
|
|
cookie,
|
|
len,
|
|
ack
|
|
);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn cfg80211_sched_scan_results(wiphy: *mut Wiphy, reqid: u64) {
|
|
if wiphy.is_null() {
|
|
return;
|
|
}
|
|
update_event_state(wiphy as usize, |state| state.sched_scan_reqid = reqid);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn ieee80211_channel_to_frequency(chan: u32, band: u32) -> u32 {
|
|
match band {
|
|
NL80211_BAND_2GHZ => match chan {
|
|
14 => 2484,
|
|
1..=13 => 2407 + chan * 5,
|
|
_ => 0,
|
|
},
|
|
NL80211_BAND_5GHZ => 5000 + chan * 5,
|
|
NL80211_BAND_6GHZ => {
|
|
if chan == 2 {
|
|
5935
|
|
} else if chan >= 1 {
|
|
5950 + chan * 5
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub extern "C" fn ieee80211_frequency_to_channel(freq: u32) -> u32 {
|
|
match freq {
|
|
2484 => 14,
|
|
2412..=2472 => (freq - 2407) / 5,
|
|
5000..=5895 => (freq - 5000) / 5,
|
|
5935 => 2,
|
|
5955..=7115 => (freq - 5950) / 5,
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::rust_impl::net::{alloc_netdev_mqs, free_netdev, netif_carrier_ok};
|
|
use std::ffi::CString;
|
|
|
|
#[test]
|
|
fn wiphy_registration_round_trip_works() {
|
|
let wiphy = wiphy_new_nm(ptr::null(), 0, ptr::null());
|
|
assert!(!wiphy.is_null());
|
|
assert_eq!(wiphy_register(wiphy), 0);
|
|
assert_eq!(wiphy_register(wiphy), -16);
|
|
assert_eq!(unsafe { (*wiphy).registered.load(Ordering::Acquire) }, 1);
|
|
wiphy_unregister(wiphy);
|
|
assert_eq!(unsafe { (*wiphy).registered.load(Ordering::Acquire) }, 0);
|
|
wiphy_free(wiphy);
|
|
}
|
|
|
|
#[test]
|
|
fn scan_and_connect_lifecycle_updates_wireless_state() {
|
|
let name = CString::new("wlan%d").expect("valid test CString");
|
|
let dev = alloc_netdev_mqs(0, name.as_ptr().cast::<u8>(), 0, None, 1, 1);
|
|
assert!(!dev.is_null());
|
|
|
|
let wiphy = wiphy_new_nm(ptr::null(), 32, ptr::null());
|
|
assert!(!wiphy.is_null());
|
|
|
|
let mut wdev = WirelessDev {
|
|
wiphy,
|
|
netdev: dev.cast::<c_void>(),
|
|
iftype: 0,
|
|
scan_in_flight: true,
|
|
scan_aborted: false,
|
|
connecting: true,
|
|
connected: false,
|
|
locally_generated: false,
|
|
last_status: u16::MAX,
|
|
last_reason: 0,
|
|
has_bssid: false,
|
|
last_bssid: [0; 6],
|
|
};
|
|
|
|
unsafe {
|
|
(*dev).ieee80211_ptr = (&mut wdev as *mut WirelessDev).cast::<c_void>();
|
|
}
|
|
|
|
let mut request = Cfg80211ScanRequest {
|
|
wiphy,
|
|
wdev: &mut wdev,
|
|
n_ssids: 1,
|
|
n_channels: 1,
|
|
};
|
|
let info = Cfg80211ScanInfo { aborted: true };
|
|
cfg80211_scan_done(&mut request, &info);
|
|
assert!(!wdev.scan_in_flight);
|
|
assert!(wdev.scan_aborted);
|
|
|
|
let bssid = [1, 2, 3, 4, 5, 6];
|
|
cfg80211_connect_result(
|
|
dev.cast::<c_void>(),
|
|
bssid.as_ptr(),
|
|
ptr::null(),
|
|
0,
|
|
ptr::null(),
|
|
0,
|
|
0,
|
|
0,
|
|
);
|
|
assert!(wdev.connected);
|
|
assert_eq!(wdev.last_status, 0);
|
|
assert!(wdev.has_bssid);
|
|
assert_eq!(wdev.last_bssid, bssid);
|
|
assert_eq!(netif_carrier_ok(dev), 1);
|
|
|
|
cfg80211_disconnected(dev.cast::<c_void>(), 7, ptr::null(), 0, true, 0);
|
|
assert!(!wdev.connected);
|
|
assert_eq!(wdev.last_reason, 7);
|
|
assert!(wdev.locally_generated);
|
|
assert_eq!(netif_carrier_ok(dev), 0);
|
|
|
|
wiphy_free(wiphy);
|
|
free_netdev(dev);
|
|
}
|
|
|
|
#[test]
|
|
fn ieee80211_channel_creation_and_flags_work() {
|
|
let channel = Ieee80211Channel {
|
|
band: NL80211_BAND_5GHZ,
|
|
center_freq: 5180,
|
|
hw_value: 36,
|
|
flags: IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_80MHZ,
|
|
max_power: 20,
|
|
max_reg_power: 23,
|
|
max_antenna_gain: 6,
|
|
beacon_found: true,
|
|
};
|
|
|
|
assert_eq!(channel.band, NL80211_BAND_5GHZ);
|
|
assert_eq!(channel.center_freq, 5180);
|
|
assert_eq!(channel.hw_value, 36);
|
|
assert_ne!(channel.flags & IEEE80211_CHAN_NO_IR, 0);
|
|
assert_ne!(channel.flags & IEEE80211_CHAN_RADAR, 0);
|
|
assert_ne!(channel.flags & IEEE80211_CHAN_NO_80MHZ, 0);
|
|
assert_eq!(channel.flags & IEEE80211_CHAN_DISABLED, 0);
|
|
assert!(channel.beacon_found);
|
|
}
|
|
|
|
#[test]
|
|
fn cfg80211_events_and_channel_frequency_conversions_work() {
|
|
let name = CString::new("wlan%d").expect("valid test CString");
|
|
let dev = alloc_netdev_mqs(0, name.as_ptr().cast::<u8>(), 0, None, 1, 1);
|
|
assert!(!dev.is_null());
|
|
let wiphy = wiphy_new_nm(ptr::null(), 0, ptr::null());
|
|
assert!(!wiphy.is_null());
|
|
let mut wdev = WirelessDev {
|
|
wiphy,
|
|
netdev: dev.cast::<c_void>(),
|
|
iftype: 0,
|
|
scan_in_flight: false,
|
|
scan_aborted: false,
|
|
connecting: false,
|
|
connected: false,
|
|
locally_generated: false,
|
|
last_status: 0,
|
|
last_reason: 0,
|
|
has_bssid: false,
|
|
last_bssid: [0; 6],
|
|
};
|
|
unsafe { (*dev).ieee80211_ptr = (&mut wdev as *mut WirelessDev).cast::<c_void>() };
|
|
|
|
let sta = [6u8, 5, 4, 3, 2, 1];
|
|
cfg80211_new_sta(dev.cast::<c_void>(), sta.as_ptr(), ptr::null(), 0);
|
|
cfg80211_rx_mgmt(&mut wdev, 2412, -42, sta.as_ptr(), sta.len(), 0);
|
|
cfg80211_mgmt_tx_status(&mut wdev, 99, sta.as_ptr(), sta.len(), true, 0);
|
|
cfg80211_sched_scan_results(wiphy, 1234);
|
|
|
|
let events = WIRELESS_EVENTS.lock().expect("wireless events lock");
|
|
let wiphy_state = events.get(&(wiphy as usize)).expect("wiphy event state");
|
|
assert_eq!(wiphy_state.new_sta, Some(sta));
|
|
assert_eq!(wiphy_state.sched_scan_reqid, 1234);
|
|
let wdev_state = events
|
|
.get(&((&mut wdev as *mut WirelessDev) as usize))
|
|
.expect("wdev event state");
|
|
assert_eq!(wdev_state.mgmt_rx_freq, 2412);
|
|
assert_eq!(wdev_state.mgmt_rx_signal, -42);
|
|
assert_eq!(wdev_state.mgmt_rx_data, sta.to_vec());
|
|
assert_eq!(wdev_state.mgmt_tx_cookie, 99);
|
|
assert!(wdev_state.mgmt_tx_ack);
|
|
assert_eq!(wdev_state.mgmt_tx_data, sta.to_vec());
|
|
drop(events);
|
|
|
|
assert_eq!(ieee80211_channel_to_frequency(1, NL80211_BAND_2GHZ), 2412);
|
|
assert_eq!(ieee80211_channel_to_frequency(36, NL80211_BAND_5GHZ), 5180);
|
|
assert_eq!(ieee80211_frequency_to_channel(2484), 14);
|
|
assert_eq!(ieee80211_frequency_to_channel(5955), 1);
|
|
|
|
wiphy_free(wiphy);
|
|
free_netdev(dev);
|
|
}
|
|
|
|
#[test]
|
|
fn test_cfg80211_put_bss_removes_from_registry() {
|
|
let name = CString::new("wlan%d").expect("valid test CString");
|
|
let dev = alloc_netdev_mqs(0, name.as_ptr().cast::<u8>(), 0, None, 1, 1);
|
|
assert!(!dev.is_null());
|
|
let wiphy = wiphy_new_nm(ptr::null(), 32, ptr::null());
|
|
assert!(!wiphy.is_null());
|
|
|
|
let mut wdev = WirelessDev {
|
|
wiphy,
|
|
netdev: dev.cast::<c_void>(),
|
|
iftype: 0,
|
|
scan_in_flight: false,
|
|
scan_aborted: false,
|
|
connecting: false,
|
|
connected: false,
|
|
locally_generated: false,
|
|
last_status: 0,
|
|
last_reason: 0,
|
|
has_bssid: false,
|
|
last_bssid: [0; 6],
|
|
};
|
|
unsafe { (*dev).ieee80211_ptr = (&mut wdev as *mut WirelessDev).cast::<c_void>() };
|
|
|
|
let bssid: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];
|
|
let ies = [1u8, 2, 3, 4];
|
|
|
|
let bss = cfg80211_inform_bss(
|
|
wiphy,
|
|
&mut wdev as *mut WirelessDev,
|
|
2412,
|
|
bssid.as_ptr(),
|
|
0,
|
|
0x0431,
|
|
100,
|
|
ies.as_ptr(),
|
|
ies.len(),
|
|
-50,
|
|
0,
|
|
);
|
|
assert!(!bss.is_null());
|
|
|
|
let found = cfg80211_get_bss(wiphy, 0, bssid.as_ptr(), ptr::null(), 0, 0, 0);
|
|
assert!(
|
|
!found.is_null(),
|
|
"BSS should be in registry after inform_bss"
|
|
);
|
|
|
|
cfg80211_put_bss(bss);
|
|
|
|
let after_put = cfg80211_get_bss(wiphy, 0, bssid.as_ptr(), ptr::null(), 0, 0, 0);
|
|
assert!(
|
|
after_put.is_null(),
|
|
"BSS should be removed from registry after put_bss"
|
|
);
|
|
|
|
wiphy_free(wiphy);
|
|
free_netdev(dev);
|
|
}
|
|
}
|