Lots of changes and release 0.2

This commit is contained in:
Dennis Schwerdel 2015-11-26 22:16:51 +01:00
parent 748f9363a6
commit 46011a03c6
14 changed files with 300 additions and 286 deletions

View File

@ -19,7 +19,6 @@ script:
travis-cargo build -- --features "crypto" && travis-cargo build -- --features "crypto" &&
travis-cargo test -- --features "crypto" && travis-cargo test -- --features "crypto" &&
travis-cargo bench -- --features "crypto" && travis-cargo bench -- --features "crypto" &&
travis-cargo --only stable doc -- --features "crypto"
' '
addons: addons:
apt: apt:
@ -28,7 +27,6 @@ addons:
- libelf-dev - libelf-dev
- libdw-dev - libdw-dev
after_success: after_success:
- travis-cargo --only stable doc-upload -- --features "crypto"
- travis-cargo coveralls --no-sudo -- --features "crypto" - travis-cargo coveralls --no-sudo -- --features "crypto"
notifications: notifications:
email: email:

View File

@ -1,6 +1,6 @@
[package] [package]
name = "vpncloud" name = "vpncloud"
version = "0.1.0" version = "0.2.0"
authors = ["Dennis Schwerdel <schwerdel@informatik.uni-kl.de>"] authors = ["Dennis Schwerdel <schwerdel@informatik.uni-kl.de>"]
build = "build.rs" build = "build.rs"

View File

@ -20,23 +20,27 @@ Some performance measurements can be found [here](performance.md).
### Current Status ### Current Status
This project is under heavy development and has not reached a stable state yet. This project is still under development and has yet to reach a stable state.
This is what currently works: However, the main functionality should work and you are invited to test it.
This is what works:
* Normal operation using TUN/TAP interfaces and different forwarding modes (Hub, Switch, Router) * Setting up tunnels between two networks via Ethernet (TAP) and IP (TUN)
* Encryption using *libsodium* * Connecting multiple networks with multiple forwarding behaviors (Hub, Switch, Router)
* Encrypted connections using *libsodium*
* Automatic peer-to-peer meshing
* NAT and (limited) firewall traversal using hole punching
* Automatic reconnecting when connections are lost
* Non-native forwarding modes, e.g. IP based learning switch and prefix routed Ethernet networks.
* High throughput and low additional latency (see [performance page](performance.md))
However there are some open issues: However there are some open issues:
* Encryption has not been thoroughly reviewed, use with care. * Encryption has not been thoroughly reviewed, use with care.
* The protocol can still change. * The software is not very well tested and the protocol can change.
* The software is not very well tested.
* The coverage score includes all unused methods from *libsodium*
* The software has not been optimized for speed.
Please feel free to help and contribute code. Please feel free to help and contribute code.
### Semantic Versioning ### Semantic Versioning
This project uses [semantic versioning](http://semver.org). Currently that means that everything can change between versions before 1.0 is finally released. This is especially true for the network protocol and even more for the crypto part of it. Expect them to change before 1.0. This project uses [semantic versioning](http://semver.org). Currently that means that everything can change between versions before 1.0 is finally released.

View File

@ -1,3 +1,9 @@
vpncloud-nocrypto (0.2.0) stable; urgency=medium
* More stable release
-- Dennis Schwerdel <schwerdel@informatik.uni-kl.de> Thu, 26 Nov 2015 17:41:40 +0100
vpncloud-nocrypto (0.1.0) stable; urgency=medium vpncloud-nocrypto (0.1.0) stable; urgency=medium
* Initial release * Initial release

View File

@ -1,3 +1,9 @@
vpncloud (0.2.0) stable; urgency=medium
* More stable release
-- Dennis Schwerdel <schwerdel@informatik.uni-kl.de> Thu, 26 Nov 2015 17:41:40 +0100
vpncloud (0.1.0) stable; urgency=medium vpncloud (0.1.0) stable; urgency=medium
* Initial release * Initial release

View File

@ -15,159 +15,71 @@ Receiver node:
* Realtek RTL8111/8168/8411 Gigabit Network * Realtek RTL8111/8168/8411 Gigabit Network
* Ubuntu 14.04 (Kernel 3.13.0-63-generic) * Ubuntu 14.04 (Kernel 3.13.0-63-generic)
VpnCloud version: `VpnCloud v0.1.0 (with crypto support, protocol version 1)` VpnCloud version: `VpnCloud v0.2.0 (with crypto support, protocol version 1)`
The sender runs the following command:
### Test 1: Unencrypted throughput
Node 1:
``` ```
$> ./vpncloud -t tap -l NODE1:3210 -c NODE2:3210 \ $> ./vpncloud -t tap -l SENDER:3210 -c RECEIVER:3210 \
--ifup 'ifconfig $IFNAME 10.2.1.1/24 mtu MTU up' &
```
Node 2:
```
$> ./vpncloud -t tap -l NODE2:3210 -c NODE1:3210 \
--ifup 'ifconfig $IFNAME 10.2.1.2/24 mtu MTU up' &
$> iperf -s &
$> top
```
First, the test is run **without VpnCloud**:
```
$> iperf -c NODE2 -t 60
```
and then **via VpnCloud**:
```
$> iperf -c 10.2.1.2 -t 60
```
**Results:**
* Throughput without VpnCloud: 926 Mbits/sec
* Throughput via VpnCloud (MTU=1400): 885 Mbits/sec
* CPU usage for VpnCloud (MTU=1400): ~85%/ ~95% of one core (sender, receiver)
* Throughput via VpnCloud (MTU=16384): 947 Mbits/sec
* CPU usage for VpnCloud (MTU=16384): ~40%/ ~45% of one core (sender, receiver)
### Test 2: Unencrypted ping
Node 1:
```
$> ./vpncloud -t tap -l NODE1:3210 -c NODE2:3210 \
--ifup 'ifconfig $IFNAME 10.2.1.1/24 mtu 1400 up' & --ifup 'ifconfig $IFNAME 10.2.1.1/24 mtu 1400 up' &
``` ```
Node 2: and the receiver runs:
``` ```
$> ./vpncloud -t tap -l NODE2:3210 -c NODE1:3210 \ $> ./vpncloud -t tap -l RECEIVER:3210 -c SENDER:3210 \
--ifup 'ifconfig $IFNAME 10.2.1.2/24 mtu 1400 up' & --ifup 'ifconfig $IFNAME 10.2.1.2/24 mtu 1400 up' &
```
Each test is first run without VpnCloud:
```
$> ping NODE2 -c 10000 -i 0.001 -s SIZE -U -q
```
and then with VpnCloud:
```
$> ping 10.2.1.2 -c 10000 -i 0.001 -s SIZE -U -q
```
For all the test, the best result out of 5 is selected.
SIZE: 100 bytes
* Without VpnCloud: Ø= 317 µs
* With VpnCloud: Ø= 433 µs
SIZE: 500 bytes
* Without VpnCloud: Ø= 330 µs
* With VpnCloud: Ø= 446 µs
SIZE: 1000 bytes
* Without VpnCloud: Ø= 356 µs
* With VpnCloud: Ø= 473 µs
### Test 3: Encrypted throughput
Node 1:
```
$> ./vpncloud -t tap -l NODE1:3210 -c NODE2:3210 \
--ifup 'ifconfig $IFNAME 10.2.1.1/24 mtu MTU up' --shared-key test &
```
Node 2:
```
$> ./vpncloud -t tap -l NODE2:3210 -c NODE1:3210 \
--ifup 'ifconfig $IFNAME 10.2.1.2/24 mtu MTU up' --shared-key test &
$> iperf -s & $> iperf -s &
$> top $> top
``` ```
First, the test is run **without VpnCloud**: For encrypted tests, `--shared-key test` is appended.
```
$> iperf -c NODE2 -t 60
```
and then **via VpnCloud**:
```
$> iperf -c 10.2.1.2 -t 60
```
**Results:**
* Throughput without VpnCloud: 926 Mbits/sec
* Throughput via VpnCloud (MTU=1400): 633 Mbits/sec
* CPU usage for VpnCloud (MTU=1400): 100% of one core on both sides
* Throughput via VpnCloud (MTU=16384): 918 Mbits/sec
* CPU usage for VpnCloud (MTU=16384): ~90% of one core on both sides
### Test 4: Encrypted ping ### Throughput
The throughput is measured with the following command:
Node 1:
``` ```
$> ./vpncloud -t tap -l NODE1:3210 -c NODE2:3210 \ $> iperf -c DST -t 60
--ifup 'ifconfig $IFNAME 10.2.1.1/24 mtu 1400 up' --shared-key test &
``` ```
Node 2: The test is run in 3 steps:
* Native throughput without VpnCloud (`DST` is the native address of the receiver)
* Throughput via VpnCloud (`DST` is `10.2.1.2`)
* Encrypted throughput via VpnCloud (`DST` is `10.2.1.2`)
| Throughput test | Bandwidth | CPU usage (one core) |
| -------------------- | ------------- | -------------------- |
| Without VpnCloud | 926 Mbits/sec | - |
| Unencrypted VpnCloud | 873 Mbits/sec | 80% / 95% |
| Encrypted VpnCloud | 635 Mbits/sec | 100% |
### Latency
The latency is measured with the following command:
``` ```
$> ./vpncloud -t tap -l NODE2:3210 -c NODE1:3210 \ $> ping DST -c 10000 -i 0.001 -s SIZE -U -q
--ifup 'ifconfig $IFNAME 10.2.1.2/24 mtu 1400 up' --shared-key test &
``` ```
Each test is first run without VpnCloud: For all the test, the best average RTT out of 5 runs is selected. The latency is
``` assumed to be half of the RTT.
$> ping NODE2 -c 10000 -i 0.001 -s SIZE -U -q
```
and then with VpnCloud:
```
$> ping 10.2.1.2 -c 10000 -i 0.001 -s SIZE -U -q
```
For all the test, the best result out of 5 is selected. | Payload size | 100 bytes | 500 bytes | 1000 bytes |
| -------------------- | --------- | --------- | ---------- |
SIZE: 100 bytes | Without VpnCloud | 158 µs | 165 µs | 178 µs |
* Without VpnCloud: Ø= 317 µs | Unencrypted VpnCloud | 210 µs | 216 µs | 237 µs |
* With VpnCloud: Ø= 454 µs | Difference | +52 µs | +51 µs | +59 µs |
| Encrypted VpnCloud | 225 µs | 252 µs | 262 µs |
SIZE: 500 bytes | Difference | +15 µs | +36 µs | +25 µs |
* Without VpnCloud: Ø= 330 µs
* With VpnCloud: Ø= 492 µs
SIZE: 1000 bytes
* Without VpnCloud: Ø= 356 µs
* With VpnCloud: Ø= 543 µs
### Conclusion ### Conclusion
* VpnCloud achieves about 885 MBit/s with default MTU settings. * VpnCloud achieves over 850 MBit/s with default MTU settings.
* In encrypted mode, VpnCloud reaches aboud 663 MBit/s with default MTU settings. * In encrypted mode, VpnCloud reaches over 600 MBit/s with default MTU settings.
* At increased MTU, VpnCloud is able to saturate a Gigabit link even when encrypting. * VpnCloud adds about 60µs to the latency.
* VpnCloud adds about 120µs to the round trip times, i.e. 60µs latency increase.
* Encryption adds an additional latency between 10µs and 35µs depending on the packet size. * Encryption adds an additional latency between 10µs and 35µs depending on the packet size.

View File

@ -194,22 +194,22 @@ impl<P: Protocol> GenericCloud<P> {
fn handle_interface_data(&mut self, payload: &[u8]) -> Result<(), Error> { fn handle_interface_data(&mut self, payload: &[u8]) -> Result<(), Error> {
let (src, dst) = try!(P::parse(payload)); let (src, dst) = try!(P::parse(payload));
debug!("Read data from interface: src: {:?}, dst: {:?}, {} bytes", src, dst, payload.len()); debug!("Read data from interface: src: {}, dst: {}, {} bytes", src, dst, payload.len());
match self.table.lookup(&dst) { match self.table.lookup(&dst) {
Some(addr) => { Some(addr) => {
debug!("Found destination for {:?} => {}", dst, addr); debug!("Found destination for {} => {}", dst, addr);
if self.peers.contains(&addr) { if self.peers.contains(&addr) {
try!(self.send_msg(addr, &Message::Data(payload))) try!(self.send_msg(addr, &Message::Data(payload)))
} else { } else {
warn!("Destination for {:?} not found in peers: {}", dst, addr); warn!("Destination for {} not found in peers: {}", dst, addr);
} }
}, },
None => { None => {
if !self.broadcast { if !self.broadcast {
debug!("No destination for {:?} found, dropping", dst); debug!("No destination for {} found, dropping", dst);
return Ok(()); return Ok(());
} }
debug!("No destination for {:?} found, broadcasting", dst); debug!("No destination for {} found, broadcasting", dst);
let msg = Message::Data(payload); let msg = Message::Data(payload);
for addr in &self.peers.as_vec() { for addr in &self.peers.as_vec() {
try!(self.send_msg(addr, &msg)); try!(self.send_msg(addr, &msg));
@ -234,7 +234,7 @@ impl<P: Protocol> GenericCloud<P> {
match self.device.write(&payload) { match self.device.write(&payload) {
Ok(()) => (), Ok(()) => (),
Err(e) => { Err(e) => {
error!("Failed to send via device {:?}", e); error!("Failed to send via device: {}", e);
return Err(Error::TunTapDevError("Failed to write to device")); return Err(Error::TunTapDevError("Failed to write to device"));
} }
} }
@ -292,14 +292,14 @@ impl<P: Protocol> GenericCloud<P> {
let (size, src) = try_fail!(self.socket.recv_from(&mut buffer), "Failed to read from network socket: {}"); let (size, src) = try_fail!(self.socket.recv_from(&mut buffer), "Failed to read from network socket: {}");
match decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) { match decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) {
Ok(_) => (), Ok(_) => (),
Err(e) => error!("Error: {:?}", e) Err(e) => error!("Error: {}", e)
} }
}, },
&1 => { &1 => {
let size = try_fail!(self.device.read(&mut buffer), "Failed to read from tap device: {}"); let size = try_fail!(self.device.read(&mut buffer), "Failed to read from tap device: {}");
match self.handle_interface_data(&buffer[..size]) { match self.handle_interface_data(&buffer[..size]) {
Ok(_) => (), Ok(_) => (),
Err(e) => error!("Error: {:?}", e) Err(e) => error!("Error: {}", e)
} }
}, },
_ => unreachable!() _ => unreachable!()
@ -313,7 +313,7 @@ impl<P: Protocol> GenericCloud<P> {
// Do the housekeeping // Do the housekeeping
match self.housekeep() { match self.housekeep() {
Ok(_) => (), Ok(_) => (),
Err(e) => error!("Error: {:?}", e) Err(e) => error!("Error: {}", e)
} }
self.next_housekeep = now() + 1 self.next_housekeep = now() + 1
} }

View File

@ -67,7 +67,7 @@ impl Table for SwitchTable {
} }
} }
for key in del { for key in del {
info!("Forgot address {:?}", key); info!("Forgot address {}", key);
self.table.remove(&key); self.table.remove(&key);
} }
self.cache = None; self.cache = None;
@ -77,7 +77,7 @@ impl Table for SwitchTable {
fn learn(&mut self, key: Address, _prefix_len: Option<u8>, addr: SocketAddr) { fn learn(&mut self, key: Address, _prefix_len: Option<u8>, addr: SocketAddr) {
let value = SwitchTableValue{address: addr, timeout: now()+self.timeout as Time}; let value = SwitchTableValue{address: addr, timeout: now()+self.timeout as Time};
if self.table.insert(key.clone(), value).is_none() { if self.table.insert(key.clone(), value).is_none() {
info!("Learned address {:?} => {}", key, addr); info!("Learned address {} => {}", key, addr);
} }
} }
@ -93,20 +93,3 @@ impl Table for SwitchTable {
unimplemented!() unimplemented!()
} }
} }
#[test]
fn without_vlan() {
let data = [6,5,4,3,2,1,1,2,3,4,5,6,1,2,3,4,5,6,7,8];
let (src, dst) = Frame::parse(&data).unwrap();
assert_eq!(src, Address{data: [1,2,3,4,5,6,0,0,0,0,0,0,0,0,0,0], len: 6});
assert_eq!(dst, Address{data: [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], len: 6});
}
#[test]
fn with_vlan() {
let data = [6,5,4,3,2,1,1,2,3,4,5,6,0x81,0,4,210,1,2,3,4,5,6,7,8];
let (src, dst) = Frame::parse(&data).unwrap();
assert_eq!(src, Address{data: [4,210,1,2,3,4,5,6,0,0,0,0,0,0,0,0], len: 8});
assert_eq!(dst, Address{data: [4,210,6,5,4,3,2,1,0,0,0,0,0,0,0,0], len: 8});
}

View File

@ -37,7 +37,7 @@ struct RoutingEntry {
prefix_len: u8 prefix_len: u8
} }
pub struct RoutingTable(HashMap<[u8; 16], Vec<RoutingEntry>>); pub struct RoutingTable(HashMap<Vec<u8>, Vec<RoutingEntry>>);
impl RoutingTable { impl RoutingTable {
pub fn new() -> Self { pub fn new() -> Self {
@ -51,12 +51,12 @@ impl Table for RoutingTable {
Some(val) => val, Some(val) => val,
None => addr.len * 8 None => addr.len * 8
}; };
info!("New routing entry: {:?}/{} => {}", addr, prefix_len, address); info!("New routing entry: {}/{} => {}", addr, prefix_len, address);
let group_len = (prefix_len as usize / 16) * 2; let group_len = prefix_len as usize / 8;
assert!(group_len <= 16); assert!(group_len <= 16);
let mut group_bytes = [0; 16]; let mut group_bytes = Vec::with_capacity(group_len);
for i in 0..group_len { for i in 0..group_len {
group_bytes[i] = addr.data[i]; group_bytes.push(addr.data[i]);
} }
let routing_entry = RoutingEntry{address: address, bytes: addr.data, prefix_len: prefix_len}; let routing_entry = RoutingEntry{address: address, bytes: addr.data, prefix_len: prefix_len};
match self.0.entry(group_bytes) { match self.0.entry(group_bytes) {
@ -66,9 +66,11 @@ impl Table for RoutingTable {
} }
fn lookup(&mut self, addr: &Address) -> Option<SocketAddr> { fn lookup(&mut self, addr: &Address) -> Option<SocketAddr> {
let len = addr.len as usize/2 * 2; let len = addr.len as usize;
for i in 0..(len/2)+1 { let mut found = None;
if let Some(group) = self.0.get(&addr.data[0..len-2*i]) { let mut found_len: isize = -1;
for i in 0..len+1 {
if let Some(group) = self.0.get(&addr.data[0..len-i]) {
for entry in group { for entry in group {
let mut match_len = 0; let mut match_len = 0;
for j in 0..addr.len as usize { for j in 0..addr.len as usize {
@ -80,13 +82,14 @@ impl Table for RoutingTable {
break; break;
} }
} }
if match_len as u8 >= entry.prefix_len { if match_len as u8 >= entry.prefix_len && match_len as isize > found_len {
return Some(entry.address); found = Some(entry.address);
found_len = match_len as isize;
} }
} }
} }
} }
None found
} }
fn housekeep(&mut self) { fn housekeep(&mut self) {

View File

@ -18,6 +18,7 @@ mod ethernet;
mod ip; mod ip;
mod cloud; mod cloud;
mod device; mod device;
#[cfg(test)] mod tests;
#[cfg(feature = "bench")] mod benches; #[cfg(feature = "bench")] mod benches;
use docopt::Docopt; use docopt::Docopt;

183
src/tests.rs Normal file
View File

@ -0,0 +1,183 @@
use std::net::{ToSocketAddrs, SocketAddr};
use std::str::FromStr;
use super::ethernet::{Frame, SwitchTable};
use super::ip::{RoutingTable, Packet};
use super::types::{Protocol, Address, Range, Table};
use super::udpmessage::{Options, Message, decode, encode};
use super::crypto::Crypto;
#[test]
fn encode_message_packet() {
let mut options = Options::default();
let mut crypto = Crypto::None;
let payload = [1,2,3,4,5];
let msg = Message::Data(&payload);
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 13);
assert_eq!(&buf[..8], &[118,112,110,1,0,0,0,0]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[cfg(feature = "crypto")]
#[test]
fn encode_message_encrypted() {
let mut options = Options::default();
let mut crypto = Crypto::from_shared_key("test");
let payload = [1,2,3,4,5];
let msg = Message::Data(&payload);
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 37);
assert_eq!(&buf[..8], &[118,112,110,1,1,0,0,0]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_message_peers() {
use std::str::FromStr;
let mut options = Options::default();
let mut crypto = Crypto::None;
let msg = Message::Peers(vec![SocketAddr::from_str("1.2.3.4:123").unwrap(), SocketAddr::from_str("5.6.7.8:12345").unwrap()]);
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 22);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,0,1,2,1,2,3,4,0,123,5,6,7,8,48,57,0]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_option_network_id() {
let mut options = Options::default();
options.network_id = Some(134);
let mut crypto = Crypto::None;
let msg = Message::Close;
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 16);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,1,3,0,0,0,0,0,0,0,134]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_message_init() {
use super::types::Address;
let mut options = Options::default();
let mut crypto = Crypto::None;
let addrs = vec![Range{base: Address{data: [0,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}, prefix_len: 24},
Range{base: Address{data: [0,1,2,3,4,5,0,0,0,0,0,0,0,0,0,0], len: 6}, prefix_len: 16}];
let msg = Message::Init(0, addrs);
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 24);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,0,2,0,2,4,0,1,2,3,24,6,0,1,2,3,4,5,16]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_message_close() {
let mut options = Options::default();
let mut crypto = Crypto::None;
let msg = Message::Close;
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 8);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,0,3]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[test]
fn decode_frame_without_vlan() {
let data = [6,5,4,3,2,1,1,2,3,4,5,6,1,2,3,4,5,6,7,8];
let (src, dst) = Frame::parse(&data).unwrap();
assert_eq!(src, Address{data: [1,2,3,4,5,6,0,0,0,0,0,0,0,0,0,0], len: 6});
assert_eq!(dst, Address{data: [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], len: 6});
}
#[test]
fn decode_frame_with_vlan() {
let data = [6,5,4,3,2,1,1,2,3,4,5,6,0x81,0,4,210,1,2,3,4,5,6,7,8];
let (src, dst) = Frame::parse(&data).unwrap();
assert_eq!(src, Address{data: [4,210,1,2,3,4,5,6,0,0,0,0,0,0,0,0], len: 8});
assert_eq!(dst, Address{data: [4,210,6,5,4,3,2,1,0,0,0,0,0,0,0,0], len: 8});
}
#[test]
fn decode_ipv4_packet() {
let data = [0x40,0,0,0,0,0,0,0,0,0,0,0,192,168,1,1,192,168,1,2];
let (src, dst) = Packet::parse(&data).unwrap();
assert_eq!(src, Address{data: [192,168,1,1,0,0,0,0,0,0,0,0,0,0,0,0], len: 4});
assert_eq!(dst, Address{data: [192,168,1,2,0,0,0,0,0,0,0,0,0,0,0,0], len: 4});
}
#[test]
fn decode_ipv6_packet() {
let data = [0x60,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,0,9,8,7,6,5,4,3,2,1,6,5,4,3,2,1];
let (src, dst) = Packet::parse(&data).unwrap();
assert_eq!(src, Address{data: [1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6], len: 16});
assert_eq!(dst, Address{data: [0,9,8,7,6,5,4,3,2,1,6,5,4,3,2,1], len: 16});
}
#[test]
fn switch() {
let mut table = SwitchTable::new(10);
let addr = Address::from_str("12:34:56:78:90:ab").unwrap();
let peer = "1.2.3.4:5678".to_socket_addrs().unwrap().next().unwrap();
assert!(table.lookup(&addr).is_none());
table.learn(addr.clone(), None, peer.clone());
assert_eq!(table.lookup(&addr), Some(peer));
}
#[test]
fn routing_table() {
let mut table = RoutingTable::new();
let peer1 = "1.2.3.4:1".to_socket_addrs().unwrap().next().unwrap();
let peer2 = "1.2.3.4:2".to_socket_addrs().unwrap().next().unwrap();
let peer3 = "1.2.3.4:3".to_socket_addrs().unwrap().next().unwrap();
assert!(table.lookup(&Address::from_str("192.168.1.1").unwrap()).is_none());
table.learn(Address::from_str("192.168.1.1").unwrap(), Some(32), peer1.clone());
assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1));
table.learn(Address::from_str("192.168.1.2").unwrap(), None, peer2.clone());
assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1));
assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2));
table.learn(Address::from_str("192.168.1.0").unwrap(), Some(24), peer3.clone());
assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1));
assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2));
assert_eq!(table.lookup(&Address::from_str("192.168.1.3").unwrap()), Some(peer3));
table.learn(Address::from_str("192.168.0.0").unwrap(), Some(16), peer1.clone());
assert_eq!(table.lookup(&Address::from_str("192.168.2.1").unwrap()), Some(peer1));
assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1));
assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2));
assert_eq!(table.lookup(&Address::from_str("192.168.1.3").unwrap()), Some(peer3));
table.learn(Address::from_str("0.0.0.0").unwrap(), Some(0), peer2.clone());
assert_eq!(table.lookup(&Address::from_str("192.168.2.1").unwrap()), Some(peer1));
assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1));
assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2));
assert_eq!(table.lookup(&Address::from_str("192.168.1.3").unwrap()), Some(peer3));
assert_eq!(table.lookup(&Address::from_str("1.2.3.4").unwrap()), Some(peer2));
table.learn(Address::from_str("192.168.2.0").unwrap(), Some(27), peer3.clone());
assert_eq!(table.lookup(&Address::from_str("192.168.2.31").unwrap()), Some(peer3));
assert_eq!(table.lookup(&Address::from_str("192.168.2.32").unwrap()), Some(peer1));
}
#[test]
fn address_fmt() {
assert_eq!(format!("{}", Address{data: [120,45,22,5,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}), "120.45.22.5");
assert_eq!(format!("{}", Address{data: [120,45,22,5,1,2,0,0,0,0,0,0,0,0,0,0], len: 6}), "78:2d:16:05:01:02");
assert_eq!(format!("{}", Address{data: [3,56,120,45,22,5,1,2,0,0,0,0,0,0,0,0], len: 8}), "vlan824/78:2d:16:05:01:02");
assert_eq!(format!("{}", Address{data: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], len: 16}), "0001:0203:0405:0607:0809:0a0b:0c0d:0e0f");
}

View File

@ -62,8 +62,7 @@ impl PartialEq for Address {
} }
} }
impl fmt::Display for Address {
impl fmt::Debug for Address {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let d = &self.data; let d = &self.data;
match self.len { match self.len {
@ -77,11 +76,17 @@ impl fmt::Debug for Address {
}, },
16 => write!(formatter, "{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}", 16 => write!(formatter, "{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}",
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]), d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]),
_ => d.fmt(formatter) _ => write!(formatter, "{:?}", d)
} }
} }
} }
impl fmt::Debug for Address {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "{}", self)
}
}
impl FromStr for Address { impl FromStr for Address {
type Err=Error; type Err=Error;
@ -119,7 +124,7 @@ impl FromStr for Address {
} }
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)]
pub struct Range { pub struct Range {
pub base: Address, pub base: Address,
pub prefix_len: u8 pub prefix_len: u8
@ -160,6 +165,18 @@ impl FromStr for Range {
} }
} }
impl fmt::Display for Range {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "{}/{}", self.base, self.prefix_len)
}
}
impl fmt::Debug for Range {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "{}", self)
}
}
#[derive(RustcDecodable, Debug, Clone, Copy)] #[derive(RustcDecodable, Debug, Clone, Copy)]
pub enum Type { pub enum Type {
@ -221,12 +238,3 @@ impl fmt::Display for Error {
} }
} }
} }
#[test]
fn address_fmt() {
assert_eq!(format!("{:?}", Address{data: [120,45,22,5,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}), "120.45.22.5");
assert_eq!(format!("{:?}", Address{data: [120,45,22,5,1,2,0,0,0,0,0,0,0,0,0,0], len: 6}), "78:2d:16:05:01:02");
assert_eq!(format!("{:?}", Address{data: [3,56,120,45,22,5,1,2,0,0,0,0,0,0,0,0], len: 8}), "vlan824/78:2d:16:05:01:02");
assert_eq!(format!("{:?}", Address{data: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], len: 16}), "0001:0203:0405:0607:0809:0a0b:0c0d:0e0f");
}

View File

@ -1,7 +1,7 @@
use std::{mem, fmt}; use std::{mem, fmt};
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr};
use super::types::{Error, NetworkId, Range, Address}; use super::types::{Error, NetworkId, Range};
use super::util::{Encoder, memcopy}; use super::util::{Encoder, memcopy};
use super::crypto::Crypto; use super::crypto::Crypto;
@ -270,95 +270,3 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8], crypto: &mut Cry
} }
pos pos
} }
#[test]
fn encode_message_packet() {
let mut options = Options::default();
let mut crypto = Crypto::None;
let payload = [1,2,3,4,5];
let msg = Message::Data(&payload);
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 13);
assert_eq!(&buf[..8], &[118,112,110,1,0,0,0,0]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[cfg(feature = "crypto")]
#[test]
fn encode_message_encrypted() {
let mut options = Options::default();
let mut crypto = Crypto::from_shared_key("test");
let payload = [1,2,3,4,5];
let msg = Message::Data(&payload);
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 37);
assert_eq!(&buf[..8], &[118,112,110,1,1,0,0,0]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_message_peers() {
use std::str::FromStr;
let mut options = Options::default();
let mut crypto = Crypto::None;
let msg = Message::Peers(vec![SocketAddr::from_str("1.2.3.4:123").unwrap(), SocketAddr::from_str("5.6.7.8:12345").unwrap()]);
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 22);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,0,1,2,1,2,3,4,0,123,5,6,7,8,48,57,0]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_option_network_id() {
let mut options = Options::default();
options.network_id = Some(134);
let mut crypto = Crypto::None;
let msg = Message::Close;
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 16);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,1,3,0,0,0,0,0,0,0,134]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_message_init() {
let mut options = Options::default();
let mut crypto = Crypto::None;
let addrs = vec![Range{base: Address{data: [0,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}, prefix_len: 24},
Range{base: Address{data: [0,1,2,3,4,5,0,0,0,0,0,0,0,0,0,0], len: 6}, prefix_len: 16}];
let msg = Message::Init(0, addrs);
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 24);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,0,2,0,2,4,0,1,2,3,24,6,0,1,2,3,4,5,16]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_message_close() {
let mut options = Options::default();
let mut crypto = Crypto::None;
let msg = Message::Close;
let mut buf = [0; 1024];
let size = encode(&mut options, &msg, &mut buf[..], &mut crypto);
assert_eq!(size, 8);
assert_eq!(&buf[..size], &[118,112,110,1,0,0,0,3]);
let (options2, msg2) = decode(&mut buf[..size], &mut crypto).unwrap();
assert_eq!(options, options2);
assert_eq!(msg, msg2);
}

View File

@ -289,7 +289,9 @@ field will follow:
* **Initial message** (message type 2): * **Initial message** (message type 2):
This packet contains all the local subnets claimed by the nodes. This packet contains all the local subnets claimed by the nodes.
The subnet list is encoded in the following way: The first byte of data Its first byte marks the stage of the initial handshake process. After that,
the list of local subnets follows.
The subnet list is encoded in the following way: Its first byte of data
contains the number of encoded subnets that follow. After that, the given contains the number of encoded subnets that follow. After that, the given
number of encoded subnets follow. number of encoded subnets follow.
For each subnet, the first byte is the length of bytes in the base address For each subnet, the first byte is the length of bytes in the base address