summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2026-02-17 14:47:15 +0000
committerdiogo464 <[email protected]>2026-02-17 14:47:15 +0000
commita0a5cb22ecf4c1760a3eadaea5ed2c0a55aa62c7 (patch)
tree880a2ca8992d4e621e5531b37f9a79afed9c93fc
parent0c6a34024786d6117c238a0164218a4e718178f0 (diff)
add wireguard-show binary and optional handshake timestamps
-rw-r--r--Cargo.toml4
-rw-r--r--bin/wireguard-show.rs80
-rw-r--r--src/view.rs33
3 files changed, 107 insertions, 10 deletions
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"
19serde = "1.0.195" 19serde = "1.0.195"
20tokio = { version = "1.35.1", features = ["full"] } 20tokio = { version = "1.35.1", features = ["full"] }
21tracing = "0.1.40" 21tracing = "0.1.40"
22
23[[bin]]
24name = "wireguard-show"
25path = "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 @@
1use anyhow::Result;
2use wireguard::WireGuard;
3
4#[tokio::main]
5async fn main() -> Result<()> {
6 let mut wireguard = WireGuard::new().await?;
7 let devices = wireguard.view_devices().await?;
8
9 if devices.is_empty() {
10 println!("no wireguard devices found");
11 return Ok(());
12 }
13
14 for device in devices {
15 println!("device: {}", device.name);
16 println!(" ifindex: {}", device.ifindex);
17 println!(" private_key: {}", format_optional_key(device.private_key));
18 println!(" public_key: {}", format_optional_key(device.public_key));
19 println!(" listen_port: {}", device.listen_port);
20 println!(" fwmark: {}", device.fwmark);
21 println!(" peers: {}", device.peers.len());
22
23 for (index, peer) in device.peers.iter().enumerate() {
24 println!(" peer {}:", index + 1);
25 println!(" public_key: {}", peer.public_key);
26 println!(
27 " preshared_key: {}",
28 format_optional_key(peer.preshared_key)
29 );
30 println!(
31 " endpoint: {}",
32 peer.endpoint
33 .map(|endpoint| endpoint.to_string())
34 .unwrap_or_else(|| "none".to_string())
35 );
36 println!(
37 " persistent_keepalive: {}",
38 peer.persistent_keepalive
39 .map(|keepalive| keepalive.to_string())
40 .unwrap_or_else(|| "none".to_string())
41 );
42 println!(
43 " last_handshake: {}",
44 format_duration(peer.last_handshake)
45 );
46 println!(" rx_bytes: {}", peer.rx_bytes);
47 println!(" tx_bytes: {}", peer.tx_bytes);
48 if peer.allowed_ips.is_empty() {
49 println!(" allowed_ips: none");
50 } else {
51 let allowed_ips = peer
52 .allowed_ips
53 .iter()
54 .map(|ip| ip.to_string())
55 .collect::<Vec<_>>()
56 .join(", ");
57 println!(" allowed_ips: {}", allowed_ips);
58 }
59 }
60
61 println!();
62 }
63
64 Ok(())
65}
66
67fn format_optional_key(key: Option<wireguard::Key>) -> String {
68 key.map(|value| value.to_string())
69 .unwrap_or_else(|| "none".to_string())
70}
71
72fn format_duration(time: Option<std::time::SystemTime>) -> String {
73 match time {
74 Some(time) => match time.duration_since(std::time::UNIX_EPOCH) {
75 Ok(duration) => format!("{}.{}", duration.as_secs(), duration.subsec_nanos()),
76 Err(_) => "before-epoch".to_string(),
77 },
78 None => "none".to_string(),
79 }
80}
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 {
28 pub preshared_key: Option<Key>, 28 pub preshared_key: Option<Key>,
29 pub endpoint: Option<SocketAddr>, 29 pub endpoint: Option<SocketAddr>,
30 pub persistent_keepalive: Option<u16>, 30 pub persistent_keepalive: Option<u16>,
31 pub last_handshake: SystemTime, 31 pub last_handshake: Option<SystemTime>,
32 pub rx_bytes: u64, 32 pub rx_bytes: u64,
33 pub tx_bytes: u64, 33 pub tx_bytes: u64,
34 pub allowed_ips: Vec<IpNet>, 34 pub allowed_ips: Vec<IpNet>,
@@ -103,20 +103,33 @@ fn peer_from_wg_peer(wg_peer: WireguardPeer) -> Result<PeerView> {
103 } 103 }
104 } 104 }
105 105
106 let last_handshake = last_handshake.ok_or_else(|| Error::message("missing last_handshake"))?;
107 let rx_bytes = rx_bytes.ok_or_else(|| Error::message("missing rx_bytes"))?;
108 let tx_bytes = tx_bytes.ok_or_else(|| Error::message("missing tx_bytes"))?;
109
110 let handshake_is_zero = last_handshake.seconds == 0 && last_handshake.nano_seconds == 0;
111 let last_handshake = if handshake_is_zero && (rx_bytes == 0 || tx_bytes == 0) {
112 None
113 } else {
114 let duration = Duration::new(
115 last_handshake.seconds as u64,
116 last_handshake.nano_seconds as u32,
117 );
118 Some(
119 SystemTime::UNIX_EPOCH
120 .checked_add(duration)
121 .ok_or_else(|| Error::message("invalid last_handshake"))?,
122 )
123 };
124
106 Ok(PeerView { 125 Ok(PeerView {
107 public_key: public_key.ok_or_else(|| Error::message("missing public_key"))?, 126 public_key: public_key.ok_or_else(|| Error::message("missing public_key"))?,
108 preshared_key, 127 preshared_key,
109 endpoint, 128 endpoint,
110 persistent_keepalive, 129 persistent_keepalive,
111 last_handshake: last_handshake 130 last_handshake,
112 .map(|ts| { 131 rx_bytes,
113 SystemTime::UNIX_EPOCH 132 tx_bytes,
114 .checked_add(Duration::new(ts.seconds as u64, ts.nano_seconds as u32))
115 .unwrap()
116 })
117 .ok_or_else(|| Error::message("missing last_handshake"))?,
118 rx_bytes: rx_bytes.ok_or_else(|| Error::message("missing rx_bytes"))?,
119 tx_bytes: tx_bytes.ok_or_else(|| Error::message("missing tx_bytes"))?,
120 allowed_ips, 133 allowed_ips,
121 }) 134 })
122} 135}