refactor(inbound-tag): node-prefixed + transport-suffixed canonical shape

Tag scheme moves to "[n<nodeID>-]inbound-[<listen>:]<port>-<transport>"
so two long-standing collision classes go away on the create path:
  - tcp/443 and udp/443 on the same listener (independent sockets)
  - same listen+port living on the central panel and on a remote node

Examples:
  local TCP 443    → inbound-443-tcp
  local UDP 443    → inbound-443-udp
  node 1 TCP 443   → n1-inbound-443-tcp

Refactor:
  - composeInboundTag is the single source of truth, called from
    generateInboundTag. Transport segment is now always present
    (used to appear only on collision); n<id>- prefix is added when
    Inbound.NodeID != nil.
  - addInbound / importInbound drop their inline "inbound-<port>"
    fallback; an empty Tag now flows through resolveInboundTag, which
    keeps caller-supplied tags verbatim when free and otherwise
    delegates to generateInboundTag.
  - setRemoteTrafficLocked indexes tagToCentral under both the stored
    tag and the prefix-stripped form, so a node sending its bare tag
    still resolves to a row we may have rewritten at materialization.
    The create branch now picks between snap.Tag and the n<id>-
    prefixed form before falling back to the warn-once skip.
  - Tests updated for the always-on transport suffix, and two new
    cases cover the node-prefix behaviour.

Existing inbounds keep their tags — only newly generated tags adopt
the new shape, so user routing rules pointing at "inbound-443" still
match the row they always did until the row is recreated.
This commit is contained in:
MHSanaei
2026-05-27 19:14:22 +02:00
parent d347605233
commit 7ade9d9a1f
4 changed files with 125 additions and 75 deletions

View File

@@ -2,7 +2,6 @@ package controller
import (
"encoding/json"
"fmt"
"net"
"strconv"
"strings"
@@ -145,17 +144,6 @@ func (a *InboundController) addInbound(c *gin.Context) {
if inbound.NodeID != nil && *inbound.NodeID == 0 {
inbound.NodeID = nil
}
// When the central panel deploys an inbound to a remote node, it sends
// the Tag pre-computed (so both DBs agree on the identifier). Local
// UI submits don't include a Tag — we compute one from listen+port
// using the original collision-avoiding scheme.
if inbound.Tag == "" {
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
} else {
inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
}
}
inbound, needRestart, err := a.inboundService.AddInbound(inbound)
if err != nil {
@@ -338,13 +326,6 @@ func (a *InboundController) importInbound(c *gin.Context) {
if inbound.NodeID != nil && *inbound.NodeID == 0 {
inbound.NodeID = nil
}
if inbound.Tag == "" {
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
} else {
inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
}
}
for index := range inbound.ClientStats {
inbound.ClientStats[index].Id = 0