use super::run_shell;
use crate::errors::*;
use async_std::fs;
use ipnet::IpNet;
use libc::c_int;
use std::fmt;
use std::io::{Error, ErrorKind};
use std::os::unix::io::AsRawFd;
extern "C" {
#[link_name = "tuntap_setup"]
fn tuntap_setup(fd: c_int, name: *mut u8, mode: c_int, packet_info: c_int) -> c_int;
}
pub struct RawTun {
pub name: String,
pub raw_file: fs::File,
}
impl RawTun {
pub async fn new() -> Result<RawTun> {
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.open("/dev/net/tun")
.await
.context(CreateTun)?;
let packet_info = false;
for i in 0..1024 {
let fd = file.as_raw_fd();
let ifname = format!("utun{}", i);
let mut name_buffer = Vec::new();
name_buffer.extend_from_slice(ifname.as_bytes());
name_buffer.extend_from_slice(&[0; 33]);
let name_ptr: *mut u8 = name_buffer.as_mut_ptr();
let result = unsafe { tuntap_setup(fd, name_ptr, 1, packet_info.into()) };
if result < 0 {
let err = Error::last_os_error();
if err.kind() == ErrorKind::Other && err.to_string().contains("busy") {
continue;
}
return Err(Error::last_os_error()).context(CreateTun);
}
return Ok(RawTun {
name: ifname.to_owned(),
raw_file: file,
});
}
Err(Error::new(ErrorKind::Other, "no tunnel is available")).context(CreateTun)
}
pub fn init(&self, addr: &IpNet, mtu: u32) -> Result<()> {
run_shell(format!(
"ip link set dev {} up mtu {} qlen 100",
&self.name, mtu
))?;
run_shell(format!(
"ifconfig {} {} netmask {}",
&self.name, addr.addr(), addr.netmask(),
))?;
Ok(())
}
}