1#[cfg(not(windows))]
11mod posix;
12mod sockaddr;
13#[cfg(windows)]
14mod windows;
15
16use std::io;
17use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
18
19#[derive(Debug, PartialEq, Eq, Hash, Clone)]
21pub struct Interface {
22 pub name: String,
24 pub addr: IfAddr,
26 pub index: Option<u32>,
28}
29
30impl Interface {
31 pub fn is_loopback(&self) -> bool {
33 self.addr.is_loopback()
34 }
35
36 pub fn is_link_local(&self) -> bool {
38 self.addr.is_link_local()
39 }
40
41 pub fn ip(&self) -> IpAddr {
43 self.addr.ip()
44 }
45}
46
47#[derive(Debug, PartialEq, Eq, Hash, Clone)]
49pub enum IfAddr {
50 V4(Ifv4Addr),
52 V6(Ifv6Addr),
54}
55
56impl IfAddr {
57 pub fn is_loopback(&self) -> bool {
59 match *self {
60 IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_loopback(),
61 IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_loopback(),
62 }
63 }
64
65 pub fn is_link_local(&self) -> bool {
67 match *self {
68 IfAddr::V4(ref ifv4_addr) => ifv4_addr.is_link_local(),
69 IfAddr::V6(ref ifv6_addr) => ifv6_addr.is_link_local(),
70 }
71 }
72
73 pub fn ip(&self) -> IpAddr {
75 match *self {
76 IfAddr::V4(ref ifv4_addr) => IpAddr::V4(ifv4_addr.ip),
77 IfAddr::V6(ref ifv6_addr) => IpAddr::V6(ifv6_addr.ip),
78 }
79 }
80}
81
82#[derive(Debug, PartialEq, Eq, Hash, Clone)]
84pub struct Ifv4Addr {
85 pub ip: Ipv4Addr,
87 pub netmask: Ipv4Addr,
89 pub broadcast: Option<Ipv4Addr>,
91}
92
93impl Ifv4Addr {
94 pub fn is_loopback(&self) -> bool {
96 self.ip.octets()[0] == 127
97 }
98
99 pub fn is_link_local(&self) -> bool {
101 self.ip.is_link_local()
102 }
103}
104
105#[derive(Debug, PartialEq, Eq, Hash, Clone)]
107pub struct Ifv6Addr {
108 pub ip: Ipv6Addr,
110 pub netmask: Ipv6Addr,
112 pub broadcast: Option<Ipv6Addr>,
114}
115
116impl Ifv6Addr {
117 pub fn is_loopback(&self) -> bool {
119 self.ip.segments() == [0, 0, 0, 0, 0, 0, 0, 1]
120 }
121
122 pub fn is_link_local(&self) -> bool {
124 let bytes = self.ip.octets();
125
126 bytes[0] == 0xfe && bytes[1] == 0x80
127 }
128}
129
130#[cfg(not(windows))]
131mod getifaddrs_posix {
132 use libc::if_nametoindex;
133
134 use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
135 use crate::posix::{self as ifaddrs, IfAddrs};
136 use crate::sockaddr;
137 use std::ffi::CStr;
138 use std::io;
139 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
140
141 #[allow(unsafe_code)]
143 pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
144 let mut ret = Vec::<Interface>::new();
145 let ifaddrs = IfAddrs::new()?;
146
147 for ifaddr in ifaddrs.iter() {
148 let addr = match sockaddr::to_ipaddr(ifaddr.ifa_addr) {
149 None => continue,
150 Some(IpAddr::V4(ipv4_addr)) => {
151 let netmask = match sockaddr::to_ipaddr(ifaddr.ifa_netmask) {
152 Some(IpAddr::V4(netmask)) => netmask,
153 _ => Ipv4Addr::new(0, 0, 0, 0),
154 };
155 let broadcast = if (ifaddr.ifa_flags & 2) != 0 {
156 match ifaddrs::do_broadcast(&ifaddr) {
157 Some(IpAddr::V4(broadcast)) => Some(broadcast),
158 _ => None,
159 }
160 } else {
161 None
162 };
163
164 IfAddr::V4(Ifv4Addr {
165 ip: ipv4_addr,
166 netmask,
167 broadcast,
168 })
169 }
170 Some(IpAddr::V6(ipv6_addr)) => {
171 let netmask = match sockaddr::to_ipaddr(ifaddr.ifa_netmask) {
172 Some(IpAddr::V6(netmask)) => netmask,
173 _ => Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0),
174 };
175 let broadcast = if (ifaddr.ifa_flags & 2) != 0 {
176 match ifaddrs::do_broadcast(&ifaddr) {
177 Some(IpAddr::V6(broadcast)) => Some(broadcast),
178 _ => None,
179 }
180 } else {
181 None
182 };
183
184 IfAddr::V6(Ifv6Addr {
185 ip: ipv6_addr,
186 netmask,
187 broadcast,
188 })
189 }
190 };
191
192 let name = unsafe { CStr::from_ptr(ifaddr.ifa_name) }
193 .to_string_lossy()
194 .into_owned();
195 let index = {
196 let index = unsafe { if_nametoindex(ifaddr.ifa_name) };
197
198 if index == 0 {
202 None
203 } else {
204 Some(index)
205 }
206 };
207 ret.push(Interface { name, addr, index });
208 }
209
210 Ok(ret)
211 }
212}
213
214#[cfg(not(windows))]
216pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
217 getifaddrs_posix::get_if_addrs()
218}
219
220#[cfg(windows)]
221mod getifaddrs_windows {
222 use super::{IfAddr, Ifv4Addr, Ifv6Addr, Interface};
223 use crate::sockaddr;
224 use crate::windows::IfAddrs;
225 use std::io;
226 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
227 use windows_sys::Win32::Networking::WinSock::IpDadStatePreferred;
228
229 pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
231 let mut ret = Vec::<Interface>::new();
232 let ifaddrs = IfAddrs::new()?;
233
234 for ifaddr in ifaddrs.iter() {
235 for addr in ifaddr.unicast_addresses() {
236 if addr.DadState != IpDadStatePreferred {
237 continue;
238 }
239 let addr = match sockaddr::to_ipaddr(addr.Address.lpSockaddr) {
240 None => continue,
241 Some(IpAddr::V4(ipv4_addr)) => {
242 let mut item_netmask = Ipv4Addr::new(0, 0, 0, 0);
243 let mut item_broadcast = None;
244
245 'prefixloopv4: for prefix in ifaddr.prefixes() {
247 let ipprefix = sockaddr::to_ipaddr(prefix.Address.lpSockaddr);
248 match ipprefix {
249 Some(IpAddr::V4(ref a)) => {
250 let mut netmask: [u8; 4] = [0; 4];
251 for (n, netmask_elt) in netmask
252 .iter_mut()
253 .enumerate()
254 .take((prefix.PrefixLength as usize + 7) / 8)
255 {
256 let x_byte = ipv4_addr.octets()[n];
257 let y_byte = a.octets()[n];
258 for m in 0..8 {
259 if (n * 8) + m > prefix.PrefixLength as usize {
260 break;
261 }
262 let bit = 1 << (7 - m);
263 if (x_byte & bit) == (y_byte & bit) {
264 *netmask_elt |= bit;
265 } else {
266 continue 'prefixloopv4;
267 }
268 }
269 }
270 item_netmask = Ipv4Addr::new(
271 netmask[0], netmask[1], netmask[2], netmask[3],
272 );
273 let mut broadcast: [u8; 4] = ipv4_addr.octets();
274 for n in 0..4 {
275 broadcast[n] |= !netmask[n];
276 }
277 item_broadcast = Some(Ipv4Addr::new(
278 broadcast[0],
279 broadcast[1],
280 broadcast[2],
281 broadcast[3],
282 ));
283 break 'prefixloopv4;
284 }
285 _ => continue,
286 };
287 }
288 IfAddr::V4(Ifv4Addr {
289 ip: ipv4_addr,
290 netmask: item_netmask,
291 broadcast: item_broadcast,
292 })
293 }
294 Some(IpAddr::V6(ipv6_addr)) => {
295 let mut item_netmask = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
296 'prefixloopv6: for prefix in ifaddr.prefixes() {
298 let ipprefix = sockaddr::to_ipaddr(prefix.Address.lpSockaddr);
299 match ipprefix {
300 Some(IpAddr::V6(ref a)) => {
301 let mut netmask: [u16; 8] = [0; 8];
304 for (n, netmask_elt) in netmask
305 .iter_mut()
306 .enumerate()
307 .take((prefix.PrefixLength as usize + 15) / 16)
308 {
309 let x_word = ipv6_addr.segments()[n];
310 let y_word = a.segments()[n];
311 for m in 0..16 {
312 if (n * 16) + m > prefix.PrefixLength as usize {
313 break;
314 }
315 let bit = 1 << (15 - m);
316 if (x_word & bit) == (y_word & bit) {
317 *netmask_elt |= bit;
318 } else {
319 continue 'prefixloopv6;
320 }
321 }
322 }
323 item_netmask = Ipv6Addr::new(
324 netmask[0], netmask[1], netmask[2], netmask[3], netmask[4],
325 netmask[5], netmask[6], netmask[7],
326 );
327 break 'prefixloopv6;
328 }
329 _ => continue,
330 };
331 }
332 IfAddr::V6(Ifv6Addr {
333 ip: ipv6_addr,
334 netmask: item_netmask,
335 broadcast: None,
336 })
337 }
338 };
339
340 let index = match addr {
341 IfAddr::V4(_) => ifaddr.ipv4_index(),
342 IfAddr::V6(_) => ifaddr.ipv6_index(),
343 };
344 ret.push(Interface {
345 name: ifaddr.name(),
346 addr,
347 index,
348 });
349 }
350 }
351
352 Ok(ret)
353 }
354}
355
356#[cfg(windows)]
357pub fn get_if_addrs() -> io::Result<Vec<Interface>> {
359 getifaddrs_windows::get_if_addrs()
360}
361
362#[cfg(test)]
363mod tests {
364 use super::{get_if_addrs, Interface};
365 use std::io::Read;
366 use std::net::{IpAddr, Ipv4Addr};
367 use std::process::{Command, Stdio};
368 use std::str::FromStr;
369 use std::thread;
370 use std::time::Duration;
371
372 fn list_system_interfaces(cmd: &str, arg: &str) -> String {
373 let start_cmd = if arg == "" {
374 Command::new(cmd).stdout(Stdio::piped()).spawn()
375 } else {
376 Command::new(cmd).arg(arg).stdout(Stdio::piped()).spawn()
377 };
378 let mut process = match start_cmd {
379 Err(why) => {
380 println!("couldn't start cmd {} : {}", cmd, why.to_string());
381 return "".to_string();
382 }
383 Ok(process) => process,
384 };
385 thread::sleep(Duration::from_millis(1000));
386 let _ = process.kill();
387 let result: Vec<u8> = process
388 .stdout
389 .unwrap()
390 .bytes()
391 .map(|x| x.unwrap())
392 .collect();
393 String::from_utf8(result).unwrap()
394 }
395
396 #[cfg(windows)]
397 fn list_system_addrs() -> Vec<IpAddr> {
398 use std::net::Ipv6Addr;
399 list_system_interfaces("ipconfig", "")
400 .lines()
401 .filter_map(|line| {
402 println!("{}", line);
403 if line.contains("Address") && !line.contains("Link-local") {
404 let addr_s: Vec<&str> = line.split(" : ").collect();
405 if line.contains("IPv6") {
406 return Some(IpAddr::V6(Ipv6Addr::from_str(addr_s[1]).unwrap()));
407 } else if line.contains("IPv4") {
408 return Some(IpAddr::V4(Ipv4Addr::from_str(addr_s[1]).unwrap()));
409 }
410 }
411 None
412 })
413 .collect()
414 }
415
416 #[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))]
417 fn list_system_addrs() -> Vec<IpAddr> {
418 list_system_interfaces("ip", "addr")
419 .lines()
420 .filter_map(|line| {
421 println!("{}", line);
422 if line.contains("inet ") {
423 let addr_s: Vec<&str> = line.split_whitespace().collect();
424 let addr: Vec<&str> = addr_s[1].split('/').collect();
425 return Some(IpAddr::V4(Ipv4Addr::from_str(addr[0]).unwrap()));
426 }
427 None
428 })
429 .collect()
430 }
431
432 #[cfg(any(
433 target_os = "freebsd",
434 target_os = "macos",
435 target_os = "ios",
436 target_os = "tvos"
437 ))]
438 fn list_system_addrs() -> Vec<IpAddr> {
439 list_system_interfaces("ifconfig", "")
440 .lines()
441 .filter_map(|line| {
442 println!("{}", line);
443 if line.contains("inet ") {
444 let addr_s: Vec<&str> = line.split_whitespace().collect();
445 return Some(IpAddr::V4(Ipv4Addr::from_str(addr_s[1]).unwrap()));
446 }
447 None
448 })
449 .collect()
450 }
451
452 #[test]
453 fn test_get_if_addrs() {
454 let ifaces = get_if_addrs().unwrap();
455 println!("Local interfaces:");
456 println!("{:#?}", ifaces);
457 assert!(
459 1 <= ifaces
460 .iter()
461 .filter(|interface| interface.is_loopback())
462 .count()
463 );
464 for interface in &ifaces {
466 if let Some(idx) = interface.index {
467 assert!(idx > 0);
468 }
469 }
470
471 let is_loopback =
473 |interface: &&Interface| interface.addr.ip() == IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
474 assert_eq!(1, ifaces.iter().filter(is_loopback).count());
475
476 let system_addrs = list_system_addrs();
478 assert!(!system_addrs.is_empty());
479 for addr in system_addrs {
480 let mut listed = false;
481 println!("\n checking whether {:?} has been properly listed \n", addr);
482 for interface in &ifaces {
483 if interface.addr.ip() == addr {
484 listed = true;
485 }
486
487 assert!(interface.index.is_some());
488 }
489 assert!(listed);
490 }
491 }
492}