From c750947ab0d03864d9b5e973fd0f2956d495fd5c Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Tue, 26 Feb 2019 01:21:15 +0100 Subject: [PATCH] First cloud test --- src/cloud.rs | 53 ++++++++++++-- src/device.rs | 175 +++++++++++++++++++++++++++++++--------------- src/main.rs | 14 ++-- src/net.rs | 4 ++ src/poll/epoll.rs | 1 - src/udpmessage.rs | 2 +- 6 files changed, 181 insertions(+), 68 deletions(-) diff --git a/src/cloud.rs b/src/cloud.rs index 004fcc8..6a2bf12 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -201,7 +201,7 @@ pub struct ReconnectEntry { } -pub struct GenericCloud { +pub struct GenericCloud { config: Config, magic: HeaderMagic, node_id: NodeId, @@ -214,7 +214,7 @@ pub struct GenericCloud { table: T, socket4: S, socket6: S, - device: Device, + device: D, crypto: Crypto, next_peerlist: Time, update_freq: Duration, @@ -229,8 +229,8 @@ pub struct GenericCloud { _dummy_ts: PhantomData } -impl GenericCloud { - pub fn new(config: &Config, device: Device, table: T, +impl GenericCloud { + pub fn new(config: &Config, device: D, table: T, learning: bool, broadcast: bool, addresses: Vec, crypto: Crypto, port_forwarding: Option ) -> Self { @@ -712,6 +712,10 @@ impl GenericCloud } } + fn decode_message<'a>(&self, msg: &'a mut [u8]) -> Result, Error> { + decode(msg, self.magic, &self.crypto) + } + fn handle_socket_data(&mut self, src: SocketAddr, data: &mut [u8]) { let size = data.len(); if let Err(e) = decode(data, self.magic, &mut self.crypto).and_then(|msg| { @@ -782,3 +786,44 @@ impl GenericCloud self.broadcast_msg(&mut Message::Close).ok(); } } + + + +#[cfg(test)] use super::ethernet::{self, SwitchTable}; +#[cfg(test)] use super::util::MockTimeSource; +#[cfg(test)] use super::net::MockSocket; +#[cfg(test)] use super::device::MockDevice; + +#[cfg(test)] +impl GenericCloud { + fn is_empty(&self) -> bool { + self.device.is_empty() && self.socket4.is_empty() && self.socket6.is_empty() + } +} + +#[cfg(test)] +type TestNode = GenericCloud, MockSocket, MockTimeSource>; + +#[cfg(test)] +fn create_node() -> TestNode { + TestNode::new( + &Config::default(), + MockDevice::new(), + SwitchTable::new(1800, 10), + true, true, vec![], Crypto::None, None + ) +} + +#[test] +fn connect() { + let mut node = create_node(); + assert!(node.is_empty()); + node.connect("1.2.3.4:5678").unwrap(); + assert!(node.device.is_empty()); + assert!(node.socket6.is_empty()); + let (addr, mut message) = node.socket4.pop_outbound().unwrap(); + assert_eq!("1.2.3.4:5678".to_socket_addrs().unwrap().next().unwrap(), addr); + let message = node.decode_message(&mut message).unwrap(); + assert_eq!(Message::Init(0, node.node_id, vec![]), message); + +} \ No newline at end of file diff --git a/src/device.rs b/src/device.rs index ad3726a..4537e94 100644 --- a/src/device.rs +++ b/src/device.rs @@ -3,9 +3,10 @@ // This software is licensed under GPL-3 or newer (see LICENSE.md) use std::os::unix::io::{AsRawFd, RawFd}; -use std::io::{self, Error as IoError, Read, Write}; +use std::io::{self, Error as IoError, ErrorKind, Read, Write}; use std::fs; use std::fmt; +use std::collections::VecDeque; use super::types::Error; @@ -40,15 +41,48 @@ impl fmt::Display for Type { } +pub trait Device: AsRawFd { + /// Returns the type of this device + fn get_type(&self) -> Type; + + /// Returns the interface name of this device. + fn ifname(&self) -> &str; + + /// 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 starting position and the amount of bytes read into + /// the buffer. + /// + /// # Errors + /// This method will return an error if the underlying read call fails. + fn read(&mut self, buffer: &mut [u8]) -> Result<(usize, usize), 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 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. + fn write(&mut self, data: &mut [u8], start: usize) -> Result<(), Error>; +} + + /// Represents a tun/tap device -pub struct Device { +pub struct TunTapDevice { fd: fs::File, ifname: String, type_: Type, } -impl Device { +impl TunTapDevice { /// Creates a new tun/tap device /// /// This method creates a new device of the `type_` kind with the name `ifname`. @@ -91,7 +125,7 @@ impl Device { while ifname_c.last() == Some(&0) { ifname_c.pop(); } - Ok(Device{fd, ifname: String::from_utf8(ifname_c).unwrap(), type_}) + Ok(Self{fd, ifname: String::from_utf8(ifname_c).unwrap(), type_}) }, _ => Err(IoError::last_os_error()) } @@ -106,19 +140,6 @@ impl Device { } } - /// 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 @@ -134,31 +155,13 @@ impl Device { /// 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) -> io::Result { - Ok(Device{ + Ok(TunTapDevice{ fd: try!(fs::OpenOptions::new().create(true).read(true).write(true).open(path)), ifname: ifname.to_string(), 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 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<(usize, usize), Error> { - let read = try!(self.fd.read(&mut buffer).map_err(|e| Error::TunTapDev("Read error", e))); - 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) { @@ -179,24 +182,6 @@ impl Device { } } - /// 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 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, 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(|e| Error::TunTapDev("Flush error", e)), - Err(e) => Err(Error::TunTapDev("Write error", e)) - } - } - #[cfg(any(target_os = "linux", target_os = "android"))] #[inline] fn correct_data_before_write(&mut self, _buffer: &mut [u8], start: usize) -> usize { @@ -223,9 +208,89 @@ impl Device { } } -impl AsRawFd for Device { +impl Device for TunTapDevice { + fn get_type(&self) -> Type { + self.type_ + } + + fn ifname(&self) -> &str { + &self.ifname + } + + fn read(&mut self, mut buffer: &mut [u8]) -> Result<(usize, usize), Error> { + let read = try!(self.fd.read(&mut buffer).map_err(|e| Error::TunTapDev("Read error", e))); + let (start, read) = self.correct_data_after_read(&mut buffer, 0, read); + Ok((start, read)) + } + + 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(|e| Error::TunTapDev("Flush error", e)), + Err(e) => Err(Error::TunTapDev("Write error", e)) + } + } + +} + +impl AsRawFd for TunTapDevice { #[inline] fn as_raw_fd(&self) -> RawFd { self.fd.as_raw_fd() } } + + +pub struct MockDevice { + inbound: VecDeque>, + outbound: VecDeque> +} + +impl MockDevice { + pub fn new() -> Self { + Self { outbound: VecDeque::new(), inbound: VecDeque::new() } + } + + pub fn put_inbound(&mut self, data: Vec) { + self.inbound.push_back(data) + } + + pub fn pop_outbound(&mut self) -> Option> { + self.outbound.pop_front() + } + + pub fn is_empty(&self) -> bool { + self.inbound.is_empty() && self.outbound.is_empty() + } +} + +impl Device for MockDevice { + fn get_type(&self) -> Type { + Type::Dummy + } + + fn ifname(&self) -> &str { + unimplemented!() + } + + fn read(&mut self, buffer: &mut [u8]) -> Result<(usize, usize), Error> { + if let Some(data) = self.inbound.pop_front() { + buffer[0..data.len()].copy_from_slice(&data); + Ok((0, data.len())) + } else { + Err(Error::TunTapDev("empty", io::Error::from(ErrorKind::UnexpectedEof))) + } + } + + fn write(&mut self, data: &mut [u8], start: usize) -> Result<(), Error> { + self.outbound.push_back(data[start..].to_owned()); + Ok(()) + } +} + +impl AsRawFd for MockDevice { + #[inline] + fn as_raw_fd(&self) -> RawFd { + unimplemented!() + } +} diff --git a/src/main.rs b/src/main.rs index 3208a38..fd81265 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,7 +50,7 @@ use std::path::Path; use std::io::{self, Write}; use std::net::UdpSocket; -use device::{Device, Type}; +use device::{TunTapDevice, Device, Type}; use ethernet::SwitchTable; use ip::RoutingTable; use types::{Mode, Range, Protocol, HeaderMagic, Error}; @@ -161,20 +161,20 @@ enum AnyTable { } enum AnyCloud { - Switch(GenericCloud, UdpSocket, SystemTimeSource>), - Routing(GenericCloud) + Switch(GenericCloud, UdpSocket, SystemTimeSource>), + Routing(GenericCloud) } impl AnyCloud

{ #[allow(unknown_lints, clippy::too_many_arguments)] - fn new(config: &Config, device: Device, table: AnyTable, + fn new(config: &Config, device: TunTapDevice, table: AnyTable, learning: bool, broadcast: bool, addresses: Vec, crypto: Crypto, port_forwarding: Option) -> Self { match table { - AnyTable::Switch(t) => AnyCloud::Switch(GenericCloud::, UdpSocket, SystemTimeSource>::new( + AnyTable::Switch(t) => AnyCloud::Switch(GenericCloud::, UdpSocket, SystemTimeSource>::new( config, device,t, learning, broadcast, addresses, crypto, port_forwarding )), - AnyTable::Routing(t) => AnyCloud::Routing(GenericCloud::::new( + AnyTable::Routing(t) => AnyCloud::Routing(GenericCloud::::new( config, device,t, learning, broadcast, addresses, crypto, port_forwarding )) } @@ -211,7 +211,7 @@ impl AnyCloud

{ fn run (config: Config) { - let device = try_fail!(Device::new(&config.device_name, config.device_type, config.device_path.as_ref().map(|s| s as &str)), + let device = try_fail!(TunTapDevice::new(&config.device_name, config.device_type, config.device_path.as_ref().map(|s| s as &str)), "Failed to open virtual {} interface {}: {}", config.device_type, config.device_name); info!("Opened device {}", device.ifname()); let mut ranges = Vec::with_capacity(config.subnets.len()); diff --git a/src/net.rs b/src/net.rs index 4fd2d34..936a9e3 100644 --- a/src/net.rs +++ b/src/net.rs @@ -54,6 +54,10 @@ impl MockSocket { pub fn pop_outbound(&mut self) -> Option<(SocketAddr, Vec)> { self.outbound.pop_front() } + + pub fn is_empty(&self) -> bool { + self.inbound.is_empty() && self.outbound.is_empty() + } } impl AsRawFd for MockSocket { diff --git a/src/poll/epoll.rs b/src/poll/epoll.rs index db0acda..226b14c 100644 --- a/src/poll/epoll.rs +++ b/src/poll/epoll.rs @@ -7,7 +7,6 @@ use libc; use std::os::unix::io::RawFd; use std::io; use device::Device; -use std::os::unix::io::AsRawFd; use super::WaitResult; use ::device::Type; diff --git a/src/udpmessage.rs b/src/udpmessage.rs index 7cb9e6c..d154dce 100644 --- a/src/udpmessage.rs +++ b/src/udpmessage.rs @@ -78,7 +78,7 @@ impl<'a> fmt::Debug for Message<'a> { } #[allow(unknown_lints,clippy::needless_range_loop)] -pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &mut Crypto) -> Result, Error> { +pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &Crypto) -> Result, Error> { let mut end = data.len(); let (header, mut pos) = try!(TopHeader::read_from(&data[..end])); if header.magic != magic {