mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
cmd/tailscale: fix "tailscale ip $self-host-hostname"
And in the process, fix the related confusing error messages from pinging your own IP or hostname. Fixes #2803 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
4917a96aec
commit
7bfd4f521d
@ -91,7 +91,7 @@ func runCp(ctx context.Context, args []string) error {
|
||||
} else if hadBrackets && (err != nil || !ip.Is6()) {
|
||||
return errors.New("unexpected brackets around target")
|
||||
}
|
||||
ip, err := tailscaleIPFromArg(ctx, target)
|
||||
ip, _, err := tailscaleIPFromArg(ctx, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ func runIP(ctx context.Context, args []string) error {
|
||||
}
|
||||
ips := st.TailscaleIPs
|
||||
if of != "" {
|
||||
ip, err := tailscaleIPFromArg(ctx, of)
|
||||
ip, _, err := tailscaleIPFromArg(ctx, of)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -101,5 +101,12 @@ func peerMatchingIP(st *ipnstate.Status, ipStr string) (ps *ipnstate.PeerStatus,
|
||||
}
|
||||
}
|
||||
}
|
||||
if ps := st.Self; ps != nil {
|
||||
for _, pip := range ps.TailscaleIPs {
|
||||
if ip == pip {
|
||||
return ps, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
@ -84,10 +84,14 @@ func runPing(ctx context.Context, args []string) error {
|
||||
go func() { pumpErr <- pump(ctx, bc, c) }()
|
||||
|
||||
hostOrIP := args[0]
|
||||
ip, err := tailscaleIPFromArg(ctx, hostOrIP)
|
||||
ip, self, err := tailscaleIPFromArg(ctx, hostOrIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if self {
|
||||
fmt.Printf("%v is local Tailscale IP\n", ip)
|
||||
return nil
|
||||
}
|
||||
|
||||
if pingArgs.verbose && ip != hostOrIP {
|
||||
log.Printf("lookup %q => %q", hostOrIP, ip)
|
||||
@ -107,6 +111,10 @@ func runPing(ctx context.Context, args []string) error {
|
||||
case pr := <-prc:
|
||||
timer.Stop()
|
||||
if pr.Err != "" {
|
||||
if pr.IsLocalIP {
|
||||
fmt.Println(pr.Err)
|
||||
return nil
|
||||
}
|
||||
return errors.New(pr.Err)
|
||||
}
|
||||
latency := time.Duration(pr.LatencySeconds * float64(time.Second)).Round(time.Millisecond)
|
||||
@ -147,33 +155,39 @@ func runPing(ctx context.Context, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
func tailscaleIPFromArg(ctx context.Context, hostOrIP string) (ip string, err error) {
|
||||
func tailscaleIPFromArg(ctx context.Context, hostOrIP string) (ip string, self bool, err error) {
|
||||
// If the argument is an IP address, use it directly without any resolution.
|
||||
if net.ParseIP(hostOrIP) != nil {
|
||||
return hostOrIP, nil
|
||||
return hostOrIP, false, nil
|
||||
}
|
||||
|
||||
// Otherwise, try to resolve it first from the network peer list.
|
||||
st, err := tailscale.Status(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", false, err
|
||||
}
|
||||
match := func(ps *ipnstate.PeerStatus) bool {
|
||||
return strings.EqualFold(hostOrIP, dnsOrQuoteHostname(st, ps)) || hostOrIP == ps.DNSName
|
||||
}
|
||||
for _, ps := range st.Peer {
|
||||
if hostOrIP == dnsOrQuoteHostname(st, ps) || hostOrIP == ps.DNSName {
|
||||
if match(ps) {
|
||||
if len(ps.TailscaleIPs) == 0 {
|
||||
return "", errors.New("node found but lacks an IP")
|
||||
return "", false, errors.New("node found but lacks an IP")
|
||||
}
|
||||
return ps.TailscaleIPs[0].String(), nil
|
||||
return ps.TailscaleIPs[0].String(), false, nil
|
||||
}
|
||||
}
|
||||
if match(st.Self) && len(st.Self.TailscaleIPs) > 0 {
|
||||
return st.Self.TailscaleIPs[0].String(), true, nil
|
||||
}
|
||||
|
||||
// Finally, use DNS.
|
||||
var res net.Resolver
|
||||
if addrs, err := res.LookupHost(ctx, hostOrIP); err != nil {
|
||||
return "", fmt.Errorf("error looking up IP of %q: %v", hostOrIP, err)
|
||||
return "", false, fmt.Errorf("error looking up IP of %q: %v", hostOrIP, err)
|
||||
} else if len(addrs) == 0 {
|
||||
return "", fmt.Errorf("no IPs found for %q", hostOrIP)
|
||||
return "", false, fmt.Errorf("no IPs found for %q", hostOrIP)
|
||||
} else {
|
||||
return addrs[0], nil
|
||||
return addrs[0], false, nil
|
||||
}
|
||||
}
|
||||
|
@ -456,6 +456,10 @@ type PingResult struct {
|
||||
// running the server on.
|
||||
PeerAPIPort uint16 `json:",omitempty"`
|
||||
|
||||
// IsLocalIP is whether the ping request error is due to it being
|
||||
// a ping to the local node.
|
||||
IsLocalIP bool `json:",omitempty"`
|
||||
|
||||
// TODO(bradfitz): details like whether port mapping was used on either side? (Once supported)
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ func (e *userspaceEngine) trackOpenPostFilterOut(pp *packet.Parsed, t *tstun.Wra
|
||||
// like:
|
||||
// open-conn-track: timeout opening (100.115.73.60:52501 => 17.125.252.5:443); no associated peer node
|
||||
if runtime.GOOS == "ios" && flow.Dst.Port() == 443 && !tsaddr.IsTailscaleIP(flow.Dst.IP()) {
|
||||
if _, err := e.peerForIP(flow.Dst.IP()); err != nil {
|
||||
if _, _, err := e.peerForIP(flow.Dst.IP()); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -157,7 +157,7 @@ func (e *userspaceEngine) onOpenTimeout(flow flowtrack.Tuple) {
|
||||
}
|
||||
|
||||
// Diagnose why it might've timed out.
|
||||
n, err := e.peerForIP(flow.Dst.IP())
|
||||
n, _, err := e.peerForIP(flow.Dst.IP())
|
||||
if err != nil {
|
||||
e.logf("open-conn-track: timeout opening %v; peerForIP: %v", flow, err)
|
||||
return
|
||||
|
@ -1244,7 +1244,7 @@ func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
||||
|
||||
func (e *userspaceEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.PingResult)) {
|
||||
res := &ipnstate.PingResult{IP: ip.String()}
|
||||
peer, err := e.peerForIP(ip)
|
||||
peer, self, err := e.peerForIP(ip)
|
||||
if err != nil {
|
||||
e.logf("ping(%v): %v", ip, err)
|
||||
res.Err = err.Error()
|
||||
@ -1257,6 +1257,13 @@ func (e *userspaceEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.Pi
|
||||
cb(res)
|
||||
return
|
||||
}
|
||||
if self {
|
||||
res.Err = fmt.Sprintf("%v is local Tailscale IP", ip)
|
||||
res.IsLocalIP = true
|
||||
cb(res)
|
||||
return
|
||||
}
|
||||
|
||||
pingType := "disco"
|
||||
if useTSMP {
|
||||
pingType = "TSMP"
|
||||
@ -1400,12 +1407,12 @@ func (e *userspaceEngine) WhoIsIPPort(ipport netaddr.IPPort) (tsIP netaddr.IP, o
|
||||
//
|
||||
// peerForIP acquires both e.mu and e.wgLock, but neither at the same
|
||||
// time.
|
||||
func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, err error) {
|
||||
func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, isSelf bool, err error) {
|
||||
e.mu.Lock()
|
||||
nm := e.netMap
|
||||
e.mu.Unlock()
|
||||
if nm == nil {
|
||||
return nil, errors.New("no network map")
|
||||
return nil, false, errors.New("no network map")
|
||||
}
|
||||
|
||||
// Check for exact matches before looking for subnet matches.
|
||||
@ -1414,7 +1421,7 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, err error)
|
||||
for _, p := range nm.Peers {
|
||||
for _, a := range p.Addresses {
|
||||
if a.IP() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
|
||||
return p, nil
|
||||
return p, false, nil
|
||||
}
|
||||
}
|
||||
for _, cidr := range p.AllowedIPs {
|
||||
@ -1427,6 +1434,11 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, err error)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, a := range nm.Addresses {
|
||||
if a.IP() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
|
||||
return nm.SelfNode, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
e.wgLock.Lock()
|
||||
defer e.wgLock.Unlock()
|
||||
@ -1450,17 +1462,17 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, err error)
|
||||
if !bestKey.IsZero() {
|
||||
for _, p := range nm.Peers {
|
||||
if p.Key == bestKey {
|
||||
return p, nil
|
||||
return p, false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if bestInNM == nil {
|
||||
return nil, nil
|
||||
return nil, false, nil
|
||||
}
|
||||
if bestInNMPrefix.Bits() == 0 {
|
||||
return nil, errors.New("exit node found but not enabled")
|
||||
return nil, false, errors.New("exit node found but not enabled")
|
||||
}
|
||||
return nil, fmt.Errorf("node %q found, but not using its %v route", bestInNM.ComputedNameWithHost, bestInNMPrefix)
|
||||
return nil, false, fmt.Errorf("node %q found, but not using its %v route", bestInNM.ComputedNameWithHost, bestInNMPrefix)
|
||||
}
|
||||
|
||||
type closeOnErrorPool []func()
|
||||
|
Loading…
Reference in New Issue
Block a user