Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: time-impurity within boringtun library #45

Merged
merged 25 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7f98ab8
Deprecate `update_timers` in favor of `update_timers_at`
thomaseizinger Oct 22, 2024
3ae570f
Add `Instant` parameter to `Timers::clear`
thomaseizinger Oct 22, 2024
c13efc9
Add `Instant` parameter to `Timers::new`
thomaseizinger Oct 22, 2024
e1cfb05
Add `Instant` parameter to `current_cookie`
thomaseizinger Oct 22, 2024
0ee96ce
Add `Instant` parameter to `Timerstamper::new`
thomaseizinger Oct 22, 2024
f4e1e9a
Add `Instant` parameter to `Handshake::new`
thomaseizinger Oct 22, 2024
2f14c1a
Add `Instant` parameter to `Timerstamper::stamp`
thomaseizinger Oct 22, 2024
e7498da
Add `Instant` parameter to `receive_handshake_response`
thomaseizinger Oct 22, 2024
71350a7
Add `Instant` parameter to `format_handshake_initiation`
thomaseizinger Oct 22, 2024
f0dda99
Add `Instant` parameter to `handle_handshake_response`
thomaseizinger Oct 22, 2024
4e7a329
Add `Instant` parameter to `handle_verified_packet`
thomaseizinger Oct 22, 2024
92b712b
Deprecate `new` in favor of `new_at`
thomaseizinger Oct 22, 2024
1750008
Deprecate `reset_count` in favor of `reset_count_at`
thomaseizinger Oct 22, 2024
0952b46
Deprecate `verify_packet` in favor of `verify_packet_at`
thomaseizinger Oct 22, 2024
49b4c28
Deprecate `time_since_last_handshake` in favor of `time_since_last_ha…
thomaseizinger Oct 22, 2024
85b84c3
Deprecate `Tunn:new` in favor of `Tunn::new_at`
thomaseizinger Oct 22, 2024
251e9fb
Deprecate `Tunn::set_static_private` in favor of `Tunn::set_static_pr…
thomaseizinger Oct 22, 2024
4a4a518
Deprecate `Tunn::decapsulate` in favor of `Tunn::decapsulate_at`
thomaseizinger Oct 22, 2024
aad2c94
Deprecate `format_handshake_initiation` in favor of `format_handshake…
thomaseizinger Oct 22, 2024
c230b50
Deprecate `Tunn::encapsulate` in favor of `Tunn::encapsulate_at`
thomaseizinger Oct 22, 2024
30976da
Deprecate `Tunn::stats` in favor of `Tunn::stats_at
thomaseizinger Oct 22, 2024
3dcc65c
Refactor tests to not depend on sleepinstant
thomaseizinger Oct 22, 2024
9ff747e
Remove `mock-instant` dependency
thomaseizinger Oct 22, 2024
4987ed2
Add note about impurity of `SystemTime`
thomaseizinger Oct 22, 2024
8db8dc7
Re-add mock-instant crate to workaround cargo-semver-checks false-pos…
thomaseizinger Jan 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions boringtun/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ default = []
device = ["socket2", "thiserror"]
jni-bindings = ["ffi-bindings", "jni"]
ffi-bindings = ["tracing-subscriber"]
# mocks std::time::Instant with mock_instant
mock-instant = ["mock_instant"]
mock-instant = [] # Deprecated.

[dependencies]
base64 = "0.22"
Expand All @@ -41,7 +40,7 @@ aead = "0.5.0-pre.2"
blake2 = "0.10"
hmac = "0.12"
jni = { version = "0.19.0", optional = true }
mock_instant = { version = "0.3", optional = true }
mock_instant = { version = "0.3", optional = true } # Deprecated.
socket2 = { version = "0.5.8", features = ["all"], optional = true }
thiserror = { version = "1", optional = true }

Expand Down
3 changes: 2 additions & 1 deletion boringtun/src/device/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::io::{BufRead, BufReader, BufWriter, Write};
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::os::unix::net::{UnixListener, UnixStream};
use std::sync::atomic::Ordering;
use std::time::Instant;

const SOCK_DIR: &str = "/var/run/wireguard/";

Expand Down Expand Up @@ -193,7 +194,7 @@ fn api_get(writer: &mut BufWriter<&UnixStream>, d: &Device) -> i32 {
writeln!(writer, "last_handshake_time_nsec={}", time.subsec_nanos());
}

let (_, tx_bytes, rx_bytes, ..) = p.tunnel.stats();
let (_, tx_bytes, rx_bytes, ..) = p.tunnel.stats_at(Instant::now());

writeln!(writer, "rx_bytes={}", rx_bytes);
writeln!(writer, "tx_bytes={}", tx_bytes);
Expand Down
41 changes: 28 additions & 13 deletions boringtun/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use std::time::Instant;

use crate::noise::errors::WireGuardError;
use crate::noise::handshake::parse_handshake_anon;
Expand Down Expand Up @@ -327,13 +328,14 @@ impl Device {
.as_ref()
.expect("Private key must be set first");

let tunn = Tunn::new(
let tunn = Tunn::new_at(
device_key_pair.0.clone(),
pub_key,
preshared_key,
keepalive,
next_index,
None,
Instant::now(),
);

let peer = Peer::new(tunn, next_index, endpoint, allowed_ips, preshared_key);
Expand Down Expand Up @@ -461,13 +463,18 @@ impl Device {
return;
}

let rate_limiter = Arc::new(RateLimiter::new(&public_key, HANDSHAKE_RATE_LIMIT));
let rate_limiter = Arc::new(RateLimiter::new_at(
&public_key,
HANDSHAKE_RATE_LIMIT,
Instant::now(),
));

for peer in self.peers.values_mut() {
peer.lock().tunnel.set_static_private(
peer.lock().tunnel.set_static_private_at(
private_key.clone(),
public_key,
Some(Arc::clone(&rate_limiter)),
Instant::now(),
)
}

Expand Down Expand Up @@ -524,7 +531,7 @@ impl Device {
// Reset the rate limiter every second give or take
Box::new(|d, _| {
if let Some(r) = d.rate_limiter.as_ref() {
r.reset_count()
r.reset_count_at(Instant::now())
}
Action::Continue
}),
Expand Down Expand Up @@ -609,10 +616,11 @@ impl Device {
while let Ok((packet_len, addr)) = udp.recv_from(src_buf) {
let packet = &t.src_buf[..packet_len];
// The rate limiter initially checks mac1 and mac2, and optionally asks to send a cookie
let parsed_packet = match rate_limiter.verify_packet(
let parsed_packet = match rate_limiter.verify_packet_at(
Some(addr.as_socket().unwrap().ip()),
packet,
&mut t.dst_buf,
Instant::now(),
) {
Ok(packet) => packet,
Err(TunnResult::WriteToNetwork(cookie)) => {
Expand Down Expand Up @@ -644,10 +652,11 @@ impl Device {

// We found a peer, use it to decapsulate the message+
let mut flush = false; // Are there packets to send from the queue?
match p
.tunnel
.handle_verified_packet(parsed_packet, &mut t.dst_buf[..])
{
match p.tunnel.handle_verified_packet(
parsed_packet,
&mut t.dst_buf[..],
Instant::now(),
) {
TunnResult::Done => {}
TunnResult::Err(_) => continue,
TunnResult::WriteToNetwork(packet) => {
Expand All @@ -669,7 +678,8 @@ impl Device {
if flush {
// Flush pending queue
while let TunnResult::WriteToNetwork(packet) =
p.tunnel.decapsulate(None, &[], &mut t.dst_buf[..])
p.tunnel
.decapsulate_at(None, &[], &mut t.dst_buf[..], Instant::now())
{
let _: Result<_, _> = udp.send_to(packet, &addr);
}
Expand Down Expand Up @@ -720,10 +730,11 @@ impl Device {
while let Ok(read_bytes) = udp.recv(src_buf) {
let mut flush = false;
let mut p = peer.lock();
match p.tunnel.decapsulate(
match p.tunnel.decapsulate_at(
Some(peer_addr),
&t.src_buf[..read_bytes],
&mut t.dst_buf[..],
Instant::now(),
) {
TunnResult::Done => {}
TunnResult::Err(e) => eprintln!("Decapsulate error {:?}", e),
Expand All @@ -746,7 +757,8 @@ impl Device {
if flush {
// Flush pending queue
while let TunnResult::WriteToNetwork(packet) =
p.tunnel.decapsulate(None, &[], &mut t.dst_buf[..])
p.tunnel
.decapsulate_at(None, &[], &mut t.dst_buf[..], Instant::now())
{
let _: Result<_, _> = udp.send(packet);
}
Expand Down Expand Up @@ -806,7 +818,10 @@ impl Device {
None => continue,
};

match peer.tunnel.encapsulate(src, &mut t.dst_buf[..]) {
match peer
.tunnel
.encapsulate_at(src, &mut t.dst_buf[..], Instant::now())
{
TunnResult::Done => {}
TunnResult::Err(e) => {
tracing::error!(message = "Encapsulate error", error = ?e)
Expand Down
5 changes: 3 additions & 2 deletions boringtun/src/device/peer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use socket2::{Domain, Protocol, Type};

use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::str::FromStr;
use std::time::Instant;

use crate::device::{AllowedIps, Error};
use crate::noise::{Tunn, TunnResult};
Expand Down Expand Up @@ -71,7 +72,7 @@ impl Peer {
}

pub fn update_timers<'a>(&mut self, dst: &'a mut [u8]) -> TunnResult<'a> {
self.tunnel.update_timers(dst)
self.tunnel.update_timers_at(dst, Instant::now())
}

pub fn endpoint(&self) -> parking_lot::RwLockReadGuard<'_, Endpoint> {
Expand Down Expand Up @@ -157,7 +158,7 @@ impl Peer {
}

pub fn time_since_last_handshake(&self) -> Option<std::time::Duration> {
self.tunnel.time_since_last_handshake()
self.tunnel.time_since_last_handshake_at(Instant::now())
}

pub fn persistent_keepalive(&self) -> Option<u16> {
Expand Down
14 changes: 8 additions & 6 deletions boringtun/src/ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use std::ptr;
use std::ptr::null_mut;
use std::slice;
use std::sync::Once;
use std::time::Instant;

static PANIC_HOOK: Once = Once::new();

Expand Down Expand Up @@ -288,13 +289,14 @@ pub unsafe extern "C" fn new_tunnel(
Some(keep_alive)
};

let tunnel = Box::new(Mutex::new(Tunn::new(
let tunnel = Box::new(Mutex::new(Tunn::new_at(
private_key,
public_key,
preshared_key,
keep_alive,
index,
None,
Instant::now(),
)));

PANIC_HOOK.call_once(|| {
Expand Down Expand Up @@ -327,7 +329,7 @@ pub unsafe extern "C" fn wireguard_write(
// Slices are not owned, and therefore will not be freed by Rust
let src = slice::from_raw_parts(src, src_size as usize);
let dst = slice::from_raw_parts_mut(dst, dst_size as usize);
wireguard_result::from(tunnel.encapsulate(src, dst))
wireguard_result::from(tunnel.encapsulate_at(src, dst, Instant::now()))
}

/// Read a UDP packet from the server.
Expand All @@ -344,7 +346,7 @@ pub unsafe extern "C" fn wireguard_read(
// Slices are not owned, and therefore will not be freed by Rust
let src = slice::from_raw_parts(src, src_size as usize);
let dst = slice::from_raw_parts_mut(dst, dst_size as usize);
wireguard_result::from(tunnel.decapsulate(None, src, dst))
wireguard_result::from(tunnel.decapsulate_at(None, src, dst, Instant::now()))
}

/// This is a state keeping function, that need to be called periodically.
Expand All @@ -358,7 +360,7 @@ pub unsafe extern "C" fn wireguard_tick(
let mut tunnel = tunnel.as_ref().unwrap().lock();
// Slices are not owned, and therefore will not be freed by Rust
let dst = slice::from_raw_parts_mut(dst, dst_size as usize);
wireguard_result::from(tunnel.update_timers(dst))
wireguard_result::from(tunnel.update_timers_at(dst, Instant::now()))
}

/// Force the tunnel to initiate a new handshake, dst buffer must be at least 148 byte long.
Expand All @@ -371,7 +373,7 @@ pub unsafe extern "C" fn wireguard_force_handshake(
let mut tunnel = tunnel.as_ref().unwrap().lock();
// Slices are not owned, and therefore will not be freed by Rust
let dst = slice::from_raw_parts_mut(dst, dst_size as usize);
wireguard_result::from(tunnel.format_handshake_initiation(dst, true))
wireguard_result::from(tunnel.format_handshake_initiation_at(dst, true, Instant::now()))
}

/// Returns stats from the tunnel:
Expand All @@ -381,7 +383,7 @@ pub unsafe extern "C" fn wireguard_force_handshake(
#[no_mangle]
pub unsafe extern "C" fn wireguard_stats(tunnel: *const Mutex<Tunn>) -> stats {
let tunnel = tunnel.as_ref().unwrap().lock();
let (time, tx_bytes, rx_bytes, estimated_loss, estimated_rtt) = tunnel.stats();
let (time, tx_bytes, rx_bytes, estimated_loss, estimated_rtt) = tunnel.stats_at(Instant::now());
stats {
time_since_last_handshake: time.map(|t| t.as_secs() as i64).unwrap_or(-1),
tx_bytes,
Expand Down
3 changes: 0 additions & 3 deletions boringtun/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ pub mod ffi;
pub mod jni;
pub mod noise;

#[cfg(not(feature = "mock-instant"))]
pub(crate) mod sleepyinstant;

#[cfg(any(feature = "ffi-bindings", feature = "device"))]
pub(crate) mod serialization;

Expand Down
Loading
Loading