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 fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
38 info.ifa_ifu
39 }
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)]
workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage>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.
from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress87 fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
88 let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
89 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 let netmask =
94 unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
95 let mut addr = InterfaceAddress {
96 interface_name: ifname.to_string_lossy().to_string(),
97 flags: InterfaceFlags::from_bits_truncate(
98 info.ifa_flags as IflagsType,
99 ),
100 address,
101 netmask,
102 broadcast: None,
103 destination: None,
104 };
105
106 let ifu = get_ifu_from_sockaddr(info);
107 if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
108 addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) };
109 } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
110 addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) };
111 }
112
113 addr
114 }
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 {
drop(&mut self)129 fn drop(&mut self) {
130 unsafe { libc::freeifaddrs(self.base) };
131 }
132 }
133
134 impl Iterator for InterfaceAddressIterator {
135 type Item = InterfaceAddress;
next(&mut self) -> Option<<Self as Iterator>::Item>136 fn next(&mut self) -> Option<<Self as Iterator>::Item> {
137 match unsafe { self.next.as_ref() } {
138 Some(ifaddr) => {
139 self.next = ifaddr.ifa_next;
140 Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
141 }
142 None => None,
143 }
144 }
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 /// ```
getifaddrs() -> Result<InterfaceAddressIterator>172 pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
173 let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
174 unsafe {
175 Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
176 InterfaceAddressIterator {
177 base: addrs.assume_init(),
178 next: addrs.assume_init(),
179 }
180 })
181 }
182 }
183
184 #[cfg(test)]
185 mod tests {
186 use super::*;
187
188 // Only checks if `getifaddrs` can be invoked without panicking.
189 #[test]
test_getifaddrs()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]
test_getifaddrs_netmask_correct()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 }
218