From 96069e9393e516557161156d1f1f577c6e8e07a0 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sun, 25 Oct 2026 11:31:38 +0100 Subject: [PATCH] perf(amneziawgnet): autotune the embedded TCP receive buffer up to 8 MB gVisor ships with receive-buffer moderation off and a fixed 1 MB window capped at 4 MB, so every client upload relayed through the embedded datapath was window-bound on any real-world RTT. Moderation is now on and may grow a connection to 8 MB; connections still start at gVisor's 1 MB default and only grow while the relay drains them faster per RTT, so idle peers cost no more memory. Single-stream client upload through the panel's own embedded inbound (netns client, 50 ms RTT via netem, 16 vCPU WSL2 host, iperf3 -t 15): before (fixed buffer) 308 / 309 Mbit/s moderation, 4 MB cap 312 / 311 Mbit/s moderation, 8 MB cap 490 / 509 Mbit/s 0 ms RTT, before / after 1789-1928 / 1795 Mbit/s No test can observe a gVisor tuning option without restating it; the numbers come from a real panel + Xray run in network namespaces with a kernel AmneziaWG client. --- internal/amneziawgnet/netstack.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/internal/amneziawgnet/netstack.go b/internal/amneziawgnet/netstack.go index f1762a36d..a9a08c41f 100644 --- a/internal/amneziawgnet/netstack.go +++ b/internal/amneziawgnet/netstack.go @@ -27,6 +27,10 @@ import ( // 1024 starved simultaneous TCP slow-starts; channel.Endpoint drops silently when full. const tunQueueDepth = 8192 +// maxReceiveBuffer caps per-connection receive autotuning; gVisor's own 4 MB +// cap held a single client upload near 310 Mbit/s at 50 ms RTT. +const maxReceiveBuffer = 8 << 20 + // stackTun implements amneziawg-go tun.Device over a gVisor channel endpoint, // exposing *stack.Stack for forwarder attachment. type stackTun struct { @@ -63,6 +67,15 @@ func createNetTUNWithStack(localAddresses []netip.Addr, mtu int) (awgtun.Device, if err := dev.stack.SetTransportProtocolOption(tcp.ProtocolNumber, &sackEnabledOpt); err != nil { return nil, nil, fmt.Errorf("amneziawgnet: enable TCP SACK: %s", err) } + // Connections start at gVisor's 1 MB default and only grow while the + // relay drains them faster per RTT, so idle peers cost no more memory. + rcvBufOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: tcp.MinBufferSize, Default: tcp.DefaultReceiveBufferSize, Max: maxReceiveBuffer} + moderateOpt := tcpip.TCPModerateReceiveBufferOption(true) + for _, opt := range []tcpip.SettableTransportProtocolOption{&rcvBufOpt, &moderateOpt} { + if err := dev.stack.SetTransportProtocolOption(tcp.ProtocolNumber, opt); err != nil { + return nil, nil, fmt.Errorf("amneziawgnet: tune TCP receive buffer: %s", err) + } + } dev.notifyHandle = dev.ep.AddNotify(dev) if err := dev.stack.CreateNIC(1, dev.ep); err != nil { return nil, nil, fmt.Errorf("amneziawgnet: CreateNIC: %s", err)