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.
45/// 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.
10beacon_period: zx::MonotonicDuration,
1112/// The number of beacon periods where client doesn't receive a single beacon frame
13 /// before it declares BSS as lost.
14full_timeout: zx::MonotonicDuration,
1516/// Number of intervals since we last saw a beacon. Reset to 0 as soon as we see a beacon.
17time_since_last_beacon: zx::MonotonicDuration,
18}
1920/// 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 {
25pub fn start(beacon_period: zx::MonotonicDuration, full_timeout_beacon_count: u32) -> Self {
26Self {
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 }
3233pub fn reset(&mut self) {
34self.time_since_last_beacon = zx::MonotonicDuration::from_nanos(0);
35 }
3637/// 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.
41pub fn should_deauthenticate(&self) -> bool {
42self.time_since_last_beacon >= self.full_timeout
43 }
4445pub fn add_beacon_interval(&mut self, beacon_intervals_since_last_timeout: u32) {
46self.time_since_last_beacon += self.beacon_period * beacon_intervals_since_last_timeout;
47 }
48}
4950#[cfg(test)]
51mod tests {
52use super::*;
53use wlan_common::time::TimeUnit;
5455const TEST_BEACON_PERIOD: zx::MonotonicDuration =
56 zx::MonotonicDuration::from_micros(TimeUnit(42).into_micros());
57const TEST_TIMEOUT_BCN_COUNT: u32 = 1000;
5859#[test]
60fn test_single_uninterrupted_period() {
61let mut counter = LostBssCounter::start(TEST_BEACON_PERIOD, TEST_TIMEOUT_BCN_COUNT);
62// about to timeout but not yet.
63counter.add_beacon_interval(TEST_TIMEOUT_BCN_COUNT - 1);
64assert!(!counter.should_deauthenticate());
65// any more time will trigger auto deauth
66counter.add_beacon_interval(1);
67assert!(counter.should_deauthenticate());
68 }
6970#[test]
71fn test_beacon_received_midway() {
72let mut counter = LostBssCounter::start(TEST_BEACON_PERIOD, TEST_TIMEOUT_BCN_COUNT);
73 counter.add_beacon_interval(TEST_TIMEOUT_BCN_COUNT - 1);
74assert!(!counter.should_deauthenticate());
7576// Beacon received some time later, resetting the timeout.
77counter.reset();
7879// Verify that calling `handle_timeout` at originally scheduled time would not
80 // return false, indicating no auto-deauth yet
81counter.add_beacon_interval(1);
82assert!(!counter.should_deauthenticate());
83// But if no beacon is received in timeout + 1 intervals, auto-deauth will trigger
84counter.add_beacon_interval(TEST_TIMEOUT_BCN_COUNT - 1);
85assert!(counter.should_deauthenticate());
86 }
87}