wlan_mlme/client/
lost_bss.rs

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