From 91722f42706555cea6fdfdd3a50d6a5a493be548 Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Sat, 2 Jul 2016 14:05:24 +0200 Subject: [PATCH] Added support for strange BSD tun header --- README.md | 9 +++++--- src/benches.rs | 1 - src/cloud.rs | 4 ++-- src/device.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 64 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 9467a2d..e238e11 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,15 @@ The tests can be run via ``cargo test``. ##### Debian / Ubuntu -Deb packages for each release can be found in the [releases](https://github.com/dswd/vpncloud.rs/releases) section. -Currently only packages for amd64 are available (I am accepting help on building and packaging for other platforms). +Deb packages for each release can be found in the +[releases](https://github.com/dswd/vpncloud.rs/releases) section. Currently only +packages for amd64 are available (I am accepting help on building and packaging +for other platforms). ##### Arch Linux (AUR) -There is a [VpnCloud package for Arch Linux](https://aur.archlinux.org/packages/vpncloud/) thanks to Oscar Rainford (fourbytes). +There is a [VpnCloud package for Arch Linux](https://aur.archlinux.org/packages/vpncloud/) +thanks to Oscar Rainford (fourbytes). ### Contributions welcome diff --git a/src/benches.rs b/src/benches.rs index 5394bb1..8b57f5b 100644 --- a/src/benches.rs +++ b/src/benches.rs @@ -7,7 +7,6 @@ use test::Bencher; use std::str::FromStr; use std::net::{UdpSocket, ToSocketAddrs, Ipv4Addr, SocketAddr, SocketAddrV4}; use std::os::unix::io::AsRawFd; -use std::mem; use super::cloud::GenericCloud; use super::device::{Device, Type}; diff --git a/src/cloud.rs b/src/cloud.rs index 45eeb4e..c47922f 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -500,7 +500,7 @@ impl GenericCloud

{ Message::Data(payload, start, end) => { let (src, _dst) = try!(P::parse(&payload[start..end])); debug!("Writing data to device: {} bytes", end-start); - match self.device.write(&payload[start..end]) { + match self.device.write(&mut payload[..end], start) { Ok(()) => (), Err(e) => { error!("Failed to send via device: {}", e); @@ -592,7 +592,7 @@ impl GenericCloud

{ }, fd if (fd == device_fd) => { let start = 64; - let size = try_fail!(self.device.read(&mut buffer[start..]), "Failed to read from tap device: {}"); + let (start, size) = try_fail!(self.device.read(&mut buffer[start..]), "Failed to read from tap device: {}"); if let Err(e) = self.handle_interface_data(&mut buffer, start, start+size) { error!("Error: {}", e); } diff --git a/src/device.rs b/src/device.rs index 04e5bc3..23e15c0 100644 --- a/src/device.rs +++ b/src/device.rs @@ -126,31 +126,80 @@ impl Device { /// The `buffer` must be large enough to hold a packet/frame of maximum size, otherwise the /// packet/frame will be split. /// The method will block until a packet/frame is ready to be read. - /// On success, the method will return the amount of bytes read into the buffer (starting at - /// position 0). + /// On success, the method will return the starting position and the amount of bytes read into + /// the buffer. /// /// # Errors /// This method will return an error if the underlying read call fails. #[inline] - pub fn read(&mut self, mut buffer: &mut [u8]) -> Result { - self.fd.read(&mut buffer).map_err(|_| Error::TunTapDevError("Read error")) + pub fn read(&mut self, mut buffer: &mut [u8]) -> Result<(usize, usize), Error> { + let read = try!(self.fd.read(&mut buffer).map_err(|_| Error::TunTapDevError("Read error"))); + let (start, read) = self.correct_data_after_read(&mut buffer, 0, read); + Ok((start, read)) + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + #[inline] + fn correct_data_after_read(&mut self, _buffer: &mut [u8], start: usize, read: usize) -> (usize, usize) { + (start, read) + } + + #[cfg(any(target_os = "bitrig", target_os = "dragonfly", + target_os = "freebsd", target_os = "ios", target_os = "macos", + target_os = "netbsd", target_os = "openbsd"))] + #[inline] + fn correct_data_after_read(&mut self, buffer: &mut [u8], start: usize, read: usize) -> (usize, usize) { + if self.type_ == Type::Tun { + // BSD-based systems add a 4-byte header containing the Ethertype for TUN + assert!(read>=4); + (start+4, read-4) + } else { + (start, read) + } } /// Writes a packet/frame to the device /// /// This method writes one packet or frame (depending on the device type) from `data` to the - /// device. + /// device. The data starts at the position `start` in the buffer. The buffer should have at + /// least 4 bytes of space before the start of the packet. /// The method will block until the packet/frame has been written. /// /// # Errors /// This method will return an error if the underlying read call fails. #[inline] - pub fn write(&mut self, data: &[u8]) -> Result<(), Error> { - match self.fd.write_all(data) { + pub fn write(&mut self, mut data: &mut [u8], start: usize) -> Result<(), Error> { + let start = self.correct_data_before_write(&mut data, start); + match self.fd.write_all(&data[start..]) { Ok(_) => self.fd.flush().map_err(|_| Error::TunTapDevError("Flush error")), Err(_) => Err(Error::TunTapDevError("Write error")) } } + + #[cfg(any(target_os = "linux", target_os = "android"))] + #[inline] + fn correct_data_before_write(&mut self, _buffer: &mut [u8], start: usize) -> usize { + start + } + + #[cfg(any(target_os = "bitrig", target_os = "dragonfly", + target_os = "freebsd", target_os = "ios", target_os = "macos", + target_os = "netbsd", target_os = "openbsd"))] + #[inline] + fn correct_data_before_write(&mut self, buffer: &mut [u8], start: usize) -> usize { + if self.type_ == Type::Tun { + // BSD-based systems add a 4-byte header containing the Ethertype for TUN + assert!(start>=4); + match buffer[start] >> 4 { // IP version + 4 => buffer[start-4..start].clone_from_slice(&[0x00, 0x00, 0x08, 0x00]), + 6 => buffer[start-4..start].clone_from_slice(&[0x00, 0x00, 0x86, 0xdd]), + _ => unreachable!() + } + start-4 + } else { + start + } + } } impl AsRawFd for Device {