Files
shater/SPECS/007-AWG_OVER_WIREGUARD_DETOUR_GUARD/PLAN.md
T
Leadaxe 09944c0103 docs(specs): drop type/status from folder names — header + Roadmap only
Folder names were NNN-T-S-NAME, so the status (S) letter forced a rename on
every status change — and refs to the full name went stale each time. One was
already broken in-tree (client.go pointed at 002-F-O-… while the folder was
002-F-C), and the submodule needed a cosmetic commit once already (010 O→C).

Make the number the only stable anchor:
- Rename all 12 folders NNN-T-S-NAME → NNN-NAME (git mv, history preserved).
- Type/status now live in a table header at the top of each SPEC.md (canon),
  aggregated by the Roadmap in SPECS/README.md (added missing 010, 012).
- Fix every ref to the old full name: README(.ru), docs/lx-changelog,
  docs/lx-config, intra-SPECS cross-links, PROBE.md git-apply path,
  TASKS.md titles, and transport/v2rayxhttp/client.go:5 (also un-stales O).
- Rewrite the convention + Workflow in SPECS/README.md and the DoD ritual in
  IMPLEMENTATION_PROMPT.md ("rename folder to …-C-…" → "set status in header
  + Roadmap").
- Bump submodule wireguard-go (0c0c10b): fix comments point at the new folder
  name SPECS/010-WG_ENDPOINT_GRO_SPLIT_BRAIN. Comment-only, no behavior change.

Not touched: gro-probe.patch (historical diagnostic diff artifact; §010 closed,
no longer applied).
2026-06-22 11:36:37 +03:00

4.1 KiB

PLAN: 007 — AWG_OVER_WIREGUARD_DETOUR_GUARD

Архитектура решения

Guard живёт в ленивом резолве detour (common/dialer/detour.go DetourDialer.init()), рядом с upstream-проверкой empty direct outbound. Это даёт вариант B тем же механизмом, что и empty-direct: ошибка кэшируется в initErr (через sync.Once), DialContext/ListenPacket возвращают её вместо соединения. Ядро стартует без ошибок, прочие узлы работают, AWG-нода фейлит каждый dial. Не fatal на старте.

Откуда «источник AWG»

DetourDialer сам по себе не знает тип владельца. Прокидываем флаг: dialer.Options.IsAmneziaWG → NewDetour(..., ownerIsAmneziaWG). protocol/wireguard.NewEndpoint выставляет его из options.AmneziaWGOptions.IsSet().

Как «цель WireGuard»

Резолвленная detour-цель приводится к adapter.Outbound; триггер — Type() == C.TypeWireGuard (покрывает и плоский WG, и AWG — тип один). Группы (adapter.OutboundGroup) раскрываются рекурсивно через All() + OutboundManager.Outbound(tag), с set'ом посещённых против циклов.

Изменённые файлы

Файл Зона Что
common/dialer/detour.go // lx: upstream поле ownerIsAmneziaWG, guard в init(), рекурсивный detourTargetIsWireGuard, новый параметр NewDetour
common/dialer/dialer.go // lx: upstream поле Options.IsAmneziaWG, проброс в NewDetour
protocol/wireguard/endpoint.go // lx: upstream (AWG-шов) IsAmneziaWG: options.AmneziaWGOptions.IsSet() в dialer.Options
common/dialer/awg_detour_guard_test.go новый lx-файл unit-тест матрицы + init-пути

Логика guard (как реализовано)

// detour.go init(), после empty-direct:
if d.ownerIsAmneziaWG && detourTargetIsWireGuard(mgr, dialer, map[string]bool{}) {
    d.initErr = E.New("amneziawg endpoint cannot detour through a wireguard-based "+
        "endpoint (detour: ", d.detour, "): AmneziaWG inside a WireGuard tunnel "+
        "hangs the kernel on Android; use a non-wireguard detour (e.g. vless)")
    return
}

func detourTargetIsWireGuard(mgr, ob, visited) bool {
    if o, ok := ob.(adapter.Outbound); ok && o.Type() == C.TypeWireGuard { return true }
    if g, ok := ob.(adapter.OutboundGroup); ok {
        for _, tag := range g.All() {
            if visited[tag] { continue }
            visited[tag] = true
            if m, loaded := mgr.Outbound(tag); loaded && detourTargetIsWireGuard(mgr, m, visited) { return true }
        }
    }
    return false
}

DoD (IMPLEMENTATION_PROMPT §2) — факт

  • go build ./... без тегов — ок.
  • go build -tags "...,with_awg" ./cmd/sing-box — ок.
  • go test ./common/dialer/... — зелёный (8 подтестов).
  • gofmt -l изменённых файлов — пусто.
  • go vet ./common/dialer/... ./protocol/wireguard/... — чисто.

Зона касания upstream (для ребейза)

  • common/dialer/detour.go, common/dialer/dialer.go, protocol/wireguard/endpoint.go — upstream-файлы, правки только в // lx: блоках. Конфликт на ребейзе — лишь если upstream перепишет DetourDialer.init / сигнатуру NewDetour / dialer.Options / конструктор endpoint.
  • awg_detour_guard_test.go — lx-собственный, конфликтов не даёт.
  • Сигнатура NewDetour расширена 4-м параметром — единственный внешний вызов в dialer.go обновлён; других вызовов в дереве нет.