netconfig-rs 0.1.6

Crate for managing network interfaces and IP addresses in a cross-platform way
Documentation
use crate::sys::posix::ifreq::ifreq;
use crate::sys::posix::{dummy_socket, ioctls, InterfaceName};
use crate::sys::InterfaceHandle;
use crate::{Error, Interface};
use ipnet::IpNet;
use nix::ifaddrs::getifaddrs;
use nix::net::if_::InterfaceFlags;
use nix::sys::socket::AddressFamily::{Inet, Inet6};
use nix::sys::socket::SockaddrLike;
use std::io;
use std::net::IpAddr;
use std::os::unix::io::AsRawFd;

impl InterfaceHandle {
    pub fn addresses(&self) -> Result<Vec<IpNet>, Error> {
        let mut result = vec![];
        let name = self.name()?;

        for interface in getifaddrs()?.filter(|x| x.interface_name == name) {
            let (Some(address), Some(netmask)) = (interface.address, interface.netmask) else {
                continue;
            };

            let (address, netmask) = match (address.family(), netmask.family()) {
                (Some(Inet), Some(Inet)) => (
                    IpAddr::V4(
                        address
                            .as_sockaddr_in()
                            .ok_or(io::Error::other("empty address"))?
                            .ip(),
                    ),
                    IpAddr::V4(
                        netmask
                            .as_sockaddr_in()
                            .ok_or(io::Error::other("empty netmask"))?
                            .ip(),
                    ),
                ),
                (Some(Inet6), Some(Inet6)) => (
                    IpAddr::V6(
                        address
                            .as_sockaddr_in6()
                            .ok_or(io::Error::other("empty address"))?
                            .ip(),
                    ),
                    IpAddr::V6(
                        netmask
                            .as_sockaddr_in6()
                            .ok_or(io::Error::other("empty netmask"))?
                            .ip(),
                    ),
                ),
                (_, _) => continue,
            };

            let prefix = ipnet::ip_mask_to_prefix(netmask)
                .map_err(|e| std::io::Error::other(e.to_string()))?;

            result.push(IpNet::new(address, prefix).map_err(|e| io::Error::other(e.to_string()))?);
        }
        Ok(result)
    }

    pub fn mtu(&self) -> Result<u32, Error> {
        let mut req = ifreq::new(self.name()?)?;
        let socket = dummy_socket()?;

        unsafe {
            ioctls::siocgifmtu(socket.as_raw_fd(), &mut req)?;
            Ok(req.ifr_ifru.ifru_mtu as _)
        }
    }

    pub fn set_mtu(&self, mtu: u32) -> Result<(), Error> {
        let mut req = ifreq::new(self.name()?)?;
        req.ifr_ifru.ifru_mtu = mtu as _;

        let socket = dummy_socket()?;

        unsafe { ioctls::siocsifmtu(socket.as_raw_fd(), &req) }?;
        Ok(())
    }

    pub fn name(&self) -> Result<String, Error> {
        let mut buf = InterfaceName::default();
        let ret_buf = unsafe { libc::if_indextoname(self.index, buf.as_mut_ptr()) };

        if ret_buf.is_null() {
            return Err(Error::InterfaceNotFound);
        }

        buf.try_into().map_err(|_| Error::InvalidParameter)
    }

    pub fn try_from_name(name: &str) -> Result<Interface, Error> {
        let name = InterfaceName::try_from(name).map_err(|_| Error::InvalidParameter)?;

        match unsafe { libc::if_nametoindex(name.as_ptr()) } {
            0 => Err(Error::InterfaceNotFound),
            n => Ok(unsafe { Interface::from_index_unchecked(n) }),
        }
    }

    pub fn try_from_index(index: u32) -> Result<Interface, Error> {
        match nix::net::if_::if_nameindex()?
            .iter()
            .find(|if_| if_.index() == index)
        {
            Some(_) => Ok(unsafe { Interface::from_index_unchecked(index) }),
            None => Err(Error::InterfaceNotFound),
        }
    }

    pub fn index(&self) -> Result<u32, Error> {
        Ok(self.index)
    }

    pub fn set_up(&self, v: bool) -> Result<(), Error> {
        let mut flags = self.flags()?;
        flags.set(InterfaceFlags::IFF_UP, v);
        self.set_flags(flags)?;
        Ok(())
    }

    pub fn set_running(&self, v: bool) -> Result<(), Error> {
        let mut flags = self.flags()?;
        flags.set(InterfaceFlags::IFF_RUNNING, v);
        self.set_flags(flags)?;
        Ok(())
    }
}

impl InterfaceHandle {
    pub(crate) fn flags(&self) -> Result<InterfaceFlags, Error> {
        let mut req = ifreq::new(self.name()?)?;
        let socket = dummy_socket()?;

        unsafe {
            ioctls::siocgifflags(socket.as_raw_fd(), &mut req)?;
            Ok(InterfaceFlags::from_bits_truncate(
                req.ifr_ifru.ifru_flags as _,
            ))
        }
    }

    pub(crate) fn set_flags(&self, flags: InterfaceFlags) -> Result<InterfaceFlags, Error> {
        let mut req = ifreq::new(self.name()?)?;
        req.ifr_ifru.ifru_flags = flags.bits() as _;

        let socket = dummy_socket()?;

        unsafe {
            ioctls::siocsifflags(socket.as_raw_fd(), &req)?;
            Ok(InterfaceFlags::from_bits_truncate(
                req.ifr_ifru.ifru_flags as _,
            ))
        }
    }
}