]> code.octet-stream.net Git - netwatcher/blob - src/list_unix.rs
45ffc6361769578945558a28b6a736463a2b77ce
[netwatcher] / src / list_unix.rs
1 use std::fmt::Write;
2 use std::{collections::HashMap, net::IpAddr};
3
4 use nix::{ifaddrs::getifaddrs, net::if_::if_nametoindex};
5
6 use crate::{Error, Interface, List};
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<List, 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(List(ifs))
61 }
62
63 fn format_mac(bytes: &[u8]) -> Result<String, Error> {
64 let mut mac = String::with_capacity(bytes.len() * 3);
65 for i in 0..bytes.len() {
66 if i != 0 {
67 write!(mac, ":").map_err(|_| Error::Internal)?;
68 }
69 write!(mac, "{:02X}", bytes[i]).map_err(|_| Error::Internal)?;
70 }
71 Ok(mac)
72 }