use std::ffi::CString;
use std::mem::size_of;
use std::os::raw::{c_char, c_int};
const LIFNAMSIZ: usize = 32;
#[repr(C)]
union LifrLifru {
_addr: [u8; 336],
flags: u64,
mtu: u32,
}
#[repr(C)]
pub(crate) struct Lifreq {
lifr_name: [c_char; LIFNAMSIZ],
_lifr_lifru1: c_int,
_lifr_type: u32,
lifr_lifru: LifrLifru,
}
impl Lifreq {
fn for_name(name: &str) -> Option<Self> {
let cname = CString::new(name).ok()?;
let bytes = cname.as_bytes_with_nul();
if bytes.len() > LIFNAMSIZ {
return None;
}
let mut req: Self = unsafe { std::mem::zeroed() };
for (dst, src) in req.lifr_name.iter_mut().zip(bytes.iter()) {
*dst = *src as c_char;
}
Some(req)
}
pub(crate) unsafe fn flags(&self) -> u64 {
unsafe { self.lifr_lifru.flags }
}
pub(crate) unsafe fn mtu(&self) -> u32 {
unsafe { self.lifr_lifru.mtu }
}
}
const IOC_INOUT: u32 = 0xC000_0000;
const IOCPARM_MASK: u32 = 0xff;
const fn iowr(group: u8, num: u8, size: usize) -> c_int {
(IOC_INOUT | (((size as u32) & IOCPARM_MASK) << 16) | ((group as u32) << 8) | (num as u32))
as c_int
}
pub(crate) const SIOCGLIFFLAGS: c_int = iowr(b'i', 117, size_of::<Lifreq>());
pub(crate) const SIOCGLIFMTU: c_int = iowr(b'i', 122, size_of::<Lifreq>());
pub(crate) fn get_lifreq(af: c_int, name: &str, cmd: c_int) -> Option<Lifreq> {
let mut req = Lifreq::for_name(name)?;
let fd = unsafe { libc::socket(af, libc::SOCK_DGRAM, 0) };
if fd < 0 {
return None;
}
let res = unsafe { libc::ioctl(fd, cmd, &mut req as *mut Lifreq) };
unsafe { libc::close(fd) };
if res < 0 { None } else { Some(req) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn layout_matches_illumos_header() {
assert_eq!(size_of::<Lifreq>(), 376);
assert_eq!(SIOCGLIFFLAGS, 0xc078_6975u32 as c_int);
assert_eq!(SIOCGLIFMTU, 0xc078_697au32 as c_int);
}
}