|
|
|
@ -16,7 +16,7 @@ import (
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/peterbourgon/ff/v3/ffcli"
|
|
|
|
|
"tailscale.com/ipn"
|
|
|
|
|
"inet.af/netaddr"
|
|
|
|
|
"tailscale.com/ipn/ipnstate"
|
|
|
|
|
"tailscale.com/tailcfg"
|
|
|
|
|
)
|
|
|
|
@ -87,24 +87,10 @@ func runPing(ctx context.Context, args []string) error {
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c, bc, ctx, cancel := connect(ctx)
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
if len(args) != 1 || args[0] == "" {
|
|
|
|
|
return errors.New("usage: ping <hostname-or-IP>")
|
|
|
|
|
}
|
|
|
|
|
var ip string
|
|
|
|
|
prc := make(chan *ipnstate.PingResult, 1)
|
|
|
|
|
bc.SetNotifyCallback(func(n ipn.Notify) {
|
|
|
|
|
if n.ErrMessage != nil {
|
|
|
|
|
fatalf("Notify.ErrMessage: %v", *n.ErrMessage)
|
|
|
|
|
}
|
|
|
|
|
if pr := n.PingResult; pr != nil && pr.IP == ip {
|
|
|
|
|
prc <- pr
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
pumpErr := make(chan error, 1)
|
|
|
|
|
go func() { pumpErr <- pump(ctx, bc, c) }()
|
|
|
|
|
|
|
|
|
|
hostOrIP := args[0]
|
|
|
|
|
ip, self, err := tailscaleIPFromArg(ctx, hostOrIP)
|
|
|
|
@ -124,48 +110,47 @@ func runPing(ctx context.Context, args []string) error {
|
|
|
|
|
anyPong := false
|
|
|
|
|
for {
|
|
|
|
|
n++
|
|
|
|
|
bc.Ping(ip, pingType())
|
|
|
|
|
timer := time.NewTimer(pingArgs.timeout)
|
|
|
|
|
select {
|
|
|
|
|
case <-timer.C:
|
|
|
|
|
printf("timeout waiting for ping reply\n")
|
|
|
|
|
case err := <-pumpErr:
|
|
|
|
|
return err
|
|
|
|
|
case pr := <-prc:
|
|
|
|
|
timer.Stop()
|
|
|
|
|
if pr.Err != "" {
|
|
|
|
|
if pr.IsLocalIP {
|
|
|
|
|
outln(pr.Err)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return errors.New(pr.Err)
|
|
|
|
|
}
|
|
|
|
|
latency := time.Duration(pr.LatencySeconds * float64(time.Second)).Round(time.Millisecond)
|
|
|
|
|
via := pr.Endpoint
|
|
|
|
|
if pr.DERPRegionID != 0 {
|
|
|
|
|
via = fmt.Sprintf("DERP(%s)", pr.DERPRegionCode)
|
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, pingArgs.timeout)
|
|
|
|
|
pr, err := localClient.Ping(ctx, netaddr.MustParseIP(ip), pingType())
|
|
|
|
|
cancel()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, context.DeadlineExceeded) {
|
|
|
|
|
printf("ping %q timed out\n", ip)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if via == "" {
|
|
|
|
|
// TODO(bradfitz): populate the rest of ipnstate.PingResult for TSMP queries?
|
|
|
|
|
// For now just say which protocol it used.
|
|
|
|
|
via = string(pingType())
|
|
|
|
|
}
|
|
|
|
|
anyPong = true
|
|
|
|
|
extra := ""
|
|
|
|
|
if pr.PeerAPIPort != 0 {
|
|
|
|
|
extra = fmt.Sprintf(", %d", pr.PeerAPIPort)
|
|
|
|
|
}
|
|
|
|
|
printf("pong from %s (%s%s) via %v in %v\n", pr.NodeName, pr.NodeIP, extra, via, latency)
|
|
|
|
|
if pingArgs.tsmp || pingArgs.icmp {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if pr.Endpoint != "" && pingArgs.untilDirect {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if pr.Err != "" {
|
|
|
|
|
if pr.IsLocalIP {
|
|
|
|
|
outln(pr.Err)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
return ctx.Err()
|
|
|
|
|
return errors.New(pr.Err)
|
|
|
|
|
}
|
|
|
|
|
latency := time.Duration(pr.LatencySeconds * float64(time.Second)).Round(time.Millisecond)
|
|
|
|
|
via := pr.Endpoint
|
|
|
|
|
if pr.DERPRegionID != 0 {
|
|
|
|
|
via = fmt.Sprintf("DERP(%s)", pr.DERPRegionCode)
|
|
|
|
|
}
|
|
|
|
|
if via == "" {
|
|
|
|
|
// TODO(bradfitz): populate the rest of ipnstate.PingResult for TSMP queries?
|
|
|
|
|
// For now just say which protocol it used.
|
|
|
|
|
via = string(pingType())
|
|
|
|
|
}
|
|
|
|
|
anyPong = true
|
|
|
|
|
extra := ""
|
|
|
|
|
if pr.PeerAPIPort != 0 {
|
|
|
|
|
extra = fmt.Sprintf(", %d", pr.PeerAPIPort)
|
|
|
|
|
}
|
|
|
|
|
printf("pong from %s (%s%s) via %v in %v\n", pr.NodeName, pr.NodeIP, extra, via, latency)
|
|
|
|
|
if pingArgs.tsmp || pingArgs.icmp {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if pr.Endpoint != "" && pingArgs.untilDirect {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
|
|
|
|
|
if n == pingArgs.num {
|
|
|
|
|
if !anyPong {
|
|
|
|
|
return errors.New("no reply")
|
|
|
|
|