mirror of https://github.com/dswd/vpncloud.git
Configurable magic header instead of network id (re #5)
This commit is contained in:
parent
e160b35be6
commit
08048709b6
|
@ -4,6 +4,9 @@ This project follows [semantic versioning](http://semver.org).
|
|||
|
||||
### UNRELEASED
|
||||
|
||||
- [changed] Configurable magic header is now used instead of Network-ID (**incompatible**)
|
||||
- [fixed] Fixed documentation of listen parameter
|
||||
- [fixed] Fixed problem with multiple subnets
|
||||
|
||||
### v0.7.0 (2016-08-05)
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@ use std::str::FromStr;
|
|||
use std::net::{UdpSocket, ToSocketAddrs, Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use super::MAGIC;
|
||||
use super::cloud::GenericCloud;
|
||||
use super::device::{Device, Type};
|
||||
use super::udpmessage::{Options, Message, encode, decode};
|
||||
use super::udpmessage::{Message, encode, decode};
|
||||
use super::crypto::{Crypto, CryptoMethod};
|
||||
use super::ethernet::{Frame, SwitchTable};
|
||||
use super::types::{Address, Table, Protocol};
|
||||
|
@ -51,27 +52,25 @@ fn crypto_aes256(b: &mut Bencher) {
|
|||
|
||||
#[bench]
|
||||
fn message_encode(b: &mut Bencher) {
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let mut payload = [0; 1600];
|
||||
let mut msg = Message::Data(&mut payload, 64, 1464);
|
||||
let mut buf = [0; 1600];
|
||||
b.iter(|| {
|
||||
encode(&mut options, &mut msg, &mut buf[..], &mut crypto);
|
||||
encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
||||
});
|
||||
b.bytes = 1400;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn message_decode(b: &mut Bencher) {
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let mut payload = [0; 1600];
|
||||
let mut msg = Message::Data(&mut payload, 64, 1464);
|
||||
let mut buf = [0; 1600];
|
||||
let mut res = encode(&mut options, &mut msg, &mut buf[..], &mut crypto);
|
||||
let mut res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
||||
b.iter(|| {
|
||||
decode(&mut res, &mut crypto).unwrap();
|
||||
decode(&mut res, MAGIC, &mut crypto).unwrap();
|
||||
});
|
||||
b.bytes = 1400;
|
||||
}
|
||||
|
@ -152,7 +151,7 @@ fn epoll_wait(b: &mut Bencher) {
|
|||
#[bench]
|
||||
fn handle_interface_data(b: &mut Bencher) {
|
||||
let mut node = GenericCloud::<Frame>::new(
|
||||
Device::dummy("vpncloud0", "/dev/null", Type::Tap).unwrap(), 0, None,
|
||||
MAGIC, Device::dummy("vpncloud0", "/dev/null", Type::Tap).unwrap(), 0,
|
||||
Box::new(SwitchTable::new(300)), 1800, true, true, vec![], Crypto::None
|
||||
);
|
||||
let mut data = [0; 1500];
|
||||
|
@ -166,14 +165,14 @@ fn handle_interface_data(b: &mut Bencher) {
|
|||
#[bench]
|
||||
fn handle_net_message(b: &mut Bencher) {
|
||||
let mut node = GenericCloud::<Frame>::new(
|
||||
Device::dummy("vpncloud0", "/dev/null", Type::Tap).unwrap(), 0, None,
|
||||
MAGIC, Device::dummy("vpncloud0", "/dev/null", Type::Tap).unwrap(), 0,
|
||||
Box::new(SwitchTable::new(300)), 1800, true, true, vec![], Crypto::None
|
||||
);
|
||||
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1));
|
||||
let mut data = [0; 1500];
|
||||
data[105] = 45;
|
||||
b.iter(|| {
|
||||
node.handle_net_message(addr.clone(), Options::default(), Message::Data(&mut data, 0, 1400)).unwrap()
|
||||
node.handle_net_message(addr.clone(), Message::Data(&mut data, 0, 1400)).unwrap()
|
||||
});
|
||||
b.bytes = 1400;
|
||||
}
|
||||
|
|
30
src/cloud.rs
30
src/cloud.rs
|
@ -19,9 +19,9 @@ use signal::trap::Trap;
|
|||
use rand::{random, sample, thread_rng};
|
||||
use net2::UdpBuilder;
|
||||
|
||||
use super::types::{Table, Protocol, Range, Error, NetworkId, NodeId};
|
||||
use super::types::{Table, Protocol, Range, Error, HeaderMagic, NodeId};
|
||||
use super::device::Device;
|
||||
use super::udpmessage::{encode, decode, Options, Message};
|
||||
use super::udpmessage::{encode, decode, Message};
|
||||
use super::crypto::Crypto;
|
||||
use super::util::{now, Time, Duration, resolve};
|
||||
use super::poll::{self, Poll};
|
||||
|
@ -153,6 +153,7 @@ pub struct ReconnectEntry {
|
|||
}
|
||||
|
||||
pub struct GenericCloud<P: Protocol> {
|
||||
magic: HeaderMagic,
|
||||
node_id: NodeId,
|
||||
peers: PeerList,
|
||||
addresses: Vec<Range>,
|
||||
|
@ -164,7 +165,6 @@ pub struct GenericCloud<P: Protocol> {
|
|||
socket4: UdpSocket,
|
||||
socket6: UdpSocket,
|
||||
device: Device,
|
||||
options: Options,
|
||||
crypto: Crypto,
|
||||
next_peerlist: Time,
|
||||
update_freq: Duration,
|
||||
|
@ -176,7 +176,7 @@ pub struct GenericCloud<P: Protocol> {
|
|||
impl<P: Protocol> GenericCloud<P> {
|
||||
#[allow(unknown_lints)]
|
||||
#[allow(too_many_arguments)]
|
||||
pub fn new(device: Device, listen: u16, network_id: Option<NetworkId>, table: Box<Table>,
|
||||
pub fn new(magic: HeaderMagic, device: Device, listen: u16, table: Box<Table>,
|
||||
peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec<Range>,
|
||||
crypto: Crypto) -> Self {
|
||||
let socket4 = match UdpBuilder::new_v4().expect("Failed to obtain ipv4 socket builder")
|
||||
|
@ -190,9 +190,8 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
Ok(socket) => socket,
|
||||
Err(err) => fail!("Failed to open ipv6 address ::{}: {}", listen, err)
|
||||
};
|
||||
let mut options = Options::default();
|
||||
options.network_id = network_id;
|
||||
GenericCloud{
|
||||
magic: magic,
|
||||
node_id: random(),
|
||||
peers: PeerList::new(peer_timeout),
|
||||
addresses: addresses,
|
||||
|
@ -204,7 +203,6 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
socket4: socket4,
|
||||
socket6: socket6,
|
||||
device: device,
|
||||
options: options,
|
||||
crypto: crypto,
|
||||
next_peerlist: now(),
|
||||
update_freq: peer_timeout/2-60,
|
||||
|
@ -229,7 +227,7 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
fn broadcast_msg(&mut self, msg: &mut Message) -> Result<(), Error> {
|
||||
debug!("Broadcasting {:?}", msg);
|
||||
// Encrypt and encode once and send several times
|
||||
let msg_data = encode(&self.options, msg, &mut self.buffer_out, &mut self.crypto);
|
||||
let msg_data = encode(msg, &mut self.buffer_out, self.magic, &mut self.crypto);
|
||||
for addr in self.peers.as_vec() {
|
||||
let socket = match addr {
|
||||
SocketAddr::V4(_) => &self.socket4,
|
||||
|
@ -256,7 +254,7 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
fn send_msg(&mut self, addr: SocketAddr, msg: &mut Message) -> Result<(), Error> {
|
||||
debug!("Sending {:?} to {}", msg, addr);
|
||||
// Encrypt and encode
|
||||
let msg_data = encode(&self.options, msg, &mut self.buffer_out, &mut self.crypto);
|
||||
let msg_data = encode(msg, &mut self.buffer_out, self.magic, &mut self.crypto);
|
||||
let socket = match addr {
|
||||
SocketAddr::V4(_) => &self.socket4,
|
||||
SocketAddr::V6(_) => &self.socket6
|
||||
|
@ -456,11 +454,7 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
/// Handles a message received from the network
|
||||
///
|
||||
/// This method handles messages from the network, i.e. from peers. `peer` contains the sender
|
||||
/// of the message. `options` contains the options from the message and `msg` contains the
|
||||
/// message.
|
||||
///
|
||||
/// If the `network_id` in the messages options differs from the `network_id` of this node,
|
||||
/// the message is simply ignored.
|
||||
/// of the message and `msg` contains the message.
|
||||
///
|
||||
/// Then this method will check the message type and will handle each message type differently.
|
||||
///
|
||||
|
@ -490,11 +484,7 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
/// # `Message::Close` message
|
||||
/// If this message is received, the sender is removed from the peer list and its claimed
|
||||
/// addresses are removed from the table.
|
||||
pub fn handle_net_message(&mut self, peer: SocketAddr, options: Options, msg: Message) -> Result<(), Error> {
|
||||
if self.options.network_id != options.network_id {
|
||||
info!("Ignoring message from {} with wrong token {:?}", peer, options.network_id);
|
||||
return Err(Error::WrongNetwork(options.network_id));
|
||||
}
|
||||
pub fn handle_net_message(&mut self, peer: SocketAddr, msg: Message) -> Result<(), Error> {
|
||||
debug!("Received {:?} from {}", msg, peer);
|
||||
match msg {
|
||||
Message::Data(payload, start, end) => {
|
||||
|
@ -583,7 +573,7 @@ impl<P: Protocol> GenericCloud<P> {
|
|||
fd if fd == socket6_fd => try_fail!(self.socket6.recv_from(&mut buffer), "Failed to read from ipv6 network socket: {}"),
|
||||
_ => unreachable!()
|
||||
};
|
||||
if let Err(e) = decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) {
|
||||
if let Err(e) = decode(&mut buffer[..size], self.magic, &mut self.crypto).and_then(|msg| self.handle_net_message(src, msg)) {
|
||||
error!("Error: {}, from: {}", e, src);
|
||||
}
|
||||
},
|
||||
|
|
35
src/main.rs
35
src/main.rs
|
@ -39,11 +39,14 @@ use std::process::Command;
|
|||
use device::{Device, Type};
|
||||
use ethernet::SwitchTable;
|
||||
use ip::RoutingTable;
|
||||
use types::{Mode, Range, Table, Protocol};
|
||||
use types::{Mode, Range, Table, Protocol, HeaderMagic};
|
||||
use cloud::GenericCloud;
|
||||
use udpmessage::VERSION;
|
||||
use crypto::{Crypto, CryptoMethod};
|
||||
use util::Duration;
|
||||
use util::{Duration, Encoder};
|
||||
|
||||
|
||||
const VERSION: u8 = 1;
|
||||
const MAGIC: HeaderMagic = *b"vpn\x01";
|
||||
|
||||
|
||||
struct SimpleLogger;
|
||||
|
@ -74,6 +77,7 @@ struct Args {
|
|||
flag_device: String,
|
||||
flag_listen: u16,
|
||||
flag_network_id: Option<String>,
|
||||
flag_magic: Option<String>,
|
||||
flag_connect: Vec<String>,
|
||||
flag_peer_timeout: Duration,
|
||||
flag_dst_timeout: Duration,
|
||||
|
@ -98,7 +102,7 @@ fn run_script(script: String, ifname: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
fn run<T: Protocol> (args: Args) {
|
||||
fn run<T: Protocol> (mut args: Args) {
|
||||
let device = try_fail!(Device::new(&args.flag_device, args.flag_type),
|
||||
"Failed to open virtual {} interface {}: {}", args.flag_type, &args.flag_device);
|
||||
info!("Opened device {}", device.ifname());
|
||||
|
@ -117,17 +121,32 @@ fn run<T: Protocol> (args: Args) {
|
|||
Mode::Switch => (true, true, Box::new(SwitchTable::new(dst_timeout))),
|
||||
Mode::Hub => (false, true, Box::new(SwitchTable::new(dst_timeout)))
|
||||
};
|
||||
let network_id = args.flag_network_id.map(|name| {
|
||||
if let Some(network_id) = args.flag_network_id {
|
||||
warn!("The --network-id argument is deprecated, please use --magic instead.");
|
||||
if args.flag_magic.is_none() {
|
||||
args.flag_magic = Some(network_id);
|
||||
}
|
||||
}
|
||||
let magic = args.flag_magic.map_or(MAGIC, |name| {
|
||||
if name.starts_with("hash:") {
|
||||
let mut s = SipHasher::new();
|
||||
name.hash(&mut s);
|
||||
s.finish()
|
||||
name[6..].hash(&mut s);
|
||||
let mut data = [0; 4];
|
||||
Encoder::write_u32((s.finish() & 0xffffffff) as u32, &mut data);
|
||||
data
|
||||
} else {
|
||||
let num = try_fail!(u32::from_str_radix(&name, 16), "Failed to parse header magic: {}");
|
||||
let mut data = [0; 4];
|
||||
Encoder::write_u32(num, &mut data);
|
||||
data
|
||||
}
|
||||
});
|
||||
Crypto::init();
|
||||
let crypto = match args.flag_shared_key {
|
||||
Some(key) => Crypto::from_shared_key(args.flag_crypto, &key),
|
||||
None => Crypto::None
|
||||
};
|
||||
let mut cloud = GenericCloud::<T>::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
|
||||
let mut cloud = GenericCloud::<T>::new(magic, device, args.flag_listen, table, peer_timeout, learning, broadcasting, ranges, crypto);
|
||||
if let Some(script) = args.flag_ifup {
|
||||
run_script(script, cloud.ifname());
|
||||
}
|
||||
|
|
75
src/tests.rs
75
src/tests.rs
|
@ -5,10 +5,11 @@
|
|||
use std::net::{ToSocketAddrs, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::MAGIC;
|
||||
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::udpmessage::{Message, decode, encode};
|
||||
use super::crypto::{Crypto, CryptoMethod};
|
||||
|
||||
|
||||
|
@ -34,7 +35,6 @@ impl<'a> PartialEq for Message<'a> {
|
|||
#[test]
|
||||
#[allow(unused_assignments)]
|
||||
fn udpmessage_packet() {
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let mut payload = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
|
@ -45,7 +45,7 @@ fn udpmessage_packet() {
|
|||
let mut buf = [0; 1024];
|
||||
let mut len = 0;
|
||||
{
|
||||
let res = encode(&mut options, &mut msg, &mut [], &mut crypto);
|
||||
let res = encode(&mut msg, &mut [], MAGIC, &mut crypto);
|
||||
assert_eq!(res.len(), 13);
|
||||
assert_eq!(&res[..8], &[118,112,110,1,0,0,0,0]);
|
||||
for i in 0..res.len() {
|
||||
|
@ -53,15 +53,13 @@ fn udpmessage_packet() {
|
|||
}
|
||||
len = res.len();
|
||||
}
|
||||
let (options2, msg2) = decode(&mut buf[..len], &mut crypto).unwrap();
|
||||
assert_eq!(options, options2);
|
||||
let msg2 = decode(&mut buf[..len], MAGIC, &mut crypto).unwrap();
|
||||
assert_eq!(msg, msg2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(unused_assignments)]
|
||||
fn udpmessage_encrypted() {
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
|
||||
let mut payload = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
|
@ -77,7 +75,7 @@ fn udpmessage_encrypted() {
|
|||
let mut buf = [0; 1024];
|
||||
let mut len = 0;
|
||||
{
|
||||
let res = encode(&mut options, &mut msg, &mut [], &mut crypto);
|
||||
let res = encode(&mut msg, &mut [], MAGIC, &mut crypto);
|
||||
assert_eq!(res.len(), 41);
|
||||
assert_eq!(&res[..8], &[118,112,110,1,1,0,0,0]);
|
||||
for i in 0..res.len() {
|
||||
|
@ -85,62 +83,40 @@ fn udpmessage_encrypted() {
|
|||
}
|
||||
len = res.len();
|
||||
}
|
||||
let (options2, msg2) = decode(&mut buf[..len], &mut crypto).unwrap();
|
||||
assert_eq!(options, options2);
|
||||
let msg2 = decode(&mut buf[..len], MAGIC, &mut crypto).unwrap();
|
||||
assert_eq!(orig_msg, msg2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn udpmessage_peers() {
|
||||
use std::str::FromStr;
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let mut msg = Message::Peers(vec![SocketAddr::from_str("1.2.3.4:123").unwrap(), SocketAddr::from_str("5.6.7.8:12345").unwrap(), SocketAddr::from_str("[0001:0203:0405:0607:0809:0a0b:0c0d:0e0f]:6789").unwrap()]);
|
||||
let mut should = [118,112,110,1,0,0,0,1,2,1,2,3,4,0,123,5,6,7,8,48,57,1,0,1,2,3,4,5,6,7,
|
||||
8,9,10,11,12,13,14,15,26,133];
|
||||
{
|
||||
let mut buf = [0; 1024];
|
||||
let res = encode(&mut options, &mut msg, &mut buf[..], &mut crypto);
|
||||
let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
||||
assert_eq!(res.len(), 40);
|
||||
for i in 0..res.len() {
|
||||
assert_eq!(res[i], should[i]);
|
||||
}
|
||||
}
|
||||
let (options2, msg2) = decode(&mut should, &mut crypto).unwrap();
|
||||
assert_eq!(options, options2);
|
||||
let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap();
|
||||
assert_eq!(msg, msg2);
|
||||
// Missing IPv4 count
|
||||
assert!(decode(&mut[118,112,110,1,0,0,0,1], &mut crypto).is_err());
|
||||
assert!(decode(&mut[118,112,110,1,0,0,0,1], MAGIC, &mut crypto).is_err());
|
||||
// Truncated IPv4
|
||||
assert!(decode(&mut[118,112,110,1,0,0,0,1,1], &mut crypto).is_err());
|
||||
assert!(decode(&mut[118,112,110,1,0,0,0,1,1], MAGIC, &mut crypto).is_err());
|
||||
// Missing IPv6 count
|
||||
assert!(decode(&mut[118,112,110,1,0,0,0,1,1,1,2,3,4,0,0], &mut crypto).is_err());
|
||||
assert!(decode(&mut[118,112,110,1,0,0,0,1,1,1,2,3,4,0,0], MAGIC, &mut crypto).is_err());
|
||||
// Truncated IPv6
|
||||
assert!(decode(&mut[118,112,110,1,0,0,0,1,1,1,2,3,4,0,0,1], &mut crypto).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn udpmessage_option_network_id() {
|
||||
let mut options = Options::default();
|
||||
options.network_id = Some(134);
|
||||
let mut crypto = Crypto::None;
|
||||
let mut msg = Message::Close;
|
||||
let mut should = [118,112,110,1,0,0,1,3,0,0,0,0,0,0,0,134];
|
||||
{
|
||||
let mut buf = [0; 1024];
|
||||
let res = encode(&mut options, &mut msg, &mut buf[..], &mut crypto);
|
||||
assert_eq!(res.len(), 16);
|
||||
assert_eq!(&res, &should);
|
||||
}
|
||||
let (options2, msg2) = decode(&mut should, &mut crypto).unwrap();
|
||||
assert_eq!(options, options2);
|
||||
assert_eq!(msg, msg2);
|
||||
assert!(decode(&mut[118,112,110,1,0,0,0,1,1,1,2,3,4,0,0,1], MAGIC, &mut crypto).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn udpmessage_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}];
|
||||
|
@ -149,57 +125,52 @@ fn udpmessage_init() {
|
|||
let mut should = [118,112,110,1,0,0,0,2,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,2,4,0,1,2,3,24,6,0,1,2,3,4,5,16];
|
||||
{
|
||||
let mut buf = [0; 1024];
|
||||
let res = encode(&mut options, &mut msg, &mut buf[..], &mut crypto);
|
||||
let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
||||
assert_eq!(res.len(), 40);
|
||||
for i in 0..res.len() {
|
||||
assert_eq!(res[i], should[i]);
|
||||
}
|
||||
}
|
||||
let (options2, msg2) = decode(&mut should, &mut crypto).unwrap();
|
||||
assert_eq!(options, options2);
|
||||
let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap();
|
||||
assert_eq!(msg, msg2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn udpmessage_close() {
|
||||
let mut options = Options::default();
|
||||
let mut crypto = Crypto::None;
|
||||
let mut msg = Message::Close;
|
||||
let mut should = [118,112,110,1,0,0,0,3];
|
||||
{
|
||||
let mut buf = [0; 1024];
|
||||
let res = encode(&mut options, &mut msg, &mut buf[..], &mut crypto);
|
||||
let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
||||
assert_eq!(res.len(), 8);
|
||||
assert_eq!(&res, &should);
|
||||
}
|
||||
let (options2, msg2) = decode(&mut should, &mut crypto).unwrap();
|
||||
assert_eq!(options, options2);
|
||||
let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap();
|
||||
assert_eq!(msg, msg2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn udpmessage_invalid() {
|
||||
let mut crypto = Crypto::None;
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0], &mut crypto).is_ok());
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0], MAGIC, &mut crypto).is_ok());
|
||||
// too short
|
||||
assert!(decode(&mut [], &mut crypto).is_err());
|
||||
assert!(decode(&mut [], MAGIC, &mut crypto).is_err());
|
||||
// invalid protocol
|
||||
assert!(decode(&mut [0,1,2,0,0,0,0,0], &mut crypto).is_err());
|
||||
assert!(decode(&mut [0,1,2,0,0,0,0,0], MAGIC, &mut crypto).is_err());
|
||||
// invalid version
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,0xaa,0,0,0,0], &mut crypto).is_err());
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,0xaa,0,0,0,0], MAGIC, &mut crypto).is_err());
|
||||
// invalid crypto
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,1,0xaa,0,0,0], &mut crypto).is_err());
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,1,0xaa,0,0,0], MAGIC, &mut crypto).is_err());
|
||||
// invalid msg type
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0xaa], &mut crypto).is_err());
|
||||
// truncated options
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,1,0], &mut crypto).is_err());
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0xaa], MAGIC, &mut crypto).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn udpmessage_invalid_crypto() {
|
||||
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
|
||||
// truncated crypto
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,1,1,0,0,0], &mut crypto).is_err());
|
||||
assert!(decode(&mut [0x76,0x70,0x6e,1,1,0,0,0], MAGIC, &mut crypto).is_err());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ use super::util::{bytes_to_hex, Encoder};
|
|||
|
||||
pub const NODE_ID_BYTES: usize = 16;
|
||||
|
||||
pub type NetworkId = u64;
|
||||
pub type HeaderMagic = [u8; 4];
|
||||
pub type NodeId = [u8; NODE_ID_BYTES];
|
||||
|
||||
|
||||
|
@ -215,7 +215,7 @@ pub trait Protocol: Sized {
|
|||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Parse(&'static str),
|
||||
WrongNetwork(Option<NetworkId>),
|
||||
WrongHeaderMagic(HeaderMagic),
|
||||
Socket(&'static str, io::Error),
|
||||
Name(String),
|
||||
TunTapDev(&'static str, io::Error),
|
||||
|
@ -229,8 +229,7 @@ impl fmt::Display for Error {
|
|||
Error::TunTapDev(ref msg, ref err) => write!(formatter, "{}: {:?}", msg, err),
|
||||
Error::Crypto(ref msg) => write!(formatter, "{}", msg),
|
||||
Error::Name(ref name) => write!(formatter, "failed to resolve name '{}'", name),
|
||||
Error::WrongNetwork(Some(net)) => write!(formatter, "wrong network id: {}", net),
|
||||
Error::WrongNetwork(None) => write!(formatter, "wrong network id: none"),
|
||||
Error::WrongHeaderMagic(net) => write!(formatter, "wrong header magic: {}", bytes_to_hex(&net)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,23 +5,17 @@
|
|||
use std::fmt;
|
||||
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr};
|
||||
|
||||
use super::types::{NodeId, Error, NetworkId, Range, NODE_ID_BYTES};
|
||||
use super::types::{NodeId, HeaderMagic, Error, Range, NODE_ID_BYTES};
|
||||
use super::util::{bytes_to_hex, Encoder};
|
||||
use super::crypto::Crypto;
|
||||
|
||||
const MAGIC: [u8; 3] = *b"vpn";
|
||||
pub const VERSION: u8 = 1;
|
||||
|
||||
const NETWORK_ID_BYTES: usize = 8;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
#[repr(packed)]
|
||||
struct TopHeader {
|
||||
magic: [u8; 3],
|
||||
version: u8,
|
||||
magic: HeaderMagic,
|
||||
crypto_method : u8,
|
||||
_reserved: u8,
|
||||
flags: u8,
|
||||
_reserved1: u8,
|
||||
_reserved2: u8,
|
||||
msgtype: u8
|
||||
}
|
||||
|
||||
|
@ -36,35 +30,20 @@ impl TopHeader {
|
|||
return Err(Error::Parse("Empty message"));
|
||||
}
|
||||
let mut header = TopHeader::default();
|
||||
header.magic.copy_from_slice(&data[0..3]);
|
||||
header.version = data[3];
|
||||
header.magic.copy_from_slice(&data[0..4]);
|
||||
header.crypto_method = data[4];
|
||||
header.flags = data[6];
|
||||
header.msgtype = data[7];
|
||||
Ok((header, TopHeader::size()))
|
||||
}
|
||||
|
||||
pub fn write_to(&self, data: &mut [u8]) -> usize {
|
||||
data[0..3].copy_from_slice(&self.magic);
|
||||
data[3] = self.version;
|
||||
data[0..4].copy_from_slice(&self.magic);
|
||||
data[4] = self.crypto_method;
|
||||
data[6] = self.flags;
|
||||
data[7] = self.msgtype;
|
||||
TopHeader::size()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TopHeader {
|
||||
fn default() -> Self {
|
||||
TopHeader{magic: MAGIC, version: VERSION, crypto_method: 0, _reserved: 0, flags: 0, msgtype: 0}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Options {
|
||||
pub network_id: Option<NetworkId>,
|
||||
}
|
||||
|
||||
pub enum Message<'a> {
|
||||
Data(&'a mut[u8], usize, usize), // data, start, end
|
||||
Peers(Vec<SocketAddr>), // peers
|
||||
|
@ -96,14 +75,11 @@ impl<'a> fmt::Debug for Message<'a> {
|
|||
|
||||
#[allow(unknown_lints)]
|
||||
#[allow(needless_range_loop)]
|
||||
pub fn decode<'a>(data: &'a mut [u8], crypto: &mut Crypto) -> Result<(Options, Message<'a>), Error> {
|
||||
pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &mut Crypto) -> Result<Message<'a>, Error> {
|
||||
let mut end = data.len();
|
||||
let (header, mut pos) = try!(TopHeader::read_from(&data[..end]));
|
||||
if header.magic != MAGIC {
|
||||
return Err(Error::Parse("Wrong protocol"));
|
||||
}
|
||||
if header.version != VERSION {
|
||||
return Err(Error::Parse("Wrong version"));
|
||||
if header.magic != magic {
|
||||
return Err(Error::WrongHeaderMagic(header.magic));
|
||||
}
|
||||
if header.crypto_method != crypto.method() {
|
||||
return Err(Error::Crypto("Wrong crypto method"));
|
||||
|
@ -121,14 +97,6 @@ pub fn decode<'a>(data: &'a mut [u8], crypto: &mut Crypto) -> Result<(Options, M
|
|||
}
|
||||
assert_eq!(end, data.len()-crypto.additional_bytes());
|
||||
}
|
||||
let mut options = Options::default();
|
||||
if header.flags & 0x01 > 0 {
|
||||
if end < pos + NETWORK_ID_BYTES {
|
||||
return Err(Error::Parse("Truncated options"));
|
||||
}
|
||||
options.network_id = Some(Encoder::read_u64(&data[pos..pos+NETWORK_ID_BYTES]));
|
||||
pos += NETWORK_ID_BYTES;
|
||||
}
|
||||
let msg = match header.msgtype {
|
||||
0 => Message::Data(data, pos, end),
|
||||
1 => {
|
||||
|
@ -195,12 +163,12 @@ pub fn decode<'a>(data: &'a mut [u8], crypto: &mut Crypto) -> Result<(Options, M
|
|||
3 => Message::Close,
|
||||
_ => return Err(Error::Parse("Unknown message type"))
|
||||
};
|
||||
Ok((options, msg))
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
#[allow(unknown_lints)]
|
||||
#[allow(needless_range_loop)]
|
||||
pub fn encode<'a>(options: &Options, msg: &'a mut Message, mut buf: &'a mut [u8], crypto: &mut Crypto) -> &'a mut [u8] {
|
||||
pub fn encode<'a>(msg: &'a mut Message, mut buf: &'a mut [u8], magic: HeaderMagic, crypto: &mut Crypto) -> &'a mut [u8] {
|
||||
let mut start = 64;
|
||||
let mut end = 64;
|
||||
match *msg {
|
||||
|
@ -264,14 +232,10 @@ pub fn encode<'a>(options: &Options, msg: &'a mut Message, mut buf: &'a mut [u8]
|
|||
}
|
||||
assert!(start >= 64);
|
||||
assert!(buf.len() >= end + 64);
|
||||
if let Some(id) = options.network_id {
|
||||
assert!(start >= NETWORK_ID_BYTES);
|
||||
Encoder::write_u64(id, &mut buf[start-NETWORK_ID_BYTES..]);
|
||||
start -= NETWORK_ID_BYTES;
|
||||
}
|
||||
let crypto_start = start;
|
||||
start -= crypto.nonce_bytes();
|
||||
let mut header = TopHeader::default();
|
||||
header.magic = magic;
|
||||
header.msgtype = match *msg {
|
||||
Message::Data(_, _, _) => 0,
|
||||
Message::Peers(_) => 1,
|
||||
|
@ -279,9 +243,6 @@ pub fn encode<'a>(options: &Options, msg: &'a mut Message, mut buf: &'a mut [u8]
|
|||
Message::Close => 3
|
||||
};
|
||||
header.crypto_method = crypto.method();
|
||||
if options.network_id.is_some() {
|
||||
header.flags |= 0x01;
|
||||
}
|
||||
start -= TopHeader::size();
|
||||
header.write_to(&mut buf[start..]);
|
||||
if crypto.method() > 0 {
|
||||
|
|
|
@ -12,7 +12,10 @@ Options:
|
|||
[default: 3210]
|
||||
-c <addr>, --connect <addr> Address of a peer to connect to.
|
||||
-s <addr>, --subnet <subnet> The local subnets to use.
|
||||
--magic <hex> Override the 4-byte magic header of each
|
||||
packet.
|
||||
--network-id <id> Optional token that identifies the network.
|
||||
(DEPRECATED)
|
||||
--shared-key <key> The shared key to encrypt all traffic.
|
||||
--crypto <method> The encryption method to use ("aes256", or
|
||||
"chacha20"). [default: chacha20]
|
||||
|
|
14
src/util.rs
14
src/util.rs
|
@ -58,6 +58,20 @@ impl Encoder {
|
|||
data[1] = (val & 0xff) as u8;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u32(data: &[u8]) -> u32 {
|
||||
((data[0] as u32) << 24) | ((data[1] as u32) << 16) |
|
||||
((data[2] as u32) << 8) | data[3] as u32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_u32(val: u32, data: &mut [u8]) {
|
||||
data[0] = ((val >> 24) & 0xff) as u8;
|
||||
data[1] = ((val >> 16) & 0xff) as u8;
|
||||
data[2] = ((val >> 8) & 0xff) as u8;
|
||||
data[3] = (val & 0xff) as u8;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u64(data: &[u8]) -> u64 {
|
||||
((data[0] as u64) << 56) | ((data[1] as u64) << 48) |
|
||||
|
|
59
vpncloud.md
59
vpncloud.md
|
@ -56,10 +56,13 @@ vpncloud(1) -- Peer-to-peer VPN
|
|||
computers lacking this support, only CHACHA20 is supported.
|
||||
[default: `chacha20`]
|
||||
|
||||
* `--network-id <id>`:
|
||||
* `--magic <id>`:
|
||||
|
||||
An optional token that identifies the network and helps to distinguish it
|
||||
from other networks.
|
||||
Override the 4-byte magic header of each packet. This header identifies the
|
||||
network and helps to distinguish it from other networks and other
|
||||
applications. The id can either be a 4 byte / 8 character hexadecimal
|
||||
string or an arbitrary string prefixed with "hash:" which will then be
|
||||
hashed into 4 bytes.
|
||||
|
||||
* `--peer-timeout <secs>`:
|
||||
|
||||
|
@ -129,8 +132,8 @@ and to allow the automatic cross-connect behavior. There are some important
|
|||
things to note:
|
||||
|
||||
- To avoid that different networks that reuse each others addresses merge due
|
||||
to the cross-connect behavior, the `network_id` option can be used and set
|
||||
to any unique string to identify the network. The `network_id` must be the
|
||||
to the cross-connect behavior, the `magic` option can be used and set
|
||||
to any unique string to identify the network. The `magic` must be the
|
||||
same on all nodes of the same VPN network.
|
||||
|
||||
- The cross-connect behavior can be able to connect nodes that are behind
|
||||
|
@ -203,7 +206,7 @@ vpncloud -t tun -c REMOTE_HOST:PORT --subnet 10.0.0.X/32 --ifup 'ifconfig $IFNAM
|
|||
The shared key is hashed using *ScryptSalsa208Sha256* to derive a key,
|
||||
which is used to encrypt the payload of messages using *ChaCha20Poly1305* or
|
||||
*AES256-GCM*. The encryption includes an authentication that also protects the
|
||||
header and all additional headers.
|
||||
header.
|
||||
This method does only protect against attacks on single messages but not
|
||||
against attacks that manipulate the message series itself (i.e. suppress
|
||||
messages, reorder them, or duplicate them).
|
||||
|
@ -216,21 +219,17 @@ implementations and to maximize the performance.
|
|||
|
||||
Every packet sent over UDP contains the following header (in order):
|
||||
|
||||
* 3 bytes `magic constant` = `[0x76, 0x70, 0x6e]` ("vpn")
|
||||
* 4 bytes `magic`
|
||||
|
||||
This field is used to identify the packet and to sort out packets that do
|
||||
not belong.
|
||||
|
||||
* 1 byte `version number` = 1 (currently)
|
||||
|
||||
This field specifies the version and helps nodes to parse the rest of the
|
||||
header and the packet.
|
||||
not belong. The default is `[0x76, 0x70, 0x6e, 0x01]` ("vpn\x01").
|
||||
This field can be used to identify VpnCloud packets and might be set to
|
||||
something different to hide the protocol.
|
||||
|
||||
* 1 byte `crypto method`
|
||||
|
||||
This field specifies the method that must be used to decrypt the rest of the
|
||||
data (additional headers and message contents). The currently supported
|
||||
methods are:
|
||||
data. The currently supported methods are:
|
||||
|
||||
- Method `0`, **No encryption**: Rest of the data can be read without
|
||||
decrypting it.
|
||||
|
@ -245,21 +244,12 @@ Every packet sent over UDP contains the following header (in order):
|
|||
`libsodium::crypto_aead_aes256gcm` method, using the 8 byte header
|
||||
as additional data.
|
||||
|
||||
* 1 `reserved byte` that is currently unused
|
||||
|
||||
* 1 byte for `flags`
|
||||
|
||||
This byte contains flags that specify the presence of additional headers.
|
||||
The flags are enumerated from bit 1 (least significant bit) to bit 8
|
||||
(most significant bit). The additional headers must be present in this same
|
||||
order. Currently the following additional headers are supported:
|
||||
|
||||
- Bit 1: Network ID
|
||||
* 2 `reserved bytes` that are currently unused
|
||||
|
||||
* 1 byte for the `message type`
|
||||
|
||||
This byte specifies the type of message that follows after all additional
|
||||
headers. Currently the following message types are supported:
|
||||
This byte specifies the type of message that follows. Currently the
|
||||
following message types are supported:
|
||||
|
||||
- Type 0: Data packet
|
||||
- Type 1: Peer list
|
||||
|
@ -269,21 +259,14 @@ Every packet sent over UDP contains the following header (in order):
|
|||
After this 8 byte header, the rest of the message follows. It is encrypted using
|
||||
the method specified in the header.
|
||||
|
||||
In the decrypted data, the additional headers as specified in the `flags` field
|
||||
will follow in the order of their respective flag bits.
|
||||
|
||||
* **Network ID**:
|
||||
|
||||
The network id is encoded as 8 bytes.
|
||||
|
||||
After the additional headers, the message as specified in the `message type`
|
||||
field will follow:
|
||||
In the decrypted data, the message as specified in the `message type` field
|
||||
will follow:
|
||||
|
||||
* **Data packet** (message type 0):
|
||||
This packet contains payload. The format of the data depends on the device
|
||||
type. For TUN devices, this data contains an IP packet. For TAP devices it
|
||||
contains an Ethernet frame. The data starts right after all additional
|
||||
headers and ends at the end of the packet.
|
||||
contains an Ethernet frame. The data starts right after the header and ends
|
||||
at the end of the packet.
|
||||
If it is an Ethernet frame, it will start with the destination MAC and end
|
||||
with the payload. It does not contain the preamble, SFD, padding, and CRC
|
||||
fields.
|
||||
|
|
Loading…
Reference in New Issue