\chapter{Background} % Main chapter title \label{Background} \subsection{Nix: A Safe and Policy-Free System for Software Deployment} Nix addresses significant issues in software deployment by utilizing cryptographic hashes to ensure unique paths for component instances \cite{dolstra_nix_2004}. Features such as concurrent installation of multiple versions, atomic upgrades, and safe garbage collection make Nix a flexible deployment system. This work uses Nix to ensure that all VPN builds and system configurations are deterministic. \subsection{NixOS: A Purely Functional Linux Distribution} NixOS extends Nix principles to Linux system configuration \cite{dolstra_nixos_2008}. System configurations are reproducible and isolated from stateful interactions typical in imperative package management. This property is essential for ensuring identical test environments across benchmark runs. \subsection{UDP NAT and Firewall Puncturing in the Wild} Halkes and Pouwelse~\cite{halkes_udp_2011} measure UDP hole punching efficacy on a live P2P network using the Tribler BitTorrent client. Their study finds that 79\% of peers are unreachable due to NAT or firewall restrictions, yet 64\% reside behind configurations amenable to hole punching. Among compatible peers, over 80\% of puncturing attempts succeed, establishing hole punching as a practical NAT traversal technique. Their timeout measurements further indicate that keep-alive messages must be sent at least every 55 seconds to maintain open NAT mappings. These findings directly inform the evaluation criteria for this thesis. All mesh VPNs tested rely on UDP hole punching for NAT traversal; the 80\% success rate sets a baseline expectation, while the 55-second timeout informs analysis of each implementation's keep-alive behavior during source code review. \subsection{The Babel routing protocol} \label{sec:babel} Babel~\cite{chroboczek_babel_2021} is a distance-vector routing protocol designed for both wired and wireless mesh networks. Each node periodically sends \emph{Hello} messages to discover neighbours and \emph{Update} messages to advertise reachable prefixes along with a numeric cost metric. A node selects the route with the lowest cumulative metric for each destination, subject to a \emph{feasibility condition} that prevents routing loops. Because Babel is distance-vector rather than link-state, nodes only know the cost of their own best path, not the full topology. Two properties of Babel matter for the benchmarks in Chapter~\ref{Results}. First, route advertisements are periodic: a node will not learn about a new path until the next Update interval, which can be on the order of minutes depending on the implementation's timer settings. Second, Babel intentionally resists frequent route changes to avoid flapping; a node may continue using a suboptimal path until a significantly better alternative is advertised. Both properties can cause the selected route for a given destination to differ across consecutive benchmark runs, even when the physical topology has not changed. \subsection{TCP flow control and congestion control} \label{sec:tcp_windows} TCP uses two window mechanisms to regulate how much unacknowledged data a sender may have in flight. The \emph{receive window} (\texttt{rwnd}), also called the \emph{send window} in \texttt{iperf3} output, is advertised by the receiver and reflects how much buffer space it has available. The \emph{congestion window} (\texttt{cwnd}) is maintained locally by the sender and tracks the network's estimated capacity. At any point, the sender may transmit up to $\min(\texttt{rwnd}, \texttt{cwnd})$ bytes beyond the last acknowledged byte \cite{rfc5681}. The congestion window starts small (typically a few segments) and grows during the \emph{slow-start} phase, doubling each round trip until it reaches a threshold or triggers a loss event. After that, \emph{congestion avoidance} takes over and the window grows linearly. When the sender detects a loss (through duplicate ACKs or a retransmission timeout), it treats the loss as a signal of congestion: the window is reduced, often halved, and the sender enters a recovery phase before resuming growth. Each retransmission therefore has a direct mechanical cost: it shrinks the congestion window and reduces the instantaneous sending rate. The \emph{bandwidth-delay product} (BDP) determines how large the window must be to fully utilize a link. It is the product of the link's bandwidth and the round-trip time: \begin{equation} \text{BDP} = \text{bandwidth} \times \text{RTT} \label{eq:bdp} \end{equation} A 1\,Gbps link with a 1\,ms RTT has a BDP of 125\,KB: the sender must keep at least 125\,KB of unacknowledged data in flight to saturate the link. If the congestion window is smaller than the BDP, the sender will finish transmitting its window and then wait idle for acknowledgements, leaving bandwidth unused. High-latency paths make this problem worse because the BDP grows linearly with RTT. A 34\,ms RTT on the same 1\,Gbps link raises the BDP to 4.25\,MB, well beyond the default congestion window of most TCP stacks. One common workaround is to run multiple TCP flows in parallel: each flow maintains its own congestion window, and their aggregate in-flight data can approach the BDP even when no single flow could. In VPN benchmarks these two windows appear as distinct bottlenecks. A small receive window means the receiver (or the tunnel endpoint in front of it) cannot absorb data fast enough. A small congestion window means the path between sender and receiver is experiencing loss, forcing TCP into repeated recovery cycles. Comparing congestion windows across VPNs with different maximum segment sizes requires care, because the window is measured in bytes: a VPN with jumbo segments will report a larger byte-valued window for the same number of in-flight segments. \subsection{An Overview of Packet Reordering in TCP} TODO \cite{leung_overview_2007} \subsection{Performance Evaluation of TCP over QUIC Tunnels} TODO \cite{guo_implementation_2025}