Skip to content

Commit fae8d2e

Browse files
committed
Add survey dump
Signed-off-by: Fabian Viöl <fv@enqt.de>
1 parent 35d29ce commit fae8d2e

File tree

8 files changed

+329
-4
lines changed

8 files changed

+329
-4
lines changed

examples/dump_nl80211_survey.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 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.survey().dump(if_index).execute().await;
33+
34+
let mut msgs = Vec::new();
35+
while let Some(msg) = survey_handle.try_next().await? {
36+
msgs.push(msg);
37+
}
38+
assert!(!msgs.is_empty());
39+
for msg in msgs {
40+
println!("{:?}", msg);
41+
}
42+
Ok(())
43+
}

src/attr.rs

Lines changed: 21 additions & 3 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;
@@ -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),
@@ -625,6 +626,7 @@ impl Nla for Nl80211Attr {
625626
| Self::MaxNumPmkids(_) => 1,
626627
Self::TransmitQueueStats(nlas) => nlas.as_slice().buffer_len(),
627628
Self::StationInfo(nlas) => nlas.as_slice().buffer_len(),
629+
Self::SurveyInfo(nlas) => nlas.as_slice().buffer_len(),
628630
Self::MloLinks(links) => links.as_slice().buffer_len(),
629631
Self::MaxScanIeLen(_) | Self::MaxSchedScanIeLen(_) => 2,
630632
Self::SupportIbssRsn
@@ -704,6 +706,7 @@ impl Nla for Nl80211Attr {
704706
Self::WiphyTxPowerLevel(_) => NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
705707
Self::Ssid(_) => NL80211_ATTR_SSID,
706708
Self::StationInfo(_) => NL80211_ATTR_STA_INFO,
709+
Self::SurveyInfo(_) => NL80211_ATTR_SURVEY_INFO,
707710
Self::TransmitQueueStats(_) => NL80211_ATTR_TXQ_STATS,
708711
Self::TransmitQueueLimit(_) => NL80211_ATTR_TXQ_LIMIT,
709712
Self::TransmitQueueMemoryLimit(_) => NL80211_ATTR_TXQ_MEMORY_LIMIT,
@@ -840,6 +843,7 @@ impl Nla for Nl80211Attr {
840843
Self::WiphyChannelType(d) => write_u32(buffer, (*d).into()),
841844
Self::ChannelWidth(d) => write_u32(buffer, (*d).into()),
842845
Self::StationInfo(nlas) => nlas.as_slice().emit(buffer),
846+
Self::SurveyInfo(nlas) => nlas.as_slice().emit(buffer),
843847
Self::TransmitQueueStats(nlas) => nlas.as_slice().emit(buffer),
844848
Self::MloLinks(links) => links.as_slice().emit(buffer),
845849
Self::WiphyRetryShort(d)
@@ -1061,6 +1065,20 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
10611065
}
10621066
Self::StationInfo(nlas)
10631067
}
1068+
NL80211_ATTR_SURVEY_INFO => {
1069+
let err_msg = format!(
1070+
"Invalid NL80211_ATTR_SURVEY_INFO value {payload:?}"
1071+
);
1072+
let mut nlas = Vec::new();
1073+
for nla in NlasIterator::new(payload) {
1074+
let nla = &nla.context(err_msg.clone())?;
1075+
nlas.push(
1076+
Nl80211SurveyInfo::parse(nla)
1077+
.context(err_msg.clone())?,
1078+
);
1079+
}
1080+
Self::SurveyInfo(nlas)
1081+
}
10641082
NL80211_ATTR_TXQ_STATS => {
10651083
let err_msg = format!(
10661084
"Invalid NL80211_ATTR_TXQ_STATS value {:?}",

src/handle.rs

Lines changed: 8 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,12 @@ impl Nl80211Handle {
4142
Nl80211ScanHandle::new(self.clone())
4243
}
4344

45+
// equivalent to `iw DEVICE survey dump` command
46+
#[must_use]
47+
pub fn survey(&self) -> Nl80211SurveyHandle {
48+
Nl80211SurveyHandle::new(self.clone())
49+
}
50+
4451
pub async fn request(
4552
&mut self,
4653
message: NetlinkMessage<GenlMessage<Nl80211Message>>,

src/lib.rs

Lines changed: 4 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,9 @@ pub use self::station::{
6364
pub use self::stats::{
6465
NestedNl80211TidStats, Nl80211TidStats, Nl80211TransmitQueueStat,
6566
};
67+
pub use self::survey::{
68+
Nl80211SurveyGetRequest, Nl80211SurveyHandle, Nl80211SurveyInfo,
69+
};
6670
pub use self::wifi4::{
6771
Nl80211ElementHtCap, Nl80211HtAMpduPara, Nl80211HtAselCaps,
6872
Nl80211HtCapabilityMask, Nl80211HtCaps, Nl80211HtExtendedCap,

src/survey/get.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
if_index: u32,
15+
}
16+
17+
impl Nl80211SurveyGetRequest {
18+
pub(crate) fn new(handle: Nl80211Handle, if_index: u32) -> Self {
19+
Self { handle, if_index }
20+
}
21+
22+
pub async fn execute(
23+
self,
24+
) -> impl TryStream<Ok = GenlMessage<Nl80211Message>, Error = Nl80211Error>
25+
{
26+
let Self {
27+
mut handle,
28+
if_index,
29+
} = self;
30+
31+
let attributes = vec![Nl80211Attr::IfIndex(if_index)];
32+
let nl80211_msg = Nl80211Message {
33+
cmd: Nl80211Command::GetSurvey,
34+
attributes,
35+
};
36+
37+
let flags = NLM_F_REQUEST | NLM_F_DUMP;
38+
39+
nl80211_execute(&mut handle, nl80211_msg, flags).await
40+
}
41+
}

src/survey/handle.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use crate::{Nl80211Handle, Nl80211SurveyGetRequest};
4+
5+
pub struct Nl80211SurveyHandle(Nl80211Handle);
6+
7+
impl Nl80211SurveyHandle {
8+
#[must_use]
9+
pub fn new(handle: Nl80211Handle) -> Self {
10+
Self(handle)
11+
}
12+
13+
/// Retrieve the survey info
14+
/// (equivalent to `iw dev DEV survey dump`)
15+
#[must_use]
16+
pub fn dump(&mut self, if_index: u32) -> Nl80211SurveyGetRequest {
17+
Nl80211SurveyGetRequest::new(self.0.clone(), if_index)
18+
}
19+
}

src/survey/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mod get;
2+
mod handle;
3+
mod survey_info;
4+
5+
pub use self::get::Nl80211SurveyGetRequest;
6+
pub use self::handle::Nl80211SurveyHandle;
7+
pub use self::survey_info::Nl80211SurveyInfo;

0 commit comments

Comments
 (0)