fix(amneziawg): H1-H4 generator + queue-depth throughput fixes - #6330
Open
kuzzrus wants to merge 3 commits into
Open
fix(amneziawg): H1-H4 generator + queue-depth throughput fixes#6330kuzzrus wants to merge 3 commits into
kuzzrus wants to merge 3 commits into
Conversation
Both the Go generator and its frontend mirror picked a random *range* per H1-H4 field with only a minimum width enforced (no maximum). amneziawg-go's packet classifier only ever compares a fixed-size ciphertext prefix against these bounds, so a wide range buys no DPI resistance -- the boundaries themselves are never observable on the wire. It does cost real throughput: with randomTrailers on (the default here), the handshake-size checks relax from == to >, so a wide H-range misclassifies a proportional fraction of ordinary transport packets as handshakes and silently drops them (amnezia-vpn/amneziawg-go#183). A single value per field is strictly safer than any range, with no obfuscation trade-off. Live-tested: narrowing H1-H4 alone took AmneziaWG upload from 2-3 Mbit/s to 200+ Mbit/s on one box, and ~20 Mbit/s to 120-156 Mbit/s on another, single-variable, no other change.
1024 was sized for a single-connection buffering problem (the gVisor-to-amneziawg-go TUN handoff channel needing slack for the download direction). tcpip.Stack.Stats() during a real many-connection download (20-28 concurrent TCP flows, e.g. a segmented speed test) showed SlowStartRetransmits jump by ~770 in a single second the moment CurrentEstablished crossed ~20 -- consistent with many connections' simultaneous slow-start growth briefly exceeding 1024 outstanding packets and gVisor treating the resulting silent drops as real network loss.
Contributor
Code reviewNo blocking issues — Reviewed head: 🟡 Nit
Coverage
|
Review feedback: four comment blocks in the previous commits exceeded CLAUDE.md's 2-line-per-block hard rule (up to 13 lines). Trimmed each to the one non-obvious fact plus the amneziawg-go#183 reference; the fuller rationale already lives in the commit message. Also refreshed the stale H1-H4 range example in docs/content/docs/en/config/amneziawg.mdx to match the new single-value generator output.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent fixes for AmneziaWG throughput, both found and live-tested while investigating #6303 ("AmneziaWG import speed is extremely poor").
1. H1-H4 generator: single value instead of a range
GenerateObfuscation31(and its frontend mirror,generateAwgObfuscation) picked a random range for each of H1-H4, with only a minimum width enforced (hMinWidth = 1000), no maximum. amneziawg-go's packet classifier (DeterminePacketTypeAndPadding) only ever compares a fixed-size ciphertext prefix against these bounds to decide "handshake vs. transport" — so a wider range buys zero DPI resistance (the range boundaries themselves are never observable on the wire, only whichever single value the classifier lets through matters). It does cost real throughput:randomTrailersis generatedtruehere, and with it on, amneziawg-go's handshake-size checks relax from==to>, so a wide H-range starts misclassifying a proportional fraction of ordinary transport packets as handshakes and silently dropping them. This was independently found and filed the same week as amnezia-vpn/amneziawg-go#183, with a synthetic-packet regression test proving the mechanism.Live-tested on two boxes, single-variable (only H1-H4 narrowed, nothing else changed): upload went from 2-3 Mbit/s to 200+ Mbit/s on one, and a flat ~20 Mbit/s to 120-156 Mbit/s on another.
A single value per H field is strictly safer than any range — the same reasoning @alekskomp's own native-Amnezia-Docker config in #6303 already uses (
H1=1, H2=2, H3=3, H4=4). This changes what the generator produces, not what's accepted: a manually-entered range still validates fine (ValidateObfuscation/validateUintRangeuntouched).Fixed in both places that generate these values, since they're independent implementations that could silently drift apart again:
internal/amneziawg/params.go(Go, server-side) andfrontend/src/lib/xray/amneziawg-obfuscation.ts(what the inbound form's "Regenerate" button calls).2.
tunQueueDepth: 1024 → 8192A separate contributor to the same "AmneziaWG slower than expected" symptom, specifically on the download side. 1024 was originally sized for a single-connection buffering problem (the gVisor-to-amneziawg-go TUN handoff channel — see the pre-existing comment above the constant). Watching
tcpip.Stack.Stats()live during a real many-connection download (20-28 concurrent TCP flows, e.g. a segmented speed test) showedSlowStartRetransmitsjump by ~770 in a single second the momentCurrentEstablishedcrossed ~20 — consistent with several connections' simultaneous slow-start growth briefly exceeding 1024 outstanding packets, with gVisor's channel endpoint (whosequeue.Writeis non-blocking and drops silently when full) treating the resulting drops as ordinary network loss.Context
Both root causes, with the live-test numbers, are written up in more detail in my comment on #6303.
One thing worth flagging for review:
GenerateObfuscation31always turnsRandomTrailerson for a freshly generated set (it's a normal toggle a user can flip off afterward, but "on" is what every new/regenerated inbound gets) — which is exactly the setting that makes the H1-H4 misclassification bug bite. This PR doesn't touch that default; a single H-value is safe regardless ofrandomTrailers, so the two are orthogonal. Flagging only so reviewers have the full picture — not proposing a change there.Test plan
go vet/go testoninternal/amneziawgandinternal/amneziawgnettsc --noEmit,oxlint,oxfmt --check,vitest runon the touched frontend files