use std::{ io::{Error, IoSlice}, mem, net::{Ipv4Addr, SocketAddrV4}, os::fd::{AsRawFd, RawFd}, }; use byteorder::{ByteOrder, NetworkEndian}; use fehler::throws; use libc::{c_char, iovec, writev, AF_INET, AF_INET6}; use socket2::{Domain, SockAddr, Socket, Type}; use tracing::{self, instrument}; mod kern_control; mod sys; use kern_control::SysControlSocket; pub use super::queue::TunQueue; use super::{ifname_to_string, string_to_ifname, TunOptions}; #[derive(Debug)] pub struct TunInterface { pub(crate) socket: socket2::Socket, } impl TunInterface { #[throws] #[instrument] pub fn new() -> TunInterface { Self::new_with_options(TunOptions::new())? } #[throws] #[instrument] pub fn new_with_options(options: TunOptions) -> TunInterface { let ti = TunInterface::connect(0)?; if let Some(addr) = options.address{ if let Ok(addr) = addr.parse() { ti.set_ipv4_addr(addr)?; } } ti } #[throws] #[instrument] fn connect(index: u32) -> TunInterface { use socket2::{Domain, Protocol, Socket, Type}; let socket = Socket::new( Domain::from(sys::AF_SYSTEM), Type::DGRAM, Some(Protocol::from(sys::SYSPROTO_CONTROL)), )?; let addr = socket.resolve(sys::UTUN_CONTROL_NAME, index)?; socket.connect(&addr)?; TunInterface { socket } } #[throws] #[instrument] pub fn name(&self) -> String { let mut buf = [0 as c_char; sys::IFNAMSIZ]; let mut len = buf.len() as sys::socklen_t; sys::syscall!(getsockopt( self.as_raw_fd(), sys::SYSPROTO_CONTROL, sys::UTUN_OPT_IFNAME, buf.as_mut_ptr() as *mut sys::c_void, &mut len, ))?; ifname_to_string(buf) } #[throws] #[instrument] fn ifreq(&self) -> sys::ifreq { let mut iff: sys::ifreq = unsafe { mem::zeroed() }; iff.ifr_name = string_to_ifname(&self.name()?); iff } #[throws] #[instrument] 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) })?; tracing::info!("ipv4_addr_set: {:?} (fd: {:?})", addr, self.as_raw_fd()) } #[throws] #[instrument] pub fn ipv4_addr(&self) -> Ipv4Addr { 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 span = tracing::info_span!("perform", fd = self.as_raw_fd()); let _enter = span.enter(); let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?; perform(socket.as_raw_fd())? } #[throws] #[instrument] pub fn mtu(&self) -> i32 { let mut iff = self.ifreq()?; self.perform(|fd| unsafe { sys::if_get_mtu(fd, &mut iff) })?; let mtu = unsafe { iff.ifr_ifru.ifru_mtu }; mtu } #[throws] #[instrument] pub fn set_mtu(&self, mtu: i32) { let mut iff = self.ifreq()?; iff.ifr_ifru.ifru_mtu = mtu; self.perform(|fd| unsafe { sys::if_set_mtu(fd, &iff) })?; tracing::info!("mtu_set: {:?} (fd: {:?})", mtu, self.as_raw_fd()) } #[throws] #[instrument] pub fn netmask(&self) -> Ipv4Addr { let mut iff = self.ifreq()?; self.perform(|fd| unsafe { sys::if_get_netmask(fd, &mut iff) })?; let netmask = unsafe { *(&iff.ifr_ifru.ifru_netmask as *const _ as *const sys::sockaddr_in) }; Ipv4Addr::from(u32::from_be(netmask.sin_addr.s_addr)) } #[throws] #[instrument] pub fn set_netmask(&self, addr: Ipv4Addr) { let addr = SockAddr::from(SocketAddrV4::new(addr, 0)); let mut iff = self.ifreq()?; iff.ifr_ifru.ifru_netmask = unsafe { *addr.as_ptr() }; self.perform(|fd| unsafe { sys::if_set_netmask(fd, &iff) })?; tracing::info!( "netmask_set: {:?} (fd: {:?})", unsafe { iff.ifr_ifru.ifru_netmask }, self.as_raw_fd() ) } #[throws] #[instrument] pub fn send(&self, buf: &[u8]) -> usize { use std::io::ErrorKind; let proto = match buf[0] >> 4 { 6 => Ok(AF_INET6), 4 => Ok(AF_INET), _ => Err(Error::new(ErrorKind::InvalidInput, "Invalid IP version")), }?; let mut pbuf = [0; 4]; NetworkEndian::write_i32(&mut pbuf, proto); let bufs = [IoSlice::new(&pbuf), IoSlice::new(buf)]; let bytes_written: isize = unsafe { writev( self.as_raw_fd(), bufs.as_ptr() as *const iovec, bufs.len() as i32, ) }; bytes_written .try_into() .map_err(|_| Error::new(ErrorKind::Other, "Conversion error"))? } }