diff --git a/src/os/bsd/ipv6_addr_flags.rs b/src/os/bsd/ipv6_addr_flags.rs index a2e6852..b9562b5 100644 --- a/src/os/bsd/ipv6_addr_flags.rs +++ b/src/os/bsd/ipv6_addr_flags.rs @@ -10,11 +10,24 @@ const IN6_IFF_DEPRECATED: u32 = 0x10; const IN6_IFF_TEMPORARY: u32 = 0x80; // `libc` does not expose `in6_ifreq` on FreeBSD/OpenBSD/NetBSD. +// +// Mirrors `struct in6_ifreq` from . `SIOCGIFAFLAG_IN6` encodes +// `sizeof(struct in6_ifreq)` (0x120 = 288 bytes on LP64), and the kernel copies that many +// bytes in and out, so the union must be padded to the size of its largest member +// (`icmp6_ifstat`, 34 x u64 = 272 bytes). The flags are returned in `ifru_flags6`, which +// overlaps `ifru_addr` at the start of the union. #[repr(C)] struct In6Ifreq { ifr_name: [u8; libc::IFNAMSIZ], - ifr_addr: libc::sockaddr_in6, - ifr_flags: libc::c_int, + ifr_ifru: In6IfreqUnion, +} + +#[repr(C)] +union In6IfreqUnion { + ifru_addr: libc::sockaddr_in6, + ifru_flags6: libc::c_int, + // 8-byte aligned, like the u64 counters in `in6_ifstat` / `icmp6_ifstat`. + _size: [u64; 34], } pub(crate) fn get_ipv6_addr_flags(ifname: &str, addr: &Ipv6Addr) -> Ipv6AddrFlags { @@ -25,6 +38,7 @@ pub(crate) fn get_ipv6_addr_flags(ifname: &str, addr: &Ipv6Addr) -> Ipv6AddrFlag } let mut req: In6Ifreq = std::mem::zeroed(); + const _: () = assert!(std::mem::size_of::() == 0x120); let name_bytes = ifname.as_bytes(); let copy_len = name_bytes.len().min(libc::IFNAMSIZ - 1); @@ -34,9 +48,9 @@ pub(crate) fn get_ipv6_addr_flags(ifname: &str, addr: &Ipv6Addr) -> Ipv6AddrFlag copy_len, ); - req.ifr_addr.sin6_family = libc::AF_INET6 as libc::sa_family_t; - req.ifr_addr.sin6_len = std::mem::size_of::() as u8; - req.ifr_addr.sin6_addr.s6_addr = addr.octets(); + req.ifr_ifru.ifru_addr.sin6_family = libc::AF_INET6 as libc::sa_family_t; + req.ifr_ifru.ifru_addr.sin6_len = std::mem::size_of::() as u8; + req.ifr_ifru.ifru_addr.sin6_addr.s6_addr = addr.octets(); let ret = libc::ioctl(fd, SIOCGIFAFLAG_IN6, &mut req); libc::close(fd); @@ -45,7 +59,7 @@ pub(crate) fn get_ipv6_addr_flags(ifname: &str, addr: &Ipv6Addr) -> Ipv6AddrFlag return Ipv6AddrFlags::default(); } - let raw = req.ifr_flags as u32; + let raw = req.ifr_ifru.ifru_flags6 as u32; Ipv6AddrFlags { deprecated: raw & IN6_IFF_DEPRECATED != 0,