1use std::collections::HashMap;
4use std::collections::HashSet;
5use std::ffi::CStr;
6use std::mem::MaybeUninit;
7use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
8use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
9use std::os::raw::c_char;
10use std::ptr::null_mut;
11use std::str::from_utf8_unchecked;
12use std::{io, mem};
13
14use crate::{IpNetwork, MacAddr};
15
16pub(crate) struct InterfaceAddressIterator {
18 ifap: *mut libc::ifaddrs,
20 buf: *mut libc::ifaddrs,
22}
23
24impl Iterator for InterfaceAddressIterator {
25 type Item = (String, MacAddr);
26
27 fn next(&mut self) -> Option<Self::Item> {
28 unsafe {
29 while !self.ifap.is_null() {
30 let ifap = self.ifap;
32 self.ifap = (*ifap).ifa_next;
33
34 if let Some(addr) = parse_interface_address(ifap) {
35 let ifa_name = (*ifap).ifa_name;
36 if ifa_name.is_null() {
37 continue;
38 }
39 let mut name = vec![0u8; libc::IFNAMSIZ + 6];
42 libc::strcpy(name.as_mut_ptr() as _, (*ifap).ifa_name);
43 name.set_len(libc::strlen((*ifap).ifa_name));
44 let name = String::from_utf8_unchecked(name);
45
46 return Some((name, addr));
47 }
48 }
49 None
50 }
51 }
52}
53
54impl Drop for InterfaceAddressIterator {
55 fn drop(&mut self) {
56 unsafe {
57 libc::freeifaddrs(self.buf);
58 }
59 }
60}
61
62#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "ios"))]
63impl From<&libc::sockaddr_dl> for MacAddr {
64 fn from(value: &libc::sockaddr_dl) -> Self {
65 let sdl_data = value.sdl_data;
66 let sdl_nlen = value.sdl_nlen as usize;
68 if sdl_nlen + 5 < 12 {
70 MacAddr([
71 sdl_data[sdl_nlen] as u8,
72 sdl_data[sdl_nlen + 1] as u8,
73 sdl_data[sdl_nlen + 2] as u8,
74 sdl_data[sdl_nlen + 3] as u8,
75 sdl_data[sdl_nlen + 4] as u8,
76 sdl_data[sdl_nlen + 5] as u8,
77 ])
78 } else {
79 MacAddr::UNSPECIFIED
80 }
81 }
82}
83
84#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "ios"))]
85unsafe fn parse_interface_address(ifap: *const libc::ifaddrs) -> Option<MacAddr> {
86 let sock_addr = (*ifap).ifa_addr;
87 if sock_addr.is_null() {
88 return None;
89 }
90 match (*sock_addr).sa_family as libc::c_int {
91 libc::AF_LINK => {
92 let addr = sock_addr as *const libc::sockaddr_dl;
93 Some(MacAddr::from(&*addr))
94 }
95 _ => None,
96 }
97}
98
99#[cfg(any(target_os = "linux", target_os = "android"))]
100unsafe fn parse_interface_address(ifap: *const libc::ifaddrs) -> Option<MacAddr> {
101 use libc::sockaddr_ll;
102
103 let sock_addr = (*ifap).ifa_addr;
104 if sock_addr.is_null() {
105 return None;
106 }
107 match (*sock_addr).sa_family as libc::c_int {
108 libc::AF_PACKET => {
109 let addr = sock_addr as *const sockaddr_ll;
110 let [addr @ .., _, _] = (*addr).sll_addr;
112 Some(MacAddr(addr))
113 }
114 _ => None,
115 }
116}
117
118pub(crate) unsafe fn get_interface_address() -> Result<InterfaceAddressIterator, String> {
120 let mut ifap = null_mut();
121 if retry_eintr!(libc::getifaddrs(&mut ifap)) == 0 && !ifap.is_null() {
122 Ok(InterfaceAddressIterator { ifap, buf: ifap })
123 } else {
124 Err("failed to call getifaddrs()".to_string())
125 }
126}
127
128pub(crate) unsafe fn get_interface_ip_networks() -> HashMap<String, HashSet<IpNetwork>> {
129 let mut ifaces: HashMap<String, HashSet<IpNetwork>> = HashMap::new();
130 let mut addrs: MaybeUninit<*mut libc::ifaddrs> = MaybeUninit::uninit();
131
132 if libc::getifaddrs(addrs.as_mut_ptr()) != 0 {
134 sysinfo_debug!("Failed to operate libc::getifaddrs as ifaddrs Uninitialized");
135 return ifaces;
136 }
137
138 let addrs = addrs.assume_init();
141
142 let mut addr = addrs;
143 while !addr.is_null() {
144 let addr_ref: &libc::ifaddrs = &*addr;
147
148 let c_str = addr_ref.ifa_name as *const c_char;
149
150 let bytes = CStr::from_ptr(c_str).to_bytes();
152
153 let mut name = from_utf8_unchecked(bytes).to_owned();
155 if name.contains(':') {
157 if let Some(interface_name) = name.split(':').next() {
158 name = interface_name.to_string()
159 } else {
160 addr = addr_ref.ifa_next;
162 continue;
163 }
164 }
165
166 let ip = sockaddr_to_network_addr(addr_ref.ifa_addr as *const libc::sockaddr);
167 let netmask = sockaddr_to_network_addr(addr_ref.ifa_netmask as *const libc::sockaddr);
168 let prefix = netmask
169 .and_then(|netmask| ip_mask_to_prefix(netmask).ok())
170 .unwrap_or(0);
171 if let Some(ip) = ip {
172 ifaces
173 .entry(name)
174 .and_modify(|values| {
175 values.insert(IpNetwork { addr: ip, prefix });
176 })
177 .or_insert(HashSet::from([IpNetwork { addr: ip, prefix }]));
178 }
179 addr = addr_ref.ifa_next;
180 }
181
182 libc::freeifaddrs(addrs);
184 ifaces
185}
186
187#[cfg(any(target_os = "linux", target_os = "android"))]
188fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> Option<IpAddr> {
189 unsafe {
190 if sa.is_null() || (*sa).sa_family as libc::c_int == libc::AF_PACKET {
191 None
192 } else {
193 let addr = sockaddr_to_addr(
194 &(sa as *const libc::sockaddr_storage).read_unaligned(),
195 mem::size_of::<libc::sockaddr_storage>(),
196 );
197
198 match addr {
199 Ok(SocketAddr::V4(sa)) => Some(IpAddr::V4(*sa.ip())),
200 Ok(SocketAddr::V6(sa)) => Some(IpAddr::V6(*sa.ip())),
201 _ => None,
202 }
203 }
204 }
205}
206#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
207pub type InAddrType = libc::c_uint;
208#[cfg(any(target_os = "illumos", target_os = "solaris"))]
209pub type InAddrType = libc::c_ulonglong;
210
211fn sockaddr_to_addr(storage: &libc::sockaddr_storage, len: usize) -> io::Result<SocketAddr> {
212 match storage.ss_family as libc::c_int {
213 libc::AF_INET => {
214 assert!(len >= mem::size_of::<libc::sockaddr_in>());
215 let storage: &libc::sockaddr_in = unsafe { mem::transmute(storage) };
216 let ip = (storage.sin_addr.s_addr as InAddrType).to_be();
217 let a = (ip >> 24) as u8;
218 let b = (ip >> 16) as u8;
219 let c = (ip >> 8) as u8;
220 let d = ip as u8;
221 let sockaddrv4 = SocketAddrV4::new(Ipv4Addr::new(a, b, c, d), storage.sin_port.to_be());
222 Ok(SocketAddr::V4(sockaddrv4))
223 }
224 libc::AF_INET6 => {
225 assert!(len >= mem::size_of::<libc::sockaddr_in6>());
226 let storage: &libc::sockaddr_in6 = unsafe { mem::transmute(storage) };
227 let arr: [u16; 8] = unsafe { mem::transmute(storage.sin6_addr.s6_addr) };
228 let ip = Ipv6Addr::new(
229 arr[0].to_be(),
230 arr[1].to_be(),
231 arr[2].to_be(),
232 arr[3].to_be(),
233 arr[4].to_be(),
234 arr[5].to_be(),
235 arr[6].to_be(),
236 arr[7].to_be(),
237 );
238 Ok(SocketAddr::V6(SocketAddrV6::new(
239 ip,
240 storage.sin6_port.to_be(),
241 u32::from_be(storage.sin6_flowinfo),
242 storage.sin6_scope_id,
243 )))
244 }
245 _ => Err(io::Error::new(
246 io::ErrorKind::InvalidData,
247 "expected IPv4 or IPv6 socket",
248 )),
249 }
250}
251
252#[cfg(any(
253 target_os = "openbsd",
254 target_os = "freebsd",
255 target_os = "netbsd",
256 target_os = "illumos",
257 target_os = "solaris",
258 target_os = "macos",
259 target_os = "ios"
260))]
261fn sockaddr_to_network_addr(sa: *const libc::sockaddr) -> Option<IpAddr> {
262 unsafe {
263 if sa.is_null() || (*sa).sa_family as libc::c_int == 18 {
264 None
265 } else {
266 let addr = sockaddr_to_addr(
267 &(sa as *const libc::sockaddr_storage).read_unaligned(),
268 mem::size_of::<libc::sockaddr_storage>(),
269 );
270
271 match addr {
272 Ok(SocketAddr::V4(sa)) => Some(IpAddr::V4(*sa.ip())),
273 Ok(SocketAddr::V6(sa)) => Some(IpAddr::V6(*sa.ip())),
274 _ => None,
275 }
276 }
277 }
278}
279
280pub(crate) fn ip_mask_to_prefix(mask: IpAddr) -> Result<u8, &'static str> {
281 match mask {
282 IpAddr::V4(mask) => ipv4_mask_to_prefix(mask),
283 IpAddr::V6(mask) => ipv6_mask_to_prefix(mask),
284 }
285}
286
287pub(crate) fn ipv4_mask_to_prefix(mask: Ipv4Addr) -> Result<u8, &'static str> {
288 let mask = u32::from(mask);
289
290 let prefix = (!mask).leading_zeros() as u8;
291 if (u64::from(mask) << prefix) & 0xffff_ffff != 0 {
292 Err("invalid ipv4 prefix")
293 } else {
294 Ok(prefix)
295 }
296}
297
298pub(crate) fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, &'static str> {
299 let mask = mask.segments();
300 let mut mask_iter = mask.iter();
301
302 let mut prefix = 0;
304 for &segment in &mut mask_iter {
305 if segment == 0xffff {
306 prefix += 16;
307 } else if segment == 0 {
308 break;
310 } else {
311 let prefix_bits = (!segment).leading_zeros() as u8;
312 if segment << prefix_bits != 0 {
314 return Err("invalid ipv6 prefix");
315 }
316 prefix += prefix_bits;
317 break;
318 }
319 }
320
321 for &segment in mask_iter {
323 if segment != 0 {
324 return Err("invalid ipv6 prefix");
325 }
326 }
327
328 Ok(prefix)
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn ipv4_mask() {
337 let mask = Ipv4Addr::new(255, 255, 255, 0);
338 let prefix = ipv4_mask_to_prefix(mask).unwrap();
339 assert_eq!(prefix, 24);
340 }
341
342 #[test]
343 fn ipv4_mask_another() {
344 let mask = Ipv4Addr::new(255, 255, 255, 128);
345 let prefix = ipv4_mask_to_prefix(mask).unwrap();
346 assert_eq!(prefix, 25);
347 }
348
349 #[test]
350 fn v4_mask_to_prefix_invalid() {
351 let mask = Ipv4Addr::new(255, 128, 255, 0);
352 assert!(ipv4_mask_to_prefix(mask).is_err());
353 }
354
355 #[test]
356 fn ipv6_mask() {
357 let mask = Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0, 0, 0, 0, 0);
358 let prefix = ipv6_mask_to_prefix(mask).unwrap();
359 assert_eq!(prefix, 48);
360 }
361
362 #[test]
363 fn ipv6_mask_invalid() {
364 let mask = Ipv6Addr::new(0, 0xffff, 0xffff, 0, 0, 0, 0, 0);
365 assert!(ipv6_mask_to_prefix(mask).is_err());
366 }
367
368 #[test]
369 fn ip_mask_enum_ipv4() {
370 let mask = IpAddr::from(Ipv4Addr::new(255, 255, 255, 0));
371 let prefix = ip_mask_to_prefix(mask).unwrap();
372 assert_eq!(prefix, 24);
373 }
374
375 #[test]
376 fn ip_mask_enum_ipv4_invalid() {
377 let mask = IpAddr::from(Ipv4Addr::new(255, 0, 255, 0));
378 assert!(ip_mask_to_prefix(mask).is_err());
379 }
380
381 #[test]
382 fn ip_mask_enum_ipv6() {
383 let mask = IpAddr::from(Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0, 0, 0, 0, 0));
384 let prefix = ip_mask_to_prefix(mask).unwrap();
385 assert_eq!(prefix, 48);
386 }
387
388 #[test]
389 fn ip_mask_enum_ipv6_invalid() {
390 let mask = IpAddr::from(Ipv6Addr::new(0xffff, 0xffff, 0xff00, 0xffff, 0, 0, 0, 0));
391 assert!(ip_mask_to_prefix(mask).is_err());
392 }
393}