diff --git a/internal/amneziawgnet/manager.go b/internal/amneziawgnet/manager.go index ae0b82993..9a4c326ad 100644 --- a/internal/amneziawgnet/manager.go +++ b/internal/amneziawgnet/manager.go @@ -203,6 +203,23 @@ func (m *Manager) Reconcile(desired []Desired) { } } +// Remove tears down inbound id's embedded interface, if any -- mirrors +// internal/amneziawg.Manager.Remove, for a caller that needs to drop a +// single inbound outside a full Reconcile pass (e.g. the immediate-apply +// CRUD path in internal/web/runtime/local.go). +func (m *Manager) Remove(id int) { + m.mu.Lock() + defer m.mu.Unlock() + cur, exists := m.ifaces[id] + if !exists { + return + } + cur.udpRelay.Close() + cur.dev.Close() + delete(m.ifaces, id) + logger.Infof("amneziawgnet: stopped embedded interface for removed inbound %d", id) +} + // StopAll tears down every managed interface. Called on panel shutdown. func (m *Manager) StopAll() { m.mu.Lock() diff --git a/internal/web/job/amneziawg_job.go b/internal/web/job/amneziawg_job.go index ddd21ae92..2fd0a3162 100644 --- a/internal/web/job/amneziawg_job.go +++ b/internal/web/job/amneziawg_job.go @@ -1,33 +1,32 @@ package job import ( - "github.com/mhsanaei/3x-ui/v3/internal/amneziawg" - "github.com/mhsanaei/3x-ui/v3/internal/database/model" + "github.com/mhsanaei/3x-ui/v3/internal/amneziawgnet" "github.com/mhsanaei/3x-ui/v3/internal/logger" "github.com/mhsanaei/3x-ui/v3/internal/web/service" - "github.com/mhsanaei/3x-ui/v3/internal/xray" ) -// AmneziaWGJob reconciles the running AmneziaWG interfaces against the -// enabled AmneziaWG inbounds in the database, restarts/reloads any that -// drifted, and folds the per-peer traffic scraped from `awg show dump` into -// the usual client and inbound traffic accounting. Mirrors MtprotoJob. +// AmneziaWGJob reconciles the running embedded AmneziaWG interfaces +// (internal/amneziawgnet -- amneziawg-go over a gVisor netstack, no kernel +// module) against the enabled AmneziaWG inbounds in the database, +// rebuilding/reconfiguring any that drifted. Unlike the retired +// kernel-module Manager this job used to drive, there is no traffic/ +// online-status accounting here at all: once a peer's decapsulated traffic +// is relayed into Xray's own SOCKS5 inbound (see +// internal/web/service/xray.go's injectAmneziawgnetSocks, and +// internal/amneziawgnet.Manager's automatic forwarder/relay wiring), it's +// an ordinary Xray user, and XrayTrafficJob's existing, protocol-blind +// stats/online-status polling already picks it up for free. type AmneziaWGJob struct { inboundService service.InboundService - // warnedMissing tracks whether the "awg/awg-quick not found" warning has - // already been logged, so a host without the AmneziaWG kernel module - // (RHEL, Arch, a container, or a failed install.sh PPA step) logs it - // once instead of every @every-10s tick forever. - warnedMissing bool } -// NewAmneziaWGJob creates a new AmneziaWG reconcile/traffic job instance. +// NewAmneziaWGJob creates a new AmneziaWG reconcile job instance. func NewAmneziaWGJob() *AmneziaWGJob { return new(AmneziaWGJob) } -// Run reconciles desired AmneziaWG inbounds with running interfaces and -// records per-peer traffic deltas and online status. +// Run reconciles desired AmneziaWG inbounds with running embedded interfaces. func (j *AmneziaWGJob) Run() { desired, err := j.inboundService.DesiredAmneziaWGInstances() if err != nil { @@ -35,59 +34,9 @@ func (j *AmneziaWGJob) Run() { return } - // Only relevant once an admin actually has an AmneziaWG inbound: no - // point warning about a missing binary the panel never needed to touch. - if len(desired) > 0 && !amneziawg.IsAwgInstalled() { - if !j.warnedMissing { - j.warnedMissing = true - logger.Warningf("amneziawg job: %d AmneziaWG inbound(s) configured but awg/awg-quick not found on PATH; skipping reconcile until installed", len(desired)) - } - return - } - j.warnedMissing = false - - activeTags := make([]string, 0, len(desired)) + wanted := make([]amneziawgnet.Desired, 0, len(desired)) for _, inst := range desired { - activeTags = append(activeTags, inst.Tag) + wanted = append(wanted, amneziawgnet.Desired{Instance: inst}) } - - mgr := amneziawg.GetManager() - mgr.Reconcile(desired) - - deltas, onlineEmails := mgr.CollectTraffic() - - clientTraffics := make([]*xray.ClientTraffic, 0, len(deltas)) - inboundUp := make(map[string]int64) - inboundDown := make(map[string]int64) - for _, d := range deltas { - clientTraffics = append(clientTraffics, &xray.ClientTraffic{ - Email: d.Email, - Up: d.Up, - Down: d.Down, - }) - inboundUp[d.Tag] += d.Up - inboundDown[d.Tag] += d.Down - } - - traffics := make([]*xray.Traffic, 0, len(inboundUp)) - for tag, up := range inboundUp { - traffics = append(traffics, &xray.Traffic{ - IsInbound: true, - Tag: tag, - Up: up, - Down: inboundDown[tag], - }) - } - - if len(traffics) > 0 || len(clientTraffics) > 0 { - if _, _, err := j.inboundService.AddTraffic(traffics, clientTraffics); err != nil { - logger.Warning("amneziawg job: add traffic failed:", err) - } - } - - // Live speed: AmneziaWG never runs inside xray-core, so XrayTrafficJob's - // own 5s broadcast never mentions these tags. See sidecar_traffic.go. - broadcastSidecarTraffic(string(model.AmneziaWG), traffics, clientTraffics) - - j.inboundService.RefreshLocalOnlineClients(onlineEmails, activeTags) + amneziawgnet.GetManager().Reconcile(wanted) } diff --git a/internal/web/runtime/local.go b/internal/web/runtime/local.go index 4ae401d6a..978003cce 100644 --- a/internal/web/runtime/local.go +++ b/internal/web/runtime/local.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/mhsanaei/3x-ui/v3/internal/amneziawg" + "github.com/mhsanaei/3x-ui/v3/internal/amneziawgnet" "github.com/mhsanaei/3x-ui/v3/internal/database/model" "github.com/mhsanaei/3x-ui/v3/internal/mtproto" "github.com/mhsanaei/3x-ui/v3/internal/xray" @@ -59,7 +60,7 @@ func (l *Local) AddInbound(_ context.Context, ib *model.Inbound) error { if !ok { return nil } - return amneziawg.GetManager().Ensure(inst) + return amneziawgnet.GetManager().Ensure(amneziawgnet.Desired{Instance: inst}) } body, err := json.MarshalIndent(ib.GenXrayInboundConfig(), "", " ") if err != nil { @@ -76,7 +77,7 @@ func (l *Local) DelInbound(_ context.Context, ib *model.Inbound) error { return nil } if ib.Protocol == model.AmneziaWG { - amneziawg.GetManager().Remove(ib.Id) + amneziawgnet.GetManager().Remove(ib.Id) return nil } return l.withAPI(func(api *xray.XrayAPI) error { @@ -130,11 +131,12 @@ func (l *Local) updateMtprotoInbound(ctx context.Context, oldIb, newIb *model.In // updateAmneziaWGInbound mirrors updateMtprotoInbound: it skips the // Remove+Ensure sequence a plain Del+Add would force so that, on an // AmneziaWG-to-AmneziaWG edit, Manager.Ensure's own fingerprint comparison -// can pick a peers-only `syncconf` instead of always bouncing the interface -// (see internal/amneziawg.Manager.ensureLocked). +// can reconfigure the running embedded Device in place via IpcSet instead +// of always rebuilding it (see internal/amneziawgnet.Manager.ensureLocked -- +// only an address/MTU change forces a rebuild there, not a peer edit). func (l *Local) updateAmneziaWGInbound(ctx context.Context, oldIb, newIb *model.Inbound) error { if oldIb.Protocol == model.AmneziaWG && newIb.Protocol != model.AmneziaWG { - amneziawg.GetManager().Remove(oldIb.Id) + amneziawgnet.GetManager().Remove(oldIb.Id) if !newIb.Enable { return nil } @@ -144,15 +146,15 @@ func (l *Local) updateAmneziaWGInbound(ctx context.Context, oldIb, newIb *model. _ = l.DelInbound(ctx, oldIb) } if !newIb.Enable { - amneziawg.GetManager().Remove(newIb.Id) + amneziawgnet.GetManager().Remove(newIb.Id) return nil } inst, ok := amneziawg.InstanceFromInbound(newIb) if !ok { - amneziawg.GetManager().Remove(newIb.Id) + amneziawgnet.GetManager().Remove(newIb.Id) return nil } - return amneziawg.GetManager().Ensure(inst) + return amneziawgnet.GetManager().Ensure(amneziawgnet.Desired{Instance: inst}) } func (l *Local) AddUser(_ context.Context, ib *model.Inbound, userMap map[string]any) error { diff --git a/internal/web/web.go b/internal/web/web.go index b12708d56..6b4183fcc 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -16,7 +16,7 @@ import ( "strings" "time" - "github.com/mhsanaei/3x-ui/v3/internal/amneziawg" + "github.com/mhsanaei/3x-ui/v3/internal/amneziawgnet" "github.com/mhsanaei/3x-ui/v3/internal/config" "github.com/mhsanaei/3x-ui/v3/internal/eventbus" "github.com/mhsanaei/3x-ui/v3/internal/logger" @@ -689,7 +689,7 @@ func (s *Server) stop(stopXray bool, stopTgBot bool) error { if stopXray { _ = s.xrayService.StopXray() mtproto.GetManager().StopAll() - amneziawg.GetManager().StopAll() + amneziawgnet.GetManager().StopAll() } if s.cron != nil { s.cron.Stop()