1use itertools::Itertools;
5use pnet::{
6 datalink::NetworkInterface as PNetNetworkInterface, ipnetwork::IpNetwork, util::MacAddr,
7};
8use std::{
9 error::Error,
10 io,
11 net::{Ipv4Addr, TcpListener},
12 str::FromStr,
13};
14
15pub struct NetworkInterface {
17 pub name: String,
19 pub description: String,
21 pub cidr: String,
23 pub ipv4: Ipv4Addr,
25 pub ips: Vec<IpNetwork>,
27 pub mac: MacAddr,
29 pub flags: u32,
31 pub index: u32,
33}
34
35impl TryFrom<PNetNetworkInterface> for NetworkInterface {
36 type Error = Box<dyn Error>;
37
38 fn try_from(value: PNetNetworkInterface) -> Result<Self, Self::Error> {
39 let mac = value.mac.ok_or("failed to get mac address for interface")?;
40 let (ip, cidr) =
41 get_interface_ipv4_and_cidr(&value).ok_or("failed to get ip and cidr for interface")?;
42 let ipv4 = Ipv4Addr::from_str(ip.as_str())?;
43
44 Ok(Self {
45 name: value.name,
46 description: value.description,
47 flags: value.flags,
48 index: value.index,
49 mac,
50 ips: value.ips,
51 cidr,
52 ipv4,
53 })
54 }
55}
56
57impl From<&NetworkInterface> for PNetNetworkInterface {
58 fn from(value: &NetworkInterface) -> Self {
59 Self {
60 name: value.name.clone(),
61 flags: value.flags,
62 description: value.description.clone(),
63 index: value.index,
64 ips: value.ips.clone(),
65 mac: Some(value.mac),
66 }
67 }
68}
69
70pub fn get_interface(name: &str) -> Option<NetworkInterface> {
72 let iface = pnet::datalink::interfaces()
73 .into_iter()
74 .find(|i| i.name == name)?;
75 NetworkInterface::try_from(iface).ok()
76}
77
78pub fn get_default_interface() -> Option<NetworkInterface> {
80 let iface = pnet::datalink::interfaces()
81 .into_iter()
82 .find(|e| e.is_up() && !e.is_loopback() && e.ips.iter().any(|i| i.is_ipv4()))?;
83 NetworkInterface::try_from(iface).ok()
84}
85
86pub fn get_available_port() -> Result<u16, io::Error> {
89 let listener = TcpListener::bind(("127.0.0.1", 0))?;
90 let addr = listener.local_addr()?;
91 Ok(addr.port())
92}
93
94fn get_interface_ipv4_and_cidr(interface: &PNetNetworkInterface) -> Option<(String, String)> {
95 let ipnet = interface.ips.iter().find(|i| i.is_ipv4())?;
96 let host_ip = ipnet.ip().to_string();
97 let first_ip = ipnet
98 .iter()
99 .find_or_first(|p| p.is_ipv4() && !p.to_string().ends_with(".0"));
100 let base = first_ip
101 .map(|i| i.to_string())
102 .unwrap_or_else(|| ipnet.network().to_string());
103 let prefix = ipnet.prefix().to_string();
104 let cidr = format!("{base}/{prefix}");
105 Some((host_ip, cidr))
106}
107
108#[cfg(test)]
109#[path = "./network_tests.rs"]
110mod tests;