cotton_netif/
lib.rs

1//! Enumerating network interfaces and their IP addresses
2//!
3//! The netif crate encapsulates the obtaining of the host's network
4//! interfaces and IP addresses. It supports both static/synchronous
5//! listing (i.e., a snapshot of the current list of network
6//! interfaces) using [`get_interfaces`] and dynamic/asynchronous
7//! listing (i.e., getting events as network interfaces and addresses
8//! come and go) using [`get_interfaces_async`].
9//!
10//! At present this crate *only works on Linux* (and maybe BSD) but
11//! the structure is such that adding compatibility with other
12//! platforms in future, shouldn't require changes to any client code.
13//!
14//! Todo:
15//!  - [x] IPv6 in `linux_netlink`
16//!  - [x] Better test coverage
17//!  - [x] Does `DelAddr` need to include the address? *yes*
18//!  - [x] Can `get_interfaces_async` itself not be async?
19//!  - [ ] Can we use just one netlink socket, perhaps with lower-level neli?
20//!  - [x] Turn async into a (cargo) Feature
21//!
22
23#![cfg_attr(not(feature = "std"), no_std)]
24#![warn(missing_docs)]
25#![warn(rustdoc::missing_crate_level_docs)]
26#![cfg_attr(docsrs, feature(doc_auto_cfg))]
27#![cfg_attr(docsrs, feature(doc_cfg_hide))]
28#![cfg_attr(docsrs, doc(cfg_hide(doc)))]
29
30// NB "docsrs" here really means "nightly", but that isn't an available cfg
31
32extern crate alloc;
33
34/** Events passed to interface observers
35 */
36pub mod network_event;
37pub use network_event::{Flags, InterfaceIndex, NetworkEvent};
38
39/** Dynamic listing using Linux's netlink socket
40 */
41#[cfg(all(target_os = "linux", feature = "async"))]
42pub mod linux_netlink;
43
44#[cfg(all(target_os = "linux", feature = "async"))]
45#[doc(inline)]
46pub use linux_netlink::get_interfaces_async;
47
48/** Static listing using Linux/glibc's getifaddrs(3)
49 */
50#[cfg(all(feature = "sync", not(target_os = "none")))]
51pub mod getifaddrs;
52
53#[cfg(all(feature = "sync", not(target_os = "none")))]
54#[doc(inline)]
55pub use getifaddrs::get_interfaces;
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    #[cfg(feature = "std")]
61    use std::collections::HashMap;
62
63    fn make_index(i: u32) -> InterfaceIndex {
64        InterfaceIndex(core::num::NonZeroU32::new(i).unwrap())
65    }
66
67    #[cfg(feature = "std")]
68    #[test]
69    fn test_index_debug() {
70        let ix = make_index(3);
71        let s = format!("{ix:?}");
72        assert_eq!(s, "InterfaceIndex(3)".to_string());
73    }
74
75    #[test]
76    #[allow(clippy::clone_on_copy)]
77    fn test_index_clone() {
78        let ix = make_index(4);
79        let ix2 = ix.clone();
80        let ix3 = ix;
81        assert_eq!(ix, ix2);
82        assert_eq!(ix, ix3);
83    }
84
85    #[cfg(feature = "std")]
86    #[test]
87    fn test_index_hash() {
88        let mut h = HashMap::new();
89        h.insert(make_index(1), "eth0");
90        h.insert(make_index(2), "eth1");
91
92        assert_eq!(h.get(&make_index(1)), Some(&"eth0"));
93    }
94
95    #[test]
96    fn test_index_partialeq() {
97        assert!(make_index(1).eq(&make_index(1)));
98        assert!(make_index(2).ne(&make_index(3)));
99    }
100
101    #[test]
102    fn test_index_partialord() {
103        assert!(make_index(1).lt(&make_index(2)));
104        assert!(make_index(3).ge(&make_index(2)));
105    }
106
107    #[test]
108    fn test_index_ord() {
109        assert_eq!(
110            make_index(1).cmp(&make_index(2)),
111            core::cmp::Ordering::Less
112        );
113        assert_eq!(
114            make_index(3).cmp(&make_index(2)),
115            core::cmp::Ordering::Greater
116        );
117    }
118
119    #[cfg(feature = "std")]
120    #[test]
121    fn test_event_debug() {
122        let e = NetworkEvent::DelLink(make_index(7));
123        let s = format!("{e:?}");
124        assert_eq!(s, "DelLink(InterfaceIndex(7))");
125    }
126
127    #[test]
128    fn test_event_partialeq() {
129        assert!(NetworkEvent::DelLink(make_index(1))
130            .eq(&NetworkEvent::DelLink(make_index(1))));
131        assert!(NetworkEvent::DelLink(make_index(2))
132            .ne(&NetworkEvent::DelLink(make_index(3))));
133    }
134
135    #[test]
136    fn test_event_clone() {
137        let e = NetworkEvent::DelLink(make_index(1));
138        let e2 = e.clone();
139        assert_eq!(e, e2);
140    }
141
142    #[test]
143    fn test_flags_default() {
144        let f = Flags::default();
145        assert_eq!(f, Flags::empty());
146    }
147
148    #[test]
149    #[allow(clippy::clone_on_copy)]
150    fn test_flags_clone() {
151        let f = Flags::POINTTOPOINT;
152        let g = f.clone();
153        assert_eq!(g, Flags::POINTTOPOINT);
154    }
155
156    #[test]
157    fn test_flags_copy() {
158        let f = Flags::POINTTOPOINT;
159        let g = f;
160        assert_eq!(g, Flags::POINTTOPOINT);
161    }
162
163    #[test]
164    fn test_flags_partialeq() {
165        assert!(Flags::POINTTOPOINT.ne(&Flags::empty()));
166    }
167
168    #[test]
169    #[cfg(feature = "std")]
170    fn test_flags_debug() {
171        let s = format!("{:?}", Flags::MULTICAST);
172        assert_eq!(s, "Flags(4096)");
173    }
174}