1#[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#[derive(Debug, PartialEq, Eq, Hash, Clone)]
18pub struct Interface {
19 pub name: String,
21 pub addr: IfAddr,
23
24 pub description: String,
25
26 pub ifa_flags: u32,
27
28 pub scope_id: u32,
29}
30
31#[derive(Debug, PartialEq, Eq, Hash, Clone)]
33pub enum IfAddr {
34 V4(Ifv4Addr),
36 V6(Ifv6Addr),
38}
39
40#[derive(Debug, PartialEq, Eq, Hash, Clone)]
42pub struct Ifv4Addr {
43 pub ip: Ipv4Addr,
45 pub netmask: Ipv4Addr,
47 pub broadcast: Option<Ipv4Addr>,
49}
50
51#[derive(Debug, PartialEq, Eq, Hash, Clone)]
53pub struct Ifv6Addr {
54 pub ip: Ipv6Addr,
56 pub netmask: Ipv6Addr,
58 pub broadcast: Option<Ipv6Addr>,
60}
61
62impl Interface {
63 pub fn is_loopback(&self) -> bool {
65 self.addr.is_loopback()
66 }
67
68 pub fn ip(&self) -> IpAddr {
70 self.addr.ip()
71 }
72}
73
74impl IfAddr {
75 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 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 pub fn is_loopback(&self) -> bool {
95 self.ip.octets()[0] == 127
96 }
97}
98
99impl Ifv6Addr {
100 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, IFF_BROADCAST = 1 << 1, IFF_DEBUG = 1 << 2, IFF_LOOPBACK = 1 << 3, IFF_POINTOPOINT = 1 << 4, IFF_NOTRAILERS = 1 << 5, IFF_RUNNING = 1 << 6, IFF_NOARP = 1 << 7, IFF_PROMISC = 1 << 8, IFF_ALLMULTI = 1 << 9, IFF_MASTER = 1 << 10, IFF_SLAVE = 1 << 11, IFF_MULTICAST = 1 << 12, IFF_PORTSEL = 1 << 13, IFF_AUTOMEDIA = 1 << 14, IFF_DYNAMIC = 1 << 15, IFF_LOWER_UP = 1 << 16, IFF_DORMANT = 1 << 17, IFF_ECHO = 1 << 18, }
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 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 #[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 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 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#[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 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 pub first_prefix: *const IpAdapterPrefix,
376 }
377 #[link(name = "Iphlpapi")]
378 extern "system" {
379 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 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 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 #[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 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 if name.starts_with("docker") {
489 info!("will ignore as Docker Virtual Ethernet Adapter: {}", name);
490 continue;
491 }
492
493 let description = unsafe { u16_ptr_to_string(ifaddr.description as *const u16) }
494 .to_string_lossy()
495 .into_owned();
496
497 if description.find("VMware").is_some() {
506 info!("will ignore as VMware addr: {}", description);
507 continue;
508 }
509
510 let (addr, scope_id) = match sockaddr_to_ipaddr(addr.address.lp_socket_address) {
511 None => continue,
512 Some((IpAddr::V4(ipv4_addr), _)) => {
513
514 if ipv4_addr.is_loopback()
515 || ipv4_addr.is_link_local()
516 || ipv4_addr.is_broadcast()
517 || ipv4_addr.is_documentation()
518 || ipv4_addr.is_unspecified()
519 {
521 info!("will ignore ip addr: desc={}, addr={}", description, ipv4_addr);
522 continue;
523 }
524
525 let mut item_netmask = Ipv4Addr::new(0, 0, 0, 0);
526 let mut item_broadcast = None;
527 'prefixloopv4: for prefix in
529 unsafe { CLinkedListConst::from_ptr(ifaddr.first_prefix, |p| p.next) }
530 .iter()
531 {
532 let ipprefix = sockaddr_to_ipaddr(prefix.address.lp_socket_address);
533 match ipprefix {
534 Some((IpAddr::V4(ref a), _)) => {
535 let mut netmask: [u8; 4] = [0; 4];
536 for (n, netmask_elt) in netmask
537 .iter_mut()
538 .enumerate()
539 .take((prefix.prefix_length as usize + 7) / 8)
540 {
541 let x_byte = ipv4_addr.octets()[n];
542 let y_byte = a.octets()[n];
543 #[cfg_attr(
545 feature = "cargo-clippy",
546 allow(needless_continue)
547 )]
548 for m in 0..8 {
549 if (n * 8) + m > prefix.prefix_length as usize {
550 break;
551 }
552 let bit = 1_u8 << m as u8;
553 if (x_byte & bit) == (y_byte & bit) {
554 *netmask_elt |= bit;
555 } else {
556 continue 'prefixloopv4;
557 }
558 }
559 }
560 item_netmask = Ipv4Addr::new(
561 netmask[0], netmask[1], netmask[2], netmask[3],
562 );
563 let mut broadcast: [u8; 4] = ipv4_addr.octets();
564 for n in 0..4 {
565 broadcast[n] |= !netmask[n];
566 }
567 item_broadcast = Some(Ipv4Addr::new(
568 broadcast[0],
569 broadcast[1],
570 broadcast[2],
571 broadcast[3],
572 ));
573 break 'prefixloopv4;
574 }
575 _ => continue,
576 };
577 }
578 (IfAddr::V4(Ifv4Addr {
579 ip: ipv4_addr,
580 netmask: item_netmask,
581 broadcast: item_broadcast,
582 }), 0)
583 }
584 Some((IpAddr::V6(ipv6_addr), scope_id)) => {
585 let mut item_netmask = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
586 'prefixloopv6: for prefix in
588 unsafe { CLinkedListConst::from_ptr(ifaddr.first_prefix, |p| p.next) }
589 .iter()
590 {
591 let ipprefix = sockaddr_to_ipaddr(prefix.address.lp_socket_address);
592 match ipprefix {
593 Some((IpAddr::V6(ref a), _)) => {
594 let mut netmask: [u16; 8] = [0; 8];
597 for (n, netmask_elt) in netmask
598 .iter_mut()
599 .enumerate()
600 .take((prefix.prefix_length as usize + 15) / 16)
601 {
602 let x_word = ipv6_addr.segments()[n];
603 let y_word = a.segments()[n];
604 #[cfg_attr(
606 feature = "cargo-clippy",
607 allow(needless_continue)
608 )]
609 for m in 0..16 {
610 if (n * 16) + m > prefix.prefix_length as usize {
611 break;
612 }
613 let bit = 1_u16 << m as u16;
614 if (x_word & bit) == (y_word & bit) {
615 *netmask_elt |= bit;
616 } else {
617 continue 'prefixloopv6;
618 }
619 }
620 }
621 item_netmask = Ipv6Addr::new(
622 netmask[0], netmask[1], netmask[2], netmask[3], netmask[4],
623 netmask[5], netmask[6], netmask[7],
624 );
625 break 'prefixloopv6;
626 }
627 _ => continue,
628 };
629 }
630 (IfAddr::V6(Ifv6Addr {
631 ip: ipv6_addr,
632 netmask: item_netmask,
633 broadcast: None,
634 }), scope_id)
635 }
636 };
637 ret.push(Interface {
638 name,
639 addr,
640 description,
641 ifa_flags: ifaddr.flags as u32,
642 scope_id
643 });
644 }
645 }
646 unsafe {
647 libc::free(ifaddrs as *mut c_void);
648 }
649 Ok(ret)
650 }
651}
652
653#[cfg(windows)]
654pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
656 getifaddrs_windows::get_if_addrs()
657}
658
659#[test]
660fn test() {
661 let interfaces = get_if_addrs().unwrap();
662 for interface in interfaces {
663 let addr_str = match interface.addr {
664 IfAddr::V4(ip) => {ip.ip.to_string()}
665 IfAddr::V6(ip) => {ip.ip.to_string()}
666 };
667 println!("{}: {}", addr_str, interface.scope_id);
668 }
669}