use neli::{
consts::nl::NlmF,
genl::{AttrTypeBuilder, Genlmsghdr, GenlmsghdrBuilder, NlattrBuilder},
nl::NlPayload,
router::asynchronous,
types::{Buffer, GenlBuffer},
};
use crate::nl80211::{Attr, Cmd, NL80211_FAMILY_NAME};
use crate::{Interface, ScanError};
#[tracing::instrument(skip(socket, interface), fields(interface = %interface.name(), ifindex = interface.index()))]
pub(crate) async fn trigger_scan(
socket: &asynchronous::NlRouter,
interface: &Interface,
) -> Result<(), ScanError> {
tracing::debug!("Triggering nl80211 scan");
let family_id = socket.resolve_genl_family(NL80211_FAMILY_NAME).await?;
let ifindex_attr = NlattrBuilder::default()
.nla_type(AttrTypeBuilder::default().nla_type(Attr::Ifindex).build()?)
.nla_payload(interface.index())
.build()?;
let scan_ssids_attr = NlattrBuilder::default()
.nla_type(
AttrTypeBuilder::default()
.nla_type(Attr::ScanSsids)
.build()?,
)
.nla_payload(GenlBuffer::<u16, Buffer>::new()) .build()?;
let attrs = vec![ifindex_attr, scan_ssids_attr]
.into_iter()
.collect::<GenlBuffer<_, _>>();
let genlmsghdr = GenlmsghdrBuilder::default()
.cmd(Cmd::TriggerScan)
.attrs(attrs)
.version(1)
.build()?;
let mut responses = socket
.send::<_, _, u16, Genlmsghdr<Cmd, Attr>>(
family_id,
NlmF::REQUEST | NlmF::ACK,
NlPayload::Payload(genlmsghdr),
)
.await?;
let mut ack_received = false;
while let Some(msg) = responses.next::<u16, Genlmsghdr<Cmd, Attr>>().await {
if let Ok(msg) = msg
&& matches!(msg.nl_payload(), NlPayload::Ack(_))
{
ack_received = true;
}
}
if !ack_received {
tracing::error!("Scan request not acknowledged, permission denied");
return Err(ScanError::PermissionDenied);
}
Ok(())
}