Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

scan: Add scan trigger and schedule support #21

Merged
merged 1 commit into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@ netlink-proto = { default-features = false, version = "0.11.2" }
netlink-sys = { version = "0.8.4" }

[dev-dependencies]
tokio = { version = "1.11.0", features = ["macros", "rt", "rt-multi-thread"] }
env_logger = "0.9.0"

[dev-dependencies.tokio]
version = "1.11.0"
features = ["macros", "rt", "rt-multi-thread", "time"]
55 changes: 55 additions & 0 deletions examples/nl80211_trigger_scan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT

use std::env::args;

use anyhow::{bail, Context, Error};
use futures::stream::TryStreamExt;

fn main() -> Result<(), Error> {
let argv: Vec<_> = args().collect();

if argv.len() < 2 {
eprintln!("Usage: nl80211_trigger_scan <interface index>");
bail!("Required arguments not given");
}

let err_msg = format!("Invalid interface index value: {}", argv[1]);
let index = argv[1].parse::<u32>().context(err_msg)?;

let rt = tokio::runtime::Builder::new_current_thread()
.enable_io()
.enable_time()
.build()
.unwrap();
rt.block_on(dump_scan(index));

Ok(())
}

async fn dump_scan(if_index: u32) {
let (connection, handle, _) = wl_nl80211::new_connection().unwrap();
tokio::spawn(connection);

let attrs = wl_nl80211::Nl80211Scan::new(if_index)
.duration(5000)
.passive(true)
.build();

let mut scan_handle = handle.scan().trigger(attrs).execute().await;

let mut msgs = Vec::new();
while let Some(msg) = scan_handle.try_next().await.unwrap() {
msgs.push(msg);
}
tokio::time::sleep(std::time::Duration::from_secs(5)).await;

let mut dump = handle.scan().dump(if_index).execute().await;
let mut msgs = Vec::new();
while let Some(msg) = dump.try_next().await.unwrap() {
msgs.push(msg);
}
assert!(!msgs.is_empty());
for msg in msgs {
println!("{:?}", msg);
}
}
168 changes: 150 additions & 18 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,21 @@ use netlink_packet_utils::{

use crate::{
bytes::{write_u16, write_u32, write_u64},
scan::{Nla80211ScanFreqNlas, Nla80211ScanSsidNlas},
wiphy::Nl80211Commands,
Nl80211Band, Nl80211BandTypes, Nl80211BssInfo, Nl80211ChannelWidth,
Nl80211CipherSuit, Nl80211Command, Nl80211ExtFeature, Nl80211ExtFeatures,
Nl80211ExtendedCapability, Nl80211Features, Nl80211HtCapabilityMask,
Nl80211HtWiphyChannelType, Nl80211IfMode, Nl80211IfTypeExtCapa,
Nl80211IfTypeExtCapas, Nl80211IfaceComb, Nl80211IfaceFrameType,
Nl80211InterfaceType, Nl80211InterfaceTypes, Nl80211MloLink,
Nl80211ScanFlags, Nl80211SchedScanMatch, Nl80211SchedScanPlan,
Nl80211StationInfo, Nl80211TransmitQueueStat, Nl80211VhtCapability,
Nl80211WowlanTrigersSupport,
};

const ETH_ALEN: usize = 6;

struct MacAddressNlas(Vec<MacAddressNla>);

impl std::ops::Deref for MacAddressNlas {
Expand Down Expand Up @@ -160,8 +164,8 @@ const NL80211_ATTR_WIPHY_CHANNEL_TYPE: u16 = 39;
// const NL80211_ATTR_MGMT_SUBTYPE:u16 = 41;
// const NL80211_ATTR_IE:u16 = 42;
const NL80211_ATTR_MAX_NUM_SCAN_SSIDS: u16 = 43;
// const NL80211_ATTR_SCAN_FREQUENCIES:u16 = 44;
// const NL80211_ATTR_SCAN_SSIDS:u16 = 45;
const NL80211_ATTR_SCAN_FREQUENCIES: u16 = 44;
const NL80211_ATTR_SCAN_SSIDS: u16 = 45;
const NL80211_ATTR_GENERATION: u16 = 46;
const NL80211_ATTR_BSS: u16 = 47;
// const NL80211_ATTR_REG_INITIATOR:u16 = 48;
Expand Down Expand Up @@ -236,7 +240,7 @@ const NL80211_ATTR_SUPPORT_MESH_AUTH: u16 = 115;
// const NL80211_ATTR_STA_PLINK_STATE:u16 = 116;
// const NL80211_ATTR_WOWLAN_TRIGGERS:u16 = 117;
const NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: u16 = 118;
// const NL80211_ATTR_SCHED_SCAN_INTERVAL:u16 = 119;
const NL80211_ATTR_SCHED_SCAN_INTERVAL: u16 = 119;
const NL80211_ATTR_INTERFACE_COMBINATIONS: u16 = 120;
const NL80211_ATTR_SOFTWARE_IFTYPES: u16 = 121;
// const NL80211_ATTR_REKEY_DATA:u16 = 122;
Expand All @@ -249,7 +253,7 @@ const NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: u16 = 124;
// const NL80211_ATTR_STA_WME:u16 = 129;
const NL80211_ATTR_SUPPORT_AP_UAPSD: u16 = 130;
const NL80211_ATTR_ROAM_SUPPORT: u16 = 131;
// const NL80211_ATTR_SCHED_SCAN_MATCH:u16 = 132;
const NL80211_ATTR_SCHED_SCAN_MATCH: u16 = 132;
const NL80211_ATTR_MAX_MATCH_SETS: u16 = 133;
// const NL80211_ATTR_PMKSA_CANDIDATE:u16 = 134;
// const NL80211_ATTR_TX_NO_CCK_RATE:u16 = 135;
Expand All @@ -275,7 +279,7 @@ const NL80211_ATTR_WDEV: u16 = 153;
// const NL80211_ATTR_CONN_FAILED_REASON:u16 = 155;
// const NL80211_ATTR_AUTH_DATA:u16 = 156;
const NL80211_ATTR_VHT_CAPABILITY: u16 = 157;
// const NL80211_ATTR_SCAN_FLAGS:u16 = 158;
const NL80211_ATTR_SCAN_FLAGS: u16 = 158;
const NL80211_ATTR_CHANNEL_WIDTH: u16 = 159;
const NL80211_ATTR_CENTER_FREQ1: u16 = 160;
const NL80211_ATTR_CENTER_FREQ2: u16 = 161;
Expand Down Expand Up @@ -332,17 +336,17 @@ const NL80211_ATTR_MAX_CSA_COUNTERS: u16 = 206;
// const NL80211_ATTR_ADMITTED_TIME:u16 = 212;
// const NL80211_ATTR_SMPS_MODE:u16 = 213;
// const NL80211_ATTR_OPER_CLASS:u16 = 214;
// const NL80211_ATTR_MAC_MASK:u16 = 215;
const NL80211_ATTR_MAC_MASK: u16 = 215;
const NL80211_ATTR_WIPHY_SELF_MANAGED_REG: u16 = 216;
const NL80211_ATTR_EXT_FEATURES: u16 = 217;
// const NL80211_ATTR_SURVEY_RADIO_STATS:u16 = 218;
// const NL80211_ATTR_NETNS_FD:u16 = 219;
// const NL80211_ATTR_SCHED_SCAN_DELAY:u16 = 220;
const NL80211_ATTR_SCHED_SCAN_DELAY: u16 = 220;
// const NL80211_ATTR_REG_INDOOR:u16 = 221;
const NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: u16 = 222;
const NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: u16 = 223;
const NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: u16 = 224;
// const NL80211_ATTR_SCHED_SCAN_PLANS:u16 = 225;
const NL80211_ATTR_SCHED_SCAN_PLANS: u16 = 225;
// const NL80211_ATTR_PBSS:u16 = 226;
// const NL80211_ATTR_BSS_SELECT:u16 = 227;
// const NL80211_ATTR_STA_SUPPORT_P2P_PS:u16 = 228;
Expand All @@ -352,7 +356,7 @@ const NL80211_ATTR_IFTYPE_EXT_CAPA: u16 = 230;
// const NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR:u16 = 232;
// const NL80211_ATTR_SCAN_START_TIME_TSF:u16 = 233;
// const NL80211_ATTR_SCAN_START_TIME_TSF_BSSID:u16 = 234;
// const NL80211_ATTR_MEASUREMENT_DURATION:u16 = 235;
const NL80211_ATTR_MEASUREMENT_DURATION: u16 = 235;
// const NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY:u16 = 236;
// const NL80211_ATTR_MESH_PEER_AID:u16 = 237;
// const NL80211_ATTR_NAN_MASTER_PREF:u16 = 238;
Expand Down Expand Up @@ -452,8 +456,6 @@ const NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: u16 = 323;
// const NL80211_ATTR_WIPHY_RADIOS:u16 = 331;
// const NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS:u16 = 332;

const ETH_ALEN: usize = 6;

#[derive(Debug, PartialEq, Eq, Clone)]
#[non_exhaustive]
pub enum Nl80211Attr {
Expand All @@ -464,6 +466,7 @@ pub enum Nl80211Attr {
IfType(Nl80211InterfaceType),
IfTypeExtCap(Vec<Nl80211IfTypeExtCapa>),
Mac([u8; ETH_ALEN]),
MacMask([u8; ETH_ALEN]),
MacAddrs(Vec<[u8; ETH_ALEN]>),
Wdev(u64),
Generation(u32),
Expand Down Expand Up @@ -546,6 +549,33 @@ pub enum Nl80211Attr {
MaxHwTimestampPeers(u16),
/// Basic Service Set (BSS)
Bss(Vec<Nl80211BssInfo>),
ScanSsids(Vec<String>),
ScanFlags(Nl80211ScanFlags),
MeasurementDuration(u16),
/// Scan interval in millisecond(ms)
SchedScanInterval(u32),
/// Delay before the first cycle of a scheduled scan is started. Or the
/// delay before a WoWLAN net-detect scan is started, counting from the
/// moment the system is suspended. This value is in seconds.
SchedScanDelay(u32),
/// Scan frequencies in MHz.
ScanFrequencies(Vec<u32>),
/// Sets of attributes to match during scheduled scans. Only BSSs
/// that match any of the sets will be reported. These are pass-thru
/// filter rules. For a match to succeed, the BSS must match all
/// attributes of a set. Since not every hardware supports matching all
/// types of attributes, there is no guarantee that the reported BSSs are
/// fully complying with the match sets and userspace needs to be able to
/// ignore them by itself. Thus, the implementation is somewhat
/// hardware-dependent, but this is only an optimization and the userspace
/// application needs to handle all the non-filtered results anyway.
SchedScanMatch(Vec<Nl80211SchedScanMatch>),
/// A list of scan plans for scheduled scan. Each scan plan defines the
/// number of scan iterations and the interval between scans. The last scan
/// plan will always run infinitely, thus it must not specify the number of
/// iterations, only the interval between scans. The scan plans are
/// executed sequentially.
SchedScanPlans(Vec<Nl80211SchedScanPlan>),
Other(DefaultNla),
}

Expand Down Expand Up @@ -576,10 +606,12 @@ impl Nla for Nl80211Attr {
| Self::SchedScanMaxReqs(_)
| Self::TransmitQueueLimit(_)
| Self::TransmitQueueMemoryLimit(_)
| Self::TransmitQueueQuantum(_) => 4,
| Self::TransmitQueueQuantum(_)
| Self::SchedScanInterval(_)
| Self::SchedScanDelay(_) => 4,
Self::Wdev(_) => 8,
Self::IfName(s) | Self::Ssid(s) | Self::WiphyName(s) => s.len() + 1,
Self::Mac(_) => ETH_ALEN,
Self::Mac(_) | Self::MacMask(_) => ETH_ALEN,
Self::MacAddrs(s) => {
MacAddressNlas::from(s).as_slice().buffer_len()
}
Expand Down Expand Up @@ -633,9 +665,19 @@ impl Nla for Nl80211Attr {
Self::EmlCapability(_)
| Self::MldCapaAndOps(_)
| Self::MaxNumAkmSuites(_)
| Self::MaxHwTimestampPeers(_) => 2,
| Self::MaxHwTimestampPeers(_)
| Self::MeasurementDuration(_) => 2,
Self::Bands(_) => Nl80211BandTypes::LENGTH,
Self::Bss(v) => v.as_slice().buffer_len(),
Self::ScanSsids(v) => {
Nla80211ScanSsidNlas::from(v).as_slice().buffer_len()
}
Self::ScanFlags(v) => v.buffer_len(),
Self::ScanFrequencies(v) => {
Nla80211ScanFreqNlas::from(v).as_slice().buffer_len()
}
Self::SchedScanMatch(v) => v.as_slice().buffer_len(),
Self::SchedScanPlans(v) => v.as_slice().buffer_len(),
Self::Other(attr) => attr.value_len(),
}
}
Expand All @@ -648,6 +690,7 @@ impl Nla for Nl80211Attr {
Self::IfName(_) => NL80211_ATTR_IFNAME,
Self::IfType(_) => NL80211_ATTR_IFTYPE,
Self::Mac(_) => NL80211_ATTR_MAC,
Self::MacMask(_) => NL80211_ATTR_MAC_MASK,
Self::MacAddrs(_) => NL80211_ATTR_MAC_ADDRS,
Self::Wdev(_) => NL80211_ATTR_WDEV,
Self::Generation(_) => NL80211_ATTR_GENERATION,
Expand Down Expand Up @@ -733,6 +776,14 @@ impl Nla for Nl80211Attr {
Self::MaxNumAkmSuites(_) => NL80211_ATTR_MAX_NUM_AKM_SUITES,
Self::MaxHwTimestampPeers(_) => NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS,
Self::Bss(_) => NL80211_ATTR_BSS,
Self::ScanSsids(_) => NL80211_ATTR_SCAN_SSIDS,
Self::ScanFlags(_) => NL80211_ATTR_SCAN_FLAGS,
Self::MeasurementDuration(_) => NL80211_ATTR_MEASUREMENT_DURATION,
Self::SchedScanInterval(_) => NL80211_ATTR_SCHED_SCAN_INTERVAL,
Self::SchedScanDelay(_) => NL80211_ATTR_SCHED_SCAN_DELAY,
Self::ScanFrequencies(_) => NL80211_ATTR_SCAN_FREQUENCIES,
Self::SchedScanMatch(_) => NL80211_ATTR_SCHED_SCAN_MATCH,
Self::SchedScanPlans(_) => NL80211_ATTR_SCHED_SCAN_PLANS,
Self::Other(attr) => attr.kind(),
}
}
Expand Down Expand Up @@ -760,13 +811,15 @@ impl Nla for Nl80211Attr {
| Self::SchedScanMaxReqs(d)
| Self::TransmitQueueLimit(d)
| Self::TransmitQueueMemoryLimit(d)
| Self::TransmitQueueQuantum(d) => write_u32(buffer, *d),
| Self::TransmitQueueQuantum(d)
| Self::SchedScanInterval(d)
| Self::SchedScanDelay(d) => write_u32(buffer, *d),
Self::MaxScanIeLen(d) | Self::MaxSchedScanIeLen(d) => {
write_u16(buffer, *d)
}
Self::Wdev(d) => write_u64(buffer, *d),
Self::IfType(d) => write_u32(buffer, (*d).into()),
Self::Mac(s) => buffer.copy_from_slice(s),
Self::Mac(s) | Self::MacMask(s) => buffer.copy_from_slice(s),
Self::MacAddrs(s) => {
MacAddressNlas::from(s).as_slice().emit(buffer)
}
Expand Down Expand Up @@ -834,9 +887,19 @@ impl Nla for Nl80211Attr {
Self::EmlCapability(d)
| Self::MldCapaAndOps(d)
| Self::MaxNumAkmSuites(d)
| Self::MaxHwTimestampPeers(d) => write_u16(buffer, *d),
| Self::MaxHwTimestampPeers(d)
| Self::MeasurementDuration(d) => write_u16(buffer, *d),
Self::Bands(v) => v.emit(buffer),
Self::Bss(v) => v.as_slice().emit(buffer),
Self::ScanSsids(v) => {
Nla80211ScanSsidNlas::from(v).as_slice().emit(buffer)
}
Self::ScanFlags(v) => v.emit(buffer),
Self::ScanFrequencies(v) => {
Nla80211ScanFreqNlas::from(v).as_slice().emit(buffer)
}
Self::SchedScanMatch(v) => v.as_slice().emit(buffer),
Self::SchedScanPlans(v) => v.as_slice().emit(buffer),
Self::Other(attr) => attr.emit(buffer),
}
}
Expand Down Expand Up @@ -882,11 +945,26 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
ret
} else {
return Err(format!(
"Invalid length of NL80211_ATTR_MAC, expected length {} got {:?}",
"Invalid length of NL80211_ATTR_MAC, \
expected length {} got {:?}",
ETH_ALEN, payload
)
.into());
}),
NL80211_ATTR_MAC_MASK => {
Self::MacMask(if payload.len() == ETH_ALEN {
let mut ret = [0u8; ETH_ALEN];
ret.copy_from_slice(&payload[..ETH_ALEN]);
ret
} else {
return Err(format!(
"Invalid length of NL80211_ATTR_MAC_MASK, \
expected length {} got {:?}",
ETH_ALEN, payload
)
.into());
})
}
NL80211_ATTR_MAC_ADDRS => {
Self::MacAddrs(MacAddressNlas::parse(payload)?.into())
}
Expand Down Expand Up @@ -1347,6 +1425,60 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
payload
))?,
),
NL80211_ATTR_SCAN_SSIDS => {
Self::ScanSsids(Nla80211ScanSsidNlas::parse(payload)?.into())
}
NL80211_ATTR_SCAN_FLAGS => {
Self::ScanFlags(Nl80211ScanFlags::parse(payload)?)
}
NL80211_ATTR_MEASUREMENT_DURATION => {
let err_msg = format!(
"Invalid NL80211_ATTR_MEASUREMENT_DURATION value {:?}",
payload
);
Self::MeasurementDuration(parse_u16(payload).context(err_msg)?)
}
NL80211_ATTR_SCHED_SCAN_INTERVAL => {
let err_msg = format!(
"Invalid NL80211_ATTR_SCHED_SCAN_INTERVAL value {:?}",
payload
);
Self::SchedScanInterval(parse_u32(payload).context(err_msg)?)
}
NL80211_ATTR_SCHED_SCAN_DELAY => {
let err_msg = format!(
"Invalid NL80211_ATTR_SCHED_SCAN_DELAY value {:?}",
payload
);
Self::SchedScanDelay(parse_u32(payload).context(err_msg)?)
}
NL80211_ATTR_SCAN_FREQUENCIES => Self::ScanFrequencies(
Nla80211ScanFreqNlas::parse(payload)?.into(),
),
NL80211_ATTR_SCHED_SCAN_MATCH => {
let err_msg = format!(
"Invalid NL80211_ATTR_SCHED_SCAN_MATCH value {:?}",
payload
);
let mut nlas = Vec::new();
for nla in NlasIterator::new(payload) {
let nla = &nla.context(err_msg.clone())?;
nlas.push(Nl80211SchedScanMatch::parse(nla)?);
}
Self::SchedScanMatch(nlas)
}
NL80211_ATTR_SCHED_SCAN_PLANS => {
let err_msg = format!(
"Invalid NL80211_ATTR_SCHED_SCAN_PLANS value {:?}",
payload
);
let mut nlas = Vec::new();
for nla in NlasIterator::new(payload) {
let nla = &nla.context(err_msg.clone())?;
nlas.push(Nl80211SchedScanPlan::parse(nla)?);
}
Self::SchedScanPlans(nlas)
}
_ => Self::Other(
DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?,
),
Expand Down
Loading
Loading