From 67461297df5aac1b0dee19de064fc930c741fdbe Mon Sep 17 00:00:00 2001 From: xmh0511 <970252187@qq.com> Date: Fri, 30 Aug 2024 13:48:19 +0800 Subject: [PATCH] V3 (#105) * compatible with low-version rustc * pass clippy * Bump version * prepare to support windows-tap * update * prepare to support windows-tap (#101) * compatible with low-version rustc * pass clippy * Bump version * prepare to support windows-tap * update * support windows-tap * support windows-tap * fix * cargo fmt * cargo clippy * cargo clippy * deliver layer2 for tap * deliver layer2 * Update device.rs * refine * fix codec * adjust 'AbstractDevice' * remove tap_packet * change * fix bsd error * fix bsd error * fix bsd error * fix bsd error * fix bsd error * fix bsd error * fix bsd error * fix linux error * cargo clippy * cargo clippy * fix linux error * fix cargo.toml * fix some errors * cargo fmt * fix windows example * fix windows example * Bump version * cargo clippy * fix error * fix error * fix error * update linux * update config * fix macos error * fix macos * update recv * config linux * fix some errors * Remove the MTU on Tun * fix error * fix macos * fix error * fix example * fix android * refine * fix windows async * fix async windows * fix freebsd * fix freebsd * update readme * Update README.md * windows async * windows shutdown * AsyncDevice Drop * update linux * Remove useless code * shutdown * tap drop * add shutdown * add shutdown * add shutdown * add shutdown * add feature experimental * add feature experimental * add feature experimental * add feature experimental * fix features * fix features * fix bug * remove shutdown * windows enable * windows enable * windows enable * update experimental * update experimental * cargo clippy * update experimental * add flag * recv Interest ERROR * address_with_prefix * use IntoAddress * clippy * set_destination * rename variable name * update cargo.toml * cargo clippy ios&android * fix shutdown * update macos * update macos * refine macos route * update freebsd * update freebsd * update freebsd * update freebsd * update freebsd * update freebsd * update freebsd * add tap for freebsd * select fd * add tap for freebsd * update * update * update * select pipe macos * update readme * linux if_indextoname * Change method name * use write_all * ioctl_read tungetiff * fmt * fmt * linux from_raw_fd * fmt * get name from libc * test freebsd * revert freebsd * add into_raw_fd * freebsd * freebsd * freebsd * revert freebsd * platform from_raw_fd * packet_information default is true in iOS|macos * update freebsd * update freebsd * update freebsd * update freebsd * update freebsd * mac address * mac address * mac address * mac address * add set mac address for freebsd * update * update freebsd * update freebsd * update freebsd mac * open len * open mac_address for windows * linux set/get mac addr * linux set/get mac addr * linux set/get mac addr * windows tap * cargo fmt * cargo fmt * cargo clippy * cargo clippy windows * cargo clippy windows * remove println * map_err * windows code * ping-tap * cargo fmt * cargo fmt * cargo clippy --------- Co-authored-by: lubeilin <1791778603@qq.com> Co-authored-by: lbl <49143209+lbl8603@users.noreply.github.com> --- .cargo/config.toml | 2 + Cargo.toml | 37 +- README.md | 38 +- examples/dev-config.rs | 39 +- examples/ping-tap.rs | 263 ++++++++++++ examples/ping-tun.rs | 41 +- examples/read-async-codec.rs | 98 ----- examples/read-async.rs | 27 +- examples/read.rs | 39 +- examples/write.rs | 43 +- src/address.rs | 272 +++++++------ src/async/codec.rs | 53 --- src/async/mod.rs | 7 +- src/async/unix_device.rs | 105 +---- src/async/win_device.rs | 271 ++++--------- src/configuration.rs | 177 +++++---- src/device.rs | 61 ++- src/error.rs | 3 - src/lib.rs | 2 + src/platform/android/device.rs | 78 +--- src/platform/android/mod.rs | 68 ++-- src/platform/freebsd/device.rs | 457 +++++++++++---------- src/platform/freebsd/mod.rs | 72 ++-- src/platform/freebsd/sys.rs | 152 +++---- src/platform/ios/device.rs | 82 +--- src/platform/ios/mod.rs | 127 +++--- src/platform/linux/device.rs | 369 ++++++++--------- src/platform/linux/mod.rs | 130 +++--- src/platform/linux/sys.rs | 78 ++-- src/platform/macos/device.rs | 473 +++++++++++----------- src/platform/macos/mod.rs | 136 ++++--- src/platform/mod.rs | 215 ++++++---- src/platform/posix/fd.rs | 309 +++++++++++---- src/platform/posix/sockaddr.rs | 329 ++++++++-------- src/platform/posix/tun.rs | 97 ++--- src/platform/windows/device.rs | 636 +++++++++++++----------------- src/platform/windows/ffi.rs | 243 +++++++----- src/platform/windows/mod.rs | 6 +- src/platform/windows/netsh.rs | 42 +- src/platform/windows/tap/iface.rs | 304 ++++++++++++++ src/platform/windows/tap/mod.rs | 195 +++++++++ 41 files changed, 3334 insertions(+), 2842 deletions(-) create mode 100644 examples/ping-tap.rs delete mode 100644 examples/read-async-codec.rs delete mode 100644 src/async/codec.rs create mode 100644 src/platform/windows/tap/iface.rs create mode 100644 src/platform/windows/tap/mod.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 72b2f4a..649e7cf 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,7 +5,9 @@ protocol = "sparse" # target = ["x86_64-unknown-linux-musl"] # target = ["x86_64-unknown-linux-gnu"] # target = ["aarch64-linux-android"] +# target = ["x86_64-linux-android"] # target = ["aarch64-apple-ios"] +# target = ["x86_64-apple-ios"] # target = ["x86_64-pc-windows-msvc"] # target = ["x86_64-apple-darwin"] # target = ["x86_64-unknown-freebsd"] diff --git a/Cargo.toml b/Cargo.toml index 805aad8..6fcbfe2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,14 @@ rustversion = "1.0.17" nix = { version = "0.29", features = ["ioctl"] } [target.'cfg(target_os = "windows")'.dependencies] +scopeguard = "1.2.0" +winreg = "0.52.0" +c2rust-bitfields = "0.18" windows-sys = { version = "0.59", features = [ - "Win32_Networking_WinSock", + "Win32_Devices_DeviceAndDriverInstallation", + "Win32_Storage_FileSystem", + "Win32_System_Registry", + "Win32_Networking_WinSock", "Win32_NetworkManagement_Ndis", "Win32_Foundation", "Win32_Security", @@ -42,47 +48,42 @@ windows-sys = { version = "0.59", features = [ "Win32_UI_WindowsAndMessaging", "Win32_System_LibraryLoader", ] } -wintun = { version = "0.6", features = ["panic_on_unsent_packets"],package="wintun-bindings" } +wintun = { version = "0.6", features = ["panic_on_unsent_packets"], package = "wintun-bindings" } libloading = "0.8" -winapi = {version = "0.3",features = [ - "errhandlingapi", - "combaseapi", - "ioapiset", - "winioctl", - "setupapi", - "synchapi", - "netioapi", - "fileapi","handleapi","winerror","minwindef","ifdef","basetsd","winnt","winreg","winbase","minwinbase", - "impl-default" -]} [target.'cfg(any(target_os = "macos", target_os = "freebsd"))'.dependencies] ipnet = "2" +[target.'cfg(any(target_os = "linux", target_os = "freebsd",target_os = "windows"))'.dependencies] +mac_address = "1.1.7" + [dev-dependencies] ctrlc2 = { version = "3", features = ["tokio", "termination"] } env_logger = "0.11" futures = "0.3" packet = "0.1" serde_json = "1" -tokio = { version = "1", features = ["rt-multi-thread"] } +tokio = { version = "1", features = ["rt-multi-thread", "time"] } [features] default = ["wintun-dns"] async = ["tokio", "futures-core", "tokio-util"] wintun-dns = [] +experimental = [] [package.metadata.docs.rs] -features = ["async"] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] [[example]] name = "read-async" required-features = ["async"] -[[example]] -name = "read-async-codec" -required-features = ["async"] [[example]] name = "ping-tun" required-features = ["async"] + +[[example]] +name = "ping-tap" +required-features = ["async"] \ No newline at end of file diff --git a/README.md b/README.md index 5ca4113..fda53e0 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,11 @@ -TUN interfaces +Tun/Tap interfaces ============== [![Crates.io](https://img.shields.io/crates/v/tun2.svg)](https://crates.io/crates/tun2) ![tun2](https://docs.rs/tun2/badge.svg) ![WTFPL](http://img.shields.io/badge/license-WTFPL-blue.svg) -This crate allows the creation and usage of TUN interfaces, the aim is to make this cross-platform. - -> Since the original maintainer @meh is no longer interested in continuing to maintain -> [tun](https://crates.io/crates/tun) at [repo](https://github.com/meh/rust-tun), -> We (@ssrlive, @xmh0511) created the [tun2](https://github.com/ssrlive/rust-tun) branch repo and -> continued to actively update. Welcome to any interested contributor. -> If you want to be a co-contributor and publisher of [tun2](https://crates.io/crates/tun2), -> please contact me in [issues](https://github.com/ssrlive/rust-tun/issues). -> -> For me, a submitted PR has not been reviewed for a long time, -> cannot be merged to the main branch, and cannot be published. -> It is like a patient who has not been sutured on the operating table for a long time. -> This is a bad experience. -> I believe that many people feel the same. +This crate allows the creation and usage of Tun/Tap interfaces, the aim is to make this cross-platform. + Usage ----- @@ -25,14 +13,14 @@ First, add the following to your `Cargo.toml`: ```toml [dependencies] -tun2 = "2" +tun2 = "3" ``` If you want to use the TUN interface with mio/tokio, you need to enable the `async` feature: ```toml [dependencies] -tun2 = { version = "2", features = ["async"] } +tun2 = { version = "3", features = ["async"] } ``` Example @@ -51,17 +39,11 @@ fn main() -> Result<(), Box> { .destination((10, 0, 0, 1)) .up(); - #[cfg(target_os = "linux")] - config.platform_config(|config| { - // requiring root privilege to acquire complete functions - config.ensure_root_privileges(true); - }); - - let mut dev = tun2::create(&config)?; + let dev = tun2::create(&config)?; let mut buf = [0; 4096]; loop { - let amount = dev.read(&mut buf)?; + let amount = dev.recv(&mut buf)?; println!("{:?}", &buf[0..amount]); } } @@ -123,9 +105,9 @@ pub extern "C" fn start_tun(fd: std::os::raw::c_int) { cfg.platform_config(|p_cfg| { p_cfg.packet_information(true); }); - let mut tun = tun2::create_as_async(&cfg).unwrap(); - let mut framed = tun.into_framed(); - while let Some(packet) = framed.next().await { + let tun = tun2::create_as_async(&cfg).unwrap(); + let mut buf = [0u8;1500]; + while let Ok(packet) = tun.recv(& mut buf).await { ... } }); diff --git a/examples/dev-config.rs b/examples/dev-config.rs index 9caff46..2ea0802 100644 --- a/examples/dev-config.rs +++ b/examples/dev-config.rs @@ -11,12 +11,10 @@ // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION // // 0. You just DO WHAT THE FUCK YOU WANT TO. - +#[allow(unused_imports)] use packet::{builder::Builder, icmp, ip, Packet}; -use std::io::{Read, Write}; -#[cfg(not(target_os = "windows"))] -use std::net::Ipv4Addr; use std::sync::mpsc::Receiver; +#[allow(unused_imports)] use tun2::{AbstractDevice, BoxError}; fn main() -> Result<(), BoxError> { @@ -33,22 +31,25 @@ fn main() -> Result<(), BoxError> { handle.join().unwrap(); Ok(()) } - +#[cfg(any(target_os = "ios", target_os = "android",))] +fn main_entry(_quit: Receiver<()>) -> Result<(), BoxError> { + unimplemented!() +} +#[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd", +))] fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> { let mut config = tun2::Configuration::default(); config - .address((10, 0, 0, 9)) - .netmask((255, 255, 255, 0)) + .address_with_prefix((10, 0, 0, 9), 24) .destination((10, 0, 0, 1)) .up(); - #[cfg(target_os = "linux")] - config.platform_config(|config| { - config.ensure_root_privileges(true); - }); - - let mut dev = tun2::create(&config)?; + let dev = tun2::create(&config)?; let r = dev.address()?; println!("{:?}", r); @@ -57,13 +58,7 @@ fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> { let r = dev.netmask()?; println!("{:?}", r); - - #[cfg(not(target_os = "windows"))] - { - dev.set_address(std::net::IpAddr::V4(Ipv4Addr::new(10, 0, 0, 20)))?; - dev.set_destination(std::net::IpAddr::V4(Ipv4Addr::new(10, 0, 0, 66)))?; - dev.set_netmask(std::net::IpAddr::V4(Ipv4Addr::new(255, 255, 0, 0)))?; - } + dev.set_network_address((10, 0, 0, 2), (255, 255, 255, 0), None)?; dev.set_mtu(65535)?; //dev.set_tun_name("tun8")?; @@ -77,7 +72,7 @@ fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> { std::thread::spawn(move || { loop { - let amount = dev.read(&mut buf)?; + let amount = dev.recv(&mut buf)?; let pkt = &buf[0..amount]; match ip::Packet::new(pkt) { Ok(ip::Packet::V4(pkt)) => { @@ -96,7 +91,7 @@ fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> { .sequence(icmp.sequence())? .payload(icmp.payload())? .build()?; - let size = dev.write(&reply[..])?; + let size = dev.send(&reply[..])?; println!("write {size} len {}", reply.len()); } } diff --git a/examples/ping-tap.rs b/examples/ping-tap.rs new file mode 100644 index 0000000..6f16f69 --- /dev/null +++ b/examples/ping-tap.rs @@ -0,0 +1,263 @@ +use std::{fmt, io}; +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. +#[allow(unused_imports)] +use packet::{builder::Builder, icmp, ip, Packet}; +use packet::{ether, PacketMut}; +use tokio::sync::mpsc::Receiver; +#[allow(unused_imports)] +use tun2::Layer; +#[allow(unused_imports)] +use tun2::{self, AbstractDevice, BoxError, Configuration}; + +#[tokio::main] +async fn main() -> Result<(), BoxError> { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init(); + let (tx, rx) = tokio::sync::mpsc::channel::<()>(1); + + ctrlc2::set_async_handler(async move { + tx.send(()).await.expect("Signal error"); + }) + .await; + + main_entry(rx).await?; + Ok(()) +} +#[cfg(any(target_os = "ios", target_os = "android", target_os = "macos"))] +async fn main_entry(_quit: Receiver<()>) -> Result<(), BoxError> { + unimplemented!() +} +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd",))] +async fn main_entry(mut quit: Receiver<()>) -> Result<(), BoxError> { + let mut config = Configuration::default(); + + config + .address_with_prefix((10, 0, 0, 101), 24) + .layer(Layer::L2) + .up(); + + let dev = tun2::create_as_async(&config)?; + let mut buf = vec![0; 65536]; + loop { + tokio::select! { + _ = quit.recv() => { + println!("Quit..."); + break; + } + len = dev.recv(&mut buf) => { + let pkt: Vec = buf[..len?].to_vec(); + match ether::Packet::new(pkt){ + Ok(mut packet) => { + match packet.protocol(){ + ether::Protocol::Ipv4=>{ + if ping(&mut packet)?{ + dev.send(packet.as_ref()).await?; + } + } + ether::Protocol::Arp=>{ + if arp(&mut packet)?{ + dev.send(packet.as_ref()).await?; + } + } + protocol=>{ + println!("ignore ether protocol: {:?}", protocol) + } + } + } + Err(err) => { + println!("Received an invalid packet: {:?}", err) + } + } + } + } + } + Ok(()) +} +pub fn ping(packet: &mut ether::Packet>) -> Result { + let source = packet.source(); + let destination = packet.destination(); + + match ip::Packet::new(packet.payload()) { + Ok(ip::Packet::V4(pkt)) => { + if let Ok(icmp) = icmp::Packet::new(pkt.payload()) { + if let Ok(icmp) = icmp.echo() { + println!("{:?} - {:?}", icmp.sequence(), pkt.destination()); + let reply = ip::v4::Builder::default() + .id(0x42)? + .ttl(64)? + .source(pkt.destination())? + .destination(pkt.source())? + .icmp()? + .echo()? + .reply()? + .identifier(icmp.identifier())? + .sequence(icmp.sequence())? + .payload(icmp.payload())? + .build()?; + packet.payload_mut().copy_from_slice(&reply); + packet.set_destination(source)?; + packet.set_source(destination)?; + return Ok(true); + } + } + } + Err(err) => println!("Received an invalid packet: {:?}", err), + _ => {} + } + Ok(false) +} +pub fn arp(packet: &mut ether::Packet>) -> Result { + const MAC: [u8; 6] = [0xf, 0xf, 0xf, 0xf, 0xe, 0x9]; + let sender_h = packet.source(); + let target_h = packet.source(); + let mut arp_packet = ArpPacket::new(packet.payload_mut())?; + println!("arp_packet={:?}", arp_packet); + if arp_packet.op_code() != 1 { + return Ok(false); + } + let sender_p: [u8; 4] = arp_packet.sender_protocol_addr().try_into().unwrap(); + let target_p: [u8; 4] = arp_packet.target_protocol_addr().try_into().unwrap(); + if target_p == [0, 0, 0, 0] || sender_p == [0, 0, 0, 0] || target_p == sender_p { + return Ok(false); + } + arp_packet.set_op_code(2); + arp_packet.set_target_hardware_addr(&sender_h.octets()); + arp_packet.set_target_protocol_addr(&sender_p); + arp_packet.set_sender_protocol_addr(&target_p); + arp_packet.set_sender_hardware_addr(&MAC); + packet.set_destination(sender_h)?; + packet.set_source(target_h)?; + Ok(true) +} + +/// 地址解析协议,由IP地址找到MAC地址 +/// https://www.ietf.org/rfc/rfc6747.txt +/* + 0 2 4 5 6 8 10 (字节) + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 硬件类型|协议类型|硬件地址长度|协议地址长度|操作类型| + | 源MAC地址 | 源ip地址 | + | 目的MAC地址 | 目的ip地址 | +*/ + +pub struct ArpPacket { + buffer: B, +} + +impl> ArpPacket { + pub fn unchecked(buffer: B) -> Self { + Self { buffer } + } + pub fn new(buffer: B) -> io::Result { + if buffer.as_ref().len() != 28 { + Err(io::Error::from(io::ErrorKind::InvalidData))?; + } + let packet = Self::unchecked(buffer); + Ok(packet) + } +} + +impl> ArpPacket { + /// 硬件类型 以太网类型为1 + pub fn hardware_type(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap()) + } + /// 上层协议类型,ipv4是0x0800 + pub fn protocol_type(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap()) + } + /// 如果是MAC地址 则长度为6 + pub fn hardware_size(&self) -> u8 { + self.buffer.as_ref()[4] + } + /// 如果是IPv4 则长度为4 + pub fn protocol_size(&self) -> u8 { + self.buffer.as_ref()[5] + } + /// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应 + pub fn op_code(&self) -> u16 { + u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) + } + /// 发送端硬件地址,仅支持以太网 + pub fn sender_hardware_addr(&self) -> &[u8] { + &self.buffer.as_ref()[8..14] + } + /// 发送端协议地址,仅支持IPv4 + pub fn sender_protocol_addr(&self) -> &[u8] { + &self.buffer.as_ref()[14..18] + } + /// 接收端硬件地址,仅支持以太网 + pub fn target_hardware_addr(&self) -> &[u8] { + &self.buffer.as_ref()[18..24] + } + /// 接收端协议地址,仅支持IPv4 + pub fn target_protocol_addr(&self) -> &[u8] { + &self.buffer.as_ref()[24..28] + } +} + +impl + AsMut<[u8]>> ArpPacket { + /// 硬件类型 以太网类型为1 + pub fn set_hardware_type(&mut self, value: u16) { + self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes()) + } + /// 上层协议类型,ipv4是0x0800 + pub fn set_protocol_type(&mut self, value: u16) { + self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes()) + } + /// 如果是MAC地址 则长度为6 + pub fn set_hardware_size(&mut self, value: u8) { + self.buffer.as_mut()[4] = value + } + /// 如果是IPv4 则长度为4 + pub fn set_protocol_size(&mut self, value: u8) { + self.buffer.as_mut()[5] = value + } + /// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应 + pub fn set_op_code(&mut self, value: u16) { + self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes()) + } + /// 发送端硬件地址,仅支持以太网 + pub fn set_sender_hardware_addr(&mut self, buf: &[u8]) { + self.buffer.as_mut()[8..14].copy_from_slice(buf) + } + /// 发送端协议地址,仅支持IPv4 + pub fn set_sender_protocol_addr(&mut self, buf: &[u8]) { + self.buffer.as_mut()[14..18].copy_from_slice(buf) + } + /// 接收端硬件地址,仅支持以太网 + pub fn set_target_hardware_addr(&mut self, buf: &[u8]) { + self.buffer.as_mut()[18..24].copy_from_slice(buf) + } + /// 接收端协议地址,仅支持IPv4 + pub fn set_target_protocol_addr(&mut self, buf: &[u8]) { + self.buffer.as_mut()[24..28].copy_from_slice(buf) + } +} + +impl> fmt::Debug for ArpPacket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ArpPacket") + .field("hardware_type", &self.hardware_type()) + .field("protocol_type", &self.protocol_type()) + .field("hardware_size", &self.hardware_size()) + .field("protocol_size", &self.protocol_size()) + .field("op_code", &self.op_code()) + .field("sender_hardware_addr", &self.sender_hardware_addr()) + .field("sender_protocol_addr", &self.sender_protocol_addr()) + .field("target_hardware_addr", &self.target_hardware_addr()) + .field("target_protocol_addr", &self.target_protocol_addr()) + .finish() + } +} diff --git a/examples/ping-tun.rs b/examples/ping-tun.rs index 2641bb6..92de227 100644 --- a/examples/ping-tun.rs +++ b/examples/ping-tun.rs @@ -11,11 +11,11 @@ // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION // // 0. You just DO WHAT THE FUCK YOU WANT TO. - -use futures::{SinkExt, StreamExt}; +#[allow(unused_imports)] use packet::{builder::Builder, icmp, ip, Packet}; use tokio::sync::mpsc::Receiver; -use tun2::{self, BoxError, Configuration}; +#[allow(unused_imports)] +use tun2::{self, AbstractDevice, BoxError, Configuration}; #[tokio::main] async fn main() -> Result<(), BoxError> { @@ -30,40 +30,45 @@ async fn main() -> Result<(), BoxError> { main_entry(rx).await?; Ok(()) } - +#[cfg(any(target_os = "ios", target_os = "android",))] +async fn main_entry(_quit: Receiver<()>) -> Result<(), BoxError> { + unimplemented!() +} +#[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd", +))] async fn main_entry(mut quit: Receiver<()>) -> Result<(), BoxError> { let mut config = Configuration::default(); config - .address((10, 0, 0, 9)) - .netmask((255, 255, 255, 0)) + .address_with_prefix((10, 0, 0, 9), 24) .destination((10, 0, 0, 1)) .up(); - #[cfg(target_os = "linux")] + #[cfg(target_os = "windows")] config.platform_config(|config| { - #[allow(deprecated)] - config.packet_information(true); - config.ensure_root_privileges(true); + config.device_guid(9099482345783245345345_u128); }); - #[cfg(target_os = "windows")] + #[cfg(target_os = "macos")] config.platform_config(|config| { - config.device_guid(9099482345783245345345_u128); + config.packet_information(false); }); let dev = tun2::create_as_async(&config)?; - - let mut framed = dev.into_framed(); - + let size = dev.mtu()? as usize + tun2::PACKET_INFORMATION_LENGTH; + let mut buf = vec![0; size]; loop { tokio::select! { _ = quit.recv() => { println!("Quit..."); break; } - Some(packet) = framed.next() => { - let pkt: Vec = packet?; + len = dev.recv(&mut buf) => { + let pkt: Vec = buf[..len?].to_vec(); match ip::Packet::new(pkt) { Ok(ip::Packet::V4(pkt)) => { if let Ok(icmp) = icmp::Packet::new(pkt.payload()) { @@ -81,7 +86,7 @@ async fn main_entry(mut quit: Receiver<()>) -> Result<(), BoxError> { .sequence(icmp.sequence())? .payload(icmp.payload())? .build()?; - framed.send(reply).await?; + dev.send(&reply).await?; } } } diff --git a/examples/read-async-codec.rs b/examples/read-async-codec.rs deleted file mode 100644 index 96ce862..0000000 --- a/examples/read-async-codec.rs +++ /dev/null @@ -1,98 +0,0 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -use bytes::BytesMut; -use futures::StreamExt; -use packet::{ip::Packet, Error}; -use tokio::sync::mpsc::Receiver; -use tokio_util::codec::{Decoder, FramedRead}; -use tun2::BoxError; - -pub struct IPPacketCodec; - -impl Decoder for IPPacketCodec { - type Item = Packet; - type Error = Error; - - fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { - if buf.is_empty() { - return Ok(None); - } - - let buf = buf.split_to(buf.len()); - Ok(match Packet::no_payload(buf) { - Ok(pkt) => Some(pkt), - Err(err) => { - println!("error {:?}", err); - None - } - }) - } -} - -#[tokio::main] -async fn main() -> Result<(), BoxError> { - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init(); - let (tx, rx) = tokio::sync::mpsc::channel::<()>(1); - - ctrlc2::set_async_handler(async move { - tx.send(()).await.expect("Signal error"); - }) - .await; - - main_entry(rx).await?; - Ok(()) -} - -async fn main_entry(mut quit: Receiver<()>) -> Result<(), BoxError> { - let mut config = tun2::Configuration::default(); - - config - .address((10, 0, 0, 9)) - .netmask((255, 255, 255, 0)) - .destination((10, 0, 0, 1)) - .up(); - - #[cfg(target_os = "linux")] - config.platform_config(|config| { - #[allow(deprecated)] - config.packet_information(true); - config.ensure_root_privileges(true); - }); - - #[cfg(target_os = "windows")] - config.platform_config(|config| { - config.device_guid(9099482345783245345345_u128); - }); - - let dev = tun2::create_as_async(&config)?; - - let mut stream = FramedRead::new(dev, IPPacketCodec); - - loop { - tokio::select! { - _ = quit.recv() => { - println!("Quit..."); - break; - } - Some(packet) = stream.next() => { - match packet { - Ok(pkt) => println!("pkt: {:#?}", pkt), - Err(err) => panic!("Error: {:?}", err), - } - } - }; - } - Ok(()) -} diff --git a/examples/read-async.rs b/examples/read-async.rs index fdcc226..4f27b67 100644 --- a/examples/read-async.rs +++ b/examples/read-async.rs @@ -11,9 +11,11 @@ // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION // // 0. You just DO WHAT THE FUCK YOU WANT TO. +#[allow(unused_imports)] +use std::sync::Arc; -use tokio::io::AsyncReadExt; use tokio::sync::mpsc::Receiver; +#[allow(unused_imports)] use tun2::{AbstractDevice, BoxError}; #[tokio::main] @@ -29,23 +31,27 @@ async fn main() -> Result<(), BoxError> { main_entry(rx).await?; Ok(()) } +#[cfg(any(target_os = "ios", target_os = "android",))] +async fn main_entry(_quit: Receiver<()>) -> Result<(), BoxError> { + unimplemented!() +} +#[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd", +))] async fn main_entry(mut quit: Receiver<()>) -> Result<(), BoxError> { let mut config = tun2::Configuration::default(); config - .address((10, 0, 0, 9)) - .netmask((255, 255, 255, 0)) + .address_with_prefix((10, 0, 0, 9), 24) .destination((10, 0, 0, 1)) .mtu(tun2::DEFAULT_MTU) .up(); - #[cfg(target_os = "linux")] - config.platform_config(|config| { - config.ensure_root_privileges(true); - }); - - let mut dev = tun2::create_as_async(&config)?; + let dev = Arc::new(tun2::create_as_async(&config)?); let size = dev.mtu()? as usize + tun2::PACKET_INFORMATION_LENGTH; let mut buf = vec![0; size]; loop { @@ -54,7 +60,8 @@ async fn main_entry(mut quit: Receiver<()>) -> Result<(), BoxError> { println!("Quit..."); break; } - len = dev.read(&mut buf) => { + len = dev.recv(&mut buf) => { + println!("len = {len:?}"); println!("pkt: {:?}", &buf[..len?]); } }; diff --git a/examples/read.rs b/examples/read.rs index 36a47a7..63a6b6c 100644 --- a/examples/read.rs +++ b/examples/read.rs @@ -12,9 +12,12 @@ // // 0. You just DO WHAT THE FUCK YOU WANT TO. -use std::io::Read; use std::sync::mpsc::Receiver; -use tun2::BoxError; +use std::sync::Arc; +use tun2::{AbstractDevice, BoxError}; + +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd",))] +use tun2::Layer; fn main() -> Result<(), BoxError> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("trace")).init(); @@ -30,31 +33,41 @@ fn main() -> Result<(), BoxError> { handle.join().unwrap(); Ok(()) } - +#[cfg(any(target_os = "ios", target_os = "android",))] +fn main_entry(_quit: Receiver<()>) -> Result<(), BoxError> { + unimplemented!() +} +#[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd", +))] fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> { let mut config = tun2::Configuration::default(); + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd",))] + config.layer(Layer::L2); + config - .address((10, 0, 0, 9)) - .netmask((255, 255, 255, 0)) + .address_with_prefix((10, 0, 0, 39), 24) .destination((10, 0, 0, 1)) + .name("tun39") .up(); - #[cfg(target_os = "linux")] - config.platform_config(|config| { - config.ensure_root_privileges(true); - }); - - let mut dev = tun2::create(&config)?; - std::thread::spawn(move || { + let dev = Arc::new(tun2::create(&config)?); + let dev_t = dev.clone(); + let join = std::thread::spawn(move || { let mut buf = [0; 4096]; loop { - let amount = dev.read(&mut buf)?; + let amount = dev.recv(&mut buf)?; println!("{:?}", &buf[0..amount]); } #[allow(unreachable_code)] Ok::<(), BoxError>(()) }); quit.recv().expect("Quit error."); + dev_t.enabled(false)?; + join.join().unwrap().unwrap(); Ok(()) } diff --git a/examples/write.rs b/examples/write.rs index 4dbd4e7..89d70de 100644 --- a/examples/write.rs +++ b/examples/write.rs @@ -11,10 +11,10 @@ // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION // // 0. You just DO WHAT THE FUCK YOU WANT TO. - +#[allow(unused_imports)] use packet::{builder::Builder, icmp, ip, Packet}; -use std::io::{Read, Write}; -use std::sync::mpsc::Receiver; +#[allow(unused_imports)] +use std::sync::{mpsc::Receiver, Arc}; use tun2::BoxError; fn main() -> Result<(), BoxError> { @@ -31,28 +31,47 @@ fn main() -> Result<(), BoxError> { handle.join().unwrap(); Ok(()) } - +#[cfg(any(target_os = "ios", target_os = "android",))] +fn main_entry(_quit: Receiver<()>) -> Result<(), BoxError> { + unimplemented!() +} +#[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd", +))] fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> { let mut config = tun2::Configuration::default(); config - .address((10, 0, 0, 9)) - .netmask((255, 255, 255, 0)) - .destination((10, 0, 0, 1)) + .address_with_prefix((10, 0, 0, 9), 24) + // .destination((10, 0, 0, 1)) .up(); - #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] config.platform_config(|config| { - config.ensure_root_privileges(true); + config.packet_information(false); }); - let mut dev = tun2::create(&config)?; + let dev = Arc::new(tun2::create(&config)?); let mut buf = [0; 4096]; + #[cfg(feature = "experimental")] + let dev2 = dev.clone(); + #[cfg(feature = "experimental")] + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_secs(5)); + dev2.shutdown().unwrap(); + }); + std::thread::spawn(move || { loop { - let amount = dev.read(&mut buf)?; + let amount = dev.recv(&mut buf); + println!("amount == {amount:?}"); + let amount = amount?; let pkt = &buf[0..amount]; + println!("pkt = {pkt:?}"); match ip::Packet::new(pkt) { Ok(ip::Packet::V4(pkt)) => { if let Ok(icmp) = icmp::Packet::new(pkt.payload()) { @@ -70,7 +89,7 @@ fn main_entry(quit: Receiver<()>) -> Result<(), BoxError> { .sequence(icmp.sequence())? .payload(icmp.payload())? .build()?; - let size = dev.write(&reply[..])?; + let size = dev.send(&reply[..])?; println!("write {size} len {}", reply.len()); } } diff --git a/src/address.rs b/src/address.rs index 94f775b..71349ef 100644 --- a/src/address.rs +++ b/src/address.rs @@ -1,127 +1,145 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -use crate::error::{Error, Result}; -use std::net::{IpAddr, Ipv4Addr}; -use std::net::{SocketAddr, SocketAddrV4}; - -/// Helper trait to convert things into IPv4 addresses. -#[allow(clippy::wrong_self_convention)] -pub trait IntoAddress { - /// Convert the type to an `Ipv4Addr`. - fn into_address(&self) -> Result; -} - -impl IntoAddress for u32 { - fn into_address(&self) -> Result { - Ok(IpAddr::V4(Ipv4Addr::new( - ((*self) & 0xff) as u8, - ((*self >> 8) & 0xff) as u8, - ((*self >> 16) & 0xff) as u8, - ((*self >> 24) & 0xff) as u8, - ))) - } -} - -impl IntoAddress for i32 { - fn into_address(&self) -> Result { - (*self as u32).into_address() - } -} - -impl IntoAddress for (u8, u8, u8, u8) { - fn into_address(&self) -> Result { - Ok(IpAddr::V4(Ipv4Addr::new(self.0, self.1, self.2, self.3))) - } -} - -impl IntoAddress for str { - fn into_address(&self) -> Result { - self.parse().map_err(|_| Error::InvalidAddress) - } -} - -impl<'a> IntoAddress for &'a str { - fn into_address(&self) -> Result { - (*self).into_address() - } -} - -impl IntoAddress for String { - fn into_address(&self) -> Result { - self.as_str().into_address() - } -} - -impl<'a> IntoAddress for &'a String { - fn into_address(&self) -> Result { - self.as_str().into_address() - } -} - -impl IntoAddress for Ipv4Addr { - fn into_address(&self) -> Result { - Ok(IpAddr::V4(*self)) - } -} - -impl<'a> IntoAddress for &'a Ipv4Addr { - fn into_address(&self) -> Result { - (*self).into_address() - } -} - -impl IntoAddress for IpAddr { - fn into_address(&self) -> Result { - match self { - IpAddr::V4(value) => Ok(IpAddr::V4(*value)), - - IpAddr::V6(_) => unimplemented!(), - } - } -} - -impl<'a> IntoAddress for &'a IpAddr { - fn into_address(&self) -> Result { - (*self).into_address() - } -} - -impl IntoAddress for SocketAddrV4 { - fn into_address(&self) -> Result { - Ok(IpAddr::V4(*self.ip())) - } -} - -impl<'a> IntoAddress for &'a SocketAddrV4 { - fn into_address(&self) -> Result { - (*self).into_address() - } -} - -impl IntoAddress for SocketAddr { - fn into_address(&self) -> Result { - match self { - SocketAddr::V4(value) => Ok(IpAddr::V4(*value.ip())), - - SocketAddr::V6(_) => unimplemented!(), - } - } -} - -impl<'a> IntoAddress for &'a SocketAddr { - fn into_address(&self) -> Result { - (*self).into_address() - } -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +use crate::error::{Error, Result}; +use std::net::{IpAddr, Ipv4Addr}; +use std::net::{SocketAddr, SocketAddrV4}; + +/// Helper trait to convert things into IPv4 addresses. +#[allow(clippy::wrong_self_convention)] +pub trait IntoAddress { + /// Convert the type to an `Ipv4Addr`. + fn into_address(&self) -> Result; +} + +impl IntoAddress for u32 { + fn into_address(&self) -> Result { + Ok(IpAddr::V4(Ipv4Addr::new( + ((*self) & 0xff) as u8, + ((*self >> 8) & 0xff) as u8, + ((*self >> 16) & 0xff) as u8, + ((*self >> 24) & 0xff) as u8, + ))) + } +} +impl IntoAddress for u8 { + fn into_address(&self) -> Result { + let prefix = *self; + let mask = if prefix == 0 { + 0 + } else { + (!0u32) << (32 - prefix) + }; + Ok(Ipv4Addr::from(mask).into()) + } +} + +impl IntoAddress for i32 { + fn into_address(&self) -> Result { + (*self as u32).into_address() + } +} + +impl IntoAddress for (u8, u8, u8, u8) { + fn into_address(&self) -> Result { + Ok(IpAddr::V4(Ipv4Addr::new(self.0, self.1, self.2, self.3))) + } +} + +impl IntoAddress for str { + fn into_address(&self) -> Result { + self.parse().map_err(|_| Error::InvalidAddress) + } +} + +impl<'a> IntoAddress for &'a str { + fn into_address(&self) -> Result { + (*self).into_address() + } +} + +impl IntoAddress for String { + fn into_address(&self) -> Result { + self.as_str().into_address() + } +} + +impl<'a> IntoAddress for &'a String { + fn into_address(&self) -> Result { + self.as_str().into_address() + } +} + +impl IntoAddress for Ipv4Addr { + fn into_address(&self) -> Result { + Ok(IpAddr::V4(*self)) + } +} + +impl<'a> IntoAddress for &'a Ipv4Addr { + fn into_address(&self) -> Result { + (*self).into_address() + } +} + +impl IntoAddress for IpAddr { + fn into_address(&self) -> Result { + match self { + IpAddr::V4(value) => Ok(IpAddr::V4(*value)), + + IpAddr::V6(_) => unimplemented!(), + } + } +} + +impl<'a> IntoAddress for &'a IpAddr { + fn into_address(&self) -> Result { + (*self).into_address() + } +} + +impl IntoAddress for SocketAddrV4 { + fn into_address(&self) -> Result { + Ok(IpAddr::V4(*self.ip())) + } +} + +impl<'a> IntoAddress for &'a SocketAddrV4 { + fn into_address(&self) -> Result { + (*self).into_address() + } +} + +impl IntoAddress for SocketAddr { + fn into_address(&self) -> Result { + match self { + SocketAddr::V4(value) => Ok(IpAddr::V4(*value.ip())), + + SocketAddr::V6(_) => unimplemented!(), + } + } +} + +impl<'a> IntoAddress for &'a SocketAddr { + fn into_address(&self) -> Result { + (*self).into_address() + } +} +#[allow(dead_code)] +pub fn format_mac_address(mac: &[u8; 6]) -> String { + mac.iter() + .map(|b| format!("{:02X}", b)) + .collect::>() + .join("") +} diff --git a/src/async/codec.rs b/src/async/codec.rs deleted file mode 100644 index 363d857..0000000 --- a/src/async/codec.rs +++ /dev/null @@ -1,53 +0,0 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. -use bytes::{BufMut, BytesMut}; -use tokio_util::codec::{Decoder, Encoder}; - -/// A TUN packet Encoder/Decoder. -#[derive(Debug, Default)] -pub struct TunPacketCodec(usize); - -impl TunPacketCodec { - /// Create a new `TunPacketCodec` specifying whether the underlying - /// tunnel Device has enabled the packet information header. - pub fn new(mtu: usize) -> TunPacketCodec { - TunPacketCodec(mtu) - } -} - -impl Decoder for TunPacketCodec { - type Item = Vec; - type Error = std::io::Error; - - fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { - if buf.is_empty() { - return Ok(None); - } - let pkt = buf.split_to(buf.len()); - //reserve enough space for the next packet - buf.reserve(self.0); - Ok(Some(pkt.freeze().to_vec())) - } -} - -impl Encoder> for TunPacketCodec { - type Error = std::io::Error; - - fn encode(&mut self, item: Vec, dst: &mut BytesMut) -> Result<(), Self::Error> { - let bytes = item.as_slice(); - dst.reserve(bytes.len()); - dst.put(bytes); - Ok(()) - } -} diff --git a/src/async/mod.rs b/src/async/mod.rs index f4db429..ad5ff63 100644 --- a/src/async/mod.rs +++ b/src/async/mod.rs @@ -19,9 +19,6 @@ use crate::error; use crate::configuration::Configuration; use crate::platform::create; -mod codec; -pub use codec::TunPacketCodec; - #[cfg(unix)] mod unix_device; #[cfg(unix)] @@ -30,10 +27,10 @@ pub use unix_device::AsyncDevice; #[cfg(target_os = "windows")] mod win_device; #[cfg(target_os = "windows")] -pub use win_device::{AsyncDevice, DeviceReader, DeviceWriter}; +pub use win_device::AsyncDevice; /// Create a TUN device with the given name. pub fn create_as_async(configuration: &Configuration) -> Result { let device = create(configuration)?; - AsyncDevice::new(device).map_err(|err| err.into()) + AsyncDevice::new(device.0).map_err(|err| err.into()) } diff --git a/src/async/unix_device.rs b/src/async/unix_device.rs index 1e268fa..5542558 100644 --- a/src/async/unix_device.rs +++ b/src/async/unix_device.rs @@ -12,27 +12,18 @@ // // 0. You just DO WHAT THE FUCK YOU WANT TO. -use core::pin::Pin; -use core::task::{Context, Poll}; -use futures_core::ready; -use std::io::{IoSlice, Read, Write}; +use crate::platform::DeviceInner; use tokio::io::unix::AsyncFd; use tokio::io::Interest; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use tokio_util::codec::Framed; - -use super::TunPacketCodec; -use crate::device::AbstractDevice; -use crate::platform::Device; /// An async TUN device wrapper around a TUN device. pub struct AsyncDevice { - inner: AsyncFd, + inner: AsyncFd, } /// Returns a shared reference to the underlying Device object. impl core::ops::Deref for AsyncDevice { - type Target = Device; + type Target = DeviceInner; fn deref(&self) -> &Self::Target { self.inner.get_ref() @@ -48,102 +39,26 @@ impl core::ops::DerefMut for AsyncDevice { impl AsyncDevice { /// Create a new `AsyncDevice` wrapping around a `Device`. - pub fn new(device: Device) -> std::io::Result { + pub(crate) fn new(device: DeviceInner) -> std::io::Result { device.set_nonblock()?; Ok(AsyncDevice { inner: AsyncFd::new(device)?, }) } - /// Consumes this AsyncDevice and return a Framed object (unified Stream and Sink interface) - pub fn into_framed(self) -> Framed { - let mtu = self.mtu().unwrap_or(crate::DEFAULT_MTU); - let codec = TunPacketCodec::new(mtu as usize); - // associate mtu with the capacity of ReadBuf - Framed::with_capacity(self, codec, mtu as usize) - } - /// Recv a packet from tun device pub async fn recv(&self, buf: &mut [u8]) -> std::io::Result { - let guard = self.inner.readable().await?; - guard - .get_ref() - .async_io(Interest::READABLE, |inner| inner.recv(buf)) + self.inner + .async_io(Interest::READABLE.add(Interest::ERROR), |device| { + device.recv(buf) + }) .await } /// Send a packet to tun device pub async fn send(&self, buf: &[u8]) -> std::io::Result { - let guard = self.inner.writable().await?; - guard - .get_ref() - .async_io(Interest::WRITABLE, |inner| inner.send(buf)) + self.inner + .async_io(Interest::WRITABLE, |device| device.send(buf)) .await } } - -impl AsyncRead for AsyncDevice { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf, - ) -> Poll> { - loop { - let mut guard = ready!(self.inner.poll_read_ready_mut(cx))?; - let rbuf = buf.initialize_unfilled(); - match guard.try_io(|inner| inner.get_mut().read(rbuf)) { - Ok(res) => return Poll::Ready(res.map(|n| buf.advance(n))), - Err(_wb) => continue, - } - } - } -} - -impl AsyncWrite for AsyncDevice { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - println!("poll write {:?}", buf); - loop { - let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?; - match guard.try_io(|inner| inner.get_mut().write(buf)) { - Ok(res) => return Poll::Ready(res), - Err(_wb) => continue, - } - } - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?; - match guard.try_io(|inner| inner.get_mut().flush()) { - Ok(res) => return Poll::Ready(res), - Err(_wb) => continue, - } - } - } - - fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn poll_write_vectored( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[IoSlice<'_>], - ) -> Poll> { - loop { - let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?; - match guard.try_io(|inner| inner.get_mut().write_vectored(bufs)) { - Ok(res) => return Poll::Ready(res), - Err(_wb) => continue, - } - } - } - - fn is_write_vectored(&self) -> bool { - true - } -} diff --git a/src/async/win_device.rs b/src/async/win_device.rs index f65fa60..defc0cf 100644 --- a/src/async/win_device.rs +++ b/src/async/win_device.rs @@ -1,198 +1,73 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -use core::pin::Pin; -use core::task::{Context, Poll}; -use std::io; -use std::io::Error; -use std::sync::Arc; - -use super::TunPacketCodec; -use crate::device::AbstractDevice; -use crate::platform::windows::{Driver, PacketVariant}; -use crate::platform::Device; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use tokio::sync::mpsc::error::TrySendError; -use tokio_util::codec::Framed; - -/// An async TUN device wrapper around a TUN device. -pub struct AsyncDevice { - inner: Device, - session_reader: DeviceReader, - session_writer: DeviceWriter, -} - -/// Returns a shared reference to the underlying Device object. -impl core::ops::Deref for AsyncDevice { - type Target = Device; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -/// Returns a mutable reference to the underlying Device object. -impl core::ops::DerefMut for AsyncDevice { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -impl AsyncDevice { - /// Create a new `AsyncDevice` wrapping around a `Device`. - pub fn new(device: Device) -> io::Result { - let session_reader = DeviceReader::new(device.driver.clone())?; - let session_writer = DeviceWriter::new(device.driver.clone())?; - Ok(AsyncDevice { - inner: device, - session_reader, - session_writer, - }) - } - - /// Consumes this AsyncDevice and return a Framed object (unified Stream and Sink interface) - pub fn into_framed(self) -> Framed { - let mtu = self.mtu().unwrap_or(crate::DEFAULT_MTU); - let codec = TunPacketCodec::new(mtu as usize); - // guarantee to avoid the mtu of wintun may far away larger than the default provided capacity of ReadBuf of Framed - Framed::with_capacity(self, codec, mtu as usize) - } - pub fn split(self) -> io::Result<(DeviceWriter, DeviceReader)> { - Ok((self.session_writer, self.session_reader)) - } - - /// Recv a packet from tun device - Not implemented for windows - pub async fn recv(&self, _buf: &mut [u8]) -> std::io::Result { - unimplemented!() - } - - /// Send a packet to tun device - Not implemented for windows - pub async fn send(&self, _buf: &[u8]) -> std::io::Result { - unimplemented!() - } -} - -impl AsyncRead for AsyncDevice { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - Pin::new(&mut self.session_reader).poll_read(cx, buf) - } -} - -impl AsyncWrite for AsyncDevice { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Pin::new(&mut self.session_writer).poll_write(cx, buf) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.session_writer).poll_flush(cx) - } - - fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.session_writer).poll_shutdown(cx) - } -} - -pub struct DeviceReader { - receiver: tokio::sync::mpsc::Receiver, - _task: std::thread::JoinHandle<()>, -} -impl DeviceReader { - fn new(session: Arc) -> Result { - let (receiver_tx, receiver_rx) = tokio::sync::mpsc::channel(1024); - let task = std::thread::spawn(move || loop { - match session.receive_blocking() { - Ok(packet) => { - if let Err(err) = receiver_tx.try_send(packet) { - match err { - TrySendError::Full(_) => { - log::error!("receiver_tx Full"); - continue; - } - TrySendError::Closed(_) => { - log::error!("receiver_tx Closed"); - break; - } - } - } - } - Err(err) => { - log::info!("{}", err); - break; - } - } - }); - Ok(DeviceReader { - receiver: receiver_rx, - _task: task, - }) - } -} -pub struct DeviceWriter { - session: Arc, -} -impl DeviceWriter { - fn new(session: Arc) -> Result { - Ok(Self { session }) - } -} - -impl AsyncRead for DeviceReader { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - match std::task::ready!(self.receiver.poll_recv(cx)) { - Some(bytes) => { - match bytes { - PacketVariant::Tap(bytes) => { - buf.put_slice(&bytes); - } - PacketVariant::Tun(bytes) => { - buf.put_slice(bytes.bytes()); - } - } - std::task::Poll::Ready(Ok(())) - } - None => std::task::Poll::Ready(Ok(())), - } - } -} - -impl AsyncWrite for DeviceWriter { - fn poll_write( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - let len = self.session.write_by_ref(buf)?; - Poll::Ready(Ok(len)) - } - - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +use std::io; +use std::sync::Arc; + +use crate::platform::windows::PacketVariant; +use crate::platform::DeviceInner; + +/// An async TUN device wrapper around a TUN device. +pub struct AsyncDevice { + inner: Arc, +} + +/// Returns a shared reference to the underlying Device object. +impl core::ops::Deref for AsyncDevice { + type Target = DeviceInner; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} +impl Drop for AsyncDevice { + fn drop(&mut self) { + let _ = self.inner.shutdown(); + } +} + +impl AsyncDevice { + /// Create a new `AsyncDevice` wrapping around a `Device`. + pub(crate) fn new(device: DeviceInner) -> io::Result { + let inner = Arc::new(device); + + Ok(AsyncDevice { inner }) + } + + /// Recv a packet from tun device - Not implemented for windows + pub async fn recv(&self, buf: &mut [u8]) -> std::io::Result { + let device = self.inner.clone(); + let packet = tokio::task::spawn_blocking(move || device.driver.receive_blocking()) + .await + .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{}", e)))??; + let packet = match &packet { + PacketVariant::Tun(packet) => packet.bytes(), + PacketVariant::Tap(packet) => packet.as_ref(), + }; + let len = packet.len(); + if buf.len() < len { + Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "buffer too small", + ))?; + } + buf[0..len].copy_from_slice(packet); + Ok(len) + } + + /// Send a packet to tun device - Not implemented for windows + pub async fn send(&self, buf: &[u8]) -> std::io::Result { + self.inner.send(buf) + } +} diff --git a/src/configuration.rs b/src/configuration.rs index e56e699..2d69553 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -12,12 +12,12 @@ // // 0. You just DO WHAT THE FUCK YOU WANT TO. +#[allow(unused_imports)] use crate::address::IntoAddress; use crate::platform::PlatformConfig; use crate::AbstractDevice; +#[allow(unused_imports)] use std::net::IpAddr; -#[cfg(unix)] -use std::os::unix::io::RawFd; cfg_if::cfg_if! { if #[cfg(windows)] { @@ -40,29 +40,67 @@ pub enum Layer { /// Configuration builder for a TUN interface. #[derive(Clone, Default, Debug)] pub struct Configuration { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - pub(crate) tun_name_: Option, + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] + pub(crate) name: Option, pub(crate) platform_config: PlatformConfig, + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd"))] + pub(crate) mac_addr: Option<[u8; 6]>, + #[allow(dead_code)] pub(crate) address: Option, + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] pub(crate) destination: Option, + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] pub(crate) broadcast: Option, + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] pub(crate) netmask: Option, + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] pub(crate) mtu: Option, + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "windows" + ))] pub(crate) enabled: Option, + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd",))] pub(crate) layer: Option, - pub(crate) queues: Option, - #[cfg(unix)] - pub(crate) raw_fd: Option, - #[cfg(not(unix))] - pub(crate) raw_fd: Option, #[cfg(windows)] pub(crate) raw_handle: Option, #[cfg(windows)] pub(crate) ring_capacity: Option, #[cfg(windows)] pub(crate) metric: Option, - #[cfg(unix)] - pub(crate) close_fd_on_drop: Option, } impl Configuration { @@ -75,98 +113,101 @@ impl Configuration { self } - /// Functionally equivalent to `tun_name` - #[deprecated( - since = "1.1.2", - note = "Since the API `name` may have an easy name conflict when IDE prompts, it is replaced by `tun_name` for better coding experience" - )] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - pub fn name>(&mut self, tun_name: S) -> &mut Self { - self.tun_name_ = Some(tun_name.as_ref().into()); - self - } - /// Set the tun name. /// /// [Note: on macOS, the tun name must be the form `utunx` where `x` is a number, such as `utun3`. -- end note] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - pub fn tun_name>(&mut self, tun_name: S) -> &mut Self { - self.tun_name_ = Some(tun_name.as_ref().into()); + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] + pub fn name>(&mut self, tun_name: S) -> &mut Self { + self.name = Some(tun_name.as_ref().into()); self } /// Set the address. - pub fn address(&mut self, value: A) -> &mut Self { + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] + pub fn address_with_prefix(&mut self, value: A, prefix: u8) -> &mut Self { self.address = Some(value.into_address().unwrap()); + self.netmask = Some(prefix.into_address().unwrap()); self } /// Set the destination address. + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] pub fn destination(&mut self, value: A) -> &mut Self { self.destination = Some(value.into_address().unwrap()); self } /// Set the broadcast address. + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] pub fn broadcast(&mut self, value: A) -> &mut Self { self.broadcast = Some(value.into_address().unwrap()); self } - /// Set the netmask. - pub fn netmask(&mut self, value: A) -> &mut Self { - self.netmask = Some(value.into_address().unwrap()); - self - } - /// Set the MTU. /// - /// [Note: mtu on the Windows platform is always 65535 due to wintun -- end note] + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd" + ))] pub fn mtu(&mut self, value: u16) -> &mut Self { - // mtu on windows platform is always 65535 due to wintun - if cfg!(target_family = "unix") { - self.mtu = Some(value); - } + self.mtu = Some(value); self } /// Set the interface to be enabled once created. + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "windows" + ))] pub fn up(&mut self) -> &mut Self { self.enabled = Some(true); self } /// Set the interface to be disabled once created. + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "windows" + ))] pub fn down(&mut self) -> &mut Self { self.enabled = Some(false); self } /// Set the OSI layer of operation. + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd",))] pub fn layer(&mut self, value: Layer) -> &mut Self { self.layer = Some(value); self } - /// Set the number of queues. - /// Note: The queues must be 1, otherwise will failed. - #[deprecated(since = "1.0.0", note = "The queues will always be 1.")] - pub fn queues(&mut self, value: usize) -> &mut Self { - self.queues = Some(value); - self - } - - /// Set the raw fd. - #[cfg(unix)] - pub fn raw_fd(&mut self, fd: RawFd) -> &mut Self { - self.raw_fd = Some(fd); - self - } - #[cfg(not(unix))] - pub fn raw_fd(&mut self, fd: i32) -> &mut Self { - self.raw_fd = Some(fd); - self - } #[cfg(windows)] pub fn raw_handle(&mut self, handle: std::os::windows::raw::HANDLE) -> &mut Self { self.raw_handle = Some(WinHandle(handle)); @@ -182,21 +223,12 @@ impl Configuration { self.metric = Some(metric); self } - /// Set whether to close the received raw file descriptor on drop or not. - /// The default behaviour is to close the received or tun2 generated file descriptor. - /// Note: If this is set to false, it is up to the caller to ensure the - /// file descriptor that they pass via [Configuration::raw_fd] is properly closed. - #[cfg(unix)] - pub fn close_fd_on_drop(&mut self, value: bool) -> &mut Self { - self.close_fd_on_drop = Some(value); - self - } } /// Reconfigure the device. #[allow(dead_code)] pub(crate) fn configure( - device: &mut D, + device: &D, config: &Configuration, ) -> crate::error::Result<()> { #[cfg(any( @@ -213,12 +245,23 @@ pub(crate) fn configure( device.set_network_address(address, netmask, config.destination)?; } } + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd",))] + if config.layer == Some(Layer::L2) { + if let Some(mac_addr) = config.mac_addr { + device.set_mac_address(mac_addr)?; + } + } #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] if let Some(ip) = config.broadcast { device.set_broadcast(ip)?; } - #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "windows" + ))] if let Some(enabled) = config.enabled { device.enabled(enabled)?; } diff --git a/src/device.rs b/src/device.rs index f689a42..3f5db6c 100644 --- a/src/device.rs +++ b/src/device.rs @@ -11,12 +11,15 @@ // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION // // 0. You just DO WHAT THE FUCK YOU WANT TO. - #[allow(unused_imports)] -use std::net::IpAddr; - use crate::error::Result; +#[allow(unused_imports)] +use crate::IntoAddress; +#[allow(unused_imports)] +use std::net::IpAddr; +#[allow(dead_code)] +pub(crate) const ETHER_ADDR_LEN: u8 = 6; /// A TUN abstract device interface. pub trait AbstractDevice { /// Get the device tun name. @@ -26,14 +29,19 @@ pub trait AbstractDevice { target_os = "macos", target_os = "freebsd" ))] - fn tun_name(&self) -> Result; + fn name(&self) -> Result; /// Set the device tun name. #[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd"))] - fn set_tun_name(&self, tun_name: &str) -> Result<()>; + fn set_name(&self, tun_name: &str) -> Result<()>; /// Turn on or off the interface. - #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "windows" + ))] fn enabled(&self, value: bool) -> Result<()>; /// Get the address. @@ -45,10 +53,6 @@ pub trait AbstractDevice { ))] fn address(&self) -> Result; - /// Set the address. - #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] - fn set_address(&self, value: IpAddr) -> Result<()>; - /// Get the destination address. #[cfg(any( target_os = "windows", @@ -58,22 +62,13 @@ pub trait AbstractDevice { ))] fn destination(&self) -> Result; - /// Set the destination address. - #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] - fn set_destination(&self, value: IpAddr) -> Result<()>; - /// Get the broadcast address. - #[cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "macos", - target_os = "freebsd" - ))] + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] fn broadcast(&self) -> Result; /// Set the broadcast address. #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] - fn set_broadcast(&self, value: IpAddr) -> Result<()>; + fn set_broadcast(&self, value: A) -> Result<()>; /// Get the netmask. #[cfg(any( @@ -84,10 +79,6 @@ pub trait AbstractDevice { ))] fn netmask(&self) -> Result; - /// Set the netmask. - #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] - fn set_netmask(&self, value: IpAddr) -> Result<()>; - /// Sets the network addresses of this adapter, including network address, subnet mask, and gateway #[cfg(any( target_os = "windows", @@ -95,11 +86,11 @@ pub trait AbstractDevice { target_os = "macos", target_os = "freebsd" ))] - fn set_network_address( + fn set_network_address( &self, - address: IpAddr, - netmask: IpAddr, - destination: Option, + address: A, + netmask: A, + destination: Option, ) -> Result<()>; /// Get the MTU. @@ -108,8 +99,6 @@ pub trait AbstractDevice { target_os = "linux", target_os = "macos", target_os = "freebsd", - target_os = "ios", - target_os = "android" ))] fn mtu(&self) -> Result; @@ -119,11 +108,17 @@ pub trait AbstractDevice { target_os = "linux", target_os = "macos", target_os = "freebsd", - target_os = "ios", - target_os = "android" ))] fn set_mtu(&self, value: u16) -> Result<()>; /// Return whether the underlying tun device on the platform has packet information + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios",))] fn packet_information(&self) -> bool; + + /// Set mac address + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd",))] + fn set_mac_address(&self, eth_addr: [u8; ETHER_ADDR_LEN as usize]) -> Result<()>; + /// Get mac address + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "freebsd",))] + fn get_mac_address(&self) -> Result<[u8; ETHER_ADDR_LEN as usize]>; } diff --git a/src/error.rs b/src/error.rs index 1b06de6..933418b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -35,9 +35,6 @@ pub enum Error { #[error("unsuported network layer of operation")] UnsupportedLayer, - #[error("invalid queues number")] - InvalidQueuesNumber, - #[error("out of range integral type conversion attempted")] TryFromIntError, diff --git a/src/lib.rs b/src/lib.rs index e8663e4..181440e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,8 @@ pub use crate::configuration::{Configuration, Layer}; pub mod platform; pub use crate::platform::create; +#[cfg(unix)] +pub use crate::platform::create_with_fd; #[cfg(feature = "async")] pub mod r#async; diff --git a/src/platform/android/device.rs b/src/platform/android/device.rs index 94b7ce6..631d6f4 100644 --- a/src/platform/android/device.rs +++ b/src/platform/android/device.rs @@ -13,97 +13,43 @@ // 0. You just DO WHAT THE FUCK YOU WANT TO. #![allow(unused_variables)] -use std::io::{Read, Write}; +use std::os::fd::FromRawFd; use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; -use crate::configuration::Configuration; -use crate::device::AbstractDevice; -use crate::error::{Error, Result}; use crate::platform::posix::{Fd, Tun}; /// A TUN device for Android. pub struct Device { tun: Tun, } - -impl AsRef for Device { - fn as_ref(&self) -> &(dyn AbstractDevice + 'static) { - self - } -} - -impl AsMut for Device { - fn as_mut(&mut self) -> &mut (dyn AbstractDevice + 'static) { - self +impl FromRawFd for Device { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + let tun = Fd::new(fd, true).unwrap(); + Device { + tun: Tun::new(tun, false), + } } } impl Device { - /// Create a new `Device` for the given `Configuration`. - pub fn new(config: &Configuration) -> Result { - let close_fd_on_drop = config.close_fd_on_drop.unwrap_or(true); - let fd = match config.raw_fd { - Some(raw_fd) => raw_fd, - _ => return Err(Error::InvalidConfig), - }; - let device = { - let mtu = config.mtu.unwrap_or(crate::DEFAULT_MTU); - let tun = Fd::new(fd, close_fd_on_drop).map_err(|_| std::io::Error::last_os_error())?; - - Device { - tun: Tun::new(tun, mtu, false), - } - }; - - Ok(device) - } - /// Set non-blocking mode pub fn set_nonblock(&self) -> std::io::Result<()> { self.tun.set_nonblock() } /// Recv a packet from tun device - pub fn recv(&self, buf: &mut [u8]) -> std::io::Result { + pub(crate) fn recv(&self, buf: &mut [u8]) -> std::io::Result { self.tun.recv(buf) } /// Send a packet to tun device - pub fn send(&self, buf: &[u8]) -> std::io::Result { + pub(crate) fn send(&self, buf: &[u8]) -> std::io::Result { self.tun.send(buf) } -} - -impl Read for Device { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - self.tun.read(buf) - } -} - -impl Write for Device { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.tun.write(buf) - } - - fn flush(&mut self) -> std::io::Result<()> { - self.tun.flush() - } -} - -impl AbstractDevice for Device { - fn mtu(&self) -> Result { - // TODO: must get the mtu from the underlying device driver - Ok(self.tun.mtu()) - } - - fn set_mtu(&self, value: u16) -> Result<()> { - // TODO: must set the mtu to the underlying device driver - self.tun.set_mtu(value); - Ok(()) - } - fn packet_information(&self) -> bool { - self.tun.packet_information() + #[cfg(feature = "experimental")] + pub(crate) fn shutdown(&self) -> std::io::Result<()> { + self.tun.shutdown() } } diff --git a/src/platform/android/mod.rs b/src/platform/android/mod.rs index d4b08e3..4dba635 100644 --- a/src/platform/android/mod.rs +++ b/src/platform/android/mod.rs @@ -1,30 +1,38 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -//! Android specific functionality. - -mod device; -pub use self::device::Device; - -use crate::configuration::Configuration; -use crate::error::Result; - -/// Android-only interface configuration. -#[derive(Copy, Clone, Default, Debug)] -pub struct PlatformConfig; - -/// Create a TUN device with the given name. -pub fn create(configuration: &Configuration) -> Result { - Device::new(configuration) -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +//! Android specific functionality. + +mod device; + +pub use self::device::Device; +use std::os::fd::{FromRawFd, RawFd}; + +use crate::configuration::Configuration; +use crate::error::Result; + +/// Android-only interface configuration. +#[derive(Copy, Clone, Default, Debug)] +pub struct PlatformConfig; + +use super::Device as DeviceWrapper; +/// Create a TUN device with the given name. +pub fn create(_configuration: &Configuration) -> Result { + unimplemented!() +} +/// # Safety +/// The fd passed in must be an owned file descriptor; in particular, it must be open. +pub unsafe fn create_with_fd(fd: RawFd) -> Result { + Ok(DeviceWrapper(Device::from_raw_fd(fd))) +} diff --git a/src/platform/freebsd/device.rs b/src/platform/freebsd/device.rs index e7edaf7..9e8845b 100644 --- a/src/platform/freebsd/device.rs +++ b/src/platform/freebsd/device.rs @@ -12,158 +12,166 @@ // // 0. You just DO WHAT THE FUCK YOU WANT TO. +use crate::{ + configuration::{Configuration, Layer}, + device::{AbstractDevice, ETHER_ADDR_LEN}, + error::{Error, Result}, + platform::freebsd::sys::*, + platform::posix::{self, sockaddr_union, Fd, Tun}, + IntoAddress, +}; use libc::{ - self, c_char, c_short, ifreq, AF_INET, IFF_RUNNING, IFF_UP, IFNAMSIZ, O_RDWR, SOCK_DGRAM, + self, c_char, c_short, fcntl, ifreq, kinfo_file, AF_INET, AF_LINK, F_KINFO, IFF_RUNNING, + IFF_UP, IFNAMSIZ, KINFO_FILE_SIZE, O_RDWR, SOCK_DGRAM, }; +use std::os::fd::FromRawFd; use std::{ - // ffi::{CStr, CString}, - io::{self, Read, Write}, - mem, - net::{IpAddr, Ipv4Addr}, + ffi::CStr, + io, mem, + net::IpAddr, os::unix::io::{AsRawFd, IntoRawFd, RawFd}, ptr, - sync::{Mutex, RwLock}, + sync::Mutex, }; -use crate::{ - configuration::{Configuration, Layer}, - device::AbstractDevice, - error::{Error, Result}, - platform::freebsd::sys::*, - platform::posix::{self, sockaddr_union, Fd, Tun}, -}; +use mac_address::mac_address_by_name; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] struct Route { - addr: Ipv4Addr, - netmask: Ipv4Addr, - dest: Ipv4Addr, + addr: IpAddr, + netmask: IpAddr, + dest: IpAddr, } /// A TUN device using the TUN/TAP Linux driver. pub struct Device { - tun_name: RwLock, tun: Tun, ctl: Fd, - route: Mutex>, + alias_lock: Mutex<()>, } -impl AsRef for Device { - fn as_ref(&self) -> &(dyn AbstractDevice + 'static) { - self +impl FromRawFd for Device { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + let tun = Fd::new(fd, true).unwrap(); + Device { + tun: Tun::new(tun, false), + ctl: unsafe { ctl().unwrap() }, + alias_lock: Mutex::new(()), + } } } - -impl AsMut for Device { - fn as_mut(&mut self) -> &mut (dyn AbstractDevice + 'static) { - self - } +unsafe fn ctl() -> io::Result { + Ok(Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0), true)?) } - impl Device { /// Create a new `Device` for the given `Configuration`. pub fn new(config: &Configuration) -> Result { let layer = config.layer.unwrap_or(Layer::L3); - if layer == Layer::L3 { - let mut device = unsafe { - let dev = match config.tun_name_.as_ref() { - Some(tun_name) => { - let tun_name = tun_name.clone(); - - if tun_name.len() > IFNAMSIZ { - return Err(Error::NameTooLong); - } + let device_prefix = if layer == Layer::L3 { + "tun".to_string() + } else { + "tap".to_string() + }; + let device = unsafe { + let dev_index = match config.name.as_ref() { + Some(tun_name) => { + let tun_name = tun_name.clone(); - Some(tun_name) + if tun_name.len() > IFNAMSIZ { + return Err(Error::NameTooLong); } - None => None, - }; - - if config.layer.filter(|l| *l != Layer::L3).is_some() { - return Err(Error::UnsupportedLayer); + if layer == Layer::L3 && !tun_name.starts_with("tun") { + return Err(Error::InvalidName); + } + if layer == Layer::L2 && !tun_name.starts_with("tap") { + return Err(Error::InvalidName); + } + Some(tun_name[3..].parse::()? + 1_u32) } - let queues_num = config.queues.unwrap_or(1); - if queues_num != 1 { - return Err(Error::InvalidQueuesNumber); - } + None => None, + }; - let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0), true)?; - - let (tun, tun_name) = { - if let Some(name) = dev.as_ref() { - let device_path = format!("/dev/{}\0", name); - let fd = libc::open(device_path.as_ptr() as *const _, O_RDWR); - let tun = Fd::new(fd, true).map_err(|_| io::Error::last_os_error())?; - (tun, name.clone()) - } else { - let (tun, device_name) = 'End: { - for i in 0..256 { - let device_name = format!("tun{i}"); - let device_path = format!("/dev/{device_name}\0"); - let fd = libc::open(device_path.as_ptr() as *const _, O_RDWR); - if fd > 0 { - let tun = Fd::new(fd, true) - .map_err(|_| io::Error::last_os_error())?; - break 'End (tun, device_name); - } + let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0), true)?; + + let (tun, _tun_name) = { + if let Some(name_index) = dev_index.as_ref() { + let device_name = format!("{}{}", device_prefix, name_index); + let device_path = format!("/dev/{}\0", device_name); + let fd = libc::open(device_path.as_ptr() as *const _, O_RDWR); + let tun = Fd::new(fd, true).map_err(|_| io::Error::last_os_error())?; + (tun, device_name) + } else { + let (tun, device_name) = 'End: { + for i in 0..256 { + let device_name = format!("{device_prefix}{i}"); + let device_path = format!("/dev/{device_name}\0"); + let fd = libc::open(device_path.as_ptr() as *const _, O_RDWR); + if fd > 0 { + let tun = + Fd::new(fd, true).map_err(|_| io::Error::last_os_error())?; + break 'End (tun, device_name); } - return Err(Error::Io(std::io::Error::new( - std::io::ErrorKind::AlreadyExists, - "no avaiable file descriptor", - ))); - }; - (tun, device_name) - } - }; - - let mtu = config.mtu.unwrap_or(crate::DEFAULT_MTU); - - Device { - tun_name: RwLock::new(tun_name), - tun: Tun::new(tun, mtu, false), - ctl, - route: Mutex::new(None), + } + return Err(Error::Io(std::io::Error::new( + std::io::ErrorKind::AlreadyExists, + "no avaiable file descriptor", + ))); + }; + (tun, device_name) } }; - device.set_alias( - config - .address - .unwrap_or(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))), - config - .destination - .unwrap_or(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 255))), - config - .netmask - .unwrap_or(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 0))), - )?; - - crate::configuration::configure(&mut device, config)?; - - Ok(device) - } else { - unimplemented!() - } + Device { + tun: Tun::new(tun, false), + ctl, + alias_lock: Mutex::new(()), + } + }; + + crate::configuration::configure(&device, config)?; + + Ok(device) + } + + // fn current_route(&self) -> Option { + // let addr = self.address().ok()?; + // let netmask = self.netmask().ok()?; + // let dest = self + // .destination() + // .unwrap_or(self.calc_dest_addr(addr, netmask).ok()?); + // Some(Route { + // addr, + // netmask, + // dest, + // }) + // } + + fn calc_dest_addr(&self, addr: IpAddr, netmask: IpAddr) -> Result { + let prefix_len = ipnet::ip_mask_to_prefix(netmask).map_err(|_| Error::InvalidConfig)?; + Ok(ipnet::IpNet::new(addr, prefix_len) + .map_err(|_| Error::InvalidConfig)? + .broadcast()) } /// Set the IPv4 alias of the device. fn set_alias(&self, addr: IpAddr, dest: IpAddr, mask: IpAddr) -> Result<()> { - let IpAddr::V4(addr) = addr else { + let IpAddr::V4(_) = addr else { unimplemented!("do not support IPv6 yet") }; - let IpAddr::V4(dest) = dest else { + let IpAddr::V4(_) = dest else { unimplemented!("do not support IPv6 yet") }; - let IpAddr::V4(mask) = mask else { + let IpAddr::V4(_) = mask else { unimplemented!("do not support IPv6 yet") }; + let _guard = self.alias_lock.lock().unwrap(); + // let old_route = self.current_route(); let ctl = &self.ctl; unsafe { let mut req: ifaliasreq = mem::zeroed(); - let tun_name = self.tun_name.read().unwrap(); - let tun_name = &*tun_name; + let tun_name = self.name()?; ptr::copy_nonoverlapping( tun_name.as_ptr() as *const c_char, req.ifran.as_mut_ptr(), @@ -178,13 +186,12 @@ impl Device { return Err(io::Error::from(err).into()); } - let route = Route { + let new_route = Route { addr, netmask: mask, dest, }; - let mut route_guard = self.route.lock().unwrap(); - if let Err(e) = self.set_route(route, &mut route_guard) { + if let Err(e) = self.set_route(None, new_route) { log::warn!("{e:?}"); } @@ -193,17 +200,16 @@ impl Device { } /// Prepare a new request. - unsafe fn request(&self) -> ifreq { + unsafe fn request(&self) -> Result { let mut req: ifreq = mem::zeroed(); - let tun_name = self.tun_name.read().unwrap(); - let tun_name = &*tun_name; + let tun_name = self.name()?; ptr::copy_nonoverlapping( tun_name.as_ptr() as *const c_char, req.ifr_name.as_mut_ptr(), tun_name.len(), ); - req + Ok(req) } /// Set non-blocking mode @@ -211,89 +217,103 @@ impl Device { self.tun.set_nonblock() } - fn set_route(&self, route: Route, route_guard: &mut Option) -> Result<()> { - // if let Some(v) = &self.route { - // let prefix_len = ipnet::ip_mask_to_prefix(IpAddr::V4(v.netmask)) - // .map_err(|_| Error::InvalidConfig)?; - // let network = ipnet::Ipv4Net::new(v.addr, prefix_len) - // .map_err(|e| Error::InvalidConfig)? - // .network(); - // // command: route -n delete -net 10.0.0.0/24 10.0.0.1 - // let args = [ - // "-n", - // "delete", - // "-net", - // &format!("{}/{}", network, prefix_len), - // &v.dest.to_string(), - // ]; - // println!("{args:?}"); - // run_command("route", &args); - // log::info!("route {}", args.join(" ")); - // } - - // command: route -n add -net 10.0.0.9/24 10.0.0.1 - let prefix_len = ipnet::ip_mask_to_prefix(IpAddr::V4(route.netmask)) - .map_err(|_| Error::InvalidConfig)?; + fn set_route(&self, _old_route: Option, new_route: Route) -> Result<()> { + let prefix_len = + ipnet::ip_mask_to_prefix(new_route.netmask).map_err(|_| Error::InvalidConfig)?; let args = [ "-n", "add", "-net", - &format!("{}/{}", route.addr, prefix_len), - &route.dest.to_string(), + &format!("{}/{}", new_route.addr, prefix_len), + &new_route.dest.to_string(), ]; run_command("route", &args)?; log::info!("route {}", args.join(" ")); - *route_guard = Some(route); Ok(()) } /// Recv a packet from tun device - pub fn recv(&self, buf: &mut [u8]) -> io::Result { + pub(crate) fn recv(&self, buf: &mut [u8]) -> io::Result { self.tun.recv(buf) } /// Send a packet to tun device - pub fn send(&self, buf: &[u8]) -> io::Result { + pub(crate) fn send(&self, buf: &[u8]) -> io::Result { self.tun.send(buf) } -} - -impl Read for Device { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.tun.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - self.tun.read_vectored(bufs) - } -} - -impl Write for Device { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.tun.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.tun.flush() - } - - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - self.tun.write_vectored(bufs) - } + #[cfg(feature = "experimental")] + pub(crate) fn shutdown(&self) -> io::Result<()> { + self.tun.shutdown() + } + + // fn set_address(&self, value: IpAddr) -> Result<()> { + // unsafe { + // let req = self.request(); + // if let Err(err) = siocdifaddr(self.ctl.as_raw_fd(), &req) { + // return Err(io::Error::from(err).into()); + // } + // let previous = self.current_route().ok_or(Error::InvalidConfig)?; + // self.set_alias(value, previous.dest, previous.netmask)?; + // } + // Ok(()) + // } + + // fn set_netmask(&self, value: IpAddr) -> Result<()> { + // unsafe { + // let req = self.request(); + // if let Err(err) = siocdifaddr(self.ctl.as_raw_fd(), &req) { + // return Err(io::Error::from(err).into()); + // } + // let previous = self.current_route().ok_or(Error::InvalidConfig)?; + // self.set_alias(previous.addr, previous.dest, value)?; + // } + // Ok(()) + // } + + // fn set_destination(&self, value: A) -> Result<()> { + // let value = value.into_address()?; + // unsafe { + // let req = self.request(); + // if let Err(err) = siocdifaddr(self.ctl.as_raw_fd(), &req) { + // return Err(io::Error::from(err).into()); + // } + // let previous = self.current_route().ok_or(Error::InvalidConfig)?; + // self.set_alias(previous.addr, value, previous.netmask)?; + // } + // Ok(()) + // } } impl AbstractDevice for Device { - fn tun_name(&self) -> Result { - Ok(self.tun_name.read().unwrap().clone()) + fn name(&self) -> Result { + use std::path::PathBuf; + unsafe { + let mut path_info: kinfo_file = std::mem::zeroed(); + path_info.kf_structsize = KINFO_FILE_SIZE; + if fcntl(self.tun.as_raw_fd(), F_KINFO, &mut path_info as *mut _) < 0 { + return Err(io::Error::last_os_error().into()); + } + let dev_path = CStr::from_ptr(path_info.kf_path.as_ptr() as *const c_char) + .to_string_lossy() + .into_owned(); + let path = PathBuf::from(dev_path); + let device_name = path + .file_name() + .ok_or(Error::InvalidConfig)? + .to_string_lossy() + .to_string(); + Ok(device_name) + } } - fn set_tun_name(&self, value: &str) -> Result<()> { + fn set_name(&self, value: &str) -> Result<()> { use std::ffi::CString; unsafe { if value.len() > IFNAMSIZ { return Err(Error::NameTooLong); } - let mut req = self.request(); + let mut req = self.request()?; let tun_name = CString::new(value)?; let mut tun_name: Vec = tun_name .into_bytes_with_nul() @@ -305,14 +325,13 @@ impl AbstractDevice for Device { return Err(io::Error::from(err).into()); } - *self.tun_name.write().unwrap() = value.to_string(); Ok(()) } } fn enabled(&self, value: bool) -> Result<()> { unsafe { - let mut req = self.request(); + let mut req = self.request()?; if let Err(err) = siocgifflags(self.ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); @@ -334,7 +353,7 @@ impl AbstractDevice for Device { fn address(&self) -> Result { unsafe { - let mut req = self.request(); + let mut req = self.request()?; if let Err(err) = siocgifaddr(self.ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } @@ -343,25 +362,9 @@ impl AbstractDevice for Device { } } - fn set_address(&self, value: IpAddr) -> Result<()> { - unsafe { - let req = self.request(); - if let Err(err) = siocdifaddr(self.ctl.as_raw_fd(), &req) { - return Err(io::Error::from(err).into()); - } - let previous = { (*self.route.lock().unwrap()).ok_or(Error::InvalidConfig)? }; - self.set_alias( - value, - IpAddr::V4(previous.dest), - IpAddr::V4(previous.netmask), - )?; - } - Ok(()) - } - fn destination(&self) -> Result { unsafe { - let mut req = self.request(); + let mut req = self.request()?; if let Err(err) = siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } @@ -370,25 +373,9 @@ impl AbstractDevice for Device { } } - fn set_destination(&self, value: IpAddr) -> Result<()> { - unsafe { - let req = self.request(); - if let Err(err) = siocdifaddr(self.ctl.as_raw_fd(), &req) { - return Err(io::Error::from(err).into()); - } - let previous = { (*self.route.lock().unwrap()).ok_or(Error::InvalidConfig)? }; - self.set_alias( - IpAddr::V4(previous.addr), - value, - IpAddr::V4(previous.netmask), - )?; - } - Ok(()) - } - fn broadcast(&self) -> Result { unsafe { - let mut req = self.request(); + let mut req = self.request()?; if let Err(err) = siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } @@ -397,13 +384,13 @@ impl AbstractDevice for Device { } } - fn set_broadcast(&self, _value: IpAddr) -> Result<()> { + fn set_broadcast(&self, _value: A) -> Result<()> { Ok(()) } fn netmask(&self) -> Result { unsafe { - let mut req = self.request(); + let mut req = self.request()?; if let Err(err) = siocgifnetmask(self.ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } @@ -413,21 +400,9 @@ impl AbstractDevice for Device { } } - fn set_netmask(&self, value: IpAddr) -> Result<()> { - unsafe { - let req = self.request(); - if let Err(err) = siocdifaddr(self.ctl.as_raw_fd(), &req) { - return Err(io::Error::from(err).into()); - } - let previous = { (*self.route.lock().unwrap()).ok_or(Error::InvalidConfig)? }; - self.set_alias(IpAddr::V4(previous.addr), IpAddr::V4(previous.dest), value)?; - } - Ok(()) - } - fn mtu(&self) -> Result { unsafe { - let mut req = self.request(); + let mut req = self.request()?; if let Err(err) = siocgifmtu(self.ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); @@ -442,33 +417,51 @@ impl AbstractDevice for Device { fn set_mtu(&self, value: u16) -> Result<()> { unsafe { - let mut req = self.request(); + let mut req = self.request()?; req.ifr_ifru.ifru_mtu = value as i32; if let Err(err) = siocsifmtu(self.ctl.as_raw_fd(), &req) { return Err(io::Error::from(err).into()); } - self.tun.set_mtu(value); Ok(()) } } - fn set_network_address( + fn set_network_address( &self, - address: IpAddr, - netmask: IpAddr, - destination: Option, + address: A, + netmask: A, + destination: Option, ) -> Result<()> { - self.set_address(address)?; - self.set_netmask(netmask)?; - if let Some(dest) = destination { - self.set_destination(dest)?; - } + let addr = address.into_address()?; + let netmask = netmask.into_address()?; + let default_dest = self.calc_dest_addr(addr, netmask)?; + let dest = destination + .map(|d| d.into_address().unwrap_or(default_dest)) + .unwrap_or(default_dest); + self.set_alias(addr, dest, netmask)?; Ok(()) } - fn packet_information(&self) -> bool { - self.tun.packet_information() + fn set_mac_address(&self, eth_addr: [u8; ETHER_ADDR_LEN as usize]) -> Result<()> { + unsafe { + let mut req = self.request()?; + req.ifr_ifru.ifru_addr.sa_len = ETHER_ADDR_LEN; + req.ifr_ifru.ifru_addr.sa_family = AF_LINK as u8; + req.ifr_ifru.ifru_addr.sa_data[0..ETHER_ADDR_LEN as usize] + .copy_from_slice(eth_addr.map(|c| c as i8).as_slice()); + if let Err(err) = siocsiflladdr(self.ctl.as_raw_fd(), &req) { + return Err(io::Error::from(err).into()); + } + Ok(()) + } + } + + fn get_mac_address(&self) -> Result<[u8; ETHER_ADDR_LEN as usize]> { + let mac = mac_address_by_name(&self.name()?) + .map_err(|e| io::Error::other(e.to_string()))? + .ok_or(Error::InvalidConfig)?; + Ok(mac.bytes()) } } diff --git a/src/platform/freebsd/mod.rs b/src/platform/freebsd/mod.rs index ccf42fb..30d19ad 100644 --- a/src/platform/freebsd/mod.rs +++ b/src/platform/freebsd/mod.rs @@ -1,32 +1,40 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -//! Linux specific functionality. - -pub mod sys; - -mod device; -pub use self::device::Device; - -use crate::configuration::Configuration; -use crate::error::Result; - -/// FreeBSD-only interface configuration. -#[derive(Copy, Clone, Default, Debug)] -pub struct PlatformConfig; - -/// Create a TUN device with the given name. -pub fn create(configuration: &Configuration) -> Result { - Device::new(configuration) -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +//! Linux specific functionality. + +pub mod sys; + +mod device; + +pub use self::device::Device; +use std::os::fd::{FromRawFd, RawFd}; + +use crate::configuration::Configuration; +use crate::error::Result; + +/// FreeBSD-only interface configuration. +#[derive(Copy, Clone, Default, Debug)] +pub struct PlatformConfig; + +use super::Device as DeviceWrapper; +/// Create a TUN device with the given name. +pub fn create(configuration: &Configuration) -> Result { + Ok(DeviceWrapper(Device::new(configuration)?)) +} +/// # Safety +/// The fd passed in must be an owned file descriptor; in particular, it must be open. +pub unsafe fn create_with_fd(fd: RawFd) -> Result { + Ok(DeviceWrapper(Device::from_raw_fd(fd))) +} diff --git a/src/platform/freebsd/sys.rs b/src/platform/freebsd/sys.rs index df19272..070aed5 100644 --- a/src/platform/freebsd/sys.rs +++ b/src/platform/freebsd/sys.rs @@ -1,75 +1,77 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, March 2024 -// -// Copyleft (ↄ) xmh. <970252187@qq.com> -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -//! Bindings to internal FreeBSD stuff. - -use libc::{c_char, c_int, c_uint, ifreq, sockaddr, IFNAMSIZ}; -use nix::{ioctl_readwrite, ioctl_write_ptr}; - -#[allow(non_camel_case_types)] -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ctl_info { - pub ctl_id: c_uint, - pub ctl_name: [c_char; 96], -} - -#[allow(non_camel_case_types)] -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ifaliasreq { - pub ifran: [c_char; IFNAMSIZ], - pub addr: sockaddr, - pub dstaddr: sockaddr, - pub mask: sockaddr, - pub ifra_vhid: c_int, -} - -// #[allow(non_camel_case_types)] -// #[repr(C)] -// #[derive(Copy, Clone)] -// pub struct in_aliasreq { -// pub ifra_name: [c_char; IFNAMSIZ], -// pub ifra_addr: sockaddr_in, -// pub ifra_dstaddr: sockaddr_in, -// pub ifra_mask: sockaddr_in, -// pub ifra_vhid:c_int -// } - -ioctl_write_ptr!(siocsifflags, b'i', 16, ifreq); -ioctl_readwrite!(siocgifflags, b'i', 17, ifreq); - -ioctl_write_ptr!(siocsifaddr, b'i', 12, ifreq); -ioctl_readwrite!(siocgifaddr, b'i', 33, ifreq); - -ioctl_write_ptr!(siocsifdstaddr, b'i', 14, ifreq); -ioctl_readwrite!(siocgifdstaddr, b'i', 34, ifreq); - -ioctl_write_ptr!(siocsifbrdaddr, b'i', 19, ifreq); -ioctl_readwrite!(siocgifbrdaddr, b'i', 35, ifreq); - -ioctl_write_ptr!(siocsifnetmask, b'i', 22, ifreq); -ioctl_readwrite!(siocgifnetmask, b'i', 37, ifreq); - -ioctl_write_ptr!(siocsifmtu, b'i', 52, ifreq); -ioctl_readwrite!(siocgifmtu, b'i', 51, ifreq); - -ioctl_write_ptr!(siocaifaddr, b'i', 43, ifaliasreq); -ioctl_write_ptr!(siocdifaddr, b'i', 25, ifreq); - -ioctl_write_ptr!(siocifcreate, b'i', 122, ifreq); - -ioctl_write_ptr!(siocsifphyaddr, b'i', 70, ifaliasreq); - -ioctl_write_ptr!(siocsifname, b'i', 40, ifreq); +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, March 2024 +// +// Copyleft (ↄ) xmh. <970252187@qq.com> +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +//! Bindings to internal FreeBSD stuff. + +use libc::{c_char, c_int, c_uint, ifreq, sockaddr, IFNAMSIZ}; +use nix::{ioctl_readwrite, ioctl_write_ptr}; + +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Copy, Clone)] +pub struct ctl_info { + pub ctl_id: c_uint, + pub ctl_name: [c_char; 96], +} + +#[allow(non_camel_case_types)] +#[repr(C)] +#[derive(Copy, Clone)] +pub struct ifaliasreq { + pub ifran: [c_char; IFNAMSIZ], + pub addr: sockaddr, + pub dstaddr: sockaddr, + pub mask: sockaddr, + pub ifra_vhid: c_int, +} + +// #[allow(non_camel_case_types)] +// #[repr(C)] +// #[derive(Copy, Clone)] +// pub struct in_aliasreq { +// pub ifra_name: [c_char; IFNAMSIZ], +// pub ifra_addr: sockaddr_in, +// pub ifra_dstaddr: sockaddr_in, +// pub ifra_mask: sockaddr_in, +// pub ifra_vhid:c_int +// } + +ioctl_write_ptr!(siocsifflags, b'i', 16, ifreq); +ioctl_readwrite!(siocgifflags, b'i', 17, ifreq); + +ioctl_write_ptr!(siocsifaddr, b'i', 12, ifreq); +ioctl_readwrite!(siocgifaddr, b'i', 33, ifreq); + +ioctl_write_ptr!(siocsifdstaddr, b'i', 14, ifreq); +ioctl_readwrite!(siocgifdstaddr, b'i', 34, ifreq); + +ioctl_write_ptr!(siocsifbrdaddr, b'i', 19, ifreq); +ioctl_readwrite!(siocgifbrdaddr, b'i', 35, ifreq); + +ioctl_write_ptr!(siocsifnetmask, b'i', 22, ifreq); +ioctl_readwrite!(siocgifnetmask, b'i', 37, ifreq); + +ioctl_write_ptr!(siocsifmtu, b'i', 52, ifreq); +ioctl_readwrite!(siocgifmtu, b'i', 51, ifreq); + +ioctl_write_ptr!(siocaifaddr, b'i', 43, ifaliasreq); +ioctl_write_ptr!(siocdifaddr, b'i', 25, ifreq); + +ioctl_write_ptr!(siocifcreate, b'i', 122, ifreq); + +ioctl_write_ptr!(siocsifphyaddr, b'i', 70, ifaliasreq); + +ioctl_write_ptr!(siocsifname, b'i', 40, ifreq); + +ioctl_write_ptr!(siocsiflladdr, b'i', 60, ifreq); diff --git a/src/platform/ios/device.rs b/src/platform/ios/device.rs index 8946d93..8e44e32 100644 --- a/src/platform/ios/device.rs +++ b/src/platform/ios/device.rs @@ -13,105 +13,51 @@ // 0. You just DO WHAT THE FUCK YOU WANT TO. #![allow(unused_variables)] +use std::os::fd::FromRawFd; +use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; + use crate::{ - configuration::Configuration, device::AbstractDevice, - error::{Error, Result}, platform::posix::{Fd, Tun}, }; -use std::{ - io::{Read, Write}, - os::unix::io::{AsRawFd, IntoRawFd, RawFd}, -}; /// A TUN device for iOS. pub struct Device { tun: Tun, } -impl AsRef for Device { - fn as_ref(&self) -> &(dyn AbstractDevice + 'static) { - self - } -} - -impl AsMut for Device { - fn as_mut(&mut self) -> &mut (dyn AbstractDevice + 'static) { - self +impl FromRawFd for Device { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + let tun = Fd::new(fd, true).unwrap(); + Device { + tun: Tun::new(tun, true), + } } } impl Device { - /// Create a new `Device` for the given `Configuration`. - pub fn new(config: &Configuration) -> Result { - let close_fd_on_drop = config.close_fd_on_drop.unwrap_or(true); - let fd = match config.raw_fd { - Some(raw_fd) => raw_fd, - _ => return Err(Error::InvalidConfig), - }; - let device = { - let mtu = config.mtu.unwrap_or(crate::DEFAULT_MTU); - let fd = Fd::new(fd, close_fd_on_drop).map_err(|_| std::io::Error::last_os_error())?; - Device { - tun: Tun::new(fd, mtu, config.platform_config.packet_information), - } - }; - - Ok(device) - } - /// Set non-blocking mode pub fn set_nonblock(&self) -> std::io::Result<()> { self.tun.set_nonblock() } /// Recv a packet from tun device - pub fn recv(&self, buf: &mut [u8]) -> std::io::Result { + pub(crate) fn recv(&self, buf: &mut [u8]) -> std::io::Result { self.tun.recv(buf) } /// Send a packet to tun device - pub fn send(&self, buf: &[u8]) -> std::io::Result { + pub(crate) fn send(&self, buf: &[u8]) -> std::io::Result { self.tun.send(buf) } -} - -impl Read for Device { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - self.tun.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result { - self.tun.read_vectored(bufs) - } -} - -impl Write for Device { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.tun.write(buf) - } - - fn flush(&mut self) -> std::io::Result<()> { - self.tun.flush() - } - - fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { - self.tun.write_vectored(bufs) + #[cfg(feature = "experimental")] + pub(crate) fn shutdown(&self) -> std::io::Result<()> { + self.tun.shutdown() } } impl AbstractDevice for Device { - fn mtu(&self) -> Result { - // TODO: must get the mtu from the underlying device driver - Ok(self.tun.mtu()) - } - - fn set_mtu(&self, value: u16) -> Result<()> { - // TODO: must set the mtu to the underlying device driver - self.tun.set_mtu(value); - Ok(()) - } - fn packet_information(&self) -> bool { self.tun.packet_information() } diff --git a/src/platform/ios/mod.rs b/src/platform/ios/mod.rs index a465117..49dd866 100644 --- a/src/platform/ios/mod.rs +++ b/src/platform/ios/mod.rs @@ -1,59 +1,68 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -//! iOS specific functionality. - -mod device; -pub use device::Device; - -use crate::configuration::Configuration; -use crate::error::Result; - -/// iOS-only interface configuration. -#[derive(Copy, Clone, Debug)] -pub struct PlatformConfig { - /// switch of Enable/Disable packet information for network driver - pub(crate) packet_information: bool, -} - -impl Default for PlatformConfig { - fn default() -> Self { - PlatformConfig { - packet_information: true, // default is true in iOS - } - } -} - -impl PlatformConfig { - /// Enable or disable packet information, the first 4 bytes of - /// each packet delivered from/to iOS underlying API is a header with flags and protocol type when enabled. - /// - /// - If get the fd from - /// ```Objective-C - /// int32_t tunFd = [[NEPacketTunnelProvider::packetFlow valueForKeyPath:@"socket.fileDescriptor"] intValue]; - /// ``` - /// there exist PI. - /// - /// - But if get packet from `[NEPacketTunnelProvider::packetFlow readPacketsWithCompletionHandler:]` - /// and write packet via `[NEPacketTunnelProvider::packetFlow writePackets:withProtocols:]`, there is no PI. - pub fn packet_information(&mut self, value: bool) -> &mut Self { - self.packet_information = value; - self - } -} - -/// Create a TUN device with the given name. -pub fn create(configuration: &Configuration) -> Result { - Device::new(configuration) -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +//! iOS specific functionality. + +mod device; + +pub use device::Device; +use std::os::fd::{FromRawFd, RawFd}; + +use crate::configuration::Configuration; +use crate::error::Result; + +/// iOS-only interface configuration. +#[derive(Copy, Clone, Debug)] +pub struct PlatformConfig { + /// switch of Enable/Disable packet information for network driver + pub(crate) packet_information: bool, +} + +impl Default for PlatformConfig { + fn default() -> Self { + PlatformConfig { + packet_information: true, // default is true in iOS + } + } +} + +impl PlatformConfig { + /// Enable or disable packet information, the first 4 bytes of + /// each packet delivered from/to iOS underlying API is a header with flags and protocol type when enabled. + /// + /// - If get the fd from + /// ```Objective-C + /// int32_t tunFd = [[NEPacketTunnelProvider::packetFlow valueForKeyPath:@"socket.fileDescriptor"] intValue]; + /// ``` + /// there exist PI. + /// + /// - But if get packet from `[NEPacketTunnelProvider::packetFlow readPacketsWithCompletionHandler:]` + /// and write packet via `[NEPacketTunnelProvider::packetFlow writePackets:withProtocols:]`, there is no PI. + pub fn packet_information(&mut self, value: bool) -> &mut Self { + self.packet_information = value; + self + } +} + +use super::Device as DeviceWrapper; +/// Create a TUN device with the given name. +pub fn create(_configuration: &Configuration) -> Result { + unimplemented!() +} + +/// # Safety +/// The fd passed in must be an owned file descriptor; in particular, it must be open. +pub unsafe fn create_with_fd(fd: RawFd) -> Result { + Ok(DeviceWrapper(Device::from_raw_fd(fd))) +} diff --git a/src/platform/linux/device.rs b/src/platform/linux/device.rs index b230c2f..e7b114b 100644 --- a/src/platform/linux/device.rs +++ b/src/platform/linux/device.rs @@ -12,149 +12,98 @@ // // 0. You just DO WHAT THE FUCK YOU WANT TO. +use crate::configuration::configure; +use crate::{ + configuration::{Configuration, Layer}, + device::{AbstractDevice, ETHER_ADDR_LEN}, + error::{Error, Result}, + platform::linux::sys::*, + platform::posix::{ipaddr_to_sockaddr, sockaddr_union, Fd, Tun}, + IntoAddress, +}; use libc::{ - self, c_char, c_short, ifreq, AF_INET, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_RUNNING, IFF_TAP, - IFF_TUN, IFF_UP, IFNAMSIZ, O_RDWR, SOCK_DGRAM, + self, c_char, c_short, ifreq, AF_INET, ARPHRD_ETHER, IFF_MULTI_QUEUE, IFF_NO_PI, IFF_RUNNING, + IFF_TAP, IFF_TUN, IFF_UP, IFNAMSIZ, O_RDWR, SOCK_DGRAM, }; +use mac_address::mac_address_by_name; +use std::os::fd::FromRawFd; use std::{ - ffi::{CStr, CString}, - io::{self, Read, Write}, + ffi::CString, + io::{self}, mem, net::IpAddr, os::unix::io::{AsRawFd, IntoRawFd, RawFd}, ptr, - sync::RwLock, -}; - -use crate::{ - configuration::{Configuration, Layer}, - device::AbstractDevice, - error::{Error, Result}, - platform::linux::sys::*, - platform::posix::{self, ipaddr_to_sockaddr, sockaddr_union, Fd, Tun}, }; const OVERWRITE_SIZE: usize = std::mem::size_of::(); /// A TUN device using the TUN/TAP Linux driver. pub struct Device { - tun_name: RwLock, tun: Tun, - ctl: Fd, -} - -impl AsRef for Device { - fn as_ref(&self) -> &(dyn AbstractDevice + 'static) { - self - } } - -impl AsMut for Device { - fn as_mut(&mut self) -> &mut (dyn AbstractDevice + 'static) { - self +impl FromRawFd for Device { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + let tun = Fd::new(fd, true).unwrap(); + Device { + tun: Tun::new(tun, false), + } } } impl Device { /// Create a new `Device` for the given `Configuration`. pub fn new(config: &Configuration) -> Result { - let layer = config.layer.unwrap_or(Layer::L3); - let mtu = config.mtu.unwrap_or(crate::DEFAULT_MTU); - if layer == Layer::L3 { - if let Some(fd) = config.raw_fd { - let close_fd_on_drop = config.close_fd_on_drop.unwrap_or(true); - let tun = Fd::new(fd, close_fd_on_drop).map_err(|_| io::Error::last_os_error())?; - let device = Device { - tun_name: RwLock::new(String::new()), - tun: posix::Tun::new(tun, mtu, config.platform_config.packet_information), - ctl: Fd::new(unsafe { libc::socket(AF_INET, SOCK_DGRAM, 0) }, true)?, - }; - return Ok(device); - } - let mut device = unsafe { - let dev_name = match config.tun_name_.as_ref() { - Some(tun_name) => { - let tun_name = CString::new(tun_name.clone())?; - - if tun_name.as_bytes_with_nul().len() > IFNAMSIZ { - return Err(Error::NameTooLong); - } - - Some(tun_name) - } - - None => None, - }; + let dev_name = match config.name.as_ref() { + Some(tun_name) => { + let tun_name = CString::new(tun_name.clone())?; - let mut req: ifreq = mem::zeroed(); - - if let Some(dev_name) = dev_name.as_ref() { - ptr::copy_nonoverlapping( - dev_name.as_ptr() as *const c_char, - req.ifr_name.as_mut_ptr(), - dev_name.as_bytes_with_nul().len(), - ); + if tun_name.as_bytes_with_nul().len() > IFNAMSIZ { + return Err(Error::NameTooLong); } - let device_type: c_short = config.layer.unwrap_or(Layer::L3).into(); - - let queues_num = config.queues.unwrap_or(1); - if queues_num != 1 { - return Err(Error::InvalidQueuesNumber); - } + Some(tun_name) + } - let iff_no_pi = IFF_NO_PI as c_short; - let iff_multi_queue = IFF_MULTI_QUEUE as c_short; - let packet_information = config.platform_config.packet_information; - req.ifr_ifru.ifru_flags = device_type - | if packet_information { 0 } else { iff_no_pi } - | if queues_num > 1 { iff_multi_queue } else { 0 }; - - let tun_fd = { - let fd = libc::open(b"/dev/net/tun\0".as_ptr() as *const _, O_RDWR); - let tun_fd = Fd::new(fd, true).map_err(|_| io::Error::last_os_error())?; - - if let Err(err) = tunsetiff(tun_fd.inner, &mut req as *mut _ as *mut _) { - return Err(io::Error::from(err).into()); - } - - tun_fd - }; - - let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0), true)?; - - let tun_name = CStr::from_ptr(req.ifr_name.as_ptr()) - .to_string_lossy() - .to_string(); - Device { - tun_name: RwLock::new(tun_name), - tun: Tun::new(tun_fd, mtu, packet_information), - ctl, - } - }; + None => None, + }; + unsafe { + let mut req: ifreq = mem::zeroed(); + + if let Some(dev_name) = dev_name.as_ref() { + ptr::copy_nonoverlapping( + dev_name.as_ptr() as *const c_char, + req.ifr_name.as_mut_ptr(), + dev_name.as_bytes_with_nul().len(), + ); + } - if config.platform_config.ensure_root_privileges { - crate::configuration::configure(&mut device, config)?; + let device_type: c_short = config.layer.unwrap_or(Layer::L3).into(); + let queues_num = 1; + let iff_no_pi = IFF_NO_PI as c_short; + let iff_multi_queue = IFF_MULTI_QUEUE as c_short; + let packet_information = config.platform_config.packet_information; + req.ifr_ifru.ifru_flags = device_type + | if packet_information { 0 } else { iff_no_pi } + | if queues_num > 1 { iff_multi_queue } else { 0 }; + + let fd = libc::open(b"/dev/net/tun\0".as_ptr() as *const _, O_RDWR); + let tun_fd = Fd::new(fd, true)?; + if let Err(err) = tunsetiff(tun_fd.inner, &mut req as *mut _ as *mut _) { + return Err(io::Error::from(err).into()); } + let device = Device { + tun: Tun::new(tun_fd, config.platform_config.packet_information), + }; + configure(&device, config)?; Ok(device) - } else { - unimplemented!() } } /// Prepare a new request. - unsafe fn request(&self) -> ifreq { - let mut req: ifreq = mem::zeroed(); - let tun_name = self.tun_name.read().unwrap(); - let tun_name = &*tun_name; - ptr::copy_nonoverlapping( - tun_name.as_ptr() as *const c_char, - req.ifr_name.as_mut_ptr(), - tun_name.len(), - ); - - req + unsafe fn request(&self) -> Result { + request(&self.name()?) } /// Make the device persistent. @@ -196,46 +145,78 @@ impl Device { } /// Recv a packet from tun device - pub fn recv(&self, buf: &mut [u8]) -> io::Result { + pub(crate) fn recv(&self, buf: &mut [u8]) -> io::Result { self.tun.recv(buf) } /// Send a packet to tun device - pub fn send(&self, buf: &[u8]) -> io::Result { + pub(crate) fn send(&self, buf: &[u8]) -> io::Result { self.tun.send(buf) } -} - -impl Read for Device { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.tun.read(buf) + #[cfg(feature = "experimental")] + pub(crate) fn shutdown(&self) -> io::Result<()> { + self.tun.shutdown() } - - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - self.tun.read_vectored(bufs) - } -} - -impl Write for Device { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.tun.write(buf) + fn set_address(&self, value: IpAddr) -> Result<()> { + unsafe { + let mut req = self.request()?; + ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_addr, OVERWRITE_SIZE); + if let Err(err) = siocsifaddr(ctl()?.as_raw_fd(), &req) { + return Err(io::Error::from(err).into()); + } + Ok(()) + } } - - fn flush(&mut self) -> io::Result<()> { - self.tun.flush() + fn set_netmask(&self, value: IpAddr) -> Result<()> { + unsafe { + let mut req = self.request()?; + ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_netmask, OVERWRITE_SIZE); + if let Err(err) = siocsifnetmask(ctl()?.as_raw_fd(), &req) { + return Err(io::Error::from(err).into()); + } + Ok(()) + } } - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - self.tun.write_vectored(bufs) + fn set_destination(&self, value: A) -> Result<()> { + let value = value.into_address()?; + unsafe { + let mut req = self.request()?; + ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_dstaddr, OVERWRITE_SIZE); + if let Err(err) = siocsifdstaddr(ctl()?.as_raw_fd(), &req) { + return Err(io::Error::from(err).into()); + } + Ok(()) + } } } - +unsafe fn ctl() -> io::Result { + Ok(Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0), true)?) +} +unsafe fn name(fd: RawFd) -> io::Result { + let mut req: ifreq = mem::zeroed(); + if let Err(err) = tungetiff(fd, &mut req as *mut _ as *mut _) { + return Err(io::Error::from(err)); + } + let c_str = std::ffi::CStr::from_ptr(req.ifr_name.as_ptr() as *const c_char); + let tun_name = c_str.to_string_lossy().into_owned(); + Ok(tun_name) +} +unsafe fn request(name: &str) -> Result { + let mut req: ifreq = mem::zeroed(); + ptr::copy_nonoverlapping( + name.as_ptr() as *const c_char, + req.ifr_name.as_mut_ptr(), + name.len(), + ); + Ok(req) +} impl AbstractDevice for Device { - fn tun_name(&self) -> Result { - Ok(self.tun_name.read().unwrap().clone()) + fn name(&self) -> Result { + unsafe { name(self.as_raw_fd()).map_err(|e| e.into()) } } - fn set_tun_name(&self, value: &str) -> Result<()> { + fn set_name(&self, value: &str) -> Result<()> { unsafe { let tun_name = CString::new(value)?; @@ -243,28 +224,27 @@ impl AbstractDevice for Device { return Err(Error::NameTooLong); } - let mut req = self.request(); + let mut req = self.request()?; ptr::copy_nonoverlapping( tun_name.as_ptr() as *const c_char, req.ifr_ifru.ifru_newname.as_mut_ptr(), value.len(), ); - if let Err(err) = siocsifname(self.ctl.as_raw_fd(), &req) { + if let Err(err) = siocsifname(ctl()?.as_raw_fd(), &req) { return Err(io::Error::from(err).into()); } - *self.tun_name.write().unwrap() = value.into(); - Ok(()) } } fn enabled(&self, value: bool) -> Result<()> { unsafe { - let mut req = self.request(); + let ctl = ctl()?; + let mut req = self.request()?; - if let Err(err) = siocgifflags(self.ctl.as_raw_fd(), &mut req) { + if let Err(err) = siocgifflags(ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } @@ -274,7 +254,7 @@ impl AbstractDevice for Device { req.ifr_ifru.ifru_flags &= !(IFF_UP as c_short); } - if let Err(err) = siocsifflags(self.ctl.as_raw_fd(), &req) { + if let Err(err) = siocsifflags(ctl.as_raw_fd(), &req) { return Err(io::Error::from(err).into()); } @@ -284,8 +264,8 @@ impl AbstractDevice for Device { fn address(&self) -> Result { unsafe { - let mut req = self.request(); - if let Err(err) = siocgifaddr(self.ctl.as_raw_fd(), &mut req) { + let mut req = self.request()?; + if let Err(err) = siocgifaddr(ctl()?.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } let sa = sockaddr_union::from(req.ifr_ifru.ifru_addr); @@ -293,21 +273,10 @@ impl AbstractDevice for Device { } } - fn set_address(&self, value: IpAddr) -> Result<()> { - unsafe { - let mut req = self.request(); - ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_addr, OVERWRITE_SIZE); - if let Err(err) = siocsifaddr(self.ctl.as_raw_fd(), &req) { - return Err(io::Error::from(err).into()); - } - Ok(()) - } - } - fn destination(&self) -> Result { unsafe { - let mut req = self.request(); - if let Err(err) = siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) { + let mut req = self.request()?; + if let Err(err) = siocgifdstaddr(ctl()?.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } let sa = sockaddr_union::from(req.ifr_ifru.ifru_dstaddr); @@ -315,21 +284,10 @@ impl AbstractDevice for Device { } } - fn set_destination(&self, value: IpAddr) -> Result<()> { - unsafe { - let mut req = self.request(); - ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_dstaddr, OVERWRITE_SIZE); - if let Err(err) = siocsifdstaddr(self.ctl.as_raw_fd(), &req) { - return Err(io::Error::from(err).into()); - } - Ok(()) - } - } - fn broadcast(&self) -> Result { unsafe { - let mut req = self.request(); - if let Err(err) = siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) { + let mut req = self.request()?; + if let Err(err) = siocgifbrdaddr(ctl()?.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } let sa = sockaddr_union::from(req.ifr_ifru.ifru_broadaddr); @@ -337,11 +295,12 @@ impl AbstractDevice for Device { } } - fn set_broadcast(&self, value: IpAddr) -> Result<()> { + fn set_broadcast(&self, value: A) -> Result<()> { + let value = value.into_address()?; unsafe { - let mut req = self.request(); + let mut req = self.request()?; ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_broadaddr, OVERWRITE_SIZE); - if let Err(err) = siocsifbrdaddr(self.ctl.as_raw_fd(), &req) { + if let Err(err) = siocsifbrdaddr(ctl()?.as_raw_fd(), &req) { return Err(io::Error::from(err).into()); } Ok(()) @@ -350,8 +309,8 @@ impl AbstractDevice for Device { fn netmask(&self) -> Result { unsafe { - let mut req = self.request(); - if let Err(err) = siocgifnetmask(self.ctl.as_raw_fd(), &mut req) { + let mut req = self.request()?; + if let Err(err) = siocgifnetmask(ctl()?.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } let sa = sockaddr_union::from(req.ifr_ifru.ifru_netmask); @@ -359,22 +318,25 @@ impl AbstractDevice for Device { } } - fn set_netmask(&self, value: IpAddr) -> Result<()> { - unsafe { - let mut req = self.request(); - ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_netmask, OVERWRITE_SIZE); - if let Err(err) = siocsifnetmask(self.ctl.as_raw_fd(), &req) { - return Err(io::Error::from(err).into()); - } - Ok(()) + fn set_network_address( + &self, + address: A, + netmask: A, + destination: Option, + ) -> Result<()> { + self.set_address(address.into_address()?)?; + self.set_netmask(netmask.into_address()?)?; + if let Some(destination) = destination { + self.set_destination(destination.into_address()?)?; } + Ok(()) } fn mtu(&self) -> Result { unsafe { - let mut req = self.request(); + let mut req = self.request()?; - if let Err(err) = siocgifmtu(self.ctl.as_raw_fd(), &mut req) { + if let Err(err) = siocgifmtu(ctl()?.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); } @@ -387,33 +349,38 @@ impl AbstractDevice for Device { fn set_mtu(&self, value: u16) -> Result<()> { unsafe { - let mut req = self.request(); + let mut req = self.request()?; req.ifr_ifru.ifru_mtu = value as i32; - if let Err(err) = siocsifmtu(self.ctl.as_raw_fd(), &req) { + if let Err(err) = siocsifmtu(ctl()?.as_raw_fd(), &req) { return Err(io::Error::from(err).into()); } - self.tun.set_mtu(value); Ok(()) } } - fn set_network_address( - &self, - address: IpAddr, - netmask: IpAddr, - destination: Option, - ) -> Result<()> { - self.set_address(address)?; - self.set_netmask(netmask)?; - if let Some(dest) = destination { - self.set_destination(dest)?; + fn packet_information(&self) -> bool { + self.tun.packet_information() + } + + fn set_mac_address(&self, eth_addr: [u8; ETHER_ADDR_LEN as usize]) -> Result<()> { + unsafe { + let mut req = self.request()?; + req.ifr_ifru.ifru_hwaddr.sa_family = ARPHRD_ETHER; + req.ifr_ifru.ifru_hwaddr.sa_data[0..ETHER_ADDR_LEN as usize] + .copy_from_slice(eth_addr.map(|c| c as i8).as_slice()); + if let Err(err) = siocsifhwaddr(ctl()?.as_raw_fd(), &req) { + return Err(io::Error::from(err).into()); + } + Ok(()) } - Ok(()) } - fn packet_information(&self) -> bool { - self.tun.packet_information() + fn get_mac_address(&self) -> Result<[u8; ETHER_ADDR_LEN as usize]> { + let mac = mac_address_by_name(&self.name()?) + .map_err(|e| Error::String(e.to_string()))? + .ok_or(Error::InvalidConfig)?; + Ok(mac.bytes()) } } diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index f94b649..fdfc369 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -1,69 +1,61 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -//! Linux specific functionality. - -pub mod sys; - -mod device; -pub use self::device::Device; - -use crate::configuration::Configuration; -use crate::error::Result; - -/// Linux-only interface configuration. -#[derive(Copy, Clone, Debug)] -pub struct PlatformConfig { - /// switch of Enable/Disable packet information for network driver - pub(crate) packet_information: bool, - /// root privileges required or not - pub(crate) ensure_root_privileges: bool, -} - -/// `packet_information` is default to be `false` and `ensure_root_privileges` is default to be `true`. -impl Default for PlatformConfig { - fn default() -> Self { - PlatformConfig { - packet_information: false, - ensure_root_privileges: true, - } - } -} - -impl PlatformConfig { - /// Enable or disable packet information, the first 4 bytes of - /// each packet delivered from/to Linux underlying API is a header with flags and protocol type when enabled. - /// - /// [Note: This configuration just applies to the Linux underlying API and is a no-op on tun2(i.e. the packets delivered from/to tun2 always contain no packet information) -- end note]. - #[deprecated( - since = "1.0.0", - note = "No effect applies to the packets delivered from/to tun2 since the packets always contain no header on all platforms." - )] - pub fn packet_information(&mut self, value: bool) -> &mut Self { - self.packet_information = value; - self - } - - /// Indicated whether tun2 running in root privilege, - /// since some operations need it such as assigning IP/netmask/destination etc. - pub fn ensure_root_privileges(&mut self, value: bool) -> &mut Self { - self.ensure_root_privileges = value; - self - } -} - -/// Create a TUN device with the given name. -pub fn create(configuration: &Configuration) -> Result { - Device::new(configuration) -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +//! Linux specific functionality. + +pub mod sys; + +mod device; + +pub use self::device::Device; +use std::os::fd::{FromRawFd, RawFd}; + +use crate::configuration::Configuration; +use crate::error::Result; + +/// Linux-only interface configuration. +#[derive(Copy, Clone, Debug)] +pub struct PlatformConfig { + /// switch of Enable/Disable packet information for network driver + pub(crate) packet_information: bool, +} + +/// `packet_information` is default to be `false` and `ensure_root_privileges` is default to be `true`. +impl Default for PlatformConfig { + fn default() -> Self { + PlatformConfig { + packet_information: false, + } + } +} + +impl PlatformConfig { + /// Enable or disable packet information, the first 4 bytes of + /// each packet delivered from/to Linux underlying API is a header with flags and protocol type when enabled. + pub fn packet_information(&mut self, value: bool) -> &mut Self { + self.packet_information = value; + self + } +} + +use super::Device as DeviceWrapper; +/// Create a TUN device with the given name. +pub fn create(configuration: &Configuration) -> Result { + Ok(DeviceWrapper(Device::new(configuration)?)) +} +/// # Safety +/// The fd passed in must be an owned file descriptor; in particular, it must be open. +pub unsafe fn create_with_fd(fd: RawFd) -> Result { + Ok(DeviceWrapper(Device::from_raw_fd(fd))) +} diff --git a/src/platform/linux/sys.rs b/src/platform/linux/sys.rs index 92dcae3..aebd59a 100644 --- a/src/platform/linux/sys.rs +++ b/src/platform/linux/sys.rs @@ -1,37 +1,41 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -//! Bindings to internal Linux stuff. - -use libc::{c_int, ifreq}; -use nix::{ioctl_read_bad, ioctl_write_ptr, ioctl_write_ptr_bad}; - -ioctl_read_bad!(siocgifflags, 0x8913, ifreq); -ioctl_write_ptr_bad!(siocsifflags, 0x8914, ifreq); -ioctl_read_bad!(siocgifaddr, 0x8915, ifreq); -ioctl_write_ptr_bad!(siocsifaddr, 0x8916, ifreq); -ioctl_read_bad!(siocgifdstaddr, 0x8917, ifreq); -ioctl_write_ptr_bad!(siocsifdstaddr, 0x8918, ifreq); -ioctl_read_bad!(siocgifbrdaddr, 0x8919, ifreq); -ioctl_write_ptr_bad!(siocsifbrdaddr, 0x891a, ifreq); -ioctl_read_bad!(siocgifnetmask, 0x891b, ifreq); -ioctl_write_ptr_bad!(siocsifnetmask, 0x891c, ifreq); -ioctl_read_bad!(siocgifmtu, 0x8921, ifreq); -ioctl_write_ptr_bad!(siocsifmtu, 0x8922, ifreq); -ioctl_write_ptr_bad!(siocsifname, 0x8923, ifreq); - -ioctl_write_ptr!(tunsetiff, b'T', 202, c_int); -ioctl_write_ptr!(tunsetpersist, b'T', 203, c_int); -ioctl_write_ptr!(tunsetowner, b'T', 204, c_int); -ioctl_write_ptr!(tunsetgroup, b'T', 206, c_int); +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +//! Bindings to internal Linux stuff. + +use libc::{c_int, ifreq}; +use nix::{ioctl_read, ioctl_read_bad, ioctl_write_ptr, ioctl_write_ptr_bad}; + +ioctl_read_bad!(siocgifflags, 0x8913, ifreq); +ioctl_write_ptr_bad!(siocsifflags, 0x8914, ifreq); +ioctl_read_bad!(siocgifaddr, 0x8915, ifreq); +ioctl_write_ptr_bad!(siocsifaddr, 0x8916, ifreq); +ioctl_read_bad!(siocgifdstaddr, 0x8917, ifreq); +ioctl_write_ptr_bad!(siocsifdstaddr, 0x8918, ifreq); +ioctl_read_bad!(siocgifbrdaddr, 0x8919, ifreq); +ioctl_write_ptr_bad!(siocsifbrdaddr, 0x891a, ifreq); +ioctl_read_bad!(siocgifnetmask, 0x891b, ifreq); +ioctl_write_ptr_bad!(siocsifnetmask, 0x891c, ifreq); +ioctl_read_bad!(siocgifmtu, 0x8921, ifreq); +ioctl_write_ptr_bad!(siocsifmtu, 0x8922, ifreq); +ioctl_write_ptr_bad!(siocsifname, 0x8923, ifreq); + +ioctl_write_ptr_bad!(siocsifhwaddr, 0x8924, ifreq); + +ioctl_read!(tungetiff, b'T', 210, c_int); + +ioctl_write_ptr!(tunsetiff, b'T', 202, c_int); +ioctl_write_ptr!(tunsetpersist, b'T', 203, c_int); +ioctl_write_ptr!(tunsetowner, b'T', 204, c_int); +ioctl_write_ptr!(tunsetgroup, b'T', 206, c_int); diff --git a/src/platform/macos/device.rs b/src/platform/macos/device.rs index c376262..36b4f4b 100644 --- a/src/platform/macos/device.rs +++ b/src/platform/macos/device.rs @@ -14,180 +14,132 @@ #![allow(unused_variables)] use crate::{ - configuration::{Configuration, Layer}, + configuration::Configuration, device::AbstractDevice, error::{Error, Result}, platform::{ macos::sys::*, posix::{self, ipaddr_to_sockaddr, sockaddr_union, Fd}, }, + IntoAddress, }; const OVERWRITE_SIZE: usize = std::mem::size_of::(); +use crate::platform::Tun; use libc::{ self, c_char, c_short, c_uint, c_void, sockaddr, socklen_t, AF_INET, AF_SYSTEM, AF_SYS_CONTROL, IFF_RUNNING, IFF_UP, IFNAMSIZ, PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, }; +use std::os::fd::FromRawFd; use std::{ ffi::CStr, - io::{self, Read, Write}, - mem, - net::{IpAddr, Ipv4Addr}, + io, mem, + net::IpAddr, os::unix::io::{AsRawFd, IntoRawFd, RawFd}, ptr, sync::Mutex, }; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] struct Route { - addr: Ipv4Addr, - netmask: Ipv4Addr, - dest: Ipv4Addr, + addr: IpAddr, + netmask: IpAddr, + dest: IpAddr, } /// A TUN device using the TUN macOS driver. pub struct Device { - tun_name: Option, tun: posix::Tun, - ctl: Option, - route: Mutex>, + alias_lock: Mutex<()>, } - -impl AsRef for Device { - fn as_ref(&self) -> &(dyn AbstractDevice + 'static) { - self +impl FromRawFd for Device { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + let tun = Fd::new(fd, true).unwrap(); + Device { + tun: Tun::new(tun, true), + alias_lock: Mutex::new(()), + } } } - -impl AsMut for Device { - fn as_mut(&mut self) -> &mut (dyn AbstractDevice + 'static) { - self - } +unsafe fn ctl() -> io::Result { + Ok(Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0), true)?) } - impl Device { /// Create a new `Device` for the given `Configuration`. pub fn new(config: &Configuration) -> Result { - let layer = config.layer.unwrap_or(Layer::L3); let mtu = config.mtu.unwrap_or(crate::DEFAULT_MTU); - if layer == Layer::L3 { - if let Some(fd) = config.raw_fd { - let close_fd_on_drop = config.close_fd_on_drop.unwrap_or(true); - let tun = Fd::new(fd, close_fd_on_drop).map_err(|_| io::Error::last_os_error())?; - let device = Device { - tun_name: None, - tun: posix::Tun::new(tun, mtu, config.platform_config.packet_information), - ctl: None, - route: Mutex::new(None), - }; - return Ok(device); + + let id = if let Some(tun_name) = config.name.as_ref() { + if tun_name.len() > IFNAMSIZ { + return Err(Error::NameTooLong); } - let id = if let Some(tun_name) = config.tun_name_.as_ref() { - if tun_name.len() > IFNAMSIZ { - return Err(Error::NameTooLong); - } + if !tun_name.starts_with("utun") { + return Err(Error::InvalidName); + } + tun_name[4..].parse::()? + 1_u32 + } else { + 0_u32 + }; - if !tun_name.starts_with("utun") { - return Err(Error::InvalidName); - } + let device = unsafe { + let fd = libc::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + let tun = posix::Fd::new(fd, true).map_err(|_| io::Error::last_os_error())?; + + let mut info = ctl_info { + ctl_id: 0, + ctl_name: { + let mut buffer = [0; 96]; + for (i, o) in UTUN_CONTROL_NAME.as_bytes().iter().zip(buffer.iter_mut()) { + *o = *i as _; + } + buffer + }, + }; - tun_name[4..].parse::()? + 1_u32 - } else { - 0_u32 + if let Err(err) = ctliocginfo(tun.inner, &mut info as *mut _ as *mut _) { + return Err(io::Error::from(err).into()); + } + + let addr = libc::sockaddr_ctl { + sc_id: info.ctl_id, + sc_len: mem::size_of::() as _, + sc_family: AF_SYSTEM as _, + ss_sysaddr: AF_SYS_CONTROL as _, + sc_unit: id as c_uint, + sc_reserved: [0; 5], }; - if config.layer.filter(|l| *l != Layer::L3).is_some() { - return Err(Error::UnsupportedLayer); + let address = &addr as *const libc::sockaddr_ctl as *const sockaddr; + if libc::connect(tun.inner, address, mem::size_of_val(&addr) as socklen_t) < 0 { + return Err(io::Error::last_os_error().into()); } - let queues_number = config.queues.unwrap_or(1); - if queues_number != 1 { - return Err(Error::InvalidQueuesNumber); + let mut tun_name = [0u8; 64]; + let mut name_len: socklen_t = 64; + + let optval = &mut tun_name as *mut _ as *mut c_void; + let optlen = &mut name_len as *mut socklen_t; + if libc::getsockopt(tun.inner, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, optval, optlen) < 0 { + return Err(io::Error::last_os_error().into()); } - let mut device = unsafe { - let fd = libc::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); - let tun = posix::Fd::new(fd, true).map_err(|_| io::Error::last_os_error())?; - - let mut info = ctl_info { - ctl_id: 0, - ctl_name: { - let mut buffer = [0; 96]; - for (i, o) in UTUN_CONTROL_NAME.as_bytes().iter().zip(buffer.iter_mut()) { - *o = *i as _; - } - buffer - }, - }; - - if let Err(err) = ctliocginfo(tun.inner, &mut info as *mut _ as *mut _) { - return Err(io::Error::from(err).into()); - } - - let addr = libc::sockaddr_ctl { - sc_id: info.ctl_id, - sc_len: mem::size_of::() as _, - sc_family: AF_SYSTEM as _, - ss_sysaddr: AF_SYS_CONTROL as _, - sc_unit: id as c_uint, - sc_reserved: [0; 5], - }; - - let address = &addr as *const libc::sockaddr_ctl as *const sockaddr; - if libc::connect(tun.inner, address, mem::size_of_val(&addr) as socklen_t) < 0 { - return Err(io::Error::last_os_error().into()); - } - - let mut tun_name = [0u8; 64]; - let mut name_len: socklen_t = 64; - - let optval = &mut tun_name as *mut _ as *mut c_void; - let optlen = &mut name_len as *mut socklen_t; - if libc::getsockopt(tun.inner, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, optval, optlen) - < 0 - { - return Err(io::Error::last_os_error().into()); - } - - let ctl = Some(posix::Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0), true)?); - - Device { - tun_name: Some( - CStr::from_ptr(tun_name.as_ptr() as *const c_char) - .to_string_lossy() - .into(), - ), - tun: posix::Tun::new(tun, mtu, config.platform_config.packet_information), - ctl, - route: Mutex::new(None), - } - }; + let ctl = Some(posix::Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0), true)?); - crate::configuration::configure(&mut device, config)?; - device.set_alias( - config - .address - .unwrap_or(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))), - config - .destination - .unwrap_or(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 255))), - config - .netmask - .unwrap_or(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 0))), - )?; - - Ok(device) - } else { - unimplemented!() - } + Device { + tun: posix::Tun::new(tun, config.platform_config.packet_information), + alias_lock: Mutex::new(()), + } + }; + crate::configuration::configure(&device, config)?; + Ok(device) } /// Prepare a new request. /// # Safety unsafe fn request(&self) -> Result { - let tun_name = self.tun_name.as_ref().ok_or(Error::InvalidConfig)?; + let tun_name = self.name()?; let mut req: libc::ifreq = mem::zeroed(); ptr::copy_nonoverlapping( tun_name.as_ptr() as *const c_char, @@ -198,19 +150,40 @@ impl Device { Ok(req) } + fn current_route(&self) -> Option { + let addr = self.address().ok()?; + let netmask = self.netmask().ok()?; + let dest = self + .destination() + .unwrap_or(self.calc_dest_addr(addr, netmask).ok()?); + Some(Route { + addr, + netmask, + dest, + }) + } + + fn calc_dest_addr(&self, addr: IpAddr, netmask: IpAddr) -> Result { + let prefix_len = ipnet::ip_mask_to_prefix(netmask).map_err(|_| Error::InvalidConfig)?; + Ok(ipnet::IpNet::new(addr, prefix_len) + .map_err(|_| Error::InvalidConfig)? + .broadcast()) + } + /// Set the IPv4 alias of the device. - fn set_alias(&self, addr: IpAddr, broadaddr: IpAddr, mask: IpAddr) -> Result<()> { - let IpAddr::V4(addr) = addr else { + fn set_alias(&self, addr: IpAddr, dest: IpAddr, mask: IpAddr) -> Result<()> { + let IpAddr::V4(_) = addr else { unimplemented!("do not support IPv6 yet") }; - let IpAddr::V4(broadaddr) = broadaddr else { + let IpAddr::V4(_) = dest else { unimplemented!("do not support IPv6 yet") }; - let IpAddr::V4(mask) = mask else { + let IpAddr::V4(_) = mask else { unimplemented!("do not support IPv6 yet") }; - let tun_name = self.tun_name.as_ref().ok_or(Error::InvalidConfig)?; - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; + let _guard = self.alias_lock.lock().unwrap(); + let old_route = self.current_route(); + let tun_name = self.name()?; unsafe { let mut req: ifaliasreq = mem::zeroed(); ptr::copy_nonoverlapping( @@ -220,18 +193,18 @@ impl Device { ); req.ifra_addr = sockaddr_union::from((addr, 0)).addr; - req.ifra_broadaddr = sockaddr_union::from((broadaddr, 0)).addr; + req.ifra_broadaddr = sockaddr_union::from((dest, 0)).addr; req.ifra_mask = sockaddr_union::from((mask, 0)).addr; - if let Err(err) = siocaifaddr(ctl.as_raw_fd(), &req) { + if let Err(err) = siocaifaddr(ctl()?.as_raw_fd(), &req) { return Err(io::Error::from(err).into()); } - let route = Route { + let new_route = Route { addr, netmask: mask, - dest: broadaddr, + dest, }; - if let Err(e) = self.set_route(route) { + if let Err(e) = self.set_route(old_route, new_route) { log::warn!("{e:?}"); } Ok(()) @@ -243,12 +216,11 @@ impl Device { self.tun.set_nonblock() } - fn set_route(&self, route: Route) -> Result<()> { - let mut route_guard = self.route.lock().unwrap(); - if let Some(v) = &*route_guard { - let prefix_len = ipnet::ip_mask_to_prefix(IpAddr::V4(v.netmask)) - .map_err(|_| Error::InvalidConfig)?; - let network = ipnet::Ipv4Net::new(v.addr, prefix_len) + fn set_route(&self, old_route: Option, new_route: Route) -> Result<()> { + if let Some(v) = old_route { + let prefix_len = + ipnet::ip_mask_to_prefix(v.netmask).map_err(|_| Error::InvalidConfig)?; + let network = ipnet::IpNet::new(v.addr, prefix_len) .map_err(|e| Error::InvalidConfig)? .network(); // command: route -n delete -net 10.0.0.0/24 10.0.0.1 @@ -259,61 +231,131 @@ impl Device { &format!("{}/{}", network, prefix_len), &v.dest.to_string(), ]; - run_command("route", &args)?; - log::info!("route {}", args.join(" ")); + if run_command("route", &args).is_err() { + log::error!("route {}", args.join(" ")); + } else { + log::info!("route {}", args.join(" ")); + } } // command: route -n add -net 10.0.0.9/24 10.0.0.1 - let prefix_len = ipnet::ip_mask_to_prefix(IpAddr::V4(route.netmask)) - .map_err(|_| Error::InvalidConfig)?; + let prefix_len = + ipnet::ip_mask_to_prefix(new_route.netmask).map_err(|_| Error::InvalidConfig)?; let args = [ "-n", "add", "-net", - &format!("{}/{}", route.addr, prefix_len), - &route.dest.to_string(), + &format!("{}/{}", new_route.addr, prefix_len), + &new_route.dest.to_string(), ]; run_command("route", &args)?; log::info!("route {}", args.join(" ")); - *route_guard = Some(route); Ok(()) } /// Recv a packet from tun device - pub fn recv(&self, buf: &mut [u8]) -> io::Result { + pub(crate) fn recv(&self, buf: &mut [u8]) -> io::Result { self.tun.recv(buf) } /// Send a packet to tun device - pub fn send(&self, buf: &[u8]) -> io::Result { + pub(crate) fn send(&self, buf: &[u8]) -> io::Result { self.tun.send(buf) } -} - -impl Read for Device { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.tun.read(buf) - } -} -impl Write for Device { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.tun.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.tun.flush() + #[cfg(feature = "experimental")] + pub(crate) fn shutdown(&self) -> io::Result<()> { + self.tun.shutdown() } + // fn set_address(&self, value: IpAddr) -> Result<()> { + // let IpAddr::V4(value) = value else { + // unimplemented!("do not support IPv6 yet") + // }; + // let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; + // unsafe { + // let mut req = self.request()?; + // ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_addr, OVERWRITE_SIZE); + // if let Err(err) = siocsifaddr(ctl.as_raw_fd(), &req) { + // return Err(io::Error::from(err).into()); + // } + // let route = { *self.route.lock().unwrap() }; + // if let Some(mut route) = route { + // route.addr = value; + // self.set_route(route)?; + // } + // Ok(()) + // } + // } + // fn set_netmask(&self, value: IpAddr) -> Result<()> { + // let IpAddr::V4(value) = value else { + // unimplemented!("do not support IPv6 yet") + // }; + // let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; + // unsafe { + // let mut req = self.request()?; + // // Note: Here should be `ifru_netmask`, but it is not defined in `ifreq`. + // ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_addr, OVERWRITE_SIZE); + // if let Err(err) = siocsifnetmask(ctl.as_raw_fd(), &req) { + // return Err(io::Error::from(err).into()); + // } + // let route = { *self.route.lock().unwrap() }; + // if let Some(mut route) = route { + // route.netmask = value; + // self.set_route(route)?; + // } + // Ok(()) + // } + // } + + // fn set_destination(&self, value: A) -> Result<()> { + // let value = value.into_address()?; + // let IpAddr::V4(value) = value else { + // unimplemented!("do not support IPv6 yet") + // }; + // let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; + // unsafe { + // let mut req = self.request()?; + // ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_dstaddr, OVERWRITE_SIZE); + // if let Err(err) = siocsifdstaddr(ctl.as_raw_fd(), &req) { + // return Err(io::Error::from(err).into()); + // } + // let route = { *self.route.lock().unwrap() }; + // if let Some(mut route) = route { + // route.dest = value; + // self.set_route(route)?; + // } + // Ok(()) + // } + // } } impl AbstractDevice for Device { - fn tun_name(&self) -> Result { - self.tun_name.as_ref().cloned().ok_or(Error::InvalidConfig) + fn name(&self) -> Result { + let mut tun_name = [0u8; 64]; + let mut name_len: socklen_t = 64; + + let optval = &mut tun_name as *mut _ as *mut c_void; + let optlen = &mut name_len as *mut socklen_t; + unsafe { + if libc::getsockopt( + self.tun.as_raw_fd(), + SYSPROTO_CONTROL, + UTUN_OPT_IFNAME, + optval, + optlen, + ) < 0 + { + return Err(io::Error::last_os_error().into()); + } + Ok(CStr::from_ptr(tun_name.as_ptr() as *const c_char) + .to_string_lossy() + .into()) + } } fn enabled(&self, value: bool) -> Result<()> { - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { + let ctl = ctl()?; let mut req = self.request()?; if let Err(err) = siocgifflags(ctl.as_raw_fd(), &mut req) { @@ -335,8 +377,8 @@ impl AbstractDevice for Device { } fn address(&self) -> Result { - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { + let ctl = ctl()?; let mut req = self.request()?; if let Err(err) = siocgifaddr(ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); @@ -346,29 +388,9 @@ impl AbstractDevice for Device { } } - fn set_address(&self, value: IpAddr) -> Result<()> { - let IpAddr::V4(value) = value else { - unimplemented!("do not support IPv6 yet") - }; - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; - unsafe { - let mut req = self.request()?; - ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_addr, OVERWRITE_SIZE); - if let Err(err) = siocsifaddr(ctl.as_raw_fd(), &req) { - return Err(io::Error::from(err).into()); - } - let route = { *self.route.lock().unwrap() }; - if let Some(mut route) = route { - route.addr = value; - self.set_route(route)?; - } - Ok(()) - } - } - fn destination(&self) -> Result { - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { + let ctl = ctl()?; let mut req = self.request()?; if let Err(err) = siocgifdstaddr(ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); @@ -378,30 +400,10 @@ impl AbstractDevice for Device { } } - fn set_destination(&self, value: IpAddr) -> Result<()> { - let IpAddr::V4(value) = value else { - unimplemented!("do not support IPv6 yet") - }; - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; - unsafe { - let mut req = self.request()?; - ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_dstaddr, OVERWRITE_SIZE); - if let Err(err) = siocsifdstaddr(ctl.as_raw_fd(), &req) { - return Err(io::Error::from(err).into()); - } - let route = { *self.route.lock().unwrap() }; - if let Some(mut route) = route { - route.dest = value; - self.set_route(route)?; - } - Ok(()) - } - } - /// Question on macOS fn broadcast(&self) -> Result { - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { + let ctl = ctl()?; let mut req = self.request()?; if let Err(err) = siocgifbrdaddr(ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); @@ -412,12 +414,13 @@ impl AbstractDevice for Device { } /// Question on macOS - fn set_broadcast(&self, value: IpAddr) -> Result<()> { + fn set_broadcast(&self, value: A) -> Result<()> { + let value = value.into_address()?; let IpAddr::V4(value) = value else { unimplemented!("do not support IPv6 yet") }; - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { + let ctl = ctl()?; let mut req = self.request()?; ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_broadaddr, OVERWRITE_SIZE); if let Err(err) = siocsifbrdaddr(ctl.as_raw_fd(), &req) { @@ -428,8 +431,8 @@ impl AbstractDevice for Device { } fn netmask(&self) -> Result { - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { + let ctl = ctl()?; let mut req = self.request()?; if let Err(err) = siocgifnetmask(ctl.as_raw_fd(), &mut req) { return Err(io::Error::from(err).into()); @@ -439,30 +442,9 @@ impl AbstractDevice for Device { } } - fn set_netmask(&self, value: IpAddr) -> Result<()> { - let IpAddr::V4(value) = value else { - unimplemented!("do not support IPv6 yet") - }; - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; - unsafe { - let mut req = self.request()?; - // Note: Here should be `ifru_netmask`, but it is not defined in `ifreq`. - ipaddr_to_sockaddr(value, 0, &mut req.ifr_ifru.ifru_addr, OVERWRITE_SIZE); - if let Err(err) = siocsifnetmask(ctl.as_raw_fd(), &req) { - return Err(io::Error::from(err).into()); - } - let route = { *self.route.lock().unwrap() }; - if let Some(mut route) = route { - route.netmask = value; - self.set_route(route)?; - } - Ok(()) - } - } - fn mtu(&self) -> Result { - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { + let ctl = ctl()?; let mut req = self.request()?; if let Err(err) = siocgifmtu(ctl.as_raw_fd(), &mut req) { @@ -477,30 +459,31 @@ impl AbstractDevice for Device { } fn set_mtu(&self, value: u16) -> Result<()> { - let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { + let ctl = ctl()?; let mut req = self.request()?; req.ifr_ifru.ifru_mtu = value as i32; if let Err(err) = siocsifmtu(ctl.as_raw_fd(), &req) { return Err(io::Error::from(err).into()); } - self.tun.set_mtu(value); Ok(()) } } - fn set_network_address( + fn set_network_address( &self, - address: IpAddr, - netmask: IpAddr, - destination: Option, + address: A, + netmask: A, + destination: Option, ) -> Result<()> { - self.set_address(address)?; - self.set_netmask(netmask)?; - if let Some(dest) = destination { - self.set_destination(dest)?; - } + let addr = address.into_address()?; + let netmask = netmask.into_address()?; + let default_dest = self.calc_dest_addr(addr, netmask)?; + let dest = destination + .map(|d| d.into_address().unwrap_or(default_dest)) + .unwrap_or(default_dest); + self.set_alias(addr, dest, netmask)?; Ok(()) } diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index ae35ba0..dfd4f31 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -1,64 +1,72 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -//! macOS specific functionality. - -pub mod sys; - -mod device; -pub use self::device::Device; - -use crate::configuration::Configuration; -use crate::error::Result; - -/// macOS-only interface configuration. -#[derive(Copy, Clone, Debug)] -pub struct PlatformConfig { - pub(crate) packet_information: bool, -} - -impl Default for PlatformConfig { - fn default() -> Self { - PlatformConfig { - packet_information: true, // default is true in macOS - } - } -} - -impl PlatformConfig { - /// Enable or disable packet information, the first 4 bytes of - /// each packet delivered from/to macOS underlying API is a header with flags and protocol type when enabled. - /// - /// - If we open an `utun` device, there always exist PI. - /// - /// - If we use `Network Extension` to build our App: - /// - /// - If get the fd from - /// ```Objective-C - /// int32_t tunFd = [[NEPacketTunnelProvider::packetFlow valueForKeyPath:@"socket.fileDescriptor"] intValue]; - /// ``` - /// there exist PI. - /// - /// - But if get packet from `[NEPacketTunnelProvider::packetFlow readPacketsWithCompletionHandler:]` - /// and write packet via `[NEPacketTunnelProvider::packetFlow writePackets:withProtocols:]`, there is no PI. - pub fn packet_information(&mut self, value: bool) -> &mut Self { - self.packet_information = value; - self - } -} - -/// Create a TUN device with the given name. -pub fn create(configuration: &Configuration) -> Result { - Device::new(configuration) -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +//! macOS specific functionality. + +pub mod sys; + +mod device; + +pub use self::device::Device; +use std::os::fd::{FromRawFd, RawFd}; + +use crate::configuration::Configuration; +use crate::error::Result; + +/// macOS-only interface configuration. +#[derive(Copy, Clone, Debug)] +pub struct PlatformConfig { + pub(crate) packet_information: bool, +} + +impl Default for PlatformConfig { + fn default() -> Self { + PlatformConfig { + packet_information: true, // default is true in macOS + } + } +} + +impl PlatformConfig { + /// Enable or disable packet information, the first 4 bytes of + /// each packet delivered from/to macOS underlying API is a header with flags and protocol type when enabled. + /// + /// - If we open an `utun` device, there always exist PI. + /// + /// - If we use `Network Extension` to build our App: + /// + /// - If get the fd from + /// ```Objective-C + /// int32_t tunFd = [[NEPacketTunnelProvider::packetFlow valueForKeyPath:@"socket.fileDescriptor"] intValue]; + /// ``` + /// there exist PI. + /// + /// - But if get packet from `[NEPacketTunnelProvider::packetFlow readPacketsWithCompletionHandler:]` + /// and write packet via `[NEPacketTunnelProvider::packetFlow writePackets:withProtocols:]`, there is no PI. + pub fn packet_information(&mut self, value: bool) -> &mut Self { + self.packet_information = value; + self + } +} + +use super::Device as DeviceWrapper; +/// Create a TUN device with the given name. +pub fn create(configuration: &Configuration) -> Result { + Ok(DeviceWrapper(Device::new(configuration)?)) +} +/// # Safety +/// The fd passed in must be an owned file descriptor; in particular, it must be open. +pub unsafe fn create_with_fd(fd: RawFd) -> Result { + Ok(DeviceWrapper(Device::from_raw_fd(fd))) +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 15c3f05..85795a9 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,83 +1,132 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -//! Platform specific modules. - -#[cfg(unix)] -pub mod posix; - -#[cfg(target_os = "linux")] -pub mod linux; -#[cfg(target_os = "linux")] -pub use self::linux::{create, Device, PlatformConfig}; - -#[cfg(target_os = "freebsd")] -pub mod freebsd; -#[cfg(target_os = "freebsd")] -pub use self::freebsd::{create, Device, PlatformConfig}; - -#[cfg(target_os = "macos")] -pub mod macos; -#[cfg(target_os = "macos")] -pub use self::macos::{create, Device, PlatformConfig}; - -#[cfg(target_os = "ios")] -pub mod ios; -#[cfg(target_os = "ios")] -pub use self::ios::{create, Device, PlatformConfig}; - -#[cfg(target_os = "android")] -pub mod android; -#[cfg(target_os = "android")] -pub use self::android::{create, Device, PlatformConfig}; - -#[cfg(unix)] -pub use crate::platform::posix::Tun; - -#[cfg(target_os = "windows")] -pub mod windows; -#[cfg(target_os = "windows")] -pub use self::windows::{create, Device, PlatformConfig, Tun}; - -#[cfg(test)] -mod test { - use crate::configuration::Configuration; - use crate::device::AbstractDevice; - use std::net::Ipv4Addr; - - #[test] - fn create() { - let dev = super::create( - Configuration::default() - .tun_name("utun6") - .address("192.168.50.1") - .netmask("255.255.0.0") - .mtu(crate::DEFAULT_MTU) - .up(), - ) - .unwrap(); - - assert_eq!( - "192.168.50.1".parse::().unwrap(), - dev.address().unwrap() - ); - - assert_eq!( - "255.255.0.0".parse::().unwrap(), - dev.netmask().unwrap() - ); - - assert_eq!(crate::DEFAULT_MTU, dev.mtu().unwrap()); - } -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +//! Platform specific modules. + +#[cfg(unix)] +pub mod posix; + +#[cfg(target_os = "linux")] +pub mod linux; +use std::ops::Deref; + +#[cfg(target_os = "linux")] +pub use self::linux::{Device as DeviceInner, *}; + +#[cfg(target_os = "freebsd")] +pub mod freebsd; +#[cfg(target_os = "freebsd")] +pub use self::freebsd::{Device as DeviceInner, *}; + +#[cfg(target_os = "macos")] +pub mod macos; +#[cfg(target_os = "macos")] +pub use self::macos::{Device as DeviceInner, *}; + +#[cfg(target_os = "ios")] +pub mod ios; +#[cfg(target_os = "ios")] +pub use self::ios::{Device as DeviceInner, *}; + +#[cfg(target_os = "android")] +pub mod android; +#[cfg(target_os = "android")] +pub use self::android::{Device as DeviceInner, *}; + +#[cfg(unix)] +pub use crate::platform::posix::Tun; + +#[cfg(target_os = "windows")] +pub mod windows; +#[cfg(target_os = "windows")] +pub use self::windows::{create, Device as DeviceInner, PlatformConfig, Tun}; + +#[cfg(target_family = "unix")] +use std::os::unix::io::RawFd; + +#[repr(transparent)] +pub struct Device(pub(crate) DeviceInner); + +impl Device { + /// Recv a packet from tun device + #[inline] + pub fn recv(&self, buf: &mut [u8]) -> std::io::Result { + self.0.recv(buf) + } + + /// Send a packet to tun device + #[inline] + pub fn send(&self, buf: &[u8]) -> std::io::Result { + self.0.send(buf) + } + + #[cfg_attr(docsrs, doc(cfg(feature = "experimental")))] + #[cfg(any(feature = "experimental", target_os = "windows"))] + /// Do not use nonblocking fd when you want to use shutdown + pub fn shutdown(&self) -> std::io::Result<()> { + self.0.shutdown() + } +} + +#[cfg(target_family = "unix")] +impl std::os::fd::IntoRawFd for Device { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl Deref for Device { + type Target = DeviceInner; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "macos", + target_os = "freebsd", +))] +#[cfg(test)] +mod test { + use crate::configuration::Configuration; + use crate::device::AbstractDevice; + use std::net::Ipv4Addr; + + #[test] + fn create() { + let dev = super::create( + Configuration::default() + .name("utun6") + .address_with_prefix("192.168.50.1", 24) + .mtu(crate::DEFAULT_MTU) + .up(), + ) + .unwrap(); + + assert_eq!( + "192.168.50.1".parse::().unwrap(), + dev.address().unwrap() + ); + + assert_eq!( + "255.255.0.0".parse::().unwrap(), + dev.netmask().unwrap() + ); + + assert_eq!(crate::DEFAULT_MTU, dev.mtu().unwrap()); + } +} diff --git a/src/platform/posix/fd.rs b/src/platform/posix/fd.rs index 1b8c883..e19dc67 100644 --- a/src/platform/posix/fd.rs +++ b/src/platform/posix/fd.rs @@ -1,84 +1,225 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -use crate::error::{Error, Result}; -use libc::{self, fcntl, F_GETFL, F_SETFL, O_NONBLOCK}; -use std::io; -use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; - -/// POSIX file descriptor support for `io` traits. -pub(crate) struct Fd { - pub(crate) inner: RawFd, - close_fd_on_drop: bool, -} - -impl Fd { - pub fn new(value: RawFd, close_fd_on_drop: bool) -> Result { - if value < 0 { - return Err(Error::InvalidDescriptor); - } - Ok(Fd { - inner: value, - close_fd_on_drop, - }) - } - - /// Enable non-blocking mode - pub fn set_nonblock(&self) -> io::Result<()> { - match unsafe { fcntl(self.inner, F_SETFL, fcntl(self.inner, F_GETFL) | O_NONBLOCK) } { - 0 => Ok(()), - _ => Err(io::Error::last_os_error()), - } - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let fd = self.as_raw_fd(); - let amount = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) }; - if amount < 0 { - return Err(io::Error::last_os_error()); - } - Ok(amount as usize) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let fd = self.as_raw_fd(); - let amount = unsafe { libc::write(fd, buf.as_ptr() as *const _, buf.len()) }; - if amount < 0 { - return Err(io::Error::last_os_error()); - } - Ok(amount as usize) - } -} - -impl AsRawFd for Fd { - fn as_raw_fd(&self) -> RawFd { - self.inner - } -} - -impl IntoRawFd for Fd { - fn into_raw_fd(mut self) -> RawFd { - let fd = self.inner; - self.inner = -1; - fd - } -} - -impl Drop for Fd { - fn drop(&mut self) { - if self.close_fd_on_drop && self.inner >= 0 { - unsafe { libc::close(self.inner) }; - } - } -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +use std::io; +use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; +#[cfg(feature = "experimental")] +use std::sync::atomic::{AtomicBool, Ordering}; + +use libc::{self, fcntl, F_GETFL, F_SETFL, O_NONBLOCK}; + +use crate::error::{Error, Result}; + +/// POSIX file descriptor support for `io` traits. +pub(crate) struct Fd { + pub(crate) inner: RawFd, + close_fd_on_drop: bool, + #[cfg(feature = "experimental")] + is_shutdown: AtomicBool, + #[cfg(feature = "experimental")] + event_fd: EventFd, +} + +impl Fd { + pub fn new(value: RawFd, close_fd_on_drop: bool) -> Result { + if value < 0 { + return Err(Error::InvalidDescriptor); + } + Ok(Fd { + inner: value, + close_fd_on_drop, + #[cfg(feature = "experimental")] + is_shutdown: AtomicBool::new(false), + #[cfg(feature = "experimental")] + event_fd: EventFd::new()?, + }) + } + + /// Enable non-blocking mode + pub fn set_nonblock(&self) -> io::Result<()> { + match unsafe { fcntl(self.inner, F_SETFL, fcntl(self.inner, F_GETFL) | O_NONBLOCK) } { + 0 => Ok(()), + _ => Err(io::Error::last_os_error()), + } + } + + #[inline] + fn read0(&self, buf: &mut [u8]) -> io::Result { + let fd = self.as_raw_fd(); + let amount = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) }; + if amount < 0 { + return Err(io::Error::last_os_error()); + } + Ok(amount as usize) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let fd = self.as_raw_fd(); + let amount = unsafe { libc::write(fd, buf.as_ptr() as *const _, buf.len()) }; + if amount < 0 { + return Err(io::Error::last_os_error()); + } + Ok(amount as usize) + } +} +#[cfg(not(feature = "experimental"))] +impl Fd { + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.read0(buf) + } +} +#[cfg(feature = "experimental")] +impl Fd { + fn is_fd_nonblocking(&self) -> io::Result { + unsafe { + let flags = fcntl(self.inner, F_GETFL); + if flags == -1 { + return Err(io::Error::last_os_error()); + } + Ok((flags & O_NONBLOCK) != 0) + } + } + pub fn read(&self, buf: &mut [u8]) -> io::Result { + if self.is_shutdown.load(Ordering::Relaxed) { + return Err(io::Error::new(io::ErrorKind::ConnectionAborted, "close")); + } + if self.is_fd_nonblocking()? { + return self.read0(buf); + } + let fd = self.as_raw_fd() as libc::c_int; + + let event_fd = self.event_fd.as_event_fd(); + let mut readfds: libc::fd_set = unsafe { std::mem::zeroed() }; + unsafe { + libc::FD_SET(fd, &mut readfds); + libc::FD_SET(event_fd, &mut readfds); + } + let result = unsafe { + libc::select( + fd.max(event_fd) + 1, + &mut readfds, + std::ptr::null_mut(), + std::ptr::null_mut(), + std::ptr::null_mut(), + ) + }; + if self.is_shutdown.load(Ordering::SeqCst) { + return Err(io::Error::new(io::ErrorKind::ConnectionAborted, "close")); + } + if result == -1 { + return Err(io::Error::last_os_error()); + } + if result == 0 { + return Err(io::Error::from(io::ErrorKind::TimedOut)); + } + self.read0(buf) + } + pub fn shutdown(&self) -> io::Result<()> { + self.is_shutdown.store(true, Ordering::SeqCst); + self.event_fd.wake() + } +} + +#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] +#[cfg(feature = "experimental")] +struct EventFd(std::fs::File); +#[cfg(feature = "experimental")] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] +impl EventFd { + fn new() -> io::Result { + #[cfg(not(target_os = "espidf"))] + let flags = libc::EFD_CLOEXEC | libc::EFD_NONBLOCK; + // ESP-IDF is EFD_NONBLOCK by default and errors if you try to pass this flag. + #[cfg(target_os = "espidf")] + let flags = 0; + let event_fd = unsafe { libc::eventfd(0, flags) }; + if event_fd < 0 { + return Err(io::Error::last_os_error()); + } + use std::os::fd::FromRawFd; + let file = unsafe { std::fs::File::from_raw_fd(event_fd) }; + Ok(Self(file)) + } + fn wake(&self) -> io::Result<()> { + use std::io::Write; + let buf: [u8; 8] = 1u64.to_ne_bytes(); + match (&self.0).write_all(&buf) { + Ok(_) => Ok(()), + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(()), + Err(err) => Err(err), + } + } + fn as_event_fd(&self) -> libc::c_int { + self.0.as_raw_fd() as _ + } +} +#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(feature = "experimental")] +struct EventFd(libc::c_int, libc::c_int); +#[cfg(feature = "experimental")] +#[cfg(any(target_os = "macos", target_os = "ios"))] +impl EventFd { + fn new() -> io::Result { + let mut fds: [libc::c_int; 2] = [0; 2]; + if unsafe { libc::pipe(fds.as_mut_ptr()) } == -1 { + return Err(io::Error::last_os_error()); + } + let read_fd = fds[0]; + let write_fd = fds[1]; + Ok(Self(read_fd, write_fd)) + } + fn wake(&self) -> io::Result<()> { + let buf: [u8; 8] = 1u64.to_ne_bytes(); + let res = unsafe { libc::write(self.1, buf.as_ptr() as *const libc::c_void, buf.len()) }; + if res == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } + fn as_event_fd(&self) -> libc::c_int { + self.0 + } +} +#[cfg(feature = "experimental")] +#[cfg(any(target_os = "macos", target_os = "ios"))] +impl Drop for EventFd { + fn drop(&mut self) { + unsafe { + let _ = libc::close(self.0); + let _ = libc::close(self.1); + } + } +} +impl AsRawFd for Fd { + fn as_raw_fd(&self) -> RawFd { + self.inner + } +} + +impl IntoRawFd for Fd { + fn into_raw_fd(mut self) -> RawFd { + let fd = self.inner; + self.inner = -1; + fd + } +} + +impl Drop for Fd { + fn drop(&mut self) { + if self.close_fd_on_drop && self.inner >= 0 { + unsafe { libc::close(self.inner) }; + } + } +} diff --git a/src/platform/posix/sockaddr.rs b/src/platform/posix/sockaddr.rs index 7a46db6..526e9be 100644 --- a/src/platform/posix/sockaddr.rs +++ b/src/platform/posix/sockaddr.rs @@ -1,163 +1,166 @@ -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// Version 2, December 2004 -// -// Copyleft (ↄ) meh. | http://meh.schizofreni.co -// -// Everyone is permitted to copy and distribute verbatim or modified -// copies of this license document, and changing it is allowed as long -// as the name is changed. -// -// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE -// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -// -// 0. You just DO WHAT THE FUCK YOU WANT TO. - -/// # Safety -unsafe fn sockaddr_to_rs_addr(sa: &sockaddr_union) -> Option { - match sa.addr_stor.ss_family as libc::c_int { - libc::AF_INET => { - let sa_in = sa.addr4; - let ip = std::net::Ipv4Addr::from(sa_in.sin_addr.s_addr.to_ne_bytes()); - let port = u16::from_be(sa_in.sin_port); - Some(std::net::SocketAddr::new(ip.into(), port)) - } - libc::AF_INET6 => { - let sa_in6 = sa.addr6; - let ip = std::net::Ipv6Addr::from(sa_in6.sin6_addr.s6_addr); - let port = u16::from_be(sa_in6.sin6_port); - Some(std::net::SocketAddr::new(ip.into(), port)) - } - _ => None, - } -} - -fn rs_addr_to_sockaddr(addr: std::net::SocketAddr) -> sockaddr_union { - match addr { - std::net::SocketAddr::V4(ipv4) => { - let mut addr: sockaddr_union = unsafe { std::mem::zeroed() }; - #[cfg(any(target_os = "freebsd", target_os = "macos"))] - { - addr.addr4.sin_len = std::mem::size_of::() as u8; - } - addr.addr4.sin_family = libc::AF_INET as libc::sa_family_t; - addr.addr4.sin_addr.s_addr = u32::from_ne_bytes(ipv4.ip().octets()); - addr.addr4.sin_port = ipv4.port().to_be(); - addr - } - std::net::SocketAddr::V6(ipv6) => { - let mut addr: sockaddr_union = unsafe { std::mem::zeroed() }; - #[cfg(any(target_os = "freebsd", target_os = "macos"))] - { - addr.addr6.sin6_len = std::mem::size_of::() as u8; - } - addr.addr6.sin6_family = libc::AF_INET6 as libc::sa_family_t; - addr.addr6.sin6_addr.s6_addr = ipv6.ip().octets(); - addr.addr6.sin6_port = ipv6.port().to_be(); - addr - } - } -} - -/// # Safety -/// Fill the `addr` with the `src_addr` and `src_port`, the `size` should be the size of overwriting -#[cfg(any(target_os = "linux", target_os = "macos"))] -pub(crate) unsafe fn ipaddr_to_sockaddr( - src_addr: T, - src_port: u16, - addr: &mut libc::sockaddr, - size: usize, -) where - T: Into, -{ - let sa = rs_addr_to_sockaddr((src_addr.into(), src_port).into()); - std::ptr::copy_nonoverlapping( - &sa as *const _ as *const libc::c_void, - addr as *mut _ as *mut libc::c_void, - size.min(std::mem::size_of::()), - ); -} - -#[repr(C)] -#[derive(Clone, Copy)] -pub union sockaddr_union { - pub addr_stor: libc::sockaddr_storage, - pub addr6: libc::sockaddr_in6, - pub addr4: libc::sockaddr_in, - pub addr: libc::sockaddr, -} - -impl From for sockaddr_union { - fn from(addr: libc::sockaddr_storage) -> Self { - sockaddr_union { addr_stor: addr } - } -} - -impl From for sockaddr_union { - fn from(addr: libc::sockaddr_in6) -> Self { - sockaddr_union { addr6: addr } - } -} - -impl From for sockaddr_union { - fn from(addr: libc::sockaddr_in) -> Self { - sockaddr_union { addr4: addr } - } -} - -impl From for sockaddr_union { - fn from(addr: libc::sockaddr) -> Self { - sockaddr_union { addr } - } -} - -impl From for sockaddr_union { - fn from(addr: std::net::SocketAddr) -> Self { - rs_addr_to_sockaddr(addr) - } -} - -impl TryFrom for std::net::SocketAddr { - type Error = std::io::Error; - - fn try_from(addr: sockaddr_union) -> Result { - unsafe { sockaddr_to_rs_addr(&addr).ok_or(std::io::ErrorKind::InvalidInput.into()) } - } -} - -impl> From<(T, u16)> for sockaddr_union { - fn from((ip, port): (T, u16)) -> Self { - let ip: std::net::IpAddr = ip.into(); - rs_addr_to_sockaddr(std::net::SocketAddr::new(ip, port)) - } -} - -#[test] -fn test_conversion() { - let old = std::net::SocketAddr::new([127, 0, 0, 1].into(), 0x0208); - let addr = rs_addr_to_sockaddr(old); - unsafe { - if cfg!(target_endian = "big") { - assert_eq!(0x7f000001, addr.addr4.sin_addr.s_addr); - assert_eq!(0x0208, addr.addr4.sin_port); - } else if cfg!(target_endian = "little") { - assert_eq!(0x0100007f, addr.addr4.sin_addr.s_addr); - assert_eq!(0x0802, addr.addr4.sin_port); - } else { - unreachable!(); - } - }; - let ip = unsafe { sockaddr_to_rs_addr(&addr).unwrap() }; - assert_eq!(ip, old); - - let old = std::net::SocketAddr::new(std::net::Ipv6Addr::LOCALHOST.into(), 0x0208); - let addr = rs_addr_to_sockaddr(old); - let ip = unsafe { sockaddr_to_rs_addr(&addr).unwrap() }; - assert_eq!(ip, old); - - let old = std::net::IpAddr::V4([10, 0, 0, 33].into()); - let mut addr: sockaddr_union = unsafe { std::mem::zeroed() }; - let size = std::mem::size_of::(); - unsafe { ipaddr_to_sockaddr(old, 0x0208, &mut addr.addr, size) }; - let ip = unsafe { sockaddr_to_rs_addr(&addr).unwrap() }; - assert_eq!(ip, std::net::SocketAddr::new(old, 0x0208)); -} +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// Version 2, December 2004 +// +// Copyleft (ↄ) meh. | http://meh.schizofreni.co +// +// Everyone is permitted to copy and distribute verbatim or modified +// copies of this license document, and changing it is allowed as long +// as the name is changed. +// +// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +// +// 0. You just DO WHAT THE FUCK YOU WANT TO. + +/// # Safety +unsafe fn sockaddr_to_rs_addr(sa: &sockaddr_union) -> Option { + match sa.addr_stor.ss_family as libc::c_int { + libc::AF_INET => { + let sa_in = sa.addr4; + let ip = std::net::Ipv4Addr::from(sa_in.sin_addr.s_addr.to_ne_bytes()); + let port = u16::from_be(sa_in.sin_port); + Some(std::net::SocketAddr::new(ip.into(), port)) + } + libc::AF_INET6 => { + let sa_in6 = sa.addr6; + let ip = std::net::Ipv6Addr::from(sa_in6.sin6_addr.s6_addr); + let port = u16::from_be(sa_in6.sin6_port); + Some(std::net::SocketAddr::new(ip.into(), port)) + } + _ => None, + } +} + +fn rs_addr_to_sockaddr(addr: std::net::SocketAddr) -> sockaddr_union { + match addr { + std::net::SocketAddr::V4(ipv4) => { + let mut addr: sockaddr_union = unsafe { std::mem::zeroed() }; + #[cfg(any(target_os = "freebsd", target_os = "macos"))] + { + addr.addr4.sin_len = std::mem::size_of::() as u8; + } + addr.addr4.sin_family = libc::AF_INET as libc::sa_family_t; + addr.addr4.sin_addr.s_addr = u32::from_ne_bytes(ipv4.ip().octets()); + addr.addr4.sin_port = ipv4.port().to_be(); + addr + } + std::net::SocketAddr::V6(ipv6) => { + let mut addr: sockaddr_union = unsafe { std::mem::zeroed() }; + #[cfg(any(target_os = "freebsd", target_os = "macos"))] + { + addr.addr6.sin6_len = std::mem::size_of::() as u8; + } + addr.addr6.sin6_family = libc::AF_INET6 as libc::sa_family_t; + addr.addr6.sin6_addr.s6_addr = ipv6.ip().octets(); + addr.addr6.sin6_port = ipv6.port().to_be(); + addr + } + } +} + +/// # Safety +/// Fill the `addr` with the `src_addr` and `src_port`, the `size` should be the size of overwriting +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub(crate) unsafe fn ipaddr_to_sockaddr( + src_addr: T, + src_port: u16, + addr: &mut libc::sockaddr, + size: usize, +) where + T: Into, +{ + let sa = rs_addr_to_sockaddr((src_addr.into(), src_port).into()); + std::ptr::copy_nonoverlapping( + &sa as *const _ as *const libc::c_void, + addr as *mut _ as *mut libc::c_void, + size.min(std::mem::size_of::()), + ); +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union sockaddr_union { + pub addr_stor: libc::sockaddr_storage, + pub addr6: libc::sockaddr_in6, + pub addr4: libc::sockaddr_in, + pub addr: libc::sockaddr, +} + +impl From for sockaddr_union { + fn from(addr: libc::sockaddr_storage) -> Self { + sockaddr_union { addr_stor: addr } + } +} + +impl From for sockaddr_union { + fn from(addr: libc::sockaddr_in6) -> Self { + sockaddr_union { addr6: addr } + } +} + +impl From for sockaddr_union { + fn from(addr: libc::sockaddr_in) -> Self { + sockaddr_union { addr4: addr } + } +} + +impl From for sockaddr_union { + fn from(addr: libc::sockaddr) -> Self { + sockaddr_union { addr } + } +} + +impl From for sockaddr_union { + fn from(addr: std::net::SocketAddr) -> Self { + rs_addr_to_sockaddr(addr) + } +} + +impl TryFrom for std::net::SocketAddr { + type Error = std::io::Error; + + fn try_from(addr: sockaddr_union) -> Result { + unsafe { sockaddr_to_rs_addr(&addr).ok_or(std::io::ErrorKind::InvalidInput.into()) } + } +} + +impl> From<(T, u16)> for sockaddr_union { + fn from((ip, port): (T, u16)) -> Self { + let ip: std::net::IpAddr = ip.into(); + rs_addr_to_sockaddr(std::net::SocketAddr::new(ip, port)) + } +} + +#[test] +fn test_conversion() { + let old = std::net::SocketAddr::new([127, 0, 0, 1].into(), 0x0208); + let addr = rs_addr_to_sockaddr(old); + unsafe { + if cfg!(target_endian = "big") { + assert_eq!(0x7f000001, addr.addr4.sin_addr.s_addr); + assert_eq!(0x0208, addr.addr4.sin_port); + } else if cfg!(target_endian = "little") { + assert_eq!(0x0100007f, addr.addr4.sin_addr.s_addr); + assert_eq!(0x0802, addr.addr4.sin_port); + } else { + unreachable!(); + } + }; + let ip = unsafe { sockaddr_to_rs_addr(&addr).unwrap() }; + assert_eq!(ip, old); + + let old = std::net::SocketAddr::new(std::net::Ipv6Addr::LOCALHOST.into(), 0x0208); + let addr = rs_addr_to_sockaddr(old); + let ip = unsafe { sockaddr_to_rs_addr(&addr).unwrap() }; + assert_eq!(ip, old); + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + let old = std::net::IpAddr::V4([10, 0, 0, 33].into()); + let mut addr: sockaddr_union = unsafe { std::mem::zeroed() }; + let size = std::mem::size_of::(); + + unsafe { ipaddr_to_sockaddr(old, 0x0208, &mut addr.addr, size) }; + let ip = unsafe { sockaddr_to_rs_addr(&addr).unwrap() }; + assert_eq!(ip, std::net::SocketAddr::new(old, 0x0208)); + } +} diff --git a/src/platform/posix/tun.rs b/src/platform/posix/tun.rs index cefb650..ab51ee9 100644 --- a/src/platform/posix/tun.rs +++ b/src/platform/posix/tun.rs @@ -17,7 +17,6 @@ use crate::PACKET_INFORMATION_LENGTH as PIL; use bytes::BufMut; use std::io::{self, Read, Write}; use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; -use std::sync::RwLock; /// Infer the protocol based on the first nibble in the packet buffer. pub(crate) fn is_ipv6(buf: &[u8]) -> std::io::Result { @@ -123,17 +122,18 @@ macro_rules! need_mut { pub struct Tun { pub(crate) fd: Fd, pub(crate) offset: usize, - pub(crate) mtu: RwLock, pub(crate) packet_information: bool, } impl Tun { - pub(crate) fn new(fd: Fd, mtu: u16, packet_information: bool) -> Self { - let offset = if packet_information { PIL } else { 0 }; + pub(crate) fn new(fd: Fd, packet_information: bool) -> Self { + #[cfg(any(target_os = "macos", target_os = "ios"))] + let offset = if !packet_information { PIL } else { 0 }; + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + let offset = 0; Self { fd, offset, - mtu: RwLock::new(mtu), packet_information, } } @@ -142,71 +142,60 @@ impl Tun { self.fd.set_nonblock() } - pub fn set_mtu(&self, value: u16) { - *self.mtu.write().unwrap() = value; - } - - pub fn mtu(&self) -> u16 { - *self.mtu.read().unwrap() - } - pub fn packet_information(&self) -> bool { self.packet_information } pub(crate) fn send(&self, in_buf: &[u8]) -> io::Result { - const STACK_BUF_LEN: usize = crate::DEFAULT_MTU as usize + PIL; - let in_buf_len = in_buf.len() + self.offset; - - // The following logic is to prevent dynamically allocating Vec on every send - // As long as the MTU is set to value lesser than 1500, this api uses `stack_buf` - // and avoids `Vec` allocation - let local_buf_v0 = - local_buf_util!(in_buf_len > STACK_BUF_LEN && self.offset != 0, in_buf_len); - need_mut! {local_buf_v1,local_buf_v0}; - #[allow(clippy::useless_asref)] - let local_buf = local_buf_v1.as_mut(); - - let either_buf = if self.offset != 0 { + if self.offset != 0 { let ipv6 = is_ipv6(in_buf)?; if let Some(header) = generate_packet_information(true, ipv6) { + const STACK_BUF_LEN: usize = crate::DEFAULT_MTU as usize + PIL; + let in_buf_len = in_buf.len() + self.offset; + + // The following logic is to prevent dynamically allocating Vec on every send + // As long as the MTU is set to value lesser than 1500, this api uses `stack_buf` + // and avoids `Vec` allocation + let local_buf_v0 = + local_buf_util!(in_buf_len > STACK_BUF_LEN && self.offset != 0, in_buf_len); + need_mut! {local_buf_v1,local_buf_v0}; + #[allow(clippy::useless_asref)] + let local_buf = local_buf_v1.as_mut(); + (&mut local_buf[..self.offset]).put_slice(header.as_ref()); (&mut local_buf[self.offset..in_buf_len]).put_slice(in_buf); - &local_buf[..in_buf_len] - } else { - in_buf + let amount = self.fd.write(&local_buf[..in_buf_len])?; + return Ok(amount - self.offset); } - } else { - in_buf - }; - let amount = self.fd.write(either_buf)?; + } + let amount = self.fd.write(in_buf)?; Ok(amount - self.offset) } pub(crate) fn recv(&self, mut in_buf: &mut [u8]) -> io::Result { - const STACK_BUF_LEN: usize = crate::DEFAULT_MTU as usize + PIL; - let in_buf_len = in_buf.len() + self.offset; - - // The following logic is to prevent dynamically allocating Vec on every recv - // As long as the MTU is set to value lesser than 1500, this api uses `stack_buf` - // and avoids `Vec` allocation - - let local_buf_v0 = - local_buf_util!(in_buf_len > STACK_BUF_LEN && self.offset != 0, in_buf_len); - need_mut! {local_buf_v1,local_buf_v0}; - #[allow(clippy::useless_asref)] - let local_buf = local_buf_v1.as_mut(); - - let either_buf = if self.offset != 0 { - &mut *local_buf - } else { - &mut *in_buf - }; - let amount = self.fd.read(either_buf)?; if self.offset != 0 { + const STACK_BUF_LEN: usize = crate::DEFAULT_MTU as usize + PIL; + let in_buf_len = in_buf.len() + self.offset; + + // The following logic is to prevent dynamically allocating Vec on every recv + // As long as the MTU is set to value lesser than 1500, this api uses `stack_buf` + // and avoids `Vec` allocation + + let local_buf_v0 = + local_buf_util!(in_buf_len > STACK_BUF_LEN && self.offset != 0, in_buf_len); + need_mut! {local_buf_v1,local_buf_v0}; + #[allow(clippy::useless_asref)] + let local_buf = local_buf_v1.as_mut(); + let amount = self.fd.read(local_buf)?; in_buf.put_slice(&local_buf[self.offset..amount]); + Ok(amount - self.offset) + } else { + self.fd.read(in_buf) } - Ok(amount - self.offset) + } + #[cfg(feature = "experimental")] + pub(crate) fn shutdown(&self) -> io::Result<()> { + self.fd.shutdown() } } @@ -234,6 +223,6 @@ impl AsRawFd for Tun { impl IntoRawFd for Tun { fn into_raw_fd(self) -> RawFd { - self.fd.as_raw_fd() + self.fd.into_raw_fd() } } diff --git a/src/platform/windows/device.rs b/src/platform/windows/device.rs index 3c05946..40fb4e2 100644 --- a/src/platform/windows/device.rs +++ b/src/platform/windows/device.rs @@ -12,65 +12,26 @@ // // 0. You just DO WHAT THE FUCK YOU WANT TO. -use std::io::{self, Read, Write}; -use std::net::{IpAddr, Ipv4Addr}; +use std::io; +use std::net::IpAddr; use std::sync::Arc; +use wintun::{Packet, Session}; + use crate::configuration::{configure, Configuration}; -use crate::device::AbstractDevice; +use crate::device::{AbstractDevice, ETHER_ADDR_LEN}; use crate::error::{Error, Result}; use crate::platform::windows::netsh; +use crate::platform::windows::tap::TapDevice; use crate::platform::windows::verify_dll_file::{ get_dll_absolute_path, get_signer_name, verify_embedded_signature, }; -use crate::Layer; -use winapi::shared::ifdef::NET_LUID; -use winapi::shared::minwindef::DWORD; -use winapi::um::fileapi::OPEN_EXISTING; -use winapi::um::winbase::FILE_FLAG_OVERLAPPED; -use winapi::um::winioctl::{FILE_ANY_ACCESS, FILE_DEVICE_UNKNOWN, METHOD_BUFFERED}; -use winapi::um::winnt::{ - FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE, -}; -use wintun::{Packet, Session}; - -use super::ffi; - -/* Present in 8.1 */ -const TAP_WIN_IOCTL_GET_MAC: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS); -#[allow(dead_code)] -const TAP_WIN_IOCTL_GET_VERSION: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS); -const TAP_WIN_IOCTL_GET_MTU: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS); -#[allow(dead_code)] -const TAP_WIN_IOCTL_GET_INFO: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_ANY_ACCESS); -#[allow(dead_code)] -const TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 5, METHOD_BUFFERED, FILE_ANY_ACCESS); -const TAP_WIN_IOCTL_SET_MEDIA_STATUS: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS); -#[allow(dead_code)] -const TAP_WIN_IOCTL_CONFIG_DHCP_MASQ: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 7, METHOD_BUFFERED, FILE_ANY_ACCESS); -#[allow(dead_code)] -const TAP_WIN_IOCTL_GET_LOG_LINE: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 8, METHOD_BUFFERED, FILE_ANY_ACCESS); -#[allow(dead_code)] -const TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 9, METHOD_BUFFERED, FILE_ANY_ACCESS); -/* Added in 8.2 */ -/* obsoletes TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT */ -#[allow(dead_code)] -const TAP_WIN_IOCTL_CONFIG_TUN: DWORD = - ctl_code(FILE_DEVICE_UNKNOWN, 10, METHOD_BUFFERED, FILE_ANY_ACCESS); +use crate::{IntoAddress, Layer}; pub enum Driver { Tun(Tun), #[allow(dead_code)] - Tap(Tap), + Tap(TapDevice), } pub enum PacketVariant { Tun(Packet), @@ -83,7 +44,7 @@ impl Driver { let index = tun.session.get_adapter().get_adapter_index()?; Ok(index) } - Driver::Tap(tap) => Ok(tap.index), + Driver::Tap(tap) => Ok(tap.index()), } } pub fn name(&self) -> Result { @@ -93,26 +54,23 @@ impl Driver { Ok(name) } Driver::Tap(tap) => { - let name = tap.name()?; + let name = tap.get_name()?; Ok(name) } } } pub fn read_by_ref(&self, buf: &mut [u8]) -> std::io::Result { match self { - Driver::Tap(tap) => tap.read_by_ref(buf), + Driver::Tap(tap) => tap.read(buf), Driver::Tun(tun) => tun.read_by_ref(buf), } } pub fn write_by_ref(&self, buf: &[u8]) -> std::io::Result { match self { - Driver::Tap(tap) => tap.write_by_ref(buf), + Driver::Tap(tap) => tap.write(buf), Driver::Tun(tun) => tun.write_by_ref(buf), } } - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } pub fn receive_blocking(&self) -> std::io::Result { match self { Driver::Tun(tun) => { @@ -121,7 +79,7 @@ impl Driver { } Driver::Tap(tap) => { let mut buf = [0u8; u16::MAX as usize]; - let len = tap.read_by_ref(&mut buf)?; + let len = tap.read(&mut buf)?; let mut vec = vec![]; vec.extend_from_slice(&buf[..len]); Ok(PacketVariant::Tap(vec.into_boxed_slice())) @@ -132,8 +90,7 @@ impl Driver { /// A TUN device using the wintun driver. pub struct Device { - pub(crate) driver: Arc, - mtu: u16, + pub(crate) driver: Driver, } macro_rules! driver_case { @@ -149,17 +106,9 @@ impl Device { /// Create a new `Device` for the given `Configuration`. pub fn new(config: &Configuration) -> Result { let layer = config.layer.unwrap_or(Layer::L3); - let tun_name = config.tun_name_.as_deref().unwrap_or("wintun"); - let address = config - .address - .unwrap_or(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))); - - let mask = config - .netmask - .unwrap_or(IpAddr::V4(Ipv4Addr::new(255, 255, 255, 0))); - let mtu = config.mtu.unwrap_or(crate::DEFAULT_MTU); + let name = config.name.as_deref().unwrap_or("tun3"); - let mut device = if layer == Layer::L3 { + let device = if layer == Layer::L3 { let wintun_file = &config.platform_config.wintun_file; let wintun = unsafe { // Ensure the dll file has not been tampered with. @@ -175,13 +124,10 @@ impl Device { wintun::load_from_library(wintun)? }; let guid = config.platform_config.device_guid; - let adapter = match wintun::Adapter::open(&wintun, tun_name) { + let adapter = match wintun::Adapter::open(&wintun, name) { Ok(a) => a, - Err(_) => wintun::Adapter::create(&wintun, tun_name, tun_name, guid)?, + Err(_) => wintun::Adapter::create(&wintun, name, name, guid)?, }; - if let Some(metric) = config.metric { - netsh::set_interface_metric(adapter.get_adapter_index()?, metric)?; - } #[cfg(feature = "wintun-dns")] if let Some(dns_servers) = &config.platform_config.dns_servers { @@ -190,121 +136,120 @@ impl Device { let session = adapter.start_session(config.ring_capacity.unwrap_or(wintun::MAX_RING_CAPACITY))?; - adapter.set_mtu(mtu as _)?; Device { - driver: Arc::new(Driver::Tun(Tun { + driver: Driver::Tun(Tun { session: Arc::new(session), - })), - mtu, + }), } } else if layer == Layer::L2 { - let tap = Tap::new(tun_name.to_owned())?; - tap.set_ip(address, mask)?; - tap.set_mtu(mtu as u32)?; + const HARDWARE_ID: &str = "tap0901"; + let tap = if let Ok(tap) = TapDevice::open(HARDWARE_ID, name) { + tap + } else { + let tap = TapDevice::create(HARDWARE_ID)?; + tap.set_name(name)?; + tap + }; Device { - driver: Arc::new(Driver::Tap(tap)), - mtu, + driver: Driver::Tap(tap), } } else { panic!("unknow layer {:?}", layer); }; - configure(&mut device, config)?; + configure(&device, config)?; + if let Some(metric) = config.metric { + netsh::set_interface_metric(device.driver.index()?, metric)?; + } Ok(device) } - pub fn split(self) -> (Reader, Writer) { - (Reader(self.driver.clone()), Writer(self.driver)) - } - /// Recv a packet from tun device - pub fn recv(&self, buf: &mut [u8]) -> io::Result { + pub(crate) fn recv(&self, buf: &mut [u8]) -> io::Result { driver_case!( - &*self.driver; + &self.driver; tun =>{ tun.recv(buf) }; - tap=> - { - tap.recv(buf) + tap=>{ + tap.read(buf) } ) } /// Send a packet to tun device - pub fn send(&self, buf: &[u8]) -> io::Result { + pub(crate) fn send(&self, buf: &[u8]) -> io::Result { driver_case!( - &*self.driver; - tun=> { + &self.driver; + tun=>{ tun.send(buf) }; - tap=> - { - tap.send(buf) + tap=>{ + tap.write(buf) } ) } -} - -impl Read for Device { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.driver.read_by_ref(buf) - } -} - -impl Write for Device { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.driver.write_by_ref(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.driver.flush() - } -} - -impl AsRef for Device { - fn as_ref(&self) -> &(dyn AbstractDevice + 'static) { - self - } -} - -impl AsMut for Device { - fn as_mut(&mut self) -> &mut (dyn AbstractDevice + 'static) { - self + pub(crate) fn shutdown(&self) -> io::Result<()> { + driver_case!( + &self.driver; + tun=>{ + tun.get_session().shutdown().map_err(|e|io::Error::new(io::ErrorKind::Other,format!("{:?}",e))) + }; + tap=>{ + tap.down() + } + ) } } impl AbstractDevice for Device { - fn tun_name(&self) -> Result { + fn name(&self) -> Result { driver_case!( - &*self.driver; - tun=> { + &self.driver; + tun=>{ Ok(tun.session.get_adapter().get_name()?) }; - tap=> - { - Ok(tap.name()?) + tap=>{ + Ok(tap.get_name()?) } ) } - fn set_tun_name(&self, value: &str) -> Result<()> { + fn set_name(&self, value: &str) -> Result<()> { driver_case!( - &*self.driver; - tun=> { + &self.driver; + tun=>{ tun.session.get_adapter().set_name(value)?; - Ok(()) }; - tap=> - { - tap.set_name(value).map_err(|e|e.into()) + tap=>{ + tap.set_name(value)? } - ) + ); + Ok(()) + } + + fn enabled(&self, value: bool) -> Result<()> { + driver_case!( + &self.driver; + tun=>{ + if !value{ + tun.session.shutdown()?; + } + }; + tap=>{ + if value{ + tap.up()? + }else{ + tap.down()? + } + } + ); + Ok(()) } fn address(&self) -> Result { driver_case!( - &*self.driver; - tun=> { + &self.driver; + tun=>{ let addresses =tun.session.get_adapter().get_addresses()?; addresses .iter() @@ -314,9 +259,8 @@ impl AbstractDevice for Device { }) .ok_or(Error::InvalidConfig) }; - tap=> - { - tap.address() + tap=>{ + tap.get_address().map_err(|e|e.into()) } ) } @@ -324,8 +268,8 @@ impl AbstractDevice for Device { fn destination(&self) -> Result { // It's just the default gateway in windows. driver_case!( - &*self.driver; - tun=> { + &self.driver; + tun=>{ tun .session .get_adapter() @@ -337,72 +281,99 @@ impl AbstractDevice for Device { }) .ok_or(Error::InvalidConfig) }; - tap=> - { - tap.destination() + tap=>{ + tap.get_destination().map_err(|e|e.into()) } ) } - fn broadcast(&self) -> Result { - Err(Error::NotImplemented) - } - fn netmask(&self) -> Result { let current_addr = self.address()?; driver_case!( - &*self.driver; - tun=> { + &self.driver; + tun=>{ tun .session .get_adapter() .get_netmask_of_address(¤t_addr) .map_err(Error::WintunError) }; - tap=> - { - tap.netmask() + tap=>{ + tap.get_netmask().map_err(|e|e.into()) } ) } - fn set_network_address( + fn set_network_address( &self, - address: IpAddr, - netmask: IpAddr, - destination: Option, + address: A, + netmask: A, + destination: Option, ) -> Result<()> { + let destination = if let Some(destination) = destination { + Some(destination.into_address()?) + } else { + None + }; netsh::set_interface_ip( self.driver.index()?, - &address, - &netmask, - destination.as_ref(), + address.into_address()?, + netmask.into_address()?, + destination, )?; Ok(()) } /// The return value is always `Ok(65535)` due to wintun fn mtu(&self) -> Result { - Ok(self.mtu) + driver_case!( + &self.driver; + tun=>{ + let mtu = tun.session.get_adapter().get_mtu()?; + Ok(mtu as _) + }; + tap=>{ + let mtu = tap.get_mtu()?; + Ok(mtu as _) + } + ) } /// This setting has no effect since the mtu of wintun is always 65535 fn set_mtu(&self, mtu: u16) -> Result<()> { driver_case!( - &*self.driver; - tun=> { + &self.driver; + tun=>{ tun.session.get_adapter().set_mtu(mtu as _)?; Ok(()) }; - tap=> - { - tap.set_mtu(mtu as u32).map_err(|e|e.into()) + tap=>{ + tap.set_mtu(mtu).map_err(|e|e.into()) + } + ) + } + + fn set_mac_address(&self, eth_addr: [u8; ETHER_ADDR_LEN as usize]) -> Result<()> { + driver_case!( + &self.driver; + _tun=>{ + Err(io::Error::from(io::ErrorKind::Unsupported))? + }; + tap=>{ + tap.set_mac(ð_addr).map_err(|e|e.into()) } ) } - fn packet_information(&self) -> bool { - // Note: wintun does not support packet information - false + fn get_mac_address(&self) -> Result<[u8; ETHER_ADDR_LEN as usize]> { + driver_case!( + &self.driver; + _tun=>{ + Err(io::Error::from(io::ErrorKind::Unsupported))? + }; + tap=>{ + tap.get_mac().map_err(|e|e.into()) + } + ) } } @@ -447,196 +418,137 @@ impl Tun { self.write_by_ref(buf) } } - -impl Read for Tun { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.read_by_ref(buf) - } -} - -impl Write for Tun { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.write_by_ref(buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -// impl Drop for Tun { +// +// pub struct Tap { +// handle: HANDLE, +// index: u32, +// luid: NET_LUID, +// #[allow(dead_code)] +// mac: [u8; 6], +// } +// unsafe impl Send for Tap {} +// unsafe impl Sync for Tap {} +// impl Drop for Tap { // fn drop(&mut self) { -// // The session has implemented drop -// if let Err(err) = self.session.shutdown() { -// log::error!("failed to shutdown session: {:?}", err); +// if let Err(e) = self.shutdown() { +// log::warn!("shutdown={:?}", e) +// } +// if let Err(e) = ffi::close_handle(self.handle) { +// log::warn!("close_handle={:?}", e) // } // } // } - -pub struct Tap { - handle: HANDLE, - index: u32, - luid: NET_LUID, - #[allow(dead_code)] - mac: [u8; 6], -} -unsafe impl Send for Tap {} -unsafe impl Sync for Tap {} - -impl Tap { - pub(crate) fn new(name: String) -> std::io::Result { - let luid = ffi::alias_to_luid(&encode_utf16(&name)).map_err(|e| { - io::Error::new(e.kind(), format!("alias_to_luid name={},err={:?}", name, e)) - })?; - let guid = ffi::luid_to_guid(&luid) - .and_then(|guid| ffi::string_from_guid(&guid)) - .map_err(|e| { - io::Error::new(e.kind(), format!("luid_to_guid name={},err={:?}", name, e)) - })?; - let path = format!(r"\\.\Global\{}.tap", decode_utf16(&guid)); - let handle = ffi::create_file( - &encode_utf16(&path), - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - ) - .map_err(|e| io::Error::new(e.kind(), format!("tap name={},err={:?}", name, e)))?; - - let mut mac = [0u8; 6]; - ffi::device_io_control(handle, TAP_WIN_IOCTL_GET_MAC, &(), &mut mac) - .map_err(|e| { - io::Error::new( - e.kind(), - format!("TAP_WIN_IOCTL_CONFIG_TUN name={},err={:?}", name, e), - ) - }) - .map_err(|e| io::Error::new(e.kind(), format!("TAP_WIN_IOCTL_GET_MAC,err={:?}", e)))?; - let index = ffi::luid_to_index(&luid)?; - // 设置网卡跃点 - if let Err(e) = netsh::set_interface_metric(index, 0) { - log::warn!("{:?}", e); - } - let tap = Self { - handle, - index, - luid, - mac, - }; - tap.enabled(true)?; - Ok(tap) - } - - pub fn write_by_ref(&self, buf: &[u8]) -> io::Result { - ffi::write_file(self.handle, buf).map(|res| res as _) - } - pub fn read_by_ref(&self, buf: &mut [u8]) -> io::Result { - ffi::read_file(self.handle, buf).map(|res| res as usize) - } - pub fn enabled(&self, value: bool) -> io::Result<()> { - let status: u32 = if value { 1 } else { 0 }; - ffi::device_io_control( - self.handle, - TAP_WIN_IOCTL_SET_MEDIA_STATUS, - &status, - &mut (), - ) - } - /// Recv a packet from tun device - pub fn recv(&self, buf: &mut [u8]) -> io::Result { - self.read_by_ref(buf) - } - - /// Send a packet to tun device - pub fn send(&self, buf: &[u8]) -> io::Result { - self.write_by_ref(buf) - } - pub fn name(&self) -> std::io::Result { - ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name)) - } - pub fn set_name(&self, _name: &str) -> std::io::Result<()> { - unimplemented!() - } - pub fn shutdown(&self) -> io::Result<()> { - self.enabled(false) - } - - pub fn set_ip(&self, address: IpAddr, mask: IpAddr) -> io::Result<()> { - netsh::set_interface_ip(self.index, &address, &mask, None) - } - - pub fn address(&self) -> Result { - unimplemented!() - } - pub fn netmask(&self) -> Result { - unimplemented!() - } - pub fn set_address(&self, _address: Ipv4Addr) -> io::Result<()> { - unimplemented!() - } - pub fn mtu(&self) -> io::Result { - let mut mtu = 0; - ffi::device_io_control(self.handle, TAP_WIN_IOCTL_GET_MTU, &(), &mut mtu).map(|_| mtu) - } - - pub fn set_mtu(&self, value: u32) -> io::Result<()> { - netsh::set_interface_mtu(self.index, value) - } - pub fn destination(&self) -> Result { - unimplemented!() - } - pub fn set_destination(&self, _address: Ipv4Addr) -> Result<()> { - unimplemented!() - } -} - -impl Read for Tap { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.read_by_ref(buf) - } -} - -impl Write for Tap { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.write_by_ref(buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -fn encode_utf16(string: &str) -> Vec { - use std::iter::once; - string.encode_utf16().chain(once(0)).collect() -} - -fn decode_utf16(string: &[u16]) -> String { - let end = string.iter().position(|b| *b == 0).unwrap_or(string.len()); - String::from_utf16_lossy(&string[..end]) -} -const fn ctl_code(device_type: DWORD, function: DWORD, method: DWORD, access: DWORD) -> DWORD { - (device_type << 16) | (access << 14) | (function << 2) | method -} - -#[repr(transparent)] -pub struct Reader(Arc); - -impl Read for Reader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read_by_ref(buf) - } -} - -#[repr(transparent)] -pub struct Writer(Arc); - -impl Write for Writer { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write_by_ref(buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} +// impl Tap { +// pub(crate) fn new(name: String) -> std::io::Result { +// let luid = ffi::alias_to_luid(&encode_utf16(&name)).map_err(|e| { +// io::Error::new(e.kind(), format!("alias_to_luid name={},err={:?}", name, e)) +// })?; +// let guid = ffi::luid_to_guid(&luid) +// .and_then(|guid| ffi::string_from_guid(&guid)) +// .map_err(|e| { +// io::Error::new(e.kind(), format!("luid_to_guid name={},err={:?}", name, e)) +// })?; +// let path = format!(r"\\.\Global\{}.tap", decode_utf16(&guid)); +// let handle = ffi::create_file( +// &encode_utf16(&path), +// GENERIC_READ | GENERIC_WRITE, +// FILE_SHARE_READ | FILE_SHARE_WRITE, +// OPEN_EXISTING, +// FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, +// ) +// .map_err(|e| io::Error::new(e.kind(), format!("tap name={},err={:?}", name, e)))?; +// +// let mut mac = [0u8; 6]; +// ffi::device_io_control(handle, TAP_WIN_IOCTL_GET_MAC, &(), &mut mac) +// .map_err(|e| { +// io::Error::new( +// e.kind(), +// format!("TAP_WIN_IOCTL_CONFIG_TUN name={},err={:?}", name, e), +// ) +// }) +// .map_err(|e| io::Error::new(e.kind(), format!("TAP_WIN_IOCTL_GET_MAC,err={:?}", e)))?; +// let index = ffi::luid_to_index(&luid)?; +// let tap = Self { +// handle, +// index, +// luid, +// mac, +// }; +// Ok(tap) +// } +// +// pub fn write_by_ref(&self, buf: &[u8]) -> io::Result { +// ffi::write_file(self.handle, buf).map(|res| res as _) +// } +// pub fn read_by_ref(&self, buf: &mut [u8]) -> io::Result { +// ffi::read_file(self.handle, buf).map(|res| res as usize) +// } +// pub fn enabled(&self, value: bool) -> io::Result<()> { +// let status: u32 = if value { 1 } else { 0 }; +// ffi::device_io_control( +// self.handle, +// TAP_WIN_IOCTL_SET_MEDIA_STATUS, +// &status, +// &mut (), +// ) +// } +// /// Recv a packet from tun device +// pub fn recv(&self, buf: &mut [u8]) -> io::Result { +// self.read_by_ref(buf) +// } +// +// /// Send a packet to tun device +// pub fn send(&self, buf: &[u8]) -> io::Result { +// self.write_by_ref(buf) +// } +// pub fn name(&self) -> std::io::Result { +// ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name)) +// } +// pub fn set_name(&self, _name: &str) -> std::io::Result<()> { +// unimplemented!() +// } +// pub fn shutdown(&self) -> io::Result<()> { +// self.enabled(false) +// } +// +// pub fn set_ip(&self, address: IpAddr, mask: IpAddr) -> io::Result<()> { +// netsh::set_interface_ip(self.index, address, mask, None) +// } +// +// pub fn address(&self) -> Result { +// unimplemented!() +// } +// pub fn netmask(&self) -> Result { +// unimplemented!() +// } +// pub fn set_address(&self, _address: Ipv4Addr) -> io::Result<()> { +// unimplemented!() +// } +// pub fn mtu(&self) -> io::Result { +// let mut mtu = 0; +// ffi::device_io_control(self.handle, TAP_WIN_IOCTL_GET_MTU, &(), &mut mtu).map(|_| mtu) +// } +// +// pub fn set_mtu(&self, value: u32) -> io::Result<()> { +// netsh::set_interface_mtu(self.index, value) +// } +// pub fn destination(&self) -> Result { +// unimplemented!() +// } +// pub fn set_destination(&self, _address: Ipv4Addr) -> Result<()> { +// unimplemented!() +// } +// } +// +// fn encode_utf16(string: &str) -> Vec { +// use std::iter::once; +// string.encode_utf16().chain(once(0)).collect() +// } +// +// fn decode_utf16(string: &[u16]) -> String { +// let end = string.iter().position(|b| *b == 0).unwrap_or(string.len()); +// String::from_utf16_lossy(&string[..end]) +// } +// const fn ctl_code(device_type: DWORD, function: DWORD, method: DWORD, access: DWORD) -> DWORD { +// (device_type << 16) | (access << 14) | (function << 2) | method +// } diff --git a/src/platform/windows/ffi.rs b/src/platform/windows/ffi.rs index 99805ae..33f048b 100644 --- a/src/platform/windows/ffi.rs +++ b/src/platform/windows/ffi.rs @@ -3,27 +3,50 @@ //! Module holding safe wrappers over winapi functions -use winapi::shared::basetsd::*; -use winapi::shared::guiddef::GUID; -use winapi::shared::ifdef::*; -use winapi::shared::minwindef::*; -use winapi::shared::netioapi::*; -use winapi::shared::winerror::*; - -use winapi::um::combaseapi::*; -use winapi::um::errhandlingapi::*; -use winapi::um::fileapi::*; -use winapi::um::handleapi::*; -use winapi::um::ioapiset::*; -use winapi::um::setupapi::*; -use winapi::um::synchapi::*; -use winapi::um::winioctl::*; -use winapi::um::winnt::*; -use winapi::um::winreg::*; - -use std::error::Error; use std::{io, mem, ptr}; -use winapi::um::minwinbase::OVERLAPPED_u; + +use windows_sys::Win32::Foundation::ERROR_IO_PENDING; +use windows_sys::Win32::System::IO::GetOverlappedResult; +use windows_sys::{ + core::{GUID, PCWSTR}, + Win32::{ + Devices::DeviceAndDriverInstallation::{ + SetupDiBuildDriverInfoList, SetupDiCallClassInstaller, SetupDiClassNameFromGuidW, + SetupDiCreateDeviceInfoList, SetupDiCreateDeviceInfoW, SetupDiDestroyDeviceInfoList, + SetupDiDestroyDriverInfoList, SetupDiEnumDeviceInfo, SetupDiEnumDriverInfoW, + SetupDiGetClassDevsW, SetupDiGetDeviceRegistryPropertyW, SetupDiGetDriverInfoDetailW, + SetupDiOpenDevRegKey, SetupDiSetClassInstallParamsW, SetupDiSetDeviceRegistryPropertyW, + SetupDiSetSelectedDevice, SetupDiSetSelectedDriverW, HDEVINFO, MAX_CLASS_NAME_LEN, + SP_DEVINFO_DATA, SP_DRVINFO_DATA_V2_W, SP_DRVINFO_DETAIL_DATA_W, + }, + Foundation::{ + CloseHandle, GetLastError, BOOL, ERROR_NO_MORE_ITEMS, FALSE, FILETIME, HANDLE, TRUE, + }, + NetworkManagement::{ + IpHelper::{ + ConvertInterfaceAliasToLuid, ConvertInterfaceLuidToAlias, + ConvertInterfaceLuidToGuid, ConvertInterfaceLuidToIndex, + }, + Ndis::NET_LUID_LH, + }, + Storage::FileSystem::{ + CreateFileW, ReadFile, WriteFile, FILE_CREATION_DISPOSITION, FILE_FLAGS_AND_ATTRIBUTES, + FILE_SHARE_MODE, + }, + System::{ + Com::StringFromGUID2, + Registry::{RegNotifyChangeKeyValue, HKEY}, + Threading::{CreateEventW, WaitForSingleObject}, + IO::DeviceIoControl, + }, + }, +}; + +fn to_pcwstr(s: &str) -> PCWSTR { + let mut wide: Vec = s.encode_utf16().collect(); + wide.push(0); + wide.as_ptr() as PCWSTR +} #[allow(non_camel_case_types)] #[allow(non_snake_case)] @@ -31,61 +54,67 @@ use winapi::um::minwinbase::OVERLAPPED_u; #[derive(Clone, Copy)] /// Custom type to handle variable size SP_DRVINFO_DETAIL_DATA_W pub struct SP_DRVINFO_DETAIL_DATA_W2 { - pub cbSize: DWORD, + pub cbSize: u32, pub InfDate: FILETIME, - pub CompatIDsOffset: DWORD, - pub CompatIDsLength: DWORD, - pub Reserved: ULONG_PTR, - pub SectionName: [WCHAR; 256], - pub InfFileName: [WCHAR; 260], - pub DrvDescription: [WCHAR; 256], - pub HardwareID: [WCHAR; 512], + pub CompatIDsOffset: u32, + pub CompatIDsLength: u32, + pub Reserved: usize, + pub SectionName: [u16; 256], + pub InfFileName: [u16; 260], + pub DrvDescription: [u16; 256], + pub HardwareID: [u16; 512], +} +/// Encode a string as a utf16 buffer +pub fn encode_utf16(string: &str) -> Vec { + use std::iter::once; + string.encode_utf16().chain(once(0)).collect() } -pub fn string_from_guid(guid: &GUID) -> io::Result> { - // GUID_STRING_CHARACTERS + 1 +pub fn decode_utf16(string: &[u16]) -> String { + let end = string.iter().position(|b| *b == 0).unwrap_or(string.len()); + String::from_utf16_lossy(&string[..end]) +} + +pub fn string_from_guid(guid: &GUID) -> io::Result { let mut string = vec![0; 39]; match unsafe { StringFromGUID2(guid, string.as_mut_ptr(), string.len() as _) } { - 0 => Err(io::Error::new(io::ErrorKind::Other, "Insufficent buffer")), - _ => Ok(string), + 0 => Err(io::Error::last_os_error()), + _ => Ok(decode_utf16(&string)), } } -pub fn alias_to_luid(alias: &[WCHAR]) -> io::Result { +pub fn alias_to_luid(alias: &str) -> io::Result { + let alias = encode_utf16(alias); let mut luid = unsafe { mem::zeroed() }; - match unsafe { ConvertInterfaceAliasToLuid(alias.as_ptr(), &mut luid) } { 0 => Ok(luid), - err => Err(io::Error::from_raw_os_error(err as _)), + err => Err(io::Error::last_os_error()), } } -pub fn luid_to_index(luid: &NET_LUID) -> io::Result { +pub fn luid_to_index(luid: &NET_LUID_LH) -> io::Result { let mut index = 0; - match unsafe { ConvertInterfaceLuidToIndex(luid, &mut index) } { 0 => Ok(index), - err => Err(io::Error::from_raw_os_error(err as _)), + err => Err(io::Error::last_os_error()), } } -pub fn luid_to_guid(luid: &NET_LUID) -> io::Result { +pub fn luid_to_guid(luid: &NET_LUID_LH) -> io::Result { let mut guid = unsafe { mem::zeroed() }; - match unsafe { ConvertInterfaceLuidToGuid(luid, &mut guid) } { 0 => Ok(guid), - err => Err(io::Error::from_raw_os_error(err as _)), + err => Err(io::Error::last_os_error()), } } -pub fn luid_to_alias(luid: &NET_LUID) -> io::Result> { +pub fn luid_to_alias(luid: &NET_LUID_LH) -> io::Result { // IF_MAX_STRING_SIZE + 1 let mut alias = vec![0; 257]; - match unsafe { ConvertInterfaceLuidToAlias(luid, alias.as_mut_ptr(), alias.len()) } { - 0 => Ok(alias), - err => Err(io::Error::from_raw_os_error(err as _)), + 0 => Ok(decode_utf16(&alias)), + err => Err(io::Error::last_os_error()), } } @@ -97,13 +126,14 @@ pub fn close_handle(handle: HANDLE) -> io::Result<()> { } pub fn create_file( - file_name: &[WCHAR], - desired_access: DWORD, - share_mode: DWORD, - creation_disposition: DWORD, - flags_and_attributes: DWORD, + file_name: &str, + desired_access: u32, + share_mode: FILE_SHARE_MODE, + creation_disposition: FILE_CREATION_DISPOSITION, + flags_and_attributes: FILE_FLAGS_AND_ATTRIBUTES, ) -> io::Result { - match unsafe { + let file_name = encode_utf16(file_name); + let handle = unsafe { CreateFileW( file_name.as_ptr(), desired_access, @@ -113,20 +143,27 @@ pub fn create_file( flags_and_attributes, ptr::null_mut(), ) - } { - INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()), - handle => Ok(handle), + }; + if handle.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(handle) } } -pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result { +pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result { let mut ret = 0; //https://www.cnblogs.com/linyilong3/archive/2012/05/03/2480451.html unsafe { - let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED { + let mut ip_overlapped = windows_sys::Win32::System::IO::OVERLAPPED { Internal: 0, InternalHigh: 0, - u: Default::default(), + Anonymous: windows_sys::Win32::System::IO::OVERLAPPED_0 { + Anonymous: windows_sys::Win32::System::IO::OVERLAPPED_0_0 { + Offset: 0, + OffsetHigh: 0, + }, + }, hEvent: ptr::null_mut(), }; if 0 == ReadFile( @@ -138,7 +175,7 @@ pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result { ) { let e = io::Error::last_os_error(); if e.raw_os_error().unwrap_or(0) == ERROR_IO_PENDING as i32 { - if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) { + if 0 == GetOverlappedResult(handle, &ip_overlapped, &mut ret, 1) { return Err(e); } } else { @@ -149,12 +186,17 @@ pub fn read_file(handle: HANDLE, buffer: &mut [u8]) -> io::Result { } } -pub fn write_file(handle: HANDLE, buffer: &[u8]) -> io::Result { +pub fn write_file(handle: HANDLE, buffer: &[u8]) -> io::Result { let mut ret = 0; - let mut ip_overlapped = winapi::um::minwinbase::OVERLAPPED { + let mut ip_overlapped = windows_sys::Win32::System::IO::OVERLAPPED { Internal: 0, InternalHigh: 0, - u: Default::default(), + Anonymous: windows_sys::Win32::System::IO::OVERLAPPED_0 { + Anonymous: windows_sys::Win32::System::IO::OVERLAPPED_0_0 { + Offset: 0, + OffsetHigh: 0, + }, + }, hEvent: ptr::null_mut(), }; unsafe { @@ -167,7 +209,7 @@ pub fn write_file(handle: HANDLE, buffer: &[u8]) -> io::Result { ) { let e = io::Error::last_os_error(); if e.raw_os_error().unwrap_or(0) == ERROR_IO_PENDING as i32 { - if 0 == GetOverlappedResult(handle, &mut ip_overlapped, &mut ret, 1) { + if 0 == GetOverlappedResult(handle, &ip_overlapped, &mut ret, 1) { return Err(e); } } else { @@ -180,14 +222,14 @@ pub fn write_file(handle: HANDLE, buffer: &[u8]) -> io::Result { pub fn create_device_info_list(guid: &GUID) -> io::Result { match unsafe { SetupDiCreateDeviceInfoList(guid, ptr::null_mut()) } { - INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()), + -1 => Err(io::Error::last_os_error()), devinfo => Ok(devinfo), } } -pub fn get_class_devs(guid: &GUID, flags: DWORD) -> io::Result { +pub fn get_class_devs(guid: &GUID, flags: u32) -> io::Result { match unsafe { SetupDiGetClassDevsW(guid, ptr::null(), ptr::null_mut(), flags) } { - INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()), + -1 => Err(io::Error::last_os_error()), devinfo => Ok(devinfo), } } @@ -199,9 +241,8 @@ pub fn destroy_device_info_list(devinfo: HDEVINFO) -> io::Result<()> { } } -pub fn class_name_from_guid(guid: &GUID) -> io::Result> { - let mut class_name = vec![0; 32]; - +pub fn class_name_from_guid(guid: &GUID) -> io::Result { + let mut class_name = vec![0; MAX_CLASS_NAME_LEN as usize]; match unsafe { SetupDiClassNameFromGuidW( guid, @@ -211,20 +252,21 @@ pub fn class_name_from_guid(guid: &GUID) -> io::Result> { ) } { 0 => Err(io::Error::last_os_error()), - _ => Ok(class_name), + _ => Ok(decode_utf16(&class_name)), } } pub fn create_device_info( devinfo: HDEVINFO, - device_name: &[WCHAR], + device_name: &str, guid: &GUID, - device_description: &[WCHAR], - creation_flags: DWORD, + device_description: &str, + creation_flags: u32, ) -> io::Result { let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() }; devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _; - + let device_name = encode_utf16(device_name); + let device_description = encode_utf16(device_description); match unsafe { SetupDiCreateDeviceInfoW( devinfo, @@ -251,9 +293,10 @@ pub fn set_selected_device(devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA) -> pub fn set_device_registry_property( devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA, - property: DWORD, - value: &[WCHAR], + property: u32, + value: &str, ) -> io::Result<()> { + let value = encode_utf16(value); match unsafe { SetupDiSetDeviceRegistryPropertyW( devinfo, @@ -271,8 +314,8 @@ pub fn set_device_registry_property( pub fn get_device_registry_property( devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA, - property: DWORD, -) -> io::Result> { + property: u32, +) -> io::Result { let mut value = vec![0; 32]; match unsafe { @@ -287,14 +330,14 @@ pub fn get_device_registry_property( ) } { 0 => Err(io::Error::last_os_error()), - _ => Ok(value), + _ => Ok(decode_utf16(&value)), } } pub fn build_driver_info_list( devinfo: HDEVINFO, - devinfo_data: &SP_DEVINFO_DATA, - driver_type: DWORD, + devinfo_data: &mut SP_DEVINFO_DATA, + driver_type: u32, ) -> io::Result<()> { match unsafe { SetupDiBuildDriverInfoList(devinfo, devinfo_data as *const _ as _, driver_type) } { @@ -306,7 +349,7 @@ pub fn build_driver_info_list( pub fn destroy_driver_info_list( devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA, - driver_type: DWORD, + driver_type: u32, ) -> io::Result<()> { match unsafe { SetupDiDestroyDriverInfoList(devinfo, devinfo_data as *const _ as _, driver_type) @@ -319,7 +362,7 @@ pub fn destroy_driver_info_list( pub fn get_driver_info_detail( devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA, - drvinfo_data: &SP_DRVINFO_DATA_W, + drvinfo_data: &SP_DRVINFO_DATA_V2_W, ) -> io::Result { let mut drvinfo_detail: SP_DRVINFO_DETAIL_DATA_W2 = unsafe { mem::zeroed() }; drvinfo_detail.cbSize = mem::size_of::() as _; @@ -342,7 +385,7 @@ pub fn get_driver_info_detail( pub fn set_selected_driver( devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA, - drvinfo_data: &SP_DRVINFO_DATA_W, + drvinfo_data: &SP_DRVINFO_DATA_V2_W, ) -> io::Result<()> { match unsafe { SetupDiSetSelectedDriverW( @@ -377,7 +420,7 @@ pub fn set_class_install_params( pub fn call_class_installer( devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA, - install_function: DI_FUNCTION, + install_function: u32, ) -> io::Result<()> { match unsafe { SetupDiCallClassInstaller(install_function, devinfo, devinfo_data as *const _ as _) @@ -390,12 +433,12 @@ pub fn call_class_installer( pub fn open_dev_reg_key( devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA, - scope: DWORD, - hw_profile: DWORD, - key_type: DWORD, - sam_desired: REGSAM, + scope: u32, + hw_profile: u32, + key_type: u32, + sam_desired: u32, ) -> io::Result { - const INVALID_KEY_VALUE: HKEY = INVALID_HANDLE_VALUE as _; + const INVALID_KEY_VALUE: HKEY = windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE as _; match unsafe { SetupDiOpenDevRegKey( @@ -415,9 +458,11 @@ pub fn open_dev_reg_key( pub fn notify_change_key_value( key: HKEY, watch_subtree: BOOL, - notify_filter: DWORD, - milliseconds: DWORD, + notify_filter: u32, + milliseconds: u32, ) -> io::Result<()> { + const INVALID_HANDLE_VALUE: HKEY = windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE as _; + let event = match unsafe { CreateEventW(ptr::null_mut(), FALSE, FALSE, ptr::null()) } { INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()), event => Ok(event), @@ -425,7 +470,7 @@ pub fn notify_change_key_value( match unsafe { RegNotifyChangeKeyValue(key, watch_subtree, notify_filter, event, TRUE) } { 0 => Ok(()), - err => Err(io::Error::from_raw_os_error(err)), + err => Err(io::Error::last_os_error()), }?; match unsafe { WaitForSingleObject(event, milliseconds) } { @@ -441,12 +486,11 @@ pub fn notify_change_key_value( pub fn enum_driver_info( devinfo: HDEVINFO, devinfo_data: &SP_DEVINFO_DATA, - driver_type: DWORD, - member_index: DWORD, -) -> Option> { - let mut drvinfo_data: SP_DRVINFO_DATA_W = unsafe { mem::zeroed() }; + driver_type: u32, + member_index: u32, +) -> Option> { + let mut drvinfo_data: SP_DRVINFO_DATA_V2_W = unsafe { mem::zeroed() }; drvinfo_data.cbSize = mem::size_of_val(&drvinfo_data) as _; - match unsafe { SetupDiEnumDriverInfoW( devinfo, @@ -464,7 +508,7 @@ pub fn enum_driver_info( pub fn enum_device_info( devinfo: HDEVINFO, - member_index: DWORD, + member_index: u32, ) -> Option> { let mut devinfo_data: SP_DEVINFO_DATA = unsafe { mem::zeroed() }; devinfo_data.cbSize = mem::size_of_val(&devinfo_data) as _; @@ -478,12 +522,11 @@ pub fn enum_device_info( pub fn device_io_control( handle: HANDLE, - io_control_code: DWORD, + io_control_code: u32, in_buffer: &impl Copy, out_buffer: &mut impl Copy, ) -> io::Result<()> { let mut junk = 0; - match unsafe { DeviceIoControl( handle, diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 847b01f..a35d725 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -17,6 +17,7 @@ mod device; mod ffi; mod netsh; +mod tap; mod verify_dll_file; use crate::configuration::Configuration; @@ -69,7 +70,8 @@ impl PlatformConfig { } } +use super::Device as DeviceWrapper; /// Create a TUN device with the given name. -pub fn create(configuration: &Configuration) -> Result { - Device::new(configuration) +pub fn create(configuration: &Configuration) -> Result { + Ok(DeviceWrapper(Device::new(configuration)?)) } diff --git a/src/platform/windows/netsh.rs b/src/platform/windows/netsh.rs index ead721a..70dd3f2 100644 --- a/src/platform/windows/netsh.rs +++ b/src/platform/windows/netsh.rs @@ -1,9 +1,16 @@ use std::io; use std::net::IpAddr; use std::os::windows::process::CommandExt; - +use std::process::Command; use windows_sys::Win32::System::Threading::CREATE_NO_WINDOW; +pub fn set_interface_name(old_name: &str, new_name: &str) -> io::Result<()> { + let cmd = format!( + " netsh interface set interface name={:?} newname={:?}", + old_name, new_name + ); + exe_cmd(&cmd) +} pub fn set_interface_metric(index: u32, metric: u16) -> io::Result<()> { let cmd = format!( "netsh interface ip set interface {} metric={}", @@ -12,7 +19,7 @@ pub fn set_interface_metric(index: u32, metric: u16) -> io::Result<()> { exe_cmd(&cmd) } pub fn exe_cmd(cmd: &str) -> io::Result<()> { - let out = std::process::Command::new("cmd") + let out = Command::new("cmd") .creation_flags(CREATE_NO_WINDOW) .arg("/C") .arg(cmd) @@ -25,16 +32,26 @@ pub fn exe_cmd(cmd: &str) -> io::Result<()> { } Ok(()) } +pub fn exe_command(cmd: &mut Command) -> io::Result<()> { + let out = cmd.output()?; + if !out.status.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("cmd={:?},out={:?} ", cmd, String::from_utf8(out.stdout)), + )); + } + Ok(()) +} /// 设置网卡ip pub fn set_interface_ip( index: u32, - address: &IpAddr, - netmask: &IpAddr, - gateway: Option<&IpAddr>, + address: IpAddr, + netmask: IpAddr, + gateway: Option, ) -> io::Result<()> { - let mut binding = std::process::Command::new("netsh"); - let mut cmd = binding + let mut binding = Command::new("netsh"); + let cmd = binding .arg("interface") .arg(if address.is_ipv4() { "ipv4" } else { "ipv6" }) .arg("set") @@ -44,16 +61,9 @@ pub fn set_interface_ip( .arg(format!("address={}", address).as_str()) .arg(format!("mask={}", netmask).as_str()); if let Some(gateway) = gateway { - cmd = cmd.arg(format!("gateway={}", gateway).as_str()); - } - let out = cmd.output()?; - if !out.status.success() { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("cmd={:?},out={:?} ", cmd, String::from_utf8(out.stderr)), - )); + _ = cmd.arg(format!("gateway={}", gateway).as_str()); } - Ok(()) + exe_command(cmd) } pub fn set_interface_mtu(index: u32, mtu: u32) -> io::Result<()> { diff --git a/src/platform/windows/tap/iface.rs b/src/platform/windows/tap/iface.rs new file mode 100644 index 0000000..9397364 --- /dev/null +++ b/src/platform/windows/tap/iface.rs @@ -0,0 +1,304 @@ +use std::io; + +use crate::platform::windows::ffi; +use crate::platform::windows::ffi::decode_utf16; +use scopeguard::{guard, ScopeGuard}; +use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OVERLAPPED; +use windows_sys::{ + core::GUID, + Win32::{ + Devices::DeviceAndDriverInstallation::{ + DICD_GENERATE_ID, DICS_FLAG_GLOBAL, DIF_INSTALLDEVICE, DIF_INSTALLINTERFACES, + DIF_REGISTERDEVICE, DIF_REGISTER_COINSTALLERS, DIF_REMOVE, DIGCF_PRESENT, DIREG_DRV, + SPDIT_COMPATDRIVER, SPDRP_HARDWAREID, + }, + Foundation::{GENERIC_READ, GENERIC_WRITE, HANDLE, TRUE}, + NetworkManagement::Ndis::NET_LUID_LH, + Storage::FileSystem::{ + FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING, + }, + System::Registry::{KEY_NOTIFY, KEY_QUERY_VALUE, REG_NOTIFY_CHANGE_NAME}, + }, +}; + +const GUID_NETWORK_ADAPTER: GUID = GUID { + data1: 0x4d36e972, + data2: 0xe325, + data3: 0x11ce, + data4: [0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18], +}; + +#[repr(C, align(1))] +#[derive(c2rust_bitfields::BitfieldStruct)] +#[allow(non_snake_case)] +#[allow(non_camel_case_types)] +struct _NET_LUID_LH { + #[bitfield(name = "Reserved", ty = "u64", bits = "0..=23")] + #[bitfield(name = "NetLuidIndex", ty = "u64", bits = "24..=47")] + #[bitfield(name = "IfType", ty = "u64", bits = "48..=63")] + _Value: [u8; 8], +} + +/// Create a new interface and returns its NET_LUID +pub fn create_interface(component_id: &str) -> io::Result { + let devinfo = ffi::create_device_info_list(&GUID_NETWORK_ADAPTER)?; + + let _guard = guard((), |_| { + let _ = ffi::destroy_device_info_list(devinfo); + }); + + let class_name = ffi::class_name_from_guid(&GUID_NETWORK_ADAPTER)?; + + let mut devinfo_data = ffi::create_device_info( + devinfo, + &class_name, + &GUID_NETWORK_ADAPTER, + "", + DICD_GENERATE_ID, + )?; + + ffi::set_selected_device(devinfo, &devinfo_data)?; + ffi::set_device_registry_property(devinfo, &devinfo_data, SPDRP_HARDWAREID, component_id)?; + + ffi::build_driver_info_list(devinfo, &mut devinfo_data, SPDIT_COMPATDRIVER)?; + + let _guard = guard((), |_| { + let _ = ffi::destroy_driver_info_list(devinfo, &devinfo_data, SPDIT_COMPATDRIVER); + }); + + let mut driver_version = 0; + let mut member_index = 0; + + while let Some(drvinfo_data) = + ffi::enum_driver_info(devinfo, &devinfo_data, SPDIT_COMPATDRIVER, member_index) + { + member_index += 1; + + if drvinfo_data.is_err() { + continue; + } + let drvinfo_data = drvinfo_data?; + if drvinfo_data.DriverVersion <= driver_version { + continue; + } + + let drvinfo_detail = + match ffi::get_driver_info_detail(devinfo, &devinfo_data, &drvinfo_data) { + Ok(drvinfo_detail) => drvinfo_detail, + _ => continue, + }; + + let hardware_id = decode_utf16(&drvinfo_detail.HardwareID); + if !hardware_id.eq_ignore_ascii_case(component_id) { + continue; + } + + if ffi::set_selected_driver(devinfo, &devinfo_data, &drvinfo_data).is_err() { + continue; + } + + driver_version = drvinfo_data.DriverVersion; + } + + if driver_version == 0 { + return Err(io::Error::new(io::ErrorKind::NotFound, "No driver found")); + } + + let uninstaller = guard((), |_| { + let _ = ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE); + }); + + ffi::call_class_installer(devinfo, &devinfo_data, DIF_REGISTERDEVICE)?; + + let _ = ffi::call_class_installer(devinfo, &devinfo_data, DIF_REGISTER_COINSTALLERS); + let _ = ffi::call_class_installer(devinfo, &devinfo_data, DIF_INSTALLINTERFACES); + + ffi::call_class_installer(devinfo, &devinfo_data, DIF_INSTALLDEVICE)?; + + let key = ffi::open_dev_reg_key( + devinfo, + &devinfo_data, + DICS_FLAG_GLOBAL, + 0, + DIREG_DRV, + KEY_QUERY_VALUE | KEY_NOTIFY, + )?; + + let key = winreg::RegKey::predef(key as _); + + while key.get_value::("*IfType").is_err() { + ffi::notify_change_key_value(key.raw_handle() as _, TRUE, REG_NOTIFY_CHANGE_NAME, 2000)?; + } + + while key.get_value::("NetLuidIndex").is_err() { + ffi::notify_change_key_value(key.raw_handle() as _, TRUE, REG_NOTIFY_CHANGE_NAME, 2000)?; + } + + let if_type: u32 = key.get_value("*IfType")?; + let luid_index: u32 = key.get_value("NetLuidIndex")?; + + // Defuse the uninstaller + ScopeGuard::into_inner(uninstaller); + + let mut luid = NET_LUID_LH { Value: 0 }; + + unsafe { + let luid = &mut luid as *mut NET_LUID_LH as *mut _NET_LUID_LH; + (*luid).set_IfType(if_type as _); + (*luid).set_NetLuidIndex(luid_index as _); + } + + Ok(luid) +} + +/// Check if the given interface exists and is a valid network device +pub fn check_interface(component_id: &str, luid: &NET_LUID_LH) -> io::Result<()> { + let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?; + + let _guard = guard((), |_| { + let _ = ffi::destroy_device_info_list(devinfo); + }); + + let mut member_index = 0; + + while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index) { + member_index += 1; + + if devinfo_data.is_err() { + continue; + } + let devinfo_data = devinfo_data?; + + let hardware_id = + ffi::get_device_registry_property(devinfo, &devinfo_data, SPDRP_HARDWAREID); + if hardware_id.is_err() { + continue; + } + if !hardware_id?.eq_ignore_ascii_case(component_id) { + continue; + } + + let key = match ffi::open_dev_reg_key( + devinfo, + &devinfo_data, + DICS_FLAG_GLOBAL, + 0, + DIREG_DRV, + KEY_QUERY_VALUE | KEY_NOTIFY, + ) { + Ok(key) => winreg::RegKey::predef(key as _), + Err(_) => continue, + }; + + let if_type: u32 = match key.get_value("*IfType") { + Ok(if_type) => if_type, + Err(_) => continue, + }; + + let luid_index: u32 = match key.get_value("NetLuidIndex") { + Ok(luid_index) => luid_index, + Err(_) => continue, + }; + + let mut luid2 = NET_LUID_LH { Value: 0 }; + + unsafe { + let luid2 = &mut luid2 as *mut NET_LUID_LH as *mut _NET_LUID_LH; + (*luid2).set_IfType(if_type as _); + (*luid2).set_NetLuidIndex(luid_index as _); + } + + if unsafe { luid.Value != luid2.Value } { + continue; + } + + // Found it! + return Ok(()); + } + + Err(io::Error::new(io::ErrorKind::NotFound, "Device not found")) +} + +/// Deletes an existing interface +pub fn delete_interface(component_id: &str, luid: &NET_LUID_LH) -> io::Result<()> { + let devinfo = ffi::get_class_devs(&GUID_NETWORK_ADAPTER, DIGCF_PRESENT)?; + + let _guard = guard((), |_| { + let _ = ffi::destroy_device_info_list(devinfo); + }); + + let mut member_index = 0; + + while let Some(devinfo_data) = ffi::enum_device_info(devinfo, member_index) { + member_index += 1; + + if devinfo_data.is_err() { + continue; + } + let devinfo_data = devinfo_data?; + + let hardware_id = + ffi::get_device_registry_property(devinfo, &devinfo_data, SPDRP_HARDWAREID); + if hardware_id.is_err() { + continue; + } + if !hardware_id?.eq_ignore_ascii_case(component_id) { + continue; + } + + let key = ffi::open_dev_reg_key( + devinfo, + &devinfo_data, + DICS_FLAG_GLOBAL, + 0, + DIREG_DRV, + KEY_QUERY_VALUE | KEY_NOTIFY, + ); + if key.is_err() { + continue; + } + let key = winreg::RegKey::predef(key? as _); + + let if_type: u32 = match key.get_value("*IfType") { + Ok(if_type) => if_type, + Err(_) => continue, + }; + + let luid_index: u32 = match key.get_value("NetLuidIndex") { + Ok(luid_index) => luid_index, + Err(_) => continue, + }; + + let mut luid2 = NET_LUID_LH { Value: 0 }; + + unsafe { + let luid2 = &mut luid2 as *mut NET_LUID_LH as *mut _NET_LUID_LH; + (*luid2).set_IfType(if_type as _); + (*luid2).set_NetLuidIndex(luid_index as _); + } + + if unsafe { luid.Value != luid2.Value } { + continue; + } + + // Found it! + return ffi::call_class_installer(devinfo, &devinfo_data, DIF_REMOVE); + } + + Err(io::Error::new(io::ErrorKind::NotFound, "Device not found")) +} + +/// Open an handle to an interface +pub fn open_interface(luid: &NET_LUID_LH) -> io::Result { + let guid = ffi::luid_to_guid(luid).and_then(|guid| ffi::string_from_guid(&guid))?; + + let path = format!(r"\\.\Global\{}.tap", guid); + + ffi::create_file( + &path, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + ) +} diff --git a/src/platform/windows/tap/mod.rs b/src/platform/windows/tap/mod.rs new file mode 100644 index 0000000..9e52c70 --- /dev/null +++ b/src/platform/windows/tap/mod.rs @@ -0,0 +1,195 @@ +use crate::platform::windows::{ffi, netsh}; +use std::{io, net, time}; +use windows_sys::Win32::Foundation::HANDLE; +use windows_sys::Win32::NetworkManagement::Ndis::NET_LUID_LH; +use windows_sys::Win32::System::Ioctl::{FILE_ANY_ACCESS, FILE_DEVICE_UNKNOWN, METHOD_BUFFERED}; + +mod iface; + +pub struct TapDevice { + luid: NET_LUID_LH, + handle: HANDLE, + component_id: String, + index: u32, +} +unsafe impl Send for TapDevice {} +unsafe impl Sync for TapDevice {} +impl Drop for TapDevice { + fn drop(&mut self) { + let _ = iface::delete_interface(&self.component_id, &self.luid); + let _ = ffi::close_handle(self.handle); + } +} +fn get_version(handle: HANDLE) -> io::Result<[u64; 3]> { + let in_version: [u64; 3] = [0; 3]; + let mut out_version: [u64; 3] = [0; 3]; + ffi::device_io_control(handle, TAP_IOCTL_GET_VERSION, &in_version, &mut out_version) + .map(|_| out_version) +} +impl TapDevice { + pub fn index(&self) -> u32 { + self.index + } + /// Creates a new tap-windows device + pub fn create(component_id: &str) -> io::Result { + let luid = iface::create_interface(component_id)?; + // Even after retrieving the luid, we might need to wait + let start = time::Instant::now(); + let handle = loop { + // If we surpassed 2 seconds just return + let now = time::Instant::now(); + if now - start > time::Duration::from_secs(3) { + return Err(io::Error::new( + io::ErrorKind::TimedOut, + "Interface timed out", + )); + } + + match iface::open_interface(&luid) { + Err(_) => { + std::thread::yield_now(); + continue; + } + Ok(handle) => { + if get_version(handle).is_err() { + std::thread::sleep(time::Duration::from_millis(200)); + continue; + } + break handle; + } + }; + }; + + let index = ffi::luid_to_index(&luid)?; + Ok(Self { + luid, + handle, + index, + component_id: component_id.to_owned(), + }) + } + + /// Opens an existing tap-windows device by name + pub fn open(component_id: &str, name: &str) -> io::Result { + let luid = ffi::alias_to_luid(name)?; + iface::check_interface(component_id, &luid)?; + + let handle = iface::open_interface(&luid)?; + let index = ffi::luid_to_index(&luid)?; + Ok(Self { + index, + luid, + handle, + component_id: component_id.to_owned(), + }) + } + + /// Deletes the interface before closing it. + /// By default interfaces are never deleted on Drop, + /// with this you can choose if you want deletion or not + pub fn delete(self) -> io::Result<()> { + iface::delete_interface(&self.component_id, &self.luid)?; + + Ok(()) + } + + /// Sets the status of the interface to connected. + /// Equivalent to `.set_status(true)` + pub fn up(&self) -> io::Result<()> { + self.set_status(true) + } + + /// Sets the status of the interface to disconnected. + /// Equivalent to `.set_status(false)` + pub fn down(&self) -> io::Result<()> { + self.set_status(false) + } + + /// Retieve the mac of the interface + pub fn get_mac(&self) -> io::Result<[u8; 6]> { + let mut mac = [0; 6]; + ffi::device_io_control(self.handle, TAP_IOCTL_GET_MAC, &(), &mut mac).map(|_| mac) + } + pub fn set_mac(&self, _mac: &[u8; 6]) -> io::Result<()> { + Err(io::Error::from(io::ErrorKind::Unsupported))? + } + + /// Retrieve the version of the driver + pub fn get_version(&self) -> io::Result<[u64; 3]> { + get_version(self.handle) + } + + /// Retieve the mtu of the interface + pub fn get_mtu(&self) -> io::Result { + let in_mtu: u32 = 0; + let mut out_mtu = 0; + ffi::device_io_control(self.handle, TAP_IOCTL_GET_MTU, &in_mtu, &mut out_mtu) + .map(|_| out_mtu) + } + pub fn set_mtu(&self, mtu: u16) -> io::Result<()> { + netsh::set_interface_mtu(self.index, mtu as _) + } + pub fn get_address(&self) -> io::Result { + unimplemented!() + } + pub fn get_netmask(&self) -> io::Result { + unimplemented!() + } + pub fn get_destination(&self) -> io::Result { + unimplemented!() + } + /// Retrieve the name of the interface + pub fn get_name(&self) -> io::Result { + ffi::luid_to_alias(&self.luid) + } + + /// Set the name of the interface + pub fn set_name(&self, newname: &str) -> io::Result<()> { + let name = self.get_name()?; + netsh::set_interface_name(&name, newname) + } + + // /// Set the ip of the interface + // pub fn set_ip(&self, address: A, mask: B) -> io::Result<()> + // where + // A: Into, + // B: Into, + // { + // let address = address.into().to_string(); + // let mask = mask.into().to_string(); + // + // netsh::set_interface_ip(self.index, address.into(), mask.into(), None) + // } + + /// Set the status of the interface, true for connected, + /// false for disconnected. + pub fn set_status(&self, status: bool) -> io::Result<()> { + let status: u32 = if status { 1 } else { 0 }; + let mut out_status: u32 = 0; + ffi::device_io_control( + self.handle, + TAP_IOCTL_SET_MEDIA_STATUS, + &status, + &mut out_status, + ) + } + pub fn read(&self, buf: &mut [u8]) -> io::Result { + ffi::read_file(self.handle, buf).map(|res| res as _) + } + pub fn write(&self, buf: &[u8]) -> io::Result { + ffi::write_file(self.handle, buf).map(|res| res as _) + } +} + +#[allow(non_snake_case)] +#[inline] +const fn CTL_CODE(DeviceType: u32, Function: u32, Method: u32, Access: u32) -> u32 { + (DeviceType << 16) | (Access << 14) | (Function << 2) | Method +} + +const TAP_IOCTL_GET_MAC: u32 = CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_IOCTL_GET_VERSION: u32 = + CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_IOCTL_GET_MTU: u32 = CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS); +const TAP_IOCTL_SET_MEDIA_STATUS: u32 = + CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS);