From d06e02998ba7a14e620db9557514b7b298e847f1 Mon Sep 17 00:00:00 2001 From: sagit Date: Mon, 9 Feb 2026 03:12:33 +0000 Subject: [PATCH 1/6] fix(limiter): fix traffic limiter ScopeClient behavior to allow per-user limits --- go-gost/x/limiter/traffic/traffic.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/go-gost/x/limiter/traffic/traffic.go b/go-gost/x/limiter/traffic/traffic.go index 6f9c2b2..785fc86 100644 --- a/go-gost/x/limiter/traffic/traffic.go +++ b/go-gost/x/limiter/traffic/traffic.go @@ -136,6 +136,9 @@ func (l *trafficLimiter) In(ctx context.Context, key string, opts ...limiter.Opt return nil case limiter.ScopeClient: + if lim, ok := l.inLimits.Get(key); ok && lim != nil { + return lim.(traffic.Limiter) + } return nil case limiter.ScopeConn: @@ -215,6 +218,9 @@ func (l *trafficLimiter) Out(ctx context.Context, key string, opts ...limiter.Op return nil case limiter.ScopeClient: + if lim, ok := l.outLimits.Get(key); ok && lim != nil { + return lim.(traffic.Limiter) + } return nil case limiter.ScopeConn: From 634562e56d29ee168360d17920c42a072e13188e Mon Sep 17 00:00:00 2001 From: sagit Date: Mon, 9 Feb 2026 03:17:02 +0000 Subject: [PATCH 2/6] fix(config): support raw number string for limiter configuration --- go-gost/x/config/parsing/service/parse.go | 32 +++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/go-gost/x/config/parsing/service/parse.go b/go-gost/x/config/parsing/service/parse.go index 3dbcb2b..0b7957c 100644 --- a/go-gost/x/config/parsing/service/parse.go +++ b/go-gost/x/config/parsing/service/parse.go @@ -3,6 +3,7 @@ package service import ( "fmt" "runtime" + "strconv" "strings" "time" @@ -30,6 +31,7 @@ import ( logger_parser "github.com/go-gost/x/config/parsing/logger" selector_parser "github.com/go-gost/x/config/parsing/selector" tls_util "github.com/go-gost/x/internal/util/tls" + xtraffic "github.com/go-gost/x/limiter/traffic" cache_limiter "github.com/go-gost/x/limiter/traffic/cache" "github.com/go-gost/x/metadata" mdutil "github.com/go-gost/x/metadata/util" @@ -181,6 +183,27 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { ) } + var trafficLimiter listener.Option + if cfg.Limiter != "" { + lim := registry.TrafficLimiterRegistry().Get(cfg.Limiter) + if lim == nil { + // Try to parse as simple number (bandwidth in bytes/sec) + if val, err := strconv.Atoi(cfg.Limiter); err == nil && val > 0 { + lim = xtraffic.NewTrafficLimiter( + xtraffic.LimitsOption(fmt.Sprintf("%s %d %d", xtraffic.ServiceLimitKey, val, val)), + ) + } + } + trafficLimiter = listener.TrafficLimiterOption( + cache_limiter.NewCachedTrafficLimiter( + lim, + cache_limiter.RefreshIntervalOption(limiterRefreshInterval), + cache_limiter.CleanupIntervalOption(limiterCleanupInterval), + cache_limiter.ScopeOption(limiterScope), + ), + ) + } + listenOpts := []listener.Option{ listener.AddrOption(cfg.Addr), listener.RouterOption(xchain.NewRouter(routerOpts...)), @@ -188,14 +211,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { listener.AuthOption(auth_parser.Info(cfg.Listener.Auth)), listener.TLSConfigOption(tlsConfig), listener.AdmissionOption(xadmission.AdmissionGroup(admissions...)), - listener.TrafficLimiterOption( - cache_limiter.NewCachedTrafficLimiter( - registry.TrafficLimiterRegistry().Get(cfg.Limiter), - cache_limiter.RefreshIntervalOption(limiterRefreshInterval), - cache_limiter.CleanupIntervalOption(limiterCleanupInterval), - cache_limiter.ScopeOption(limiterScope), - ), - ), + trafficLimiter, listener.ConnLimiterOption(registry.ConnLimiterRegistry().Get(cfg.CLimiter)), listener.ServiceOption(cfg.Name), listener.ProxyProtocolOption(ppv), From 3a14b22ebcb60a8c730c1a09f0be005dd2b5db8f Mon Sep 17 00:00:00 2001 From: sagit Date: Mon, 9 Feb 2026 04:39:27 +0000 Subject: [PATCH 3/6] fix: prevent nil pointer dereference in listener config parsing --- go-gost/x/config/parsing/service/parse.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/go-gost/x/config/parsing/service/parse.go b/go-gost/x/config/parsing/service/parse.go index 0b7957c..1c4f684 100644 --- a/go-gost/x/config/parsing/service/parse.go +++ b/go-gost/x/config/parsing/service/parse.go @@ -211,7 +211,6 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { listener.AuthOption(auth_parser.Info(cfg.Listener.Auth)), listener.TLSConfigOption(tlsConfig), listener.AdmissionOption(xadmission.AdmissionGroup(admissions...)), - trafficLimiter, listener.ConnLimiterOption(registry.ConnLimiterRegistry().Get(cfg.CLimiter)), listener.ServiceOption(cfg.Name), listener.ProxyProtocolOption(ppv), @@ -219,6 +218,9 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { listener.NetnsOption(netnsIn), listener.LoggerOption(listenerLogger), } + if trafficLimiter != nil { + listenOpts = append(listenOpts, trafficLimiter) + } if netnsIn != "" { runtime.LockOSThread() From 0c7b7deaf5502cecadcd0b3b0e2cf04ce25512f7 Mon Sep 17 00:00:00 2001 From: sagit Date: Mon, 9 Feb 2026 05:17:46 +0000 Subject: [PATCH 4/6] fix(backend): fix tunnel batch redeploy logic for type 2 tunnels --- .../internal/http/handler/control_plane.go | 10 +- go-backend/internal/http/handler/mutations.go | 96 +++++++++++++++++++ 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/go-backend/internal/http/handler/control_plane.go b/go-backend/internal/http/handler/control_plane.go index c06fd02..e7f81df 100644 --- a/go-backend/internal/http/handler/control_plane.go +++ b/go-backend/internal/http/handler/control_plane.go @@ -59,6 +59,8 @@ type chainNodeRecord struct { NodeID int64 Port int NodeName string + Protocol string + Strategy string } type diagnosisTarget struct { @@ -862,7 +864,7 @@ func firstPortFromRange(portRange string) int { func (h *Handler) listChainNodesForTunnel(tunnelID int64) ([]chainNodeRecord, error) { rows, err := h.repo.DB().Query(` - SELECT ct.chain_type, COALESCE(ct.inx, 0), ct.node_id, COALESCE(ct.port, 0), n.name + SELECT ct.chain_type, COALESCE(ct.inx, 0), ct.node_id, COALESCE(ct.port, 0), n.name, ct.protocol, ct.strategy FROM chain_tunnel ct LEFT JOIN node n ON n.id = ct.node_id WHERE ct.tunnel_id = ? @@ -877,7 +879,9 @@ func (h *Handler) listChainNodesForTunnel(tunnelID int64) ([]chainNodeRecord, er for rows.Next() { var item chainNodeRecord var name sql.NullString - if err := rows.Scan(&item.ChainType, &item.Inx, &item.NodeID, &item.Port, &name); err != nil { + var protocol sql.NullString + var strategy sql.NullString + if err := rows.Scan(&item.ChainType, &item.Inx, &item.NodeID, &item.Port, &name, &protocol, &strategy); err != nil { return nil, err } if strings.TrimSpace(name.String) == "" { @@ -885,6 +889,8 @@ func (h *Handler) listChainNodesForTunnel(tunnelID int64) ([]chainNodeRecord, er } else { item.NodeName = name.String } + item.Protocol = defaultString(protocol.String, "tls") + item.Strategy = defaultString(strategy.String, "round") result = append(result, item) } if err := rows.Err(); err != nil { diff --git a/go-backend/internal/http/handler/mutations.go b/go-backend/internal/http/handler/mutations.go index ab4d701..31da320 100644 --- a/go-backend/internal/http/handler/mutations.go +++ b/go-backend/internal/http/handler/mutations.go @@ -731,6 +731,82 @@ func (h *Handler) tunnelBatchDelete(w http.ResponseWriter, r *http.Request) { response.WriteJSON(w, response.OK(map[string]interface{}{"successCount": success, "failCount": fail})) } +func (h *Handler) reconstructTunnelState(tunnelID int64) (*tunnelCreateState, error) { + tunnel, err := h.getTunnelRecord(tunnelID) + if err != nil { + return nil, err + } + + chainRows, err := h.listChainNodesForTunnel(tunnelID) + if err != nil { + return nil, err + } + + state := &tunnelCreateState{ + TunnelID: tunnelID, + Type: tunnel.Type, + InNodes: make([]tunnelRuntimeNode, 0), + ChainHops: make([][]tunnelRuntimeNode, 0), + OutNodes: make([]tunnelRuntimeNode, 0), + Nodes: make(map[int64]*nodeRecord), + NodeIDList: make([]int64, 0), + } + + inNodes, chainHops, outNodes := splitChainNodeGroups(chainRows) + + for _, r := range inNodes { + state.InNodes = append(state.InNodes, tunnelRuntimeNode{ + NodeID: r.NodeID, + Protocol: r.Protocol, + Strategy: r.Strategy, + ChainType: 1, + }) + state.NodeIDList = append(state.NodeIDList, r.NodeID) + } + + for _, r := range outNodes { + state.OutNodes = append(state.OutNodes, tunnelRuntimeNode{ + NodeID: r.NodeID, + Protocol: r.Protocol, + Strategy: r.Strategy, + ChainType: 3, + Port: r.Port, + }) + state.NodeIDList = append(state.NodeIDList, r.NodeID) + } + + for _, hop := range chainHops { + stateHop := make([]tunnelRuntimeNode, 0) + for _, r := range hop { + stateHop = append(stateHop, tunnelRuntimeNode{ + NodeID: r.NodeID, + Protocol: r.Protocol, + Strategy: r.Strategy, + ChainType: 2, + Inx: int(r.Inx), + Port: r.Port, + }) + state.NodeIDList = append(state.NodeIDList, r.NodeID) + } + state.ChainHops = append(state.ChainHops, stateHop) + } + + seen := make(map[int64]struct{}) + for _, id := range state.NodeIDList { + if _, ok := seen[id]; ok { + continue + } + seen[id] = struct{}{} + node, err := h.getNodeRecord(id) + if err != nil { + return nil, err + } + state.Nodes[id] = node + } + + return state, nil +} + func (h *Handler) tunnelBatchRedeploy(w http.ResponseWriter, r *http.Request) { ids := idsFromBody(r, w) if ids == nil { @@ -739,6 +815,26 @@ func (h *Handler) tunnelBatchRedeploy(w http.ResponseWriter, r *http.Request) { success := 0 fail := 0 for _, tunnelID := range ids { + tunnel, err := h.getTunnelRecord(tunnelID) + if err != nil { + fail++ + continue + } + + if tunnel.Type == 2 { + h.cleanupTunnelRuntime(tunnelID) + state, err := h.reconstructTunnelState(tunnelID) + if err != nil { + fail++ + continue + } + _, _, applyErr := h.applyTunnelRuntime(state) + if applyErr != nil { + fail++ + continue + } + } + forwards, err := h.listForwardsByTunnel(tunnelID) if err != nil { fail++ From 67d8f7a3817d6cbcda55d8f2cf0b28cc9cba9981 Mon Sep 17 00:00:00 2001 From: sagit Date: Mon, 9 Feb 2026 06:13:56 +0000 Subject: [PATCH 5/6] fix(backend): correct speed limit unit conversion from Mbps to Bytes/s --- go-backend/internal/http/handler/control_plane.go | 5 ++++- go-gost/x/config/parsing/service/parse.go | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/go-backend/internal/http/handler/control_plane.go b/go-backend/internal/http/handler/control_plane.go index e7f81df..174405a 100644 --- a/go-backend/internal/http/handler/control_plane.go +++ b/go-backend/internal/http/handler/control_plane.go @@ -1045,7 +1045,10 @@ func buildForwardServiceConfigs(baseName string, forward *forwardRecord, tunnel service["metadata"] = map[string]interface{}{"interface": node.InterfaceName} } if limiter != nil && *limiter > 0 { - service["limiter"] = strconv.Itoa(*limiter) + // Convert Mbps to Bytes/s + // 1 Mbps = 1,000,000 bits/s = 125,000 Bytes/s + // We use decimal Mbps standard as is common in networking + service["limiter"] = strconv.Itoa(*limiter * 125000) } services = append(services, service) } diff --git a/go-gost/x/config/parsing/service/parse.go b/go-gost/x/config/parsing/service/parse.go index 1c4f684..2e02e29 100644 --- a/go-gost/x/config/parsing/service/parse.go +++ b/go-gost/x/config/parsing/service/parse.go @@ -193,6 +193,11 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { xtraffic.LimitsOption(fmt.Sprintf("%s %d %d", xtraffic.ServiceLimitKey, val, val)), ) } + if lim == nil { + lim = xtraffic.NewTrafficLimiter( + xtraffic.LimitsOption(fmt.Sprintf("%s %s %s", xtraffic.ServiceLimitKey, cfg.Limiter, cfg.Limiter)), + ) + } } trafficLimiter = listener.TrafficLimiterOption( cache_limiter.NewCachedTrafficLimiter( From e94aa0121340b3271de5b106157056054966789a Mon Sep 17 00:00:00 2001 From: sagit Date: Mon, 9 Feb 2026 06:22:47 +0000 Subject: [PATCH 6/6] fix(gost): append 'B' suffix to speed limit values for correct unit parsing --- go-gost/x/config/parsing/service/parse.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-gost/x/config/parsing/service/parse.go b/go-gost/x/config/parsing/service/parse.go index 2e02e29..07d278c 100644 --- a/go-gost/x/config/parsing/service/parse.go +++ b/go-gost/x/config/parsing/service/parse.go @@ -190,7 +190,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { // Try to parse as simple number (bandwidth in bytes/sec) if val, err := strconv.Atoi(cfg.Limiter); err == nil && val > 0 { lim = xtraffic.NewTrafficLimiter( - xtraffic.LimitsOption(fmt.Sprintf("%s %d %d", xtraffic.ServiceLimitKey, val, val)), + xtraffic.LimitsOption(fmt.Sprintf("%s %dB %dB", xtraffic.ServiceLimitKey, val, val)), ) } if lim == nil {