use nix::sys::socket::{AddressFamily, SockaddrLike}; use std::collections::HashMap; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::str::FromStr; use sysinfo::Networks; pub fn extract_ipv4(ip: IpAddr) -> Ipv4Addr { match ip { IpAddr::V4(i) => i, IpAddr::V6(_) => { panic!("IPv6 found in IPv4 definition!") } } } pub fn extract_ipv6(ip: IpAddr) -> Ipv6Addr { match ip { IpAddr::V4(_) => { panic!("IPv4 found in IPv6 definition!") } IpAddr::V6(i) => i, } } pub fn is_ipv4_address_valid>(ip: D) -> bool { Ipv4Addr::from_str(ip.as_ref()).is_ok() } pub fn is_ipv6_address_valid>(ip: D) -> bool { Ipv6Addr::from_str(ip.as_ref()).is_ok() } pub fn is_ipv4_mask_valid(mask: u8) -> bool { mask <= 32 } pub fn is_ipv6_mask_valid(mask: u8) -> bool { mask <= 128 } pub fn is_mask_valid(ipv: usize, mask: u8) -> bool { match ipv { 4 => is_ipv4_mask_valid(mask), 6 => is_ipv6_mask_valid(mask), _ => panic!("Unsupported IP version"), } } pub fn is_mac_address_valid>(mac: D) -> bool { lazy_regex::regex!("^([a-fA-F0-9]{2}[:-]){5}[a-fA-F0-9]{2}$").is_match(mac.as_ref()) } pub fn is_net_interface_name_valid>(int: D) -> bool { lazy_regex::regex!("^[a-zA-Z0-9]+$").is_match(int.as_ref()) } /// Get the list of available network interfaces pub fn net_list() -> Vec { let mut networks = Networks::new(); networks.refresh_list(); networks .list() .iter() .map(|n| n.0.to_string()) .collect::>() } /// Get the list of available network interfaces associated with their IP address pub fn net_list_and_ips() -> anyhow::Result>> { let addrs = nix::ifaddrs::getifaddrs().unwrap(); let mut res = HashMap::new(); for ifaddr in addrs { let address = match ifaddr.address { Some(address) => address, None => { log::debug!( "Interface {} has an unsupported address family", ifaddr.interface_name ); continue; } }; let addr_str = match address.family() { Some(AddressFamily::Inet) => { let address = address.to_string(); address .split_once(':') .map(|a| a.0) .unwrap_or(&address) .to_string() } Some(AddressFamily::Inet6) => { let address = address.to_string(); let address = address .split_once(']') .map(|a| a.0) .unwrap_or(&address) .to_string(); let address = address .split_once('%') .map(|a| a.0) .unwrap_or(&address) .to_string(); address.strip_prefix('[').unwrap_or(&address).to_string() } _ => { log::debug!( "Interface {} has an unsupported address family {:?}", ifaddr.interface_name, address.family() ); continue; } }; log::debug!( "Process ip {addr_str} for interface {}", ifaddr.interface_name ); let ip = IpAddr::from_str(&addr_str)?; if !res.contains_key(&ifaddr.interface_name) { res.insert(ifaddr.interface_name.to_string(), Vec::with_capacity(1)); } res.get_mut(&ifaddr.interface_name).unwrap().push(ip); } Ok(res) } #[cfg(test)] mod tests { use crate::utils::net_utils::{ is_ipv4_address_valid, is_ipv6_address_valid, is_mac_address_valid, is_mask_valid, is_net_interface_name_valid, }; #[test] fn test_is_mac_address_valid() { assert!(is_mac_address_valid("FF:FF:FF:FF:FF:FF")); assert!(is_mac_address_valid("02:42:a4:6e:f2:be")); assert!(!is_mac_address_valid("tata")); assert!(!is_mac_address_valid("FF:FF:FF:FF:FF:FZ")); assert!(!is_mac_address_valid("FF:FF:FF:FF:FF:FF:FF")); } #[test] fn test_is_ipv4_address_valid() { assert!(is_ipv4_address_valid("10.0.0.1")); assert!(is_ipv4_address_valid("2.56.58.156")); assert!(!is_ipv4_address_valid("tata")); assert!(!is_ipv4_address_valid("1.25.25.288")); assert!(!is_ipv4_address_valid("5.5.5.5.5")); assert!(!is_ipv4_address_valid("fe80::")); } #[test] fn test_is_ipv6_address_valid() { assert!(is_ipv6_address_valid("fe80::")); assert!(is_ipv6_address_valid("fe80:dd::")); assert!(is_ipv6_address_valid("00:00:00:00:00::")); assert!(is_ipv6_address_valid("0:0:0:0:0:0:0:0")); assert!(!is_ipv6_address_valid("tata")); assert!(!is_ipv6_address_valid("2.56.58.156")); assert!(!is_ipv6_address_valid("fe::dd::dd")); } #[test] fn test_is_mask_valid() { assert!(is_mask_valid(4, 25)); assert!(is_mask_valid(4, 32)); assert!(is_mask_valid(6, 32)); assert!(is_mask_valid(6, 34)); assert!(!is_mask_valid(4, 34)); assert!(is_mask_valid(6, 69)); assert!(is_mask_valid(6, 128)); assert!(!is_mask_valid(6, 129)); } #[test] fn test_is_net_interface_name_valid() { assert!(is_net_interface_name_valid("eth0")); assert!(is_net_interface_name_valid("enp0s25")); assert!(!is_net_interface_name_valid("enp0s25 ")); assert!(!is_net_interface_name_valid("@enp0s25 ")); } }