mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
cmd/tailscale, ipn/ipnlocal: add "peerapi" ping type
For debugging when stuff like #4750 isn't working. RELNOTE=tailscale ping -peerapi Change-Id: I9c52c90fb046e3ab7d2b121387073319fbf27b99 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
575aacb1e2
commit
4d85cf586b
@ -51,6 +51,7 @@
|
||||
fs.BoolVar(&pingArgs.untilDirect, "until-direct", true, "stop once a direct path is established")
|
||||
fs.BoolVar(&pingArgs.tsmp, "tsmp", false, "do a TSMP-level ping (through WireGuard, but not either host OS stack)")
|
||||
fs.BoolVar(&pingArgs.icmp, "icmp", false, "do a ICMP-level ping (through WireGuard, but not the local host OS stack)")
|
||||
fs.BoolVar(&pingArgs.peerAPI, "peerapi", false, "try hitting the peer's peerapi HTTP server")
|
||||
fs.IntVar(&pingArgs.num, "c", 10, "max number of pings to send")
|
||||
fs.DurationVar(&pingArgs.timeout, "timeout", 5*time.Second, "timeout before giving up on a ping")
|
||||
return fs
|
||||
@ -63,6 +64,7 @@
|
||||
verbose bool
|
||||
tsmp bool
|
||||
icmp bool
|
||||
peerAPI bool
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
@ -73,6 +75,9 @@ func pingType() tailcfg.PingType {
|
||||
if pingArgs.icmp {
|
||||
return tailcfg.PingICMP
|
||||
}
|
||||
if pingArgs.peerAPI {
|
||||
return tailcfg.PingPeerAPI
|
||||
}
|
||||
return tailcfg.PingDisco
|
||||
}
|
||||
|
||||
@ -137,6 +142,10 @@ func runPing(ctx context.Context, args []string) error {
|
||||
// For now just say which protocol it used.
|
||||
via = string(pingType())
|
||||
}
|
||||
if pingArgs.peerAPI {
|
||||
printf("hit peerapi of %s (%s) at %s in %s\n", pr.NodeIP, pr.NodeName, pr.PeerAPIURL, latency)
|
||||
return nil
|
||||
}
|
||||
anyPong = true
|
||||
extra := ""
|
||||
if pr.PeerAPIPort != 0 {
|
||||
|
@ -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
|
||||
|
@ -508,6 +508,10 @@ type PingResult struct {
|
||||
// running the server on.
|
||||
PeerAPIPort uint16 `json:",omitempty"`
|
||||
|
||||
// PeerAPIURL is the URL that was hit for pings of type "peerapi" (tailcfg.PingPeerAPI).
|
||||
// It's of the form "http://ip:port" (or [ip]:port for IPv6).
|
||||
PeerAPIURL string `json:",omitempty"`
|
||||
|
||||
// IsLocalIP is whether the ping request error is due to it being
|
||||
// a ping to the local node.
|
||||
IsLocalIP bool `json:",omitempty"`
|
||||
|
@ -1097,6 +1097,9 @@ type DNSRecord struct {
|
||||
// PingICMP performs a ping between two tailscale nodes using ICMP that is
|
||||
// received by the target systems IP stack.
|
||||
PingICMP PingType = "ICMP"
|
||||
// PingPeerAPI performs a ping between two tailscale nodes using ICMP that is
|
||||
// received by the target systems IP stack.
|
||||
PingPeerAPI PingType = "peerapi"
|
||||
)
|
||||
|
||||
// PingRequest with no IP and Types is a request to send an HTTP request to prove the
|
||||
|
@ -72,6 +72,24 @@ type NetworkMap struct {
|
||||
UserProfiles map[tailcfg.UserID]tailcfg.UserProfile
|
||||
}
|
||||
|
||||
// PeerByTailscaleIP returns a peer's Node based on its Tailscale IP.
|
||||
//
|
||||
// If nm is nil or no peer is found, ok is false.
|
||||
func (nm *NetworkMap) PeerByTailscaleIP(ip netaddr.IP) (peer *tailcfg.Node, ok bool) {
|
||||
// TODO(bradfitz):
|
||||
if nm == nil {
|
||||
return nil, false
|
||||
}
|
||||
for _, n := range nm.Peers {
|
||||
for _, a := range n.Addresses {
|
||||
if a.IP() == ip {
|
||||
return n, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// MagicDNSSuffix returns the domain's MagicDNS suffix (even if
|
||||
// MagicDNS isn't necessarily in use).
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user