|
|
|
|
@ -1706,6 +1706,27 @@ func (b *LocalBackend) StartLoginInteractive() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (b *LocalBackend) Ping(ctx context.Context, ip netaddr.IP, pingType tailcfg.PingType) (*ipnstate.PingResult, error) { |
|
|
|
|
if pingType == tailcfg.PingPeerAPI { |
|
|
|
|
t0 := time.Now() |
|
|
|
|
node, base, err := b.pingPeerAPI(ctx, ip) |
|
|
|
|
if err != nil && ctx.Err() != nil { |
|
|
|
|
return nil, ctx.Err() |
|
|
|
|
} |
|
|
|
|
d := time.Since(t0) |
|
|
|
|
pr := &ipnstate.PingResult{ |
|
|
|
|
IP: ip.String(), |
|
|
|
|
NodeIP: ip.String(), |
|
|
|
|
LatencySeconds: d.Seconds(), |
|
|
|
|
PeerAPIURL: base, |
|
|
|
|
} |
|
|
|
|
if err != nil { |
|
|
|
|
pr.Err = err.Error() |
|
|
|
|
} |
|
|
|
|
if node != nil { |
|
|
|
|
pr.NodeName = node.Name |
|
|
|
|
} |
|
|
|
|
return pr, nil |
|
|
|
|
} |
|
|
|
|
ch := make(chan *ipnstate.PingResult, 1) |
|
|
|
|
b.e.Ping(ip, pingType, func(pr *ipnstate.PingResult) { |
|
|
|
|
select { |
|
|
|
|
@ -1721,6 +1742,37 @@ func (b *LocalBackend) Ping(ctx context.Context, ip netaddr.IP, pingType tailcfg |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (b *LocalBackend) pingPeerAPI(ctx context.Context, ip netaddr.IP) (peer *tailcfg.Node, peerBase string, err error) { |
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, 10*time.Second) |
|
|
|
|
defer cancel() |
|
|
|
|
nm := b.NetMap() |
|
|
|
|
if nm == nil { |
|
|
|
|
return nil, "", errors.New("no netmap") |
|
|
|
|
} |
|
|
|
|
peer, ok := nm.PeerByTailscaleIP(ip) |
|
|
|
|
if !ok { |
|
|
|
|
return nil, "", fmt.Errorf("no peer found with Tailscale IP %v", ip) |
|
|
|
|
} |
|
|
|
|
base := peerAPIBase(nm, peer) |
|
|
|
|
if base == "" { |
|
|
|
|
return nil, "", fmt.Errorf("no peer API base found for peer %v (%v)", peer.ID, ip) |
|
|
|
|
} |
|
|
|
|
outReq, err := http.NewRequestWithContext(ctx, "HEAD", base, nil) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, "", err |
|
|
|
|
} |
|
|
|
|
tr := b.Dialer().PeerAPITransport() |
|
|
|
|
res, err := tr.RoundTrip(outReq) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, "", err |
|
|
|
|
} |
|
|
|
|
defer res.Body.Close() // but unnecessary on HEAD responses
|
|
|
|
|
if res.StatusCode != http.StatusOK { |
|
|
|
|
return nil, "", fmt.Errorf("HTTP status %v", res.Status) |
|
|
|
|
} |
|
|
|
|
return peer, base, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// parseWgStatusLocked returns an EngineStatus based on s.
|
|
|
|
|
//
|
|
|
|
|
// b.mu must be held; mostly because the caller is about to anyway, and doing so
|
|
|
|
|
|