From e97eb737a4a933563cfd5017f4dccd2dbd59dfd5 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Thu, 5 Aug 2021 09:38:14 +0900 Subject: [PATCH] shared(PeerDiff): refactor struct and update peer endpoints only when handshake failed The past behavior of clients was to, on every fetch from the server, update each of its peer's endpoints with the one reported from the server. While this wasn't a problem on certain types of NATs to help with holepunching, in some situations it caused previously working connections to no longer work (when one peer had a port-restricted or symmetric cone type NAT). --- Cargo.lock | 54 ++++- client/src/main.rs | 123 ++++++------ shared/Cargo.toml | 2 +- shared/src/types.rs | 301 ++++++++++++++++++++-------- shared/src/wg.rs | 1 + wgctrl-rs/src/backends/kernel.rs | 1 - wgctrl-rs/src/backends/userspace.rs | 1 - wgctrl-rs/src/config.rs | 7 +- wgctrl-rs/src/device.rs | 11 +- wgctrl-sys/Cargo.toml | 2 +- 10 files changed, 348 insertions(+), 155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b4e3ea..71b8636 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bindgen" -version = "0.58.1" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f" +checksum = "453c49e5950bb0eb63bb3df640e31618846c89d5b7faa54040d76e98e0134375" dependencies = [ "bitflags", "cexpr", @@ -85,6 +85,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitvec" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -105,9 +117,9 @@ checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" [[package]] name = "cexpr" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +checksum = "db507a7679252d2276ed0dd8113c6875ec56d3089f9225b2b42c30cc1f8e5c89" dependencies = [ "nom", ] @@ -277,6 +289,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "futures-channel" version = "0.3.16" @@ -613,9 +631,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c5374735aa0cd07cb7fd820b656062b187b5588d79517f72956b57c6de9ef" +checksum = "f48ea34ea0678719815c3753155067212f853ad2d8ef4a49167bae7f7c254188" dependencies = [ "libc", "log", @@ -623,10 +641,12 @@ dependencies = [ [[package]] name = "nom" -version = "5.1.2" +version = "6.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" dependencies = [ + "bitvec", + "funty", "memchr", "version_check", ] @@ -785,6 +805,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + [[package]] name = "rand" version = "0.8.4" @@ -1064,6 +1090,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.2.0" @@ -1370,6 +1402,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "x25519-dalek" version = "1.1.0" diff --git a/client/src/main.rs b/client/src/main.rs index 4434321..9b7880d 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -6,14 +6,14 @@ use indoc::eprintdoc; use shared::{ interface_config::InterfaceConfig, prompts, AddAssociationOpts, AddCidrOpts, AddPeerOpts, Association, AssociationContents, Cidr, CidrTree, DeleteCidrOpts, EndpointContents, - InstallOpts, Interface, IoErrorContext, NetworkOpt, Peer, RedeemContents, RenamePeerOpts, - State, WrappedIoError, CLIENT_CONFIG_DIR, REDEEM_TRANSITION_WAIT, + InstallOpts, Interface, IoErrorContext, NetworkOpt, Peer, PeerDiff, RedeemContents, + RenamePeerOpts, State, WrappedIoError, CLIENT_CONFIG_DIR, REDEEM_TRANSITION_WAIT, }; use std::{ fmt, io, path::{Path, PathBuf}, thread, - time::{Duration, SystemTime}, + time::Duration, }; use structopt::{clap::AppSettings, StructOpt}; use wgctrl::{Device, DeviceUpdate, InterfaceName, PeerConfigBuilder, PeerInfo}; @@ -446,10 +446,9 @@ fn fetch( network: NetworkOpt, ) -> Result<(), Error> { let config = InterfaceConfig::from_interface(interface)?; - let interface_up = if let Ok(interfaces) = Device::list(network.backend) { - interfaces.iter().any(|name| name == interface) - } else { - false + let interface_up = match Device::list(network.backend) { + Ok(interfaces) => interfaces.iter().any(|name| name == interface), + _ => false, }; if !interface_up { @@ -493,61 +492,68 @@ fn fetch( .unwrap_or_default(); let existing_peers = &device_info.peers; - let peer_configs_diff = peers - .iter() - .filter(|peer| !peer.is_disabled && peer.public_key != interface_public_key) - .filter_map(|peer| { + // Match existing peers (by pubkey) to new peer information from the server. + let modifications = peers.iter().filter_map(|peer| { + if peer.is_disabled || peer.public_key == interface_public_key { + None + } else { let existing_peer = existing_peers .iter() .find(|p| p.config.public_key.to_base64() == peer.public_key); + PeerDiff::new(existing_peer, Some(peer)).unwrap() + } + }); - let change = match existing_peer { - Some(existing_peer) => peer.diff(&existing_peer.config).map(|diff| { - if let Some(endpoint) = diff.endpoint { - log::debug!(" Peer endpoint changed: {:?}", endpoint); - } - (PeerConfigBuilder::from(&diff), peer, "modified".normal()) - }), - None => Some((PeerConfigBuilder::from(peer), peer, "added".green())), + // Remove any peers on the interface that aren't in the server's peer list any more. + let removals = existing_peers.iter().filter_map(|existing| { + let public_key = existing.config.public_key.to_base64(); + if peers.iter().any(|p| p.public_key == public_key) { + None + } else { + PeerDiff::new(Some(&existing), None).unwrap() + } + }); + + let updates = modifications + .chain(removals) + .inspect(|diff| { + let public_key = diff.public_key().to_base64(); + + let text = match (diff.old, diff.new) { + (None, Some(_)) => "added".green(), + (Some(_), Some(_)) => "modified".yellow(), + (Some(_), None) => "removed".red(), + _ => unreachable!("PeerDiff can't be None -> None"), }; - change.map(|(builder, peer, text)| { - println!( - " peer {} ({}...) was {}.", - peer.name.yellow(), - &peer.public_key[..10].dimmed(), - text - ); - builder - }) - }) - .collect::>(); + // Grab the peer name from either the new data, or the historical data (if the peer is removed). + let peer_hostname = match diff.new { + Some(peer) => Some(peer.name.clone()), + _ => store + .peers() + .iter() + .find(|p| p.public_key == public_key) + .map(|p| p.name.clone()), + }; + let peer_name = peer_hostname.as_deref().unwrap_or("[unknown]"); - let mut device_config_builder = DeviceUpdate::new(); - let mut device_config_changed = false; - - if !peer_configs_diff.is_empty() { - device_config_builder = device_config_builder.add_peers(&peer_configs_diff); - device_config_changed = true; - } - - for peer in existing_peers { - let public_key = peer.config.public_key.to_base64(); - if !peers.iter().any(|p| p.public_key == public_key) { - println!( - " peer ({}...) was {}.", - &public_key[..10].yellow(), - "removed".red() + log::info!( + " peer {} ({}...) was {}.", + peer_name.yellow(), + &public_key[..10].dimmed(), + text ); - device_config_builder = - device_config_builder.remove_peer_by_key(&peer.config.public_key); - device_config_changed = true; - } - } + for change in diff.changes() { + log::debug!(" {}", change); + } + }) + .map(PeerConfigBuilder::from) + .collect::>(); - if device_config_changed { - device_config_builder + if !updates.is_empty() { + DeviceUpdate::new() + .add_peers(&updates) .apply(interface, network.backend) .with_str(interface.to_string())?; @@ -986,17 +992,16 @@ fn print_peer(peer: &PeerState, short: bool, level: usize) { let pad = level * 2; let PeerState { peer, info } = peer; if short { - let last_handshake = info - .and_then(|i| i.stats.last_handshake_time) - .and_then(|t| t.elapsed().ok()) - .unwrap_or_else(|| SystemTime::UNIX_EPOCH.elapsed().unwrap()); - - let online = last_handshake <= Duration::from_secs(180) || info.is_none(); + let connected = PeerDiff::peer_recently_connected(info); println_pad!( pad, "| {} {}: {} ({}{}…)", - if online { "◉".bold() } else { "◯".dimmed() }, + if connected { + "◉".bold() + } else { + "◯".dimmed() + }, peer.ip.to_string().yellow().bold(), peer.name.yellow(), if info.is_none() { "you, " } else { "" }, diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 0025e64..84048fa 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -25,7 +25,7 @@ url = "2" wgctrl = { path = "../wgctrl-rs" } [target.'cfg(target_os = "linux")'.dependencies] -netlink-sys = "0.6" +netlink-sys = "0.7" netlink-packet-core = "0.2" netlink-packet-route = "0.7" wgctrl-sys = { path = "../wgctrl-sys" } diff --git a/shared/src/types.rs b/shared/src/types.rs index 789675a..41bae3c 100644 --- a/shared/src/types.rs +++ b/shared/src/types.rs @@ -1,3 +1,4 @@ +use anyhow::{anyhow, Error}; use ipnetwork::IpNetwork; use lazy_static::lazy_static; use regex::Regex; @@ -14,7 +15,10 @@ use std::{ }; use structopt::StructOpt; use url::Host; -use wgctrl::{Backend, InterfaceName, InvalidInterfaceName, Key, PeerConfig, PeerConfigBuilder}; +use wgctrl::{ + AllowedIp, Backend, InterfaceName, InvalidInterfaceName, Key, PeerConfig, PeerConfigBuilder, + PeerInfo, +}; #[derive(Debug, Clone)] pub struct Interface { @@ -113,7 +117,7 @@ impl<'de> Deserialize<'de> for Endpoint { } } -impl fmt::Display for Endpoint { +impl Display for Endpoint { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.host.fmt(f)?; f.write_str(":")?; @@ -428,96 +432,179 @@ impl Display for Peer { } } -#[derive(Debug, PartialEq)] -pub struct PeerDiff { - pub public_key: String, - pub endpoint: Option, - pub persistent_keepalive_interval: Option, - pub is_disabled: bool, +#[derive(Clone, Debug, PartialEq)] +pub struct ChangeString { + name: &'static str, + old: Option, + new: Option, } -impl Peer { - pub fn diff(&self, peer: &PeerConfig) -> Option { - assert_eq!(self.public_key, peer.public_key.to_base64()); +impl Display for ChangeString { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "{}: {} => {}", + self.name, + self.old.as_deref().unwrap_or("[none]"), + self.new.as_deref().unwrap_or("[none]") + ) + } +} - let endpoint_diff = if let Some(ref endpoint) = self.endpoint { - match endpoint.resolve() { - Ok(resolved) if Some(resolved) != peer.endpoint => Some(resolved), - _ => None, - } - } else { - None +impl ChangeString { + pub fn new(name: &'static str, old: Option, new: Option) -> Self + where + T: fmt::Debug, + U: fmt::Debug, + { + Self { + name, + old: old.map(|t| format!("{:?}", t)), + new: new.map(|t| format!("{:?}", t)), + } + } +} + +/// Encompasses the logic for comparing the peer configuration currently on the WireGuard interface +/// to a (potentially) more current peer configuration from the innernet server. +#[derive(Clone, Debug, PartialEq)] +pub struct PeerDiff<'a> { + pub old: Option<&'a PeerConfig>, + pub new: Option<&'a Peer>, + builder: PeerConfigBuilder, + changes: Vec, +} + +impl<'a> PeerDiff<'a> { + pub fn new( + old_info: Option<&'a PeerInfo>, + new: Option<&'a Peer>, + ) -> Result, Error> { + let old = old_info.map(|p| &p.config); + match (old_info, new) { + (Some(old), Some(new)) if old.config.public_key.to_base64() != new.public_key => Err( + anyhow!("old and new peer configs have different public keys"), + ), + (None, None) => Ok(None), + _ => Ok( + Self::peer_config_builder(old_info, new).map(|(builder, changes)| Self { + old, + new, + builder, + changes, + }), + ), + } + } + + /// WireGuard rejects any communication after REJECT_AFTER_TIME, so we can use this + /// as a heuristic for "currentness" without relying on heavier things like ICMP. + pub fn peer_recently_connected(peer: &Option<&PeerInfo>) -> bool { + const REJECT_AFTER_TIME: u64 = 180; + + let last_handshake = peer + .and_then(|p| p.stats.last_handshake_time) + .and_then(|t| t.elapsed().ok()) + .unwrap_or_else(|| SystemTime::UNIX_EPOCH.elapsed().unwrap()); + + last_handshake <= Duration::from_secs(REJECT_AFTER_TIME) + } + + pub fn public_key(&self) -> &Key { + &self.builder.public_key() + } + + pub fn changes(&self) -> &[ChangeString] { + &self.changes + } + + fn peer_config_builder( + old_info: Option<&PeerInfo>, + new: Option<&Peer>, + ) -> Option<(PeerConfigBuilder, Vec)> { + let old = old_info.map(|p| &p.config); + let public_key = match (old, new) { + (Some(old), _) => old.public_key.clone(), + (_, Some(new)) => Key::from_base64(&new.public_key).unwrap(), + _ => return None, }; + let mut builder = PeerConfigBuilder::new(&public_key); + let mut changes = vec![]; - let keepalive_diff = - if peer.persistent_keepalive_interval != self.persistent_keepalive_interval { - self.persistent_keepalive_interval - } else { - None + // Remove peer from interface if they're deleted or disabled, and we can return early. + if new.is_none() || matches!(new, Some(new) if new.is_disabled) { + return Some((builder.remove(), changes)); + } + // diff.new is now guaranteed to be a Some(_) variant. + let new = new.unwrap(); + + // TODO(jake): use contains() when stable: https://github.com/rust-lang/rust/issues/62358 + + let new_allowed_ips = &[AllowedIp { + address: new.ip, + cidr: if new.ip.is_ipv4() { 32 } else { 128 }, + }]; + if old.is_none() || matches!(old, Some(old) if old.allowed_ips != new_allowed_ips) { + builder = builder + .replace_allowed_ips() + .add_allowed_ips(new_allowed_ips); + changes.push(ChangeString::new( + "AllowedIPs", + old.map(|o| &o.allowed_ips[..]), + Some(&new_allowed_ips[0]), + )); + } + + if old.is_none() + || matches!(old, Some(old) if old.persistent_keepalive_interval != new.persistent_keepalive_interval) + { + builder = match new.persistent_keepalive_interval { + Some(interval) => builder.set_persistent_keepalive_interval(interval), + None => builder.unset_persistent_keepalive(), }; + changes.push(ChangeString::new( + "PersistentKeepalive", + old.and_then(|p| p.persistent_keepalive_interval), + new.persistent_keepalive_interval, + )); + } - if endpoint_diff.is_none() && keepalive_diff.is_none() { - None + // We won't update the endpoint if there's already a stable connection. + if !Self::peer_recently_connected(&old_info) { + let resolved = new.endpoint.as_ref().and_then(|e| e.resolve().ok()); + if let Some(addr) = resolved { + if old.is_none() || matches!(old, Some(old) if old.endpoint != resolved) { + builder = builder.set_endpoint(addr); + changes.push(ChangeString::new( + "Endpoint", + old.and_then(|p| p.endpoint), + Some(addr), + )); + } + } + } + if !changes.is_empty() { + Some((builder, changes)) } else { - Some(PeerDiff { - public_key: self.public_key.clone(), - endpoint: endpoint_diff, - persistent_keepalive_interval: keepalive_diff, - is_disabled: self.is_disabled, - }) + None } } } impl<'a> From<&'a Peer> for PeerConfigBuilder { fn from(peer: &Peer) -> Self { - let builder = PeerConfigBuilder::new(&Key::from_base64(&peer.public_key).unwrap()) - .replace_allowed_ips() - .add_allowed_ip(peer.ip, if peer.ip.is_ipv4() { 32 } else { 128 }); - - let builder = if peer.is_disabled { - builder.remove() - } else { - builder - }; - - let builder = if let Some(interval) = peer.persistent_keepalive_interval { - builder.set_persistent_keepalive_interval(interval) - } else { - builder - }; - - let resolved = peer.endpoint.as_ref().map(|e| e.resolve().ok()).flatten(); - - if let Some(endpoint) = resolved { - builder.set_endpoint(endpoint) - } else { - builder - } + PeerDiff::new(None, Some(peer)) + .expect("No Err on explicitly set peer data") + .expect("None -> Some(peer) will always create a PeerDiff") + .into() } } -impl<'a> From<&'a PeerDiff> for PeerConfigBuilder { - fn from(peer: &PeerDiff) -> Self { - let builder = PeerConfigBuilder::new(&Key::from_base64(&peer.public_key).unwrap()); - - let builder = if peer.is_disabled { - builder.remove() - } else { - builder - }; - - let builder = if let Some(interval) = peer.persistent_keepalive_interval { - builder.set_persistent_keepalive_interval(interval) - } else { - builder - }; - - if let Some(endpoint) = peer.endpoint { - builder.set_endpoint(endpoint) - } else { - builder - } +impl<'a> From> for PeerConfigBuilder { + /// Turn a PeerDiff into a minimal set of instructions to update the WireGuard interface, + /// hopefully minimizing dropped packets and other interruptions. + fn from(diff: PeerDiff) -> Self { + diff.builder } } @@ -646,7 +733,7 @@ pub struct WrappedIoError { context: String, } -impl std::fmt::Display for WrappedIoError { +impl Display for WrappedIoError { fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { write!(f, "{} - {}", self.context, self.io_error) } @@ -666,7 +753,7 @@ impl std::error::Error for WrappedIoError {} mod tests { use super::*; use std::net::IpAddr; - use wgctrl::{Key, PeerConfigBuilder}; + use wgctrl::{Key, PeerConfigBuilder, PeerStats}; #[test] fn test_peer_no_diff() { @@ -691,8 +778,15 @@ mod tests { PeerConfigBuilder::new(&Key::from_base64(PUBKEY).unwrap()).add_allowed_ip(ip, 32); let config = builder.into_peer_config(); + let info = PeerInfo { + config, + stats: Default::default(), + }; - assert_eq!(peer.diff(&config), None); + let diff = PeerDiff::new(Some(&info), Some(&peer)).unwrap(); + + println!("{:?}", diff); + assert_eq!(diff, None); } #[test] @@ -718,9 +812,56 @@ mod tests { PeerConfigBuilder::new(&Key::from_base64(PUBKEY).unwrap()).add_allowed_ip(ip, 32); let config = builder.into_peer_config(); + let info = PeerInfo { + config, + stats: Default::default(), + }; + let diff = PeerDiff::new(Some(&info), Some(&peer)).unwrap(); println!("{:?}", peer); - println!("{:?}", config); - assert!(matches!(peer.diff(&config), Some(_))); + println!("{:?}", info.config); + assert!(matches!(diff, Some(_))); + } + + #[test] + fn test_peer_diff_handshake_time() { + const PUBKEY: &str = "4CNZorWVtohO64n6AAaH/JyFjIIgBFrfJK2SGtKjzEE="; + let ip: IpAddr = "10.0.0.1".parse().unwrap(); + let peer = Peer { + id: 1, + contents: PeerContents { + name: "peer1".parse().unwrap(), + ip, + cidr_id: 1, + public_key: PUBKEY.to_owned(), + endpoint: Some("1.1.1.1:1111".parse().unwrap()), + persistent_keepalive_interval: None, + is_admin: false, + is_disabled: false, + is_redeemed: true, + invite_expires: None, + }, + }; + let builder = + PeerConfigBuilder::new(&Key::from_base64(PUBKEY).unwrap()).add_allowed_ip(ip, 32); + + let config = builder.into_peer_config(); + let mut info = PeerInfo { + config, + stats: PeerStats { + last_handshake_time: Some(SystemTime::now() - Duration::from_secs(200)), + ..Default::default() + }, + }; + + // If there hasn't been a recent handshake, endpoint should be being set. + assert!(matches!( + PeerDiff::new(Some(&info), Some(&peer)), + Ok(Some(_)) + )); + + // If there *has* been a recent handshake, endpoint should *not* be being set. + info.stats.last_handshake_time = Some(SystemTime::now()); + assert!(matches!(PeerDiff::new(Some(&info), Some(&peer)), Ok(None))); } } diff --git a/shared/src/wg.rs b/shared/src/wg.rs index 428df7a..521b783 100644 --- a/shared/src/wg.rs +++ b/shared/src/wg.rs @@ -85,6 +85,7 @@ pub fn up( ) })?) .add_allowed_ip(address, prefix) + .set_persistent_keepalive_interval(25) .set_endpoint(endpoint); device = device.add_peer(peer_config); } diff --git a/wgctrl-rs/src/backends/kernel.rs b/wgctrl-rs/src/backends/kernel.rs index f5a0703..740961c 100644 --- a/wgctrl-rs/src/backends/kernel.rs +++ b/wgctrl-rs/src/backends/kernel.rs @@ -58,7 +58,6 @@ impl<'a> From<&'a wgctrl_sys::wg_peer> for PeerInfo { }, rx_bytes: raw.rx_bytes, tx_bytes: raw.tx_bytes, - __cant_construct_me: (), }, } } diff --git a/wgctrl-rs/src/backends/userspace.rs b/wgctrl-rs/src/backends/userspace.rs index dbb8e51..5792d3f 100644 --- a/wgctrl-rs/src/backends/userspace.rs +++ b/wgctrl-rs/src/backends/userspace.rs @@ -94,7 +94,6 @@ fn new_peer_info(public_key: Key) -> PeerInfo { last_handshake_time: None, rx_bytes: 0, tx_bytes: 0, - __cant_construct_me: (), }, } } diff --git a/wgctrl-rs/src/config.rs b/wgctrl-rs/src/config.rs index ac5f439..85d8178 100644 --- a/wgctrl-rs/src/config.rs +++ b/wgctrl-rs/src/config.rs @@ -73,6 +73,11 @@ impl PeerConfigBuilder { } } + /// The public key used in this builder. + pub fn public_key(&self) -> &Key { + &self.public_key + } + /// Creates a `PeerConfigBuilder` from a [`PeerConfig`](PeerConfig). /// /// This is mostly a convenience method for cases when you want to copy @@ -120,7 +125,7 @@ impl PeerConfigBuilder { } /// Specifies that this peer does not require keepalive packets. - pub fn disable_persistent_keepalive(self) -> Self { + pub fn unset_persistent_keepalive(self) -> Self { self.set_persistent_keepalive_interval(0) } diff --git a/wgctrl-rs/src/device.rs b/wgctrl-rs/src/device.rs index c9c0128..721dce1 100644 --- a/wgctrl-rs/src/device.rs +++ b/wgctrl-rs/src/device.rs @@ -12,7 +12,7 @@ use std::{ }; /// Represents an IP address a peer is allowed to have, in CIDR notation. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone)] pub struct AllowedIp { /// The IP address. pub address: IpAddr, @@ -20,6 +20,12 @@ pub struct AllowedIp { pub cidr: u8, } +impl fmt::Debug for AllowedIp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}/{}", self.address, self.cidr) + } +} + impl std::str::FromStr for AllowedIp { type Err = (); @@ -58,7 +64,7 @@ pub struct PeerConfig { /// /// These are the attributes that will change over time; to update them, /// re-read the information from the interface. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct PeerStats { /// Time of the last handshake/rekey with this peer. pub last_handshake_time: Option, @@ -66,7 +72,6 @@ pub struct PeerStats { pub rx_bytes: u64, /// Number of bytes transmitted to this peer. pub tx_bytes: u64, - pub(crate) __cant_construct_me: (), } /// Represents the complete status of a peer. diff --git a/wgctrl-sys/Cargo.toml b/wgctrl-sys/Cargo.toml index b510c88..0f66cea 100644 --- a/wgctrl-sys/Cargo.toml +++ b/wgctrl-sys/Cargo.toml @@ -13,5 +13,5 @@ version = "1.4.1" libc = "0.2" [build-dependencies] -bindgen = { version = "0.58", default-features = false } +bindgen = { version = "0", default-features = false } cc = "1.0"