More tests

pull/29/head
Dennis Schwerdel 2019-02-26 18:36:54 +01:00
parent c750947ab0
commit 5be254db4a
10 changed files with 394 additions and 53 deletions

View File

@ -40,7 +40,7 @@ struct PeerData {
alt_addrs: Vec<SocketAddr>,
}
struct PeerList<TS: TimeSource> {
pub struct PeerList<TS: TimeSource> {
timeout: Duration,
peers: HashMap<SocketAddr, PeerData, Hash>,
nodes: HashMap<NodeId, SocketAddr, Hash>,
@ -81,12 +81,12 @@ impl<TS: TimeSource> PeerList<TS> {
}
#[inline]
fn contains_addr(&self, addr: &SocketAddr) -> bool {
pub fn contains_addr(&self, addr: &SocketAddr) -> bool {
self.addresses.contains_key(addr)
}
#[inline]
fn is_connected<Addr: ToSocketAddrs+fmt::Debug>(&self, addr: Addr) -> Result<bool, Error> {
pub fn is_connected<Addr: ToSocketAddrs+fmt::Debug>(&self, addr: Addr) -> Result<bool, Error> {
for addr in try!(resolve(&addr)) {
if self.contains_addr(&addr) {
return Ok(true);
@ -96,7 +96,7 @@ impl<TS: TimeSource> PeerList<TS> {
}
#[inline]
fn contains_node(&self, node_id: &NodeId) -> bool {
pub fn contains_node(&self, node_id: &NodeId) -> bool {
self.nodes.contains_key(node_id)
}
@ -142,23 +142,23 @@ impl<TS: TimeSource> PeerList<TS> {
}
#[inline]
fn get_node_id(&self, addr: &SocketAddr) -> Option<NodeId> {
pub fn get_node_id(&self, addr: &SocketAddr) -> Option<NodeId> {
self.addresses.get(addr).map(|n| *n)
}
#[inline]
fn as_vec(&self) -> Vec<SocketAddr> {
pub fn as_vec(&self) -> Vec<SocketAddr> {
self.addresses.keys().cloned().collect()
}
#[inline]
fn len(&self) -> usize {
pub fn len(&self) -> usize {
self.peers.len()
}
#[inline]
#[allow(dead_code)]
fn is_empty(&self) -> bool {
pub fn is_empty(&self) -> bool {
self.peers.is_empty()
}
@ -687,12 +687,13 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
}
// Reply with stage=1 if stage is 0
if stage == 0 {
let peers = self.peers.as_vec();
let own_addrs = self.addresses.clone();
let own_node_id = self.node_id;
try!(self.send_msg(peer, &mut Message::Init(stage+1, own_node_id, own_addrs)));
try!(self.send_msg(peer, &mut Message::Peers(peers)));
}
// Send peers in any case
let peers = self.peers.as_vec();
try!(self.send_msg(peer, &mut Message::Peers(peers)));
},
Message::Close => {
self.peers.remove(&peer);
@ -712,10 +713,6 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
}
}
fn decode_message<'a>(&self, msg: &'a mut [u8]) -> Result<Message<'a>, 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| {
@ -788,42 +785,52 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
}
#[cfg(test)] use super::ethernet::{self, SwitchTable};
#[cfg(test)] use super::util::MockTimeSource;
#[cfg(test)] use super::net::MockSocket;
#[cfg(test)] use super::util::MockTimeSource;
#[cfg(test)] use super::device::MockDevice;
#[cfg(test)]
impl<P: Protocol, T: Table, TS: TimeSource> GenericCloud<MockDevice, P, T, MockSocket, TS> {
fn is_empty(&self) -> bool {
self.device.is_empty() && self.socket4.is_empty() && self.socket6.is_empty()
impl<P: Protocol, T: Table> GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource> {
pub fn socket4(&mut self) -> &mut MockSocket {
&mut self.socket4
}
pub fn socket6(&mut self) -> &mut MockSocket {
&mut self.socket6
}
pub fn device(&mut self) -> &mut MockDevice {
&mut self.device
}
pub fn trigger_socket_v4_event(&mut self) {
let mut buffer = [0; 64*1024];
self.handle_socket_v4_event(&mut buffer);
}
pub fn trigger_socket_v6_event(&mut self) {
let mut buffer = [0; 64*1024];
self.handle_socket_v6_event(&mut buffer);
}
pub fn trigger_device_event(&mut self) {
let mut buffer = [0; 64*1024];
self.handle_device_event(&mut buffer);
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn peers(&self) -> &PeerList<MockTimeSource> {
&self.peers
}
pub fn own_addresses(&self) -> &[SocketAddr] {
&self.own_addresses
}
pub fn decode_message<'a>(&self, msg: &'a mut [u8]) -> Result<Message<'a>, Error> {
decode(msg, self.magic, &self.crypto)
}
}
#[cfg(test)]
type TestNode = GenericCloud<MockDevice, ethernet::Frame, SwitchTable<MockTimeSource>, 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);
}

View File

@ -259,8 +259,8 @@ impl MockDevice {
self.outbound.pop_front()
}
pub fn is_empty(&self) -> bool {
self.inbound.is_empty() && self.outbound.is_empty()
pub fn has_inbound(&self) -> bool {
!self.inbound.is_empty()
}
}

View File

@ -25,6 +25,7 @@ extern crate base_62;
#[cfg(feature = "bench")] extern crate test;
#[macro_use] pub mod util;
#[cfg(test)] #[macro_use] mod tests;
pub mod types;
pub mod crypto;
pub mod udpmessage;

View File

@ -54,10 +54,6 @@ impl MockSocket {
pub fn pop_outbound(&mut self) -> Option<(SocketAddr, Vec<u8>)> {
self.outbound.pop_front()
}
pub fn is_empty(&self) -> bool {
self.inbound.is_empty() && self.outbound.is_empty()
}
}
impl AsRawFd for MockSocket {

94
src/tests/connect.rs Normal file
View File

@ -0,0 +1,94 @@
// VpnCloud - Peer-to-Peer VPN
// Copyright (C) 2015-2019 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
use super::*;
#[test]
fn connect_v4() {
let mut node1 = create_tap_node();
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node();
let node2_addr = addr!("2.3.4.5:6789");
assert_clean!(node1, node2);
assert!(!node1.peers().contains_node(&node2.node_id()));
assert!(!node2.peers().contains_node(&node1.node_id()));
node1.connect("2.3.4.5:6789").unwrap();
// Node 1 -> Node 2: Init 0
assert_message4!(node1, node1_addr, node2, node2_addr, Message::Init(0, node1.node_id(), vec![]));
assert_clean!(node1);
assert!(node2.peers().contains_node(&node1.node_id()));
// Node 2 -> Node 1: Init 1 | Node 2 -> Node 1: Peers
assert_message4!(node2, node2_addr, node1, node1_addr, Message::Init(1, node2.node_id(), vec![]));
assert!(node1.peers().contains_node(&node2.node_id()));
assert_message4!(node2, node2_addr, node1, node1_addr, Message::Peers(vec![node1_addr]));
assert_clean!(node2);
// Node 1 -> Node 2: Peers | Node 1 -> Node 1: Init 0
assert_message4!(node1, node1_addr, node2, node2_addr, Message::Peers(vec![node2_addr]));
assert_message4!(node1, node1_addr, node1, node1_addr, Message::Init(0, node1.node_id(), vec![]));
assert!(node1.own_addresses().contains(&node1_addr));
assert_clean!(node1);
// Node 2 -> Node 2: Init 0
assert_message4!(node2, node2_addr, node2, node2_addr, Message::Init(0, node2.node_id(), vec![]));
assert_clean!(node2);
assert!(node2.own_addresses().contains(&node2_addr));
assert_connected!(node1, node2);
}
#[test]
fn connect_v6() {
let mut node1 = create_tap_node();
let node1_addr = addr!("[::1]:5678");
let mut node2 = create_tap_node();
let node2_addr = addr!("[::2]:6789");
node1.connect("[::2]:6789").unwrap();
simulate!(node1 => node1_addr, node2 => node2_addr);
assert_connected!(node1, node2);
}
#[test]
fn cross_connect() {
let mut node1 = create_tap_node();
let node1_addr = addr!("1.1.1.1:1111");
let mut node2 = create_tap_node();
let node2_addr = addr!("2.2.2.2:2222");
let mut node3 = create_tap_node();
let node3_addr = addr!("3.3.3.3:3333");
let mut node4 = create_tap_node();
let node4_addr = addr!("4.4.4.4:4444");
node1.connect("2.2.2.2:2222").unwrap();
node3.connect("4.4.4.4:4444").unwrap();
simulate!(node1 => node1_addr, node2 => node2_addr, node3 => node3_addr, node4 => node4_addr);
assert_connected!(node1, node2);
assert_connected!(node3, node4);
node1.connect("3.3.3.3:3333").unwrap();
simulate!(node1 => node1_addr, node2 => node2_addr, node3 => node3_addr, node4 => node4_addr);
// existing connections
assert_connected!(node1, node2);
assert_connected!(node3, node4);
// new connection
assert_connected!(node1, node3);
// transient connections 1st degree
assert_connected!(node1, node4);
assert_connected!(node3, node2);
// transient connections 2nd degree
assert_connected!(node1, node4);
}

52
src/tests/helper.rs Normal file
View File

@ -0,0 +1,52 @@
macro_rules! assert_clean {
($($node: expr),*) => {
$(
assert_eq!($node.socket4().pop_outbound().map(|(addr, mut msg)| (addr, $node.decode_message(&mut msg).unwrap().without_data())), None);
assert_eq!($node.socket6().pop_outbound().map(|(addr, mut msg)| (addr, $node.decode_message(&mut msg).unwrap().without_data())), None);
assert_eq!($node.device().pop_outbound(), None);
)*
};
}
macro_rules! assert_message4 {
($from: expr, $from_addr: expr, $to: expr, $to_addr: expr, $message: expr) => {
let (addr, mut data) = msg4_get(&mut $from);
assert_eq!($to_addr, addr);
{
let message = $to.decode_message(&mut data).unwrap();
assert_eq!($message, message.without_data());
}
msg4_put(&mut $to, $from_addr, data);
};
}
macro_rules! assert_message6 {
($from: expr, $from_addr: expr, $to: expr, $to_addr: expr, $message: expr) => {
let (addr, mut data) = msg6_get(&mut $from);
assert_eq!($to_addr, addr);
{
let message = $to.decode_message(&mut data).unwrap();
assert_eq!($message, message.without_data());
}
msg6_put(&mut $to, $from_addr, data);
};
}
macro_rules! simulate {
($($node: expr => $addr: expr),*) => {
simulate(&mut [$((&mut $node, $addr)),*]);
};
}
macro_rules! assert_connected {
($($node:expr),*) => {
for node1 in [$(&$node),*].iter() {
for node2 in [$(&$node),*].iter() {
if node1.node_id() == node2.node_id() {
continue
}
assert!(node1.peers().contains_node(&node2.node_id()));
}
}
};
}

110
src/tests/mod.rs Normal file
View File

@ -0,0 +1,110 @@
// VpnCloud - Peer-to-Peer VPN
// Copyright (C) 2015-2019 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
#[macro_use] mod helper;
mod connect;
mod payload;
pub use std::net::SocketAddr;
pub use super::ethernet::{self, SwitchTable};
pub use super::util::MockTimeSource;
pub use super::net::MockSocket;
pub use super::device::MockDevice;
pub use super::udpmessage::Message;
pub use super::config::Config;
pub use super::crypto::Crypto;
pub use super::cloud::GenericCloud;
pub use super::types::{Protocol, Table, Range};
pub use super::ip::{self, RoutingTable};
type TestNode<P, T> = GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource>;
type TapTestNode = TestNode<ethernet::Frame, SwitchTable<MockTimeSource>>;
type TunTestNode = TestNode<ip::Packet, RoutingTable>;
fn create_tap_node() -> TapTestNode {
TestNode::new(
&Config::default(),
MockDevice::new(),
SwitchTable::new(1800, 10),
true, true, vec![], Crypto::None, None
)
}
fn create_tun_node(addresses: Vec<Range>) -> TunTestNode {
TestNode::new(
&Config::default(),
MockDevice::new(),
RoutingTable::new(),
false, false, addresses, Crypto::None, None
)
}
fn msg4_get<P: Protocol, T: Table>(node: &mut TestNode<P, T>) -> (SocketAddr, Vec<u8>) {
let msg = node.socket4().pop_outbound();
assert!(msg.is_some());
msg.unwrap()
}
fn msg6_get<P: Protocol, T: Table>(node: &mut TestNode<P, T>) -> (SocketAddr, Vec<u8>) {
let msg = node.socket6().pop_outbound();
assert!(msg.is_some());
msg.unwrap()
}
fn msg4_put<P: Protocol, T: Table>(node: &mut TestNode<P, T>, from: SocketAddr, msg: Vec<u8>) {
node.socket4().put_inbound(from, msg);
node.trigger_socket_v4_event();
}
fn msg6_put<P: Protocol, T: Table>(node: &mut TestNode<P, T>, from: SocketAddr, msg: Vec<u8>) {
node.socket6().put_inbound(from, msg);
node.trigger_socket_v6_event();
}
fn simulate<P: Protocol, T: Table>(nodes: &mut [(&mut TestNode<P, T>, SocketAddr)]) {
for (ref mut node, ref from_addr) in nodes.iter_mut() {
while node.device().has_inbound() {
node.trigger_device_event();
}
}
let mut clean = false;
while !clean {
clean = true;
let mut msgs = Vec::new();
for (ref mut node, ref from_addr) in nodes.iter_mut() {
while let Some((to_addr, msg)) = node.socket4().pop_outbound() {
msgs.push((msg, *from_addr, to_addr));
}
}
clean &= msgs.is_empty();
for (msg, from_addr, to_addr) in msgs {
for (ref mut node, ref addr) in nodes.iter_mut() {
if *addr == to_addr {
msg4_put(node, from_addr, msg);
break
}
}
}
let mut msgs = Vec::new();
for (ref mut node, ref from_addr) in nodes.iter_mut() {
while let Some((to_addr, msg)) = node.socket6().pop_outbound() {
msgs.push((msg, *from_addr, to_addr));
}
}
clean &= msgs.is_empty();
for (msg, from_addr, to_addr) in msgs {
for (ref mut node, ref addr) in nodes.iter_mut() {
if *addr == to_addr {
msg6_put(node, from_addr, msg);
break
}
}
}
}
}

62
src/tests/payload.rs Normal file
View File

@ -0,0 +1,62 @@
// VpnCloud - Peer-to-Peer VPN
// Copyright (C) 2015-2019 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
use super::*;
#[test]
fn ethernet_send() {
let mut node1 = create_tap_node();
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node();
let node2_addr = addr!("2.3.4.5:6789");
node1.connect("2.3.4.5:6789").unwrap();
simulate!(node1 => node1_addr, node2 => node2_addr);
assert_connected!(node1, node2);
let payload = vec![2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5];
node1.device().put_inbound(payload.clone());
simulate!(node1 => node1_addr, node2 => node2_addr);
assert_eq!(Some(payload), node2.device().pop_outbound());
assert_clean!(node1, node2);
}
#[test]
fn learning_switch() {
let mut node1 = create_tap_node();
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node();
let node2_addr = addr!("2.3.4.5:6789");
let mut node3 = create_tap_node();
let node3_addr = addr!("3.4.5.6:7890");
node1.connect("2.3.4.5:6789").unwrap();
node1.connect("3.4.5.6:7890").unwrap();
simulate!(node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
assert_connected!(node1, node2, node3);
let payload = vec![2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5];
node1.device().put_inbound(payload.clone());
simulate!(node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
assert_eq!(Some(&payload), node2.device().pop_outbound().as_ref());
assert_eq!(Some(&payload), node3.device().pop_outbound().as_ref());
let payload = vec![1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 5, 4, 3, 2, 1];
node2.device().put_inbound(payload.clone());
simulate!(node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
assert_eq!(Some(&payload), node1.device().pop_outbound().as_ref());
assert_clean!(node3);
assert_clean!(node1, node2, node3);
}

View File

@ -55,6 +55,17 @@ pub enum Message<'a> {
Close,
}
impl<'a> Message<'a> {
pub fn without_data(self) -> Message<'static> {
match self {
Message::Data(_, start, end) => Message::Data(&mut [], start, end),
Message::Peers(peers) => Message::Peers(peers),
Message::Init(step, node_id, ranges) => Message::Init(step, node_id, ranges),
Message::Close => Message::Close
}
}
}
impl<'a> fmt::Debug for Message<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {

View File

@ -129,6 +129,14 @@ pub fn resolve<Addr: ToSocketAddrs+fmt::Debug>(addr: Addr) -> Result<Vec<SocketA
Ok(addrs)
}
macro_rules! addr {
($addr: expr) => {
{
std::net::ToSocketAddrs::to_socket_addrs($addr).unwrap().next().unwrap()
}
};
}
pub struct Bytes(pub u64);