Files
3x-ui/internal/amneziawg/portfwd.go
T
Kuzz007 59dff059c1 feat(amneziawg): retire the kernel-module OS-shellout code and install.sh path
Hard cutover, part 3: everything that only ever existed to drive the
kernel-module (DKMS) + awg-quick + TPROXY architecture is gone now that
internal/amneziawgnet's embedded path is wired in as the real thing.

internal/amneziawg/manager.go -> instance.go (renamed, ~90% smaller): kept
InstanceFromInbound and its direct helpers (interfaceNameForID,
serverAddress, serverAddressV6) plus the exported FirstIPv4 (still used by
server.go's access-log email index) -- all pure, protocol-shape-only code
with no OS dependency, reused by both the old and new paths historically.
Deleted the old Manager (GetManager/Ensure/Reconcile/StopAll/CollectTraffic/
the fingerprint methods), generateServerConfig and everything under it
(writeObfuscation, defaultPostUpDown, appendOrTrue, detectDefaultInterface),
and process control (interfaceUp/Down, syncConfig, getPeerStats,
IsAwgInstalled). route_egress.go deleted entirely (the TPROXY bridge's
port/fwmark/table constants and rule-rendering, fully superseded by
internal/amneziawgnet's SOCKSPortForInbound/SocksPassword). portfwd.go
trimmed to just the parsing/validation half (ForwardedPortsInclude, still
used for save-time conflict checks); the iptables DNAT rendering half is
gone -- per-client port-forwarding has no equivalent under the embedded
path yet (tracked as Phase 3.6).

install.sh: removed install_ndppd, enable_ipv6_forwarding,
enable_tproxy_support, should/install_amneziawg, and check_secure_boot (and
their call sites) -- roughly 265 lines. No more DKMS build, PPA/keyring
setup, TPROXY kernel module loading, or Secure Boot warning: the embedded
path needs none of it.

Not in this commit (tracked as an explicit follow-up, not silently
dropped): the frontend's routeThroughXray toggle is now vestigial (the
field stays in the Go/JSON schema for backward compat with existing stored
settings, see types.go) but its UI/schema removal needs the frontend
type-regen + openapi.json hand-patch dance this fork always does for a
settings-shape change, which is its own separate pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 20:01:08 +03:00

94 lines
2.7 KiB
Go

package amneziawg
import (
"fmt"
"strconv"
"strings"
)
// portSpec is a single port (start == end) or an inclusive range start..end.
type portSpec struct {
start int
end int
}
// parseForwardedPorts splits a user-supplied string ("80, 443; 8000-8100")
// into validated port specs. Tokens are separated by comma or semicolon;
// whitespace is ignored. Invalid tokens are silently dropped — the input is
// a free-form text field and validation is best-effort by design.
func parseForwardedPorts(input string) []portSpec {
if input == "" {
return nil
}
input = strings.ReplaceAll(input, ";", ",")
tokens := strings.Split(input, ",")
var specs []portSpec
seen := make(map[string]struct{}, len(tokens))
for _, tok := range tokens {
tok = strings.TrimSpace(tok)
if tok == "" {
continue
}
spec, ok := parsePortToken(tok)
if !ok {
continue
}
key := fmt.Sprintf("%d-%d", spec.start, spec.end)
if _, dup := seen[key]; dup {
continue
}
seen[key] = struct{}{}
specs = append(specs, spec)
}
return specs
}
func parsePortToken(tok string) (portSpec, bool) {
if idx := strings.IndexByte(tok, '-'); idx >= 0 {
start, ok1 := parsePortNumber(strings.TrimSpace(tok[:idx]))
end, ok2 := parsePortNumber(strings.TrimSpace(tok[idx+1:]))
if !ok1 || !ok2 || start > end {
return portSpec{}, false
}
return portSpec{start: start, end: end}, true
}
p, ok := parsePortNumber(tok)
if !ok {
return portSpec{}, false
}
return portSpec{start: p, end: p}, true
}
func parsePortNumber(s string) (int, bool) {
n, err := strconv.Atoi(s)
if err != nil || n < 1 || n > 65535 {
return 0, false
}
return n, true
}
// ForwardedPortsInclude reports whether port is covered by any spec in a raw
// ForwardedPorts string (a single port or an inclusive range). Used for
// save-time validation that a client isn't about to hijack the panel's own
// port or another inbound's port -- see
// internal/web/service/inbound_amneziawg.go's port-conflict checks.
//
// The field itself is currently inert: per-client port-forwarding was
// implemented via PostUp/PostDown iptables DNAT rules under the retired
// kernel-module architecture (internal/amneziawg's old Manager), which had
// no equivalent under the embedded amneziawg-go path
// (internal/amneziawgnet) as of the hard cutover -- see the migration
// plan's Phase 3.6 for the panel-side relay design that will restore it.
// The field and this validation are kept so existing values aren't lost and
// re-validated identically once that phase lands, not because anything
// currently acts on them.
func ForwardedPortsInclude(forwardedPorts string, port int) bool {
for _, spec := range parseForwardedPorts(forwardedPorts) {
if port >= spec.start && port <= spec.end {
return true
}
}
return false
}