summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--talpid-core/Cargo.toml2
-rw-r--r--talpid-wireguard/Cargo.toml1
-rw-r--r--talpid-wireguard/src/boringtun/mod.rs9
-rw-r--r--talpid-wireguard/src/connectivity/check.rs5
-rw-r--r--talpid-wireguard/src/connectivity/mock.rs3
-rw-r--r--talpid-wireguard/src/connectivity/monitor.rs1
-rw-r--r--talpid-wireguard/src/snapshots/talpid_wireguard__stats__test__stats_debug.snap10
-rw-r--r--talpid-wireguard/src/stats.rs64
-rw-r--r--talpid-wireguard/src/wireguard_go/mod.rs33
-rw-r--r--talpid-wireguard/src/wireguard_kernel/stats.rs25
-rw-r--r--talpid-wireguard/src/wireguard_nt/mod.rs20
12 files changed, 171 insertions, 3 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b92081e112..13214926ba 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5858,6 +5858,7 @@ dependencies = [
"chrono",
"futures",
"hex",
+ "insta",
"internet-checksum",
"ipnetwork",
"libc",
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index d771bb23a6..5712135f9d 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -113,4 +113,4 @@ tonic-build = { workspace = true, default-features = false, features = ["transpo
[dev-dependencies]
test-log = "0.2.17"
tokio = { workspace = true, features = ["io-util", "test-util", "time"] }
-insta = "1.42"
+insta = { workspace = true }
diff --git a/talpid-wireguard/Cargo.toml b/talpid-wireguard/Cargo.toml
index 2bfea215a7..02a37c4215 100644
--- a/talpid-wireguard/Cargo.toml
+++ b/talpid-wireguard/Cargo.toml
@@ -97,3 +97,4 @@ features = [
[dev-dependencies]
proptest = { workspace = true }
tokio = { workspace = true, features = ["test-util"] }
+insta = { workspace = true }
diff --git a/talpid-wireguard/src/boringtun/mod.rs b/talpid-wireguard/src/boringtun/mod.rs
index f4ba01f089..c854213d71 100644
--- a/talpid-wireguard/src/boringtun/mod.rs
+++ b/talpid-wireguard/src/boringtun/mod.rs
@@ -25,6 +25,7 @@ use std::{
net::{IpAddr, Ipv4Addr, Ipv6Addr},
ops::Deref,
sync::{Arc, Mutex},
+ time::{Duration, SystemTime},
};
use talpid_tunnel::tun_provider::{self, Tun, TunProvider};
use talpid_tunnel_config_client::DaitaSettings;
@@ -376,11 +377,19 @@ impl Tunnel for BoringTun {
};
for peer in response.peers {
+ let last_handshake = || -> Option<SystemTime> {
+ let handshake_sec = peer.last_handshake_time_sec?;
+ let handshake_nsec = peer.last_handshake_time_nsec?;
+ // TODO: Boringtun should probably return a Unix timestamp (like wg-go)
+ Some(SystemTime::now() - Duration::new(handshake_sec, handshake_nsec))
+ };
+
stats.insert(
peer.peer.public_key.0,
Stats {
tx_bytes: peer.tx_bytes.unwrap_or_default(),
rx_bytes: peer.rx_bytes.unwrap_or_default(),
+ last_handshake_time: last_handshake(),
},
);
}
diff --git a/talpid-wireguard/src/connectivity/check.rs b/talpid-wireguard/src/connectivity/check.rs
index 3690356ff8..89043fb54c 100644
--- a/talpid-wireguard/src/connectivity/check.rs
+++ b/talpid-wireguard/src/connectivity/check.rs
@@ -515,6 +515,7 @@ mod test {
Stats {
rx_bytes: 1,
tx_bytes: 0,
+ last_handshake_time: None,
},
);
conn_state.update(Instant::now(), stats);
@@ -540,6 +541,7 @@ mod test {
Stats {
rx_bytes: 1,
tx_bytes: 0,
+ last_handshake_time: None,
},
);
conn_state.update(connect_time, stats);
@@ -564,6 +566,7 @@ mod test {
Stats {
rx_bytes: 1,
tx_bytes: 0,
+ last_handshake_time: None,
},
);
conn_state.update(start, stats);
@@ -575,6 +578,7 @@ mod test {
Stats {
rx_bytes: 1,
tx_bytes: 1,
+ last_handshake_time: None,
},
);
conn_state.update(update_time, stats);
@@ -671,6 +675,7 @@ mod test {
Stats {
tx_bytes: 0,
rx_bytes: 0,
+ last_handshake_time: None,
},
);
MockTunnel::new(move || Ok(tunnel_stats.clone())).boxed()
diff --git a/talpid-wireguard/src/connectivity/mock.rs b/talpid-wireguard/src/connectivity/mock.rs
index a7a1851e31..ef8bf4103f 100644
--- a/talpid-wireguard/src/connectivity/mock.rs
+++ b/talpid-wireguard/src/connectivity/mock.rs
@@ -35,6 +35,7 @@ pub fn connected_state(timestamp: Instant) -> ConnState {
Stats {
tx_bytes: 0,
rx_bytes: 0,
+ last_handshake_time: None,
},
);
ConnState::Connected {
@@ -65,6 +66,7 @@ impl MockTunnel {
Stats {
tx_bytes: 0,
rx_bytes: 0,
+ last_handshake_time: None,
},
);
let peers = std::sync::Mutex::new(map);
@@ -89,6 +91,7 @@ impl MockTunnel {
Stats {
tx_bytes: 0,
rx_bytes: 0,
+ last_handshake_time: None,
},
);
Ok(map)
diff --git a/talpid-wireguard/src/connectivity/monitor.rs b/talpid-wireguard/src/connectivity/monitor.rs
index 7b2eb5e6a6..644f2c9d80 100644
--- a/talpid-wireguard/src/connectivity/monitor.rs
+++ b/talpid-wireguard/src/connectivity/monitor.rs
@@ -131,6 +131,7 @@ mod test {
Stats {
tx_bytes: 0,
rx_bytes: 0,
+ last_handshake_time: None,
},
);
let tunnel_stats = std::sync::Mutex::new(map);
diff --git a/talpid-wireguard/src/snapshots/talpid_wireguard__stats__test__stats_debug.snap b/talpid-wireguard/src/snapshots/talpid_wireguard__stats__test__stats_debug.snap
new file mode 100644
index 0000000000..a3dc8c3b6a
--- /dev/null
+++ b/talpid-wireguard/src/snapshots/talpid_wireguard__stats__test__stats_debug.snap
@@ -0,0 +1,10 @@
+---
+source: talpid-wireguard/src/stats.rs
+expression: "StatsDebug { now, stats: &stats }"
+snapshot_kind: text
+---
+Stats {
+ tx_bytes: 100,
+ rx_bytes: 100,
+ last_handshake: "60000 ms ago",
+}
diff --git a/talpid-wireguard/src/stats.rs b/talpid-wireguard/src/stats.rs
index cdbe8318cf..4707339f75 100644
--- a/talpid-wireguard/src/stats.rs
+++ b/talpid-wireguard/src/stats.rs
@@ -1,9 +1,71 @@
+use std::fmt;
+use std::time::{Duration, SystemTime};
+
/// Contains bytes sent and received through a tunnel
-#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
+#[derive(Default, PartialEq, Eq, Clone, Copy)]
pub struct Stats {
pub tx_bytes: u64,
pub rx_bytes: u64,
+ pub last_handshake_time: Option<SystemTime>,
+}
+
+impl fmt::Debug for Stats {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let stats = StatsDebug {
+ now: SystemTime::now(),
+ stats: self,
+ };
+ fmt::Debug::fmt(&stats, f)
+ }
+}
+
+struct StatsDebug<'a> {
+ pub now: SystemTime,
+ pub stats: &'a Stats,
+}
+
+impl fmt::Debug for StatsDebug<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut dbg = f.debug_struct("Stats");
+
+ dbg.field("tx_bytes", &self.stats.tx_bytes)
+ .field("rx_bytes", &self.stats.rx_bytes);
+
+ if let Some(last_handshake) = self.stats.last_handshake_time {
+ let time_since_handshake = self
+ .now
+ .duration_since(last_handshake)
+ .unwrap_or(Duration::ZERO);
+
+ dbg.field(
+ "last_handshake",
+ &format_args!("\"{} ms ago\"", time_since_handshake.as_millis()),
+ );
+ } else {
+ dbg.field("last_handshake", &"no handshake");
+ }
+
+ dbg.finish()
+ }
}
/// A map from peer pubkeys to peer stats.
pub type StatsMap = std::collections::HashMap<[u8; 32], Stats>;
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_stats_debug() {
+ let now = SystemTime::UNIX_EPOCH + Duration::from_secs(60);
+
+ let stats = Stats {
+ tx_bytes: 100,
+ rx_bytes: 100,
+ last_handshake_time: Some(SystemTime::UNIX_EPOCH),
+ };
+
+ insta::assert_debug_snapshot!(StatsDebug { now, stats: &stats });
+ }
+}
diff --git a/talpid-wireguard/src/wireguard_go/mod.rs b/talpid-wireguard/src/wireguard_go/mod.rs
index 6e277926c3..b12afe40b4 100644
--- a/talpid-wireguard/src/wireguard_go/mod.rs
+++ b/talpid-wireguard/src/wireguard_go/mod.rs
@@ -758,6 +758,7 @@ impl Tunnel for WgGoTunnel {
mod stats {
use super::{Stats, StatsMap};
+ use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(thiserror::Error, Debug, PartialEq)]
pub enum Error {
@@ -773,8 +774,11 @@ mod stats {
let mut map = StatsMap::new();
let mut peer = None;
+
let mut tx_bytes = None;
let mut rx_bytes = None;
+ let mut last_handshake_time_sec = None;
+ let mut last_handshake_time_nsec = None;
// parts iterates over keys and values
let parts = config.split('\n').filter_map(|line| {
@@ -793,6 +797,8 @@ mod stats {
peer = Some(buffer);
tx_bytes = None;
rx_bytes = None;
+ last_handshake_time_sec = None;
+ last_handshake_time_nsec = None;
}
"rx_bytes" => {
rx_bytes = Some(
@@ -810,6 +816,22 @@ mod stats {
.map_err(|err| Error::IntParse(value.to_string(), err))?,
);
}
+ "last_handshake_time_sec" => {
+ last_handshake_time_sec = Some(
+ value
+ .trim()
+ .parse()
+ .map_err(|err| Error::IntParse(value.to_string(), err))?,
+ );
+ }
+ "last_handshake_time_nsec" => {
+ last_handshake_time_nsec = Some(
+ value
+ .trim()
+ .parse()
+ .map_err(|err| Error::IntParse(value.to_string(), err))?,
+ );
+ }
_ => continue,
}
@@ -817,16 +839,27 @@ mod stats {
if let (Some(peer_val), Some(tx_bytes_val), Some(rx_bytes_val)) =
(peer, tx_bytes, rx_bytes)
{
+ let last_handshake_time = || -> Option<SystemTime> {
+ let handshake_sec = last_handshake_time_sec?;
+ let handshake_nsec = last_handshake_time_nsec?;
+ // handshake_{sec,nsec} are relative to UNIX_EPOCH
+ // https://www.wireguard.com/xplatform/
+ Some(UNIX_EPOCH + Duration::new(handshake_sec, handshake_nsec))
+ };
+
map.insert(
peer_val,
Self {
tx_bytes: tx_bytes_val,
rx_bytes: rx_bytes_val,
+ last_handshake_time: last_handshake_time(),
},
);
peer = None;
tx_bytes = None;
rx_bytes = None;
+ last_handshake_time_sec = None;
+ last_handshake_time_nsec = None;
}
}
Ok(map)
diff --git a/talpid-wireguard/src/wireguard_kernel/stats.rs b/talpid-wireguard/src/wireguard_kernel/stats.rs
index 8604e8243f..a055a35d10 100644
--- a/talpid-wireguard/src/wireguard_kernel/stats.rs
+++ b/talpid-wireguard/src/wireguard_kernel/stats.rs
@@ -1,3 +1,5 @@
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+
use super::wg_message::{DeviceMessage, DeviceNla, PeerNla};
use crate::stats::{Stats, StatsMap};
@@ -10,18 +12,39 @@ impl Stats {
for msg in peers {
let mut tx_bytes = 0;
let mut rx_bytes = 0;
+ let mut last_handshake_time = None;
let mut pub_key = None;
for nla in &msg.0 {
match nla {
PeerNla::TxBytes(bytes) => tx_bytes = *bytes,
PeerNla::RxBytes(bytes) => rx_bytes = *bytes,
+ PeerNla::LastHandshakeTime(time) => {
+ last_handshake_time = || -> Option<SystemTime> {
+ // handshake_{sec,nsec} are relative to UNIX_EPOCH
+ // https://www.wireguard.com/xplatform/
+ Some(
+ UNIX_EPOCH
+ + Duration::new(
+ time.tv_sec().try_into().ok()?,
+ time.tv_nsec().try_into().ok()?,
+ ),
+ )
+ }();
+ }
PeerNla::PublicKey(key) => pub_key = Some(*key),
_ => continue,
}
}
if let Some(key) = pub_key {
- map.insert(key, Stats { tx_bytes, rx_bytes });
+ map.insert(
+ key,
+ Stats {
+ tx_bytes,
+ rx_bytes,
+ last_handshake_time,
+ },
+ );
}
}
}
diff --git a/talpid-wireguard/src/wireguard_nt/mod.rs b/talpid-wireguard/src/wireguard_nt/mod.rs
index 7fe748ec5c..0f650d3866 100644
--- a/talpid-wireguard/src/wireguard_nt/mod.rs
+++ b/talpid-wireguard/src/wireguard_nt/mod.rs
@@ -22,6 +22,7 @@ use std::{
pin::Pin,
ptr,
sync::{Arc, LazyLock, Mutex},
+ time::{Duration, SystemTime, UNIX_EPOCH},
};
#[cfg(daita)]
use std::{ffi::c_uchar, path::PathBuf};
@@ -1072,11 +1073,17 @@ impl Tunnel for WgNtTunnel {
super::TunnelError::GetConfigError
})?;
for (peer, _allowed_ips) in &peers {
+ // last_handshake is in 100s of ns relative to 1601-01-01 UTC
+ // https://git.zx2c4.com/wireguard-nt/tree/api/wireguard.h?id=30a2817d913460ed8a23388d3da485cf9347afa3#n246
+ let last_handshake_time =
+ (peer.last_handshake > 0).then(|| filetime_to_systemtime(peer.last_handshake));
+
map.insert(
peer.public_key,
Stats {
tx_bytes: peer.tx_bytes,
rx_bytes: peer.rx_bytes,
+ last_handshake_time,
},
);
}
@@ -1134,6 +1141,19 @@ pub fn as_uninit_byte_slice<T: Copy + Sized>(value: &T) -> &[mem::MaybeUninit<u8
unsafe { std::slice::from_raw_parts(value as *const _ as *const _, mem::size_of::<T>()) }
}
+/// wireguard-nt uses the `FILETIME` timestamp (100ns intervals since 1601-01-01).
+/// This function converts this to [SystemTime].
+fn filetime_to_systemtime(filetime: u64) -> SystemTime {
+ // Difference between 1601-01-01 and 1970-01-01 in 100ns intervals
+ const WINDOWS_TO_UNIX_EPOCH_DIFF: u64 = 11644473600u64;
+ const HUNDRED_NANOSECONDS: u64 = 10_000_000;
+
+ let seconds = filetime / HUNDRED_NANOSECONDS;
+ let nanos = (filetime % HUNDRED_NANOSECONDS) * 100;
+
+ UNIX_EPOCH + Duration::new(seconds - WINDOWS_TO_UNIX_EPOCH_DIFF, nanos as u32)
+}
+
#[cfg(test)]
mod tests {
use super::*;