Skip to content
Closed
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ smol_socket = ["netlink-proto/smol_socket", "async-std"]
[dependencies]
async-std = { version = "1.9.0", optional = true}
bitflags = "2"
bstr = "1.12.0"
byteorder = "1.4.3"
futures = "0.3.17"
log = "0.4.14"
thiserror = "1.0.29"
Expand Down
18 changes: 17 additions & 1 deletion examples/dump_nl80211_scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::env::args;

use futures::stream::TryStreamExt;
use netlink_packet_core::{DecodeError, ErrorContext};
use netlink_packet_core::{DecodeError, Emitable, ErrorContext, Parseable};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let argv: Vec<_> = args().collect();
Expand Down Expand Up @@ -41,5 +41,21 @@ async fn dump_scan(if_index: u32) {
assert!(!msgs.is_empty());
for msg in msgs {
println!("{msg:?}");

println!("Information Elements");
let ie: Vec<_> =
msg.payload.attributes.iter().filter_map(|attr| match attr {
wl_nl80211::Nl80211Attr::Bss(info) => {
let ies: Vec<_> = info.iter().filter_map(|info| match info {
wl_nl80211::Nl80211BssInfo::BeaconInformationElements(ie)| wl_nl80211::Nl80211BssInfo::InformationElements(ie) | wl_nl80211::Nl80211BssInfo::ProbeResponseInformationElements(ie) =>
Some(wl_nl80211::Nl80211Elements::parse(ie).unwrap()),
_ => None,
}).collect();
Some(ies)
},
_ => None,
}).flatten().collect();

println!("{ie:?}");
}
}
106 changes: 98 additions & 8 deletions examples/nl80211_trigger_scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,26 @@

use std::env::args;

use futures::stream::TryStreamExt;
use netlink_packet_core::{DecodeError, ErrorContext};
use futures::{stream::TryStreamExt, StreamExt};
use netlink_packet_core::{DecodeError, ErrorContext, ParseableParametrized};
use netlink_packet_core::{NetlinkMessage, NetlinkPayload, NLM_F_REQUEST};
use netlink_packet_generic::{
ctrl::{
nlas::{GenlCtrlAttrs, McastGrpAttrs},
GenlCtrl, GenlCtrlCmd,
},
GenlMessage,
};
use netlink_sys::AsyncSocket;
use wl_nl80211::{Nl80211Command, Nl80211Message};

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

if argv.len() < 2 {
eprintln!("Usage: nl80211_trigger_scan <interface index>");
panic!("Required arguments not given");
return Err(Box::new(std::io::Error::other(
"Usage: nl80211_trigger_scan <interface index>",
)));
}

let err_msg = format!("Invalid interface index value: {}", argv[1]);
Expand All @@ -30,12 +41,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}

async fn dump_scan(if_index: u32) -> Result<(), Box<dyn std::error::Error>> {
let (connection, handle, _) = wl_nl80211::new_connection()?;
let (mut connection, handle, mut messages) = wl_nl80211::new_connection()?;

// Attach the connection socket to the multicast scan group to find out,
// when the scan is finished.
let socket = connection.socket_mut().socket_mut();
socket.bind_auto()?;
socket.add_membership(get_scan_multicast_id().await?)?;

tokio::spawn(connection);

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

Expand All @@ -45,7 +62,21 @@ async fn dump_scan(if_index: u32) -> Result<(), Box<dyn std::error::Error>> {
while let Some(msg) = scan_handle.try_next().await? {
msgs.push(msg);
}
tokio::time::sleep(std::time::Duration::from_millis(duration.into())).await;

while let Some((message, _)) = messages.next().await {
match message.payload {
NetlinkPayload::InnerMessage(msg) => {
let msg = Nl80211Message::parse_with_param(
msg.payload.as_slice(),
msg.header,
)?;
if msg.cmd == Nl80211Command::NewScanResults {
break;
}
}
_ => continue,
}
}

let mut dump = handle.scan().dump(if_index).execute().await;
let mut msgs = Vec::new();
Expand All @@ -58,3 +89,62 @@ async fn dump_scan(if_index: u32) -> Result<(), Box<dyn std::error::Error>> {
}
Ok(())
}

async fn get_scan_multicast_id() -> Result<u32, Box<dyn std::error::Error>> {
let (conn, mut handle, _) = wl_nl80211::new_connection()?;
tokio::spawn(conn);

let mut nl_msg =
NetlinkMessage::from(GenlMessage::from_payload(GenlCtrl {
cmd: GenlCtrlCmd::GetFamily,
nlas: vec![GenlCtrlAttrs::FamilyName("nl80211".to_owned())],
}));

// To get the mcast groups for the nl80211 family, we must also set the
// message type id
nl_msg.header.message_type =
handle.handle.resolve_family_id::<Nl80211Message>().await?;
// This is a request, but not a dump. Which means, the family name has to be
// specified, to obtain it's information.
nl_msg.header.flags = NLM_F_REQUEST;

let responses = handle.handle.request(nl_msg).await?;
let nl80211_family: Vec<Vec<GenlCtrlAttrs>> = responses
.try_filter_map(|msg| async move {
match msg.payload {
NetlinkPayload::InnerMessage(genlmsg)
if genlmsg.payload.cmd == GenlCtrlCmd::NewFamily
&& genlmsg.payload.nlas.contains(
&GenlCtrlAttrs::FamilyName("nl80211".to_owned()),
) =>
{
Ok(Some(genlmsg.payload.nlas.clone()))
}
_ => Ok(None),
}
})
.try_collect()
.await?;

// Now get the mcid for "nl80211" "scan" group
let scan_multicast_id = nl80211_family
.first()
.ok_or_else(|| anyhow!("Missing \"nl80211\" family"))?
.iter()
.find_map(|attr| match attr {
GenlCtrlAttrs::McastGroups(mcast_groups) => Some(mcast_groups),
_ => None,
})
.ok_or_else(|| anyhow!("Missing McastGroup attribute"))?
.iter()
.find(|grp| grp.contains(&McastGrpAttrs::Name("scan".to_owned())))
.ok_or_else(|| anyhow!("Missing scan group"))?
.iter()
.find_map(|grp_attr| match grp_attr {
McastGrpAttrs::Id(id) => Some(*id),
_ => None,
})
.ok_or_else(|| anyhow!("No multicast id defined for scan group"))?;

Ok(scan_multicast_id)
}
34 changes: 25 additions & 9 deletions src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
*
*/

use bstr::BString;
use netlink_packet_core::{
parse_string, parse_u16, parse_u32, parse_u64, parse_u8, DecodeError,
parse_u16, parse_u32, parse_u64, parse_u8, DecodeError,
DefaultNla, Emitable, ErrorContext, Nla, NlaBuffer, NlasIterator,
Parseable, ParseableParametrized,
};
Expand All @@ -50,6 +51,21 @@ use crate::{
Nl80211VhtCapability, Nl80211WowlanTriggersSupport,
};

// TODO: move to netlink_packet_utils
pub fn parse_bstring(payload: &[u8]) -> Result<BString, DecodeError> {
if payload.is_empty() {
return Ok(BString::new(Vec::new()));
}
// iproute2 is a bit inconsistent with null-terminated strings.
let slice = if payload[payload.len() - 1] == 0 {
&payload[..payload.len() - 1]
} else {
&payload[..payload.len()]
};
let s = BString::new(slice.to_vec());
Ok(s)
}

const ETH_ALEN: usize = 6;

struct MacAddressNlas(Vec<MacAddressNla>);
Expand Down Expand Up @@ -459,9 +475,9 @@ const NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: u16 = 323;
#[non_exhaustive]
pub enum Nl80211Attr {
Wiphy(u32),
WiphyName(String),
WiphyName(BString),
IfIndex(u32),
IfName(String),
IfName(BString),
IfType(Nl80211InterfaceType),
IfTypeExtCap(Vec<Nl80211IfTypeExtCapa>),
Mac([u8; ETH_ALEN]),
Expand All @@ -477,7 +493,7 @@ pub enum Nl80211Attr {
CenterFreq1(u32),
CenterFreq2(u32),
WiphyTxPowerLevel(u32),
Ssid(String),
Ssid(BString),
StationInfo(Vec<Nl80211StationInfo>),
SurveyInfo(Vec<Nl80211SurveyInfo>),
TransmitQueueStats(Vec<Nl80211TransmitQueueStat>),
Expand Down Expand Up @@ -550,7 +566,7 @@ pub enum Nl80211Attr {
MaxHwTimestampPeers(u16),
/// Basic Service Set (BSS)
Bss(Vec<Nl80211BssInfo>),
ScanSsids(Vec<String>),
ScanSsids(Vec<BString>),
ScanFlags(Nl80211ScanFlags),
MeasurementDuration(u16),
/// Scan interval in millisecond(ms)
Expand Down Expand Up @@ -829,7 +845,7 @@ impl Nla for Nl80211Attr {
MacAddressNlas::from(s).as_slice().emit(buffer)
}
Self::IfName(s) | Self::Ssid(s) | Self::WiphyName(s) => {
buffer[..s.len()].copy_from_slice(s.as_bytes());
buffer[..s.len()].copy_from_slice(s.as_slice());
buffer[s.len()] = 0;
}
Self::Use4Addr(d) => buffer[0] = *d as u8,
Expand Down Expand Up @@ -930,12 +946,12 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
let err_msg = format!(
"Invalid NL80211_ATTR_WIPHY_NAME value {payload:?}"
);
Self::WiphyName(parse_string(payload).context(err_msg)?)
Self::WiphyName(parse_bstring(payload).context(err_msg)?)
}
NL80211_ATTR_IFNAME => {
let err_msg =
format!("Invalid NL80211_ATTR_IFNAME value {payload:?}");
Self::IfName(parse_string(payload).context(err_msg)?)
Self::IfName(parse_bstring(payload).context(err_msg)?)
}
NL80211_ATTR_IFTYPE => {
Self::IfType(Nl80211InterfaceType::parse(payload)?)
Expand Down Expand Up @@ -1040,7 +1056,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
NL80211_ATTR_SSID => {
let err_msg =
format!("Invalid NL80211_ATTR_SSID value {payload:?}");
Self::Ssid(parse_string(payload).context(err_msg)?)
Self::Ssid(parse_bstring(payload).context(err_msg)?)
}
NL80211_ATTR_STA_INFO => {
let err_msg =
Expand Down
2 changes: 1 addition & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ impl<T> Nl80211AttrsBuilder<T> {
}

pub fn ssid(self, ssid: &str) -> Self {
self.append(Nl80211Attr::Ssid(ssid.to_string()))
self.append(Nl80211Attr::Ssid(bstr::BString::from(ssid)))
}
}
Loading
Loading