Skip to main content

core_api/host/
network.rs

1//! Host-owned IPv4 LAN observations, shared through local IPC.
2use serde::{Deserialize, Serialize};
3use std::net::Ipv4Addr;
4
5pub const NETWORK_REFRESH_INTERVAL_SECS: u64 = 15;
6/// Allows for both the Host sampling interval and a consumer's polling interval.
7pub const NETWORK_MAX_AGE_MS: i64 = 45_000;
8
9#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct HostNetworkSnapshot {
12    pub lan_ipv4: Option<Ipv4Addr>,
13    pub observed_at_ms: i64,
14}
15
16impl HostNetworkSnapshot {
17    pub fn available_ipv4(&self, now_ms: i64) -> Option<Ipv4Addr> {
18        let age = now_ms.checked_sub(self.observed_at_ms)?;
19        self.lan_ipv4.filter(|ip| {
20            ip.is_private() && self.observed_at_ms > 0 && (0..NETWORK_MAX_AGE_MS).contains(&age)
21        })
22    }
23}
24
25#[cfg(test)]
26mod tests {
27    use super::*;
28
29    #[test]
30    fn only_fresh_private_ipv4_is_available() {
31        for ip in ["192.168.0.2", "10.1.2.3", "172.31.2.3"] {
32            let snapshot = HostNetworkSnapshot {
33                lan_ipv4: Some(ip.parse().unwrap()),
34                observed_at_ms: 100_000,
35            };
36            assert_eq!(snapshot.available_ipv4(100_000), snapshot.lan_ipv4);
37            assert_eq!(snapshot.available_ipv4(144_999), snapshot.lan_ipv4);
38            assert_eq!(snapshot.available_ipv4(145_000), None);
39            assert_eq!(snapshot.available_ipv4(99_999), None);
40        }
41        for ip in [
42            "127.0.0.1",
43            "0.0.0.0",
44            "8.8.8.8",
45            "169.254.1.2",
46            "172.32.1.2",
47        ] {
48            assert_eq!(
49                HostNetworkSnapshot {
50                    lan_ipv4: Some(ip.parse().unwrap()),
51                    observed_at_ms: 100_000,
52                }
53                .available_ipv4(100_000),
54                None
55            );
56        }
57        assert_eq!(HostNetworkSnapshot::default().available_ipv4(1), None);
58    }
59}