Skip to content

survey: implement Nl80211SurveyInfo #26

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ version = "0.2.0"
authors = ["Gris Ge <[email protected]>"]
license = "MIT"
edition = "2021"
rust-version = "1.87"
description = "Linux kernel wireless(802.11) netlink Library"
homepage = "https://github.com/rust-netlink/wl-nl80211"
repository = "https://github.com/rust-netlink/wl-nl80211"
Expand Down
47 changes: 47 additions & 0 deletions examples/dump_nl80211_survey.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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: dump_nl80211_survey <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()
.build()
.unwrap();
rt.block_on(dump_survey(index))?;

Ok(())
}

async fn dump_survey(if_index: u32) -> Result<(), Error> {
let (connection, handle, _) = wl_nl80211::new_connection()?;
tokio::spawn(connection);

let mut survey_handle = handle
.survey()
.dump(wl_nl80211::Nl80211Survey::new(if_index).radio(true).build())
.execute()
.await;

let mut msgs = Vec::new();
while let Some(msg) = survey_handle.try_next().await? {
msgs.push(msg);
}
assert!(!msgs.is_empty());
for msg in msgs {
println!("{:?}", msg);
}
Ok(())
}
16 changes: 9 additions & 7 deletions examples/nl80211_trigger_scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,37 @@ fn main() -> Result<(), Error> {
.enable_time()
.build()
.unwrap();
rt.block_on(dump_scan(index));
rt.block_on(dump_scan(index))?;

Ok(())
}

async fn dump_scan(if_index: u32) {
let (connection, handle, _) = wl_nl80211::new_connection().unwrap();
async fn dump_scan(if_index: u32) -> Result<(), Error> {
let (connection, handle, _) = wl_nl80211::new_connection()?;
tokio::spawn(connection);

let duration = 5000;
let attrs = wl_nl80211::Nl80211Scan::new(if_index)
.duration(5000)
.duration(duration)
.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() {
while let Some(msg) = scan_handle.try_next().await? {
msgs.push(msg);
}
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
tokio::time::sleep(std::time::Duration::from_millis(duration.into())).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() {
while let Some(msg) = dump.try_next().await? {
msgs.push(msg);
}
assert!(!msgs.is_empty());
for msg in msgs {
println!("{msg:?}");
}
Ok(())
}
31 changes: 27 additions & 4 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ use crate::{
Nl80211IfTypeExtCapas, Nl80211IfaceComb, Nl80211IfaceFrameType,
Nl80211InterfaceType, Nl80211InterfaceTypes, Nl80211MloLink,
Nl80211ScanFlags, Nl80211SchedScanMatch, Nl80211SchedScanPlan,
Nl80211StationInfo, Nl80211TransmitQueueStat, Nl80211VhtCapability,
Nl80211WowlanTriggersSupport,
Nl80211StationInfo, Nl80211SurveyInfo, Nl80211TransmitQueueStat,
Nl80211VhtCapability, Nl80211WowlanTriggersSupport,
};

const ETH_ALEN: usize = 6;
Expand Down Expand Up @@ -204,7 +204,7 @@ const NL80211_ATTR_WIPHY_RTS_THRESHOLD: u16 = 64;
// const NL80211_ATTR_KEYS:u16 = 81;
// const NL80211_ATTR_PID:u16 = 82;
const NL80211_ATTR_4ADDR: u16 = 83;
// const NL80211_ATTR_SURVEY_INFO:u16 = 84;
const NL80211_ATTR_SURVEY_INFO: u16 = 84;
// const NL80211_ATTR_PMKID:u16 = 85;
const NL80211_ATTR_MAX_NUM_PMKIDS: u16 = 86;
// const NL80211_ATTR_DURATION:u16 = 87;
Expand Down Expand Up @@ -339,7 +339,7 @@ const NL80211_ATTR_MAX_CSA_COUNTERS: u16 = 206;
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_SURVEY_RADIO_STATS: u16 = 218;
// const NL80211_ATTR_NETNS_FD:u16 = 219;
const NL80211_ATTR_SCHED_SCAN_DELAY: u16 = 220;
// const NL80211_ATTR_REG_INDOOR:u16 = 221;
Expand Down Expand Up @@ -480,6 +480,7 @@ pub enum Nl80211Attr {
WiphyTxPowerLevel(u32),
Ssid(String),
StationInfo(Vec<Nl80211StationInfo>),
SurveyInfo(Vec<Nl80211SurveyInfo>),
TransmitQueueStats(Vec<Nl80211TransmitQueueStat>),
TransmitQueueLimit(u32),
TransmitQueueMemoryLimit(u32),
Expand Down Expand Up @@ -519,6 +520,7 @@ pub enum Nl80211Attr {
/// in milliseconds
MaxRemainOnChannelDuration(u32),
OffchannelTxOk,
SurveyRadioStats,
WowlanTriggersSupport(Vec<Nl80211WowlanTriggersSupport>),
SoftwareIftypes(Vec<Nl80211InterfaceType>),
Features(Nl80211Features),
Expand Down Expand Up @@ -625,6 +627,7 @@ impl Nla for Nl80211Attr {
| Self::MaxNumPmkids(_) => 1,
Self::TransmitQueueStats(nlas) => nlas.as_slice().buffer_len(),
Self::StationInfo(nlas) => nlas.as_slice().buffer_len(),
Self::SurveyInfo(nlas) => nlas.as_slice().buffer_len(),
Self::MloLinks(links) => links.as_slice().buffer_len(),
Self::MaxScanIeLen(_) | Self::MaxSchedScanIeLen(_) => 2,
Self::SupportIbssRsn
Expand All @@ -635,6 +638,7 @@ impl Nla for Nl80211Attr {
| Self::TdlsExternalSetup
| Self::ControlPortEthertype
| Self::OffchannelTxOk
| Self::SurveyRadioStats
| Self::WiphySelfManagedReg => 0,
Self::CipherSuites(s) => 4 * s.len(),
Self::SupportedIftypes(s) => s.as_slice().buffer_len(),
Expand Down Expand Up @@ -704,6 +708,8 @@ impl Nla for Nl80211Attr {
Self::WiphyTxPowerLevel(_) => NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
Self::Ssid(_) => NL80211_ATTR_SSID,
Self::StationInfo(_) => NL80211_ATTR_STA_INFO,
Self::SurveyInfo(_) => NL80211_ATTR_SURVEY_INFO,
Self::SurveyRadioStats => NL80211_ATTR_SURVEY_RADIO_STATS,
Self::TransmitQueueStats(_) => NL80211_ATTR_TXQ_STATS,
Self::TransmitQueueLimit(_) => NL80211_ATTR_TXQ_LIMIT,
Self::TransmitQueueMemoryLimit(_) => NL80211_ATTR_TXQ_MEMORY_LIMIT,
Expand Down Expand Up @@ -836,10 +842,12 @@ impl Nla for Nl80211Attr {
| Self::TdlsExternalSetup
| Self::ControlPortEthertype
| Self::OffchannelTxOk
| Self::SurveyRadioStats
| Self::WiphySelfManagedReg => (),
Self::WiphyChannelType(d) => write_u32(buffer, (*d).into()),
Self::ChannelWidth(d) => write_u32(buffer, (*d).into()),
Self::StationInfo(nlas) => nlas.as_slice().emit(buffer),
Self::SurveyInfo(nlas) => nlas.as_slice().emit(buffer),
Self::TransmitQueueStats(nlas) => nlas.as_slice().emit(buffer),
Self::MloLinks(links) => links.as_slice().emit(buffer),
Self::WiphyRetryShort(d)
Expand Down Expand Up @@ -1048,6 +1056,21 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
}
Self::StationInfo(nlas)
}
NL80211_ATTR_SURVEY_INFO => {
let err_msg = format!(
"Invalid NL80211_ATTR_SURVEY_INFO value {payload:?}"
);
let mut nlas = Vec::new();
for nla in NlasIterator::new(payload) {
let nla = &nla.context(err_msg.clone())?;
nlas.push(
Nl80211SurveyInfo::parse(nla)
.context(err_msg.clone())?,
);
}
Self::SurveyInfo(nlas)
}
NL80211_ATTR_SURVEY_RADIO_STATS => Self::SurveyRadioStats,
NL80211_ATTR_TXQ_STATS => {
let err_msg =
format!("Invalid NL80211_ATTR_TXQ_STATS value {payload:?}");
Expand Down
8 changes: 7 additions & 1 deletion src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use netlink_packet_utils::DecodeError;

use crate::{
try_nl80211, Nl80211Error, Nl80211InterfaceHandle, Nl80211Message,
Nl80211ScanHandle, Nl80211StationHandle, Nl80211WiphyHandle,
Nl80211ScanHandle, Nl80211StationHandle, Nl80211SurveyHandle,
Nl80211WiphyHandle,
};

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -41,6 +42,11 @@ impl Nl80211Handle {
Nl80211ScanHandle::new(self.clone())
}

// equivalent to `iw DEVICE survey dump` command
pub fn survey(&self) -> Nl80211SurveyHandle {
Nl80211SurveyHandle::new(self.clone())
}

pub async fn request(
&mut self,
message: NetlinkMessage<GenlMessage<Nl80211Message>>,
Expand Down
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod mlo;
mod scan;
mod station;
mod stats;
mod survey;
mod wifi4;
mod wifi5;
mod wifi6;
Expand Down Expand Up @@ -63,6 +64,10 @@ pub use self::station::{
pub use self::stats::{
NestedNl80211TidStats, Nl80211TidStats, Nl80211TransmitQueueStat,
};
pub use self::survey::{
Nl80211Survey, Nl80211SurveyGetRequest, Nl80211SurveyHandle,
Nl80211SurveyInfo,
};
pub use self::wifi4::{
Nl80211ElementHtCap, Nl80211HtAMpduPara, Nl80211HtAselCaps,
Nl80211HtCapabilityMask, Nl80211HtCaps, Nl80211HtExtendedCap,
Expand Down
43 changes: 43 additions & 0 deletions src/survey/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT

use futures::TryStream;
use netlink_packet_core::{NLM_F_DUMP, NLM_F_REQUEST};
use netlink_packet_generic::GenlMessage;

use crate::{
nl80211_execute, Nl80211Attr, Nl80211Command, Nl80211Error, Nl80211Handle,
Nl80211Message,
};

pub struct Nl80211SurveyGetRequest {
handle: Nl80211Handle,
attributes: Vec<Nl80211Attr>,
}

impl Nl80211SurveyGetRequest {
pub(crate) fn new(
handle: Nl80211Handle,
attributes: Vec<Nl80211Attr>,
) -> Self {
Self { handle, attributes }
}

pub async fn execute(
self,
) -> impl TryStream<Ok = GenlMessage<Nl80211Message>, Error = Nl80211Error>
{
let Self {
mut handle,
attributes,
} = self;

let nl80211_msg = Nl80211Message {
cmd: Nl80211Command::GetSurvey,
attributes,
};

let flags = NLM_F_REQUEST | NLM_F_DUMP;

nl80211_execute(&mut handle, nl80211_msg, flags).await
}
}
46 changes: 46 additions & 0 deletions src/survey/handle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT

use netlink_packet_utils::nla::Nla;

use crate::{
Nl80211Attr, Nl80211AttrsBuilder, Nl80211Handle, Nl80211SurveyGetRequest,
};

pub struct Nl80211SurveyHandle(Nl80211Handle);

#[derive(Debug)]
pub struct Nl80211Survey;

impl Nl80211Survey {
/// Perform a survey dump
pub fn new(if_index: u32) -> Nl80211AttrsBuilder<Self> {
Nl80211AttrsBuilder::<Self>::new().if_index(if_index)
}
}

impl Nl80211AttrsBuilder<Nl80211Survey> {
/// Request overall radio statistics to be returned along with other survey
/// data
pub fn radio(self, value: bool) -> Self {
if value {
self.replace(Nl80211Attr::SurveyRadioStats)
} else {
self.remove(Nl80211Attr::SurveyRadioStats.kind())
}
}
}

impl Nl80211SurveyHandle {
pub fn new(handle: Nl80211Handle) -> Self {
Self(handle)
}

/// Retrieve the survey info
/// (equivalent to `iw dev DEV survey dump`)
pub fn dump(
&mut self,
attributes: Vec<Nl80211Attr>,
) -> Nl80211SurveyGetRequest {
Nl80211SurveyGetRequest::new(self.0.clone(), attributes)
}
}
9 changes: 9 additions & 0 deletions src/survey/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT

mod get;
mod handle;
mod survey_info;

pub use self::get::Nl80211SurveyGetRequest;
pub use self::handle::{Nl80211Survey, Nl80211SurveyHandle};
pub use self::survey_info::Nl80211SurveyInfo;
Loading