Initial commit
This commit is contained in:
commit
c1e7415871
56 changed files with 3225 additions and 0 deletions
53
tun/src/apple/kern_control.rs
Normal file
53
tun/src/apple/kern_control.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use libc::{sockaddr_ctl, AF_SYSTEM, AF_SYS_CONTROL};
|
||||
use std::io::Result;
|
||||
use std::mem::size_of;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
/// Trait to connect to kernel extensions on Apple platforms
|
||||
///
|
||||
/// Pulled from XNU source: https://github.com/apple/darwin-xnu/blob/main/bsd/sys/kern_control.h
|
||||
pub trait SysControlSocket {
|
||||
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr>;
|
||||
}
|
||||
|
||||
impl SysControlSocket for socket2::Socket {
|
||||
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr> {
|
||||
let mut info = sys::ctl_info {
|
||||
ctl_id: 0,
|
||||
ctl_name: [0; 96],
|
||||
};
|
||||
info.ctl_name[..name.len()].copy_from_slice(name.as_bytes());
|
||||
|
||||
unsafe { sys::resolve_ctl_info(self.as_raw_fd(), &mut info as *mut sys::ctl_info)? };
|
||||
|
||||
let (_, addr) = unsafe {
|
||||
socket2::SockAddr::init(|addr_storage, len| {
|
||||
*len = size_of::<sockaddr_ctl>() as u32;
|
||||
|
||||
let mut addr: &mut sockaddr_ctl = &mut *addr_storage.cast();
|
||||
addr.sc_len = *len as u8;
|
||||
addr.sc_family = AF_SYSTEM as u8;
|
||||
addr.ss_sysaddr = AF_SYS_CONTROL as u16;
|
||||
addr.sc_id = info.ctl_id;
|
||||
addr.sc_unit = index;
|
||||
Ok(())
|
||||
})
|
||||
}?;
|
||||
|
||||
Ok(addr)
|
||||
}
|
||||
}
|
||||
|
||||
mod sys {
|
||||
use nix::ioctl_readwrite;
|
||||
|
||||
const MAX_KCTL_NAME: usize = 96;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ctl_info {
|
||||
pub ctl_id: u32,
|
||||
pub ctl_name: [u8; MAX_KCTL_NAME],
|
||||
}
|
||||
|
||||
ioctl_readwrite!(resolve_ctl_info, b'N', 3, ctl_info);
|
||||
}
|
||||
90
tun/src/apple/mod.rs
Normal file
90
tun/src/apple/mod.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
use socket2::SockAddr;
|
||||
use std::io::Result;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
|
||||
mod kern_control;
|
||||
mod queue;
|
||||
|
||||
pub use queue::TunQueue;
|
||||
|
||||
use crate::syscall;
|
||||
use crate::unix::copy_if_name;
|
||||
use kern_control::SysControlSocket;
|
||||
|
||||
pub struct TunInterface {
|
||||
socket: socket2::Socket,
|
||||
}
|
||||
|
||||
impl TunInterface {
|
||||
pub fn new() -> Result<TunInterface> {
|
||||
TunInterface::connect(None)
|
||||
}
|
||||
|
||||
fn connect(addr: Option<SockAddr>) -> Result<TunInterface> {
|
||||
use socket2::{Domain, Protocol, Socket, Type};
|
||||
|
||||
let socket = Socket::new(
|
||||
Domain::from(libc::AF_SYSTEM),
|
||||
Type::DGRAM,
|
||||
Some(Protocol::from(libc::SYSPROTO_CONTROL)),
|
||||
)?;
|
||||
let addr = match addr {
|
||||
Some(addr) => addr,
|
||||
None => socket.resolve(sys::UTUN_CONTROL_NAME, 0)?,
|
||||
};
|
||||
socket.connect(&addr)?;
|
||||
|
||||
Ok(TunInterface { socket })
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Result<String> {
|
||||
let mut buf = [0u8; libc::IFNAMSIZ];
|
||||
let mut len = buf.len() as libc::socklen_t;
|
||||
syscall!(getsockopt(
|
||||
self.as_raw_fd(),
|
||||
libc::SYSPROTO_CONTROL,
|
||||
sys::UTUN_OPT_IFNAME,
|
||||
buf.as_mut_ptr() as *mut libc::c_void,
|
||||
&mut len,
|
||||
))?;
|
||||
let name = copy_if_name(buf);
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
pub fn queue(&self) -> Result<TunQueue> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for TunInterface {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.socket.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for TunInterface {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.socket.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
mod sys {
|
||||
pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control";
|
||||
|
||||
pub const UTUN_OPT_IFNAME: libc::c_int = 2;
|
||||
|
||||
/// Copied from https://github.com/rust-lang/socket2/blob/61314a231f73964b3db969ef72c0e9479df320f3/src/sys/unix.rs#L168-L178
|
||||
/// getsockopt is not exposed by socket2
|
||||
#[macro_export]
|
||||
macro_rules! syscall {
|
||||
($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
let res = unsafe { libc::$fn($($arg, )*) };
|
||||
if res == -1 {
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(res)
|
||||
}
|
||||
}};
|
||||
}
|
||||
}
|
||||
17
tun/src/apple/queue.rs
Normal file
17
tun/src/apple/queue.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
|
||||
pub struct TunQueue {
|
||||
socket: socket2::Socket,
|
||||
}
|
||||
|
||||
impl AsRawFd for TunQueue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.socket.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for TunQueue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.socket.into_raw_fd()
|
||||
}
|
||||
}
|
||||
16
tun/src/lib.rs
Normal file
16
tun/src/lib.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#[cfg(target_vendor = "apple")]
|
||||
#[path = "apple/mod.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[path = "linux.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "windows/mod.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||
pub(crate) mod unix;
|
||||
|
||||
pub use imp::{TunInterface, TunQueue};
|
||||
56
tun/src/linux.rs
Normal file
56
tun/src/linux.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use std::fs::OpenOptions;
|
||||
use std::io::Result;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
||||
|
||||
use crate::unix::copy_if_name;
|
||||
|
||||
pub struct TunInterface {
|
||||
inner: socket2::Socket,
|
||||
}
|
||||
|
||||
impl TunInterface {
|
||||
pub fn new() -> Result<TunInterface> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/net/tun")?;
|
||||
|
||||
let mut iff = libc::ifreq {
|
||||
ifr_name: [0; libc::IFNAMSIZ],
|
||||
ifr_ifru: libc::__c_anonymous_ifr_ifru {
|
||||
ifru_flags: (libc::IFF_TUN | libc::IFF_TUN_EXCL | libc::IFF_NO_PI) as i16,
|
||||
},
|
||||
};
|
||||
unsafe { sys::tun_set_iff(file.as_raw_fd(), &mut iff)? };
|
||||
|
||||
let inner = unsafe { socket2::Socket::from_raw_fd(file.into_raw_fd()) };
|
||||
Ok(TunInterface { inner })
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Result<String> {
|
||||
let mut iff = libc::ifreq {
|
||||
ifr_name: [0; libc::IFNAMSIZ],
|
||||
ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 },
|
||||
};
|
||||
unsafe { sys::tun_get_iff(self.inner.as_raw_fd(), &mut iff)? };
|
||||
|
||||
let name = copy_if_name(iff.ifr_name);
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
mod sys {
|
||||
use nix::{ioctl_read_bad, ioctl_write_ptr_bad, request_code_read, request_code_write};
|
||||
use std::mem::size_of;
|
||||
|
||||
ioctl_write_ptr_bad!(
|
||||
tun_set_iff,
|
||||
request_code_write!(b'T', 202, size_of::<libc::c_int>()),
|
||||
libc::ifreq
|
||||
);
|
||||
ioctl_read_bad!(
|
||||
tun_get_iff,
|
||||
request_code_read!(b'T', 210, size_of::<libc::c_int>()),
|
||||
libc::ifreq
|
||||
);
|
||||
}
|
||||
1
tun/src/queue.rs
Normal file
1
tun/src/queue.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
11
tun/src/unix.rs
Normal file
11
tun/src/unix.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use std::ffi::CStr;
|
||||
|
||||
pub fn copy_if_name(buf: [u8; libc::IFNAMSIZ]) -> String {
|
||||
// TODO: Switch to `CStr::from_bytes_until_nul` when stabilized
|
||||
unsafe {
|
||||
CStr::from_ptr(buf.as_ptr() as *const _)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
44
tun/src/windows/mod.rs
Normal file
44
tun/src/windows/mod.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use std::io::Result;
|
||||
use std::ptr;
|
||||
use widestring::{u16cstr, U16CString};
|
||||
|
||||
pub struct TunInterface {
|
||||
wintun: sys::wintun,
|
||||
handle: sys::WINTUN_ADAPTER_HANDLE,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl TunInterface {
|
||||
pub fn new() -> Result<TunInterface> {
|
||||
let name = U16CString::from(u16cstr!("ConradNet"));
|
||||
let wintun = sys::wintun::default();
|
||||
let handle =
|
||||
unsafe { wintun.WintunCreateAdapter(name.as_ptr(), name.as_ptr(), ptr::null()) };
|
||||
Ok(TunInterface {
|
||||
wintun,
|
||||
handle,
|
||||
name: String::from("ConradNet"),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TunInterface {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.wintun.WintunCloseAdapter(self.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sys {
|
||||
#![allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/wintun.rs"));
|
||||
|
||||
impl Default for wintun {
|
||||
fn default() -> Self {
|
||||
unsafe { wintun::new(format!("{}/wintun.dll", env!("OUT_DIR"))).unwrap() }
|
||||
}
|
||||
}
|
||||
}
|
||||
19
tun/src/windows/queue.rs
Normal file
19
tun/src/windows/queue.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use crate::TunInterface;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
|
||||
pub struct TunQueue {
|
||||
inner: crate::windows::sys::
|
||||
inner: socket2::Socket,
|
||||
}
|
||||
|
||||
impl AsRawFd for TunQueue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.socket.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for TunQueue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.socket.into_raw_fd()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue