Skip to main content

git_ark/
subnet.rs

1//! Client-side LAN facts: the client's own IPv4 and the hosts of its /24.
2
3use std::net::{Ipv4Addr, SocketAddr, UdpSocket};
4
5/// The client's primary IPv4, via the routing table — bind a UDP socket and
6/// "connect" it to a public address (no packet is sent; this just selects the
7/// default-route interface), then read the local address. `None` if there is no
8/// usable route (e.g. an isolated host); the caller then needs `--subnet`.
9pub fn local_ipv4() -> Option<Ipv4Addr> {
10    let sock = UdpSocket::bind("0.0.0.0:0").ok()?;
11    sock.connect("8.8.8.8:80").ok()?;
12    match sock.local_addr().ok()? {
13        SocketAddr::V4(a) => Some(*a.ip()),
14        SocketAddr::V6(_) => None,
15    }
16}
17
18/// The 254 usable host addresses of `base`'s /24 (`.1`–`.254`), network and
19/// broadcast excluded.
20pub fn hosts_in_slash24(base: Ipv4Addr) -> Vec<Ipv4Addr> {
21    let o = base.octets();
22    (1u8..=254)
23        .map(|last| Ipv4Addr::new(o[0], o[1], o[2], last))
24        .collect()
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30    use std::net::Ipv4Addr;
31
32    #[test]
33    fn slash24_has_254_hosts_first_and_last() {
34        let h = hosts_in_slash24(Ipv4Addr::new(192, 168, 1, 42));
35        assert_eq!(h.len(), 254);
36        assert_eq!(h[0], Ipv4Addr::new(192, 168, 1, 1));
37        assert_eq!(h[253], Ipv4Addr::new(192, 168, 1, 254));
38        // network (.0) and broadcast (.255) are excluded
39        assert!(!h.contains(&Ipv4Addr::new(192, 168, 1, 0)));
40        assert!(!h.contains(&Ipv4Addr::new(192, 168, 1, 255)));
41    }
42}