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()) {
|
} else if hadBrackets && (err != nil || !ip.Is6()) {
|
||||||
return errors.New("unexpected brackets around target")
|
return errors.New("unexpected brackets around target")
|
||||||
}
|
}
|
||||||
ip, err := tailscaleIPFromArg(ctx, target)
|
ip, _, err := tailscaleIPFromArg(ctx, target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func runIP(ctx context.Context, args []string) error {
|
|||||||
}
|
}
|
||||||
ips := st.TailscaleIPs
|
ips := st.TailscaleIPs
|
||||||
if of != "" {
|
if of != "" {
|
||||||
ip, err := tailscaleIPFromArg(ctx, of)
|
ip, _, err := tailscaleIPFromArg(ctx, of)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
return nil, false
|
||||||
}
|
}
|
||||||
|
@ -84,10 +84,14 @@ func runPing(ctx context.Context, args []string) error {
|
|||||||
go func() { pumpErr <- pump(ctx, bc, c) }()
|
go func() { pumpErr <- pump(ctx, bc, c) }()
|
||||||
|
|
||||||
hostOrIP := args[0]
|
hostOrIP := args[0]
|
||||||
ip, err := tailscaleIPFromArg(ctx, hostOrIP)
|
ip, self, err := tailscaleIPFromArg(ctx, hostOrIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if self {
|
||||||
|
fmt.Printf("%v is local Tailscale IP\n", ip)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if pingArgs.verbose && ip != hostOrIP {
|
if pingArgs.verbose && ip != hostOrIP {
|
||||||
log.Printf("lookup %q => %q", hostOrIP, ip)
|
log.Printf("lookup %q => %q", hostOrIP, ip)
|
||||||
@ -107,6 +111,10 @@ func runPing(ctx context.Context, args []string) error {
|
|||||||
case pr := <-prc:
|
case pr := <-prc:
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
if pr.Err != "" {
|
if pr.Err != "" {
|
||||||
|
if pr.IsLocalIP {
|
||||||
|
fmt.Println(pr.Err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return errors.New(pr.Err)
|
return errors.New(pr.Err)
|
||||||
}
|
}
|
||||||
latency := time.Duration(pr.LatencySeconds * float64(time.Second)).Round(time.Millisecond)
|
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 the argument is an IP address, use it directly without any resolution.
|
||||||
if net.ParseIP(hostOrIP) != nil {
|
if net.ParseIP(hostOrIP) != nil {
|
||||||
return hostOrIP, nil
|
return hostOrIP, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, try to resolve it first from the network peer list.
|
// Otherwise, try to resolve it first from the network peer list.
|
||||||
st, err := tailscale.Status(ctx)
|
st, err := tailscale.Status(ctx)
|
||||||
if err != nil {
|
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 {
|
for _, ps := range st.Peer {
|
||||||
if hostOrIP == dnsOrQuoteHostname(st, ps) || hostOrIP == ps.DNSName {
|
if match(ps) {
|
||||||
if len(ps.TailscaleIPs) == 0 {
|
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.
|
// Finally, use DNS.
|
||||||
var res net.Resolver
|
var res net.Resolver
|
||||||
if addrs, err := res.LookupHost(ctx, hostOrIP); err != nil {
|
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 {
|
} 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 {
|
} else {
|
||||||
return addrs[0], nil
|
return addrs[0], false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,6 +456,10 @@ type PingResult struct {
|
|||||||
// running the server on.
|
// running the server on.
|
||||||
PeerAPIPort uint16 `json:",omitempty"`
|
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)
|
// 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:
|
// like:
|
||||||
// open-conn-track: timeout opening (100.115.73.60:52501 => 17.125.252.5:443); no associated peer node
|
// 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 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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ func (e *userspaceEngine) onOpenTimeout(flow flowtrack.Tuple) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Diagnose why it might've timed out.
|
// Diagnose why it might've timed out.
|
||||||
n, err := e.peerForIP(flow.Dst.IP())
|
n, _, err := e.peerForIP(flow.Dst.IP())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.logf("open-conn-track: timeout opening %v; peerForIP: %v", flow, err)
|
e.logf("open-conn-track: timeout opening %v; peerForIP: %v", flow, err)
|
||||||
return
|
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)) {
|
func (e *userspaceEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.PingResult)) {
|
||||||
res := &ipnstate.PingResult{IP: ip.String()}
|
res := &ipnstate.PingResult{IP: ip.String()}
|
||||||
peer, err := e.peerForIP(ip)
|
peer, self, err := e.peerForIP(ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.logf("ping(%v): %v", ip, err)
|
e.logf("ping(%v): %v", ip, err)
|
||||||
res.Err = err.Error()
|
res.Err = err.Error()
|
||||||
@ -1257,6 +1257,13 @@ func (e *userspaceEngine) Ping(ip netaddr.IP, useTSMP bool, cb func(*ipnstate.Pi
|
|||||||
cb(res)
|
cb(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if self {
|
||||||
|
res.Err = fmt.Sprintf("%v is local Tailscale IP", ip)
|
||||||
|
res.IsLocalIP = true
|
||||||
|
cb(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
pingType := "disco"
|
pingType := "disco"
|
||||||
if useTSMP {
|
if useTSMP {
|
||||||
pingType = "TSMP"
|
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
|
// peerForIP acquires both e.mu and e.wgLock, but neither at the same
|
||||||
// time.
|
// 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()
|
e.mu.Lock()
|
||||||
nm := e.netMap
|
nm := e.netMap
|
||||||
e.mu.Unlock()
|
e.mu.Unlock()
|
||||||
if nm == nil {
|
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.
|
// 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 _, p := range nm.Peers {
|
||||||
for _, a := range p.Addresses {
|
for _, a := range p.Addresses {
|
||||||
if a.IP() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
|
if a.IP() == ip && a.IsSingleIP() && tsaddr.IsTailscaleIP(ip) {
|
||||||
return p, nil
|
return p, false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, cidr := range p.AllowedIPs {
|
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()
|
e.wgLock.Lock()
|
||||||
defer e.wgLock.Unlock()
|
defer e.wgLock.Unlock()
|
||||||
@ -1450,17 +1462,17 @@ func (e *userspaceEngine) peerForIP(ip netaddr.IP) (n *tailcfg.Node, err error)
|
|||||||
if !bestKey.IsZero() {
|
if !bestKey.IsZero() {
|
||||||
for _, p := range nm.Peers {
|
for _, p := range nm.Peers {
|
||||||
if p.Key == bestKey {
|
if p.Key == bestKey {
|
||||||
return p, nil
|
return p, false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if bestInNM == nil {
|
if bestInNM == nil {
|
||||||
return nil, nil
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
if bestInNMPrefix.Bits() == 0 {
|
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()
|
type closeOnErrorPool []func()
|
||||||
|
Loading…
Reference in New Issue
Block a user