use std::net::IpAddr;
use getifs::{
gateway_addrs, interface_addrs, interface_by_index, interface_by_name, interfaces, local_addrs,
Flags, IfAddr, IfNet, Interface,
};
use iprobe::{ipv4, ipv6};
#[derive(Debug)]
struct IfStats {
loopback: u32, other: u32, }
impl IfStats {
fn stats(ift: &[Interface]) -> Self {
let mut loopback = 0;
let mut other = 0;
for ifi in ift {
if ifi.flags().contains(Flags::UP) {
if ifi.flags().contains(Flags::LOOPBACK) {
loopback += 1;
} else {
other += 1;
}
}
}
Self { loopback, other }
}
}
#[derive(Default, Debug)]
struct RouteStats {
ipv4: u32, ipv6: u32, }
fn validate_interface_unicast_addrs(ifat: &[IfNet]) -> std::io::Result<RouteStats> {
let mut stats = RouteStats::default();
for ifa in ifat {
if ifa.addr().is_multicast() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("unexpected multicast address: {ifa:?}"),
));
}
let prefix_len = ifa.prefix_len();
let max_prefix_len = ifa.max_prefix_len();
match ifa.addr() {
IpAddr::V4(addr) => {
if prefix_len == 0 || prefix_len > 8 * 4 || max_prefix_len != 8 * 4 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("unexpected prefix length {ifa:?}"),
));
}
if addr.is_loopback() && prefix_len < 8 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"unexpected prefix length",
));
}
stats.ipv4 += 1;
}
IpAddr::V6(addr) => {
if prefix_len == 0 || prefix_len > 8 * 16 || max_prefix_len != 8 * 16 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("unexpected prefix length {ifa:?}"),
));
}
if addr.is_loopback() && prefix_len < 8 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"unexpected prefix length",
));
}
stats.ipv6 += 1;
}
}
}
Ok(stats)
}
fn check_unicast_stats(ifstats: &IfStats, uni_stats: &RouteStats) -> std::io::Result<()> {
if ipv4() && ifstats.loopback + ifstats.other > 0 && uni_stats.ipv4 == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("num Ipv4 unicast routes = 0; want>0; summary:{ifstats:?}, {uni_stats:?}"),
));
}
if ipv6() && ifstats.loopback > 0 && uni_stats.ipv6 == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("num Ipv6 unicast routes = 0; want>0; summary:{ifstats:?}, {uni_stats:?}"),
));
}
Ok(())
}
fn validate_interface_multicast_addrs(ifmat: &[IfAddr]) -> std::io::Result<RouteStats> {
let mut stats = RouteStats::default();
for ifa in ifmat.iter().map(|ifa| ifa.addr()) {
if ifa.is_unspecified() || !ifa.is_multicast() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("unexpected unicast address: {ifa}"),
));
}
match ifa {
IpAddr::V4(addr) => {
if addr.is_multicast() {
stats.ipv4 += 1;
}
}
IpAddr::V6(addr) => {
if addr.is_multicast() {
stats.ipv6 += 1;
}
}
}
}
Ok(stats)
}
fn check_multicast_stats(
ifstats: &IfStats,
uni_stats: &RouteStats,
multi_stats: &RouteStats,
) -> std::io::Result<()> {
if ipv6() && ifstats.loopback > 0 && uni_stats.ipv6 > 1 && multi_stats.ipv6 == 0 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, format!("num Ipv6 multicast routes = 0; want>0; summary:{ifstats:?}, {uni_stats:?}, {multi_stats:?}")));
}
Ok(())
}
#[test]
fn ifis() {
let ift = interfaces().unwrap();
for ifi in ift {
println!(
"{}: flags={:?} index={} mtu={} hwaddr={:?}",
ifi.name(),
ifi.flags(),
ifi.index(),
ifi.mtu(),
ifi.mac_addr()
);
let ifxi = interface_by_index(ifi.index()).unwrap().unwrap();
assert_eq!(ifi, ifxi);
let ifxn = interface_by_name(ifi.name()).unwrap().unwrap();
assert_eq!(ifi, ifxn);
}
}
#[test]
fn if_addrs() {
let ift = interfaces().unwrap();
let stats = IfStats::stats(&ift);
let ifat = interface_addrs().unwrap();
for ifa in &ifat {
println!("{ifa:?}");
}
let uni_stats = validate_interface_unicast_addrs(&ifat).unwrap();
check_unicast_stats(&stats, &uni_stats).unwrap();
}
#[test]
fn if_unicast_addrs() {
let ift = interfaces().unwrap();
let if_stats = IfStats::stats(&ift);
let mut uni_stats = RouteStats::default();
for ifi in ift {
let ifat = ifi.addrs().unwrap();
let stats = validate_interface_unicast_addrs(&ifat).unwrap();
uni_stats.ipv4 += stats.ipv4;
uni_stats.ipv6 += stats.ipv6;
}
check_unicast_stats(&if_stats, &uni_stats).unwrap();
}
#[test]
fn gw_addrs() {
let addrs = gateway_addrs().unwrap();
for addr in addrs {
println!("Gateway {addr}");
}
}
#[test]
fn lc_addrs() {
let addrs = local_addrs().unwrap();
for addr in addrs {
println!("Local {addr}");
}
}
#[test]
fn if_multicast_addrs() {
let ift = interfaces().unwrap();
let if_stats = IfStats::stats(&ift);
let ifat = interface_addrs().unwrap();
let uni_stats = validate_interface_unicast_addrs(&ifat).unwrap();
let mut multi_stats = RouteStats::default();
for ifi in ift {
let ifmat = ifi.multicast_addrs().unwrap();
let stats = validate_interface_multicast_addrs(&ifmat).unwrap();
multi_stats.ipv4 += stats.ipv4;
multi_stats.ipv6 += stats.ipv6;
}
check_multicast_stats(&if_stats, &uni_stats, &multi_stats).unwrap();
}