mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-05 09:57:14 +00:00
58671533bb
New internal/amneziawgnet package: builds a real amneziawg-go Device over a gVisor netstack from an existing amneziawg.Instance, with a TCP/UDP forwarder that recovers each tunnel connection's real destination and a peer-identity index keyed by AllowedIPs. This is the foundation for migrating AmneziaWG off the kernel-module+TPROXY path (see the AmneziaWG-go vs kernel-module decision) -- nothing wires into live traffic yet, that's Phase 2 (relay into Xray's own SOCKS5 inbound). Covered by three real end-to-end tests: a genuine handshake + TCP forwarder + identity resolution, the same for UDP (including a reply routed back through the tunnel), and the manager's reconfigure-in-place vs. rebuild lifecycle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
63 lines
1.9 KiB
Go
63 lines
1.9 KiB
Go
package amneziawgnet
|
|
|
|
import (
|
|
"net/netip"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
|
|
)
|
|
|
|
// PeerIndex resolves a decapsulated connection's tunnel-internal source
|
|
// address back to the peer it belongs to, the same role Xray-core's own
|
|
// wireguard proxy's GetUserByAddr plays -- sourced here from an
|
|
// amneziawg.Instance's own Peers (already carries Email per peer, no new
|
|
// data needed) rather than a separate user table.
|
|
type PeerIndex struct {
|
|
entries []peerIndexEntry
|
|
}
|
|
|
|
type peerIndexEntry struct {
|
|
prefix netip.Prefix
|
|
peer amneziawg.Peer
|
|
}
|
|
|
|
// NewPeerIndex builds a lookup index from peers' AllowedIPs. Entries with an
|
|
// unparseable AllowedIPs value are skipped rather than failing the whole
|
|
// index -- by the time an Instance reaches this package, AllowedIPs has
|
|
// already been accepted at save time (see internal/amneziawg's own
|
|
// validation), so a bad entry here would only mean stale/manually-edited
|
|
// data, not something worth refusing to serve the rest of the peers over.
|
|
func NewPeerIndex(peers []amneziawg.Peer) *PeerIndex {
|
|
idx := &PeerIndex{}
|
|
for _, p := range peers {
|
|
for _, a := range p.AllowedIPs {
|
|
prefix, err := netip.ParsePrefix(a)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
idx.entries = append(idx.entries, peerIndexEntry{prefix: prefix, peer: p})
|
|
}
|
|
}
|
|
return idx
|
|
}
|
|
|
|
// Lookup returns the peer whose AllowedIPs most specifically contains addr --
|
|
// the same longest-prefix-match rule a real AmneziaWG interface's own
|
|
// AllowedIPs routing table uses for outbound packets, applied here in
|
|
// reverse to attribute an inbound (tunnel-internal-source) packet back to
|
|
// its owning peer.
|
|
func (idx *PeerIndex) Lookup(addr netip.Addr) (amneziawg.Peer, bool) {
|
|
bestBits := -1
|
|
var bestPeer amneziawg.Peer
|
|
for _, e := range idx.entries {
|
|
if e.prefix.Bits() <= bestBits || !e.prefix.Contains(addr) {
|
|
continue
|
|
}
|
|
bestBits = e.prefix.Bits()
|
|
bestPeer = e.peer
|
|
}
|
|
if bestBits < 0 {
|
|
return amneziawg.Peer{}, false
|
|
}
|
|
return bestPeer, true
|
|
}
|