use super::run_shell;
use crate::errors::*;
use ipnet::IpNet;
use libc::c_int;
use std::fs::File;
use std::io::{Error, ErrorKind};
extern "C" {
#[link_name = "utun_open"]
fn utun_open(fd: *mut c_int, idx: c_int) -> c_int;
}
pub struct RawTun {
name: String,
reader: Option<File>,
writer: Option<File>,
}
impl RawTun {
pub async fn new() -> Result<RawTun> {
let mut fd: c_int = 0;
for tun_idx in 1..1024 {
let status = unsafe { utun_open(&mut fd, tun_idx) };
match status {
0 => {
let f2: std::fs::File = unsafe { std::os::unix::io::FromRawFd::from_raw_fd(fd) };
let name = format!("utun{}", tun_idx);
let write_fd = f2.try_clone().context(CreateTun)?;
return Ok(RawTun {
name,
reader: Some(f2),
writer: Some(write_fd),
});
}
16 => { }
_ => Err(Error::from_raw_os_error(status)).context(CreateTun)?,
}
}
Err(Error::new(ErrorKind::AddrNotAvailable, "can't open tunnel")).context(CreateTun)?
}
pub fn init(&self, addr: &IpNet, mtu: u32) -> Result<()> {
run_shell(format!(
"ifconfig {} {} {} mtu {} netmask {} up",
&self.name,
addr.addr(),
addr.addr(),
mtu,
addr.netmask(),
))?;
run_shell(format!(
"route add -net {} -interface {}",
&addr.network(),
&self.name
))?;
Ok(())
}
pub fn take_reader(&mut self) -> Option<File> {
self.reader.take()
}
pub fn take_writer(&mut self) -> Option<File> {
self.writer.take()
}
}