Lots of changes and release 0.2

pull/9/head
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 test -- --features "crypto" &&
travis-cargo bench -- --features "crypto" &&
travis-cargo --only stable doc -- --features "crypto"
'
addons:
apt:
@ -28,7 +27,6 @@ addons:
- libelf-dev
- libdw-dev
after_success:
- travis-cargo --only stable doc-upload -- --features "crypto"
- travis-cargo coveralls --no-sudo -- --features "crypto"
notifications:
email:

View File

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

View File

@ -20,23 +20,27 @@ Some performance measurements can be found [here](performance.md).
### Current Status
This project is under heavy development and has not reached a stable state yet.
This is what currently works:
This project is still under development and has yet to reach a stable state.
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)
* Encryption using *libsodium*
* Setting up tunnels between two networks via Ethernet (TAP) and IP (TUN)
* 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:
* Encryption has not been thoroughly reviewed, use with care.
* The protocol can still 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.
* The software is not very well tested and the protocol can change.
Please feel free to help and contribute code.
### 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
* 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
* Initial release

View File

@ -15,159 +15,71 @@ Receiver node:
* Realtek RTL8111/8168/8411 Gigabit Network
* 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 \
--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 \
$> ./vpncloud -t tap -l SENDER:3210 -c RECEIVER:3210 \
--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' &
```
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 &
$> 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): 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
For encrypted tests, `--shared-key test` is appended.
### Test 4: Encrypted ping
### Throughput
The throughput is measured with the following command:
Node 1:
```
$> ./vpncloud -t tap -l NODE1:3210 -c NODE2:3210 \
--ifup 'ifconfig $IFNAME 10.2.1.1/24 mtu 1400 up' --shared-key test &
$> iperf -c DST -t 60
```
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 \
--ifup 'ifconfig $IFNAME 10.2.1.2/24 mtu 1400 up' --shared-key test &
$> ping DST -c 10000 -i 0.001 -s SIZE -U -q
```
Each test is first run without VpnCloud:
```
$> ping NODE2 -c 10000 -i 0.001 -s SIZE -U -q
```
For all the test, the best average RTT out of 5 runs is selected. The latency is
assumed to be half of the RTT.
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: Ø= 454 µs
SIZE: 500 bytes
* Without VpnCloud: Ø= 330 µs
* With VpnCloud: Ø= 492 µs
SIZE: 1000 bytes
* Without VpnCloud: Ø= 356 µs
* With VpnCloud: Ø= 543 µs
| Payload size | 100 bytes | 500 bytes | 1000 bytes |
| -------------------- | --------- | --------- | ---------- |
| Without VpnCloud | 158 µs | 165 µs | 178 µs |
| Unencrypted VpnCloud | 210 µs | 216 µs | 237 µs |
| Difference | +52 µs | +51 µs | +59 µs |
| Encrypted VpnCloud | 225 µs | 252 µs | 262 µs |
| Difference | +15 µs | +36 µs | +25 µs |
### Conclusion
* VpnCloud achieves about 885 MBit/s with default MTU settings.
* In encrypted mode, VpnCloud reaches aboud 663 MBit/s with default MTU settings.
* At increased MTU, VpnCloud is able to saturate a Gigabit link even when encrypting.
* VpnCloud adds about 120µs to the round trip times, i.e. 60µs latency increase.
* VpnCloud achieves over 850 MBit/s with default MTU settings.
* In encrypted mode, VpnCloud reaches over 600 MBit/s with default MTU settings.
* VpnCloud adds about 60µs to the latency.
* 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> {
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) {
Some(addr) => {
debug!("Found destination for {:?} => {}", dst, addr);
debug!("Found destination for {} => {}", dst, addr);
if self.peers.contains(&addr) {
try!(self.send_msg(addr, &Message::Data(payload)))
} else {
warn!("Destination for {:?} not found in peers: {}", dst, addr);
warn!("Destination for {} not found in peers: {}", dst, addr);
}
},
None => {
if !self.broadcast {
debug!("No destination for {:?} found, dropping", dst);
debug!("No destination for {} found, dropping", dst);
return Ok(());
}
debug!("No destination for {:?} found, broadcasting", dst);
debug!("No destination for {} found, broadcasting", dst);
let msg = Message::Data(payload);
for addr in &self.peers.as_vec() {
try!(self.send_msg(addr, &msg));
@ -234,7 +234,7 @@ impl<P: Protocol> GenericCloud<P> {
match self.device.write(&payload) {
Ok(()) => (),
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"));
}
}
@ -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: {}");
match decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) {
Ok(_) => (),
Err(e) => error!("Error: {:?}", e)
Err(e) => error!("Error: {}", e)
}
},
&1 => {
let size = try_fail!(self.device.read(&mut buffer), "Failed to read from tap device: {}");
match self.handle_interface_data(&buffer[..size]) {
Ok(_) => (),
Err(e) => error!("Error: {:?}", e)
Err(e) => error!("Error: {}", e)
}
},
_ => unreachable!()
@ -313,7 +313,7 @@ impl<P: Protocol> GenericCloud<P> {
// Do the housekeeping
match self.housekeep() {
Ok(_) => (),
Err(e) => error!("Error: {:?}", e)
Err(e) => error!("Error: {}", e)
}
self.next_housekeep = now() + 1
}

View File

@ -67,7 +67,7 @@ impl Table for SwitchTable {
}
}
for key in del {
info!("Forgot address {:?}", key);
info!("Forgot address {}", key);
self.table.remove(&key);
}
self.cache = None;
@ -77,7 +77,7 @@ impl Table for SwitchTable {
fn learn(&mut self, key: Address, _prefix_len: Option<u8>, addr: SocketAddr) {
let value = SwitchTableValue{address: addr, timeout: now()+self.timeout as Time};
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!()
}
}
#[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
}
pub struct RoutingTable(HashMap<[u8; 16], Vec<RoutingEntry>>);
pub struct RoutingTable(HashMap<Vec<u8>, Vec<RoutingEntry>>);
impl RoutingTable {
pub fn new() -> Self {
@ -51,12 +51,12 @@ impl Table for RoutingTable {
Some(val) => val,
None => addr.len * 8
};
info!("New routing entry: {:?}/{} => {}", addr, prefix_len, address);
let group_len = (prefix_len as usize / 16) * 2;
info!("New routing entry: {}/{} => {}", addr, prefix_len, address);
let group_len = prefix_len as usize / 8;
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 {
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};
match self.0.entry(group_bytes) {
@ -66,9 +66,11 @@ impl Table for RoutingTable {
}
fn lookup(&mut self, addr: &Address) -> Option<SocketAddr> {
let len = addr.len as usize/2 * 2;
for i in 0..(len/2)+1 {
if let Some(group) = self.0.get(&addr.data[0..len-2*i]) {
let len = addr.len as usize;
let mut found = None;
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 {
let mut match_len = 0;
for j in 0..addr.len as usize {
@ -80,13 +82,14 @@ impl Table for RoutingTable {
break;
}
}
if match_len as u8 >= entry.prefix_len {
return Some(entry.address);
if match_len as u8 >= entry.prefix_len && match_len as isize > found_len {
found = Some(entry.address);
found_len = match_len as isize;
}
}
}
}
None
found
}
fn housekeep(&mut self) {

View File

@ -18,6 +18,7 @@ mod ethernet;
mod ip;
mod cloud;
mod device;
#[cfg(test)] mod tests;
#[cfg(feature = "bench")] mod benches;
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::Debug for Address {
impl fmt::Display for Address {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let d = &self.data;
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}",
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 {
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 base: Address,
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)]
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::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::crypto::Crypto;
@ -270,95 +270,3 @@ pub fn encode(options: &Options, msg: &Message, buf: &mut [u8], crypto: &mut Cry
}
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):
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
number of encoded subnets follow.
For each subnet, the first byte is the length of bytes in the base address