Skip to content

Commit 6b77fa8

Browse files
committed
survey: implement Nl80211SurveyInfo
Signed-off-by: Fabian Viöl <[email protected]>
1 parent c3e57b9 commit 6b77fa8

File tree

9 files changed

+383
-12
lines changed

9 files changed

+383
-12
lines changed

examples/dump_nl80211_survey.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use std::env::args;
4+
5+
use anyhow::{bail, Context, Error};
6+
use futures::stream::TryStreamExt;
7+
8+
fn main() -> Result<(), Error> {
9+
let argv: Vec<_> = args().collect();
10+
11+
if argv.len() < 2 {
12+
eprintln!("Usage: dump_nl80211_survey <interface index>");
13+
bail!("Required arguments not given");
14+
}
15+
16+
let err_msg = format!("Invalid interface index value: {}", argv[1]);
17+
let index = argv[1].parse::<u32>().context(err_msg)?;
18+
19+
let rt = tokio::runtime::Builder::new_current_thread()
20+
.enable_io()
21+
.build()
22+
.unwrap();
23+
rt.block_on(dump_survey(index))?;
24+
25+
Ok(())
26+
}
27+
28+
async fn dump_survey(if_index: u32) -> Result<(), Error> {
29+
let (connection, handle, _) = wl_nl80211::new_connection()?;
30+
tokio::spawn(connection);
31+
32+
let mut survey_handle = handle
33+
.survey()
34+
.dump(wl_nl80211::Nl80211Survey::new(if_index).radio(true).build())
35+
.execute()
36+
.await;
37+
38+
let mut msgs = Vec::new();
39+
while let Some(msg) = survey_handle.try_next().await? {
40+
msgs.push(msg);
41+
}
42+
assert!(!msgs.is_empty());
43+
for msg in msgs {
44+
println!("{:?}", msg);
45+
}
46+
Ok(())
47+
}

examples/nl80211_trigger_scan.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,37 @@ fn main() -> Result<(), Error> {
2121
.enable_time()
2222
.build()
2323
.unwrap();
24-
rt.block_on(dump_scan(index));
24+
rt.block_on(dump_scan(index))?;
2525

2626
Ok(())
2727
}
2828

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

33+
let duration = 5000;
3334
let attrs = wl_nl80211::Nl80211Scan::new(if_index)
34-
.duration(5000)
35+
.duration(duration)
3536
.passive(true)
3637
.build();
3738

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

4041
let mut msgs = Vec::new();
41-
while let Some(msg) = scan_handle.try_next().await.unwrap() {
42+
while let Some(msg) = scan_handle.try_next().await? {
4243
msgs.push(msg);
4344
}
44-
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
45+
tokio::time::sleep(std::time::Duration::from_millis(duration.into())).await;
4546

4647
let mut dump = handle.scan().dump(if_index).execute().await;
4748
let mut msgs = Vec::new();
48-
while let Some(msg) = dump.try_next().await.unwrap() {
49+
while let Some(msg) = dump.try_next().await? {
4950
msgs.push(msg);
5051
}
5152
assert!(!msgs.is_empty());
5253
for msg in msgs {
5354
println!("{msg:?}");
5455
}
56+
Ok(())
5557
}

src/attr.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ use crate::{
4747
Nl80211IfTypeExtCapas, Nl80211IfaceComb, Nl80211IfaceFrameType,
4848
Nl80211InterfaceType, Nl80211InterfaceTypes, Nl80211MloLink,
4949
Nl80211ScanFlags, Nl80211SchedScanMatch, Nl80211SchedScanPlan,
50-
Nl80211StationInfo, Nl80211TransmitQueueStat, Nl80211VhtCapability,
51-
Nl80211WowlanTrigersSupport,
50+
Nl80211StationInfo, Nl80211SurveyInfo, Nl80211TransmitQueueStat,
51+
Nl80211VhtCapability, Nl80211WowlanTrigersSupport,
5252
};
5353

5454
const ETH_ALEN: usize = 6;
@@ -204,7 +204,7 @@ const NL80211_ATTR_WIPHY_RTS_THRESHOLD: u16 = 64;
204204
// const NL80211_ATTR_KEYS:u16 = 81;
205205
// const NL80211_ATTR_PID:u16 = 82;
206206
const NL80211_ATTR_4ADDR: u16 = 83;
207-
// const NL80211_ATTR_SURVEY_INFO:u16 = 84;
207+
const NL80211_ATTR_SURVEY_INFO: u16 = 84;
208208
// const NL80211_ATTR_PMKID:u16 = 85;
209209
const NL80211_ATTR_MAX_NUM_PMKIDS: u16 = 86;
210210
// const NL80211_ATTR_DURATION:u16 = 87;
@@ -339,7 +339,7 @@ const NL80211_ATTR_MAX_CSA_COUNTERS: u16 = 206;
339339
const NL80211_ATTR_MAC_MASK: u16 = 215;
340340
const NL80211_ATTR_WIPHY_SELF_MANAGED_REG: u16 = 216;
341341
const NL80211_ATTR_EXT_FEATURES: u16 = 217;
342-
// const NL80211_ATTR_SURVEY_RADIO_STATS:u16 = 218;
342+
const NL80211_ATTR_SURVEY_RADIO_STATS: u16 = 218;
343343
// const NL80211_ATTR_NETNS_FD:u16 = 219;
344344
const NL80211_ATTR_SCHED_SCAN_DELAY: u16 = 220;
345345
// const NL80211_ATTR_REG_INDOOR:u16 = 221;
@@ -480,6 +480,7 @@ pub enum Nl80211Attr {
480480
WiphyTxPowerLevel(u32),
481481
Ssid(String),
482482
StationInfo(Vec<Nl80211StationInfo>),
483+
SurveyInfo(Vec<Nl80211SurveyInfo>),
483484
TransmitQueueStats(Vec<Nl80211TransmitQueueStat>),
484485
TransmitQueueLimit(u32),
485486
TransmitQueueMemoryLimit(u32),
@@ -519,6 +520,7 @@ pub enum Nl80211Attr {
519520
/// in milliseconds
520521
MaxRemainOnChannelDuration(u32),
521522
OffchannelTxOk,
523+
SurveyRadioStats,
522524
WowlanTrigersSupport(Vec<Nl80211WowlanTrigersSupport>),
523525
SoftwareIftypes(Vec<Nl80211InterfaceType>),
524526
Features(Nl80211Features),
@@ -625,6 +627,7 @@ impl Nla for Nl80211Attr {
625627
| Self::MaxNumPmkids(_) => 1,
626628
Self::TransmitQueueStats(nlas) => nlas.as_slice().buffer_len(),
627629
Self::StationInfo(nlas) => nlas.as_slice().buffer_len(),
630+
Self::SurveyInfo(nlas) => nlas.as_slice().buffer_len(),
628631
Self::MloLinks(links) => links.as_slice().buffer_len(),
629632
Self::MaxScanIeLen(_) | Self::MaxSchedScanIeLen(_) => 2,
630633
Self::SupportIbssRsn
@@ -635,6 +638,7 @@ impl Nla for Nl80211Attr {
635638
| Self::TdlsExternalSetup
636639
| Self::ControlPortEthertype
637640
| Self::OffchannelTxOk
641+
| Self::SurveyRadioStats
638642
| Self::WiphySelfManagedReg => 0,
639643
Self::CipherSuites(s) => 4 * s.len(),
640644
Self::SupportedIftypes(s) => s.as_slice().buffer_len(),
@@ -704,6 +708,8 @@ impl Nla for Nl80211Attr {
704708
Self::WiphyTxPowerLevel(_) => NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
705709
Self::Ssid(_) => NL80211_ATTR_SSID,
706710
Self::StationInfo(_) => NL80211_ATTR_STA_INFO,
711+
Self::SurveyInfo(_) => NL80211_ATTR_SURVEY_INFO,
712+
Self::SurveyRadioStats => NL80211_ATTR_SURVEY_RADIO_STATS,
707713
Self::TransmitQueueStats(_) => NL80211_ATTR_TXQ_STATS,
708714
Self::TransmitQueueLimit(_) => NL80211_ATTR_TXQ_LIMIT,
709715
Self::TransmitQueueMemoryLimit(_) => NL80211_ATTR_TXQ_MEMORY_LIMIT,
@@ -836,10 +842,12 @@ impl Nla for Nl80211Attr {
836842
| Self::TdlsExternalSetup
837843
| Self::ControlPortEthertype
838844
| Self::OffchannelTxOk
845+
| Self::SurveyRadioStats
839846
| Self::WiphySelfManagedReg => (),
840847
Self::WiphyChannelType(d) => write_u32(buffer, (*d).into()),
841848
Self::ChannelWidth(d) => write_u32(buffer, (*d).into()),
842849
Self::StationInfo(nlas) => nlas.as_slice().emit(buffer),
850+
Self::SurveyInfo(nlas) => nlas.as_slice().emit(buffer),
843851
Self::TransmitQueueStats(nlas) => nlas.as_slice().emit(buffer),
844852
Self::MloLinks(links) => links.as_slice().emit(buffer),
845853
Self::WiphyRetryShort(d)
@@ -1048,6 +1056,21 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
10481056
}
10491057
Self::StationInfo(nlas)
10501058
}
1059+
NL80211_ATTR_SURVEY_INFO => {
1060+
let err_msg = format!(
1061+
"Invalid NL80211_ATTR_SURVEY_INFO value {payload:?}"
1062+
);
1063+
let mut nlas = Vec::new();
1064+
for nla in NlasIterator::new(payload) {
1065+
let nla = &nla.context(err_msg.clone())?;
1066+
nlas.push(
1067+
Nl80211SurveyInfo::parse(nla)
1068+
.context(err_msg.clone())?,
1069+
);
1070+
}
1071+
Self::SurveyInfo(nlas)
1072+
}
1073+
NL80211_ATTR_SURVEY_RADIO_STATS => Self::SurveyRadioStats,
10511074
NL80211_ATTR_TXQ_STATS => {
10521075
let err_msg =
10531076
format!("Invalid NL80211_ATTR_TXQ_STATS value {payload:?}");

src/handle.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use netlink_packet_utils::DecodeError;
88

99
use crate::{
1010
try_nl80211, Nl80211Error, Nl80211InterfaceHandle, Nl80211Message,
11-
Nl80211ScanHandle, Nl80211StationHandle, Nl80211WiphyHandle,
11+
Nl80211ScanHandle, Nl80211StationHandle, Nl80211SurveyHandle,
12+
Nl80211WiphyHandle,
1213
};
1314

1415
#[derive(Clone, Debug)]
@@ -41,6 +42,11 @@ impl Nl80211Handle {
4142
Nl80211ScanHandle::new(self.clone())
4243
}
4344

45+
// equivalent to `iw DEVICE survey dump` command
46+
pub fn survey(&self) -> Nl80211SurveyHandle {
47+
Nl80211SurveyHandle::new(self.clone())
48+
}
49+
4450
pub async fn request(
4551
&mut self,
4652
message: NetlinkMessage<GenlMessage<Nl80211Message>>,

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod mlo;
1818
mod scan;
1919
mod station;
2020
mod stats;
21+
mod survey;
2122
mod wifi4;
2223
mod wifi5;
2324
mod wifi6;
@@ -63,6 +64,10 @@ pub use self::station::{
6364
pub use self::stats::{
6465
NestedNl80211TidStats, Nl80211TidStats, Nl80211TransmitQueueStat,
6566
};
67+
pub use self::survey::{
68+
Nl80211Survey, Nl80211SurveyGetRequest, Nl80211SurveyHandle,
69+
Nl80211SurveyInfo,
70+
};
6671
pub use self::wifi4::{
6772
Nl80211ElementHtCap, Nl80211HtAMpduPara, Nl80211HtAselCaps,
6873
Nl80211HtCapabilityMask, Nl80211HtCaps, Nl80211HtExtendedCap,

src/survey/get.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use futures::TryStream;
4+
use netlink_packet_core::{NLM_F_DUMP, NLM_F_REQUEST};
5+
use netlink_packet_generic::GenlMessage;
6+
7+
use crate::{
8+
nl80211_execute, Nl80211Attr, Nl80211Command, Nl80211Error, Nl80211Handle,
9+
Nl80211Message,
10+
};
11+
12+
pub struct Nl80211SurveyGetRequest {
13+
handle: Nl80211Handle,
14+
attributes: Vec<Nl80211Attr>,
15+
}
16+
17+
impl Nl80211SurveyGetRequest {
18+
pub(crate) fn new(
19+
handle: Nl80211Handle,
20+
attributes: Vec<Nl80211Attr>,
21+
) -> Self {
22+
Self { handle, attributes }
23+
}
24+
25+
pub async fn execute(
26+
self,
27+
) -> impl TryStream<Ok = GenlMessage<Nl80211Message>, Error = Nl80211Error>
28+
{
29+
let Self {
30+
mut handle,
31+
attributes,
32+
} = self;
33+
34+
let nl80211_msg = Nl80211Message {
35+
cmd: Nl80211Command::GetSurvey,
36+
attributes,
37+
};
38+
39+
let flags = NLM_F_REQUEST | NLM_F_DUMP;
40+
41+
nl80211_execute(&mut handle, nl80211_msg, flags).await
42+
}
43+
}

src/survey/handle.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use netlink_packet_utils::nla::Nla;
4+
5+
use crate::{
6+
Nl80211Attr, Nl80211AttrsBuilder, Nl80211Handle, Nl80211SurveyGetRequest,
7+
};
8+
9+
pub struct Nl80211SurveyHandle(Nl80211Handle);
10+
11+
#[derive(Debug)]
12+
pub struct Nl80211Survey;
13+
14+
impl Nl80211Survey {
15+
/// Perform a survey dump
16+
pub fn new(if_index: u32) -> Nl80211AttrsBuilder<Self> {
17+
Nl80211AttrsBuilder::<Self>::new().if_index(if_index)
18+
}
19+
}
20+
21+
impl Nl80211AttrsBuilder<Nl80211Survey> {
22+
/// Request overall radio statistics to be returned along with other survey
23+
/// data
24+
pub fn radio(self, value: bool) -> Self {
25+
if value {
26+
self.replace(Nl80211Attr::SurveyRadioStats)
27+
} else {
28+
self.remove(Nl80211Attr::SurveyRadioStats.kind())
29+
}
30+
}
31+
}
32+
33+
impl Nl80211SurveyHandle {
34+
pub fn new(handle: Nl80211Handle) -> Self {
35+
Self(handle)
36+
}
37+
38+
/// Retrieve the survey info
39+
/// (equivalent to `iw dev DEV survey dump`)
40+
pub fn dump(
41+
&mut self,
42+
attributes: Vec<Nl80211Attr>,
43+
) -> Nl80211SurveyGetRequest {
44+
Nl80211SurveyGetRequest::new(self.0.clone(), attributes)
45+
}
46+
}

src/survey/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
mod get;
4+
mod handle;
5+
mod survey_info;
6+
7+
pub use self::get::Nl80211SurveyGetRequest;
8+
pub use self::handle::{Nl80211Survey, Nl80211SurveyHandle};
9+
pub use self::survey_info::Nl80211SurveyInfo;

0 commit comments

Comments
 (0)