Skip to content

Commit 0a027c1

Browse files
committed
Use multicast to wait for new scan results
1 parent 3eea6a5 commit 0a027c1

File tree

1 file changed

+102
-7
lines changed

1 file changed

+102
-7
lines changed

examples/nl80211_trigger_scan.rs

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,21 @@
22

33
use std::env::args;
44

5-
use anyhow::{bail, Context, Error};
6-
use futures::stream::TryStreamExt;
5+
use anyhow::{anyhow, bail, Context, Result};
6+
use futures::{stream::TryStreamExt, StreamExt};
7+
use netlink_packet_core::{NetlinkMessage, NetlinkPayload, NLM_F_REQUEST};
8+
use netlink_packet_generic::{
9+
ctrl::{
10+
nlas::{GenlCtrlAttrs, McastGrpAttrs},
11+
GenlCtrl, GenlCtrlCmd,
12+
},
13+
GenlMessage,
14+
};
15+
use netlink_packet_utils::ParseableParametrized;
16+
use netlink_sys::AsyncSocket;
17+
use wl_nl80211::{Nl80211Attr, Nl80211Command, Nl80211Message};
718

8-
fn main() -> Result<(), Error> {
19+
fn main() -> Result<()> {
920
let argv: Vec<_> = args().collect();
1021

1122
if argv.len() < 2 {
@@ -21,13 +32,20 @@ fn main() -> Result<(), Error> {
2132
.enable_time()
2233
.build()
2334
.unwrap();
24-
rt.block_on(dump_scan(index));
35+
rt.block_on(dump_scan(index))?;
2536

2637
Ok(())
2738
}
2839

29-
async fn dump_scan(if_index: u32) {
30-
let (connection, handle, _) = wl_nl80211::new_connection().unwrap();
40+
async fn dump_scan(if_index: u32) -> Result<()> {
41+
let (mut connection, handle, mut messages) = wl_nl80211::new_connection()?;
42+
43+
// Attach the connection socket to the multicast scan group to find out,
44+
// when the scan is finished.
45+
let socket = connection.socket_mut().socket_mut();
46+
socket.bind_auto()?;
47+
socket.add_membership(get_scan_multicast_id().await?)?;
48+
3149
tokio::spawn(connection);
3250

3351
let attrs = wl_nl80211::Nl80211Scan::new(if_index)
@@ -41,7 +59,23 @@ async fn dump_scan(if_index: u32) {
4159
while let Some(msg) = scan_handle.try_next().await.unwrap() {
4260
msgs.push(msg);
4361
}
44-
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
62+
63+
while let Some((message, _)) = messages.next().await {
64+
match message.payload {
65+
NetlinkPayload::InnerMessage(msg) => {
66+
let msg = Nl80211Message::parse_with_param(
67+
msg.payload.as_slice(),
68+
msg.header,
69+
)?;
70+
if msg.cmd == Nl80211Command::NewScanResults
71+
&& msg.attributes.contains(&Nl80211Attr::IfIndex(if_index))
72+
{
73+
break;
74+
}
75+
}
76+
_ => continue,
77+
}
78+
}
4579

4680
let mut dump = handle.scan().dump(if_index).execute().await;
4781
let mut msgs = Vec::new();
@@ -52,4 +86,65 @@ async fn dump_scan(if_index: u32) {
5286
for msg in msgs {
5387
println!("{msg:?}");
5488
}
89+
90+
Ok(())
91+
}
92+
93+
async fn get_scan_multicast_id() -> Result<u32> {
94+
let (conn, mut handle, _) = wl_nl80211::new_connection()?;
95+
tokio::spawn(conn);
96+
97+
let mut nl_msg =
98+
NetlinkMessage::from(GenlMessage::from_payload(GenlCtrl {
99+
cmd: GenlCtrlCmd::GetFamily,
100+
nlas: vec![GenlCtrlAttrs::FamilyName("nl80211".to_owned())],
101+
}));
102+
103+
// To get the mcast groups for the nl80211 family, we must also set the
104+
// message type id
105+
nl_msg.header.message_type =
106+
handle.handle.resolve_family_id::<Nl80211Message>().await?;
107+
// This is a request, but not a dump. Which means, the family name has to be
108+
// specified, to obtain it's information.
109+
nl_msg.header.flags = NLM_F_REQUEST;
110+
111+
let responses = handle.handle.request(nl_msg).await?;
112+
let nl80211_family: Vec<Vec<GenlCtrlAttrs>> = responses
113+
.try_filter_map(|msg| async move {
114+
match msg.payload {
115+
NetlinkPayload::InnerMessage(genlmsg)
116+
if genlmsg.payload.cmd == GenlCtrlCmd::NewFamily
117+
&& genlmsg.payload.nlas.contains(
118+
&GenlCtrlAttrs::FamilyName("nl80211".to_owned()),
119+
) =>
120+
{
121+
Ok(Some(genlmsg.payload.nlas.clone()))
122+
}
123+
_ => Ok(None),
124+
}
125+
})
126+
.try_collect()
127+
.await?;
128+
129+
// Now get the mcid for "nl80211" "scan" group
130+
let scan_multicast_id = nl80211_family
131+
.first()
132+
.ok_or_else(|| anyhow!("Missing \"nl80211\" family"))?
133+
.iter()
134+
.find_map(|attr| match attr {
135+
GenlCtrlAttrs::McastGroups(mcast_groups) => Some(mcast_groups),
136+
_ => None,
137+
})
138+
.ok_or_else(|| anyhow!("Missing McastGroup attribute"))?
139+
.iter()
140+
.find(|grp| grp.contains(&McastGrpAttrs::Name("scan".to_owned())))
141+
.ok_or_else(|| anyhow!("Missing scan group"))?
142+
.iter()
143+
.find_map(|grp_attr| match grp_attr {
144+
McastGrpAttrs::Id(id) => Some(*id),
145+
_ => None,
146+
})
147+
.ok_or_else(|| anyhow!("No multicast id defined for scan group"))?;
148+
149+
Ok(scan_multicast_id)
55150
}

0 commit comments

Comments
 (0)