mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-29 11:11:31 -04:00
net/dnscache: run happy eyeballs with more than one dest IP (#19770)
If the context given to DialContext has a shorter lifetime than the OS TCP SYN timeout, and TCP SYNs are dropped from the path to the remote, DialContext would never fall back to try IPv6 after IPv4. Instead, use the normal happy eyeballs race if there is more than one address. This does remove the implicit prioritization of IPv4 over IPv6 in cases where there is only a single IPv4 remote address. Updates #13346 Signed-off-by: Claus Lensbøl <claus@tailscale.com>
This commit is contained in:
@@ -422,24 +422,21 @@ func (d *dialer) DialContext(ctx context.Context, network, address string) (retC
|
||||
}
|
||||
}()
|
||||
|
||||
ip, ip6, allIPs, err := d.dnsCache.LookupIP(ctx, host)
|
||||
ip, _, allIPs, err := d.dnsCache.LookupIP(ctx, host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to resolve %q: %w", host, err)
|
||||
}
|
||||
i4s := v4addrs(allIPs)
|
||||
if len(i4s) < 2 {
|
||||
|
||||
// If we only have one candidate, just dial that, no matter what the
|
||||
// address family is.
|
||||
if len(allIPs) == 1 {
|
||||
d.dnsCache.dlogf("dialing %s, %s for %s", network, ip, address)
|
||||
c, err := dc.dialOne(ctx, ip.Unmap())
|
||||
if err == nil || ctx.Err() != nil || !ip6.IsValid() {
|
||||
return c, err
|
||||
}
|
||||
// Fall back to trying IPv6.
|
||||
return dc.dialOne(ctx, ip6)
|
||||
return dc.dialOne(ctx, ip.Unmap())
|
||||
}
|
||||
|
||||
// Multiple IPv4 candidates, and 0+ IPv6.
|
||||
ipsToTry := append(i4s, v6addrs(allIPs)...)
|
||||
return dc.raceDial(ctx, ipsToTry)
|
||||
// If we have multiple candidates, across address families, use happy
|
||||
// eyeballs to find a connection.
|
||||
return dc.raceDial(ctx, allIPs)
|
||||
}
|
||||
|
||||
func (d *dialer) shouldTryBootstrap(ctx context.Context, err error, dc *dialCall) bool {
|
||||
@@ -645,25 +642,6 @@ type res struct {
|
||||
}
|
||||
}
|
||||
|
||||
func v4addrs(aa []netip.Addr) (ret []netip.Addr) {
|
||||
for _, a := range aa {
|
||||
a = a.Unmap()
|
||||
if a.Is4() {
|
||||
ret = append(ret, a)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func v6addrs(aa []netip.Addr) (ret []netip.Addr) {
|
||||
for _, a := range aa {
|
||||
if a.Is6() && !a.Is4In6() {
|
||||
ret = append(ret, a)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// TLSDialer is like Dialer but returns a func suitable for using with net/http.Transport.DialTLSContext.
|
||||
// It returns a *tls.Conn type on success.
|
||||
// On TLS cert validation failure, it can invoke a backup DNS resolution strategy.
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package vmtest_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
@@ -13,8 +12,6 @@
|
||||
"tailscale.com/tstest/natlab/vnet"
|
||||
)
|
||||
|
||||
var knownBroken = flag.Bool("known-broken", false, "run known-broken tests")
|
||||
|
||||
func v6cidr(n int) string {
|
||||
return fmt.Sprintf("2000:%d::1/64", n)
|
||||
}
|
||||
@@ -228,12 +225,7 @@ func TestSingleJustIPv6(t *testing.T) {
|
||||
|
||||
// TestSingleDualBrokenIPv4 tests a dual-stack node with broken
|
||||
// (blackholed) IPv4.
|
||||
//
|
||||
// See https://github.com/tailscale/tailscale/issues/13346
|
||||
func TestSingleDualBrokenIPv4(t *testing.T) {
|
||||
if !*knownBroken {
|
||||
t.Skip("skipping known-broken test; set --known-broken to run; see https://github.com/tailscale/tailscale/issues/13346")
|
||||
}
|
||||
env := vmtest.New(t)
|
||||
v6AndBlackholedIPv4(env)
|
||||
env.Start()
|
||||
|
||||
Reference in New Issue
Block a user