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