use std::net::Ipv6Addr;
use crate::interface::ipv6_addr_flags::Ipv6AddrFlags;
const SIOCGIFAFLAG_IN6: libc::c_ulong = 0xC1206949;
const IN6_IFF_TENTATIVE: u32 = 0x02;
const IN6_IFF_DUPLICATED: u32 = 0x04;
const IN6_IFF_DEPRECATED: u32 = 0x10;
const IN6_IFF_TEMPORARY: u32 = 0x80;
#[repr(C)]
struct In6Ifreq {
ifr_name: [u8; libc::IFNAMSIZ],
ifr_ifru: In6IfreqUnion,
}
#[repr(C)]
union In6IfreqUnion {
addr: libc::sockaddr_in6,
flags6: libc::c_int,
icmp6_stats: [u64; 34],
}
const _: [(); 288] = [(); std::mem::size_of::<In6Ifreq>()];
pub(crate) fn get_ipv6_addr_flags(ifname: &str, addr: &Ipv6Addr) -> Ipv6AddrFlags {
unsafe {
let fd = libc::socket(libc::AF_INET6, libc::SOCK_DGRAM, 0);
if fd < 0 {
return Ipv6AddrFlags::default();
}
let mut req: In6Ifreq = std::mem::zeroed();
let name_bytes = ifname.as_bytes();
let copy_len = name_bytes.len().min(libc::IFNAMSIZ - 1);
std::ptr::copy_nonoverlapping(
name_bytes.as_ptr(),
req.ifr_name.as_mut_ptr().cast(),
copy_len,
);
req.ifr_ifru.addr.sin6_family = libc::AF_INET6 as libc::sa_family_t;
req.ifr_ifru.addr.sin6_len = std::mem::size_of::<libc::sockaddr_in6>() as u8;
req.ifr_ifru.addr.sin6_addr.s6_addr = addr.octets();
if addr.is_unicast_link_local() {
req.ifr_ifru.addr.sin6_scope_id = libc::if_nametoindex(req.ifr_name.as_ptr().cast());
}
let ret = libc::ioctl(fd, SIOCGIFAFLAG_IN6, &mut req);
libc::close(fd);
if ret < 0 {
return Ipv6AddrFlags::default();
}
let raw = req.ifr_ifru.flags6 as u32;
Ipv6AddrFlags {
deprecated: raw & IN6_IFF_DEPRECATED != 0,
temporary: raw & IN6_IFF_TEMPORARY != 0,
tentative: raw & IN6_IFF_TENTATIVE != 0,
duplicated: raw & IN6_IFF_DUPLICATED != 0,
permanent: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn request_matches_bsd_kernel_abi() {
assert_eq!(std::mem::size_of::<In6Ifreq>(), 288);
assert_eq!(std::mem::offset_of!(In6Ifreq, ifr_ifru), 16);
assert_eq!((SIOCGIFAFLAG_IN6 >> 16) & 0x1fff, 288);
let mut req: In6Ifreq = unsafe { std::mem::zeroed() };
req.ifr_ifru.flags6 = IN6_IFF_TEMPORARY as libc::c_int;
let flags_ptr = &raw const req.ifr_ifru.flags6;
let addr_ptr = &raw const req.ifr_ifru.addr;
assert_eq!(flags_ptr.cast::<u8>(), addr_ptr.cast::<u8>());
}
}