104 Commits
Author SHA1 Message Date
Markus Pettersson 805068bd80 Use SIMD instructions for LWO 2 data packet path
Reimplement the `xor_protected_area` algorithm using explicit SIMD
instructions for the data packet path. This is by far the most common
type of WireGuard packet, and it also maps very cleanly onto a 8x16 SIMD
vector type since the first 16 bytes of a data packet is obfuscated in
LWO.

This path seems to auto-vectorize well on x86_64 (I saw no performance
increase on my AMD Ryzen 7 PRO 6850U), but less so on more niche CPU
archs. On Raspberry PI I saw a ~20% increase in throuhput and on Mac Neo
I saw a ~25% increase in throuhput.
2026-09-29 10:01:57 +02:00
David Lönnhager ce139350be Move GotaTun obfuscation transports to tunnel-obfuscation 2026-09-22 15:50:53 +02:00
David Lönnhager fab4185484 Remove ObfuscatedTransport::endpoint
The source address we report when implementing `UdpRecv` should not
actually matter. Let `talpid-wireguard` take care of it and just set it
to the wireguard endpoint instead.
2026-09-15 11:35:22 +02:00
David Lönnhager 122ea16cdf Make the multiplexer an obfuscated transport
It no longer pays the large cost of having a local UDP socket in front
of it. The `ObfuscatedTransport` does do some work on each `recv` to
handle different multiplexer phases. I didn't attempt to optimize this
because the cost was negligible compared to the socket syscalls.

The socket interface is still available by wrapping the multiplexer in a
`LocalSocketRunner`, as with other obfuscation types.
2026-09-15 11:35:22 +02:00
David Lönnhager d9cc008527 Use shared constants for LWOv2 2026-09-01 17:18:57 +02:00
David Lönnhager dbf1bc77e9 Update multiplexer bench 2026-09-01 17:18:55 +02:00
David Lönnhager fba7f0599b Validate the handshake before selecting a transport
The multiplexer now records the `sender_index` of every handshake
initiation it fans out, and only selects a transport that answers one
of them with a handshake response whose `mac1` is valid for the
client's public key.
2026-09-01 17:18:52 +02:00
David Lönnhager 41890833bb Add criterion benches for multiplexer throughput
Sending one packet through the multiplexer to a relay on loopback, with a
single LWO transport (medians):

				 before      after
	plain UDP (control)     6.28 µs    6.29 µs
	single obfuscator      21.37 µs   21.54 µs
	 (via socket)
	multiplexer            32.56 µs   21.44 µs

The 11 µs is one loopback round trip, which puts the multiplexer level
with reaching the obfuscator directly plus one loopback socket.
2026-09-01 13:16:57 +02:00
David Lönnhager 5ec9c1707a Derive the multiplexer's packet overhead from its transports
The multiplexer reported a hardcoded 60. It now reports the largest
overhead among the transports it may select, which is what has to fit
while it is still deciding which one to use.
2026-09-01 13:16:57 +02:00
David Lönnhager 041ca5ebfc Send multiplexer traffic directly on each transport
Drive obfuscation transports using `ObfuscatedTransport` instead of
loopback sockets.

The multiplexer itself is still behind a loopback socket.

A transport that fails to receive is now dropped instead of failing the
multiplexer, so the remaining ones can still connect.
2026-09-01 13:16:57 +02:00
Markus Pettersson 964f75d77a Run tombi format 2026-08-28 15:02:09 +02:00
Markus Pettersson e289c71982 Promote udp-over-tcp to a workspace dependency 2026-08-27 15:17:58 +02:00
David Lönnhager bcf31dd2db Add benchmark for LWOv2 send path with padding
`pad` followed by `obfuscate`, against `obfuscate` alone:

                            obfuscate   pad + obfuscate
    data (1232 B)             8.80 ns          13.30 ns
    handshake init (148 B)   70.98 ns         190.29 ns
2026-08-27 14:49:04 +02:00
David Lönnhager 2f16bd2d8e Define LWO v2 timers in tunnel-obfuscation 2026-08-27 14:49:02 +02:00
David Lönnhager 74171b8c6f Add LWO v2 criterion benchmark to tunnel-obfuscation
LWO v2 is ~2x faster, likely since it XORs only the 16-byte protected
area rather than the full 32-byte header:

                  v1                      v2
    obfuscate     20.97 ns (54.7 GiB/s)   9.87 ns (116.3 GiB/s)
    deobfuscate   19.10 ns (60.1 GiB/s)   9.02 ns (127.2 GiB/s)
2026-08-27 14:48:48 +02:00
David Lönnhager a5512029e6 Implement LWO v2
Improves LWO obfuscation by making WG traffic less recognizable (e.g.
via timers and packet sizes).
2026-08-27 14:48:28 +02:00
David Lönnhager a84938bbd9 Subtract the obfuscation overhead when it is applied inline
The MTU was only reduced by the obfuscation's per-packet overhead when
the obfuscator ran behind a local socket.
2026-08-25 16:50:37 +02:00
David Lönnhager 835a8ef9e1 Drive QUIC through the generic obfuscated transport
I could not find any measurable overhead due to dynamic dispatch (for
`dyn Transport`) in `MaybeObfuscatingTransportFactory`.
2026-08-25 16:50:37 +02:00
David Lönnhager c6996952bf Add MaybeObfuscatingTransportFactory variant for any ObfuscatedTransport
This adds userspace obfuscation for udp2tcp and Shadowsocks.
2026-08-25 16:50:37 +02:00
David Lönnhager 0750e4f3b9 Add a send/recv interface for obfuscation methods
Each obfuscation method implements a send()/recv() interface --
`ObfuscatedTransport` -- rather than binding a client socket on localhost
and serving it itself.

The local socket becomes a layer on top of that interface.
2026-08-24 19:02:46 +02:00
David Lönnhager 750ebfa81b Bump udp-over-tcp to latest commit
`udp-over-tcp` now exposes `process_udp_over_tcp`, so we can remove
some unsafe raw socket shenanigans.
2026-08-18 15:33:49 +02:00
David Lönnhager e6a97a90bb Restrict firewall to the selected obfuscation endpoint
While connecting with multiplexer obfuscation, every candidate
obfuscation endpoint has to be allowed through the firewall, since it is
not known which one will be used. Previously that wide set was kept for
the entire lifetime of the tunnel. The multiplexer now announces the
remote endpoint it commits to, and the connected state only allows that
one.

The multiplexer also stops the transports it did not select.

Note that the host routes to each candidate endpoint, added on macOS and
Windows, are left in place.
2026-08-13 12:58:01 +02:00
Sebastian Holmin 51f3251a62 Add inline QUIC transport for GotaTun
Added `QuicTransportFactory` with `QuicSend`/`QuicRecv` channel-based
adapters that let GotaTun proxy traffic through QUIC without a local
UDP socket.
2026-08-05 13:52:30 +02:00
Sebastian Holmin d785142355 Refactor masque-proxy-client with connected socket 2026-08-05 13:50:35 +02:00
Sebastian Holmin 9ef7a1a30c Refactor local socket obfuscation setup 2026-08-05 13:50:35 +02:00
David Lönnhager 7b1fbea5ee Add newtype wrapping a socket and its bypass guard 2026-07-28 15:13:54 +02:00
David Lönnhager f61ad727e5 Remove fwmark from all obfuscator settings 2026-07-28 15:13:50 +02:00
David Lönnhager 4af2660ce6 Remove Obfuscator::remote_socket_fd
This method has been superseded by the SocketBypass trait
2026-07-28 15:13:46 +02:00
David Lönnhager 5a1d32037d Bypass remote sockets in obfuscators
Exclude every socket that talks to a remote obfuscation server from the
tunnel, using the bypass provided by the caller. The default bypass is a
no-op, so this is a no-op until a caller provides an implementation.
2026-07-28 15:13:39 +02:00
David Lönnhager 0d30010690 Update udp-over-tcp 2026-07-28 15:13:29 +02:00
Samy 96481692a2 Ignore unselected multiplexer packets 2026-07-15 15:02:56 +02:00
Ap4sh d0c9eb4cd7 Fix LWO marker byte randomization
Signed-off-by: Markus Pettersson <markus.pettersson@mullvad.net>
2026-07-03 11:12:41 +02:00
Markus Pettersson 11cb82c505 Promote async-trait to a workspace dependency 2026-06-05 08:16:50 +02:00
Joakim Hulthe 3df0c826ec Update udp-over-tcp 2026-04-29 11:21:28 +02:00
Markus Pettersson 90dc29d89e Update dependants of rand 0.8.5 2026-04-15 14:25:38 +02:00
David Lönnhager 87fdc063ba Switch to ThreadRng for LWO obfuscation
SmallRng is not noticeably faster
2026-04-14 09:31:33 +02:00
David Lönnhager ac666b7b4f Implement inline LWO obfuscation for GotaTun (no proxy round-trip)
When GotaTun is used with LWO obfuscation, packets previously traveled
through a localhost UDP proxy:

  GotaTun -> loopback UDP -> LWO proxy -> remote socket -> relay

Kernel WireGuard continues to use the existing proxy path unchanged, as
do other obfuscation methods
2026-04-14 09:31:33 +02:00
Joakim Hulthe 4b5411d321 Fix broken docstrings in tunnel-obfuscation 2026-03-04 15:53:39 +01:00
Sebastian Holmin 6cd822db94 Format Cargo.toml files using tombi 2026-01-23 15:05:36 +01:00
Linus Färnstrand 1aad3d5a44 Remove unused dependencies found with cargo-machete 2026-01-08 16:46:28 +01:00
Markus Pettersson 778f3afb79 Upgrade shadowsocks to 1.23.2 2025-12-10 12:59:43 +01:00
Markus Pettersson a0eedb98df Update all references of boringtun to gotatun 2025-11-11 21:41:52 +01:00
Joakim Hulthe 91058627be Ensure we yield result from first task that errors 2025-11-07 10:50:37 +01:00
Markus Pettersson a2ec1fa984 Fix running masque proxy not aborting spawned tasks when dropped 2025-11-07 10:50:22 +01:00
Markus Pettersson 87b3bb87a5 Connect LWO obfuscator when run is invoked
Fixes race condition with socket bypass on Android
2025-10-09 16:07:54 +02:00
David Lönnhager 3ec7bb153b Improve xor_bytes performance 2025-09-22 09:58:20 +02:00
David Lönnhager a074cb8e36 Add unit test for multiplexer obfuscation 2025-09-18 17:20:50 +02:00
David Lönnhager c7385fc062 Limit number of initial packets in multiplexer 2025-09-18 17:20:49 +02:00
David Lönnhager 72ec0d4db8 Add documentation for multiplexer obfuscation 2025-09-18 17:20:44 +02:00
David Lönnhager 9ddc3c1ddf Add multiplexer POC to daemon, TSM, and relay selector 2025-09-18 17:20:42 +02:00