1use serde::{Deserialize, Serialize};
3use std::net::Ipv4Addr;
4
5pub const NETWORK_REFRESH_INTERVAL_SECS: u64 = 15;
6pub 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}