From a0fa261a8a9542418072c42cfe73807d8d30cf88 Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Sun, 3 Nov 2019 18:33:49 +0100 Subject: Work on UAPI parser --- src/configuration/config.rs | 53 ++++++++------- src/configuration/error.rs | 28 ++++++++ src/configuration/mod.rs | 4 ++ src/configuration/uapi.rs | 161 ++++++++++++++++++++++++++++++++++++++++++++ src/wireguard/tests.rs | 4 +- src/wireguard/wireguard.rs | 22 +++--- 6 files changed, 231 insertions(+), 41 deletions(-) create mode 100644 src/configuration/error.rs create mode 100644 src/configuration/uapi.rs (limited to 'src') diff --git a/src/configuration/config.rs b/src/configuration/config.rs index d68084c..e3eb0af 100644 --- a/src/configuration/config.rs +++ b/src/configuration/config.rs @@ -1,5 +1,7 @@ use spin::Mutex; use std::net::{IpAddr, SocketAddr}; +use std::sync::atomic::Ordering; +use std::time::SystemTime; use x25519_dalek::{PublicKey, StaticSecret}; use super::*; @@ -11,12 +13,12 @@ use bind::Owner; /// Describes a snapshot of the state of a peer pub struct PeerState { - rx_bytes: u64, - tx_bytes: u64, - last_handshake_time_sec: u64, - last_handshake_time_nsec: u64, - public_key: PublicKey, - allowed_ips: Vec<(IpAddr, u32)>, + pub rx_bytes: u64, + pub tx_bytes: u64, + pub last_handshake_time_sec: u64, + pub last_handshake_time_nsec: u64, + pub public_key: PublicKey, + pub allowed_ips: Vec<(IpAddr, u32)>, } pub struct WireguardConfig { @@ -33,23 +35,6 @@ impl WireguardConfig { } } -pub enum ConfigError { - NoSuchPeer, - NotListening, - FailedToBind, -} - -impl ConfigError { - fn errno(&self) -> i32 { - // TODO: obtain the correct error values - match self { - ConfigError::NoSuchPeer => 1, - ConfigError::NotListening => 2, - ConfigError::FailedToBind => 3, - } - } -} - /// Exposed configuration interface pub trait Configuration { /// Updates the private key of the device @@ -244,7 +229,7 @@ impl Configuration for WireguardConfig { } fn add_peer(&self, peer: &PublicKey) -> bool { - self.wireguard.new_peer(*peer); + self.wireguard.add_peer(*peer); false } @@ -301,6 +286,24 @@ impl Configuration for WireguardConfig { } fn get_peers(&self) -> Vec { - vec![] + let peers = self.wireguard.list_peers(); + let mut state = Vec::with_capacity(peers.len()); + for p in peers { + // convert the system time to (secs, nano) since epoch + let last_handshake = (*p.walltime_last_handshake.lock()) + .duration_since(SystemTime::UNIX_EPOCH) + .expect("There should be no earlier time"); + + // extract state into PeerState + state.push(PeerState { + rx_bytes: p.rx_bytes.load(Ordering::Relaxed), + tx_bytes: p.tx_bytes.load(Ordering::Relaxed), + allowed_ips: p.router.list_allowed_ips(), + last_handshake_time_nsec: last_handshake.subsec_nanos() as u64, + last_handshake_time_sec: last_handshake.as_secs(), + public_key: p.pk, + }) + } + state } } diff --git a/src/configuration/error.rs b/src/configuration/error.rs new file mode 100644 index 0000000..74e2144 --- /dev/null +++ b/src/configuration/error.rs @@ -0,0 +1,28 @@ +pub enum ConfigError { + NoSuchPeer, + NotListening, + FailedToBind, + InvalidHexValue, + InvalidPortNumber, + InvalidFwmark, + InvalidKey, + UnsupportedValue, + UnsupportedProtocolVersion, +} + +impl ConfigError { + fn errno(&self) -> i32 { + // TODO: obtain the correct error values + match self { + ConfigError::NoSuchPeer => 1, + ConfigError::NotListening => 2, + ConfigError::FailedToBind => 3, + ConfigError::InvalidHexValue => 4, + ConfigError::InvalidPortNumber => 5, + ConfigError::InvalidFwmark => 6, + ConfigError::UnsupportedValue => 7, + ConfigError::InvalidKey => 8, + ConfigError::UnsupportedProtocolVersion => 9, + } + } +} diff --git a/src/configuration/mod.rs b/src/configuration/mod.rs index 83419bc..26f0c6e 100644 --- a/src/configuration/mod.rs +++ b/src/configuration/mod.rs @@ -1,8 +1,12 @@ mod config; +mod error; +mod uapi; use super::platform::Endpoint; use super::platform::{bind, tun}; use super::wireguard::Wireguard; +pub use error::ConfigError; + pub use config::Configuration; pub use config::WireguardConfig; diff --git a/src/configuration/uapi.rs b/src/configuration/uapi.rs new file mode 100644 index 0000000..d5b3262 --- /dev/null +++ b/src/configuration/uapi.rs @@ -0,0 +1,161 @@ +use hex::FromHex; +use x25519_dalek::{PublicKey, StaticSecret}; + +use super::{ConfigError, Configuration}; + +struct StreamPeer { + public_key: PublicKey, + update_only: bool, + added: bool, +} + +struct StreamParser { + config: C, + update_only: bool, + peer: Option, +} + +impl StreamParser { + fn parse_interface_line(&mut self, key: &str, value: &str) -> (bool, Option) { + let err = match key { + "private_key" => match <[u8; 32]>::from_hex(value) { + Ok(sk) => { + self.config.set_private_key(if sk == [0u8; 32] { + None + } else { + Some(StaticSecret::from(sk)) + }); + None + } + Err(_) => Some(ConfigError::InvalidHexValue), + }, + "listen_port" => match value.parse() { + Ok(port) => { + self.config.set_listen_port(Some(port)); + None + } + Err(_) => Some(ConfigError::InvalidPortNumber), + }, + "fwmark" => match value.parse() { + Ok(fwmark) => { + self.config + .set_fwmark(if fwmark == 0 { None } else { Some(fwmark) }); + None + } + Err(_) => Some(ConfigError::InvalidFwmark), + }, + "replace_peers" => match value { + "true" => { + for p in self.config.get_peers() { + self.config.remove_peer(&p.public_key) + } + None + } + _ => Some(ConfigError::UnsupportedValue), + }, + + // transition to peer configuration + "public_key" => { + return (true, None); + } + + // unknown key + _ => Some(ConfigError::InvalidKey), + }; + (false, err) + } + + fn parse_peer_line(&mut self, key: &str, value: &str) -> Option { + // add a p + let mut flush_peer = || match self.peer.as_mut() { + None => (), + Some(peer) => { + if !peer.added { + peer.added = true; + if !peer.update_only { + self.config.add_peer(&peer.public_key); + } + } + } + }; + + match key { + // new peer + "public_key" => { + // add previous peer + flush_peer(); + + // create state for new peer + match <[u8; 32]>::from_hex(value) { + Ok(pk) => { + self.peer = Some(StreamPeer { + public_key: PublicKey::from(pk), + update_only: false, + added: false, + }); + None + } + Err(_) => Some(ConfigError::InvalidHexValue), + } + } + + "remove" => { + let peer = self.peer.as_ref().unwrap(); + self.config.remove_peer(&peer.public_key); + None + } + + "update_only" => { + let peer = self.peer.as_mut().unwrap(); + peer.update_only = true; + None + } + + "preshared_key" => { + // add peer (if not exists) + let peer = self.peer.as_mut().unwrap(); + if !peer.added && !peer.update_only { + self.config.add_peer(&peer.public_key); + peer.added = true; + } + + // set preshared key + match <[u8; 32]>::from_hex(value) { + Ok(psk) => { + self.config.set_preshared_key( + &peer.public_key, + if psk == [0u8; 32] { None } else { Some(psk) }, + ); + None + } + Err(_) => Some(ConfigError::InvalidHexValue), + } + } + + "endpoint" => None, + + "persistent_keepalive_interval" => None, + + "replace_allowed_ips" => None, + + "allowed_ip" => None, + + // set protocol version of peer + "protocol_version" => { + let parse_res: Result = value.parse(); + match parse_res { + Ok(version) => { + if version == 0 || version > self.config.get_protocol_version() { + Some(ConfigError::UnsupportedProtocolVersion) + } else { + None + } + } + Err(_) => Some(ConfigError::UnsupportedProtocolVersion), + } + } + // unknown key + _ => Some(ConfigError::InvalidKey), + } + } +} diff --git a/src/wireguard/tests.rs b/src/wireguard/tests.rs index 6a02e1f..83ef594 100644 --- a/src/wireguard/tests.rs +++ b/src/wireguard/tests.rs @@ -120,8 +120,8 @@ fn test_pure_wireguard() { let pk2 = PublicKey::from(&sk2); - wg1.new_peer(pk2); - wg2.new_peer(pk1); + wg1.add_peer(pk2); + wg2.add_peer(pk1); wg1.set_key(Some(sk1)); wg2.set_key(Some(sk2)); diff --git a/src/wireguard/wireguard.rs b/src/wireguard/wireguard.rs index 78f24da..6cdae6c 100644 --- a/src/wireguard/wireguard.rs +++ b/src/wireguard/wireguard.rs @@ -148,16 +148,6 @@ impl PeerInner { self.queue.lock().send(HandshakeJob::New(self.pk)).unwrap(); } } - - pub fn set_persistent_keepalive_interval(&self, interval: usize) { - self.timers().send_persistent_keepalive.stop(); - self.keepalive.store(interval, Ordering::SeqCst); - if interval > 0 { - self.timers() - .send_persistent_keepalive - .start(Duration::from_secs(internal as u64)); - } - } } struct Handshake { @@ -260,7 +250,11 @@ impl Wireguard { self.state.handshake.write().device.set_psk(pk, psk).is_ok() } - pub fn new_peer(&self, pk: PublicKey) { + pub fn add_peer(&self, pk: PublicKey) { + if self.state.peers.read().contains_key(pk.as_bytes()) { + return; + } + let mut rng = OsRng::new().unwrap(); let state = Arc::new(PeerInner { id: rng.gen(), @@ -278,9 +272,6 @@ impl Wireguard { // create a router peer let router = Arc::new(self.state.router.new_peer(state.clone())); - // add to the handshake device - self.state.handshake.write().device.add(pk).unwrap(); // TODO: handle adding of public key for interface - // form WireGuard peer let peer = Peer { router, state }; @@ -295,6 +286,9 @@ impl Wireguard { // finally, add the peer to the wireguard device let mut peers = self.state.peers.write(); peers.entry(*pk.as_bytes()).or_insert(peer); + + // add to the handshake device + self.state.handshake.write().device.add(pk).unwrap(); // TODO: handle adding of public key for interface } /* Begin consuming messages from the reader. -- cgit v1.2.3-59-g8ed1b