aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHristo Venev <hristo@venev.name>2019-09-28 16:21:28 +0000
committerHristo Venev <hristo@venev.name>2019-09-28 16:22:07 +0000
commit8e6318f29eb022126e82e0c084262c77d9ee3300 (patch)
tree0a6d8a88e1e1f96daff61e1d19b144f93aa8f256
parent3a5552509439612accf579becc7cb85a596bd466 (diff)
Implement peer overrides.
-rw-r--r--src/config.rs12
-rw-r--r--src/main.rs4
-rw-r--r--src/manager/builder.rs119
-rw-r--r--src/manager/mod.rs46
-rw-r--r--src/model.rs11
-rw-r--r--src/wg.rs4
6 files changed, 119 insertions, 77 deletions
diff --git a/src/config.rs b/src/config.rs
index ccdb042..08db0eb 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -20,11 +20,20 @@ pub struct Source {
#[serde(deny_unknown_fields)]
#[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq, Eq, Debug)]
+pub struct Peer {
+ pub source: Option<String>,
+ pub psk: Option<Key>,
+}
+
+#[serde(deny_unknown_fields)]
+#[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct GlobalConfig {
#[serde(default = "default_min_keepalive")]
pub min_keepalive: u32,
#[serde(default = "default_max_keepalive")]
pub max_keepalive: u32,
+ #[serde(rename = "peer")]
+ pub peers: HashMap<Key, Peer>,
}
impl Default for GlobalConfig {
@@ -33,6 +42,7 @@ impl Default for GlobalConfig {
Self {
min_keepalive: default_min_keepalive(),
max_keepalive: default_max_keepalive(),
+ peers: HashMap::new(),
}
}
}
@@ -72,7 +82,7 @@ impl Default for UpdaterConfig {
#[serde(deny_unknown_fields)]
#[derive(serde_derive::Serialize, serde_derive::Deserialize, Default, Clone, Debug)]
pub struct Config {
- pub runtime_directory: Option<PathBuf>,
+ pub state_directory: Option<PathBuf>,
#[serde(flatten)]
pub global: GlobalConfig,
diff --git a/src/main.rs b/src/main.rs
index 438cb3c..b6b5ce1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -188,12 +188,12 @@ fn run_with_cmdline(argv0: &str, args: &mut impl Iterator<Item = OsString>) -> i
fn run_daemon(ifname: OsString, mut config: config::Config) -> i32 {
maybe_get_var(&mut config.updater.cache_directory, "CACHE_DIRECTORY");
- maybe_get_var(&mut config.runtime_directory, "RUNTIME_DIRECTORY");
+ maybe_get_var(&mut config.state_directory, "RUNTIME_DIRECTORY");
let mut m = match manager::Manager::new(ifname, config) {
Ok(m) => m,
Err(e) => {
- eprintln!("<1>Failed to open device: {}", e);
+ eprintln!("<1>Failed to start: {}", e);
return 1;
}
};
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);
diff --git a/src/model.rs b/src/model.rs
index 51386a5..90f9b44 100644
--- a/src/model.rs
+++ b/src/model.rs
@@ -15,7 +15,7 @@ pub use ip::*;
pub type KeyParseError = base64::DecodeError;
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Key([u8; 32]);
impl Key {
@@ -82,7 +82,7 @@ impl<'de> serde::Deserialize<'de> for Key {
}
}
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Endpoint {
address: Ipv6Addr,
port: u16,
@@ -195,13 +195,6 @@ pub struct Config {
pub peers: HashMap<Key, Peer>,
}
-impl Default for Config {
- #[inline]
- fn default() -> Self {
- Self::empty()
- }
-}
-
impl Config {
#[inline]
pub fn empty() -> Self {
diff --git a/src/wg.rs b/src/wg.rs
index c3eeb57..7565a02 100644
--- a/src/wg.rs
+++ b/src/wg.rs
@@ -13,7 +13,7 @@ pub struct Device {
impl Device {
#[inline]
- pub fn new(ifname: OsString) -> io::Result<Self> {
+ pub fn open(ifname: OsString) -> io::Result<Self> {
let dev = Self { ifname };
let _ = dev.get_public_key()?;
Ok(dev)
@@ -67,7 +67,7 @@ impl Device {
if *old_peer == *conf {
continue;
}
- old_endpoint = old_peer.endpoint.clone();
+ old_endpoint = old_peer.endpoint;
} else {
old_endpoint = None;
}