Commit Graph

5 Commits

Author SHA1 Message Date
MHSanaei 5e6ffebdf2 fix(amneziawg): give each peer a share of the forwarder and UDP relay
The embedded interface's TCP forwarder admits 65535 connections in
flight for all peers together, and a forwarded request holds its slot
until the handshake ends or gVisor gives up on it. One client sending
bare SYNs from fresh source ports therefore took every slot and left
the other clients' connections unanswered. The UDP relay likewise
opened a SOCKS5 association and a host UDP socket for every fresh
tunnel source port with no per-peer bound.

The forwarder now names the peer on the SYN, refuses a source no peer
owns and caps one peer at 4096 connections; the UDP relay caps one peer
at 1024 sessions and drops the datagrams that would need more.
2026-11-01 00:16:04 +01:00
MHSanaei 53ee53ed9c fix(amneziawg): take a forwarded TCP connection's source from the request
The TCP forwarder handler read the tunnel client's address from the
accepted connection's RemoteAddr, which asks the live endpoint and is
nil once the peer's RST has been processed. A client sending SYN, ACK
and RST through its own tunnel could hit that window and crash the panel
on a goroutine with no recover. The forwarder now hands the handler the
source from the request's endpoint ID, as it already did for the
destination.
2026-10-31 15:05:12 +01:00
MHSanaei 805f94a00c chore(amneziawgnet): bind test sockets to loopback
Windows Firewall prompted on every run of amneziawgnet.test.exe, since
the tests opened AmneziaWG, outbound-client and port-forward sockets on
all interfaces and go test rebuilds the binary under a fresh temp path,
so a granted exception never sticks.

Route every "all interfaces" bind through wildcardBindHost, which is
empty in production (behaviour unchanged) and pinned to 127.0.0.1 by
the package TestMain.
2026-10-03 00:00:36 +02:00
MHSanaei 09617f04f5 feat(panel): let a sponsor slot start at a scheduled time
sponsors.json only had an end date, so a booked placement had to be
added to the file on the day it started. An optional `from` now hides
a sponsor (and its logo) until that instant; entries without it show
immediately as before.
2026-09-27 16:32:45 +02:00
Sanaei 24cb6bfe1f perf(amneziawg): return gVisor's pooled buffers on the embedded data path
Every packet crossing the embedded AmneziaWG interface allocated instead of
reusing gVisor's pools, in both directions. stackTun.Write injected each
decrypted packet and never called DecRef, so the packet buffer and its chunk
were never returned; stackTun.Read copied each view out and never released
it. gVisor's own link endpoints settle the ownership question -- loopback.go
and sharedmem.go both DecRef immediately after DeliverNetworkPacket, because
the injector owns the buffer.

AttachUDPHandler compounded it by cloning a packet buffer it then dropped on
the floor, on top of a Data().AsRange().ToSlice() that already returns an
owned copy, so the clone bought nothing and stranded a pooled buffer plus a
cloned view per datagram.

Measured with the benchmarks added here:

  stackTunWrite (upload)     794ns -> 107ns   4 -> 0 allocs
  stackTunRead  (download)   707ns -> 129ns   3 -> 0 allocs
  UDP datagram, end to end  2.69us -> 1.58us  8 -> 2 allocs

The remaining UDP allocation is the ToSlice copy itself. Through a real
handshaked tunnel -- both devices in one process over loopback, so
ChaCha20-Poly1305 and the UDP syscalls dominate -- it is worth -48% bytes/op
and -33% allocs/op, and about +4.8% throughput in each direction (n=18,
p<=0.01). On a small VPS, where the allocation pressure is not spread over
24 idle cores, the throughput share should be larger; that part is reasoning,
not something measured here.

The three regression tests assert allocations per packet rather than timing,
since the defect is the pool miss, not the nanoseconds. Thresholds leave room
for the extra allocation -race adds.
2026-09-04 14:56:54 +02:00