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 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 {
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 '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 #[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 '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 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 #[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)]
635pub 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}