netdev 0.41.0

Cross-platform library for enumerating network interfaces with metadata.
Documentation
use crate::net::device::NetworkDevice;
use crate::net::mac::MacAddr;
use std::collections::HashMap;
use std::fs::read_to_string;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;

const PATH_PROC_NET_ROUTE: &str = "/proc/net/route";
const PATH_PROC_NET_IPV6_ROUTE: &str = "/proc/net/ipv6_route";
const PATH_PROC_NET_ARP: &str = "/proc/net/arp";

fn convert_hex_ipv4(hex_ip: &str) -> Ipv4Addr {
    if hex_ip.len() != 8 {
        return Ipv4Addr::UNSPECIFIED;
    }
    let o1: u8 = u8::from_str_radix(&hex_ip[6..8], 0x10).unwrap_or(0);
    let o2: u8 = u8::from_str_radix(&hex_ip[4..6], 0x10).unwrap_or(0);
    let o3: u8 = u8::from_str_radix(&hex_ip[2..4], 0x10).unwrap_or(0);
    let o4: u8 = u8::from_str_radix(&hex_ip[0..2], 0x10).unwrap_or(0);
    Ipv4Addr::new(o1, o2, o3, o4)
}

fn convert_hex_ipv6(hex_ip: &str) -> Ipv6Addr {
    if hex_ip.len() != 32 {
        return Ipv6Addr::UNSPECIFIED;
    }
    let h1: u16 = u16::from_str_radix(&hex_ip[0..4], 0x10).unwrap_or(0);
    let h2: u16 = u16::from_str_radix(&hex_ip[4..8], 0x10).unwrap_or(0);
    let h3: u16 = u16::from_str_radix(&hex_ip[8..12], 0x10).unwrap_or(0);
    let h4: u16 = u16::from_str_radix(&hex_ip[12..16], 0x10).unwrap_or(0);
    let h5: u16 = u16::from_str_radix(&hex_ip[16..20], 0x10).unwrap_or(0);
    let h6: u16 = u16::from_str_radix(&hex_ip[20..24], 0x10).unwrap_or(0);
    let h7: u16 = u16::from_str_radix(&hex_ip[24..28], 0x10).unwrap_or(0);
    let h8: u16 = u16::from_str_radix(&hex_ip[28..32], 0x10).unwrap_or(0);
    Ipv6Addr::new(h1, h2, h3, h4, h5, h6, h7, h8)
}

fn get_arp_map() -> HashMap<Ipv4Addr, MacAddr> {
    let mut arp_map: HashMap<Ipv4Addr, MacAddr> = HashMap::new();
    if let Ok(arp_text) = read_to_string(PATH_PROC_NET_ARP) {
        for row in arp_text.lines() {
            let mut fields = row.split_whitespace();
            let Some(ip_addr) = fields.next() else {
                continue;
            };
            let _hw_type = fields.next();
            let _flags = fields.next();
            let Some(mac_addr) = fields.next() else {
                continue;
            };
            if let Ok(ipv4_addr) = Ipv4Addr::from_str(ip_addr) {
                arp_map.insert(ipv4_addr, MacAddr::from_hex_format(mac_addr));
            }
        }
    }
    arp_map
}

fn get_ipv4_gateway_map() -> HashMap<String, Ipv4Addr> {
    let mut ipv4_gateway_map: HashMap<String, Ipv4Addr> = HashMap::new();
    if let Ok(route_text) = read_to_string(PATH_PROC_NET_ROUTE) {
        for row in route_text.lines() {
            let mut fields = row.split_whitespace();
            let Some(if_name) = fields.next() else {
                continue;
            };
            let _destination = fields.next();
            let Some(gateway) = fields.next() else {
                continue;
            };
            if gateway != "00000000" {
                ipv4_gateway_map.insert(if_name.to_owned(), convert_hex_ipv4(gateway));
            }
        }
    }
    ipv4_gateway_map
}

fn get_ipv6_gateway_map() -> HashMap<String, Ipv6Addr> {
    let mut ipv6_gateway_map: HashMap<String, Ipv6Addr> = HashMap::new();
    if let Ok(route_text) = read_to_string(PATH_PROC_NET_IPV6_ROUTE) {
        for row in route_text.lines() {
            let fields: Vec<&str> = row.split_whitespace().collect();
            if fields.len() < 10 {
                continue;
            }
            // default route has zero destination and zero prefix length
            if fields[0] == "00000000000000000000000000000000"
                && fields[1] == "00"
                && fields[4] != "00000000000000000000000000000000"
            {
                ipv6_gateway_map.insert(fields[9].to_owned(), convert_hex_ipv6(fields[4]));
            }
        }
    }
    ipv6_gateway_map
}

pub fn get_gateway_map() -> HashMap<String, NetworkDevice> {
    let mut gateway_map: HashMap<String, NetworkDevice> = HashMap::new();
    let arp_map: HashMap<Ipv4Addr, MacAddr> = get_arp_map();
    for (if_name, ipv4_addr) in get_ipv4_gateway_map() {
        let gateway = gateway_map.entry(if_name).or_insert(NetworkDevice::new());
        if let Some(mac_addr) = arp_map.get(&ipv4_addr) {
            gateway.mac_addr = *mac_addr;
        }
        gateway.ipv4.push(ipv4_addr);
    }
    for (if_name, ipv6_addr) in get_ipv6_gateway_map() {
        let gateway = gateway_map.entry(if_name).or_insert(NetworkDevice::new());
        gateway.ipv6.push(ipv6_addr);
    }
    gateway_map
}