Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fefd54d90 | ||
|
|
1a13b77295 |
20 changed files with 808 additions and 470 deletions
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
|
@ -7,10 +7,10 @@
|
|||
"files.trimTrailingWhitespace": true,
|
||||
"editor.suggest.preview": true,
|
||||
"editor.acceptSuggestionOnEnter": "on",
|
||||
"rust-analyzer.checkOnSave.command": "clippy",
|
||||
"rust-analyzer.check.command": "clippy",
|
||||
"rust-analyzer.restartServerOnConfigChange": true,
|
||||
"rust-analyzer.cargo.features": "all",
|
||||
"[rust]": {
|
||||
"editor.defaultFormatter": "rust-lang.rust-analyzer",
|
||||
},
|
||||
}
|
||||
}
|
||||
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"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
fehler = "1.0"
|
||||
tokio = { version = "1.21", features = ["rt", "macros"] }
|
||||
tun = { version = "0.1", path = "../tun" }
|
||||
tun-async = { version = "0.1", path = "../tun-async" }
|
||||
|
|
|
|||
|
|
@ -1,14 +1,23 @@
|
|||
use std::{mem::MaybeUninit, net::Ipv4Addr};
|
||||
|
||||
use tokio::io::Result;
|
||||
use tun::TunInterface;
|
||||
use tun::TunQueue;
|
||||
|
||||
async fn lol() -> Result<()> {
|
||||
let iface = TunInterface::new()?;
|
||||
println!("{:?}", iface.name());
|
||||
async fn try_main() -> Result<()> {
|
||||
let iface = tun::create_interface();
|
||||
iface.set_ipv4(Ipv4Addr::new(10, 0, 0, 2))?;
|
||||
|
||||
Ok(())
|
||||
let queue = TunQueue::from(iface);
|
||||
|
||||
loop {
|
||||
let mut buf = [MaybeUninit::<u8>::uninit(); 1500];
|
||||
let len = queue.recv(&mut buf)?;
|
||||
println!("Received {len} bytes");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
lol().await.unwrap();
|
||||
try_main().await.unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ name = "tun-async"
|
|||
version = "0.1.0"
|
||||
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]
|
||||
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 {
|
||||
left + right
|
||||
}
|
||||
#[cfg(feature = "tokio")]
|
||||
#[path = "tokio/mod.rs"]
|
||||
pub(crate) mod imp;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
pub use imp::TunQueue;
|
||||
|
|
|
|||
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]
|
||||
libc = "0.2"
|
||||
fehler = "1.0"
|
||||
nix = { version = "0.25", features = ["ioctl"] }
|
||||
socket2 = "0.4"
|
||||
tokio = { version = "1.21", features = [] }
|
||||
|
|
@ -20,5 +21,5 @@ hex-literal = "0.3"
|
|||
platforms = "3.0"
|
||||
reqwest = { version = "0.11", features = ["native-tls"] }
|
||||
sha2 = "0.10"
|
||||
tokio = { version = "1.21", features = ["rt"] }
|
||||
tokio = { version = "1.0", features = ["rt"] }
|
||||
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,42 @@
|
|||
#[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;
|
||||
#[path = "unix/mod.rs"]
|
||||
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};
|
||||
|
|
|
|||
|
|
@ -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 std::io::Result;
|
||||
use std::io::Error;
|
||||
use std::mem::size_of;
|
||||
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
|
||||
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 {
|
||||
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 {
|
||||
ctl_id: 0,
|
||||
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;
|
||||
93
tun/src/unix/linux/mod.rs
Normal file
93
tun/src/unix/linux/mod.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
61
tun/src/unix/queue.rs
Normal file
61
tun/src/unix/queue.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
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