This commit is contained in:
Conrad Kramer 2023-04-17 11:58:31 -04:00
parent b37086e8f6
commit 1a13b77295
20 changed files with 767 additions and 468 deletions

View file

@ -0,0 +1,56 @@
use fehler::throws;
use libc::{sockaddr_ctl, AF_SYSTEM, AF_SYS_CONTROL};
use std::io::Error;
use std::mem::size_of;
use std::os::unix::io::AsRawFd;
/// Trait to connect to kernel extensions on Apple platforms
///
/// Pulled from XNU source: https://github.com/apple/darwin-xnu/blob/main/bsd/sys/kern_control.h
pub trait SysControlSocket {
#[throws]
fn resolve(&self, name: &str, index: u32) -> socket2::SockAddr;
}
impl SysControlSocket for socket2::Socket {
#[throws]
fn resolve(&self, name: &str, index: u32) -> socket2::SockAddr {
let mut info = sys::ctl_info {
ctl_id: 0,
ctl_name: [0; 96],
};
info.ctl_name[..name.len()].copy_from_slice(name.as_bytes());
unsafe { sys::resolve_ctl_info(self.as_raw_fd(), &mut info as *mut sys::ctl_info)? };
let (_, addr) = unsafe {
socket2::SockAddr::init(|addr_storage, len| {
*len = size_of::<sockaddr_ctl>() as u32;
let mut addr: &mut sockaddr_ctl = &mut *addr_storage.cast();
addr.sc_len = *len as u8;
addr.sc_family = AF_SYSTEM as u8;
addr.ss_sysaddr = AF_SYS_CONTROL as u16;
addr.sc_id = info.ctl_id;
addr.sc_unit = index;
Ok(())
})
}?;
addr
}
}
mod sys {
use nix::ioctl_readwrite;
const MAX_KCTL_NAME: usize = 96;
#[repr(C)]
pub struct ctl_info {
pub ctl_id: u32,
pub ctl_name: [u8; MAX_KCTL_NAME],
}
ioctl_readwrite!(resolve_ctl_info, b'N', 3, ctl_info);
}

98
tun/src/unix/apple/mod.rs Normal file
View file

@ -0,0 +1,98 @@
use fehler::throws;
use libc::c_char;
use socket2::{Domain, SockAddr};
use std::mem;
use std::net::SocketAddrV4;
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
mod kern_control;
mod sys;
pub use super::queue::TunQueue;
use super::{ifname_to_string, string_to_ifname};
use kern_control::SysControlSocket;
#[derive(Debug)]
pub struct TunInterface {
pub(crate) socket: socket2::Socket,
}
impl TunInterface {
#[throws]
pub fn new() -> TunInterface {
TunInterface::connect(0)?
}
#[throws]
fn connect(index: u32) -> TunInterface {
use socket2::{Domain, Protocol, Socket, Type};
let socket = Socket::new(
Domain::from(libc::AF_SYSTEM),
Type::DGRAM,
Some(Protocol::from(libc::SYSPROTO_CONTROL)),
)?;
let addr = socket.resolve(sys::UTUN_CONTROL_NAME, index)?;
socket.connect(&addr)?;
TunInterface { socket }
}
#[throws]
pub fn name(&self) -> String {
let mut buf = [0 as c_char; libc::IFNAMSIZ];
let mut len = buf.len() as libc::socklen_t;
sys::syscall!(getsockopt(
self.as_raw_fd(),
libc::SYSPROTO_CONTROL,
sys::UTUN_OPT_IFNAME,
buf.as_mut_ptr() as *mut libc::c_void,
&mut len,
))?;
ifname_to_string(buf)
}
#[throws]
pub fn index(&self) -> i32 {
let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
unsafe {
let mut iff: libc::ifreq = mem::zeroed();
iff.ifr_name = string_to_ifname(self.name()?);
sys::if_get_index(socket.as_raw_fd(), &mut iff)?;
iff.ifr_ifru.ifru_ifindex
}
}
#[throws]
pub fn set_addr(&self, addr: IpAddr) {
match addr {
IpAddr::V4(addr) => self.set_ipv4_addr(addr)?,
_ => (),
}
}
#[throws]
pub fn set_ipv4_addr(&self, addr: Ipv4Addr) {
let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
let addr = SockAddr::from(SocketAddrV4::new(addr, 0));
unsafe {
let mut iff: libc::ifreq = mem::zeroed();
iff.ifr_name = string_to_ifname(self.name()?);
iff.ifr_ifru.ifru_addr = *addr.as_ptr();
sys::if_set_addr(socket.as_raw_fd(), &iff)?;
}
}
}
impl AsRawFd for TunInterface {
fn as_raw_fd(&self) -> RawFd {
self.socket.as_raw_fd()
}
}
impl IntoRawFd for TunInterface {
fn into_raw_fd(self) -> RawFd {
self.socket.into_raw_fd()
}
}

22
tun/src/unix/apple/sys.rs Normal file
View file

@ -0,0 +1,22 @@
pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control";
pub const UTUN_OPT_IFNAME: libc::c_int = 2;
#[repr(C)]
pub struct ifreq {}
/// 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)
}
}};
}
pub use syscall;