sfo_net_utils/
get_if_addrs.rs

1//! get_if_addrs
2#[cfg(windows)]
3extern crate winapi;
4
5use std::io;
6use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
7
8#[cfg(not(windows))]
9#[cfg(windows)]
10use std::os::windows::prelude::*;
11
12extern crate c_linked_list;
13#[cfg(not(windows))]
14extern crate libc;
15
16/// Details about an interface on this host
17#[derive(Debug, PartialEq, Eq, Hash, Clone)]
18pub struct Interface {
19    /// The name of the interface.
20    pub name: String,
21    /// The address details of the interface.
22    pub addr: IfAddr,
23
24    pub description: String,
25
26    pub ifa_flags: u32,
27
28    pub scope_id: u32,
29}
30
31/// Details about the address of an interface on this host
32#[derive(Debug, PartialEq, Eq, Hash, Clone)]
33pub enum IfAddr {
34    /// This is an Ipv4 interface.
35    V4(Ifv4Addr),
36    /// This is an Ipv6 interface.
37    V6(Ifv6Addr),
38}
39
40/// Details about the ipv4 address of an interface on this host
41#[derive(Debug, PartialEq, Eq, Hash, Clone)]
42pub struct Ifv4Addr {
43    /// The IP address of the interface.
44    pub ip: Ipv4Addr,
45    /// The netmask of the interface.
46    pub netmask: Ipv4Addr,
47    /// The broadcast address of the interface.
48    pub broadcast: Option<Ipv4Addr>,
49}
50
51/// Details about the ipv6 address of an interface on this host
52#[derive(Debug, PartialEq, Eq, Hash, Clone)]
53pub struct Ifv6Addr {
54    /// The IP address of the interface.
55    pub ip: Ipv6Addr,
56    /// The netmask of the interface.
57    pub netmask: Ipv6Addr,
58    /// The broadcast address of the interface.
59    pub broadcast: Option<Ipv6Addr>,
60}
61
62impl Interface {
63    /// Check whether this is a loopback interface.
64    pub fn is_loopback(&self) -> bool {
65        self.addr.is_loopback()
66    }
67
68    /// Get the IP address of this interface.
69    pub fn ip(&self) -> IpAddr {
70        self.addr.ip()
71    }
72}
73
74impl IfAddr {
75    /// Check whether this is a loopback address.
76    pub fn is_loopback(&self) -> bool {
77        match *self {
78            IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_loopback(),
79            IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_loopback(),
80        }
81    }
82
83    /// Get the IP address of this interface address.
84    pub fn ip(&self) -> IpAddr {
85        match *self {
86            IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.ip),
87            IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.ip),
88        }
89    }
90}
91
92impl Ifv4Addr {
93    /// Check whether this is a loopback address.
94    pub fn is_loopback(&self) -> bool {
95        self.ip.octets()[0] == 127
96    }
97}
98
99impl Ifv6Addr {
100    /// Check whether this is a loopback address.
101    pub fn is_loopback(&self) -> bool {
102        self.ip.segments() == [0, 0, 0, 0, 0, 0, 0, 1]
103    }
104}
105
106#[cfg(not(windows))]
107mod getifaddrs_posix {
108    use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
109    use c_linked_list::CLinkedListMut;
110    use libc::freeifaddrs as posix_freeifaddrs;
111    use libc::getifaddrs as posix_getifaddrs;
112    use libc::ifaddrs as posix_ifaddrs;
113
114    use libc::sockaddr as posix_sockaddr;
115    use libc::sockaddr_in as posix_sockaddr_in;
116    use libc::sockaddr_in6 as posix_sockaddr_in6;
117    use libc::{AF_INET, AF_INET6};
118    use std::ffi::CStr;
119    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
120    use std::{io, mem};
121
122    #[allow(non_camel_case_types)]
123    pub enum IFFFlags {
124        IFF_UP = 1 << 0,          /* sysfs */
125        IFF_BROADCAST = 1 << 1,   /* volatile */
126        IFF_DEBUG = 1 << 2,       /* sysfs */
127        IFF_LOOPBACK = 1 << 3,    /* volatile */
128        IFF_POINTOPOINT = 1 << 4, /* volatile */
129        IFF_NOTRAILERS = 1 << 5,  /* sysfs */
130        IFF_RUNNING = 1 << 6,     /* volatile */
131        IFF_NOARP = 1 << 7,       /* sysfs */
132        IFF_PROMISC = 1 << 8,     /* sysfs */
133        IFF_ALLMULTI = 1 << 9,    /* sysfs */
134        IFF_MASTER = 1 << 10,     /* volatile */
135        IFF_SLAVE = 1 << 11,      /* volatile */
136        IFF_MULTICAST = 1 << 12,  /* sysfs */
137        IFF_PORTSEL = 1 << 13,    /* sysfs */
138        IFF_AUTOMEDIA = 1 << 14,  /* sysfs */
139        IFF_DYNAMIC = 1 << 15,    /* sysfs */
140        IFF_LOWER_UP = 1 << 16,   /* volatile */
141        IFF_DORMANT = 1 << 17,    /* volatile */
142        IFF_ECHO = 1 << 18,       /* volatile */
143    }
144
145    #[allow(unsafe_code)]
146    fn sockaddr_to_ipaddr(sockaddr: *const posix_sockaddr) -> Option<(IpAddr, u32)> {
147        if sockaddr.is_null() {
148            return None;
149        }
150
151        let sa_family = u32::from(unsafe { *sockaddr }.sa_family);
152
153        if sa_family == AF_INET as u32 {
154            #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
155            let sa = &unsafe { *(sockaddr as *const posix_sockaddr_in) };
156            Some((IpAddr::V4(Ipv4Addr::new(
157                ((sa.sin_addr.s_addr) & 255) as u8,
158                ((sa.sin_addr.s_addr >> 8) & 255) as u8,
159                ((sa.sin_addr.s_addr >> 16) & 255) as u8,
160                ((sa.sin_addr.s_addr >> 24) & 255) as u8,
161            )), 0))
162        } else if sa_family == AF_INET6 as u32 {
163            #[cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
164            let sa = &unsafe { *(sockaddr as *const posix_sockaddr_in6) };
165            // Ignore all fe80:: addresses as these are link locals
166            /*
167            if sa.sin6_addr.s6_addr[0] != 0xfe || sa.sin6_addr.s6_addr[1] != 0x80 {
168                return None;
169            }
170            */
171            Some((IpAddr::V6(Ipv6Addr::from(sa.sin6_addr.s6_addr)), sa.sin6_scope_id))
172        } else {
173            None
174        }
175    }
176
177    #[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))]
178    fn do_broadcast(ifaddr: &posix_ifaddrs) -> Option<(IpAddr, u32)> {
179        sockaddr_to_ipaddr(ifaddr.ifa_ifu)
180    }
181
182    #[cfg(any(
183        target_os = "freebsd",
184        target_os = "ios",
185        target_os = "macos",
186        target_os = "openbsd"
187    ))]
188    fn do_broadcast(ifaddr: &posix_ifaddrs) -> Option<(IpAddr, u32)> {
189        sockaddr_to_ipaddr(ifaddr.ifa_addr)
190    }
191
192    /// Return a vector of IP details for all the valid interfaces on this host
193    #[allow(unsafe_code)]
194    #[allow(trivial_casts)]
195    pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
196        let mut ret = Vec::<Interface>::new();
197        let mut ifaddrs: *mut posix_ifaddrs;
198        unsafe {
199            ifaddrs = mem::MaybeUninit::zeroed().assume_init();
200            if -1 == posix_getifaddrs(&mut ifaddrs) {
201                return Err(io::Error::last_os_error());
202            }
203        }
204
205        for ifaddr in unsafe { CLinkedListMut::from_ptr(ifaddrs, |a| a.ifa_next) }.iter() {
206            if ifaddr.ifa_addr.is_null() {
207                continue;
208            }
209
210            let name = unsafe { CStr::from_ptr(ifaddr.ifa_name as *const _) }
211                .to_string_lossy()
212                .into_owned();
213
214            // 过滤掉状态不为up和一些虚拟网卡
215            if ifaddr.ifa_flags & (IFFFlags::IFF_UP as u32) == 0 {
216                info!("will ignore iface {}", name);
217                continue;
218            }
219
220            if (ifaddr.ifa_flags & (IFFFlags::IFF_LOOPBACK as u32) != 0)
221                || (ifaddr.ifa_flags & (IFFFlags::IFF_POINTOPOINT as u32) != 0)
222            {
223                info!("will ignore iface {}", name);
224                continue;
225            }
226
227
228            let (addr, scope_id) = match sockaddr_to_ipaddr(ifaddr.ifa_addr) {
229                None => continue,
230                Some((IpAddr::V4(ipv4_addr), _)) => {
231                    if ipv4_addr.is_loopback()
232                        || ipv4_addr.is_unspecified()
233                        || ipv4_addr.is_link_local()
234                    {
235                        continue;
236                    }
237
238                    let netmask = match sockaddr_to_ipaddr(ifaddr.ifa_netmask) {
239                        Some((IpAddr::V4(netmask), _)) => netmask,
240                        _ => Ipv4Addr::new(0, 0, 0, 0),
241                    };
242
243                    let broadcast = if (ifaddr.ifa_flags & IFFFlags::IFF_BROADCAST as u32) != 0 {
244                        match do_broadcast(ifaddr) {
245                            Some((IpAddr::V4(broadcast), _)) => Some(broadcast),
246                            _ => None,
247                        }
248                    } else {
249                        None
250                    };
251
252                    (IfAddr::V4(Ifv4Addr {
253                        ip: ipv4_addr,
254                        netmask,
255                        broadcast,
256                    }), 0)
257                }
258                Some((IpAddr::V6(ipv6_addr), scope_id)) => {
259                    if ipv6_addr.is_loopback() || ipv6_addr.is_unspecified() {
260                        continue;
261                    }
262
263                    let netmask = match sockaddr_to_ipaddr(ifaddr.ifa_netmask) {
264                        Some((IpAddr::V6(netmask), _)) => netmask,
265                        _ => Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0),
266                    };
267
268                    // if (ifaddr.ifa_flags & 0x01)==0x01 {
269                    //     continue;
270                    // }
271
272                    let broadcast = if (ifaddr.ifa_flags & IFFFlags::IFF_BROADCAST as u32) != 0 {
273                        match do_broadcast(ifaddr) {
274                            Some((IpAddr::V6(broadcast), _)) => Some(broadcast),
275                            _ => None,
276                        }
277                    } else {
278                        None
279                    };
280
281                    (IfAddr::V6(Ifv6Addr {
282                        ip: ipv6_addr,
283                        netmask,
284                        broadcast,
285                    }), scope_id)
286                }
287            };
288
289            ret.push(Interface {
290                name,
291                addr,
292                description: String::from(""),
293                ifa_flags: ifaddr.ifa_flags as u32,
294                scope_id
295            });
296        }
297        unsafe {
298            posix_freeifaddrs(ifaddrs);
299        }
300        Ok(ret)
301    }
302}
303
304/// Get a list of all the network interfaces on this machine along with their IP info.
305#[cfg(not(windows))]
306pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
307    getifaddrs_posix::get_if_addrs()
308}
309
310#[cfg(not(windows))]
311pub use getifaddrs_posix::IFFFlags;
312
313#[cfg(windows)]
314mod getifaddrs_windows {
315    use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
316    use c_linked_list::CLinkedListConst;
317    use libc;
318    use libc::{c_char, c_int, c_ulong, size_t};
319    use std::ffi::{c_void, CStr, OsString};
320    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
321    use std::os::windows::prelude::*;
322    use std::{io, ptr};
323    use winapi::shared::minwindef::DWORD;
324    use winapi::shared::winerror::ERROR_SUCCESS;
325    use winapi::shared::ws2def::SOCKADDR as sockaddr;
326    use winapi::shared::ws2def::SOCKADDR_IN as sockaddr_in;
327    use winapi::shared::ws2def::{AF_INET, AF_INET6};
328    use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6;
329
330    #[repr(C)]
331    struct SocketAddress {
332        pub lp_socket_address: *const sockaddr,
333        pub i_socket_address_length: c_int,
334    }
335    #[repr(C)]
336    struct IpAdapterUnicastAddress {
337        pub length: c_ulong,
338        pub flags: DWORD,
339        pub next: *const IpAdapterUnicastAddress,
340        // Loads more follows, but I'm not bothering to map these for now
341        pub address: SocketAddress,
342        pub prefix_origin: c_ulong,
343        pub suffix_origin: c_ulong,
344    }
345    #[repr(C)]
346    struct IpAdapterPrefix {
347        pub length: c_ulong,
348        pub flags: DWORD,
349        pub next: *const IpAdapterPrefix,
350        pub address: SocketAddress,
351        pub prefix_length: c_ulong,
352    }
353    #[repr(C)]
354    struct IpAdapterAddresses {
355        pub length: c_ulong,
356        pub if_index: DWORD,
357        pub next: *const IpAdapterAddresses,
358        pub adapter_name: *const c_char,
359        pub first_unicast_address: *const IpAdapterUnicastAddress,
360        first_anycast_address: *const c_void,
361        first_multicast_address: *const c_void,
362        first_dns_server_address: *const c_void,
363        dns_suffix: *const c_void,
364        description: *const c_void,
365        friendly_name: *const c_void,
366        physical_address: [c_char; 8],
367        physical_address_length: DWORD,
368        flags: DWORD,
369        mtu: DWORD,
370        if_type: DWORD,
371        oper_status: c_int,
372        ipv6_if_index: DWORD,
373        zone_indices: [DWORD; 16],
374        // Loads more follows, but I'm not bothering to map these for now
375        pub first_prefix: *const IpAdapterPrefix,
376    }
377    #[link(name = "Iphlpapi")]
378    extern "system" {
379        /// get adapter's addresses
380        fn GetAdaptersAddresses(
381            family: c_ulong,
382            flags: c_ulong,
383            reserved: *const c_void,
384            addresses: *const IpAdapterAddresses,
385            size: *mut c_ulong,
386        ) -> c_ulong;
387    }
388
389    #[allow(unsafe_code)]
390    fn sockaddr_to_ipaddr(sockaddr: *const sockaddr) -> Option<(IpAddr, u32)> {
391        if sockaddr.is_null() {
392            return None;
393        }
394        if unsafe { *sockaddr }.sa_family as u32 == AF_INET as u32 {
395            let sa = &unsafe { *(sockaddr as *const sockaddr_in) };
396            // Ignore all 169.254.x.x addresses as these are not active interfaces
397
398            if unsafe { sa.sin_addr.S_un.S_un_w().s_w1 } == 0xa9fe {
399                return None;
400            }
401
402            Some((IpAddr::V4(Ipv4Addr::new(
403                unsafe { sa.sin_addr.S_un.S_un_b().s_b1 },
404                unsafe { sa.sin_addr.S_un.S_un_b().s_b2 },
405                unsafe { sa.sin_addr.S_un.S_un_b().s_b3 },
406                unsafe { sa.sin_addr.S_un.S_un_b().s_b4 },
407            )), 0))
408        } else if unsafe { *sockaddr }.sa_family as u32 == AF_INET6 as u32 {
409            let sa = &unsafe { *(sockaddr as *const sockaddr_in6) };
410            // Ignore all fe80:: addresses as these are link locals
411            /*
412            unsafe {
413                if sa.sin6_addr.u.Word()[0] != 0x80fe {
414                    return None;
415                }
416            }
417             */
418
419            let mut v6byte = [0_u8; 16];
420            v6byte.copy_from_slice(unsafe { sa.sin6_addr.u.Byte() });
421            Some((IpAddr::V6(Ipv6Addr::from(v6byte)), *unsafe { sa.u.sin6_scope_id() }))
422        } else {
423            None
424        }
425    }
426
427    unsafe fn u16_ptr_to_string(ptr: *const u16) -> OsString {
428        let len = (0..).take_while(|&i| *ptr.offset(i) != 0).count();
429        let slice = std::slice::from_raw_parts(ptr, len);
430
431        OsString::from_wide(slice)
432    }
433
434    // trivial_numeric_casts lint may become allow by default.
435    // Refer: https://github.com/rust-lang/rfcs/issues/1020
436    /// Return a vector of IP details for all the valid interfaces on this host
437    #[allow(unsafe_code, trivial_numeric_casts)]
438    pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
439        let mut ret = Vec::<Interface>::new();
440        let mut ifaddrs: *const IpAdapterAddresses;
441        let mut buffersize: c_ulong = 15000;
442        loop {
443            unsafe {
444                ifaddrs = libc::malloc(buffersize as size_t) as *mut IpAdapterAddresses;
445                if ifaddrs.is_null() {
446                    panic!("Failed to allocate buffer in get_if_addrs()");
447                }
448                let retcode = GetAdaptersAddresses(
449                    0,
450                    // GAA_FLAG_SKIP_ANYCAST       |
451                    // GAA_FLAG_SKIP_MULTICAST     |
452                    // GAA_FLAG_SKIP_DNS_SERVER    |
453                    // GAA_FLAG_INCLUDE_PREFIX     |
454                    // GAA_FLAG_SKIP_FRIENDLY_NAME
455                    0x3e,
456                    ptr::null(),
457                    ifaddrs,
458                    &mut buffersize,
459                );
460                match retcode {
461                    ERROR_SUCCESS => break,
462                    111 => {
463                        libc::free(ifaddrs as *mut c_void);
464                        buffersize *= 2;
465                        continue;
466                    }
467                    _ => return Err(io::Error::last_os_error()),
468                }
469            }
470        }
471
472        for ifaddr in unsafe { CLinkedListConst::from_ptr(ifaddrs, |a| a.next) }.iter() {
473            if ifaddr.oper_status != 1 {
474                continue;
475            }
476            if ifaddr.if_type == 24 || ifaddr.if_type == 131 {
477                continue;
478            }
479            for addr in
480                unsafe { CLinkedListConst::from_ptr(ifaddr.first_unicast_address, |a| a.next) }
481                    .iter()
482            {
483                let name = unsafe { CStr::from_ptr(ifaddr.adapter_name) }
484                    .to_string_lossy()
485                    .into_owned();
486
487                let description = unsafe { u16_ptr_to_string(ifaddr.description as *const u16) }
488                    .to_string_lossy()
489                    .into_owned();
490
491                let (addr, scope_id) = match sockaddr_to_ipaddr(addr.address.lp_socket_address) {
492                    None => continue,
493                    Some((IpAddr::V4(ipv4_addr), _)) => {
494
495                        if ipv4_addr.is_loopback()
496                            || ipv4_addr.is_link_local()
497                            || ipv4_addr.is_broadcast()
498                            || ipv4_addr.is_documentation()
499                            || ipv4_addr.is_unspecified()
500                            // || ipv4_addr.is_reserved()
501                        {
502                            info!("will ignore ip addr: desc={}, addr={}", description, ipv4_addr);
503                            continue;
504                        }
505
506                        let mut item_netmask = Ipv4Addr::new(0, 0, 0, 0);
507                        let mut item_broadcast = None;
508                        // Search prefixes for a prefix matching addr
509                        'prefixloopv4: for prefix in
510                            unsafe { CLinkedListConst::from_ptr(ifaddr.first_prefix, |p| p.next) }
511                                .iter()
512                        {
513                            let ipprefix = sockaddr_to_ipaddr(prefix.address.lp_socket_address);
514                            match ipprefix {
515                                Some((IpAddr::V4(ref a), _)) => {
516                                    let mut netmask: [u8; 4] = [0; 4];
517                                    for (n, netmask_elt) in netmask
518                                        .iter_mut()
519                                        .enumerate()
520                                        .take((prefix.prefix_length as usize + 7) / 8)
521                                    {
522                                        let x_byte = ipv4_addr.octets()[n];
523                                        let y_byte = a.octets()[n];
524                                        // Clippy 0.0.128 doesn't handle the label on the `continue`
525                                        #[cfg_attr(
526                                            feature = "cargo-clippy",
527                                            allow(needless_continue)
528                                        )]
529                                        for m in 0..8 {
530                                            if (n * 8) + m > prefix.prefix_length as usize {
531                                                break;
532                                            }
533                                            let bit = 1_u8 << m as u8;
534                                            if (x_byte & bit) == (y_byte & bit) {
535                                                *netmask_elt |= bit;
536                                            } else {
537                                                continue 'prefixloopv4;
538                                            }
539                                        }
540                                    }
541                                    item_netmask = Ipv4Addr::new(
542                                        netmask[0], netmask[1], netmask[2], netmask[3],
543                                    );
544                                    let mut broadcast: [u8; 4] = ipv4_addr.octets();
545                                    for n in 0..4 {
546                                        broadcast[n] |= !netmask[n];
547                                    }
548                                    item_broadcast = Some(Ipv4Addr::new(
549                                        broadcast[0],
550                                        broadcast[1],
551                                        broadcast[2],
552                                        broadcast[3],
553                                    ));
554                                    break 'prefixloopv4;
555                                }
556                                _ => continue,
557                            };
558                        }
559                        (IfAddr::V4(Ifv4Addr {
560                            ip: ipv4_addr,
561                            netmask: item_netmask,
562                            broadcast: item_broadcast,
563                        }), 0)
564                    }
565                    Some((IpAddr::V6(ipv6_addr), scope_id)) => {
566                        let mut item_netmask = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
567                        // Search prefixes for a prefix matching addr
568                        'prefixloopv6: for prefix in
569                            unsafe { CLinkedListConst::from_ptr(ifaddr.first_prefix, |p| p.next) }
570                                .iter()
571                        {
572                            let ipprefix = sockaddr_to_ipaddr(prefix.address.lp_socket_address);
573                            match ipprefix {
574                                Some((IpAddr::V6(ref a), _)) => {
575                                    // Iterate the bits in the prefix, if they all match this prefix
576                                    // is the right one, else try the next prefix
577                                    let mut netmask: [u16; 8] = [0; 8];
578                                    for (n, netmask_elt) in netmask
579                                        .iter_mut()
580                                        .enumerate()
581                                        .take((prefix.prefix_length as usize + 15) / 16)
582                                    {
583                                        let x_word = ipv6_addr.segments()[n];
584                                        let y_word = a.segments()[n];
585                                        // Clippy 0.0.128 doesn't handle the label on the `continue`
586                                        #[cfg_attr(
587                                            feature = "cargo-clippy",
588                                            allow(needless_continue)
589                                        )]
590                                        for m in 0..16 {
591                                            if (n * 16) + m > prefix.prefix_length as usize {
592                                                break;
593                                            }
594                                            let bit = 1_u16 << m as u16;
595                                            if (x_word & bit) == (y_word & bit) {
596                                                *netmask_elt |= bit;
597                                            } else {
598                                                continue 'prefixloopv6;
599                                            }
600                                        }
601                                    }
602                                    item_netmask = Ipv6Addr::new(
603                                        netmask[0], netmask[1], netmask[2], netmask[3], netmask[4],
604                                        netmask[5], netmask[6], netmask[7],
605                                    );
606                                    break 'prefixloopv6;
607                                }
608                                _ => continue,
609                            };
610                        }
611                        (IfAddr::V6(Ifv6Addr {
612                            ip: ipv6_addr,
613                            netmask: item_netmask,
614                            broadcast: None,
615                        }), scope_id)
616                    }
617                };
618                ret.push(Interface {
619                    name,
620                    addr,
621                    description,
622                    ifa_flags: ifaddr.flags as u32,
623                    scope_id
624                });
625            }
626        }
627        unsafe {
628            libc::free(ifaddrs as *mut c_void);
629        }
630        Ok(ret)
631    }
632}
633
634#[cfg(windows)]
635/// Get address
636pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
637    getifaddrs_windows::get_if_addrs()
638}
639
640#[test]
641fn test() {
642    let interfaces = get_if_addrs().unwrap();
643    for interface in interfaces {
644        let addr_str = match interface.addr {
645            IfAddr::V4(ip) => {ip.ip.to_string()}
646            IfAddr::V6(ip) => {ip.ip.to_string()}
647        };
648        println!("{}: {}", addr_str, interface.scope_id);
649    }
650}