When `connlib` detects that no data is being sent on a connection, it enters a "low-power" mode within which timers are set to a much longer interval than usual. For `boringtun` this moves the timer from 1s to 30s. At present, this timer also guards, how often we actually update the timer state within `boringtun`. Instead of following a "only update exactly when this timer fires"-policy, we now adopt a "update at least this often"-policy. The difference here is that while we are executing the `handle_timeout` function, we might as well call into `boringtun` and update its timer state too. Another side-effect of this timer is that `boringtun` may not be woken in time to initiate a rekey when the session expires. WireGuard sessions without activity expire after 3 minutes. Only the initiater should then recreate the session. If this doesn't happen in time, the responder (Gateway) may trigger a keep-alive timeout. Without an active session, keep-alives also initiate sessions, resulting in us having two competing sessions. This fixes the failing test cases added in this PR: There, we ran into a situation where a WireGuard tunnel idled for so long that the spec requires the session to expire. In the test, we then sent a packet using such an expired session but that packet got discarded by the Gateway because of the expired session. The timers are what check whether a session is expired: - By calling `update_timers_at` more often, we can expire the session in time and `boringtun` will buffer the to-be-sent packet until the new session is established. - By deactivating the keep-alive on the Gateway, we ensure that we only ever have a single WireGuard session active. - With https://github.com/firezone/boringtun/pull/53, we ensure the Gateway doesn't initiate a new session in the beginning. - With https://github.com/firezone/boringtun/pull/51, we ensure the Client only ever initiates a single session. To be entirely reliable, we also had to remove the idle WG timer and update `boringtun`'s state every second. This is unfortunate but can long-term be fixed by patching WireGuard to tell us, when it exactly wants to be woken instead of us having to proactively wake it every second _in case_ it needs to act on a timer. Related: https://github.com/firezone/boringtun/issues/54.
Rust development guide
Firezone uses Rust for all data plane components. This directory contains the Linux and Windows clients, and low-level networking implementations related to STUN/TURN.
We target the last stable release of Rust using rust-toolchain.toml.
If you are using rustup, that is automatically handled for you.
Otherwise, ensure you have the latest stable version of Rust installed.
Reading Client logs
The Client logs are written as JSONL for machine-readability.
To make them more human-friendly, pipe them through jq like this:
cd path/to/logs # e.g. `$HOME/.cache/dev.firezone.client/data/logs` on Linux
cat *.log | jq -r '"\(.time) \(.severity) \(.message)"'
Resulting in, e.g.
2024-04-01T18:25:47.237661392Z INFO started log
2024-04-01T18:25:47.238193266Z INFO GIT_VERSION = 1.0.0-pre.11-35-gcc0d43531
2024-04-01T18:25:48.295243016Z INFO No token / actor_name on disk, starting in signed-out state
2024-04-01T18:25:48.295360641Z INFO null
Benchmarking on Linux
The recommended way for benchmarking any of the Rust components is Linux' perf utility.
For example, to attach to a running application, do:
- Ensure the binary you are profiling is compiled with the
releaseprofile. sudo perf record -g --freq 10000 --pid $(pgrep <your-binary>).- Run the speed test or whatever load-inducing task you want to measure.
sudo perf script > profile.perf- Open profiler.firefox.com and load
profile.perf
Instead of attaching to a process with --pid, you can also specify the path to executable directly.
That is useful if you want to capture perf data for a test or a micro-benchmark.