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());
    }
}