At present, the eBPF code assumes that the incoming packet needs to be sent back to the same MAC address that it came from. This is only true if there is at least one IP layer hop in-between the relay and the Client / Gateway. When setting up Firezone in my local LAN to debug the eBPF code, all components are within the same subnet and thus can send packets directly to each other, without having to go through the router. In such a scenario, simply swapping the Ethernet addresses is not correct. As part of witnessing traffic coming in via the network, we can build up a mapping of IP to MAC address. This mapping can then later be used to set the correct MAC address for a given destination IP. All of this functions entirely without interaction from userspace. Unless you are running in a LAN environment, most if not all IPs will point to the same MAC address (the one of the next IP layer hop, i.e. the router). For the very first packet that we want to relay, we will not have a MAC address for the destination IP. This doesn't matter though, we simply pass that packet up to userspace and handle it there. Pretty much all communication on the Internet is bi-directional because you need some kind of ACK. As soon as we receive the first ACK, e.g. the response to a binding request, we will learn the MAC address for the given target IP and the eBPF router can kick in for all packets going forward. Related: #7518
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.