libtun 0.1.0

a cross-platform(macosx, linux) tunnel library
Documentation
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,
}

// https://www.kernel.org/doc/Documentation/networking/tuntap.txt
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(),
    ))?;
    // run_shell(format!(
      // "ip addr add dev {} local {} peer {}",
      // &self.name,
      // addr.addr(),
      // addr.addr()
    // ))?;
    // run_shell(format!(
      // "ip route add {}/{} via {} dev {}",
      // addr.network(), addr.prefix_len(),
      // addr.addr(),
      // &self.name
    // ))?;
    Ok(())
  }
}