diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index d74464198..c1c4b62ac 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -22,6 +22,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/derp/derphttp from tailscale.com/net/netcheck tailscale.com/derp/derpmap from tailscale.com/cmd/tailscale/cli tailscale.com/disco from tailscale.com/derp + tailscale.com/hostinfo from tailscale.com/net/interfaces tailscale.com/ipn from tailscale.com/cmd/tailscale/cli+ tailscale.com/ipn/ipnstate from tailscale.com/cmd/tailscale/cli+ tailscale.com/metrics from tailscale.com/derp @@ -54,7 +55,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep tailscale.com/util/dnsname from tailscale.com/cmd/tailscale/cli+ W tailscale.com/util/endian from tailscale.com/net/netns tailscale.com/util/groupmember from tailscale.com/cmd/tailscale/cli - L tailscale.com/util/lineread from tailscale.com/net/interfaces + tailscale.com/util/lineread from tailscale.com/net/interfaces+ tailscale.com/version from tailscale.com/cmd/tailscale/cli+ tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+ tailscale.com/wgengine/filter from tailscale.com/types/netmap diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index ac4287b4d..7397195b8 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -81,7 +81,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/derp/derpmap from tailscale.com/cmd/tailscaled+ tailscale.com/disco from tailscale.com/derp+ tailscale.com/health from tailscale.com/control/controlclient+ - L tailscale.com/hostinfo from tailscale.com/control/controlclient + tailscale.com/hostinfo from tailscale.com/control/controlclient+ tailscale.com/internal/deephash from tailscale.com/ipn/ipnlocal+ tailscale.com/ipn from tailscale.com/ipn/ipnserver+ tailscale.com/ipn/ipnlocal from tailscale.com/ipn/ipnserver+ @@ -137,7 +137,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/util/dnsname from tailscale.com/ipn/ipnstate+ LW tailscale.com/util/endian from tailscale.com/net/netns+ tailscale.com/util/groupmember from tailscale.com/ipn/ipnserver - L tailscale.com/util/lineread from tailscale.com/control/controlclient+ + tailscale.com/util/lineread from tailscale.com/control/controlclient+ tailscale.com/util/osshare from tailscale.com/cmd/tailscaled+ tailscale.com/util/pidowner from tailscale.com/ipn/ipnserver tailscale.com/util/racebuild from tailscale.com/logpolicy diff --git a/net/interfaces/interfaces.go b/net/interfaces/interfaces.go index 8edbe89c4..ddd290c5f 100644 --- a/net/interfaces/interfaces.go +++ b/net/interfaces/interfaces.go @@ -15,6 +15,7 @@ "strings" "inet.af/netaddr" + "tailscale.com/hostinfo" "tailscale.com/net/tsaddr" "tailscale.com/net/tshttpproxy" ) @@ -81,13 +82,16 @@ func isProblematicInterface(nif *net.Interface) bool { } // LocalAddresses returns the machine's IP addresses, separated by -// whether they're loopback addresses. +// whether they're loopback addresses. If there are no regular addresses +// it will return any IPv4 linklocal or IPv6 unique local addresses because we +// know of environments where these are used with NAT to provide connectivity. func LocalAddresses() (regular, loopback []netaddr.IP, err error) { // TODO(crawshaw): don't serve interface addresses that we are routing ifaces, err := net.Interfaces() if err != nil { return nil, nil, err } + var regular4, regular6, linklocal4, ula6 []netaddr.IP for i := range ifaces { iface := &ifaces[i] if !isUp(iface) || isProblematicInterface(iface) { @@ -117,17 +121,44 @@ func LocalAddresses() (regular, loopback []netaddr.IP, err error) { if tsaddr.IsTailscaleIP(ip) { continue } - if ip.IsLinkLocalUnicast() { - continue - } if ip.IsLoopback() || ifcIsLoopback { loopback = append(loopback, ip) + } else if ip.IsLinkLocalUnicast() { + if ip.Is4() { + linklocal4 = append(linklocal4, ip) + } + + // We know of no cases where the IPv6 fe80:: addresses + // are used to provide WAN connectivity. It is also very + // common for users to have no IPv6 WAN connectivity, + // but their OS supports IPv6 so they have an fe80:: + // address. We don't want to report all of those + // IPv6 LL to Control. + } else if ip.Is6() && tsaddr.IsULA(ip) { + // Google Cloud Run uses NAT with IPv6 Unique + // Local Addresses to provide IPv6 connectivity. + ula6 = append(ula6, ip) } else { - regular = append(regular, ip) + if ip.Is4() { + regular4 = append(regular4, ip) + } else { + regular6 = append(regular6, ip) + } } } } } + if len(regular4) == 0 && len(regular6) == 0 { + // if we have no usable IP addresses then be willing to accept + // addresses we otherwise wouldn't, like: + // + 169.254.x.x (AWS Lambda uses NAT with these) + // + IPv6 ULA (Google Cloud Run uses these with address translation) + if hostinfo.GetEnvType() == hostinfo.AWSLambda { + regular4 = linklocal4 + } + regular6 = ula6 + } + regular = append(regular4, regular6...) sortIPs(regular) sortIPs(loopback) return regular, loopback, nil @@ -407,11 +438,11 @@ func GetState() (*State, error) { return } for _, pfx := range pfxs { - if pfx.IP().IsLoopback() || pfx.IP().IsLinkLocalUnicast() { + if pfx.IP().IsLoopback() { continue } s.HaveV6 = s.HaveV6 || isUsableV6(pfx.IP()) - s.HaveV4 = s.HaveV4 || pfx.IP().Is4() + s.HaveV4 = s.HaveV4 || isUsableV4(pfx.IP()) } }); err != nil { return nil, err @@ -503,6 +534,24 @@ func isPrivateIP(ip netaddr.IP) bool { return private1.Contains(ip) || private2.Contains(ip) || private3.Contains(ip) } +// isUsableV4 reports whether ip is a usable IPv4 address which could +// conceivably be used to get Internet connectivity. Globally routable and +// private IPv4 addresses are always Usable, and link local 169.254.x.x +// addresses are in some environments. +func isUsableV4(ip netaddr.IP) bool { + if !ip.Is4() || ip.IsLoopback() { + return false + } + if ip.IsLinkLocalUnicast() { + return hostinfo.GetEnvType() == hostinfo.AWSLambda + } + return true +} + +// isUsableV6 reports whether ip is a usable IPv6 address which could +// conceivably be used to get Internet connectivity. Globally routable +// IPv6 addresses are always Usable, and Unique Local Addresses +// (fc00::/7) are in some environments used with address translation. func isUsableV6(ip netaddr.IP) bool { return v6Global1.Contains(ip) || (tsaddr.IsULA(ip) && !tsaddr.TailscaleULARange().Contains(ip))