wlan_mlme/client/lost_bss.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Struct used to count remaining time BSS has not been detected. Used to determine
/// when trigger auto deauth.
#[derive(Debug)]
pub struct LostBssCounter {
/// beacon_period in zx::MonotonicDuration as obtained from the AP, used to convert beacon_count to time.
beacon_period: zx::MonotonicDuration,
/// The number of beacon periods where client doesn't receive a single beacon frame
/// before it declares BSS as lost.
full_timeout: zx::MonotonicDuration,
/// Number of intervals since we last saw a beacon. Reset to 0 as soon as we see a beacon.
time_since_last_beacon: zx::MonotonicDuration,
}
/// In a typical use case, a full association status check interval is added every time the timeout
/// fires. This could lead to slight over-counting since the client may have received a beacon
/// during this period. To counter this effect, call should_deauthenticate() before calling
/// add_beacon_interval().
impl LostBssCounter {
pub fn start(beacon_period: zx::MonotonicDuration, full_timeout_beacon_count: u32) -> Self {
Self {
beacon_period: beacon_period.clone(),
full_timeout: beacon_period * full_timeout_beacon_count as i64,
time_since_last_beacon: zx::MonotonicDuration::from_nanos(0),
}
}
pub fn reset(&mut self) {
self.time_since_last_beacon = zx::MonotonicDuration::from_nanos(0);
}
/// In the most typical use case, a full association status check interval is added when
/// the timeout fires. So to prevent auto-deauth from triggering prematurely, it is important to
/// call `should_deauthenticate()` first and only call `add_beacon_interval()`
/// if `should_deauthenticate()` is false.
pub fn should_deauthenticate(&self) -> bool {
self.time_since_last_beacon >= self.full_timeout
}
pub fn add_beacon_interval(&mut self, beacon_intervals_since_last_timeout: u32) {
self.time_since_last_beacon += self.beacon_period * beacon_intervals_since_last_timeout;
}
/// add_time() is used to record any time that is shorter than a full status check interval.
/// (typically when the client goes off-channel to scan while associated).
pub fn add_time(&mut self, time: zx::MonotonicDuration) {
self.time_since_last_beacon += time;
}
}
#[cfg(test)]
mod tests {
use super::*;
use wlan_common::time::TimeUnit;
const TEST_BEACON_PERIOD: zx::MonotonicDuration =
zx::MonotonicDuration::from_micros(TimeUnit(42).into_micros());
const TEST_TIMEOUT_BCN_COUNT: u32 = 1000;
#[test]
fn test_single_uninterrupted_period() {
let mut counter = LostBssCounter::start(TEST_BEACON_PERIOD, TEST_TIMEOUT_BCN_COUNT);
// about to timeout but not yet.
counter.add_beacon_interval(TEST_TIMEOUT_BCN_COUNT - 1);
assert!(!counter.should_deauthenticate());
// any more time will trigger auto deauth
counter.add_beacon_interval(1);
assert!(counter.should_deauthenticate());
}
#[test]
fn test_beacon_received_midway() {
let mut counter = LostBssCounter::start(TEST_BEACON_PERIOD, TEST_TIMEOUT_BCN_COUNT);
counter.add_beacon_interval(TEST_TIMEOUT_BCN_COUNT - 1);
assert!(!counter.should_deauthenticate());
// Beacon received some time later, resetting the timeout.
counter.reset();
// Verify that calling `handle_timeout` at originally scheduled time would not
// return false, indicating no auto-deauth yet
counter.add_beacon_interval(1);
assert!(!counter.should_deauthenticate());
// But if no beacon is received in timeout + 1 intervals, auto-deauth will trigger
counter.add_beacon_interval(TEST_TIMEOUT_BCN_COUNT - 1);
assert!(counter.should_deauthenticate());
}
#[test]
fn test_add_time_uninterrupted() {
let mut counter = LostBssCounter::start(TEST_BEACON_PERIOD, TEST_TIMEOUT_BCN_COUNT);
// about to timeout but not yet.
counter.add_time(
TEST_BEACON_PERIOD * TEST_TIMEOUT_BCN_COUNT - zx::MonotonicDuration::from_nanos(1),
);
assert!(!counter.should_deauthenticate());
// any more time will trigger auto deauth
counter.add_time(zx::MonotonicDuration::from_nanos(1));
assert!(counter.should_deauthenticate());
}
#[test]
fn test_add_time_beacon_received() {
let mut counter = LostBssCounter::start(TEST_BEACON_PERIOD, TEST_TIMEOUT_BCN_COUNT);
counter.add_beacon_interval(TEST_TIMEOUT_BCN_COUNT - 1);
assert!(!counter.should_deauthenticate());
// Beacon received some time later, resetting the timeout.
counter.reset();
// Verify that calling `handle_timeout` at originally scheduled time would not
// return false, indicating no auto-deauth yet
counter.add_time(TEST_BEACON_PERIOD);
assert!(!counter.should_deauthenticate());
// But if no beacon is received in timeout + 1 intervals, auto-deauth will trigger
counter.add_beacon_interval(TEST_TIMEOUT_BCN_COUNT - 1);
assert!(counter.should_deauthenticate());
}
}