Compare commits
No commits in common. "8fefd54d9095e94c2af3fd245c0aeba5c93cf7ad" and "b37086e8f6ac09fc1811b6aa0dc82b6c1bf31581" have entirely different histories.
8fefd54d90
...
b37086e8f6
20 changed files with 467 additions and 805 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.check.command": "clippy",
|
"rust-analyzer.checkOnSave.command": "clippy",
|
||||||
"rust-analyzer.restartServerOnConfigChange": true,
|
"rust-analyzer.restartServerOnConfigChange": true,
|
||||||
"rust-analyzer.cargo.features": "all",
|
"rust-analyzer.cargo.features": "all",
|
||||||
"[rust]": {
|
"[rust]": {
|
||||||
|
|
|
||||||
611
Cargo.lock
generated
611
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,23 +1,14 @@
|
||||||
use std::{mem::MaybeUninit, net::Ipv4Addr};
|
|
||||||
|
|
||||||
use tokio::io::Result;
|
use tokio::io::Result;
|
||||||
use tun::TunInterface;
|
use tun::TunInterface;
|
||||||
use tun::TunQueue;
|
|
||||||
|
|
||||||
async fn try_main() -> Result<()> {
|
async fn lol() -> Result<()> {
|
||||||
let iface = tun::create_interface();
|
let iface = TunInterface::new()?;
|
||||||
iface.set_ipv4(Ipv4Addr::new(10, 0, 0, 2))?;
|
println!("{:?}", iface.name());
|
||||||
|
|
||||||
let queue = TunQueue::from(iface);
|
Ok(())
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut buf = [MaybeUninit::<u8>::uninit(); 1500];
|
|
||||||
let len = queue.recv(&mut buf)?;
|
|
||||||
println!("Received {len} bytes");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
try_main().await.unwrap();
|
lol().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,7 @@ name = "tun-async"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[features]
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
default = ["tokio"]
|
|
||||||
tokio = ["dep:tokio"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fehler = "1.0"
|
tun = { version = "0.1", path = "../tun" }
|
||||||
tun = { path = "../tun" }
|
|
||||||
tokio = { version = "1.0", features = ["net"], optional = true }
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
#[cfg(feature = "tokio")]
|
pub fn add(left: usize, right: usize) -> usize {
|
||||||
#[path = "tokio/mod.rs"]
|
left + right
|
||||||
pub(crate) mod imp;
|
}
|
||||||
|
|
||||||
pub use imp::TunQueue;
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
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,7 +5,6 @@ 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 = [] }
|
||||||
|
|
@ -21,5 +20,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.0", features = ["rt"] }
|
tokio = { version = "1.21", features = ["rt"] }
|
||||||
zip = { version = "0.6", features = ["deflate"] }
|
zip = { version = "0.6", features = ["deflate"] }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use fehler::throws;
|
|
||||||
use libc::{sockaddr_ctl, AF_SYSTEM, AF_SYS_CONTROL};
|
use libc::{sockaddr_ctl, AF_SYSTEM, AF_SYS_CONTROL};
|
||||||
use std::io::Error;
|
use std::io::Result;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
|
|
@ -8,13 +7,11 @@ 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 {
|
||||||
#[throws]
|
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr>;
|
||||||
fn resolve(&self, name: &str, index: u32) -> socket2::SockAddr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SysControlSocket for socket2::Socket {
|
impl SysControlSocket for socket2::Socket {
|
||||||
#[throws]
|
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr> {
|
||||||
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],
|
||||||
|
|
@ -37,7 +34,7 @@ impl SysControlSocket for socket2::Socket {
|
||||||
})
|
})
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
addr
|
Ok(addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = [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)
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
}
|
||||||
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,42 +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")]
|
#[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"))]
|
||||||
#[path = "unix/mod.rs"]
|
pub(crate) mod unix;
|
||||||
pub(crate) mod imp;
|
|
||||||
|
|
||||||
/**
|
pub use imp::{TunInterface, TunQueue};
|
||||||
* Standard platform-independent interface for a tunnel.
|
|
||||||
*/
|
|
||||||
pub trait TunInterface {
|
|
||||||
/**
|
|
||||||
* Sets the interface IP address. Accepts either IPv6 or IPv4
|
|
||||||
*/
|
|
||||||
fn set_ip(&self, ip: IpAddr) -> Result<(), std::io::Error>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the interface IP address to an IPv4 address.
|
|
||||||
*
|
|
||||||
* Used by [set_ip](TunInterface::set_ip)
|
|
||||||
*/
|
|
||||||
fn set_ipv4(&self, ip: Ipv4Addr) -> Result<(), std::io::Error>;
|
|
||||||
|
|
||||||
fn get_ip(&self) -> Result<IpAddr, std::io::Error>;
|
|
||||||
|
|
||||||
fn get_interface_name(&self) -> Result<String, std::io::Error>;
|
|
||||||
|
|
||||||
fn into_raw_socket(self) -> socket2::Socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
pub fn create_interface() -> impl TunInterface {
|
|
||||||
PlatformTun::new().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
net::{IpAddr, Ipv4Addr},
|
|
||||||
sync::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use imp::{PlatformTun, TunQueue};
|
|
||||||
|
|
|
||||||
58
tun/src/linux.rs
Normal file
58
tun/src/linux.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
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;
|
||||||
11
tun/src/unix.rs
Normal file
11
tun/src/unix.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
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,98 +0,0 @@
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
use fehler::throws;
|
|
||||||
|
|
||||||
use socket2::{Domain, SockAddr, Socket, Type};
|
|
||||||
use std::fs::OpenOptions;
|
|
||||||
use std::io::Error;
|
|
||||||
use std::mem;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddrV4};
|
|
||||||
use std::os::fd::RawFd;
|
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
|
||||||
use std::ptr;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use super::{ifname_to_string, string_to_ifname};
|
|
||||||
use crate::TunInterface;
|
|
||||||
|
|
||||||
mod sys;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PlatformTun {
|
|
||||||
pub(crate) socket: socket2::Socket,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlatformTun {
|
|
||||||
#[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.get_interface_name()?);
|
|
||||||
sys::if_get_index(socket.as_raw_fd(), &mut iff)?;
|
|
||||||
iff.ifr_ifru.ifru_ifindex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[throws]
|
|
||||||
pub(crate) fn new() -> PlatformTun {
|
|
||||||
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()) };
|
|
||||||
PlatformTun { socket }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TunInterface for PlatformTun {
|
|
||||||
#[throws]
|
|
||||||
fn get_interface_name(&self) -> String {
|
|
||||||
unsafe {
|
|
||||||
let mut iff = mem::zeroed();
|
|
||||||
sys::tun_get_iff(self.socket.as_raw_fd(), &mut iff)?;
|
|
||||||
ifname_to_string(iff.ifr_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
fn set_ip(&self, addr: IpAddr) {
|
|
||||||
match addr {
|
|
||||||
IpAddr::V4(addr) => self.set_ipv4(addr)?,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
fn set_ipv4(&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.get_interface_name()?);
|
|
||||||
iff.ifr_ifru.ifru_addr = *addr.as_ptr();
|
|
||||||
sys::if_set_addr(socket.as_raw_fd(), &iff)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
fn get_ip(&self) -> IpAddr {
|
|
||||||
let addr = self.socket.local_addr()?;
|
|
||||||
addr.as_socket().unwrap().ip()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_raw_socket(self) -> socket2::Socket {
|
|
||||||
self.socket
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
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);
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
mod queue;
|
|
||||||
|
|
||||||
#[cfg(target_vendor = "apple")]
|
|
||||||
#[path = "apple/mod.rs"]
|
|
||||||
mod imp;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
#[path = "linux/mod.rs"]
|
|
||||||
mod imp;
|
|
||||||
|
|
||||||
pub use crate::TunInterface;
|
|
||||||
pub use imp::PlatformTun;
|
|
||||||
use libc::IFNAMSIZ;
|
|
||||||
pub use queue::TunQueue;
|
|
||||||
use std::ffi::CString;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
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 TunQueue {
|
|
||||||
pub fn from<T: TunInterface>(interface: T) -> TunQueue {
|
|
||||||
TunQueue {
|
|
||||||
socket: interface.into_raw_socket(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRawFd for TunQueue {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
self.socket.as_raw_fd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: error[E0507]: cannot move out of `*self.socket` which is behind a mutable reference
|
|
||||||
|
|
||||||
//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