Skip to main content

sysinfo/common/
network.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::collections::HashMap;
4use std::ffi::OsString;
5use std::fmt;
6use std::net::{AddrParseError, IpAddr};
7use std::num::ParseIntError;
8use std::str::FromStr;
9
10use crate::{Error, NetworkDataInner, NetworksInner};
11
12/// Interacting with network interfaces.
13///
14/// ```no_run
15/// use sysinfo::Networks;
16///
17/// if let Ok(networks) = Networks::new_with_refreshed_list() {
18///     for (interface_name, network) in &networks {
19///         println!("[{}]: {network:?}", interface_name.display());
20///     }
21/// }
22/// ```
23pub struct Networks {
24    pub(crate) inner: NetworksInner,
25}
26
27impl<'a> IntoIterator for &'a Networks {
28    type Item = (&'a OsString, &'a NetworkData);
29    type IntoIter = std::collections::hash_map::Iter<'a, OsString, NetworkData>;
30
31    fn into_iter(self) -> Self::IntoIter {
32        self.iter()
33    }
34}
35
36impl Networks {
37    /// Creates a new empty [`Networks`][crate::Networks] type.
38    ///
39    /// If you want it to be filled directly, take a look at [`Networks::new_with_refreshed_list`].
40    ///
41    /// ```no_run
42    /// use sysinfo::Networks;
43    ///
44    /// if let Ok(mut networks) = Networks::new() {
45    ///     networks.refresh(true);
46    ///     for (interface_name, network) in &networks {
47    ///         println!("[{}]: {network:?}", interface_name.display());
48    ///     }
49    /// }
50    /// ```
51    pub fn new() -> Result<Self, Error> {
52        Ok(Self {
53            inner: NetworksInner::new()?,
54        })
55    }
56
57    /// Creates a new [`Networks`][crate::Networks] type with the network interfaces
58    /// list loaded.
59    ///
60    /// ```no_run
61    /// use sysinfo::Networks;
62    ///
63    /// if let Ok(networks) = Networks::new_with_refreshed_list() {
64    ///     for network in &networks {
65    ///         println!("{network:?}");
66    ///     }
67    /// }
68    /// ```
69    pub fn new_with_refreshed_list() -> Result<Self, Error> {
70        let mut networks = Self::new()?;
71        networks.refresh(false);
72        Ok(networks)
73    }
74
75    /// Refreshes the network interfaces.
76    ///
77    /// ```no_run
78    /// use sysinfo::Networks;
79    ///
80    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
81    ///     // Wait some time...? Then refresh the data of each network.
82    ///     networks.refresh(true);
83    /// }
84    /// ```
85    pub fn refresh(&mut self, remove_not_listed_interfaces: bool) {
86        // Most supported system first list all interfaces, then `retain` only the one that got
87        // refreshed, then get data for each interface. So sadly, we can't do the cleanup here...
88        self.inner.refresh(remove_not_listed_interfaces);
89    }
90}
91
92impl std::ops::Deref for Networks {
93    type Target = HashMap<OsString, NetworkData>;
94
95    fn deref(&self) -> &Self::Target {
96        self.inner.list()
97    }
98}
99
100/// Getting volume of received and transmitted data.
101///
102/// ```no_run
103/// use sysinfo::Networks;
104///
105/// if let Ok(networks) = Networks::new_with_refreshed_list() {
106///     for (interface_name, network) in &networks {
107///         println!("[{}] {network:?}", interface_name.display());
108///     }
109/// }
110/// ```
111pub struct NetworkData {
112    pub(crate) inner: NetworkDataInner,
113}
114
115impl NetworkData {
116    /// Returns the number of received bytes since the last refresh.
117    ///
118    /// If you want the total number of bytes received, take a look at the
119    /// [`total_received`](NetworkData::total_received) method.
120    ///
121    /// ```no_run
122    /// use sysinfo::Networks;
123    /// use std::{thread, time};
124    ///
125    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
126    ///     // Waiting a bit to get data from network...
127    ///     thread::sleep(time::Duration::from_millis(10));
128    ///     // Refreshing again to generate diff.
129    ///     networks.refresh(true);
130    ///
131    ///     for (interface_name, network) in &networks {
132    ///         println!("in: {} B", network.received());
133    ///     }
134    /// }
135    /// ```
136    pub fn received(&self) -> u64 {
137        self.inner.received()
138    }
139
140    /// Returns the total number of received bytes.
141    ///
142    /// If you want the amount of received bytes since the last refresh, take a look at the
143    /// [`received`](NetworkData::received) method.
144    ///
145    /// ```no_run
146    /// use sysinfo::Networks;
147    ///
148    /// if let Ok(networks) = Networks::new_with_refreshed_list() {
149    ///     for (interface_name, network) in &networks {
150    ///         println!("in: {} B", network.total_received());
151    ///     }
152    /// }
153    /// ```
154    pub fn total_received(&self) -> u64 {
155        self.inner.total_received()
156    }
157
158    /// Returns the number of transmitted bytes since the last refresh.
159    ///
160    /// If you want the total number of bytes transmitted, take a look at the
161    /// [`total_transmitted`](NetworkData::total_transmitted) method.
162    ///
163    /// ```no_run
164    /// use sysinfo::Networks;
165    /// use std::{thread, time};
166    ///
167    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
168    ///     // Waiting a bit to get data from network...
169    ///     thread::sleep(time::Duration::from_millis(10));
170    ///     // Refreshing again to generate diff.
171    ///     networks.refresh(true);
172    ///
173    ///     for (interface_name, network) in &networks {
174    ///         println!("out: {} B", network.transmitted());
175    ///     }
176    /// }
177    /// ```
178    pub fn transmitted(&self) -> u64 {
179        self.inner.transmitted()
180    }
181
182    /// Returns the total number of transmitted bytes.
183    ///
184    /// If you want the amount of transmitted bytes since the last refresh, take a look at the
185    /// [`transmitted`](NetworkData::transmitted) method.
186    ///
187    /// ```no_run
188    /// use sysinfo::Networks;
189    ///
190    /// if let Ok(networks) = Networks::new_with_refreshed_list() {
191    ///     for (interface_name, network) in &networks {
192    ///         println!("out: {} B", network.total_transmitted());
193    ///     }
194    /// }
195    /// ```
196    pub fn total_transmitted(&self) -> u64 {
197        self.inner.total_transmitted()
198    }
199
200    /// Returns the number of incoming packets since the last refresh.
201    ///
202    /// If you want the total number of packets received, take a look at the
203    /// [`total_packets_received`](NetworkData::total_packets_received) method.
204    ///
205    /// ```no_run
206    /// use sysinfo::Networks;
207    /// use std::{thread, time};
208    ///
209    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
210    ///     // Waiting a bit to get data from network...
211    ///     thread::sleep(time::Duration::from_millis(10));
212    ///     // Refreshing again to generate diff.
213    ///     networks.refresh(true);
214    ///
215    ///     for (interface_name, network) in &networks {
216    ///         println!("in: {}", network.packets_received());
217    ///     }
218    /// }
219    /// ```
220    pub fn packets_received(&self) -> u64 {
221        self.inner.packets_received()
222    }
223
224    /// Returns the total number of incoming packets.
225    ///
226    /// If you want the amount of received packets since the last refresh, take a look at the
227    /// [`packets_received`](NetworkData::packets_received) method.
228    ///
229    /// ```no_run
230    /// use sysinfo::Networks;
231    ///
232    /// if let Ok(networks) = Networks::new_with_refreshed_list() {
233    ///     for (interface_name, network) in &networks {
234    ///         println!("in: {}", network.total_packets_received());
235    ///     }
236    /// }
237    /// ```
238    pub fn total_packets_received(&self) -> u64 {
239        self.inner.total_packets_received()
240    }
241
242    /// Returns the number of outcoming packets since the last refresh.
243    ///
244    /// If you want the total number of packets transmitted, take a look at the
245    /// [`total_packets_transmitted`](NetworkData::total_packets_transmitted) method.
246    ///
247    /// ```no_run
248    /// use sysinfo::Networks;
249    /// use std::{thread, time};
250    ///
251    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
252    ///     // Waiting a bit to get data from network...
253    ///     thread::sleep(time::Duration::from_millis(10));
254    ///     // Refreshing again to generate diff.
255    ///     networks.refresh(true);
256    ///
257    ///     for (interface_name, network) in &networks {
258    ///         println!("out: {}", network.packets_transmitted());
259    ///     }
260    /// }
261    /// ```
262    pub fn packets_transmitted(&self) -> u64 {
263        self.inner.packets_transmitted()
264    }
265
266    /// Returns the total number of outcoming packets.
267    ///
268    /// If you want the amount of transmitted packets since the last refresh, take a look at the
269    /// [`packets_transmitted`](NetworkData::packets_transmitted) method.
270    ///
271    /// ```no_run
272    /// use sysinfo::Networks;
273    ///
274    /// if let Ok(networks) = Networks::new_with_refreshed_list() {
275    ///     for (interface_name, network) in &networks {
276    ///         println!("out: {}", network.total_packets_transmitted());
277    ///     }
278    /// }
279    /// ```
280    pub fn total_packets_transmitted(&self) -> u64 {
281        self.inner.total_packets_transmitted()
282    }
283
284    /// Returns the number of incoming errors since the last refresh.
285    ///
286    /// If you want the total number of errors on received packets, take a look at the
287    /// [`total_errors_on_received`](NetworkData::total_errors_on_received) method.
288    ///
289    /// ```no_run
290    /// use sysinfo::Networks;
291    /// use std::{thread, time};
292    ///
293    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
294    ///     // Waiting a bit to get data from network...
295    ///     thread::sleep(time::Duration::from_millis(10));
296    ///     // Refreshing again to generate diff.
297    ///     networks.refresh(true);
298    ///
299    ///     for (interface_name, network) in &networks {
300    ///         println!("in: {}", network.errors_on_received());
301    ///     }
302    /// }
303    /// ```
304    pub fn errors_on_received(&self) -> u64 {
305        self.inner.errors_on_received()
306    }
307
308    /// Returns the total number of incoming errors.
309    ///
310    /// If you want the amount of errors on received packets since the last refresh, take a look at
311    /// the [`errors_on_received`](NetworkData::errors_on_received) method.
312    ///
313    /// ```no_run
314    /// use sysinfo::Networks;
315    ///
316    /// if let Ok(networks) = Networks::new_with_refreshed_list() {
317    ///     for (interface_name, network) in &networks {
318    ///         println!("in: {}", network.total_errors_on_received());
319    ///     }
320    /// }
321    /// ```
322    pub fn total_errors_on_received(&self) -> u64 {
323        self.inner.total_errors_on_received()
324    }
325
326    /// Returns the number of outcoming errors since the last refresh.
327    ///
328    /// If you want the total number of errors on transmitted packets, take a look at the
329    /// [`total_errors_on_transmitted`](NetworkData::total_errors_on_transmitted) method.
330    ///
331    /// ```no_run
332    /// use sysinfo::Networks;
333    /// use std::{thread, time};
334    ///
335    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
336    ///     // Waiting a bit to get data from network...
337    ///     thread::sleep(time::Duration::from_millis(10));
338    ///     // Refreshing again to generate diff.
339    ///     networks.refresh(true);
340    ///
341    ///     for (interface_name, network) in &networks {
342    ///         println!("out: {}", network.errors_on_transmitted());
343    ///     }
344    /// }
345    /// ```
346    pub fn errors_on_transmitted(&self) -> u64 {
347        self.inner.errors_on_transmitted()
348    }
349
350    /// Returns the total number of outcoming errors.
351    ///
352    /// If you want the amount of errors on transmitted packets since the last refresh, take a look at
353    /// the [`errors_on_transmitted`](NetworkData::errors_on_transmitted) method.
354    ///
355    /// ```no_run
356    /// use sysinfo::Networks;
357    ///
358    /// if let Ok(networks) = Networks::new_with_refreshed_list() {
359    ///     for (interface_name, network) in &networks {
360    ///         println!("out: {}", network.total_errors_on_transmitted());
361    ///     }
362    /// }
363    /// ```
364    pub fn total_errors_on_transmitted(&self) -> u64 {
365        self.inner.total_errors_on_transmitted()
366    }
367
368    /// Returns the MAC address associated to current interface.
369    ///
370    /// ```no_run
371    /// use sysinfo::Networks;
372    ///
373    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
374    ///     for (interface_name, network) in &networks {
375    ///         println!("MAC address: {}", network.mac_address());
376    ///     }
377    /// }
378    /// ```
379    pub fn mac_address(&self) -> MacAddr {
380        self.inner.mac_address()
381    }
382
383    /// Returns the Ip Networks associated to current interface.
384    ///
385    /// ```no_run
386    /// use sysinfo::Networks;
387    ///
388    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
389    ///     for (interface_name, network) in &networks {
390    ///         println!("Ip Networks: {:?}", network.ip_networks());
391    ///     }
392    /// }
393    /// ```
394    pub fn ip_networks(&self) -> &[IpNetwork] {
395        self.inner.ip_networks()
396    }
397
398    /// Returns the Maximum Transfer Unit (MTU) of the interface.
399    ///
400    /// ```no_run
401    /// use sysinfo::Networks;
402    ///
403    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
404    ///     for (interface_name, network) in &networks {
405    ///         println!("mtu: {}", network.mtu());
406    ///     }
407    /// }
408    /// ```
409    pub fn mtu(&self) -> u64 {
410        self.inner.mtu()
411    }
412
413    /// Returns the transmit link speed of the interface in bits per second.
414    ///
415    /// For Ethernet interfaces, this is the negotiated link speed.
416    ///
417    /// For Wi-Fi interfaces, this is the link speed reported by the OS.
418    ///
419    /// Returns the same value as [Self::receive_link_speed] on Unix systems.
420    ///
421    /// Returns `None` when the link speed is not available.
422    /// For example, when the interface is down or when the driver does not provide a link speed.
423    ///
424    /// ```no_run
425    /// use sysinfo::Networks;
426    ///
427    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
428    ///     for (interface_name, network) in &networks {
429    ///         println!("transmit link speed: {:?}", network.transmit_link_speed());
430    ///     }
431    /// }
432    /// ```
433    pub fn transmit_link_speed(&self) -> Option<u64> {
434        self.inner.transmit_link_speed()
435    }
436
437    /// Returns the receive link speed of the interface in bits per second.
438    ///
439    /// For Ethernet interfaces, this is the negotiated link speed.
440    ///
441    /// For Wi-Fi interfaces, this is the link speed reported by the OS.
442    ///
443    /// Returns the same value as [Self::transmit_link_speed] on Unix systems.
444    ///
445    /// Returns `None` when the link speed is not available.
446    /// For example, when the interface is down or when the driver does not provide a link speed.
447    ///
448    /// ```no_run
449    /// use sysinfo::Networks;
450    ///
451    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
452    ///     for (interface_name, network) in &networks {
453    ///         println!("receive link speed: {:?}", network.receive_link_speed());
454    ///     }
455    /// }
456    /// ```
457    pub fn receive_link_speed(&self) -> Option<u64> {
458        self.inner.receive_link_speed()
459    }
460
461    /// Returns the operational state of the interface.
462    ///
463    /// The operational state indicates whether the interface is able to pass packets or not.
464    /// For details on the individual states see the documentation of the [`InterfaceOperationalState`] enum and its variants.
465    ///
466    /// ```no_run
467    /// use sysinfo::Networks;
468    ///
469    /// if let Ok(mut networks) = Networks::new_with_refreshed_list() {
470    ///     for (interface_name, network) in &networks {
471    ///         println!("operational state: {}", network.operational_state());
472    ///     }
473    /// }
474    /// ```
475    pub fn operational_state(&self) -> InterfaceOperationalState {
476        self.inner.operational_state()
477    }
478}
479
480/// MAC address for network interface.
481///
482/// It is returned by [`NetworkData::mac_address`][crate::NetworkData::mac_address].
483#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
484#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
485pub struct MacAddr(pub [u8; 6]);
486
487impl MacAddr {
488    /// A `MacAddr` with all bytes set to `0`.
489    pub const UNSPECIFIED: Self = MacAddr([0; 6]);
490
491    /// Checks if this `MacAddr` has all bytes equal to `0`.
492    pub fn is_unspecified(&self) -> bool {
493        self == &MacAddr::UNSPECIFIED
494    }
495}
496
497impl fmt::Display for MacAddr {
498    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499        let data = &self.0;
500        write!(
501            f,
502            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
503            data[0], data[1], data[2], data[3], data[4], data[5],
504        )
505    }
506}
507
508/// Error type returned from `MacAddr::from_str` implementation.
509#[derive(Debug, Clone, PartialEq, Eq)]
510pub enum MacAddrFromStrError {
511    /// A number is not in hexadecimal format.
512    IntError(ParseIntError),
513    /// Input is not of format `{02X}:{02X}:{02X}:{02X}:{02X}:{02X}`.
514    InvalidAddrFormat,
515}
516
517impl FromStr for MacAddr {
518    type Err = MacAddrFromStrError;
519
520    fn from_str(s: &str) -> Result<Self, Self::Err> {
521        let mut parts = s
522            .split(':')
523            .map(|s| u8::from_str_radix(s, 16).map_err(MacAddrFromStrError::IntError));
524
525        let Some(data0) = parts.next() else {
526            return Err(MacAddrFromStrError::InvalidAddrFormat);
527        };
528        let Some(data1) = parts.next() else {
529            return Err(MacAddrFromStrError::InvalidAddrFormat);
530        };
531        let Some(data2) = parts.next() else {
532            return Err(MacAddrFromStrError::InvalidAddrFormat);
533        };
534        let Some(data3) = parts.next() else {
535            return Err(MacAddrFromStrError::InvalidAddrFormat);
536        };
537        let Some(data4) = parts.next() else {
538            return Err(MacAddrFromStrError::InvalidAddrFormat);
539        };
540        let Some(data5) = parts.next() else {
541            return Err(MacAddrFromStrError::InvalidAddrFormat);
542        };
543
544        if parts.next().is_some() {
545            return Err(MacAddrFromStrError::InvalidAddrFormat);
546        }
547
548        Ok(MacAddr([data0?, data1?, data2?, data3?, data4?, data5?]))
549    }
550}
551
552impl core::fmt::Display for MacAddrFromStrError {
553    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
554        match self {
555            Self::IntError(error) => write!(f, "a number is not in hexadecimal format: {error}"),
556            Self::InvalidAddrFormat => f.write_str("invalid address format"),
557        }
558    }
559}
560
561impl core::error::Error for MacAddrFromStrError {}
562
563/// IP networks address for network interface.
564///
565/// It is returned by [`NetworkData::ip_networks`][crate::NetworkData::ip_networks].
566#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
567#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
568pub struct IpNetwork {
569    /// The IP of the network interface.
570    pub addr: IpAddr,
571    /// The netmask, prefix of the IP address.
572    pub prefix: u8,
573}
574
575impl fmt::Display for IpNetwork {
576    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577        write!(f, "{}/{}", self.addr, self.prefix)
578    }
579}
580
581/// Error type returned from `MacAddr::from_str` implementation.
582#[derive(Debug, Clone, PartialEq, Eq)]
583pub enum IpNetworkFromStrError {
584    /// Prefix is not an integer.
585    PrefixError(ParseIntError),
586    /// Failed to parse IP address.
587    AddrParseError(AddrParseError),
588    /// Input is not of format `[IP address]/[number]`.
589    InvalidAddrFormat,
590}
591
592impl FromStr for IpNetwork {
593    type Err = IpNetworkFromStrError;
594
595    #[allow(clippy::from_str_radix_10)]
596    fn from_str(s: &str) -> Result<Self, Self::Err> {
597        let mut parts = s.split('/');
598
599        let Some(addr) = parts.next() else {
600            return Err(IpNetworkFromStrError::InvalidAddrFormat);
601        };
602        let Some(prefix) = parts.next() else {
603            return Err(IpNetworkFromStrError::InvalidAddrFormat);
604        };
605        if parts.next().is_some() {
606            return Err(IpNetworkFromStrError::InvalidAddrFormat);
607        }
608
609        Ok(IpNetwork {
610            addr: IpAddr::from_str(addr).map_err(IpNetworkFromStrError::AddrParseError)?,
611            prefix: u8::from_str_radix(prefix, 10).map_err(IpNetworkFromStrError::PrefixError)?,
612        })
613    }
614}
615
616impl core::fmt::Display for IpNetworkFromStrError {
617    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
618        match self {
619            Self::PrefixError(error) => write!(f, "prefix is not an integer: {error}"),
620            Self::AddrParseError(error) => write!(f, "failed to parse IP address: {error}"),
621            Self::InvalidAddrFormat => {
622                f.write_str("invalid address format, should `[IP address]/[number]`")
623            }
624        }
625    }
626}
627
628impl core::error::Error for IpNetworkFromStrError {}
629
630/// The operational state of some interface based on `IfOperStatus`
631/// from [RFC2863].
632///
633/// Not all states are supported by all systems.
634///
635/// If you want to only distinguish between potentially operational and not operational you
636/// likely want to:
637/// - treat [`Up`][Self::Up], [`Dormant`][Self::Dormant] and [`Unknown`][Self::Unknown] as potentially operational
638/// - treat [`Down`][Self::Down], [`Testing`][Self::Testing], [`NotPresent`][Self::NotPresent] and [`LowerLayerDown`][Self::LowerLayerDown] as not operational
639///
640/// [RFC2863]: https://datatracker.ietf.org/doc/html/rfc2863#section-3.1.12
641///
642// windows: https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.operationalstatus?view=net-10.0
643// linux: https://docs.kernel.org/networking/operstates.html#tlv-ifla-operstate
644// discriminants based on windows
645#[non_exhaustive]
646#[derive(Debug, Clone, Copy, PartialEq, Eq)]
647pub enum InterfaceOperationalState {
648    #[doc(hidden)]
649    #[non_exhaustive]
650    /// InterfaceOperationalState is unsupported or had an unrecognized value
651    Other,
652    /// The interface is able to pass packets.
653    Up = 1,
654    /// The interface is unable to pass packets.
655    ///
656    /// ## Examples
657    /// - an error condition has occurred
658    /// - the interface is disabled
659    Down = 2,
660    /// The interface is unable to pass packets.
661    /// The interface is performing some test(s).
662    /// Presumably it will change into another appropriate state once testing completes.
663    ///
664    /// ## Examples
665    /// - driver self-test
666    /// - media (e.g. cable) test
667    Testing = 3,
668    /// The state of the interface is unknown.
669    /// The interface may or may not be usable.
670    ///
671    /// ## Examples
672    /// - on linux the loopback interface (localhost, 127.0.0.1/8, ::1/128) typically has this state
673    Unknown = 4,
674    /// The interface is unable to pass packets.
675    /// It is in a "pending state" waiting for some external event.
676    /// When the event occurs the interface will prusumably change into the `Up`` state.
677    ///
678    /// ## Examples
679    /// - an "on-demand" interface might be waiting for a package in the transmit queue to establish a connection.
680    /// - an interface might be waiting for the remote system to establish a connection
681    /// - waiting for 802.1X protocol to establish a connection
682    Dormant = 5,
683    /// The interface is unable to pass packets.
684    /// A (hardware) component is not present.
685    ///
686    /// This is a refinement on `Down`.
687    ///
688    /// ## Examples
689    /// - hot-swap module is currently not installed
690    /// - pre-configured interface is not yet installed
691    /// - an interface has been removed but is still configured
692    NotPresent = 6,
693    /// The interface is unable to pass packets.
694    /// This interface depends on one or more interfaces and at least one of these interfaces prevents this interface from being `Up`.
695    ///
696    /// This is a refinement on `Down`.
697    ///
698    /// ## Examples
699    /// - a VPN interface depends on any physical interface being up but the physical interface is testing the cable to determine the available speed
700    /// - a single virtual interface that aggregates multiple physical interfaces but all physical interfaces are down
701    LowerLayerDown = 7,
702}
703
704impl fmt::Display for InterfaceOperationalState {
705    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706        // see /sys/class/net/<iface>/operstate section at https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-net
707        f.write_str(match self {
708            InterfaceOperationalState::Other => "other",
709            InterfaceOperationalState::Up => "up",
710            InterfaceOperationalState::Down => "down",
711            InterfaceOperationalState::Testing => "testing",
712            InterfaceOperationalState::Unknown => "unknown",
713            InterfaceOperationalState::Dormant => "dormant",
714            InterfaceOperationalState::NotPresent => "notpresent",
715            InterfaceOperationalState::LowerLayerDown => "lowerlayerdown",
716        })
717    }
718}
719
720#[cfg(test)]
721mod tests {
722    use crate::*;
723    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
724    use std::str::FromStr;
725
726    // Ensure that the `Display` and `Debug` traits are implemented on the `MacAddr` struct
727    #[test]
728    fn check_display_impl_mac_address() {
729        println!(
730            "{} {:?}",
731            MacAddr([0x1, 0x2, 0x3, 0x4, 0x5, 0x6]),
732            MacAddr([0xa, 0xb, 0xc, 0xd, 0xe, 0xf])
733        );
734    }
735
736    #[test]
737    fn check_mac_address_is_unspecified_true() {
738        assert!(MacAddr::UNSPECIFIED.is_unspecified());
739        assert!(MacAddr([0; 6]).is_unspecified());
740    }
741
742    #[test]
743    fn check_mac_address_is_unspecified_false() {
744        assert!(!MacAddr([1, 2, 3, 4, 5, 6]).is_unspecified());
745    }
746
747    #[test]
748    fn check_mac_address_conversions() {
749        let mac = MacAddr([0xa, 0xb, 0xc, 0xd, 0xe, 0xf]);
750
751        let mac_s = mac.to_string();
752        assert_eq!("0a:0b:0c:0d:0e:0f", mac_s);
753        assert_eq!(Ok(mac), MacAddr::from_str(&mac_s));
754
755        assert_eq!(
756            MacAddr::from_str("0a:0b:0c:0d:0e:0f:01"),
757            Err(MacAddrFromStrError::InvalidAddrFormat)
758        );
759        assert_eq!(
760            MacAddr::from_str("0a:0b:0c:0d:0e"),
761            Err(MacAddrFromStrError::InvalidAddrFormat)
762        );
763    }
764
765    // Ensure that the `Display` and `Debug` traits are implemented on the `IpNetwork` struct
766    #[test]
767    fn check_display_impl_ip_network_ipv4() {
768        println!(
769            "{} {:?}",
770            IpNetwork {
771                addr: IpAddr::from(Ipv4Addr::new(1, 2, 3, 4)),
772                prefix: 3
773            },
774            IpNetwork {
775                addr: IpAddr::from(Ipv4Addr::new(255, 255, 255, 0)),
776                prefix: 21
777            }
778        );
779    }
780
781    #[test]
782    fn check_display_impl_ip_network_ipv6() {
783        println!(
784            "{} {:?}",
785            IpNetwork {
786                addr: IpAddr::from(Ipv6Addr::new(0xffff, 0xaabb, 00, 0, 0, 0x000c, 11, 21)),
787                prefix: 127
788            },
789            IpNetwork {
790                addr: IpAddr::from(Ipv6Addr::new(0xffcc, 0, 0, 0xffcc, 0, 0xffff, 0, 0xccaa)),
791                prefix: 120
792            }
793        )
794    }
795
796    #[test]
797    fn check_ip_networks() {
798        let networks = check_unsupported!(Networks::new_with_refreshed_list());
799        if networks.iter().any(|(_, n)| !n.ip_networks().is_empty()) {
800            return;
801        }
802        panic!("Networks should have at least one IP network ");
803    }
804
805    #[test]
806    fn check_ip_network_conversions() {
807        let addr = IpNetwork {
808            addr: IpAddr::from(Ipv6Addr::new(0xff, 0xa, 0x8, 0x12, 0x7, 0xc, 0xa, 0xb)),
809            prefix: 12,
810        };
811
812        let addr_s = addr.to_string();
813        assert_eq!("ff:a:8:12:7:c:a:b/12", addr_s);
814        assert_eq!(Ok(addr), IpNetwork::from_str(&addr_s));
815
816        let addr = IpNetwork {
817            addr: IpAddr::from(Ipv4Addr::new(255, 255, 255, 0)),
818            prefix: 21,
819        };
820
821        let addr_s = addr.to_string();
822        assert_eq!("255.255.255.0/21", addr_s);
823        assert_eq!(Ok(addr), IpNetwork::from_str(&addr_s));
824
825        assert_eq!(
826            IpNetwork::from_str("ff:a:8:12:7:c:a:b"),
827            Err(IpNetworkFromStrError::InvalidAddrFormat)
828        );
829        assert_eq!(
830            IpNetwork::from_str("ff:a:8:12:7:c:a:b/12/12"),
831            Err(IpNetworkFromStrError::InvalidAddrFormat)
832        );
833        match IpNetwork::from_str("0a:0b:0c:0d:0e/12") {
834            Err(IpNetworkFromStrError::AddrParseError(_)) => {}
835            x => panic!("expected `IpNetworkFromStrError::AddrParseError`, found {x:?}"),
836        }
837    }
838}