]> code.octet-stream.net Git - netwatcher/blob - src/imp_mac.rs
List interfaces on Mac
[netwatcher] / src / imp_mac.rs
1 use std::{collections::HashMap, net::IpAddr};
2
3 use nix::{ifaddrs::getifaddrs, net::if_::if_nametoindex};
4
5 use crate::util::format_mac;
6 use crate::{Error, IfIndex, Interface};
7
8 struct CandidateInterface {
9 name: String,
10 index: u32,
11 hw_addr: Option<String>,
12 ips: Vec<IpAddr>,
13 }
14
15 pub(crate) fn list_interfaces() -> Result<HashMap<IfIndex, Interface>, Error> {
16 let addrs = getifaddrs().map_err(|_| Error::Internal)?;
17 let mut candidates = HashMap::new();
18
19 for addr in addrs {
20 let index = if_nametoindex(addr.interface_name.as_str()).map_err(|_| Error::Internal)?;
21 let candidate = candidates
22 .entry(addr.interface_name.clone())
23 .or_insert_with(|| CandidateInterface {
24 name: addr.interface_name.clone(),
25 index,
26 hw_addr: None,
27 ips: vec![],
28 });
29 if let Some(a) = addr.address {
30 if let Some(a) = a.as_link_addr() {
31 if let Some(raw_addr) = a.addr() {
32 candidate.hw_addr = Some(format_mac(&raw_addr)?);
33 }
34 }
35 if let Some(a) = a.as_sockaddr_in() {
36 candidate.ips.push(IpAddr::V4(a.ip()));
37 }
38 if let Some(a) = a.as_sockaddr_in6() {
39 candidate.ips.push(IpAddr::V6(a.ip()));
40 }
41 }
42 }
43
44 let ifs = candidates
45 .drain()
46 .flat_map(|(_, c)| {
47 c.hw_addr.map(|hw_addr| {
48 (
49 c.index,
50 Interface {
51 index: c.index,
52 hw_addr,
53 name: c.name,
54 ips: c.ips,
55 },
56 )
57 })
58 })
59 .collect();
60 Ok(ifs)
61 }
62
63 #[cfg(test)]
64 mod test {
65 use super::list_interfaces;
66
67 #[test]
68 fn list() {
69 let ifaces = list_interfaces().unwrap();
70 println!("{:?}", ifaces);
71 }
72 }