use crate::builder::DeviceConfig;
use crate::platform::macos::sys::{
ctl_info, ctliocginfo, in6_ifreq, siocgiflladdr, siocsiflladdr, siocsifmtu, IN6_IFF_NODAD,
UTUN_CONTROL_NAME,
};
use crate::platform::macos::tap::Tap;
use crate::platform::unix::device::ctl;
use crate::platform::unix::Tun;
use crate::platform::ETHER_ADDR_LEN;
use crate::Layer;
use libc::{
c_char, c_uint, sockaddr, socklen_t, AF_SYSTEM, AF_SYS_CONTROL, IFNAMSIZ, PF_SYSTEM,
SOCK_DGRAM, SYSPROTO_CONTROL, UTUN_OPT_IFNAME,
};
use std::ffi::{c_void, CStr};
use std::io::{ErrorKind, IoSlice, IoSliceMut};
use std::os::fd::{AsRawFd, IntoRawFd, RawFd};
use std::{io, mem, ptr};
pub enum TunTap {
Tun(Tun),
Tap(Tap),
}
impl TunTap {
pub fn new(config: DeviceConfig) -> io::Result<Self> {
let layer = config.layer.unwrap_or(Layer::L3);
let packet_information = config.packet_information.unwrap_or(false);
match layer {
Layer::L2 => Ok(TunTap::Tap(Tap::new(&config)?)),
Layer::L3 => {
let id = config
.dev_name
.as_ref()
.map(|tun_name| {
if tun_name.len() > IFNAMSIZ {
return Err(io::Error::new(
ErrorKind::InvalidInput,
"device name too long",
));
}
if !tun_name.starts_with("utun") {
return Err(io::Error::new(
ErrorKind::InvalidInput,
"device name must start with utun",
));
}
tun_name[4..]
.parse::<u32>()
.map(|v| v + 1)
.map_err(|e| io::Error::new(ErrorKind::InvalidInput, e))
})
.transpose()?
.unwrap_or(0);
unsafe {
let fd = libc::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
let tun = crate::platform::unix::Fd::new(fd)?;
_ = tun.set_cloexec();
let mut info = ctl_info {
ctl_id: 0,
ctl_name: {
let mut buffer = [0; 96];
for (i, o) in UTUN_CONTROL_NAME.as_bytes().iter().zip(buffer.iter_mut())
{
*o = *i as _;
}
buffer
},
};
if let Err(err) = ctliocginfo(tun.inner, &mut info as *mut _ as *mut _) {
return Err(io::Error::from(err));
}
let addr = libc::sockaddr_ctl {
sc_id: info.ctl_id,
sc_len: mem::size_of::<libc::sockaddr_ctl>() as _,
sc_family: AF_SYSTEM as _,
ss_sysaddr: AF_SYS_CONTROL as _,
sc_unit: id as c_uint,
sc_reserved: [0; 5],
};
let address = &addr as *const libc::sockaddr_ctl as *const sockaddr;
if libc::connect(tun.inner, address, mem::size_of_val(&addr) as socklen_t) < 0 {
return Err(io::Error::last_os_error());
}
let mut tun_name = [0u8; 64];
let mut name_len: socklen_t = 64;
let optval = &mut tun_name as *mut _ as *mut c_void;
let optlen = &mut name_len as *mut socklen_t;
if libc::getsockopt(
tun.inner,
SYSPROTO_CONTROL,
UTUN_OPT_IFNAME,
optval,
optlen,
) < 0
{
return Err(io::Error::last_os_error());
}
let tun = Tun::new(tun);
tun.set_ignore_packet_info(!packet_information);
Ok(TunTap::Tun(tun))
}
}
}
}
pub fn name(&self) -> io::Result<String> {
match &self {
TunTap::Tun(tun) => {
let mut tun_name = [0u8; 64];
let mut name_len: socklen_t = 64;
let optval = &mut tun_name as *mut _ as *mut c_void;
let optlen = &mut name_len as *mut socklen_t;
unsafe {
if libc::getsockopt(
tun.as_raw_fd(),
SYSPROTO_CONTROL,
UTUN_OPT_IFNAME,
optval,
optlen,
) < 0
{
return Err(io::Error::last_os_error());
}
Ok(CStr::from_ptr(tun_name.as_ptr() as *const c_char)
.to_string_lossy()
.into())
}
}
TunTap::Tap(tap) => Ok(tap.name().to_string()),
}
}
pub(crate) fn is_tun(&self) -> bool {
match &self {
TunTap::Tun(_) => true,
TunTap::Tap(_) => false,
}
}
pub fn is_nonblocking(&self) -> io::Result<bool> {
match &self {
TunTap::Tun(tun) => tun.is_nonblocking(),
TunTap::Tap(tap) => tap.is_nonblocking(),
}
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
match &self {
TunTap::Tun(tun) => tun.set_nonblocking(nonblocking),
TunTap::Tap(tap) => tap.set_nonblocking(nonblocking),
}
}
#[inline]
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
match &self {
TunTap::Tun(tun) => tun.send(buf),
TunTap::Tap(tap) => tap.send(buf),
}
}
#[inline]
pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
match &self {
TunTap::Tun(tun) => tun.send_vectored(bufs),
TunTap::Tap(tap) => tap.send_vectored(bufs),
}
}
#[inline]
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
match &self {
TunTap::Tun(tun) => tun.recv(buf),
TunTap::Tap(tap) => tap.recv(buf),
}
}
#[inline]
pub fn recv_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
match &self {
TunTap::Tun(tun) => tun.recv_vectored(bufs),
TunTap::Tap(tap) => tap.recv_vectored(bufs),
}
}
#[cfg(feature = "interruptible")]
#[inline]
pub(crate) fn read_interruptible(
&self,
buf: &mut [u8],
event: &crate::InterruptEvent,
timeout: Option<std::time::Duration>,
) -> io::Result<usize> {
match &self {
TunTap::Tun(tun) => tun.read_interruptible(buf, event, timeout),
TunTap::Tap(tap) => tap.read_interruptible(buf, event, timeout),
}
}
#[cfg(feature = "interruptible")]
#[inline]
pub(crate) fn readv_interruptible(
&self,
bufs: &mut [IoSliceMut<'_>],
event: &crate::InterruptEvent,
timeout: Option<std::time::Duration>,
) -> io::Result<usize> {
match &self {
TunTap::Tun(tun) => tun.readv_interruptible(bufs, event, timeout),
TunTap::Tap(tap) => tap.readv_interruptible(bufs, event, timeout),
}
}
#[cfg(feature = "interruptible")]
#[inline]
pub(crate) fn wait_readable_interruptible(
&self,
event: &crate::InterruptEvent,
timeout: Option<std::time::Duration>,
) -> io::Result<()> {
match &self {
TunTap::Tun(tun) => tun.wait_readable_interruptible(event, timeout),
TunTap::Tap(tap) => tap.wait_readable_interruptible(event, timeout),
}
}
#[cfg(feature = "interruptible")]
#[inline]
pub(crate) fn write_interruptible(
&self,
buf: &[u8],
event: &crate::InterruptEvent,
) -> io::Result<usize> {
match &self {
TunTap::Tun(tun) => tun.write_interruptible(buf, event),
TunTap::Tap(tap) => tap.write_interruptible(buf, event),
}
}
#[cfg(feature = "interruptible")]
#[inline]
pub(crate) fn writev_interruptible(
&self,
bufs: &[IoSlice<'_>],
event: &crate::InterruptEvent,
) -> io::Result<usize> {
match &self {
TunTap::Tun(tun) => tun.writev_interruptible(bufs, event),
TunTap::Tap(tap) => tap.writev_interruptible(bufs, event),
}
}
#[cfg(feature = "interruptible")]
#[inline]
pub(crate) fn wait_writable_interruptible(
&self,
event: &crate::InterruptEvent,
) -> io::Result<()> {
match &self {
TunTap::Tun(tun) => tun.wait_writable_interruptible(event),
TunTap::Tap(tap) => tap.wait_writable_interruptible(event),
}
}
pub fn request(&self) -> io::Result<libc::ifreq> {
let tun_name = self.name()?;
unsafe {
let mut req: libc::ifreq = mem::zeroed();
ptr::copy_nonoverlapping(
tun_name.as_ptr() as *const c_char,
req.ifr_name.as_mut_ptr(),
tun_name.len(),
);
Ok(req)
}
}
pub fn request_peer(&self) -> Option<libc::ifreq> {
let name = match &self {
TunTap::Tun(_) => {
return None;
}
TunTap::Tap(tap) => tap.peer_name(),
};
unsafe {
let mut req: libc::ifreq = mem::zeroed();
ptr::copy_nonoverlapping(
name.as_ptr() as *const c_char,
req.ifr_name.as_mut_ptr(),
name.len(),
);
Some(req)
}
}
pub fn request_v6(&self) -> io::Result<in6_ifreq> {
let tun_name = self.name()?;
unsafe {
let mut req: in6_ifreq = mem::zeroed();
ptr::copy_nonoverlapping(
tun_name.as_ptr() as *const c_char,
req.ifra_name.as_mut_ptr(),
tun_name.len(),
);
req.ifr_ifru.ifru_flags = IN6_IFF_NODAD as _;
Ok(req)
}
}
pub fn set_mac_address(&self, eth_addr: [u8; ETHER_ADDR_LEN as usize]) -> io::Result<()> {
match &self {
TunTap::Tun(_) => Err(io::Error::from(io::ErrorKind::Unsupported)),
TunTap::Tap(_) => {
let mut ifr = self.request()?;
unsafe {
ifr.ifr_ifru.ifru_addr.sa_family = libc::AF_LINK as _;
ifr.ifr_ifru.ifru_addr.sa_len = ETHER_ADDR_LEN;
for (i, v) in eth_addr.iter().enumerate() {
ifr.ifr_ifru.ifru_addr.sa_data[i] = *v as _;
}
siocsiflladdr(ctl()?.inner, &ifr)?;
}
Ok(())
}
}
}
pub fn mac_address(&self) -> io::Result<[u8; ETHER_ADDR_LEN as usize]> {
match &self {
TunTap::Tun(_) => Err(io::Error::from(io::ErrorKind::Unsupported)),
TunTap::Tap(_) => {
let mut ifr = self.request()?;
unsafe {
ifr.ifr_ifru.ifru_addr.sa_family = libc::AF_LINK as _;
ifr.ifr_ifru.ifru_addr.sa_len = ETHER_ADDR_LEN;
siocgiflladdr(ctl()?.inner, &mut ifr)?;
let mut eth_addr = [0; ETHER_ADDR_LEN as usize];
for (i, v) in eth_addr.iter_mut().enumerate() {
*v = ifr.ifr_ifru.ifru_addr.sa_data[i] as _;
}
Ok(eth_addr)
}
}
}
}
#[inline]
pub(crate) fn ignore_packet_info(&self) -> bool {
match &self {
TunTap::Tun(tun) => tun.ignore_packet_info(),
TunTap::Tap(_) => false,
}
}
pub(crate) fn set_ignore_packet_info(&self, ign: bool) {
match &self {
TunTap::Tun(tun) => tun.set_ignore_packet_info(ign),
TunTap::Tap(_) => {}
}
}
pub fn set_mtu(&self, value: u16) -> io::Result<()> {
unsafe {
let ctl = ctl()?;
let mut req = self.request()?;
req.ifr_ifru.ifru_mtu = value as i32;
if let Err(err) = siocsifmtu(ctl.as_raw_fd(), &req) {
return Err(io::Error::from(err));
}
if let Some(mut req) = self.request_peer() {
req.ifr_ifru.ifru_mtu = value as i32;
if let Err(err) = siocsifmtu(ctl.as_raw_fd(), &req) {
return Err(io::Error::from(err));
}
}
Ok(())
}
}
}
impl AsRawFd for TunTap {
fn as_raw_fd(&self) -> RawFd {
match &self {
TunTap::Tun(tun) => tun.as_raw_fd(),
TunTap::Tap(tap) => tap.as_raw_fd(),
}
}
}
impl IntoRawFd for TunTap {
fn into_raw_fd(self) -> RawFd {
match self {
TunTap::Tun(tun) => tun.into_raw_fd(),
TunTap::Tap(tap) => tap.into_raw_fd(),
}
}
}