diff options
Diffstat (limited to 'src/manager')
-rw-r--r-- | src/manager/builder.rs | 119 | ||||
-rw-r--r-- | src/manager/mod.rs | 46 |
2 files changed, 102 insertions, 63 deletions
diff --git a/src/manager/builder.rs b/src/manager/builder.rs index 3f3e227..e5fe2da 100644 --- a/src/manager/builder.rs +++ b/src/manager/builder.rs @@ -2,50 +2,56 @@ // // See COPYING. +use super::Source; use crate::{config, model, proto}; use std::collections::hash_map; use std::{error, fmt}; #[derive(Debug)] -pub struct ConfigError { - pub url: String, +pub struct Error { + pub src: String, pub peer: model::Key, - pub important: bool, + important: bool, err: &'static str, } -impl ConfigError { - fn new(err: &'static str, s: &config::Source, p: &proto::Peer, important: bool) -> Self { +impl Error { + fn new(err: &'static str, src: &Source, p: &proto::Peer, important: bool) -> Self { Self { - url: s.url.clone(), - peer: p.public_key.clone(), + src: src.name.clone(), + peer: p.public_key, important, err, } } + + #[inline] + pub fn important(&self) -> bool { + self.important + } } -impl error::Error for ConfigError {} -impl fmt::Display for ConfigError { +impl error::Error for Error {} +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "{} [{}] from [{}]: {}", + "{} [{}]/[{}]: {}", if self.important { "Invalid peer" } else { "Misconfigured peer" }, + self.src, self.peer, - self.url, self.err ) } } -pub struct ConfigBuilder<'a> { +pub(super) struct ConfigBuilder<'a> { c: model::Config, - err: Vec<ConfigError>, + err: Vec<Error>, public_key: model::Key, gc: &'a config::GlobalConfig, } @@ -54,7 +60,7 @@ impl<'a> ConfigBuilder<'a> { #[inline] pub fn new(public_key: model::Key, gc: &'a config::GlobalConfig) -> Self { Self { - c: model::Config::default(), + c: model::Config::empty(), err: vec![], public_key, gc, @@ -62,32 +68,48 @@ impl<'a> ConfigBuilder<'a> { } #[inline] - pub fn build(self) -> (model::Config, Vec<ConfigError>) { + pub fn build(self) -> (model::Config, Vec<Error>) { (self.c, self.err) } #[inline] - pub fn add_server(&mut self, s: &config::Source, p: &proto::Server) { + pub fn add_server(&mut self, src: &Source, p: &proto::Server) { + let gc = self.gc; + + let psk = match find_psk(gc, src, &p.peer) { + Ok(v) => v, + Err(e) => { + self.err.push(e); + return; + } + }; + if p.peer.public_key == self.public_key { return; } - let gc = self.gc; - let ent = insert_peer(&mut self.c, &mut self.err, s, &p.peer, |ent| { - ent.psk = s.psk.clone(); - ent.endpoint = Some(p.endpoint.clone()); + let ent = insert_peer(&mut self.c, &mut self.err, src, &p.peer, psk, |ent| { + ent.endpoint = Some(p.endpoint); ent.keepalive = gc.fix_keepalive(p.keepalive); }); - add_peer(&mut self.err, ent, s, &p.peer) + add_peer(&mut self.err, ent, src, &p.peer) } #[inline] - pub fn add_road_warrior(&mut self, s: &config::Source, p: &proto::RoadWarrior) { + pub fn add_road_warrior(&mut self, src: &Source, p: &proto::RoadWarrior) { + let psk = match find_psk(&self.gc, src, &p.peer) { + Ok(v) => v, + Err(e) => { + self.err.push(e); + return; + } + }; + if p.peer.public_key == self.public_key { - self.err.push(ConfigError::new( + self.err.push(Error::new( "The local peer cannot be a road warrior", - s, + src, &p.peer, true, )); @@ -95,35 +117,36 @@ impl<'a> ConfigBuilder<'a> { } let ent = if p.base == self.public_key { - insert_peer(&mut self.c, &mut self.err, s, &p.peer, |_| {}) + insert_peer(&mut self.c, &mut self.err, src, &p.peer, psk, |_| {}) } else if let Some(ent) = self.c.peers.get_mut(&p.base) { ent } else { self.err - .push(ConfigError::new("Unknown base peer", s, &p.peer, true)); + .push(Error::new("Unknown base peer", src, &p.peer, true)); return; }; - add_peer(&mut self.err, ent, s, &p.peer) + add_peer(&mut self.err, ent, src, &p.peer) } } #[inline] fn insert_peer<'b>( c: &'b mut model::Config, - err: &mut Vec<ConfigError>, - s: &config::Source, + err: &mut Vec<Error>, + src: &Source, p: &proto::Peer, + psk: Option<&model::Key>, update: impl for<'c> FnOnce(&'c mut model::Peer) -> (), ) -> &'b mut model::Peer { - match c.peers.entry(p.public_key.clone()) { + match c.peers.entry(p.public_key) { hash_map::Entry::Occupied(ent) => { - err.push(ConfigError::new("Duplicate public key", s, p, true)); + err.push(Error::new("Duplicate public key", src, p, true)); ent.into_mut() } hash_map::Entry::Vacant(ent) => { let ent = ent.insert(model::Peer { endpoint: None, - psk: None, + psk: psk.cloned(), keepalive: 0, ipv4: vec![], ipv6: vec![], @@ -134,17 +157,33 @@ fn insert_peer<'b>( } } -fn add_peer( - err: &mut Vec<ConfigError>, - ent: &mut model::Peer, - s: &config::Source, +fn find_psk<'a>( + gc: &'a config::GlobalConfig, + src: &'a Source, p: &proto::Peer, -) { +) -> Result<Option<&'a model::Key>, Error> { + let want = gc.peers.get(&p.public_key); + let want = if let Some(v) = want { + v + } else { + return Ok(None); + }; + + if let Some(ref want_src) = &want.source { + if *want_src != src.name { + return Err(Error::new("Peer source not allowed", src, p, true)); + } + } + + Ok(want.psk.as_ref().or_else(|| src.config.psk.as_ref())) +} + +fn add_peer(err: &mut Vec<Error>, ent: &mut model::Peer, src: &Source, p: &proto::Peer) { let mut added = false; let mut removed = false; for i in &p.ipv4 { - if s.ipv4.contains(i) { + if src.config.ipv4.contains(i) { ent.ipv4.push(*i); added = true; } else { @@ -152,7 +191,7 @@ fn add_peer( } } for i in &p.ipv6 { - if s.ipv6.contains(i) { + if src.config.ipv6.contains(i) { ent.ipv6.push(*i); added = true; } else { @@ -166,6 +205,6 @@ fn add_peer( } else { "All IPs removed" }; - err.push(ConfigError::new(msg, s, p, !added)); + err.push(Error::new(msg, src, p, !added)); } } diff --git a/src/manager/mod.rs b/src/manager/mod.rs index 680cbad..78d579f 100644 --- a/src/manager/mod.rs +++ b/src/manager/mod.rs @@ -3,26 +3,13 @@ // See COPYING. use crate::{config, model, proto, wg}; -use std::ffi::{OsString}; +use std::ffi::OsString; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; use std::path::{Path, PathBuf}; use std::time::{Duration, Instant, SystemTime}; use std::{fs, io}; -struct Source { - name: String, - config: config::Source, - data: proto::Source, - next_update: Instant, - backoff: Option<Duration>, -} - -mod builder; - -mod updater; -pub use updater::load_source; - fn update_file(path: &Path, data: &[u8]) -> io::Result<()> { let mut tmp_path = OsString::from(path); tmp_path.push(".tmp"); @@ -65,23 +52,36 @@ fn load_file(path: &Path) -> io::Result<Option<Vec<u8>>> { Ok(Some(data)) } +struct Source { + name: String, + config: config::Source, + data: proto::Source, + next_update: Instant, + backoff: Option<Duration>, +} + +mod updater; +pub use updater::load_source; + +mod builder; + pub struct Manager { dev: wg::Device, global_config: config::GlobalConfig, sources: Vec<Source>, current: model::Config, - runtime_directory: Option<PathBuf>, + state_directory: Option<PathBuf>, updater: updater::Updater, } impl Manager { pub fn new(ifname: OsString, c: config::Config) -> io::Result<Self> { let mut m = Self { - dev: wg::Device::new(ifname)?, + dev: wg::Device::open(ifname)?, global_config: c.global, sources: vec![], - current: model::Config::default(), - runtime_directory: c.runtime_directory, + current: model::Config::empty(), + state_directory: c.state_directory, updater: updater::Updater::new(c.updater), }; @@ -95,7 +95,7 @@ impl Manager { } fn state_path(&self) -> Option<PathBuf> { - let mut path = if let Some(ref path) = self.runtime_directory { + let mut path = if let Some(ref path) = self.state_directory { path.clone() } else { return None; @@ -191,7 +191,7 @@ impl Manager { &self, public_key: model::Key, ts: SystemTime, - ) -> (model::Config, Vec<builder::ConfigError>, SystemTime) { + ) -> (model::Config, Vec<builder::Error>, SystemTime) { let mut t_cfg = ts + Duration::from_secs(1 << 20); let mut sources: Vec<(&Source, &proto::SourceConfig)> = vec![]; for src in &self.sources { @@ -215,13 +215,13 @@ impl Manager { for (src, sc) in &sources { for peer in &sc.servers { - cfg.add_server(&src.config, peer); + cfg.add_server(src, peer); } } for (src, sc) in &sources { for peer in &sc.road_warriors { - cfg.add_road_warrior(&src.config, peer); + cfg.add_road_warrior(src, peer); } } @@ -259,7 +259,7 @@ impl Manager { if config != self.current { eprintln!("<5>Applying configuration update"); for err in &errors { - eprintln!("<{}>{}", if err.important { '4' } else { '5' }, err); + eprintln!("<{}>{}", if err.important() { '4' } else { '5' }, err); } self.dev.apply_diff(&self.current, &config)?; self.current_update(&config); |