mirror of https://github.com/dswd/vpncloud.git
Tests for NAT
This commit is contained in:
parent
9cd7e53880
commit
63dc1e3ce1
|
@ -256,7 +256,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
|||
Err(err) => fail!("Failed to open ipv6 address ::{}: {}", config.port, err)
|
||||
};
|
||||
let now = TS::now();
|
||||
let peer_timeout_publish = if S::detect_nat() {
|
||||
let peer_timeout_publish = if socket4.detect_nat() {
|
||||
info!("Private IP detected, setting published peer timeout to 300s");
|
||||
300
|
||||
} else {
|
||||
|
|
56
src/net.rs
56
src/net.rs
|
@ -1,11 +1,12 @@
|
|||
use std::{
|
||||
collections::VecDeque,
|
||||
collections::{HashMap, VecDeque},
|
||||
io::{self, ErrorKind},
|
||||
net::{SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket},
|
||||
os::unix::io::{AsRawFd, RawFd}
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
sync::atomic::{AtomicBool, Ordering}
|
||||
};
|
||||
|
||||
use super::util::get_internal_ip;
|
||||
use super::util::{get_internal_ip, MockTimeSource, Time, TimeSource};
|
||||
|
||||
use net2::UdpBuilder;
|
||||
|
||||
|
@ -16,7 +17,7 @@ pub trait Socket: AsRawFd + Sized {
|
|||
fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error>;
|
||||
fn send(&mut self, data: &[u8], addr: SocketAddr) -> Result<usize, io::Error>;
|
||||
fn address(&self) -> Result<SocketAddr, io::Error>;
|
||||
fn detect_nat() -> bool;
|
||||
fn detect_nat(&self) -> bool;
|
||||
}
|
||||
|
||||
impl Socket for UdpSocket {
|
||||
|
@ -45,13 +46,18 @@ impl Socket for UdpSocket {
|
|||
fn address(&self) -> Result<SocketAddr, io::Error> {
|
||||
self.local_addr()
|
||||
}
|
||||
fn detect_nat() -> bool {
|
||||
fn detect_nat(&self) -> bool {
|
||||
get_internal_ip().is_private()
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static MOCK_SOCKET_NAT: AtomicBool = AtomicBool::new(false);
|
||||
}
|
||||
|
||||
pub struct MockSocket {
|
||||
nat: bool,
|
||||
nat_peers: HashMap<SocketAddr, Time>,
|
||||
address: SocketAddr,
|
||||
outbound: VecDeque<(SocketAddr, Vec<u8>)>,
|
||||
inbound: VecDeque<(SocketAddr, Vec<u8>)>
|
||||
|
@ -59,11 +65,36 @@ pub struct MockSocket {
|
|||
|
||||
impl MockSocket {
|
||||
pub fn new(address: SocketAddr) -> Self {
|
||||
Self { address, outbound: VecDeque::new(), inbound: VecDeque::new() }
|
||||
Self {
|
||||
nat: Self::get_nat(),
|
||||
nat_peers: HashMap::new(),
|
||||
address,
|
||||
outbound: VecDeque::new(),
|
||||
inbound: VecDeque::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_inbound(&mut self, from: SocketAddr, data: Vec<u8>) {
|
||||
self.inbound.push_back((from, data))
|
||||
pub fn set_nat(nat: bool) {
|
||||
MOCK_SOCKET_NAT.with(|t| t.store(nat, Ordering::SeqCst))
|
||||
}
|
||||
|
||||
pub fn get_nat() -> bool {
|
||||
MOCK_SOCKET_NAT.with(|t| t.load(Ordering::SeqCst))
|
||||
}
|
||||
|
||||
pub fn put_inbound(&mut self, from: SocketAddr, data: Vec<u8>) -> bool {
|
||||
if !self.nat {
|
||||
self.inbound.push_back((from, data));
|
||||
return true
|
||||
}
|
||||
if let Some(timeout) = self.nat_peers.get(&from) {
|
||||
if *timeout >= MockTimeSource::now() {
|
||||
self.inbound.push_back((from, data));
|
||||
return true
|
||||
}
|
||||
}
|
||||
warn!("Sender {:?} is filtered out by NAT", from);
|
||||
false
|
||||
}
|
||||
|
||||
pub fn pop_outbound(&mut self) -> Option<(SocketAddr, Vec<u8>)> {
|
||||
|
@ -91,17 +122,20 @@ impl Socket for MockSocket {
|
|||
buffer[0..data.len()].copy_from_slice(&data);
|
||||
Ok((data.len(), addr))
|
||||
} else {
|
||||
Err(io::Error::from(ErrorKind::UnexpectedEof))
|
||||
Err(io::Error::new(ErrorKind::Other, "nothing in queue"))
|
||||
}
|
||||
}
|
||||
fn send(&mut self, data: &[u8], addr: SocketAddr) -> Result<usize, io::Error> {
|
||||
self.outbound.push_back((addr, data.to_owned()));
|
||||
if self.nat {
|
||||
self.nat_peers.insert(addr, MockTimeSource::now() + 300);
|
||||
}
|
||||
Ok(data.len())
|
||||
}
|
||||
fn address(&self) -> Result<SocketAddr, io::Error> {
|
||||
Ok(self.address)
|
||||
}
|
||||
fn detect_nat() -> bool {
|
||||
false
|
||||
fn detect_nat(&self) -> bool {
|
||||
self.nat
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,20 @@ macro_rules! simulate {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! simulate_time {
|
||||
($time:expr, $($node: expr => $addr: expr),*) => {
|
||||
for _ in 0..$time {
|
||||
use crate::util::{MockTimeSource, TimeSource};
|
||||
MockTimeSource::set_time(MockTimeSource::now()+1);
|
||||
$(
|
||||
$node.trigger_housekeep();
|
||||
)*
|
||||
simulate(&mut [$((&mut $node, $addr)),*]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
macro_rules! assert_connected {
|
||||
($($node:expr),*) => {
|
||||
for node1 in [$(&$node),*].iter() {
|
||||
|
|
|
@ -4,11 +4,18 @@
|
|||
|
||||
#[macro_use]
|
||||
mod helper;
|
||||
mod nat;
|
||||
mod payload;
|
||||
mod peers;
|
||||
|
||||
pub use std::net::SocketAddr;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::{
|
||||
io::Write,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Once
|
||||
}
|
||||
};
|
||||
|
||||
pub use super::{
|
||||
cloud::GenericCloud,
|
||||
|
@ -24,6 +31,37 @@ pub use super::{
|
|||
};
|
||||
|
||||
|
||||
static INIT_LOGGER: Once = Once::new();
|
||||
|
||||
pub fn init_debug_logger() {
|
||||
INIT_LOGGER.call_once(|| {
|
||||
log::set_boxed_logger(Box::new(DebugLogger)).unwrap();
|
||||
log::set_max_level(log::LevelFilter::Debug);
|
||||
})
|
||||
}
|
||||
|
||||
struct DebugLogger;
|
||||
|
||||
impl log::Log for DebugLogger {
|
||||
#[inline]
|
||||
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn log(&self, record: &log::Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
eprintln!("{} - {}", record.level(), record.args());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&self) {
|
||||
std::io::stderr().flush().expect("Failed to flush")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type TestNode<P, T> = GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource>;
|
||||
|
||||
type TapTestNode = TestNode<ethernet::Frame, SwitchTable<MockTimeSource>>;
|
||||
|
@ -35,17 +73,19 @@ thread_local! {
|
|||
static NEXT_PORT: AtomicUsize = AtomicUsize::new(1);
|
||||
}
|
||||
|
||||
fn create_tap_node() -> TapTestNode {
|
||||
create_tap_node_with_config(Config::default())
|
||||
fn create_tap_node(nat: bool) -> TapTestNode {
|
||||
create_tap_node_with_config(nat, Config::default())
|
||||
}
|
||||
|
||||
fn create_tap_node_with_config(mut config: Config) -> TapTestNode {
|
||||
fn create_tap_node_with_config(nat: bool, mut config: Config) -> TapTestNode {
|
||||
MockSocket::set_nat(nat);
|
||||
config.port = NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16;
|
||||
TestNode::new(&config, MockDevice::new(), SwitchTable::new(1800, 10), true, true, vec![], Crypto::None, None)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn create_tun_node(addresses: Vec<Range>) -> TunTestNode {
|
||||
fn create_tun_node(nat: bool, addresses: Vec<Range>) -> TunTestNode {
|
||||
MockSocket::set_nat(nat);
|
||||
TestNode::new(
|
||||
&Config { port: NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16, ..Config::default() },
|
||||
MockDevice::new(),
|
||||
|
@ -73,13 +113,15 @@ fn msg6_get<P: Protocol, T: Table>(node: &mut TestNode<P, T>) -> (SocketAddr, Ve
|
|||
}
|
||||
|
||||
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();
|
||||
if 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();
|
||||
if node.socket6().put_inbound(from, msg) {
|
||||
node.trigger_socket_v6_event();
|
||||
}
|
||||
}
|
||||
|
||||
fn simulate<P: Protocol, T: Table>(nodes: &mut [(&mut TestNode<P, T>, SocketAddr)]) {
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
// 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_nat_2_peers() {
|
||||
init_debug_logger();
|
||||
MockTimeSource::set_time(0);
|
||||
let mut node1 = create_tap_node(true);
|
||||
let node1_addr = addr!("1.2.3.4:5678");
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("2.3.4.5:6789");
|
||||
|
||||
node2.connect("1.2.3.4:5678").unwrap();
|
||||
|
||||
simulate!(node1 => node1_addr, node2 => node2_addr);
|
||||
|
||||
assert!(!node1.peers().contains_node(&node2.node_id()));
|
||||
assert!(!node2.peers().contains_node(&node1.node_id()));
|
||||
|
||||
|
||||
node1.connect("2.3.4.5:6789").unwrap();
|
||||
|
||||
simulate!(node1 => node1_addr, node2 => node2_addr);
|
||||
|
||||
assert_connected!(node1, node2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn connect_nat_3_peers() {
|
||||
init_debug_logger();
|
||||
MockTimeSource::set_time(0);
|
||||
let mut node1 = create_tap_node(true);
|
||||
let node1_addr = addr!("1.2.3.4:5678");
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("2.3.4.5:6789");
|
||||
let mut node3 = create_tap_node(false);
|
||||
let node3_addr = addr!("3.4.5.6:7890");
|
||||
node2.connect("1.2.3.4:5678").unwrap();
|
||||
node3.connect("1.2.3.4:5678").unwrap();
|
||||
simulate!(node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
|
||||
|
||||
assert!(!node1.peers().contains_node(&node2.node_id()));
|
||||
assert!(!node2.peers().contains_node(&node1.node_id()));
|
||||
assert!(!node3.peers().contains_node(&node1.node_id()));
|
||||
assert!(!node3.peers().contains_node(&node2.node_id()));
|
||||
assert!(!node1.peers().contains_node(&node3.node_id()));
|
||||
assert!(!node2.peers().contains_node(&node3.node_id()));
|
||||
|
||||
node1.connect("3.4.5.6:7890").unwrap();
|
||||
node2.connect("3.4.5.6:7890").unwrap();
|
||||
|
||||
simulate_time!(1000, node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
|
||||
|
||||
assert_connected!(node1, node3);
|
||||
assert_connected!(node2, node3);
|
||||
assert_connected!(node1, node2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nat_keepalive() {
|
||||
init_debug_logger();
|
||||
MockTimeSource::set_time(0);
|
||||
let mut node1 = create_tap_node(true);
|
||||
let node1_addr = addr!("1.2.3.4:5678");
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("2.3.4.5:6789");
|
||||
let mut node3 = create_tap_node(false);
|
||||
let node3_addr = addr!("3.4.5.6:7890");
|
||||
node1.connect("3.4.5.6:7890").unwrap();
|
||||
node2.connect("3.4.5.6:7890").unwrap();
|
||||
|
||||
simulate_time!(1000, node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
|
||||
|
||||
assert_connected!(node1, node3);
|
||||
assert_connected!(node2, node3);
|
||||
assert_connected!(node1, node2);
|
||||
|
||||
simulate_time!(10000, node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
|
||||
|
||||
assert_connected!(node1, node3);
|
||||
assert_connected!(node2, node3);
|
||||
assert_connected!(node1, node2);
|
||||
}
|
|
@ -6,9 +6,9 @@ use super::*;
|
|||
|
||||
#[test]
|
||||
fn ethernet_delivers() {
|
||||
let mut node1 = create_tap_node();
|
||||
let mut node1 = create_tap_node(false);
|
||||
let node1_addr = addr!("1.2.3.4:5678");
|
||||
let mut node2 = create_tap_node();
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("2.3.4.5:6789");
|
||||
|
||||
node1.connect("2.3.4.5:6789").unwrap();
|
||||
|
@ -28,11 +28,11 @@ fn ethernet_delivers() {
|
|||
|
||||
#[test]
|
||||
fn switch_learns() {
|
||||
let mut node1 = create_tap_node();
|
||||
let mut node1 = create_tap_node(false);
|
||||
let node1_addr = addr!("1.2.3.4:5678");
|
||||
let mut node2 = create_tap_node();
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("2.3.4.5:6789");
|
||||
let mut node3 = create_tap_node();
|
||||
let mut node3 = create_tap_node(false);
|
||||
let node3_addr = addr!("3.4.5.6:7890");
|
||||
|
||||
node1.connect("2.3.4.5:6789").unwrap();
|
||||
|
|
|
@ -6,9 +6,9 @@ use super::*;
|
|||
|
||||
#[test]
|
||||
fn connect_v4() {
|
||||
let mut node1 = create_tap_node();
|
||||
let mut node1 = create_tap_node(false);
|
||||
let node1_addr = addr!("1.2.3.4:5678");
|
||||
let mut node2 = create_tap_node();
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("2.3.4.5:6789");
|
||||
assert_clean!(node1, node2);
|
||||
assert!(!node1.peers().contains_node(&node2.node_id()));
|
||||
|
@ -43,9 +43,9 @@ fn connect_v4() {
|
|||
|
||||
#[test]
|
||||
fn connect_v6() {
|
||||
let mut node1 = create_tap_node();
|
||||
let mut node1 = create_tap_node(false);
|
||||
let node1_addr = addr!("[::1]:5678");
|
||||
let mut node2 = create_tap_node();
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("[::2]:6789");
|
||||
|
||||
node1.connect("[::2]:6789").unwrap();
|
||||
|
@ -57,13 +57,13 @@ fn connect_v6() {
|
|||
|
||||
#[test]
|
||||
fn cross_connect() {
|
||||
let mut node1 = create_tap_node();
|
||||
let mut node1 = create_tap_node(false);
|
||||
let node1_addr = addr!("1.1.1.1:1111");
|
||||
let mut node2 = create_tap_node();
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("2.2.2.2:2222");
|
||||
let mut node3 = create_tap_node();
|
||||
let mut node3 = create_tap_node(false);
|
||||
let node3_addr = addr!("3.3.3.3:3333");
|
||||
let mut node4 = create_tap_node();
|
||||
let mut node4 = create_tap_node(false);
|
||||
let node4_addr = addr!("4.4.4.4:4444");
|
||||
|
||||
node1.connect("2.2.2.2:2222").unwrap();
|
||||
|
@ -98,10 +98,10 @@ fn connect_via_beacons() {
|
|||
MockTimeSource::set_time(0);
|
||||
let beacon_path = "target/.vpncloud_test";
|
||||
let mut node1 =
|
||||
create_tap_node_with_config(Config { beacon_store: Some(beacon_path.to_string()), ..Config::default() });
|
||||
create_tap_node_with_config(false, Config { beacon_store: Some(beacon_path.to_string()), ..Config::default() });
|
||||
let node1_addr = node1.address().unwrap().0;
|
||||
let mut node2 =
|
||||
create_tap_node_with_config(Config { beacon_load: Some(beacon_path.to_string()), ..Config::default() });
|
||||
create_tap_node_with_config(false, Config { beacon_load: Some(beacon_path.to_string()), ..Config::default() });
|
||||
let node2_addr = addr!("2.2.2.2:2222");
|
||||
|
||||
assert!(!node1.peers().contains_node(&node2.node_id()));
|
||||
|
@ -122,9 +122,9 @@ fn connect_via_beacons() {
|
|||
#[test]
|
||||
fn reconnect_after_timeout() {
|
||||
MockTimeSource::set_time(0);
|
||||
let mut node1 = create_tap_node();
|
||||
let mut node1 = create_tap_node(false);
|
||||
let node1_addr = addr!("1.1.1.1:1111");
|
||||
let mut node2 = create_tap_node();
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("2.2.2.2:2222");
|
||||
|
||||
node1.add_reconnect_peer("2.2.2.2:2222".to_string());
|
||||
|
@ -148,9 +148,9 @@ fn reconnect_after_timeout() {
|
|||
|
||||
#[test]
|
||||
fn lost_init1() {
|
||||
let mut node1 = create_tap_node();
|
||||
let mut node1 = create_tap_node(false);
|
||||
let node1_addr = addr!("1.2.3.4:5678");
|
||||
let mut node2 = create_tap_node();
|
||||
let mut node2 = create_tap_node(false);
|
||||
let node2_addr = addr!("2.3.4.5:6789");
|
||||
|
||||
node1.connect("2.3.4.5:6789").unwrap();
|
||||
|
@ -170,10 +170,10 @@ fn lost_init1() {
|
|||
|
||||
#[test]
|
||||
fn wrong_magic() {
|
||||
let mut node1 = create_tap_node();
|
||||
let mut node1 = create_tap_node(false);
|
||||
let node1_addr = addr!("1.2.3.4:5678");
|
||||
let mut node2 =
|
||||
create_tap_node_with_config(Config { magic: Some("hash:different".to_string()), ..Config::default() });
|
||||
create_tap_node_with_config(false, Config { magic: Some("hash:different".to_string()), ..Config::default() });
|
||||
let node2_addr = addr!("2.3.4.5:6789");
|
||||
node1.connect("2.3.4.5:6789").unwrap();
|
||||
|
||||
|
|
|
@ -91,11 +91,13 @@ macro_rules! fail {
|
|||
($format:expr) => ( {
|
||||
use std::process;
|
||||
error!($format);
|
||||
log::logger().flush();
|
||||
process::exit(-1);
|
||||
} );
|
||||
($format:expr, $( $arg:expr ),+) => ( {
|
||||
use std::process;
|
||||
error!($format, $( $arg ),+ );
|
||||
log::logger().flush();
|
||||
process::exit(-1);
|
||||
} );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue