use std::ffi::CStr;
use std::ffi::OsStr;
use std::mem::MaybeUninit;
use std::os::unix::io::AsRawFd;
use syscalls::Errno;
use super::fd::Fd;
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
#[repr(C)]
pub struct IfName([u8; libc::IFNAMSIZ]);
#[derive(Debug, Copy, Clone)]
#[repr(C)]
struct IfReq<T> {
name: IfName,
req: T,
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct ifmap {
pub mem_start: usize,
pub mem_end: usize,
pub base_addr: u16,
pub irq: u8,
pub dma: u8,
pub port: u8,
}
impl IfName {
#![allow(unused)]
pub const LOOPBACK: Self = Self(*b"lo\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
pub fn new<S: AsRef<OsStr>>(name: S) -> Result<Self, InterfaceNameTooLong> {
use std::os::unix::ffi::OsStrExt;
let name = name.as_ref().as_bytes();
if name.len() + 1 > libc::IFNAMSIZ {
Err(InterfaceNameTooLong)
} else {
let mut arr = [0u8; libc::IFNAMSIZ];
arr[..name.len()].copy_from_slice(name);
arr[name.len()] = 0;
Ok(Self(arr))
}
}
fn ioctl_get<T>(self, ioctl: libc::c_ulong, socket: &Fd) -> Result<T, Errno> {
let mut req = IfReq::new(self, MaybeUninit::uninit());
Errno::result(unsafe { libc::ioctl(socket.as_raw_fd(), ioctl, &mut req as *mut _) })?;
Ok(unsafe { req.into_req().assume_init() })
}
fn ioctl_set<T>(self, ioctl: libc::c_ulong, socket: &Fd, value: T) -> Result<(), Errno> {
let req = IfReq::new(self, value);
Errno::result(unsafe { libc::ioctl(socket.as_raw_fd(), ioctl, &req as *const _) })?;
Ok(())
}
pub fn get_addr(&self, socket: &Fd) -> Result<libc::sockaddr, Errno> {
self.ioctl_get(libc::SIOCGIFADDR, socket)
}
pub fn set_addr(&self, socket: &Fd, addr: libc::sockaddr) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFADDR, socket, addr)
}
pub fn get_dest_addr(&self, socket: &Fd) -> Result<libc::sockaddr, Errno> {
self.ioctl_get(libc::SIOCGIFDSTADDR, socket)
}
pub fn set_dest_addr(&self, socket: &Fd, addr: libc::sockaddr) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFDSTADDR, socket, addr)
}
pub fn get_broadcast_addr(&self, socket: &Fd) -> Result<libc::sockaddr, Errno> {
self.ioctl_get(libc::SIOCGIFBRDADDR, socket)
}
pub fn set_broadcast_addr(&self, socket: &Fd, addr: libc::sockaddr) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFBRDADDR, socket, addr)
}
pub fn get_netmask(&self, socket: &Fd) -> Result<libc::sockaddr, Errno> {
self.ioctl_get(libc::SIOCGIFNETMASK, socket)
}
pub fn set_netmask(&self, socket: &Fd, addr: libc::sockaddr) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFNETMASK, socket, addr)
}
pub fn get_hw_addr(&self, socket: &Fd) -> Result<libc::sockaddr, Errno> {
self.ioctl_get(libc::SIOCGIFHWADDR, socket)
}
pub fn set_hw_addr(&self, socket: &Fd, addr: libc::sockaddr) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFHWADDR, socket, addr)
}
pub fn get_flags(&self, socket: &Fd) -> Result<i16, Errno> {
self.ioctl_get(libc::SIOCGIFFLAGS, socket)
}
pub fn set_flags(&self, socket: &Fd, flags: i16) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFFLAGS, socket, flags)
}
pub fn get_metric(&self, socket: &Fd) -> Result<i32, Errno> {
self.ioctl_get(libc::SIOCGIFMETRIC, socket)
}
pub fn set_metric(&self, socket: &Fd, value: i32) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFMETRIC, socket, value)
}
pub fn get_mtu(&self, socket: &Fd) -> Result<i32, Errno> {
self.ioctl_get(libc::SIOCGIFMTU, socket)
}
pub fn set_mtu(&self, socket: &Fd, value: i32) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFMTU, socket, value)
}
pub fn get_map(&self, socket: &Fd) -> Result<ifmap, Errno> {
self.ioctl_get(libc::SIOCGIFMAP, socket)
}
pub fn set_map(&self, socket: &Fd, map: ifmap) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFMAP, socket, map)
}
pub fn get_slave(&self, socket: &Fd) -> Result<Self, Errno> {
self.ioctl_get(libc::SIOCGIFSLAVE, socket)
}
pub fn set_slave(&self, socket: &Fd, name: Self) -> Result<(), Errno> {
self.ioctl_set(libc::SIOCSIFSLAVE, socket, name)
}
}
impl AsRef<CStr> for IfName {
fn as_ref(&self) -> &CStr {
unsafe { CStr::from_ptr(self.0.as_ptr() as *const _) }
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct InterfaceNameTooLong;
impl<T> IfReq<T> {
pub fn new(name: IfName, req: T) -> Self {
Self { name, req }
}
pub fn into_req(self) -> T {
self.req
}
}
#[cfg(test)]
mod tests {
use nix::net::if_::InterfaceFlags;
use super::*;
#[test]
fn ifname() {
assert_eq!(IfName::new("lo"), Ok(IfName::LOOPBACK));
assert_eq!(
IfName::new("too loooooooooooooooong"),
Err(InterfaceNameTooLong)
);
}
#[test]
fn smoke_tests() {
if crate::test_runs_in_own_process() {
return;
}
let sock = Fd::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP).unwrap();
let lo = IfName::LOOPBACK;
let addr = lo.get_addr(&sock).unwrap();
assert_eq!(addr.sa_family as i32, libc::AF_INET);
let flags = InterfaceFlags::from_bits_truncate(lo.get_flags(&sock).unwrap() as i32);
assert!(flags.contains(InterfaceFlags::IFF_LOOPBACK));
}
}