Skip to content

Commit

Permalink
scan: Add support of query scan results
Browse files Browse the repository at this point in the history
Work was based on #9 by
Rayzeq.

Introduce `Nl80211ScanHandle::dump()` equal to
`iw dev DEVICE scan dump`.

Example code included as `examples/dump_nl80211_scan.rs`

Signed-off-by: Gris Ge <[email protected]>
  • Loading branch information
cathay4t committed Sep 22, 2024
1 parent e0580ec commit 3adb7b9
Show file tree
Hide file tree
Showing 11 changed files with 1,515 additions and 4 deletions.
42 changes: 42 additions & 0 deletions examples/dump_nl80211_scan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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_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()
.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 mut scan_handle = handle.scan().dump(if_index).execute().await;

let mut msgs = Vec::new();
while let Some(msg) = scan_handle.try_next().await.unwrap() {
msgs.push(msg);
}
assert!(!msgs.is_empty());
for msg in msgs {
println!("{:?}", msg);
}
}
21 changes: 18 additions & 3 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ use netlink_packet_utils::{
use crate::{
bytes::{write_u16, write_u32, write_u64},
wiphy::Nl80211Commands,
Nl80211Band, Nl80211BandTypes, Nl80211ChannelWidth, Nl80211CipherSuit,
Nl80211Command, Nl80211ExtFeature, Nl80211ExtFeatures,
Nl80211Band, Nl80211BandTypes, Nl80211BssInfo, Nl80211ChannelWidth,
Nl80211CipherSuit, Nl80211Command, Nl80211ExtFeature, Nl80211ExtFeatures,
Nl80211ExtendedCapability, Nl80211Features, Nl80211HtCapabilityMask,
Nl80211HtWiphyChannelType, Nl80211IfMode, Nl80211IfTypeExtCapa,
Nl80211IfTypeExtCapas, Nl80211IfaceComb, Nl80211IfaceFrameType,
Expand Down Expand Up @@ -163,7 +163,7 @@ 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_GENERATION: u16 = 46;
// const NL80211_ATTR_BSS:u16 = 47;
const NL80211_ATTR_BSS: u16 = 47;
// const NL80211_ATTR_REG_INITIATOR:u16 = 48;
// const NL80211_ATTR_REG_TYPE:u16 = 49;
const NL80211_ATTR_SUPPORTED_COMMANDS: u16 = 50;
Expand Down Expand Up @@ -544,6 +544,8 @@ pub enum Nl80211Attr {
/// not specifying an address with set hardware timestamp) is
/// supported.
MaxHwTimestampPeers(u16),
/// Basic Service Set (BSS)
Bss(Vec<Nl80211BssInfo>),
Other(DefaultNla),
}

Expand Down Expand Up @@ -633,6 +635,7 @@ impl Nla for Nl80211Attr {
| Self::MaxNumAkmSuites(_)
| Self::MaxHwTimestampPeers(_) => 2,
Self::Bands(_) => Nl80211BandTypes::LENGTH,
Self::Bss(v) => v.as_slice().buffer_len(),
Self::Other(attr) => attr.value_len(),
}
}
Expand Down Expand Up @@ -729,6 +732,7 @@ impl Nla for Nl80211Attr {
Self::Bands(_) => NL80211_ATTR_BANDS,
Self::MaxNumAkmSuites(_) => NL80211_ATTR_MAX_NUM_AKM_SUITES,
Self::MaxHwTimestampPeers(_) => NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS,
Self::Bss(_) => NL80211_ATTR_BSS,
Self::Other(attr) => attr.kind(),
}
}
Expand Down Expand Up @@ -832,6 +836,7 @@ impl Nla for Nl80211Attr {
| Self::MaxNumAkmSuites(d)
| Self::MaxHwTimestampPeers(d) => write_u16(buffer, *d),
Self::Bands(v) => v.emit(buffer),
Self::Bss(v) => v.as_slice().emit(buffer),
Self::Other(attr) => attr.emit(buffer),
}
}
Expand Down Expand Up @@ -892,6 +897,16 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
);
Self::Generation(parse_u32(payload).context(err_msg)?)
}
NL80211_ATTR_BSS => {
let err_msg =
format!("Invalid NL80211_ATTR_BSS value {:?}", payload);
let mut nlas = Vec::new();
for nla in NlasIterator::new(payload) {
let nla = &nla.context(err_msg.clone())?;
nlas.push(Nl80211BssInfo::parse(nla)?);
}
Self::Bss(nlas)
}
NL80211_ATTR_4ADDR => {
let err_msg =
format!("Invalid NL80211_ATTR_4ADDR value {:?}", payload);
Expand Down
17 changes: 17 additions & 0 deletions src/bytes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: MIT

use netlink_packet_utils::DecodeError;

pub(crate) fn write_u16(buffer: &mut [u8], value: u16) {
buffer[..2].copy_from_slice(&value.to_ne_bytes())
}
Expand All @@ -12,10 +14,18 @@ pub(crate) fn write_u32(buffer: &mut [u8], value: u32) {
buffer[..4].copy_from_slice(&value.to_ne_bytes())
}

pub(crate) fn write_u32_le(buffer: &mut [u8], value: u32) {
buffer[..4].copy_from_slice(&value.to_le_bytes())
}

pub(crate) fn write_u64(buffer: &mut [u8], value: u64) {
buffer[..8].copy_from_slice(&value.to_ne_bytes())
}

pub(crate) fn write_i32(buffer: &mut [u8], value: i32) {
buffer[..4].copy_from_slice(&value.to_ne_bytes())
}

pub(crate) fn get_bit(data: &[u8], pos: usize) -> bool {
let index: usize = pos / 8;
let bit_pos: usize = pos % 8;
Expand Down Expand Up @@ -44,3 +54,10 @@ pub(crate) fn get_bits_as_u8(data: &[u8], start: usize, end: usize) -> u8 {
}
ret
}

pub(crate) fn parse_u16_le(payload: &[u8]) -> Result<u16, DecodeError> {
if payload.len() < 2 {
return Err(format!("Invalid payload for u16: {:?}", payload).into());
}
Ok(u16::from_le_bytes([payload[0], payload[1]]))
}
Loading

0 comments on commit 3adb7b9

Please sign in to comment.