[client] Add IPv6 support to ACL manager, USP filter, and forwarder (#5688)

This commit is contained in:
Viktor Liu
2026-04-09 16:56:08 +08:00
committed by GitHub
parent a1e7db2713
commit 1c4e5e71d7
78 changed files with 3606 additions and 1071 deletions

View File

@@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"strconv"
"time"
"github.com/spf13/cobra"
@@ -57,7 +58,11 @@ var debugSyncCmd = &cobra.Command{
SilenceUsage: true,
}
var pingTimeout string
var (
pingTimeout time.Duration
pingIPv4 bool
pingIPv6 bool
)
var debugPingCmd = &cobra.Command{
Use: "ping <account-id> <host> [port]",
@@ -108,7 +113,10 @@ func init() {
debugStatusCmd.Flags().StringVar(&statusFilterByStatus, "filter-by-status", "", "Filter by status (idle|connecting|connected)")
debugStatusCmd.Flags().StringVar(&statusFilterByConnectionType, "filter-by-connection-type", "", "Filter by connection type (P2P|Relayed)")
debugPingCmd.Flags().StringVar(&pingTimeout, "timeout", "", "Ping timeout (e.g., 10s)")
debugPingCmd.Flags().DurationVar(&pingTimeout, "timeout", 0, "Ping timeout (e.g., 10s)")
debugPingCmd.Flags().BoolVarP(&pingIPv4, "ipv4", "4", false, "Force IPv4")
debugPingCmd.Flags().BoolVarP(&pingIPv6, "ipv6", "6", false, "Force IPv6")
debugPingCmd.MarkFlagsMutuallyExclusive("ipv4", "ipv6")
debugCmd.AddCommand(debugHealthCmd)
debugCmd.AddCommand(debugClientsCmd)
@@ -157,7 +165,14 @@ func runDebugPing(cmd *cobra.Command, args []string) error {
}
port = p
}
return getDebugClient(cmd).PingTCP(cmd.Context(), args[0], args[1], port, pingTimeout)
var ipVersion string
switch {
case pingIPv4:
ipVersion = "4"
case pingIPv6:
ipVersion = "6"
}
return getDebugClient(cmd).PingTCP(cmd.Context(), args[0], args[1], port, pingTimeout, ipVersion)
}
func runDebugLogLevel(cmd *cobra.Command, args []string) error {

View File

@@ -6,10 +6,12 @@ import (
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
"time"
)
// StatusFilters contains filter options for status queries.
@@ -230,12 +232,16 @@ func (c *Client) ClientSyncResponse(ctx context.Context, accountID string) error
}
// PingTCP performs a TCP ping through a client.
func (c *Client) PingTCP(ctx context.Context, accountID, host string, port int, timeout string) error {
// ipVersion may be "4", "6", or "" for automatic.
func (c *Client) PingTCP(ctx context.Context, accountID, host string, port int, timeout time.Duration, ipVersion string) error {
params := url.Values{}
params.Set("host", host)
params.Set("port", fmt.Sprintf("%d", port))
if timeout != "" {
params.Set("timeout", timeout)
if timeout > 0 {
params.Set("timeout", timeout.String())
}
if ipVersion != "" {
params.Set("ip_version", ipVersion)
}
path := fmt.Sprintf("/debug/clients/%s/pingtcp?%s", url.PathEscape(accountID), params.Encode())
@@ -244,11 +250,17 @@ func (c *Client) PingTCP(ctx context.Context, accountID, host string, port int,
func (c *Client) printPingResult(data map[string]any) {
success, _ := data["success"].(bool)
host := net.JoinHostPort(fmt.Sprint(data["host"]), fmt.Sprint(data["port"]))
if success {
_, _ = fmt.Fprintf(c.out, "Success: %v:%v\n", data["host"], data["port"])
remote, _ := data["remote"].(string)
if remote != "" && remote != host {
_, _ = fmt.Fprintf(c.out, "Success: %s (via %s)\n", host, remote)
} else {
_, _ = fmt.Fprintf(c.out, "Success: %s\n", host)
}
_, _ = fmt.Fprintf(c.out, "Latency: %v\n", data["latency"])
} else {
_, _ = fmt.Fprintf(c.out, "Failed: %v:%v\n", data["host"], data["port"])
_, _ = fmt.Fprintf(c.out, "Failed: %s\n", host)
c.printError(data)
}
}

View File

@@ -9,6 +9,7 @@ import (
"fmt"
"html/template"
"maps"
"net"
"net/http"
"slices"
"strconv"
@@ -525,13 +526,18 @@ func (h *Handler) handlePingTCP(w http.ResponseWriter, r *http.Request, accountI
}
}
network := "tcp"
if v := r.URL.Query().Get("ip_version"); v == "4" || v == "6" {
network += v
}
ctx, cancel := context.WithTimeout(r.Context(), timeout)
defer cancel()
address := fmt.Sprintf("%s:%d", host, port)
address := net.JoinHostPort(host, strconv.Itoa(port))
start := time.Now()
conn, err := client.Dial(ctx, "tcp", address)
conn, err := client.Dial(ctx, network, address)
if err != nil {
h.writeJSON(w, map[string]interface{}{
"success": false,
@@ -541,18 +547,22 @@ func (h *Handler) handlePingTCP(w http.ResponseWriter, r *http.Request, accountI
})
return
}
remote := conn.RemoteAddr().String()
if err := conn.Close(); err != nil {
h.logger.Debugf("close tcp ping connection: %v", err)
}
latency := time.Since(start)
h.writeJSON(w, map[string]interface{}{
resp := map[string]interface{}{
"success": true,
"host": host,
"port": port,
"remote": remote,
"latency_ms": latency.Milliseconds(),
"latency": formatDuration(latency),
})
}
h.writeJSON(w, resp)
}
func (h *Handler) handleLogLevel(w http.ResponseWriter, r *http.Request, accountID types.AccountID) {