nix/
ifaddrs.rs

1//! Query network interface addresses
2//!
3//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
4//! of interfaces and their associated addresses.
5
6use cfg_if::cfg_if;
7use std::ffi;
8use std::iter::Iterator;
9use std::mem;
10use std::option::Option;
11
12use crate::{Result, Errno};
13use crate::sys::socket::SockAddr;
14use crate::net::if_::*;
15
16/// Describes a single address for an interface as returned by `getifaddrs`.
17#[derive(Clone, Debug, Eq, Hash, PartialEq)]
18pub struct InterfaceAddress {
19    /// Name of the network interface
20    pub interface_name: String,
21    /// Flags as from `SIOCGIFFLAGS` ioctl
22    pub flags: InterfaceFlags,
23    /// Network address of this interface
24    pub address: Option<SockAddr>,
25    /// Netmask of this interface
26    pub netmask: Option<SockAddr>,
27    /// Broadcast address of this interface, if applicable
28    pub broadcast: Option<SockAddr>,
29    /// Point-to-point destination address
30    pub destination: Option<SockAddr>,
31}
32
33cfg_if! {
34    if #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] {
35        fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
36            info.ifa_ifu
37        }
38    } else {
39        fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
40            info.ifa_dstaddr
41        }
42    }
43}
44
45impl InterfaceAddress {
46    /// Create an `InterfaceAddress` from the libc struct.
47    fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
48        let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
49        let address = unsafe { SockAddr::from_libc_sockaddr(info.ifa_addr) };
50        let netmask = unsafe { SockAddr::from_libc_sockaddr(info.ifa_netmask) };
51        let mut addr = InterfaceAddress {
52            interface_name: ifname.to_string_lossy().to_string(),
53            flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
54            address,
55            netmask,
56            broadcast: None,
57            destination: None,
58        };
59
60        let ifu = get_ifu_from_sockaddr(info);
61        if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
62            addr.destination = unsafe { SockAddr::from_libc_sockaddr(ifu) };
63        } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
64            addr.broadcast = unsafe { SockAddr::from_libc_sockaddr(ifu) };
65        }
66
67        addr
68    }
69}
70
71/// Holds the results of `getifaddrs`.
72///
73/// Use the function `getifaddrs` to create this Iterator. Note that the
74/// actual list of interfaces can be iterated once and will be freed as
75/// soon as the Iterator goes out of scope.
76#[derive(Debug, Eq, Hash, PartialEq)]
77pub struct InterfaceAddressIterator {
78    base: *mut libc::ifaddrs,
79    next: *mut libc::ifaddrs,
80}
81
82impl Drop for InterfaceAddressIterator {
83    fn drop(&mut self) {
84        unsafe { libc::freeifaddrs(self.base) };
85    }
86}
87
88impl Iterator for InterfaceAddressIterator {
89    type Item = InterfaceAddress;
90    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
91        match unsafe { self.next.as_ref() } {
92            Some(ifaddr) => {
93                self.next = ifaddr.ifa_next;
94                Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
95            }
96            None => None,
97        }
98    }
99}
100
101/// Get interface addresses using libc's `getifaddrs`
102///
103/// Note that the underlying implementation differs between OSes. Only the
104/// most common address families are supported by the nix crate (due to
105/// lack of time and complexity of testing). The address family is encoded
106/// in the specific variant of `SockAddr` returned for the fields `address`,
107/// `netmask`, `broadcast`, and `destination`. For any entry not supported,
108/// the returned list will contain a `None` entry.
109///
110/// # Example
111/// ```
112/// let addrs = nix::ifaddrs::getifaddrs().unwrap();
113/// for ifaddr in addrs {
114///   match ifaddr.address {
115///     Some(address) => {
116///       println!("interface {} address {}",
117///                ifaddr.interface_name, address);
118///     },
119///     None => {
120///       println!("interface {} with unsupported address family",
121///                ifaddr.interface_name);
122///     }
123///   }
124/// }
125/// ```
126pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
127    let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
128    unsafe {
129        Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
130            InterfaceAddressIterator {
131                base: addrs.assume_init(),
132                next: addrs.assume_init(),
133            }
134        })
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    // Only checks if `getifaddrs` can be invoked without panicking.
143    #[test]
144    fn test_getifaddrs() {
145        let _ = getifaddrs();
146    }
147}