From 8007e88b537b211a4254fa832a3709a9a818c022 Mon Sep 17 00:00:00 2001 From: Conrad Kramer Date: Sat, 13 May 2023 12:24:11 -0400 Subject: [PATCH] Enable IPv4 configuration on macOS This enables getting and setting the IPv4 address on tun interfaces on macOS --- tun/src/unix/apple/mod.rs | 36 +++++++++++++++---- tun/src/unix/apple/sys.rs | 73 +++++++++++++++++++++++++++++++-------- 2 files changed, 88 insertions(+), 21 deletions(-) diff --git a/tun/src/unix/apple/mod.rs b/tun/src/unix/apple/mod.rs index 11a7350..2bf44ee 100644 --- a/tun/src/unix/apple/mod.rs +++ b/tun/src/unix/apple/mod.rs @@ -1,15 +1,16 @@ use fehler::throws; use libc::c_char; -use std::io::Error; -use std::net::Ipv4Addr; -use std::os::fd::AsRawFd; +use socket2::{Domain, SockAddr, Socket, Type}; +use std::net::{Ipv4Addr, SocketAddrV4}; +use std::os::fd::{AsRawFd, RawFd}; +use std::{io::Error, mem}; mod kern_control; mod sys; pub use super::queue::TunQueue; -use super::ifname_to_string; +use super::{ifname_to_string, string_to_ifname}; use kern_control::SysControlSocket; #[derive(Debug)] @@ -53,12 +54,33 @@ impl TunInterface { } #[throws] - pub fn set_ipv4_addr(&self, _addr: Ipv4Addr) { - todo!() + fn ifreq(&self) -> sys::ifreq { + let mut iff: sys::ifreq = unsafe { mem::zeroed() }; + iff.ifr_name = string_to_ifname(&self.name()?); + iff + } + + #[throws] + pub fn set_ipv4_addr(&self, addr: Ipv4Addr) { + let addr = SockAddr::from(SocketAddrV4::new(addr, 0)); + + let mut iff = self.ifreq()?; + iff.ifr_ifru.ifru_addr = unsafe { *addr.as_ptr() }; + + self.perform(|fd| unsafe { sys::if_set_addr(fd, &iff) })?; } #[throws] pub fn ipv4_addr(&self) -> Ipv4Addr { - todo!() + let mut iff = self.ifreq()?; + self.perform(|fd| unsafe { sys::if_get_addr(fd, &mut iff) })?; + let addr = unsafe { *(&iff.ifr_ifru.ifru_addr as *const _ as *const sys::sockaddr_in) }; + Ipv4Addr::from(u32::from_be(addr.sin_addr.s_addr)) + } + + #[throws] + fn perform(&self, perform: impl FnOnce(RawFd) -> Result) -> R { + let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?; + perform(socket.as_raw_fd())? } } diff --git a/tun/src/unix/apple/sys.rs b/tun/src/unix/apple/sys.rs index ff909ed..949af2c 100644 --- a/tun/src/unix/apple/sys.rs +++ b/tun/src/unix/apple/sys.rs @@ -1,35 +1,80 @@ -pub use libc::{c_void, socklen_t, SYSPROTO_CONTROL, IFNAMSIZ, sockaddr_ctl, AF_SYSTEM, AF_SYS_CONTROL}; -use nix::ioctl_readwrite; +use std::mem; + +use libc::{c_char, c_int, c_short, c_uint, c_ulong, sockaddr}; +pub use libc::{ + c_void, sockaddr_ctl, sockaddr_in, socklen_t, AF_SYSTEM, AF_SYS_CONTROL, IFNAMSIZ, + SYSPROTO_CONTROL, +}; +use nix::{ioctl_read_bad, ioctl_readwrite, ioctl_write_ptr_bad, request_code_write}; pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control"; pub const UTUN_OPT_IFNAME: libc::c_int = 2; pub const MAX_KCTL_NAME: usize = 96; - #[repr(C)] +#[repr(C)] +#[derive(Copy, Clone, Debug)] pub struct ctl_info { pub ctl_id: u32, pub ctl_name: [u8; MAX_KCTL_NAME], } #[repr(C)] -pub struct ifreq {} +#[derive(Copy, Clone, Debug)] +pub struct ifkpi { + pub ifk_module_id: c_uint, + pub ifk_type: c_uint, + pub ifk_ptr: *mut c_void, +} -ioctl_readwrite!(resolve_ctl_info, b'N', 3, ctl_info); +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct ifdevmtu { + pub ifdm_current: c_int, + pub ifdm_min: c_int, + pub ifdm_max: c_int, +} + +#[repr(C)] +pub union ifr_ifru { + pub ifru_addr: sockaddr, + pub ifru_dstaddr: sockaddr, + pub ifru_broadaddr: sockaddr, + pub ifru_flags: c_short, + pub ifru_metric: c_int, + pub ifru_mtu: c_int, + pub ifru_phys: c_int, + pub ifru_media: c_int, + pub ifru_intval: c_int, + pub ifru_data: *mut c_char, + pub ifru_devmtu: ifdevmtu, + pub ifru_kpi: ifkpi, + pub ifru_wake_flags: u32, + pub ifru_route_refcnt: u32, + pub ifru_cap: [c_int; 2], + pub ifru_functional_type: u32, +} + +#[repr(C)] +pub struct ifreq { + pub ifr_name: [c_char; IFNAMSIZ], + pub ifr_ifru: ifr_ifru, +} + +pub const SIOCSIFADDR: c_ulong = request_code_write!(b'i', 12, mem::size_of::()); -/// Copied from https://github.com/rust-lang/socket2/blob/61314a231f73964b3db969ef72c0e9479df320f3/src/sys/unix.rs#L168-L178 -/// getsockopt is not exposed by socket2 #[macro_export] macro_rules! syscall { - ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{ - #[allow(unused_unsafe)] - let res = unsafe { libc::$fn($($arg, )*) }; - if res == -1 { - Err(std::io::Error::last_os_error()) - } else { - Ok(res) + ($call: ident ( $($arg: expr),* $(,)* ) ) => {{ + match unsafe { ::libc::$call($($arg, )*) } { + -1 => Err(::std::io::Error::last_os_error()), + res => Ok(res), } }}; } pub use syscall; + +ioctl_readwrite!(resolve_ctl_info, b'N', 3, ctl_info); +ioctl_read_bad!(if_get_addr, libc::SIOCGIFADDR, ifreq); +ioctl_write_ptr_bad!(if_set_addr, SIOCSIFADDR, ifreq);