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
|
### 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)
|
### v0.7.0 (2016-08-05)
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,10 @@ use std::str::FromStr;
|
||||||
use std::net::{UdpSocket, ToSocketAddrs, Ipv4Addr, SocketAddr, SocketAddrV4};
|
use std::net::{UdpSocket, ToSocketAddrs, Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
|
use super::MAGIC;
|
||||||
use super::cloud::GenericCloud;
|
use super::cloud::GenericCloud;
|
||||||
use super::device::{Device, Type};
|
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::crypto::{Crypto, CryptoMethod};
|
||||||
use super::ethernet::{Frame, SwitchTable};
|
use super::ethernet::{Frame, SwitchTable};
|
||||||
use super::types::{Address, Table, Protocol};
|
use super::types::{Address, Table, Protocol};
|
||||||
|
@ -51,27 +52,25 @@ fn crypto_aes256(b: &mut Bencher) {
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn message_encode(b: &mut Bencher) {
|
fn message_encode(b: &mut Bencher) {
|
||||||
let mut options = Options::default();
|
|
||||||
let mut crypto = Crypto::None;
|
let mut crypto = Crypto::None;
|
||||||
let mut payload = [0; 1600];
|
let mut payload = [0; 1600];
|
||||||
let mut msg = Message::Data(&mut payload, 64, 1464);
|
let mut msg = Message::Data(&mut payload, 64, 1464);
|
||||||
let mut buf = [0; 1600];
|
let mut buf = [0; 1600];
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
encode(&mut options, &mut msg, &mut buf[..], &mut crypto);
|
encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
||||||
});
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn message_decode(b: &mut Bencher) {
|
fn message_decode(b: &mut Bencher) {
|
||||||
let mut options = Options::default();
|
|
||||||
let mut crypto = Crypto::None;
|
let mut crypto = Crypto::None;
|
||||||
let mut payload = [0; 1600];
|
let mut payload = [0; 1600];
|
||||||
let mut msg = Message::Data(&mut payload, 64, 1464);
|
let mut msg = Message::Data(&mut payload, 64, 1464);
|
||||||
let mut buf = [0; 1600];
|
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(|| {
|
b.iter(|| {
|
||||||
decode(&mut res, &mut crypto).unwrap();
|
decode(&mut res, MAGIC, &mut crypto).unwrap();
|
||||||
});
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
@ -152,7 +151,7 @@ fn epoll_wait(b: &mut Bencher) {
|
||||||
#[bench]
|
#[bench]
|
||||||
fn handle_interface_data(b: &mut Bencher) {
|
fn handle_interface_data(b: &mut Bencher) {
|
||||||
let mut node = GenericCloud::<Frame>::new(
|
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
|
Box::new(SwitchTable::new(300)), 1800, true, true, vec![], Crypto::None
|
||||||
);
|
);
|
||||||
let mut data = [0; 1500];
|
let mut data = [0; 1500];
|
||||||
|
@ -166,14 +165,14 @@ fn handle_interface_data(b: &mut Bencher) {
|
||||||
#[bench]
|
#[bench]
|
||||||
fn handle_net_message(b: &mut Bencher) {
|
fn handle_net_message(b: &mut Bencher) {
|
||||||
let mut node = GenericCloud::<Frame>::new(
|
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
|
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 addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1));
|
||||||
let mut data = [0; 1500];
|
let mut data = [0; 1500];
|
||||||
data[105] = 45;
|
data[105] = 45;
|
||||||
b.iter(|| {
|
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;
|
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 rand::{random, sample, thread_rng};
|
||||||
use net2::UdpBuilder;
|
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::device::Device;
|
||||||
use super::udpmessage::{encode, decode, Options, Message};
|
use super::udpmessage::{encode, decode, Message};
|
||||||
use super::crypto::Crypto;
|
use super::crypto::Crypto;
|
||||||
use super::util::{now, Time, Duration, resolve};
|
use super::util::{now, Time, Duration, resolve};
|
||||||
use super::poll::{self, Poll};
|
use super::poll::{self, Poll};
|
||||||
|
@ -153,6 +153,7 @@ pub struct ReconnectEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GenericCloud<P: Protocol> {
|
pub struct GenericCloud<P: Protocol> {
|
||||||
|
magic: HeaderMagic,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
peers: PeerList,
|
peers: PeerList,
|
||||||
addresses: Vec<Range>,
|
addresses: Vec<Range>,
|
||||||
|
@ -164,7 +165,6 @@ pub struct GenericCloud<P: Protocol> {
|
||||||
socket4: UdpSocket,
|
socket4: UdpSocket,
|
||||||
socket6: UdpSocket,
|
socket6: UdpSocket,
|
||||||
device: Device,
|
device: Device,
|
||||||
options: Options,
|
|
||||||
crypto: Crypto,
|
crypto: Crypto,
|
||||||
next_peerlist: Time,
|
next_peerlist: Time,
|
||||||
update_freq: Duration,
|
update_freq: Duration,
|
||||||
|
@ -176,7 +176,7 @@ pub struct GenericCloud<P: Protocol> {
|
||||||
impl<P: Protocol> GenericCloud<P> {
|
impl<P: Protocol> GenericCloud<P> {
|
||||||
#[allow(unknown_lints)]
|
#[allow(unknown_lints)]
|
||||||
#[allow(too_many_arguments)]
|
#[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>,
|
peer_timeout: Duration, learning: bool, broadcast: bool, addresses: Vec<Range>,
|
||||||
crypto: Crypto) -> Self {
|
crypto: Crypto) -> Self {
|
||||||
let socket4 = match UdpBuilder::new_v4().expect("Failed to obtain ipv4 socket builder")
|
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,
|
Ok(socket) => socket,
|
||||||
Err(err) => fail!("Failed to open ipv6 address ::{}: {}", listen, err)
|
Err(err) => fail!("Failed to open ipv6 address ::{}: {}", listen, err)
|
||||||
};
|
};
|
||||||
let mut options = Options::default();
|
|
||||||
options.network_id = network_id;
|
|
||||||
GenericCloud{
|
GenericCloud{
|
||||||
|
magic: magic,
|
||||||
node_id: random(),
|
node_id: random(),
|
||||||
peers: PeerList::new(peer_timeout),
|
peers: PeerList::new(peer_timeout),
|
||||||
addresses: addresses,
|
addresses: addresses,
|
||||||
|
@ -204,7 +203,6 @@ impl<P: Protocol> GenericCloud<P> {
|
||||||
socket4: socket4,
|
socket4: socket4,
|
||||||
socket6: socket6,
|
socket6: socket6,
|
||||||
device: device,
|
device: device,
|
||||||
options: options,
|
|
||||||
crypto: crypto,
|
crypto: crypto,
|
||||||
next_peerlist: now(),
|
next_peerlist: now(),
|
||||||
update_freq: peer_timeout/2-60,
|
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> {
|
fn broadcast_msg(&mut self, msg: &mut Message) -> Result<(), Error> {
|
||||||
debug!("Broadcasting {:?}", msg);
|
debug!("Broadcasting {:?}", msg);
|
||||||
// Encrypt and encode once and send several times
|
// 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() {
|
for addr in self.peers.as_vec() {
|
||||||
let socket = match addr {
|
let socket = match addr {
|
||||||
SocketAddr::V4(_) => &self.socket4,
|
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> {
|
fn send_msg(&mut self, addr: SocketAddr, msg: &mut Message) -> Result<(), Error> {
|
||||||
debug!("Sending {:?} to {}", msg, addr);
|
debug!("Sending {:?} to {}", msg, addr);
|
||||||
// Encrypt and encode
|
// 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 {
|
let socket = match addr {
|
||||||
SocketAddr::V4(_) => &self.socket4,
|
SocketAddr::V4(_) => &self.socket4,
|
||||||
SocketAddr::V6(_) => &self.socket6
|
SocketAddr::V6(_) => &self.socket6
|
||||||
|
@ -456,11 +454,7 @@ impl<P: Protocol> GenericCloud<P> {
|
||||||
/// Handles a message received from the network
|
/// Handles a message received from the network
|
||||||
///
|
///
|
||||||
/// This method handles messages from the network, i.e. from peers. `peer` contains the sender
|
/// 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
|
/// of the message and `msg` contains the message.
|
||||||
/// message.
|
|
||||||
///
|
|
||||||
/// If the `network_id` in the messages options differs from the `network_id` of this node,
|
|
||||||
/// the message is simply ignored.
|
|
||||||
///
|
///
|
||||||
/// Then this method will check the message type and will handle each message type differently.
|
/// 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
|
/// # `Message::Close` message
|
||||||
/// If this message is received, the sender is removed from the peer list and its claimed
|
/// If this message is received, the sender is removed from the peer list and its claimed
|
||||||
/// addresses are removed from the table.
|
/// addresses are removed from the table.
|
||||||
pub fn handle_net_message(&mut self, peer: SocketAddr, options: Options, msg: Message) -> Result<(), Error> {
|
pub fn handle_net_message(&mut self, peer: SocketAddr, 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));
|
|
||||||
}
|
|
||||||
debug!("Received {:?} from {}", msg, peer);
|
debug!("Received {:?} from {}", msg, peer);
|
||||||
match msg {
|
match msg {
|
||||||
Message::Data(payload, start, end) => {
|
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: {}"),
|
fd if fd == socket6_fd => try_fail!(self.socket6.recv_from(&mut buffer), "Failed to read from ipv6 network socket: {}"),
|
||||||
_ => unreachable!()
|
_ => 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);
|
error!("Error: {}, from: {}", e, src);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -39,11 +39,14 @@ use std::process::Command;
|
||||||
use device::{Device, Type};
|
use device::{Device, Type};
|
||||||
use ethernet::SwitchTable;
|
use ethernet::SwitchTable;
|
||||||
use ip::RoutingTable;
|
use ip::RoutingTable;
|
||||||
use types::{Mode, Range, Table, Protocol};
|
use types::{Mode, Range, Table, Protocol, HeaderMagic};
|
||||||
use cloud::GenericCloud;
|
use cloud::GenericCloud;
|
||||||
use udpmessage::VERSION;
|
|
||||||
use crypto::{Crypto, CryptoMethod};
|
use crypto::{Crypto, CryptoMethod};
|
||||||
use util::Duration;
|
use util::{Duration, Encoder};
|
||||||
|
|
||||||
|
|
||||||
|
const VERSION: u8 = 1;
|
||||||
|
const MAGIC: HeaderMagic = *b"vpn\x01";
|
||||||
|
|
||||||
|
|
||||||
struct SimpleLogger;
|
struct SimpleLogger;
|
||||||
|
@ -74,6 +77,7 @@ struct Args {
|
||||||
flag_device: String,
|
flag_device: String,
|
||||||
flag_listen: u16,
|
flag_listen: u16,
|
||||||
flag_network_id: Option<String>,
|
flag_network_id: Option<String>,
|
||||||
|
flag_magic: Option<String>,
|
||||||
flag_connect: Vec<String>,
|
flag_connect: Vec<String>,
|
||||||
flag_peer_timeout: Duration,
|
flag_peer_timeout: Duration,
|
||||||
flag_dst_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),
|
let device = try_fail!(Device::new(&args.flag_device, args.flag_type),
|
||||||
"Failed to open virtual {} interface {}: {}", args.flag_type, &args.flag_device);
|
"Failed to open virtual {} interface {}: {}", args.flag_type, &args.flag_device);
|
||||||
info!("Opened device {}", device.ifname());
|
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::Switch => (true, true, Box::new(SwitchTable::new(dst_timeout))),
|
||||||
Mode::Hub => (false, 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 {
|
||||||
let mut s = SipHasher::new();
|
warn!("The --network-id argument is deprecated, please use --magic instead.");
|
||||||
name.hash(&mut s);
|
if args.flag_magic.is_none() {
|
||||||
s.finish()
|
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[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();
|
Crypto::init();
|
||||||
let crypto = match args.flag_shared_key {
|
let crypto = match args.flag_shared_key {
|
||||||
Some(key) => Crypto::from_shared_key(args.flag_crypto, &key),
|
Some(key) => Crypto::from_shared_key(args.flag_crypto, &key),
|
||||||
None => Crypto::None
|
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 {
|
if let Some(script) = args.flag_ifup {
|
||||||
run_script(script, cloud.ifname());
|
run_script(script, cloud.ifname());
|
||||||
}
|
}
|
||||||
|
|
75
src/tests.rs
75
src/tests.rs
|
@ -5,10 +5,11 @@
|
||||||
use std::net::{ToSocketAddrs, SocketAddr};
|
use std::net::{ToSocketAddrs, SocketAddr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use super::MAGIC;
|
||||||
use super::ethernet::{Frame, SwitchTable};
|
use super::ethernet::{Frame, SwitchTable};
|
||||||
use super::ip::{RoutingTable, Packet};
|
use super::ip::{RoutingTable, Packet};
|
||||||
use super::types::{Protocol, Address, Range, Table};
|
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};
|
use super::crypto::{Crypto, CryptoMethod};
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +35,6 @@ impl<'a> PartialEq for Message<'a> {
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
fn udpmessage_packet() {
|
fn udpmessage_packet() {
|
||||||
let mut options = Options::default();
|
|
||||||
let mut crypto = Crypto::None;
|
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,
|
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,
|
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 buf = [0; 1024];
|
||||||
let mut len = 0;
|
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.len(), 13);
|
||||||
assert_eq!(&res[..8], &[118,112,110,1,0,0,0,0]);
|
assert_eq!(&res[..8], &[118,112,110,1,0,0,0,0]);
|
||||||
for i in 0..res.len() {
|
for i in 0..res.len() {
|
||||||
|
@ -53,15 +53,13 @@ fn udpmessage_packet() {
|
||||||
}
|
}
|
||||||
len = res.len();
|
len = res.len();
|
||||||
}
|
}
|
||||||
let (options2, msg2) = decode(&mut buf[..len], &mut crypto).unwrap();
|
let msg2 = decode(&mut buf[..len], MAGIC, &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
|
||||||
assert_eq!(msg, msg2);
|
assert_eq!(msg, msg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
fn udpmessage_encrypted() {
|
fn udpmessage_encrypted() {
|
||||||
let mut options = Options::default();
|
|
||||||
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
|
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,
|
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,
|
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 buf = [0; 1024];
|
||||||
let mut len = 0;
|
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.len(), 41);
|
||||||
assert_eq!(&res[..8], &[118,112,110,1,1,0,0,0]);
|
assert_eq!(&res[..8], &[118,112,110,1,1,0,0,0]);
|
||||||
for i in 0..res.len() {
|
for i in 0..res.len() {
|
||||||
|
@ -85,62 +83,40 @@ fn udpmessage_encrypted() {
|
||||||
}
|
}
|
||||||
len = res.len();
|
len = res.len();
|
||||||
}
|
}
|
||||||
let (options2, msg2) = decode(&mut buf[..len], &mut crypto).unwrap();
|
let msg2 = decode(&mut buf[..len], MAGIC, &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
|
||||||
assert_eq!(orig_msg, msg2);
|
assert_eq!(orig_msg, msg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn udpmessage_peers() {
|
fn udpmessage_peers() {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
let mut options = Options::default();
|
|
||||||
let mut crypto = Crypto::None;
|
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 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,
|
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];
|
8,9,10,11,12,13,14,15,26,133];
|
||||||
{
|
{
|
||||||
let mut buf = [0; 1024];
|
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);
|
assert_eq!(res.len(), 40);
|
||||||
for i in 0..res.len() {
|
for i in 0..res.len() {
|
||||||
assert_eq!(res[i], should[i]);
|
assert_eq!(res[i], should[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (options2, msg2) = decode(&mut should, &mut crypto).unwrap();
|
let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
|
||||||
assert_eq!(msg, msg2);
|
assert_eq!(msg, msg2);
|
||||||
// Missing IPv4 count
|
// 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
|
// 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
|
// 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
|
// 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());
|
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_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn udpmessage_init() {
|
fn udpmessage_init() {
|
||||||
use super::types::Address;
|
use super::types::Address;
|
||||||
let mut options = Options::default();
|
|
||||||
let mut crypto = Crypto::None;
|
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},
|
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}];
|
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 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 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);
|
assert_eq!(res.len(), 40);
|
||||||
for i in 0..res.len() {
|
for i in 0..res.len() {
|
||||||
assert_eq!(res[i], should[i]);
|
assert_eq!(res[i], should[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (options2, msg2) = decode(&mut should, &mut crypto).unwrap();
|
let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
|
||||||
assert_eq!(msg, msg2);
|
assert_eq!(msg, msg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn udpmessage_close() {
|
fn udpmessage_close() {
|
||||||
let mut options = Options::default();
|
|
||||||
let mut crypto = Crypto::None;
|
let mut crypto = Crypto::None;
|
||||||
let mut msg = Message::Close;
|
let mut msg = Message::Close;
|
||||||
let mut should = [118,112,110,1,0,0,0,3];
|
let mut should = [118,112,110,1,0,0,0,3];
|
||||||
{
|
{
|
||||||
let mut buf = [0; 1024];
|
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.len(), 8);
|
||||||
assert_eq!(&res, &should);
|
assert_eq!(&res, &should);
|
||||||
}
|
}
|
||||||
let (options2, msg2) = decode(&mut should, &mut crypto).unwrap();
|
let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap();
|
||||||
assert_eq!(options, options2);
|
|
||||||
assert_eq!(msg, msg2);
|
assert_eq!(msg, msg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn udpmessage_invalid() {
|
fn udpmessage_invalid() {
|
||||||
let mut crypto = Crypto::None;
|
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
|
// too short
|
||||||
assert!(decode(&mut [], &mut crypto).is_err());
|
assert!(decode(&mut [], MAGIC, &mut crypto).is_err());
|
||||||
// invalid protocol
|
// 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
|
// 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
|
// 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
|
// invalid msg type
|
||||||
assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0xaa], &mut crypto).is_err());
|
assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0xaa], MAGIC, &mut crypto).is_err());
|
||||||
// truncated options
|
|
||||||
assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,1,0], &mut crypto).is_err());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn udpmessage_invalid_crypto() {
|
fn udpmessage_invalid_crypto() {
|
||||||
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
|
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
|
||||||
// truncated crypto
|
// 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 const NODE_ID_BYTES: usize = 16;
|
||||||
|
|
||||||
pub type NetworkId = u64;
|
pub type HeaderMagic = [u8; 4];
|
||||||
pub type NodeId = [u8; NODE_ID_BYTES];
|
pub type NodeId = [u8; NODE_ID_BYTES];
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ pub trait Protocol: Sized {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Parse(&'static str),
|
Parse(&'static str),
|
||||||
WrongNetwork(Option<NetworkId>),
|
WrongHeaderMagic(HeaderMagic),
|
||||||
Socket(&'static str, io::Error),
|
Socket(&'static str, io::Error),
|
||||||
Name(String),
|
Name(String),
|
||||||
TunTapDev(&'static str, io::Error),
|
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::TunTapDev(ref msg, ref err) => write!(formatter, "{}: {:?}", msg, err),
|
||||||
Error::Crypto(ref msg) => write!(formatter, "{}", msg),
|
Error::Crypto(ref msg) => write!(formatter, "{}", msg),
|
||||||
Error::Name(ref name) => write!(formatter, "failed to resolve name '{}'", name),
|
Error::Name(ref name) => write!(formatter, "failed to resolve name '{}'", name),
|
||||||
Error::WrongNetwork(Some(net)) => write!(formatter, "wrong network id: {}", net),
|
Error::WrongHeaderMagic(net) => write!(formatter, "wrong header magic: {}", bytes_to_hex(&net)),
|
||||||
Error::WrongNetwork(None) => write!(formatter, "wrong network id: none"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,23 +5,17 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr};
|
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::util::{bytes_to_hex, Encoder};
|
||||||
use super::crypto::Crypto;
|
use super::crypto::Crypto;
|
||||||
|
|
||||||
const MAGIC: [u8; 3] = *b"vpn";
|
#[derive(Clone, Copy, Default)]
|
||||||
pub const VERSION: u8 = 1;
|
|
||||||
|
|
||||||
const NETWORK_ID_BYTES: usize = 8;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct TopHeader {
|
struct TopHeader {
|
||||||
magic: [u8; 3],
|
magic: HeaderMagic,
|
||||||
version: u8,
|
|
||||||
crypto_method : u8,
|
crypto_method : u8,
|
||||||
_reserved: u8,
|
_reserved1: u8,
|
||||||
flags: u8,
|
_reserved2: u8,
|
||||||
msgtype: u8
|
msgtype: u8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,35 +30,20 @@ impl TopHeader {
|
||||||
return Err(Error::Parse("Empty message"));
|
return Err(Error::Parse("Empty message"));
|
||||||
}
|
}
|
||||||
let mut header = TopHeader::default();
|
let mut header = TopHeader::default();
|
||||||
header.magic.copy_from_slice(&data[0..3]);
|
header.magic.copy_from_slice(&data[0..4]);
|
||||||
header.version = data[3];
|
|
||||||
header.crypto_method = data[4];
|
header.crypto_method = data[4];
|
||||||
header.flags = data[6];
|
|
||||||
header.msgtype = data[7];
|
header.msgtype = data[7];
|
||||||
Ok((header, TopHeader::size()))
|
Ok((header, TopHeader::size()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to(&self, data: &mut [u8]) -> usize {
|
pub fn write_to(&self, data: &mut [u8]) -> usize {
|
||||||
data[0..3].copy_from_slice(&self.magic);
|
data[0..4].copy_from_slice(&self.magic);
|
||||||
data[3] = self.version;
|
|
||||||
data[4] = self.crypto_method;
|
data[4] = self.crypto_method;
|
||||||
data[6] = self.flags;
|
|
||||||
data[7] = self.msgtype;
|
data[7] = self.msgtype;
|
||||||
TopHeader::size()
|
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> {
|
pub enum Message<'a> {
|
||||||
Data(&'a mut[u8], usize, usize), // data, start, end
|
Data(&'a mut[u8], usize, usize), // data, start, end
|
||||||
Peers(Vec<SocketAddr>), // peers
|
Peers(Vec<SocketAddr>), // peers
|
||||||
|
@ -96,14 +75,11 @@ impl<'a> fmt::Debug for Message<'a> {
|
||||||
|
|
||||||
#[allow(unknown_lints)]
|
#[allow(unknown_lints)]
|
||||||
#[allow(needless_range_loop)]
|
#[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 mut end = data.len();
|
||||||
let (header, mut pos) = try!(TopHeader::read_from(&data[..end]));
|
let (header, mut pos) = try!(TopHeader::read_from(&data[..end]));
|
||||||
if header.magic != MAGIC {
|
if header.magic != magic {
|
||||||
return Err(Error::Parse("Wrong protocol"));
|
return Err(Error::WrongHeaderMagic(header.magic));
|
||||||
}
|
|
||||||
if header.version != VERSION {
|
|
||||||
return Err(Error::Parse("Wrong version"));
|
|
||||||
}
|
}
|
||||||
if header.crypto_method != crypto.method() {
|
if header.crypto_method != crypto.method() {
|
||||||
return Err(Error::Crypto("Wrong 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());
|
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 {
|
let msg = match header.msgtype {
|
||||||
0 => Message::Data(data, pos, end),
|
0 => Message::Data(data, pos, end),
|
||||||
1 => {
|
1 => {
|
||||||
|
@ -195,12 +163,12 @@ pub fn decode<'a>(data: &'a mut [u8], crypto: &mut Crypto) -> Result<(Options, M
|
||||||
3 => Message::Close,
|
3 => Message::Close,
|
||||||
_ => return Err(Error::Parse("Unknown message type"))
|
_ => return Err(Error::Parse("Unknown message type"))
|
||||||
};
|
};
|
||||||
Ok((options, msg))
|
Ok(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unknown_lints)]
|
#[allow(unknown_lints)]
|
||||||
#[allow(needless_range_loop)]
|
#[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 start = 64;
|
||||||
let mut end = 64;
|
let mut end = 64;
|
||||||
match *msg {
|
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!(start >= 64);
|
||||||
assert!(buf.len() >= end + 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;
|
let crypto_start = start;
|
||||||
start -= crypto.nonce_bytes();
|
start -= crypto.nonce_bytes();
|
||||||
let mut header = TopHeader::default();
|
let mut header = TopHeader::default();
|
||||||
|
header.magic = magic;
|
||||||
header.msgtype = match *msg {
|
header.msgtype = match *msg {
|
||||||
Message::Data(_, _, _) => 0,
|
Message::Data(_, _, _) => 0,
|
||||||
Message::Peers(_) => 1,
|
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
|
Message::Close => 3
|
||||||
};
|
};
|
||||||
header.crypto_method = crypto.method();
|
header.crypto_method = crypto.method();
|
||||||
if options.network_id.is_some() {
|
|
||||||
header.flags |= 0x01;
|
|
||||||
}
|
|
||||||
start -= TopHeader::size();
|
start -= TopHeader::size();
|
||||||
header.write_to(&mut buf[start..]);
|
header.write_to(&mut buf[start..]);
|
||||||
if crypto.method() > 0 {
|
if crypto.method() > 0 {
|
||||||
|
|
|
@ -12,7 +12,10 @@ Options:
|
||||||
[default: 3210]
|
[default: 3210]
|
||||||
-c <addr>, --connect <addr> Address of a peer to connect to.
|
-c <addr>, --connect <addr> Address of a peer to connect to.
|
||||||
-s <addr>, --subnet <subnet> The local subnets to use.
|
-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.
|
--network-id <id> Optional token that identifies the network.
|
||||||
|
(DEPRECATED)
|
||||||
--shared-key <key> The shared key to encrypt all traffic.
|
--shared-key <key> The shared key to encrypt all traffic.
|
||||||
--crypto <method> The encryption method to use ("aes256", or
|
--crypto <method> The encryption method to use ("aes256", or
|
||||||
"chacha20"). [default: chacha20]
|
"chacha20"). [default: chacha20]
|
||||||
|
|
14
src/util.rs
14
src/util.rs
|
@ -58,6 +58,20 @@ impl Encoder {
|
||||||
data[1] = (val & 0xff) as u8;
|
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]
|
#[inline]
|
||||||
pub fn read_u64(data: &[u8]) -> u64 {
|
pub fn read_u64(data: &[u8]) -> u64 {
|
||||||
((data[0] as u64) << 56) | ((data[1] as u64) << 48) |
|
((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.
|
computers lacking this support, only CHACHA20 is supported.
|
||||||
[default: `chacha20`]
|
[default: `chacha20`]
|
||||||
|
|
||||||
* `--network-id <id>`:
|
* `--magic <id>`:
|
||||||
|
|
||||||
An optional token that identifies the network and helps to distinguish it
|
Override the 4-byte magic header of each packet. This header identifies the
|
||||||
from other networks.
|
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>`:
|
* `--peer-timeout <secs>`:
|
||||||
|
|
||||||
|
@ -129,8 +132,8 @@ and to allow the automatic cross-connect behavior. There are some important
|
||||||
things to note:
|
things to note:
|
||||||
|
|
||||||
- To avoid that different networks that reuse each others addresses merge due
|
- 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 the cross-connect behavior, the `magic` option can be used and set
|
||||||
to any unique string to identify the network. The `network_id` must be the
|
to any unique string to identify the network. The `magic` must be the
|
||||||
same on all nodes of the same VPN network.
|
same on all nodes of the same VPN network.
|
||||||
|
|
||||||
- The cross-connect behavior can be able to connect nodes that are behind
|
- 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,
|
The shared key is hashed using *ScryptSalsa208Sha256* to derive a key,
|
||||||
which is used to encrypt the payload of messages using *ChaCha20Poly1305* or
|
which is used to encrypt the payload of messages using *ChaCha20Poly1305* or
|
||||||
*AES256-GCM*. The encryption includes an authentication that also protects the
|
*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
|
This method does only protect against attacks on single messages but not
|
||||||
against attacks that manipulate the message series itself (i.e. suppress
|
against attacks that manipulate the message series itself (i.e. suppress
|
||||||
messages, reorder them, or duplicate them).
|
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):
|
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
|
This field is used to identify the packet and to sort out packets that do
|
||||||
not belong.
|
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
|
||||||
* 1 byte `version number` = 1 (currently)
|
something different to hide the protocol.
|
||||||
|
|
||||||
This field specifies the version and helps nodes to parse the rest of the
|
|
||||||
header and the packet.
|
|
||||||
|
|
||||||
* 1 byte `crypto method`
|
* 1 byte `crypto method`
|
||||||
|
|
||||||
This field specifies the method that must be used to decrypt the rest of the
|
This field specifies the method that must be used to decrypt the rest of the
|
||||||
data (additional headers and message contents). The currently supported
|
data. The currently supported methods are:
|
||||||
methods are:
|
|
||||||
|
|
||||||
- Method `0`, **No encryption**: Rest of the data can be read without
|
- Method `0`, **No encryption**: Rest of the data can be read without
|
||||||
decrypting it.
|
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
|
`libsodium::crypto_aead_aes256gcm` method, using the 8 byte header
|
||||||
as additional data.
|
as additional data.
|
||||||
|
|
||||||
* 1 `reserved byte` that is currently unused
|
* 2 `reserved bytes` that are 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
|
|
||||||
|
|
||||||
* 1 byte for the `message type`
|
* 1 byte for the `message type`
|
||||||
|
|
||||||
This byte specifies the type of message that follows after all additional
|
This byte specifies the type of message that follows. Currently the
|
||||||
headers. Currently the following message types are supported:
|
following message types are supported:
|
||||||
|
|
||||||
- Type 0: Data packet
|
- Type 0: Data packet
|
||||||
- Type 1: Peer list
|
- 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
|
After this 8 byte header, the rest of the message follows. It is encrypted using
|
||||||
the method specified in the header.
|
the method specified in the header.
|
||||||
|
|
||||||
In the decrypted data, the additional headers as specified in the `flags` field
|
In the decrypted data, the message as specified in the `message type` field
|
||||||
will follow in the order of their respective flag bits.
|
will follow:
|
||||||
|
|
||||||
* **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:
|
|
||||||
|
|
||||||
* **Data packet** (message type 0):
|
* **Data packet** (message type 0):
|
||||||
This packet contains payload. The format of the data depends on the device
|
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
|
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
|
contains an Ethernet frame. The data starts right after the header and ends
|
||||||
headers and ends at the end of the packet.
|
at the end of the packet.
|
||||||
If it is an Ethernet frame, it will start with the destination MAC and end
|
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
|
with the payload. It does not contain the preamble, SFD, padding, and CRC
|
||||||
fields.
|
fields.
|
||||||
|
|
Loading…
Reference in New Issue