Coverage Report

Created: 2026-02-14 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/ifaddrs.rs
Line
Count
Source
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
6
use cfg_if::cfg_if;
7
#[cfg(apple_targets)]
8
use std::convert::TryFrom;
9
use std::ffi;
10
use std::iter::Iterator;
11
use std::mem;
12
use std::option::Option;
13
14
use crate::net::if_::*;
15
use crate::sys::socket::{SockaddrLike, SockaddrStorage};
16
use crate::{Errno, Result};
17
18
/// Describes a single address for an interface as returned by `getifaddrs`.
19
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
20
pub struct InterfaceAddress {
21
    /// Name of the network interface
22
    pub interface_name: String,
23
    /// Flags as from `SIOCGIFFLAGS` ioctl
24
    pub flags: InterfaceFlags,
25
    /// Network address of this interface
26
    pub address: Option<SockaddrStorage>,
27
    /// Netmask of this interface
28
    pub netmask: Option<SockaddrStorage>,
29
    /// Broadcast address of this interface, if applicable
30
    pub broadcast: Option<SockaddrStorage>,
31
    /// Point-to-point destination address
32
    pub destination: Option<SockaddrStorage>,
33
}
34
35
cfg_if! {
36
    if #[cfg(any(linux_android, target_os = "emscripten", target_os = "fuchsia"))] {
37
0
        fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
38
0
            info.ifa_ifu
39
0
        }
40
    } else {
41
        fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
42
            info.ifa_dstaddr
43
        }
44
    }
45
}
46
47
/// Workaround a bug in XNU where netmasks will always have the wrong size in
48
/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
49
/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
50
///
51
/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
52
/// memcpy sa_len of the netmask to that new storage. Finally, we reset the
53
/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
54
/// members of the sockaddr_storage are "ok" with being zeroed out (there are
55
/// no pointers).
56
#[cfg(apple_targets)]
57
unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
58
    let src_sock = info.ifa_netmask;
59
    if src_sock.is_null() {
60
        return None;
61
    }
62
63
    let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();
64
65
    let dst_sock = unsafe {
66
        // memcpy only sa_len bytes, assume the rest is zero
67
        std::ptr::copy_nonoverlapping(
68
            src_sock as *const u8,
69
            dst_sock.as_mut_ptr().cast(),
70
            (*src_sock).sa_len.into(),
71
        );
72
73
        // Initialize ss_len to sizeof(libc::sockaddr_storage).
74
        (*dst_sock.as_mut_ptr()).ss_len =
75
            u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
76
        dst_sock.assume_init()
77
    };
78
79
    let dst_sock_ptr =
80
        &dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
81
82
    unsafe { SockaddrStorage::from_raw(dst_sock_ptr, None) }
83
}
84
85
impl InterfaceAddress {
86
    /// Create an `InterfaceAddress` from the libc struct.
87
0
    fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
88
0
        let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
89
0
        let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
90
        #[cfg(apple_targets)]
91
        let netmask = unsafe { workaround_xnu_bug(info) };
92
        #[cfg(not(apple_targets))]
93
0
        let netmask =
94
0
            unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
95
0
        let mut addr = InterfaceAddress {
96
0
            interface_name: ifname.to_string_lossy().into_owned(),
97
0
            flags: InterfaceFlags::from_bits_truncate(
98
0
                info.ifa_flags as IflagsType,
99
0
            ),
100
0
            address,
101
0
            netmask,
102
0
            broadcast: None,
103
0
            destination: None,
104
0
        };
105
106
0
        let ifu = get_ifu_from_sockaddr(info);
107
0
        if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
108
0
            addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) };
109
0
        } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
110
0
            addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) };
111
0
        }
112
113
0
        addr
114
0
    }
115
}
116
117
/// Holds the results of `getifaddrs`.
118
///
119
/// Use the function `getifaddrs` to create this Iterator. Note that the
120
/// actual list of interfaces can be iterated once and will be freed as
121
/// soon as the Iterator goes out of scope.
122
#[derive(Debug, Eq, Hash, PartialEq)]
123
pub struct InterfaceAddressIterator {
124
    base: *mut libc::ifaddrs,
125
    next: *mut libc::ifaddrs,
126
}
127
128
impl Drop for InterfaceAddressIterator {
129
0
    fn drop(&mut self) {
130
0
        unsafe { libc::freeifaddrs(self.base) };
131
0
    }
132
}
133
134
impl Iterator for InterfaceAddressIterator {
135
    type Item = InterfaceAddress;
136
0
    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
137
0
        match unsafe { self.next.as_ref() } {
138
0
            Some(ifaddr) => {
139
0
                self.next = ifaddr.ifa_next;
140
0
                Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
141
            }
142
0
            None => None,
143
        }
144
0
    }
145
}
146
147
/// Get interface addresses using libc's `getifaddrs`
148
///
149
/// Note that the underlying implementation differs between OSes. Only the
150
/// most common address families are supported by the nix crate (due to
151
/// lack of time and complexity of testing). The address family is encoded
152
/// in the specific variant of `SockaddrStorage` returned for the fields
153
/// `address`, `netmask`, `broadcast`, and `destination`. For any entry not
154
/// supported, the returned list will contain a `None` entry.
155
///
156
/// # Example
157
/// ```
158
/// let addrs = nix::ifaddrs::getifaddrs().unwrap();
159
/// for ifaddr in addrs {
160
///   match ifaddr.address {
161
///     Some(address) => {
162
///       println!("interface {} address {}",
163
///                ifaddr.interface_name, address);
164
///     },
165
///     None => {
166
///       println!("interface {} with unsupported address family",
167
///                ifaddr.interface_name);
168
///     }
169
///   }
170
/// }
171
/// ```
172
0
pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
173
0
    let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
174
    unsafe {
175
0
        Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
176
0
            InterfaceAddressIterator {
177
0
                base: addrs.assume_init(),
178
0
                next: addrs.assume_init(),
179
0
            }
180
0
        })
181
    }
182
0
}
183
184
#[cfg(test)]
185
mod tests {
186
    use super::*;
187
188
    // Only checks if `getifaddrs` can be invoked without panicking.
189
    #[test]
190
    fn test_getifaddrs() {
191
        let _ = getifaddrs();
192
    }
193
194
    // Ensures getting the netmask works, and in particular that
195
    // `workaround_xnu_bug` works properly.
196
    #[test]
197
    fn test_getifaddrs_netmask_correct() {
198
        let addrs = getifaddrs().unwrap();
199
        for iface in addrs {
200
            let sock = if let Some(sock) = iface.netmask {
201
                sock
202
            } else {
203
                continue;
204
            };
205
            if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
206
                let _ = sock.as_sockaddr_in().unwrap();
207
                return;
208
            } else if sock.family()
209
                == Some(crate::sys::socket::AddressFamily::Inet6)
210
            {
211
                let _ = sock.as_sockaddr_in6().unwrap();
212
                return;
213
            }
214
        }
215
        panic!("No address?");
216
    }
217
}