From 17e853bc1cdf02f2d2a45999a7f1580ae824cef2 Mon Sep 17 00:00:00 2001 From: ysicing Date: Fri, 26 Apr 2024 13:44:24 +0800 Subject: [PATCH] feat: add TcpingCommand and flags for IPv4, IPv6, count, and timeout - Remove the import of `github.com/ergoapi/util/zos` - Add the `debug/tcping.go` file - Add the `TcpingCommand` function to `debug.go` - Add flags for ipv4, ipv6, count, and timeout to the `TcpingCommand` function - Add a check for the `zos.IsLinux()` condition before adding the `debug.TcpingCommand` command in `newCmdDebug` function Signed-off-by: ysicing --- cmd/debug.go | 7 +- cmd/debug/tcping.go | 158 +++++++++++++++++++++++++++++++++++++ go.mod | 6 +- go.sum | 12 +-- hack/licenses/licenses.tpl | 2 +- 5 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 cmd/debug/tcping.go diff --git a/cmd/debug.go b/cmd/debug.go index 5f03087..d8227af 100644 --- a/cmd/debug.go +++ b/cmd/debug.go @@ -10,7 +10,6 @@ import ( "fmt" "os" - "github.com/ergoapi/util/zos" "github.com/spf13/cobra" "github.com/ysicing/tiga/cmd/debug" "github.com/ysicing/tiga/cmd/xray" @@ -29,9 +28,9 @@ func newCmdDebug(f factory.Factory) *cobra.Command { debugCmd.AddCommand(debug.GOpsCommand(f)) debugCmd.AddCommand(debug.NetCheckCommand(f)) debugCmd.AddCommand(debug.IPMMDBCommand(f)) - if zos.IsLinux() { - debugCmd.AddCommand(debug.ChinaRouteCommand(f)) - } + debugCmd.AddCommand(debug.TcpingCommand(f)) + debugCmd.AddCommand(debug.ChinaRouteCommand(f)) + // Deprecated commands xray := xray.NewCmdXray(f) xray.Deprecated = "please use xray" diff --git a/cmd/debug/tcping.go b/cmd/debug/tcping.go new file mode 100644 index 0000000..2c4081f --- /dev/null +++ b/cmd/debug/tcping.go @@ -0,0 +1,158 @@ +// Copyright (c) 2023 ysicing(ysicing.me, ysicing@12306.work) All rights reserved. +// Use of this source code is covered by the following dual licenses: +// (1) Y PUBLIC LICENSE 1.0 (YPL 1.0) +// (2) Affero General Public License 3.0 (AGPL 3.0) +// License that can be found in the LICENSE file. + +package debug + +import ( + "fmt" + "net" + "os" + "os/signal" + "strings" + "syscall" + "time" + + "github.com/cockroachdb/errors" + "github.com/spf13/cobra" + "github.com/ysicing/tiga/pkg/factory" + "k8s.io/kubectl/pkg/util/templates" +) + +var ( + tcpingExample = templates.Examples(` + # simple tcping + tiga debug tcping [-4] [-6] [-n count] [-t timeout] address port + `) +) + +func TcpingCommand(f factory.Factory) *cobra.Command { + var ipv4, ipv6 bool + var count, timeout int + var stopPing chan bool + cmd := &cobra.Command{ + Use: "tcping", + Short: "tcping", + Example: tcpingExample, + PreRunE: func(cmd *cobra.Command, args []string) error { + if ipv4 && ipv6 { + return errors.New("ipv4 and ipv6 can't be used together") + } + if len(args) < 2 { + return errors.New("address and port is required") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + var err error + address := args[0] + port := args[1] + if ipv4 || (!ipv6 && isIPv4(address)) { + address, err = resolveAddress(address, "ipv4") + } else if ipv6 || isIPv6(address) { + address, err = resolveAddress(address, "ipv6") + } else { + // Default to IPv4 if no -4 or -6 flags specified and address is not explicitly IPv6 + address, err = resolveAddress(address, "ipv4") + } + if err != nil { + return err + } + f.GetLog().Infof("start ping %s:%s...", address, port) + stopPing = make(chan bool, 1) + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) + var sentCount int + var respondedCount int + var minTime, maxTime, totalResponseTime int64 + go func() { + for i := 0; count == 0 || i < count; i++ { + select { + case <-stopPing: + return + default: + start := time.Now() + conn, err := net.DialTimeout("tcp", address+":"+port, time.Duration(timeout)*time.Second) + elapsed := time.Since(start).Milliseconds() + + sentCount++ + if err != nil { + f.GetLog().Warnf("Failed to connect to %s:%s: %v", address, port, err) + } else { + conn.Close() + respondedCount++ + if respondedCount == 1 || elapsed < minTime { + minTime = elapsed + } + if elapsed > maxTime { + maxTime = elapsed + } + totalResponseTime += elapsed + fmt.Printf("tcping %s:%s in %dms\n", address, port, elapsed) + } + + if count != 0 && i == count-1 { + break + } + + time.Sleep(time.Duration(timeout) * time.Second) + } + } + stopPing <- true + }() + + select { + case <-interrupt: + f.GetLog().Warn("ping interrupted.") + stopPing <- true + case <-stopPing: + f.GetLog().Done("ping stopped.") + } + printTcpingStatistics(sentCount, respondedCount, minTime, maxTime, totalResponseTime) + return nil + }, + } + cmd.Flags().BoolVarP(&ipv4, "ipv4", "4", false, "ipv4") + cmd.Flags().BoolVarP(&ipv6, "ipv6", "6", false, "ipv6") + cmd.Flags().IntVarP(&count, "count", "c", 3, "number of pings") + cmd.Flags().IntVarP(&timeout, "timeout", "t", 3, "time interval between pings in seconds ") + return cmd +} + +func isIPv4(address string) bool { + return strings.Count(address, ":") == 0 +} + +// resolveAddress resolves the address to the specified network type +func isIPv6(address string) bool { + return strings.Count(address, ":") >= 2 +} + +func resolveAddress(address, version string) (string, error) { + ipList, err := net.LookupIP(address) + if err != nil { + return "", errors.Errorf("failed to resolve %s: %v", address, err) + } + + for _, ip := range ipList { + if version == "ipv4" && ip.To4() != nil { + return ip.String(), nil + } else if version == "ipv6" && ip.To16() != nil && ip.To4() == nil { + return "[" + ip.String() + "]", nil + } + } + return "", errors.Errorf("no %s addresses found for %s", version, address) +} + +func printTcpingStatistics(sentCount, respondedCount int, minTime, maxTime, totalResponseTime int64) { + fmt.Println("") + fmt.Println("--- Tcping Statistics ---") + fmt.Printf("%d tcp ping sent, %d tcp ping responsed, %.2f%% loss\n", sentCount, respondedCount, float64(sentCount-respondedCount)/float64(sentCount)*100) + if respondedCount > 0 { + fmt.Printf("min/avg/max = %dms/%dms/%dms\n", minTime, totalResponseTime/int64(respondedCount), maxTime) + } else { + fmt.Println("No responses received.") + } +} diff --git a/go.mod b/go.mod index b76c444..5996eb5 100644 --- a/go.mod +++ b/go.mod @@ -112,7 +112,7 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect + github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect @@ -148,8 +148,8 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.9.0 // indirect github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect - github.com/tklauser/go-sysconf v0.3.13 // indirect - github.com/tklauser/numcpus v0.7.0 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.8.0 // indirect github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 // indirect diff --git a/go.sum b/go.sum index 96cc618..a315997 100644 --- a/go.sum +++ b/go.sum @@ -200,8 +200,8 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9 github.com/loft-sh/utils v0.0.21 h1:GSmg/PL90rfR3RzVlNIu1kK5xWWECiSsxxPHwzG5rgo= github.com/loft-sh/utils v0.0.21/go.mod h1:0lw0bzifz03np3AJvwVLCTAfWK8AE2dNwfEAq/YxoyY= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a h1:3Bm7EwfUQUvhNeKIkUct/gl9eod1TcXuj8stxvi/GoI= -github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74 h1:1KuuSOy4ZNgW0KA2oYIngXVFhQcXxhLqCVK7cBcldkk= +github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= @@ -340,11 +340,11 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4= github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= -github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= -github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= diff --git a/hack/licenses/licenses.tpl b/hack/licenses/licenses.tpl index 739a5e6..dd2709a 100644 --- a/hack/licenses/licenses.tpl +++ b/hack/licenses/licenses.tpl @@ -1,4 +1,4 @@ -Copyright (c) 2023 ysicing(ysicing.me, ysicing@12306.work) All rights reserved. +Copyright (c) 2024 ysicing(ysicing.me, ysicing@12306.work) All rights reserved. Use of this source code is covered by the following dual licenses: (1) Y PUBLIC LICENSE 1.0 (YPL 1.0) (2) Affero General Public License 3.0 (AGPL 3.0)