diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b92f5846..7fd4430e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,10 +1,7 @@ name: Push or PR on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] + [push, pull_request] env: CARGO_TERM_COLOR: always diff --git a/Cargo.toml b/Cargo.toml index e7353857..198d263f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ tokio-util = { version = "0.7", features = ["codec"], optional = true } ioctl = { version = "0.8", package = "ioctl-sys" } [target.'cfg(target_os = "windows")'.dependencies] -wintun = { version = "0.3", features = ["panic_on_unsent_packets"] } +wintun = { version = "0.4", features = ["panic_on_unsent_packets"] } [dev-dependencies] futures = "0.3" diff --git a/README.md b/README.md index 7c468188..2d95c24f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ -TUN interfaces [![Crates.io](https://img.shields.io/crates/v/tun.svg)](https://crates.io/crates/tun) ![tun](https://docs.rs/tun/badge.svg) ![WTFPL](http://img.shields.io/badge/license-WTFPL-blue.svg) +TUN interfaces ============== +[![Crates.io](https://img.shields.io/crates/v/tun.svg)](https://crates.io/crates/tun) +![tun](https://docs.rs/tun/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. Usage @@ -8,7 +12,7 @@ First, add the following to your `Cargo.toml`: ```toml [dependencies] -tun = "0.6.1" +tun = "0.6" ``` Next, add this to your crate root: @@ -21,7 +25,7 @@ If you want to use the TUN interface with mio/tokio, you need to enable the `asy ```toml [dependencies] -tun = { version = "0.6.1", features = ["async"] } +tun = { version = "0.6", features = ["async"] } ``` Example @@ -43,6 +47,7 @@ fn main() { #[cfg(target_os = "linux")] config.platform(|config| { config.packet_information(true); + config.apply_settings(true); }); let mut dev = tun::create(&config).unwrap(); @@ -57,7 +62,7 @@ fn main() { Platforms ========= -Not every platform is supported. +Recently, `tun` supports Linux, Android, macOS, iOS and Windows. Linux ----- diff --git a/examples/ping-tun.rs b/examples/ping-tun.rs index 98dc4fd3..1d56502e 100644 --- a/examples/ping-tun.rs +++ b/examples/ping-tun.rs @@ -29,11 +29,12 @@ async fn main() -> Result<(), Box> { #[cfg(target_os = "linux")] config.platform(|config| { config.packet_information(true); + config.apply_settings(true); }); #[cfg(target_os = "windows")] config.platform(|config| { - config.initialize(); + config.initialize(Some(9099482345783245345345_u128)); }); let dev = tun::create_as_async(&config)?; diff --git a/examples/read-async.rs b/examples/read-async.rs index 9fe9def6..a026ef31 100644 --- a/examples/read-async.rs +++ b/examples/read-async.rs @@ -27,6 +27,7 @@ async fn main() { #[cfg(target_os = "linux")] config.platform(|config| { config.packet_information(true); + config.apply_settings(true); }); let dev = tun::create_as_async(&config).unwrap(); diff --git a/examples/read.rs b/examples/read.rs index 5beda850..c534ba6f 100644 --- a/examples/read.rs +++ b/examples/read.rs @@ -26,6 +26,7 @@ fn main() -> Result<(), Box> { #[cfg(target_os = "linux")] config.platform(|config| { config.packet_information(true); + config.apply_settings(true); }); let mut dev = tun::create(&config)?; diff --git a/src/async/codec.rs b/src/async/codec.rs index 07b2b55a..52ab762a 100644 --- a/src/async/codec.rs +++ b/src/async/codec.rs @@ -14,7 +14,6 @@ use std::io; -use byteorder::{NativeEndian, NetworkEndian, WriteBytesExt}; use bytes::{BufMut, Bytes, BytesMut}; use tokio_util::codec::{Decoder, Encoder}; @@ -54,6 +53,7 @@ impl PacketProtocol { } #[cfg(target_os = "windows")] + #[allow(dead_code)] fn into_pi_field(self) -> Result { unimplemented!() } @@ -95,8 +95,8 @@ pub struct TunPacketCodec(bool, i32); impl TunPacketCodec { /// Create a new `TunPacketCodec` specifying whether the underlying /// tunnel Device has enabled the packet information header. - pub fn new(pi: bool, mtu: i32) -> TunPacketCodec { - TunPacketCodec(pi, mtu) + pub fn new(packet_information: bool, mtu: i32) -> TunPacketCodec { + TunPacketCodec(packet_information, mtu) } } @@ -132,19 +132,24 @@ impl Encoder for TunPacketCodec { type Error = io::Error; fn encode(&mut self, item: TunPacket, dst: &mut BytesMut) -> Result<(), Self::Error> { - dst.reserve(item.get_bytes().len() + 4); + dst.reserve(item.get_bytes().len() + if self.0 { 4 } else { 0 }); match item { - TunPacket(proto, bytes) if self.0 => { - // build the packet information header comprising of 2 u16 - // fields: flags and protocol. - let mut buf = Vec::::with_capacity(4); - - // flags is always 0 - buf.write_u16::(0)?; - // write the protocol as network byte order - buf.write_u16::(proto.into_pi_field()?)?; - - dst.put_slice(&buf); + TunPacket(_proto, bytes) if self.0 => { + #[cfg(unix)] + { + use byteorder::{NativeEndian, NetworkEndian, WriteBytesExt}; + + // build the packet information header comprising of 2 u16 + // fields: flags and protocol. + let mut buf = Vec::::with_capacity(4); + + // flags is always 0 + buf.write_u16::(0)?; + // write the protocol as network byte order + buf.write_u16::(_proto.into_pi_field()?)?; + + dst.put_slice(&buf); + } dst.put(bytes); } TunPacket(_, bytes) => dst.put(bytes), diff --git a/src/async/device.rs b/src/async/device.rs index 5ac89fdb..54967ccf 100644 --- a/src/async/device.rs +++ b/src/async/device.rs @@ -51,8 +51,9 @@ impl AsyncDevice { /// Consumes this AsyncDevice and return a Framed object (unified Stream and Sink interface) pub fn into_framed(mut self) -> Framed { - let pi = self.get_mut().has_packet_information(); - let codec = TunPacketCodec::new(pi, self.inner.get_ref().mtu().unwrap_or(1504)); + let packet_information = self.get_mut().has_packet_information(); + let mtu = self.inner.get_ref().mtu().unwrap_or(1504); + let codec = TunPacketCodec::new(packet_information, mtu); Framed::new(self, codec) } } @@ -147,8 +148,8 @@ impl AsyncQueue { /// Consumes this AsyncQueue and return a Framed object (unified Stream and Sink interface) pub fn into_framed(mut self) -> Framed { - let pi = self.get_mut().has_packet_information(); - let codec = TunPacketCodec::new(pi, 1504); + let packet_information = self.get_mut().has_packet_information(); + let codec = TunPacketCodec::new(packet_information, 1504); Framed::new(self, codec) } } diff --git a/src/async/win/device.rs b/src/async/win/device.rs index 8b67037d..2cb8afc3 100644 --- a/src/async/win/device.rs +++ b/src/async/win/device.rs @@ -15,7 +15,7 @@ use core::pin::Pin; use core::task::{Context, Poll}; use std::io; -use std::io::{Error, Write}; +use std::io::Error; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio_util::codec::Framed; @@ -26,12 +26,17 @@ use crate::r#async::codec::*; pub struct AsyncDevice { inner: Device, + session: WinSession, } impl AsyncDevice { /// Create a new `AsyncDevice` wrapping around a `Device`. pub fn new(device: Device) -> io::Result { - Ok(AsyncDevice { inner: device }) + let session = WinSession::new(device.queue.get_session())?; + Ok(AsyncDevice { + inner: device, + session, + }) } /// Returns a shared reference to the underlying Device object pub fn get_ref(&self) -> &Device { @@ -56,50 +61,41 @@ impl AsyncRead for AsyncDevice { cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { - let rbuf = buf.initialize_unfilled(); - match Pin::new(&mut self.inner).poll_read(cx, rbuf) { - Poll::Ready(Ok(n)) => { - buf.advance(n); - Poll::Ready(Ok(())) - } - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - Poll::Pending => Poll::Pending, - } + Pin::new(&mut self.session).poll_read(cx, buf) } } impl AsyncWrite for AsyncDevice { fn poll_write( mut self: Pin<&mut Self>, - _cx: &mut Context<'_>, + cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - match self.inner.write(buf) { - Ok(n) => Poll::Ready(Ok(n)), - Err(e) => Poll::Ready(Err(e)), - } + Pin::new(&mut self.session).poll_write(cx, buf) } - fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - match self.inner.flush() { - Ok(n) => Poll::Ready(Ok(n)), - Err(e) => Poll::Ready(Err(e)), - } + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.session).poll_flush(cx) } - fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.session).poll_shutdown(cx) } } pub struct AsyncQueue { inner: Queue, + session: WinSession, } impl AsyncQueue { /// Create a new `AsyncQueue` wrapping around a `Queue`. pub fn new(queue: Queue) -> io::Result { - Ok(AsyncQueue { inner: queue }) + let session = WinSession::new(queue.get_session())?; + Ok(AsyncQueue { + inner: queue, + session, + }) } /// Returns a shared reference to the underlying Queue object pub fn get_ref(&self) -> &Queue { @@ -124,29 +120,87 @@ impl AsyncRead for AsyncQueue { cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { - let rbuf = buf.initialize_unfilled(); - match Pin::new(&mut self.inner).poll_read(cx, rbuf) { - Poll::Ready(Ok(n)) => { - buf.advance(n); - Poll::Ready(Ok(())) - } - Poll::Ready(Err(e)) => Poll::Ready(Err(e)), - Poll::Pending => Poll::Pending, - } + Pin::new(&mut self.session).poll_read(cx, buf) } } impl AsyncWrite for AsyncQueue { fn poll_write( mut self: Pin<&mut Self>, - _cx: &mut Context<'_>, + cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - match self.inner.write(buf) { - Ok(n) => Poll::Ready(Ok(n)), - Err(e) => Poll::Ready(Err(e)), + Pin::new(&mut self.session).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.session).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.session).poll_shutdown(cx) + } +} + +struct WinSession { + session: std::sync::Arc, + receiver: tokio::sync::mpsc::UnboundedReceiver>, + _task: std::thread::JoinHandle<()>, +} + +impl WinSession { + fn new(session: std::sync::Arc) -> Result { + let session_reader = session.clone(); + let (receiver_tx, receiver_rx) = tokio::sync::mpsc::unbounded_channel::>(); + let task = std::thread::spawn(move || loop { + match session_reader.receive_blocking() { + Ok(packet) => { + if let Err(err) = receiver_tx.send(packet.bytes().to_vec()) { + log::error!("{}", err); + } + } + Err(err) => { + log::info!("{}", err); + break; + } + } + }); + + Ok(WinSession { + session, + receiver: receiver_rx, + _task: task, + }) + } +} + +impl AsyncRead for WinSession { + 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) => { + buf.put_slice(&bytes); + std::task::Poll::Ready(Ok(())) + } + None => std::task::Poll::Ready(Ok(())), } } +} + +impl AsyncWrite for WinSession { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let mut write_pack = self.session.allocate_send_packet(buf.len() as u16)?; + write_pack.bytes_mut().copy_from_slice(buf.as_ref()); + self.session.send_packet(write_pack); + std::task::Poll::Ready(Ok(buf.len())) + } fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) diff --git a/src/error.rs b/src/error.rs index f71efefe..f6e9e53b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -54,4 +54,13 @@ pub enum Error { WintunError(#[from] wintun::Error), } +impl From for io::Error { + fn from(value: Error) -> Self { + match value { + Error::Io(err) => err, + _ => io::Error::new(io::ErrorKind::Other, value), + } + } +} + pub type Result = ::std::result::Result; diff --git a/src/platform/linux/device.rs b/src/platform/linux/device.rs index 41ce0958..fa4cf973 100644 --- a/src/platform/linux/device.rs +++ b/src/platform/linux/device.rs @@ -96,7 +96,7 @@ impl Device { queues.push(Queue { tun, - pi_enabled: config.platform.packet_information, + packet_information: config.platform.packet_information, }); } @@ -108,7 +108,9 @@ impl Device { Device { name, queues, ctl } }; - device.configure(config)?; + if config.platform.apply_settings { + device.configure(config)?; + } Ok(device) } @@ -399,12 +401,12 @@ impl IntoRawFd for Device { pub struct Queue { tun: Fd, - pi_enabled: bool, + packet_information: bool, } impl Queue { pub fn has_packet_information(&mut self) -> bool { - self.pi_enabled + self.packet_information } pub fn set_nonblock(&self) -> io::Result<()> { diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 455a2791..20003113 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -23,9 +23,19 @@ use crate::configuration::Configuration as C; use crate::error::*; /// Linux-only interface configuration. -#[derive(Copy, Clone, Default, Debug)] +#[derive(Copy, Clone, Debug)] pub struct Configuration { pub(crate) packet_information: bool, + pub(crate) apply_settings: bool, +} + +impl Default for Configuration { + fn default() -> Self { + Configuration { + packet_information: false, + apply_settings: true, + } + } } impl Configuration { @@ -35,6 +45,12 @@ impl Configuration { self.packet_information = value; self } + + /// Enable or disable to assign IP/netmask/destination etc. + pub fn apply_settings(&mut self, value: bool) -> &mut Self { + self.apply_settings = value; + self + } } /// Create a TUN device with the given name. diff --git a/src/platform/macos/device.rs b/src/platform/macos/device.rs index 6af3336c..17f5bc87 100644 --- a/src/platform/macos/device.rs +++ b/src/platform/macos/device.rs @@ -38,14 +38,24 @@ use std::{ /// A TUN device using the TUN macOS driver. pub struct Device { - name: String, + name: Option, queue: Queue, - ctl: Fd, + ctl: Option, } impl Device { /// Create a new `Device` for the given `Configuration`. pub fn new(config: &Configuration) -> Result { + if let Some(fd) = config.raw_fd { + let tun = Fd::new(fd).map_err(|_| io::Error::last_os_error())?; + let device = Device { + name: None, + queue: Queue { tun }, + ctl: None, + }; + return Ok(device); + } + let id = if let Some(name) = config.name.as_ref() { if name.len() > IFNAMSIZ { return Err(Error::NameTooLong); @@ -55,9 +65,9 @@ impl Device { return Err(Error::InvalidName); } - name[4..].parse::()? + 1u32 + name[4..].parse::()? + 1_u32 } else { - 0u32 + 0_u32 }; if config.layer.filter(|l| *l != Layer::L3).is_some() { @@ -87,16 +97,16 @@ impl Device { return Err(io::Error::last_os_error().into()); } - let addr = sockaddr_ctl { + let addr = libc::sockaddr_ctl { sc_id: info.ctl_id, - sc_len: mem::size_of::() as _, + 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 sockaddr_ctl as *const sockaddr; + let address = &addr as *const libc::sockaddr_ctl as *const sockaddr; if libc::connect(tun.0, address, mem::size_of_val(&addr) as socklen_t) < 0 { return Err(io::Error::last_os_error().into()); } @@ -110,12 +120,14 @@ impl Device { return Err(io::Error::last_os_error().into()); } - let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))?; + let ctl = Some(Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))?); Device { - name: CStr::from_ptr(name.as_ptr() as *const c_char) - .to_string_lossy() - .into(), + name: Some( + CStr::from_ptr(name.as_ptr() as *const c_char) + .to_string_lossy() + .into(), + ), queue: Queue { tun }, ctl, } @@ -133,32 +145,35 @@ impl Device { /// Prepare a new request. /// # Safety - pub unsafe fn request(&self) -> ifreq { - let mut req: ifreq = mem::zeroed(); + pub unsafe fn request(&self) -> Result { + let name = self.name.as_ref().ok_or(Error::InvalidConfig)?; + let mut req: libc::ifreq = mem::zeroed(); ptr::copy_nonoverlapping( - self.name.as_ptr() as *const c_char, - req.ifrn.name.as_mut_ptr(), - self.name.len(), + name.as_ptr() as *const c_char, + req.ifr_name.as_mut_ptr(), + name.len(), ); - req + Ok(req) } /// Set the IPv4 alias of the device. pub fn set_alias(&mut self, addr: Ipv4Addr, broadaddr: Ipv4Addr, mask: Ipv4Addr) -> Result<()> { + let name = self.name.as_ref().ok_or(Error::InvalidConfig)?; + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { let mut req: ifaliasreq = mem::zeroed(); ptr::copy_nonoverlapping( - self.name.as_ptr() as *const c_char, + name.as_ptr() as *const c_char, req.ifran.as_mut_ptr(), - self.name.len(), + name.len(), ); req.addr = SockAddr::from(addr).into(); req.broadaddr = SockAddr::from(broadaddr).into(); req.mask = SockAddr::from(mask).into(); - if siocaifaddr(self.ctl.as_raw_fd(), &req) < 0 { + if siocaifaddr(ctl.as_raw_fd(), &req) < 0 { return Err(io::Error::last_os_error().into()); } @@ -211,7 +226,7 @@ impl D for Device { type Queue = Queue; fn name(&self) -> Result { - Ok(self.name.clone()) + self.name.as_ref().cloned().ok_or(Error::InvalidConfig) } // XXX: Cannot set interface name on Darwin. @@ -220,20 +235,21 @@ impl D for Device { } fn enabled(&mut self, value: bool) -> Result<()> { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); + let mut req = self.request()?; - if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 { + if siocgifflags(ctl.as_raw_fd(), &mut req) < 0 { return Err(io::Error::last_os_error().into()); } if value { - req.ifru.flags |= (IFF_UP | IFF_RUNNING) as c_short; + req.ifr_ifru.ifru_flags |= (IFF_UP | IFF_RUNNING) as c_short; } else { - req.ifru.flags &= !(IFF_UP as c_short); + req.ifr_ifru.ifru_flags &= !(IFF_UP as c_short); } - if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 { + if siocsifflags(ctl.as_raw_fd(), &req) < 0 { return Err(io::Error::last_os_error().into()); } @@ -242,23 +258,25 @@ impl D for Device { } fn address(&self) -> Result { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); + let mut req = self.request()?; - if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 { + if siocgifaddr(ctl.as_raw_fd(), &mut req) < 0 { return Err(io::Error::last_os_error().into()); } - SockAddr::new(&req.ifru.addr).map(Into::into) + SockAddr::new(&req.ifr_ifru.ifru_addr).map(Into::into) } } fn set_address(&mut self, value: Ipv4Addr) -> Result<()> { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); - req.ifru.addr = SockAddr::from(value).into(); + let mut req = self.request()?; + req.ifr_ifru.ifru_addr = SockAddr::from(value).into(); - if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 { + if siocsifaddr(ctl.as_raw_fd(), &req) < 0 { return Err(io::Error::last_os_error().into()); } @@ -267,23 +285,25 @@ impl D for Device { } fn destination(&self) -> Result { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); + let mut req = self.request()?; - if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 { + if siocgifdstaddr(ctl.as_raw_fd(), &mut req) < 0 { return Err(io::Error::last_os_error().into()); } - SockAddr::new(&req.ifru.dstaddr).map(Into::into) + SockAddr::new(&req.ifr_ifru.ifru_dstaddr).map(Into::into) } } fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); - req.ifru.dstaddr = SockAddr::from(value).into(); + let mut req = self.request()?; + req.ifr_ifru.ifru_dstaddr = SockAddr::from(value).into(); - if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 { + if siocsifdstaddr(ctl.as_raw_fd(), &req) < 0 { return Err(io::Error::last_os_error().into()); } @@ -292,23 +312,25 @@ impl D for Device { } fn broadcast(&self) -> Result { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); + let mut req = self.request()?; - if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 { + if siocgifbrdaddr(ctl.as_raw_fd(), &mut req) < 0 { return Err(io::Error::last_os_error().into()); } - SockAddr::new(&req.ifru.broadaddr).map(Into::into) + SockAddr::new(&req.ifr_ifru.ifru_broadaddr).map(Into::into) } } fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); - req.ifru.broadaddr = SockAddr::from(value).into(); + let mut req = self.request()?; + req.ifr_ifru.ifru_broadaddr = SockAddr::from(value).into(); - if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 { + if siocsifbrdaddr(ctl.as_raw_fd(), &req) < 0 { return Err(io::Error::last_os_error().into()); } @@ -317,23 +339,25 @@ impl D for Device { } fn netmask(&self) -> Result { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); + let mut req = self.request()?; - if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 { + if siocgifnetmask(ctl.as_raw_fd(), &mut req) < 0 { return Err(io::Error::last_os_error().into()); } - SockAddr::unchecked(&req.ifru.addr).map(Into::into) + SockAddr::unchecked(&req.ifr_ifru.ifru_addr).map(Into::into) } } fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); - req.ifru.addr = SockAddr::from(value).into(); + let mut req = self.request()?; + req.ifr_ifru.ifru_addr = SockAddr::from(value).into(); - if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 { + if siocsifnetmask(ctl.as_raw_fd(), &req) < 0 { return Err(io::Error::last_os_error().into()); } @@ -342,23 +366,25 @@ impl D for Device { } fn mtu(&self) -> Result { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); + let mut req = self.request()?; - if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 { + if siocgifmtu(ctl.as_raw_fd(), &mut req) < 0 { return Err(io::Error::last_os_error().into()); } - Ok(req.ifru.mtu) + Ok(req.ifr_ifru.ifru_mtu) } } fn set_mtu(&mut self, value: i32) -> Result<()> { + let ctl = self.ctl.as_ref().ok_or(Error::InvalidConfig)?; unsafe { - let mut req = self.request(); - req.ifru.mtu = value; + let mut req = self.request()?; + req.ifr_ifru.ifru_mtu = value; - if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 { + if siocsifmtu(ctl.as_raw_fd(), &req) < 0 { return Err(io::Error::last_os_error().into()); } diff --git a/src/platform/macos/sys.rs b/src/platform/macos/sys.rs index 94412cdc..35e1d009 100644 --- a/src/platform/macos/sys.rs +++ b/src/platform/macos/sys.rs @@ -15,7 +15,7 @@ //! Bindings to internal macOS stuff. use ioctl::*; -use libc::{c_char, c_int, c_short, c_uint, c_ushort, c_void, sockaddr, IFNAMSIZ}; +use libc::{c_char, c_uint, ifreq, sockaddr, IFNAMSIZ}; pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control"; @@ -27,78 +27,6 @@ pub struct ctl_info { pub ctl_name: [c_char; 96], } -#[allow(non_camel_case_types)] -#[repr(C)] -#[derive(Copy, Clone)] -pub struct sockaddr_ctl { - pub sc_len: c_char, - pub sc_family: c_char, - pub ss_sysaddr: c_ushort, - pub sc_id: c_uint, - pub sc_unit: c_uint, - pub sc_reserved: [c_uint; 5], -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub union ifrn { - pub name: [c_char; IFNAMSIZ], -} - -#[allow(non_camel_case_types)] -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ifdevmtu { - pub current: c_int, - pub min: c_int, - pub max: c_int, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub union ifku { - pub ptr: *mut c_void, - pub value: c_int, -} - -#[allow(non_camel_case_types)] -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ifkpi { - pub module_id: c_uint, - pub type_: c_uint, - pub ifku: ifku, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub union ifru { - pub addr: sockaddr, - pub dstaddr: sockaddr, - pub broadaddr: sockaddr, - - pub flags: c_short, - pub metric: c_int, - pub mtu: c_int, - pub phys: c_int, - pub media: c_int, - pub intval: c_int, - pub data: *mut c_void, - pub devmtu: ifdevmtu, - pub wake_flags: c_uint, - pub route_refcnt: c_uint, - pub cap: [c_int; 2], - pub functional_type: c_uint, -} - -#[allow(non_camel_case_types)] -#[repr(C)] -#[derive(Copy, Clone)] -pub struct ifreq { - pub ifrn: ifrn, - pub ifru: ifru, -} - #[allow(non_camel_case_types)] #[repr(C)] #[derive(Copy, Clone)] diff --git a/src/platform/windows/device.rs b/src/platform/windows/device.rs index 53bee9e4..d39292d4 100644 --- a/src/platform/windows/device.rs +++ b/src/platform/windows/device.rs @@ -14,11 +14,7 @@ use std::io::{self, Read, Write}; use std::net::{IpAddr, Ipv4Addr}; -use std::pin::Pin; -use std::sync::{Arc, Mutex}; -use std::task::{Context, Poll}; -use std::thread; -use std::vec::Vec; +use std::sync::Arc; use wintun::Session; @@ -28,7 +24,7 @@ use crate::error::*; /// A TUN device using the wintun driver. pub struct Device { - queue: Queue, + pub(crate) queue: Queue, mtu: usize, } @@ -37,7 +33,7 @@ impl Device { pub fn new(config: &Configuration) -> Result { let wintun = unsafe { wintun::load()? }; let tun_name = config.name.as_deref().unwrap_or("wintun"); - let guid = Some(9099482345783245345345_u128); + let guid = config.platform.device_guid; let adapter = match wintun::Adapter::open(&wintun, tun_name) { Ok(a) => a, Err(_) => wintun::Adapter::create(&wintun, tun_name, tun_name, guid)?, @@ -54,7 +50,6 @@ impl Device { let mut device = Device { queue: Queue { session: Arc::new(session), - cached: Arc::new(Mutex::new(Vec::with_capacity(mtu))), }, mtu, }; @@ -64,14 +59,6 @@ impl Device { Ok(device) } - - pub fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - Pin::new(&mut self.queue).poll_read(cx, buf) - } } impl Read for Device { @@ -183,7 +170,6 @@ impl D for Device { fn set_mtu(&mut self, value: i32) -> Result<()> { self.mtu = value as usize; - self.queue.cached = Arc::new(Mutex::new(Vec::with_capacity(self.mtu))); Ok(()) } @@ -194,70 +180,11 @@ impl D for Device { pub struct Queue { session: Arc, - cached: Arc>>, } impl Queue { - pub fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - mut buf: &mut [u8], - ) -> Poll> { - { - let mut cached = self - .cached - .lock() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; - if cached.len() > 0 { - let res = match io::copy(&mut cached.as_slice(), &mut buf) { - Ok(n) => Poll::Ready(Ok(n as usize)), - Err(e) => Poll::Ready(Err(e)), - }; - cached.clear(); - return res; - } - } - let reader_session = self.session.clone(); - match reader_session.try_receive() { - Err(e) => Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e))), - Ok(Some(packet)) => match io::copy(&mut packet.bytes(), &mut buf) { - Ok(n) => Poll::Ready(Ok(n as usize)), - Err(e) => Poll::Ready(Err(e)), - }, - Ok(None) => { - let waker = cx.waker().clone(); - let cached = self.cached.clone(); - thread::spawn(move || { - match reader_session.receive_blocking() { - Ok(packet) => { - if let Ok(mut cached) = cached.lock() { - cached.extend_from_slice(packet.bytes()); - } else { - log::error!("cached lock error in wintun reciever thread, packet will be dropped"); - } - } - Err(e) => log::error!("receive_blocking error: {:?}", e), - } - waker.wake() - }); - Poll::Pending - } - } - } - - #[allow(dead_code)] - fn try_read(&mut self, mut buf: &mut [u8]) -> io::Result { - let reader_session = self.session.clone(); - match reader_session.try_receive() { - Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), - Ok(op) => match op { - None => Ok(0), - Some(packet) => match io::copy(&mut packet.bytes(), &mut buf) { - Ok(s) => Ok(s as usize), - Err(e) => Err(e), - }, - }, - } + pub fn get_session(&self) -> Arc { + self.session.clone() } } diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index d5d6c4c7..5eca4431 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -23,11 +23,14 @@ use crate::error::*; /// Windows-only interface configuration. #[derive(Copy, Clone, Default, Debug)] -pub struct Configuration {} +pub struct Configuration { + pub(crate) device_guid: Option, +} impl Configuration { - pub fn initialize(&mut self) { + pub fn initialize(&mut self, device_guid: Option) { log::trace!("Windows configuration initialize"); + self.device_guid = device_guid; } }