veilid_tools/network_interfaces/
mod.rs

1mod tools;
2
3use crate::*;
4use serde::*;
5
6cfg_if::cfg_if! {
7    if #[cfg(any(target_os = "linux", target_os = "android"))] {
8        mod netlink;
9        use self::netlink::PlatformSupportNetlink as PlatformSupport;
10    } else if #[cfg(target_os = "windows")] {
11        mod windows;
12        mod sockaddr_tools;
13        use self::windows::PlatformSupportWindows as PlatformSupport;
14    } else if #[cfg(any(target_os = "macos", target_os = "ios"))] {
15        mod apple;
16        mod sockaddr_tools;
17        use self::apple::PlatformSupportApple as PlatformSupport;
18    } else if #[cfg(target_os = "openbsd")] {
19        mod openbsd;
20        mod sockaddr_tools;
21        use self::openbsd::PlatformSupportOpenBSD as PlatformSupport;
22    } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
23        mod wasm;
24        use self::wasm::PlatformSupportWasm as PlatformSupport;
25    } else {
26        compile_error!("No network interfaces support for this platform!");
27    }
28}
29
30#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Serialize, Deserialize)]
31pub enum IfAddr {
32    V4(Ifv4Addr),
33    V6(Ifv6Addr),
34}
35
36impl IfAddr {
37    pub fn ip(&self) -> IpAddr {
38        match *self {
39            IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.ip),
40            IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.ip),
41        }
42    }
43    pub fn netmask(&self) -> IpAddr {
44        match *self {
45            IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.netmask),
46            IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.netmask),
47        }
48    }
49    pub fn broadcast(&self) -> Option<IpAddr> {
50        match *self {
51            IfAddr::V4(ref ifv4_addr) => ifv4_addr.broadcast.map(IpAddr::V4),
52            IfAddr::V6(ref ifv6_addr) => ifv6_addr.broadcast.map(IpAddr::V6),
53        }
54    }
55}
56
57/// Details about the ipv4 address of an interface on this host.
58#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Serialize, Deserialize)]
59pub struct Ifv4Addr {
60    /// The IP address of the interface.
61    pub ip: Ipv4Addr,
62    /// The netmask of the interface.
63    pub netmask: Ipv4Addr,
64    /// The broadcast address of the interface.
65    pub broadcast: Option<Ipv4Addr>,
66}
67
68/// Details about the ipv6 address of an interface on this host.
69#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Serialize, Deserialize)]
70pub struct Ifv6Addr {
71    /// The IP address of the interface.
72    pub ip: Ipv6Addr,
73    /// The netmask of the interface.
74    pub netmask: Ipv6Addr,
75    /// The broadcast address of the interface.
76    pub broadcast: Option<Ipv6Addr>,
77}
78
79/// Some of the flags associated with an interface.
80#[derive(
81    Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Serialize, Deserialize,
82)]
83pub struct InterfaceFlags {
84    pub is_loopback: bool,
85    pub is_running: bool,
86    pub is_point_to_point: bool,
87    pub has_default_route: bool,
88}
89
90/// Some of the flags associated with an address.
91#[derive(
92    Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Serialize, Deserialize,
93)]
94pub struct AddressFlags {
95    // common flags
96    pub is_dynamic: bool,
97    // ipv6 flags
98    pub is_temporary: bool,
99    pub is_preferred: bool,
100}
101
102#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
103pub struct InterfaceAddress {
104    pub if_addr: IfAddr,
105    pub flags: AddressFlags,
106}
107
108use core::cmp::Ordering;
109
110// less is less preferable, greater is more preferable
111impl Ord for InterfaceAddress {
112    fn cmp(&self, other: &Self) -> Ordering {
113        match (&self.if_addr, &other.if_addr) {
114            (IfAddr::V4(a), IfAddr::V4(b)) => {
115                // global scope addresses are better
116                let ret = ipv4addr_is_global(&a.ip).cmp(&ipv4addr_is_global(&b.ip));
117                if ret != Ordering::Equal {
118                    return ret;
119                }
120                // local scope addresses are better
121                let ret = ipv4addr_is_private(&a.ip).cmp(&ipv4addr_is_private(&b.ip));
122                if ret != Ordering::Equal {
123                    return ret;
124                }
125                // non-dynamic addresses are better
126                let ret = (!self.flags.is_dynamic).cmp(&!other.flags.is_dynamic);
127                if ret != Ordering::Equal {
128                    return ret;
129                }
130            }
131            (IfAddr::V6(a), IfAddr::V6(b)) => {
132                // preferred addresses are better
133                let ret = self.flags.is_preferred.cmp(&other.flags.is_preferred);
134                if ret != Ordering::Equal {
135                    return ret;
136                }
137                // non-temporary address are better
138                let ret = (!self.flags.is_temporary).cmp(&!other.flags.is_temporary);
139                if ret != Ordering::Equal {
140                    return ret;
141                }
142                // global scope addresses are better
143                let ret = ipv6addr_is_global(&a.ip).cmp(&ipv6addr_is_global(&b.ip));
144                if ret != Ordering::Equal {
145                    return ret;
146                }
147                // unique local unicast addresses are better
148                let ret = ipv6addr_is_unique_local(&a.ip).cmp(&ipv6addr_is_unique_local(&b.ip));
149                if ret != Ordering::Equal {
150                    return ret;
151                }
152                // unicast site local addresses are better
153                let ret = ipv6addr_is_unicast_site_local(&a.ip)
154                    .cmp(&ipv6addr_is_unicast_site_local(&b.ip));
155                if ret != Ordering::Equal {
156                    return ret;
157                }
158                // unicast link local addresses are better
159                let ret = ipv6addr_is_unicast_link_local(&a.ip)
160                    .cmp(&ipv6addr_is_unicast_link_local(&b.ip));
161                if ret != Ordering::Equal {
162                    return ret;
163                }
164                // non-dynamic addresses are better
165                let ret = (!self.flags.is_dynamic).cmp(&!other.flags.is_dynamic);
166                if ret != Ordering::Equal {
167                    return ret;
168                }
169            }
170            (IfAddr::V4(a), IfAddr::V6(b)) => {
171                // If the IPv6 address is preferred and not temporary, compare if it is global scope
172                if other.flags.is_preferred && !other.flags.is_temporary {
173                    let ret = ipv4addr_is_global(&a.ip).cmp(&ipv6addr_is_global(&b.ip));
174                    if ret != Ordering::Equal {
175                        return ret;
176                    }
177                }
178
179                // Default, prefer IPv4 because many IPv6 addresses are not actually routed
180                return Ordering::Greater;
181            }
182            (IfAddr::V6(a), IfAddr::V4(b)) => {
183                // If the IPv6 address is preferred and not temporary, compare if it is global scope
184                if self.flags.is_preferred && !self.flags.is_temporary {
185                    let ret = ipv6addr_is_global(&a.ip).cmp(&ipv4addr_is_global(&b.ip));
186                    if ret != Ordering::Equal {
187                        return ret;
188                    }
189                }
190
191                // Default, prefer IPv4 because many IPv6 addresses are not actually routed
192                return Ordering::Less;
193            }
194        }
195        // stable sort
196        let ret = self.if_addr.cmp(&other.if_addr);
197        if ret != Ordering::Equal {
198            return ret;
199        }
200        self.flags.cmp(&other.flags)
201    }
202}
203impl PartialOrd for InterfaceAddress {
204    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
205        Some(self.cmp(other))
206    }
207}
208
209impl InterfaceAddress {
210    pub fn new(if_addr: IfAddr, flags: AddressFlags) -> Self {
211        Self { if_addr, flags }
212    }
213
214    pub fn if_addr(&self) -> &IfAddr {
215        &self.if_addr
216    }
217
218    pub fn is_temporary(&self) -> bool {
219        self.flags.is_temporary
220    }
221    pub fn is_dynamic(&self) -> bool {
222        self.flags.is_dynamic
223    }
224    pub fn is_preferred(&self) -> bool {
225        self.flags.is_preferred
226    }
227}
228
229// #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
230// enum NetworkInterfaceType {
231//     Mobile,     // Least preferable, usually metered and slow
232//     Unknown,    // Everything else if we can't detect the type
233//     Wireless,   // Wifi is usually free or cheap and medium speed
234//     Wired,      // Wired is usually free or cheap and high speed
235// }
236
237#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
238pub struct NetworkInterface {
239    pub name: String,
240    pub flags: InterfaceFlags,
241    pub addrs: Vec<InterfaceAddress>,
242}
243
244impl fmt::Debug for NetworkInterface {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        f.debug_struct("NetworkInterface")
247            .field("name", &self.name)
248            .field("flags", &self.flags)
249            .field("addrs", &self.addrs)
250            .finish()?;
251        if f.alternate() {
252            writeln!(f)?;
253            writeln!(f, "// primary_ipv4: {:?}", self.primary_ipv4())?;
254            writeln!(f, "// primary_ipv6: {:?}", self.primary_ipv6())?;
255        }
256        Ok(())
257    }
258}
259impl NetworkInterface {
260    pub fn new(name: String, flags: InterfaceFlags) -> Self {
261        Self {
262            name,
263            flags,
264            addrs: Vec::new(),
265        }
266    }
267    pub fn name(&self) -> String {
268        self.name.clone()
269    }
270    pub fn is_loopback(&self) -> bool {
271        self.flags.is_loopback
272    }
273
274    pub fn is_point_to_point(&self) -> bool {
275        self.flags.is_point_to_point
276    }
277
278    pub fn is_running(&self) -> bool {
279        self.flags.is_running
280    }
281
282    pub fn has_default_route(&self) -> bool {
283        self.flags.has_default_route
284    }
285
286    pub fn primary_ipv4(&self) -> Option<InterfaceAddress> {
287        let mut ipv4addrs: Vec<&InterfaceAddress> = self
288            .addrs
289            .iter()
290            .filter(|a| matches!(a.if_addr(), IfAddr::V4(_)))
291            .collect();
292        ipv4addrs.sort();
293        ipv4addrs.last().cloned().cloned()
294    }
295
296    pub fn primary_ipv6(&self) -> Option<InterfaceAddress> {
297        let mut ipv6addrs: Vec<&InterfaceAddress> = self
298            .addrs
299            .iter()
300            .filter(|a| matches!(a.if_addr(), IfAddr::V6(_)))
301            .collect();
302        ipv6addrs.sort();
303        ipv6addrs.last().cloned().cloned()
304    }
305}
306
307pub struct NetworkInterfacesInner {
308    valid: bool,
309    interfaces: BTreeMap<String, NetworkInterface>,
310    interface_address_cache: Vec<IpAddr>,
311}
312
313#[derive(Clone)]
314pub struct NetworkInterfaces {
315    inner: Arc<Mutex<NetworkInterfacesInner>>,
316}
317
318impl fmt::Debug for NetworkInterfaces {
319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320        let inner = self.inner.lock();
321        f.debug_struct("NetworkInterfaces")
322            .field("valid", &inner.valid)
323            .field("interfaces", &inner.interfaces)
324            .finish()?;
325        if f.alternate() {
326            writeln!(f)?;
327            writeln!(
328                f,
329                "// stable_addresses: {:?}",
330                inner.interface_address_cache
331            )?;
332        }
333        Ok(())
334    }
335}
336
337impl Default for NetworkInterfaces {
338    fn default() -> Self {
339        Self::new()
340    }
341}
342
343impl NetworkInterfaces {
344    pub fn new() -> Self {
345        Self {
346            inner: Arc::new(Mutex::new(NetworkInterfacesInner {
347                valid: false,
348                interfaces: BTreeMap::new(),
349                interface_address_cache: Vec::new(),
350            })),
351        }
352    }
353
354    pub fn is_valid(&self) -> bool {
355        let inner = self.inner.lock();
356        inner.valid
357    }
358    pub fn clear(&self) {
359        let mut inner = self.inner.lock();
360
361        inner.interfaces.clear();
362        inner.interface_address_cache.clear();
363        inner.valid = false;
364    }
365    // returns false if refresh had no changes, true if changes were present
366    pub async fn refresh(&self) -> std::io::Result<bool> {
367        let mut last_interfaces = {
368            let mut last_interfaces = BTreeMap::<String, NetworkInterface>::new();
369            let mut platform_support = PlatformSupport::new();
370            platform_support
371                .get_interfaces(&mut last_interfaces)
372                .await?;
373            last_interfaces
374        };
375
376        let mut inner = self.inner.lock();
377        core::mem::swap(&mut inner.interfaces, &mut last_interfaces);
378        inner.valid = true;
379
380        if last_interfaces != inner.interfaces {
381            // get last address cache
382            let old_stable_addresses = inner.interface_address_cache.clone();
383
384            // redo the address cache
385            Self::cache_stable_addresses(&mut inner);
386
387            // See if our best addresses have changed
388            if old_stable_addresses != inner.interface_address_cache {
389                return Ok(true);
390            }
391        }
392        Ok(false)
393    }
394    pub fn with_interfaces<F, R>(&self, f: F) -> R
395    where
396        F: FnOnce(&BTreeMap<String, NetworkInterface>) -> R,
397    {
398        let inner = self.inner.lock();
399        f(&inner.interfaces)
400    }
401
402    pub fn stable_addresses(&self) -> Vec<IpAddr> {
403        let inner = self.inner.lock();
404        inner.interface_address_cache.clone()
405    }
406
407    /////////////////////////////////////////////
408
409    fn cache_stable_addresses(inner: &mut NetworkInterfacesInner) {
410        // Reduce interfaces to their best routable ip addresses
411        let mut intf_addrs = Vec::new();
412        for intf in inner.interfaces.values() {
413            if !intf.is_running() || !intf.has_default_route() || intf.is_loopback()
414            // || intf.is_point_to_point() // xxx: iOS cellular is 'point-to-point'
415            {
416                continue;
417            }
418            if let Some(pipv4) = intf.primary_ipv4() {
419                // Skip temporary addresses because they're going to change
420                if !pipv4.is_temporary() {
421                    intf_addrs.push(pipv4);
422                }
423            }
424            if let Some(pipv6) = intf.primary_ipv6() {
425                // Skip temporary addresses because they're going to change
426                if !pipv6.is_temporary() {
427                    intf_addrs.push(pipv6);
428                }
429            }
430        }
431
432        // Sort one more time to get the best interface addresses overall
433        let mut addresses = intf_addrs
434            .iter()
435            .map(|x| x.if_addr().ip())
436            .collect::<Vec<_>>();
437
438        addresses.sort();
439        addresses.dedup();
440
441        // Now export just the addresses
442        inner.interface_address_cache = addresses;
443    }
444}