Skip to content

Commit 3afd524

Browse files
committed
scan: Add scan trigger and schedule support
Signed-off-by: Gris Ge <[email protected]>
1 parent 21896a4 commit 3afd524

20 files changed

+1016
-52
lines changed

Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,8 @@ netlink-proto = { default-features = false, version = "0.11.2" }
3939
netlink-sys = { version = "0.8.4" }
4040

4141
[dev-dependencies]
42-
tokio = { version = "1.11.0", features = ["macros", "rt", "rt-multi-thread"] }
4342
env_logger = "0.9.0"
43+
44+
[dev-dependencies.tokio]
45+
version = "1.11.0"
46+
features = ["macros", "rt", "rt-multi-thread", "time"]

examples/nl80211_trigger_scan.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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: nl80211_trigger_scan <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+
.enable_time()
22+
.build()
23+
.unwrap();
24+
rt.block_on(dump_scan(index));
25+
26+
Ok(())
27+
}
28+
29+
async fn dump_scan(if_index: u32) {
30+
let (connection, handle, _) = wl_nl80211::new_connection().unwrap();
31+
tokio::spawn(connection);
32+
33+
let attrs = wl_nl80211::Nl80211Scan::new(if_index)
34+
.duration(5000)
35+
.passive(true)
36+
.build();
37+
38+
let mut scan_handle = handle.scan().trigger(attrs).execute().await;
39+
40+
let mut msgs = Vec::new();
41+
while let Some(msg) = scan_handle.try_next().await.unwrap() {
42+
msgs.push(msg);
43+
}
44+
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
45+
46+
let mut dump = handle.scan().dump(if_index).execute().await;
47+
let mut msgs = Vec::new();
48+
while let Some(msg) = dump.try_next().await.unwrap() {
49+
msgs.push(msg);
50+
}
51+
assert!(!msgs.is_empty());
52+
for msg in msgs {
53+
println!("{:?}", msg);
54+
}
55+
}

src/attr.rs

+150-18
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,21 @@ use netlink_packet_utils::{
3838

3939
use crate::{
4040
bytes::{write_u16, write_u32, write_u64},
41+
scan::{Nla80211ScanFreqNlas, Nla80211ScanSsidNlas},
4142
wiphy::Nl80211Commands,
4243
Nl80211Band, Nl80211BandTypes, Nl80211BssInfo, Nl80211ChannelWidth,
4344
Nl80211CipherSuit, Nl80211Command, Nl80211ExtFeature, Nl80211ExtFeatures,
4445
Nl80211ExtendedCapability, Nl80211Features, Nl80211HtCapabilityMask,
4546
Nl80211HtWiphyChannelType, Nl80211IfMode, Nl80211IfTypeExtCapa,
4647
Nl80211IfTypeExtCapas, Nl80211IfaceComb, Nl80211IfaceFrameType,
4748
Nl80211InterfaceType, Nl80211InterfaceTypes, Nl80211MloLink,
49+
Nl80211ScanFlags, Nl80211SchedScanMatch, Nl80211SchedScanPlan,
4850
Nl80211StationInfo, Nl80211TransmitQueueStat, Nl80211VhtCapability,
4951
Nl80211WowlanTrigersSupport,
5052
};
5153

54+
const ETH_ALEN: usize = 6;
55+
5256
struct MacAddressNlas(Vec<MacAddressNla>);
5357

5458
impl std::ops::Deref for MacAddressNlas {
@@ -160,8 +164,8 @@ const NL80211_ATTR_WIPHY_CHANNEL_TYPE: u16 = 39;
160164
// const NL80211_ATTR_MGMT_SUBTYPE:u16 = 41;
161165
// const NL80211_ATTR_IE:u16 = 42;
162166
const NL80211_ATTR_MAX_NUM_SCAN_SSIDS: u16 = 43;
163-
// const NL80211_ATTR_SCAN_FREQUENCIES:u16 = 44;
164-
// const NL80211_ATTR_SCAN_SSIDS:u16 = 45;
167+
const NL80211_ATTR_SCAN_FREQUENCIES: u16 = 44;
168+
const NL80211_ATTR_SCAN_SSIDS: u16 = 45;
165169
const NL80211_ATTR_GENERATION: u16 = 46;
166170
const NL80211_ATTR_BSS: u16 = 47;
167171
// const NL80211_ATTR_REG_INITIATOR:u16 = 48;
@@ -236,7 +240,7 @@ const NL80211_ATTR_SUPPORT_MESH_AUTH: u16 = 115;
236240
// const NL80211_ATTR_STA_PLINK_STATE:u16 = 116;
237241
// const NL80211_ATTR_WOWLAN_TRIGGERS:u16 = 117;
238242
const NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: u16 = 118;
239-
// const NL80211_ATTR_SCHED_SCAN_INTERVAL:u16 = 119;
243+
const NL80211_ATTR_SCHED_SCAN_INTERVAL: u16 = 119;
240244
const NL80211_ATTR_INTERFACE_COMBINATIONS: u16 = 120;
241245
const NL80211_ATTR_SOFTWARE_IFTYPES: u16 = 121;
242246
// const NL80211_ATTR_REKEY_DATA:u16 = 122;
@@ -249,7 +253,7 @@ const NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: u16 = 124;
249253
// const NL80211_ATTR_STA_WME:u16 = 129;
250254
const NL80211_ATTR_SUPPORT_AP_UAPSD: u16 = 130;
251255
const NL80211_ATTR_ROAM_SUPPORT: u16 = 131;
252-
// const NL80211_ATTR_SCHED_SCAN_MATCH:u16 = 132;
256+
const NL80211_ATTR_SCHED_SCAN_MATCH: u16 = 132;
253257
const NL80211_ATTR_MAX_MATCH_SETS: u16 = 133;
254258
// const NL80211_ATTR_PMKSA_CANDIDATE:u16 = 134;
255259
// const NL80211_ATTR_TX_NO_CCK_RATE:u16 = 135;
@@ -275,7 +279,7 @@ const NL80211_ATTR_WDEV: u16 = 153;
275279
// const NL80211_ATTR_CONN_FAILED_REASON:u16 = 155;
276280
// const NL80211_ATTR_AUTH_DATA:u16 = 156;
277281
const NL80211_ATTR_VHT_CAPABILITY: u16 = 157;
278-
// const NL80211_ATTR_SCAN_FLAGS:u16 = 158;
282+
const NL80211_ATTR_SCAN_FLAGS: u16 = 158;
279283
const NL80211_ATTR_CHANNEL_WIDTH: u16 = 159;
280284
const NL80211_ATTR_CENTER_FREQ1: u16 = 160;
281285
const NL80211_ATTR_CENTER_FREQ2: u16 = 161;
@@ -332,17 +336,17 @@ const NL80211_ATTR_MAX_CSA_COUNTERS: u16 = 206;
332336
// const NL80211_ATTR_ADMITTED_TIME:u16 = 212;
333337
// const NL80211_ATTR_SMPS_MODE:u16 = 213;
334338
// const NL80211_ATTR_OPER_CLASS:u16 = 214;
335-
// const NL80211_ATTR_MAC_MASK:u16 = 215;
339+
const NL80211_ATTR_MAC_MASK: u16 = 215;
336340
const NL80211_ATTR_WIPHY_SELF_MANAGED_REG: u16 = 216;
337341
const NL80211_ATTR_EXT_FEATURES: u16 = 217;
338342
// const NL80211_ATTR_SURVEY_RADIO_STATS:u16 = 218;
339343
// const NL80211_ATTR_NETNS_FD:u16 = 219;
340-
// const NL80211_ATTR_SCHED_SCAN_DELAY:u16 = 220;
344+
const NL80211_ATTR_SCHED_SCAN_DELAY: u16 = 220;
341345
// const NL80211_ATTR_REG_INDOOR:u16 = 221;
342346
const NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: u16 = 222;
343347
const NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: u16 = 223;
344348
const NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: u16 = 224;
345-
// const NL80211_ATTR_SCHED_SCAN_PLANS:u16 = 225;
349+
const NL80211_ATTR_SCHED_SCAN_PLANS: u16 = 225;
346350
// const NL80211_ATTR_PBSS:u16 = 226;
347351
// const NL80211_ATTR_BSS_SELECT:u16 = 227;
348352
// const NL80211_ATTR_STA_SUPPORT_P2P_PS:u16 = 228;
@@ -352,7 +356,7 @@ const NL80211_ATTR_IFTYPE_EXT_CAPA: u16 = 230;
352356
// const NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR:u16 = 232;
353357
// const NL80211_ATTR_SCAN_START_TIME_TSF:u16 = 233;
354358
// const NL80211_ATTR_SCAN_START_TIME_TSF_BSSID:u16 = 234;
355-
// const NL80211_ATTR_MEASUREMENT_DURATION:u16 = 235;
359+
const NL80211_ATTR_MEASUREMENT_DURATION: u16 = 235;
356360
// const NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY:u16 = 236;
357361
// const NL80211_ATTR_MESH_PEER_AID:u16 = 237;
358362
// const NL80211_ATTR_NAN_MASTER_PREF:u16 = 238;
@@ -452,8 +456,6 @@ const NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: u16 = 323;
452456
// const NL80211_ATTR_WIPHY_RADIOS:u16 = 331;
453457
// const NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS:u16 = 332;
454458

455-
const ETH_ALEN: usize = 6;
456-
457459
#[derive(Debug, PartialEq, Eq, Clone)]
458460
#[non_exhaustive]
459461
pub enum Nl80211Attr {
@@ -464,6 +466,7 @@ pub enum Nl80211Attr {
464466
IfType(Nl80211InterfaceType),
465467
IfTypeExtCap(Vec<Nl80211IfTypeExtCapa>),
466468
Mac([u8; ETH_ALEN]),
469+
MacMask([u8; ETH_ALEN]),
467470
MacAddrs(Vec<[u8; ETH_ALEN]>),
468471
Wdev(u64),
469472
Generation(u32),
@@ -546,6 +549,33 @@ pub enum Nl80211Attr {
546549
MaxHwTimestampPeers(u16),
547550
/// Basic Service Set (BSS)
548551
Bss(Vec<Nl80211BssInfo>),
552+
ScanSsids(Vec<String>),
553+
ScanFlags(Nl80211ScanFlags),
554+
MeasurementDuration(u16),
555+
/// Scan interval in millisecond(ms)
556+
SchedScanInterval(u32),
557+
/// Delay before the first cycle of a scheduled scan is started. Or the
558+
/// delay before a WoWLAN net-detect scan is started, counting from the
559+
/// moment the system is suspended. This value is in seconds.
560+
SchedScanDelay(u32),
561+
/// Scan frequencies in MHz.
562+
ScanFrequencies(Vec<u32>),
563+
/// Sets of attributes to match during scheduled scans. Only BSSs
564+
/// that match any of the sets will be reported. These are pass-thru
565+
/// filter rules. For a match to succeed, the BSS must match all
566+
/// attributes of a set. Since not every hardware supports matching all
567+
/// types of attributes, there is no guarantee that the reported BSSs are
568+
/// fully complying with the match sets and userspace needs to be able to
569+
/// ignore them by itself. Thus, the implementation is somewhat
570+
/// hardware-dependent, but this is only an optimization and the userspace
571+
/// application needs to handle all the non-filtered results anyway.
572+
SchedScanMatch(Vec<Nl80211SchedScanMatch>),
573+
/// A list of scan plans for scheduled scan. Each scan plan defines the
574+
/// number of scan iterations and the interval between scans. The last scan
575+
/// plan will always run infinitely, thus it must not specify the number of
576+
/// iterations, only the interval between scans. The scan plans are
577+
/// executed sequentially.
578+
SchedScanPlans(Vec<Nl80211SchedScanPlan>),
549579
Other(DefaultNla),
550580
}
551581

@@ -576,10 +606,12 @@ impl Nla for Nl80211Attr {
576606
| Self::SchedScanMaxReqs(_)
577607
| Self::TransmitQueueLimit(_)
578608
| Self::TransmitQueueMemoryLimit(_)
579-
| Self::TransmitQueueQuantum(_) => 4,
609+
| Self::TransmitQueueQuantum(_)
610+
| Self::SchedScanInterval(_)
611+
| Self::SchedScanDelay(_) => 4,
580612
Self::Wdev(_) => 8,
581613
Self::IfName(s) | Self::Ssid(s) | Self::WiphyName(s) => s.len() + 1,
582-
Self::Mac(_) => ETH_ALEN,
614+
Self::Mac(_) | Self::MacMask(_) => ETH_ALEN,
583615
Self::MacAddrs(s) => {
584616
MacAddressNlas::from(s).as_slice().buffer_len()
585617
}
@@ -633,9 +665,19 @@ impl Nla for Nl80211Attr {
633665
Self::EmlCapability(_)
634666
| Self::MldCapaAndOps(_)
635667
| Self::MaxNumAkmSuites(_)
636-
| Self::MaxHwTimestampPeers(_) => 2,
668+
| Self::MaxHwTimestampPeers(_)
669+
| Self::MeasurementDuration(_) => 2,
637670
Self::Bands(_) => Nl80211BandTypes::LENGTH,
638671
Self::Bss(v) => v.as_slice().buffer_len(),
672+
Self::ScanSsids(v) => {
673+
Nla80211ScanSsidNlas::from(v).as_slice().buffer_len()
674+
}
675+
Self::ScanFlags(v) => v.buffer_len(),
676+
Self::ScanFrequencies(v) => {
677+
Nla80211ScanFreqNlas::from(v).as_slice().buffer_len()
678+
}
679+
Self::SchedScanMatch(v) => v.as_slice().buffer_len(),
680+
Self::SchedScanPlans(v) => v.as_slice().buffer_len(),
639681
Self::Other(attr) => attr.value_len(),
640682
}
641683
}
@@ -648,6 +690,7 @@ impl Nla for Nl80211Attr {
648690
Self::IfName(_) => NL80211_ATTR_IFNAME,
649691
Self::IfType(_) => NL80211_ATTR_IFTYPE,
650692
Self::Mac(_) => NL80211_ATTR_MAC,
693+
Self::MacMask(_) => NL80211_ATTR_MAC_MASK,
651694
Self::MacAddrs(_) => NL80211_ATTR_MAC_ADDRS,
652695
Self::Wdev(_) => NL80211_ATTR_WDEV,
653696
Self::Generation(_) => NL80211_ATTR_GENERATION,
@@ -733,6 +776,14 @@ impl Nla for Nl80211Attr {
733776
Self::MaxNumAkmSuites(_) => NL80211_ATTR_MAX_NUM_AKM_SUITES,
734777
Self::MaxHwTimestampPeers(_) => NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS,
735778
Self::Bss(_) => NL80211_ATTR_BSS,
779+
Self::ScanSsids(_) => NL80211_ATTR_SCAN_SSIDS,
780+
Self::ScanFlags(_) => NL80211_ATTR_SCAN_FLAGS,
781+
Self::MeasurementDuration(_) => NL80211_ATTR_MEASUREMENT_DURATION,
782+
Self::SchedScanInterval(_) => NL80211_ATTR_SCHED_SCAN_INTERVAL,
783+
Self::SchedScanDelay(_) => NL80211_ATTR_SCHED_SCAN_DELAY,
784+
Self::ScanFrequencies(_) => NL80211_ATTR_SCAN_FREQUENCIES,
785+
Self::SchedScanMatch(_) => NL80211_ATTR_SCHED_SCAN_MATCH,
786+
Self::SchedScanPlans(_) => NL80211_ATTR_SCHED_SCAN_PLANS,
736787
Self::Other(attr) => attr.kind(),
737788
}
738789
}
@@ -760,13 +811,15 @@ impl Nla for Nl80211Attr {
760811
| Self::SchedScanMaxReqs(d)
761812
| Self::TransmitQueueLimit(d)
762813
| Self::TransmitQueueMemoryLimit(d)
763-
| Self::TransmitQueueQuantum(d) => write_u32(buffer, *d),
814+
| Self::TransmitQueueQuantum(d)
815+
| Self::SchedScanInterval(d)
816+
| Self::SchedScanDelay(d) => write_u32(buffer, *d),
764817
Self::MaxScanIeLen(d) | Self::MaxSchedScanIeLen(d) => {
765818
write_u16(buffer, *d)
766819
}
767820
Self::Wdev(d) => write_u64(buffer, *d),
768821
Self::IfType(d) => write_u32(buffer, (*d).into()),
769-
Self::Mac(s) => buffer.copy_from_slice(s),
822+
Self::Mac(s) | Self::MacMask(s) => buffer.copy_from_slice(s),
770823
Self::MacAddrs(s) => {
771824
MacAddressNlas::from(s).as_slice().emit(buffer)
772825
}
@@ -834,9 +887,19 @@ impl Nla for Nl80211Attr {
834887
Self::EmlCapability(d)
835888
| Self::MldCapaAndOps(d)
836889
| Self::MaxNumAkmSuites(d)
837-
| Self::MaxHwTimestampPeers(d) => write_u16(buffer, *d),
890+
| Self::MaxHwTimestampPeers(d)
891+
| Self::MeasurementDuration(d) => write_u16(buffer, *d),
838892
Self::Bands(v) => v.emit(buffer),
839893
Self::Bss(v) => v.as_slice().emit(buffer),
894+
Self::ScanSsids(v) => {
895+
Nla80211ScanSsidNlas::from(v).as_slice().emit(buffer)
896+
}
897+
Self::ScanFlags(v) => v.emit(buffer),
898+
Self::ScanFrequencies(v) => {
899+
Nla80211ScanFreqNlas::from(v).as_slice().emit(buffer)
900+
}
901+
Self::SchedScanMatch(v) => v.as_slice().emit(buffer),
902+
Self::SchedScanPlans(v) => v.as_slice().emit(buffer),
840903
Self::Other(attr) => attr.emit(buffer),
841904
}
842905
}
@@ -882,11 +945,26 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
882945
ret
883946
} else {
884947
return Err(format!(
885-
"Invalid length of NL80211_ATTR_MAC, expected length {} got {:?}",
948+
"Invalid length of NL80211_ATTR_MAC, \
949+
expected length {} got {:?}",
886950
ETH_ALEN, payload
887951
)
888952
.into());
889953
}),
954+
NL80211_ATTR_MAC_MASK => {
955+
Self::MacMask(if payload.len() == ETH_ALEN {
956+
let mut ret = [0u8; ETH_ALEN];
957+
ret.copy_from_slice(&payload[..ETH_ALEN]);
958+
ret
959+
} else {
960+
return Err(format!(
961+
"Invalid length of NL80211_ATTR_MAC_MASK, \
962+
expected length {} got {:?}",
963+
ETH_ALEN, payload
964+
)
965+
.into());
966+
})
967+
}
890968
NL80211_ATTR_MAC_ADDRS => {
891969
Self::MacAddrs(MacAddressNlas::parse(payload)?.into())
892970
}
@@ -1347,6 +1425,60 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nl80211Attr {
13471425
payload
13481426
))?,
13491427
),
1428+
NL80211_ATTR_SCAN_SSIDS => {
1429+
Self::ScanSsids(Nla80211ScanSsidNlas::parse(payload)?.into())
1430+
}
1431+
NL80211_ATTR_SCAN_FLAGS => {
1432+
Self::ScanFlags(Nl80211ScanFlags::parse(payload)?)
1433+
}
1434+
NL80211_ATTR_MEASUREMENT_DURATION => {
1435+
let err_msg = format!(
1436+
"Invalid NL80211_ATTR_MEASUREMENT_DURATION value {:?}",
1437+
payload
1438+
);
1439+
Self::MeasurementDuration(parse_u16(payload).context(err_msg)?)
1440+
}
1441+
NL80211_ATTR_SCHED_SCAN_INTERVAL => {
1442+
let err_msg = format!(
1443+
"Invalid NL80211_ATTR_SCHED_SCAN_INTERVAL value {:?}",
1444+
payload
1445+
);
1446+
Self::SchedScanInterval(parse_u32(payload).context(err_msg)?)
1447+
}
1448+
NL80211_ATTR_SCHED_SCAN_DELAY => {
1449+
let err_msg = format!(
1450+
"Invalid NL80211_ATTR_SCHED_SCAN_DELAY value {:?}",
1451+
payload
1452+
);
1453+
Self::SchedScanDelay(parse_u32(payload).context(err_msg)?)
1454+
}
1455+
NL80211_ATTR_SCAN_FREQUENCIES => Self::ScanFrequencies(
1456+
Nla80211ScanFreqNlas::parse(payload)?.into(),
1457+
),
1458+
NL80211_ATTR_SCHED_SCAN_MATCH => {
1459+
let err_msg = format!(
1460+
"Invalid NL80211_ATTR_SCHED_SCAN_MATCH value {:?}",
1461+
payload
1462+
);
1463+
let mut nlas = Vec::new();
1464+
for nla in NlasIterator::new(payload) {
1465+
let nla = &nla.context(err_msg.clone())?;
1466+
nlas.push(Nl80211SchedScanMatch::parse(nla)?);
1467+
}
1468+
Self::SchedScanMatch(nlas)
1469+
}
1470+
NL80211_ATTR_SCHED_SCAN_PLANS => {
1471+
let err_msg = format!(
1472+
"Invalid NL80211_ATTR_SCHED_SCAN_PLANS value {:?}",
1473+
payload
1474+
);
1475+
let mut nlas = Vec::new();
1476+
for nla in NlasIterator::new(payload) {
1477+
let nla = &nla.context(err_msg.clone())?;
1478+
nlas.push(Nl80211SchedScanPlan::parse(nla)?);
1479+
}
1480+
Self::SchedScanPlans(nlas)
1481+
}
13501482
_ => Self::Other(
13511483
DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?,
13521484
),

0 commit comments

Comments
 (0)