// VpnCloud - Peer-to-Peer VPN // Copyright (C) 2015-2016 Dennis Schwerdel // This software is licensed under GPL-3 or newer (see LICENSE.md) use std::os::unix::io::{AsRawFd, RawFd}; use std::io::{Result as IoResult, Error as IoError, Read, Write}; use std::fs; use std::fmt; use super::types::Error; extern { fn setup_tap_device(fd: i32, ifname: *mut u8) -> i32; fn setup_tun_device(fd: i32, ifname: *mut u8) -> i32; } /// The type of a tun/tap device #[derive(RustcDecodable, Debug, Clone, Copy)] pub enum Type { /// Tun interface: This interface transports IP packets. Tun, /// Tap interface: This insterface transports Ethernet frames. Tap } impl fmt::Display for Type { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { Type::Tun => write!(formatter, "tun"), Type::Tap => write!(formatter, "tap"), } } } /// Represents a tun/tap device pub struct Device { fd: fs::File, ifname: String, type_: Type, } impl Device { /// Creates a new tun/tap device /// /// This method creates a new device of the `type_` kind with the name `ifname`. /// /// The `ifname` must be an interface name not longer than 31 bytes. It can contain the string /// `%d` which will be replaced with the next free index number that guarantees that the /// interface name will be free. In this case, the `ifname()` method can be used to obtain the /// final interface name. /// /// # Errors /// This method will return an error when the underlying system call fails. Common cases are: /// - The special device file `/dev/net/tun` does not exist or is not accessible by the current /// user. /// - The interface name is invalid or already in use. /// - The current user does not have enough permissions to create tun/tap devices (this /// requires root permissions). /// /// # Panics /// This method panics if the interface name is longer than 31 bytes. pub fn new(ifname: &str, type_: Type) -> IoResult { let fd = try!(fs::OpenOptions::new().read(true).write(true).open("/dev/net/tun")); // Add trailing \0 to interface name let mut ifname_string = String::with_capacity(32); ifname_string.push_str(ifname); ifname_string.push('\0'); assert!(ifname_string.len() <= 32); let mut ifname_c = ifname_string.into_bytes(); let res = match type_ { Type::Tun => unsafe { setup_tun_device(fd.as_raw_fd(), ifname_c.as_mut_ptr()) }, Type::Tap => unsafe { setup_tap_device(fd.as_raw_fd(), ifname_c.as_mut_ptr()) } }; match res { 0 => { // Remove trailing \0 from name while ifname_c.last() == Some(&0) { ifname_c.pop(); } Ok(Device{fd: fd, ifname: String::from_utf8(ifname_c).unwrap(), type_: type_}) }, _ => Err(IoError::last_os_error()) } } /// Returns the interface name of this device. #[inline] pub fn ifname(&self) -> &str { &self.ifname } /// Returns the type of this device #[allow(dead_code)] #[inline] pub fn get_type(&self) -> Type { self.type_ } /// Creates a dummy device based on an existing file /// /// This method opens a regular or special file and reads from it to receive packets and /// writes to it to send packets. This method does not use a networking device and therefore /// can be used for testing. /// /// The parameter `path` is the file that should be used. Special files like `/dev/null`, /// named pipes and unix sockets can be used with this method. /// /// Both `ifname` and `type_` parameters have no effect. /// /// # Errors /// This method will return an error if the file can not be opened for reading and writing. #[allow(dead_code)] pub fn dummy(ifname: &str, path: &str, type_: Type) -> IoResult { Ok(Device{ fd: try!(fs::OpenOptions::new().create(true).read(true).write(true).open(path)), ifname: ifname.to_string(), type_: type_ }) } /// Reads a packet/frame from the device /// /// This method reads one packet or frame (depending on the device type) into the `buffer`. /// 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). /// /// # 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")) } /// Writes a packet/frame to the device /// /// This method writes one packet or frame (depending on the device type) from `data` to the /// device. /// 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) { Ok(_) => self.fd.flush().map_err(|_| Error::TunTapDevError("Flush error")), Err(_) => Err(Error::TunTapDevError("Write error")) } } } impl AsRawFd for Device { #[inline] fn as_raw_fd(&self) -> RawFd { self.fd.as_raw_fd() } }