wip
This commit is contained in:
parent
b37086e8f6
commit
1a13b77295
20 changed files with 767 additions and 468 deletions
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
|
@ -7,7 +7,7 @@
|
||||||
"files.trimTrailingWhitespace": true,
|
"files.trimTrailingWhitespace": true,
|
||||||
"editor.suggest.preview": true,
|
"editor.suggest.preview": true,
|
||||||
"editor.acceptSuggestionOnEnter": "on",
|
"editor.acceptSuggestionOnEnter": "on",
|
||||||
"rust-analyzer.checkOnSave.command": "clippy",
|
"rust-analyzer.check.command": "clippy",
|
||||||
"rust-analyzer.restartServerOnConfigChange": true,
|
"rust-analyzer.restartServerOnConfigChange": true,
|
||||||
"rust-analyzer.cargo.features": "all",
|
"rust-analyzer.cargo.features": "all",
|
||||||
"[rust]": {
|
"[rust]": {
|
||||||
|
|
|
||||||
617
Cargo.lock
generated
617
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,8 +3,8 @@ name = "burrow"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
fehler = "1.0"
|
||||||
tokio = { version = "1.21", features = ["rt", "macros"] }
|
tokio = { version = "1.21", features = ["rt", "macros"] }
|
||||||
tun = { version = "0.1", path = "../tun" }
|
tun = { version = "0.1", path = "../tun" }
|
||||||
|
tun-async = { version = "0.1", path = "../tun-async" }
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,25 @@
|
||||||
|
use std::{mem::MaybeUninit, net::Ipv4Addr};
|
||||||
|
|
||||||
use tokio::io::Result;
|
use tokio::io::Result;
|
||||||
use tun::TunInterface;
|
use tun::TunInterface;
|
||||||
|
use tun_async::TunQueue;
|
||||||
|
|
||||||
async fn lol() -> Result<()> {
|
async fn try_main() -> Result<()> {
|
||||||
let iface = TunInterface::new()?;
|
let iface = TunInterface::new()?;
|
||||||
println!("{:?}", iface.name());
|
iface.set_ipv4_addr(Ipv4Addr::new(10, 0, 0, 2))?;
|
||||||
|
println!("{:?}", iface.index()?);
|
||||||
|
println!("{:?}", iface.ipv4_addr()?);
|
||||||
|
|
||||||
Ok(())
|
let queue = TunQueue::from_queue(iface.into())?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut buf = [MaybeUninit::<u8>::uninit(); 1500];
|
||||||
|
let len = queue.try_recv(&mut buf).await?;
|
||||||
|
println!("Received {len} bytes");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
lol().await.unwrap();
|
try_main().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,11 @@ name = "tun-async"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
[features]
|
||||||
|
default = ["tokio"]
|
||||||
|
tokio = ["dep:tokio"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tun = { version = "0.1", path = "../tun" }
|
fehler = "1.0"
|
||||||
|
tun = { path = "../tun" }
|
||||||
|
tokio = { version = "1.0", features = ["net"], optional = true }
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,5 @@
|
||||||
pub fn add(left: usize, right: usize) -> usize {
|
#[cfg(feature = "tokio")]
|
||||||
left + right
|
#[path = "tokio/mod.rs"]
|
||||||
}
|
pub(crate) mod imp;
|
||||||
|
|
||||||
#[cfg(test)]
|
pub use imp::TunQueue;
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = add(2, 2);
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
29
tun-async/src/tokio/mod.rs
Normal file
29
tun-async/src/tokio/mod.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
use fehler::throws;
|
||||||
|
use std::{
|
||||||
|
io::{self, Error},
|
||||||
|
mem::MaybeUninit,
|
||||||
|
};
|
||||||
|
use tokio::io::unix::AsyncFd;
|
||||||
|
|
||||||
|
pub struct TunQueue {
|
||||||
|
io: AsyncFd<tun::TunQueue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TunQueue {
|
||||||
|
#[throws]
|
||||||
|
pub fn from_queue(queue: tun::TunQueue) -> Self {
|
||||||
|
Self {
|
||||||
|
io: AsyncFd::new(queue)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn try_recv(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
|
||||||
|
loop {
|
||||||
|
let mut guard = self.io.readable().await?;
|
||||||
|
match guard.try_io(|inner| inner.get_ref().recv(buf)) {
|
||||||
|
Ok(result) => return result,
|
||||||
|
Err(..) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
fehler = "1.0"
|
||||||
nix = { version = "0.25", features = ["ioctl"] }
|
nix = { version = "0.25", features = ["ioctl"] }
|
||||||
socket2 = "0.4"
|
socket2 = "0.4"
|
||||||
tokio = { version = "1.21", features = [] }
|
tokio = { version = "1.21", features = [] }
|
||||||
|
|
@ -20,5 +21,5 @@ hex-literal = "0.3"
|
||||||
platforms = "3.0"
|
platforms = "3.0"
|
||||||
reqwest = { version = "0.11", features = ["native-tls"] }
|
reqwest = { version = "0.11", features = ["native-tls"] }
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
tokio = { version = "1.21", features = ["rt"] }
|
tokio = { version = "1.0", features = ["rt"] }
|
||||||
zip = { version = "0.6", features = ["deflate"] }
|
zip = { version = "0.6", features = ["deflate"] }
|
||||||
|
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
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 = [0i8; 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)
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +1,9 @@
|
||||||
#[cfg(target_vendor = "apple")]
|
|
||||||
#[path = "apple/mod.rs"]
|
|
||||||
mod imp;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
#[path = "linux.rs"]
|
|
||||||
mod imp;
|
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
#[path = "windows/mod.rs"]
|
#[path = "windows/mod.rs"]
|
||||||
mod imp;
|
mod imp;
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||||
pub(crate) mod unix;
|
#[path = "unix/mod.rs"]
|
||||||
|
pub(crate) mod imp;
|
||||||
|
|
||||||
pub use imp::{TunInterface, TunQueue};
|
pub use imp::{TunInterface, TunQueue};
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
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 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(), &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_uint>()),
|
|
||||||
libc::ifreq
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TunQueue;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
use std::ffi::{c_char, CStr};
|
|
||||||
|
|
||||||
pub fn copy_if_name(buf: [c_char; 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
use fehler::throws;
|
||||||
use libc::{sockaddr_ctl, AF_SYSTEM, AF_SYS_CONTROL};
|
use libc::{sockaddr_ctl, AF_SYSTEM, AF_SYS_CONTROL};
|
||||||
use std::io::Result;
|
use std::io::Error;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
|
|
@ -7,11 +8,13 @@ use std::os::unix::io::AsRawFd;
|
||||||
///
|
///
|
||||||
/// Pulled from XNU source: https://github.com/apple/darwin-xnu/blob/main/bsd/sys/kern_control.h
|
/// Pulled from XNU source: https://github.com/apple/darwin-xnu/blob/main/bsd/sys/kern_control.h
|
||||||
pub trait SysControlSocket {
|
pub trait SysControlSocket {
|
||||||
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr>;
|
#[throws]
|
||||||
|
fn resolve(&self, name: &str, index: u32) -> socket2::SockAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SysControlSocket for socket2::Socket {
|
impl SysControlSocket for socket2::Socket {
|
||||||
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr> {
|
#[throws]
|
||||||
|
fn resolve(&self, name: &str, index: u32) -> socket2::SockAddr {
|
||||||
let mut info = sys::ctl_info {
|
let mut info = sys::ctl_info {
|
||||||
ctl_id: 0,
|
ctl_id: 0,
|
||||||
ctl_name: [0; 96],
|
ctl_name: [0; 96],
|
||||||
|
|
@ -34,7 +37,7 @@ impl SysControlSocket for socket2::Socket {
|
||||||
})
|
})
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
Ok(addr)
|
addr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
98
tun/src/unix/apple/mod.rs
Normal file
98
tun/src/unix/apple/mod.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
use fehler::throws;
|
||||||
|
use libc::c_char;
|
||||||
|
use socket2::{Domain, SockAddr};
|
||||||
|
use std::mem;
|
||||||
|
use std::net::SocketAddrV4;
|
||||||
|
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||||
|
|
||||||
|
mod kern_control;
|
||||||
|
mod sys;
|
||||||
|
|
||||||
|
pub use super::queue::TunQueue;
|
||||||
|
|
||||||
|
use super::{ifname_to_string, string_to_ifname};
|
||||||
|
use kern_control::SysControlSocket;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TunInterface {
|
||||||
|
pub(crate) socket: socket2::Socket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TunInterface {
|
||||||
|
#[throws]
|
||||||
|
pub fn new() -> TunInterface {
|
||||||
|
TunInterface::connect(0)?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
fn connect(index: u32) -> 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 = socket.resolve(sys::UTUN_CONTROL_NAME, index)?;
|
||||||
|
socket.connect(&addr)?;
|
||||||
|
|
||||||
|
TunInterface { socket }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
let mut buf = [0 as c_char; libc::IFNAMSIZ];
|
||||||
|
let mut len = buf.len() as libc::socklen_t;
|
||||||
|
sys::syscall!(getsockopt(
|
||||||
|
self.as_raw_fd(),
|
||||||
|
libc::SYSPROTO_CONTROL,
|
||||||
|
sys::UTUN_OPT_IFNAME,
|
||||||
|
buf.as_mut_ptr() as *mut libc::c_void,
|
||||||
|
&mut len,
|
||||||
|
))?;
|
||||||
|
ifname_to_string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub fn index(&self) -> i32 {
|
||||||
|
let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
|
||||||
|
unsafe {
|
||||||
|
let mut iff: libc::ifreq = mem::zeroed();
|
||||||
|
iff.ifr_name = string_to_ifname(self.name()?);
|
||||||
|
sys::if_get_index(socket.as_raw_fd(), &mut iff)?;
|
||||||
|
iff.ifr_ifru.ifru_ifindex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub fn set_addr(&self, addr: IpAddr) {
|
||||||
|
match addr {
|
||||||
|
IpAddr::V4(addr) => self.set_ipv4_addr(addr)?,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub fn set_ipv4_addr(&self, addr: Ipv4Addr) {
|
||||||
|
let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
|
||||||
|
let addr = SockAddr::from(SocketAddrV4::new(addr, 0));
|
||||||
|
unsafe {
|
||||||
|
let mut iff: libc::ifreq = mem::zeroed();
|
||||||
|
iff.ifr_name = string_to_ifname(self.name()?);
|
||||||
|
iff.ifr_ifru.ifru_addr = *addr.as_ptr();
|
||||||
|
sys::if_set_addr(socket.as_raw_fd(), &iff)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
22
tun/src/unix/apple/sys.rs
Normal file
22
tun/src/unix/apple/sys.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control";
|
||||||
|
pub const UTUN_OPT_IFNAME: libc::c_int = 2;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ifreq {}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use syscall;
|
||||||
87
tun/src/unix/linux/mod.rs
Normal file
87
tun/src/unix/linux/mod.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
use fehler::throws;
|
||||||
|
|
||||||
|
use socket2::{Domain, SockAddr, Socket, Type};
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Error;
|
||||||
|
use std::mem;
|
||||||
|
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||||
|
use std::os::fd::RawFd;
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
||||||
|
|
||||||
|
|
||||||
|
use super::{ifname_to_string, string_to_ifname};
|
||||||
|
|
||||||
|
mod sys;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TunInterface {
|
||||||
|
pub(crate) socket: socket2::Socket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TunInterface {
|
||||||
|
#[throws]
|
||||||
|
pub fn new() -> TunInterface {
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open("/dev/net/tun")?;
|
||||||
|
|
||||||
|
let 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(), &iff)? };
|
||||||
|
|
||||||
|
let socket = unsafe { socket2::Socket::from_raw_fd(file.into_raw_fd()) };
|
||||||
|
TunInterface { socket }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
let mut iff = unsafe { mem::zeroed() };
|
||||||
|
unsafe { sys::tun_get_iff(self.socket.as_raw_fd(), &mut iff)? };
|
||||||
|
ifname_to_string(iff.ifr_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
fn ifreq(&self) -> sys::ifreq {
|
||||||
|
let mut iff: sys::ifreq = unsafe { mem::zeroed() };
|
||||||
|
iff.ifr_name = string_to_ifname(&self.name()?);
|
||||||
|
iff
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub fn index(&self) -> i32 {
|
||||||
|
let mut iff = self.ifreq()?;
|
||||||
|
self.perform(|fd| unsafe { sys::if_get_index(fd, &mut iff) })?;
|
||||||
|
unsafe { iff.ifr_ifru.ifru_ifindex }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub fn set_ipv4_addr(&self, addr: Ipv4Addr) {
|
||||||
|
let addr = SockAddr::from(SocketAddrV4::new(addr, 0));
|
||||||
|
|
||||||
|
let mut iff = self.ifreq()?;
|
||||||
|
iff.ifr_ifru.ifru_addr = unsafe { *addr.as_ptr() };
|
||||||
|
|
||||||
|
self.perform(|fd| unsafe { sys::if_set_addr(fd, &iff) })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
||||||
|
let mut iff = self.ifreq()?;
|
||||||
|
self.perform(|fd| unsafe { sys::if_get_addr(fd, &mut iff) })?;
|
||||||
|
|
||||||
|
let addr = unsafe { &*(&iff.ifr_ifru.ifru_addr as *const _ as *const sys::sockaddr_in) };
|
||||||
|
|
||||||
|
Ipv4Addr::from(addr.sin_addr.s_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
fn perform<R>(&self, perform: impl FnOnce(RawFd) -> Result<R, nix::Error>) -> R {
|
||||||
|
let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
|
||||||
|
perform(socket.as_raw_fd())?
|
||||||
|
}
|
||||||
|
}
|
||||||
19
tun/src/unix/linux/sys.rs
Normal file
19
tun/src/unix/linux/sys.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
use nix::{ioctl_read_bad, ioctl_write_ptr_bad, request_code_read, request_code_write};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
pub use libc::ifreq;
|
||||||
|
pub use libc::sockaddr_in;
|
||||||
|
|
||||||
|
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_uint>()),
|
||||||
|
libc::ifreq
|
||||||
|
);
|
||||||
|
ioctl_read_bad!(if_get_index, libc::SIOCGIFINDEX, libc::ifreq);
|
||||||
|
ioctl_read_bad!(if_get_addr, libc::SIOCGIFADDR, libc::ifreq);
|
||||||
|
ioctl_write_ptr_bad!(if_set_addr, libc::SIOCSIFADDR, libc::ifreq);
|
||||||
51
tun/src/unix/mod.rs
Normal file
51
tun/src/unix/mod.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
mod queue;
|
||||||
|
|
||||||
|
#[cfg(target_vendor = "apple")]
|
||||||
|
#[path = "apple/mod.rs"]
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[path = "linux/mod.rs"]
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub use imp::TunInterface;
|
||||||
|
|
||||||
|
pub use queue::TunQueue;
|
||||||
|
|
||||||
|
pub fn ifname_to_string(buf: [libc::c_char; libc::IFNAMSIZ]) -> String {
|
||||||
|
// TODO: Switch to `CStr::from_bytes_until_nul` when stabilized
|
||||||
|
unsafe {
|
||||||
|
std::ffi::CStr::from_ptr(buf.as_ptr() as *const _)
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string_to_ifname(name: &str) -> [libc::c_char; libc::IFNAMSIZ] {
|
||||||
|
let mut buf = [0 as libc::c_char; libc::IFNAMSIZ];
|
||||||
|
let len = name.len().min(buf.len());
|
||||||
|
buf[..len].copy_from_slice(unsafe { &*(name.as_bytes() as *const _ as *const [libc::c_char]) });
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
mod sys {
|
||||||
|
/// 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! call {
|
||||||
|
($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)
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use call;
|
||||||
|
}
|
||||||
59
tun/src/unix/queue.rs
Normal file
59
tun/src/unix/queue.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use fehler::throws;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
io::{Error, Read, Write},
|
||||||
|
mem::MaybeUninit,
|
||||||
|
os::unix::io::{AsRawFd, IntoRawFd, RawFd},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::TunInterface;
|
||||||
|
|
||||||
|
pub struct TunQueue {
|
||||||
|
socket: socket2::Socket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TunQueue {
|
||||||
|
#[throws]
|
||||||
|
pub fn recv(&self, buf: &mut [MaybeUninit<u8>]) -> usize {
|
||||||
|
self.socket.recv(buf)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for TunQueue {
|
||||||
|
#[throws]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> usize {
|
||||||
|
self.socket.read(buf)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for TunQueue {
|
||||||
|
#[throws]
|
||||||
|
fn write(&mut self, buf: &[u8]) -> usize {
|
||||||
|
self.socket.write(buf)?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
fn flush(&mut self) {
|
||||||
|
self.socket.flush()?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TunInterface> for TunQueue {
|
||||||
|
fn from(interface: TunInterface) -> TunQueue {
|
||||||
|
TunQueue {
|
||||||
|
socket: interface.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