From 46011a03c651509fd9c931d7a32b8bf310bad99e Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Thu, 26 Nov 2015 22:16:51 +0100 Subject: [PATCH] Lots of changes and release 0.2 --- .travis.yml | 2 - Cargo.toml | 2 +- README.md | 22 +-- deb/vpncloud-nocrypto/debian/changelog | 6 + deb/vpncloud/debian/changelog | 6 + performance.md | 168 ++++++----------------- src/cloud.rs | 18 +-- src/ethernet.rs | 21 +-- src/ip.rs | 25 ++-- src/main.rs | 1 + src/tests.rs | 183 +++++++++++++++++++++++++ src/types.rs | 34 +++-- src/udpmessage.rs | 94 +------------ vpncloud.md | 4 +- 14 files changed, 300 insertions(+), 286 deletions(-) create mode 100644 src/tests.rs diff --git a/.travis.yml b/.travis.yml index 9c84cc9..cc45f5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: diff --git a/Cargo.toml b/Cargo.toml index 3510d08..c899c87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vpncloud" -version = "0.1.0" +version = "0.2.0" authors = ["Dennis Schwerdel "] build = "build.rs" diff --git a/README.md b/README.md index f7e6ff7..31d17e8 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/deb/vpncloud-nocrypto/debian/changelog b/deb/vpncloud-nocrypto/debian/changelog index 1903fad..9763dfc 100644 --- a/deb/vpncloud-nocrypto/debian/changelog +++ b/deb/vpncloud-nocrypto/debian/changelog @@ -1,3 +1,9 @@ +vpncloud-nocrypto (0.2.0) stable; urgency=medium + + * More stable release + + -- Dennis Schwerdel Thu, 26 Nov 2015 17:41:40 +0100 + vpncloud-nocrypto (0.1.0) stable; urgency=medium * Initial release diff --git a/deb/vpncloud/debian/changelog b/deb/vpncloud/debian/changelog index 77a50ad..671262c 100644 --- a/deb/vpncloud/debian/changelog +++ b/deb/vpncloud/debian/changelog @@ -1,3 +1,9 @@ +vpncloud (0.2.0) stable; urgency=medium + + * More stable release + + -- Dennis Schwerdel Thu, 26 Nov 2015 17:41:40 +0100 + vpncloud (0.1.0) stable; urgency=medium * Initial release diff --git a/performance.md b/performance.md index 1cedef5..b861b3d 100644 --- a/performance.md +++ b/performance.md @@ -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. diff --git a/src/cloud.rs b/src/cloud.rs index 523fde5..c74c670 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -194,22 +194,22 @@ impl GenericCloud

{ 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 GenericCloud

{ 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 GenericCloud

{ 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 GenericCloud

{ // Do the housekeeping match self.housekeep() { Ok(_) => (), - Err(e) => error!("Error: {:?}", e) + Err(e) => error!("Error: {}", e) } self.next_housekeep = now() + 1 } diff --git a/src/ethernet.rs b/src/ethernet.rs index 5846304..1f21fc6 100644 --- a/src/ethernet.rs +++ b/src/ethernet.rs @@ -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, 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}); -} diff --git a/src/ip.rs b/src/ip.rs index a849781..3b221cd 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -37,7 +37,7 @@ struct RoutingEntry { prefix_len: u8 } -pub struct RoutingTable(HashMap<[u8; 16], Vec>); +pub struct RoutingTable(HashMap, Vec>); 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 { - 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) { diff --git a/src/main.rs b/src/main.rs index f2a8b65..a15c141 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ mod ethernet; mod ip; mod cloud; mod device; +#[cfg(test)] mod tests; #[cfg(feature = "bench")] mod benches; use docopt::Docopt; diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..4f4314c --- /dev/null +++ b/src/tests.rs @@ -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"); +} diff --git a/src/types.rs b/src/types.rs index 388b8af..93b6695 100644 --- a/src/types.rs +++ b/src/types.rs @@ -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"); -} diff --git a/src/udpmessage.rs b/src/udpmessage.rs index cf7ec4c..b106bde 100644 --- a/src/udpmessage.rs +++ b/src/udpmessage.rs @@ -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); -} diff --git a/vpncloud.md b/vpncloud.md index dfad84b..3dfb5fc 100644 --- a/vpncloud.md +++ b/vpncloud.md @@ -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