From a0a5cb22ecf4c1760a3eadaea5ed2c0a55aa62c7 Mon Sep 17 00:00:00 2001 From: diogo464 Date: Tue, 17 Feb 2026 14:47:15 +0000 Subject: add wireguard-show binary and optional handshake timestamps --- Cargo.toml | 4 +++ bin/wireguard-show.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/view.rs | 33 ++++++++++++++------- 3 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 bin/wireguard-show.rs diff --git a/Cargo.toml b/Cargo.toml index aea7700..c3035c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,7 @@ rtnetlink = "=0.20.0" serde = "1.0.195" tokio = { version = "1.35.1", features = ["full"] } tracing = "0.1.40" + +[[bin]] +name = "wireguard-show" +path = "bin/wireguard-show.rs" diff --git a/bin/wireguard-show.rs b/bin/wireguard-show.rs new file mode 100644 index 0000000..17476d6 --- /dev/null +++ b/bin/wireguard-show.rs @@ -0,0 +1,80 @@ +use anyhow::Result; +use wireguard::WireGuard; + +#[tokio::main] +async fn main() -> Result<()> { + let mut wireguard = WireGuard::new().await?; + let devices = wireguard.view_devices().await?; + + if devices.is_empty() { + println!("no wireguard devices found"); + return Ok(()); + } + + for device in devices { + println!("device: {}", device.name); + println!(" ifindex: {}", device.ifindex); + println!(" private_key: {}", format_optional_key(device.private_key)); + println!(" public_key: {}", format_optional_key(device.public_key)); + println!(" listen_port: {}", device.listen_port); + println!(" fwmark: {}", device.fwmark); + println!(" peers: {}", device.peers.len()); + + for (index, peer) in device.peers.iter().enumerate() { + println!(" peer {}:", index + 1); + println!(" public_key: {}", peer.public_key); + println!( + " preshared_key: {}", + format_optional_key(peer.preshared_key) + ); + println!( + " endpoint: {}", + peer.endpoint + .map(|endpoint| endpoint.to_string()) + .unwrap_or_else(|| "none".to_string()) + ); + println!( + " persistent_keepalive: {}", + peer.persistent_keepalive + .map(|keepalive| keepalive.to_string()) + .unwrap_or_else(|| "none".to_string()) + ); + println!( + " last_handshake: {}", + format_duration(peer.last_handshake) + ); + println!(" rx_bytes: {}", peer.rx_bytes); + println!(" tx_bytes: {}", peer.tx_bytes); + if peer.allowed_ips.is_empty() { + println!(" allowed_ips: none"); + } else { + let allowed_ips = peer + .allowed_ips + .iter() + .map(|ip| ip.to_string()) + .collect::>() + .join(", "); + println!(" allowed_ips: {}", allowed_ips); + } + } + + println!(); + } + + Ok(()) +} + +fn format_optional_key(key: Option) -> String { + key.map(|value| value.to_string()) + .unwrap_or_else(|| "none".to_string()) +} + +fn format_duration(time: Option) -> String { + match time { + Some(time) => match time.duration_since(std::time::UNIX_EPOCH) { + Ok(duration) => format!("{}.{}", duration.as_secs(), duration.subsec_nanos()), + Err(_) => "before-epoch".to_string(), + }, + None => "none".to_string(), + } +} diff --git a/src/view.rs b/src/view.rs index c0bd807..666f5d4 100644 --- a/src/view.rs +++ b/src/view.rs @@ -28,7 +28,7 @@ pub struct PeerView { pub preshared_key: Option, pub endpoint: Option, pub persistent_keepalive: Option, - pub last_handshake: SystemTime, + pub last_handshake: Option, pub rx_bytes: u64, pub tx_bytes: u64, pub allowed_ips: Vec, @@ -103,20 +103,33 @@ fn peer_from_wg_peer(wg_peer: WireguardPeer) -> Result { } } + let last_handshake = last_handshake.ok_or_else(|| Error::message("missing last_handshake"))?; + let rx_bytes = rx_bytes.ok_or_else(|| Error::message("missing rx_bytes"))?; + let tx_bytes = tx_bytes.ok_or_else(|| Error::message("missing tx_bytes"))?; + + let handshake_is_zero = last_handshake.seconds == 0 && last_handshake.nano_seconds == 0; + let last_handshake = if handshake_is_zero && (rx_bytes == 0 || tx_bytes == 0) { + None + } else { + let duration = Duration::new( + last_handshake.seconds as u64, + last_handshake.nano_seconds as u32, + ); + Some( + SystemTime::UNIX_EPOCH + .checked_add(duration) + .ok_or_else(|| Error::message("invalid last_handshake"))?, + ) + }; + Ok(PeerView { public_key: public_key.ok_or_else(|| Error::message("missing public_key"))?, preshared_key, endpoint, persistent_keepalive, - last_handshake: last_handshake - .map(|ts| { - SystemTime::UNIX_EPOCH - .checked_add(Duration::new(ts.seconds as u64, ts.nano_seconds as u32)) - .unwrap() - }) - .ok_or_else(|| Error::message("missing last_handshake"))?, - rx_bytes: rx_bytes.ok_or_else(|| Error::message("missing rx_bytes"))?, - tx_bytes: tx_bytes.ok_or_else(|| Error::message("missing tx_bytes"))?, + last_handshake, + rx_bytes, + tx_bytes, allowed_ips, }) } -- cgit