mirror of https://github.com/tailscale/tailscale/
ipn/ipnlocal: extract ping implementation into its own file
There’s enough code to pull this out into its own ping.go file, since local.go contains a lot of stuff. The next commit will add a Pinger interface to this file, so I am breaking out this refactor to make identifying the changes and reviewing the actual patch easier. Signed-off-by: Simon Law <sfllaw@tailscale.com>sfllaw/tailscale-ping-for-client-side-reachability
parent
92f57f74a8
commit
5555bbcd22
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package ipnlocal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/feature"
|
||||||
|
"tailscale.com/feature/buildfeatures"
|
||||||
|
"tailscale.com/ipn/ipnstate"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *LocalBackend) Ping(ctx context.Context, ip netip.Addr, pingType tailcfg.PingType, size int) (*ipnstate.PingResult, error) {
|
||||||
|
if pingType == tailcfg.PingPeerAPI {
|
||||||
|
t0 := b.clock.Now()
|
||||||
|
node, base, err := b.pingPeerAPI(ctx, ip)
|
||||||
|
if err != nil && ctx.Err() != nil {
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
d := b.clock.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.Valid() {
|
||||||
|
pr.NodeName = node.Name()
|
||||||
|
}
|
||||||
|
return pr, nil
|
||||||
|
}
|
||||||
|
ch := make(chan *ipnstate.PingResult, 1)
|
||||||
|
b.e.Ping(ip, pingType, size, func(pr *ipnstate.PingResult) {
|
||||||
|
select {
|
||||||
|
case ch <- pr:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
select {
|
||||||
|
case pr := <-ch:
|
||||||
|
return pr, nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *LocalBackend) pingPeerAPI(ctx context.Context, ip netip.Addr) (peer tailcfg.NodeView, peerBase string, err error) {
|
||||||
|
if !buildfeatures.HasPeerAPIClient {
|
||||||
|
return peer, peerBase, feature.ErrUnavailable
|
||||||
|
}
|
||||||
|
var zero tailcfg.NodeView
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
nm := b.NetMap()
|
||||||
|
if nm == nil {
|
||||||
|
return zero, "", errors.New("no netmap")
|
||||||
|
}
|
||||||
|
peer, ok := nm.PeerByTailscaleIP(ip)
|
||||||
|
if !ok {
|
||||||
|
return zero, "", fmt.Errorf("no peer found with Tailscale IP %v", ip)
|
||||||
|
}
|
||||||
|
if peer.Expired() {
|
||||||
|
return zero, "", errors.New("peer's node key has expired")
|
||||||
|
}
|
||||||
|
base := peerAPIBase(nm, peer)
|
||||||
|
if base == "" {
|
||||||
|
return zero, "", fmt.Errorf("no PeerAPI base found for peer %v (%v)", peer.ID(), ip)
|
||||||
|
}
|
||||||
|
outReq, err := http.NewRequestWithContext(ctx, "HEAD", base, nil)
|
||||||
|
if err != nil {
|
||||||
|
return zero, "", err
|
||||||
|
}
|
||||||
|
tr := b.Dialer().PeerAPITransport()
|
||||||
|
res, err := tr.RoundTrip(outReq)
|
||||||
|
if err != nil {
|
||||||
|
return zero, "", err
|
||||||
|
}
|
||||||
|
defer res.Body.Close() // but unnecessary on HEAD responses
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
return zero, "", fmt.Errorf("HTTP status %v", res.Status)
|
||||||
|
}
|
||||||
|
return peer, base, nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue