net/dnscache: try IPv6 addresses first (#5349)
Signed-off-by: Andrew Dunham <andrew@tailscale.com> Signed-off-by: Andrew Dunham <andrew@tailscale.com>
This commit is contained in:
parent
90555c5cb2
commit
d942a2ff56
|
@ -523,6 +523,21 @@ func (dc *dialCall) raceDial(ctx context.Context, ips []netip.Addr) (net.Conn, e
|
||||||
return nil, errors.New("no IPs")
|
return nil, errors.New("no IPs")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Partition candidate list and then merge such that an IPv6 address is
|
||||||
|
// in the first spot if present, and then addresses are interleaved.
|
||||||
|
// This ensures that we're trying an IPv6 address first, then
|
||||||
|
// alternating between v4 and v6 in case one of the two networks is
|
||||||
|
// broken.
|
||||||
|
var iv4, iv6 []netip.Addr
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip.Is6() {
|
||||||
|
iv6 = append(iv6, ip)
|
||||||
|
} else {
|
||||||
|
iv4 = append(iv4, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ips = interleaveSlices(iv6, iv4)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for i, ip := range ips {
|
for i, ip := range ips {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
|
@ -580,6 +595,21 @@ func (dc *dialCall) raceDial(ctx context.Context, ips []netip.Addr) (net.Conn, e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// interleaveSlices combines two slices of the form [a, b, c] and [x, y, z]
|
||||||
|
// into a slice with elements interleaved; i.e. [a, x, b, y, c, z].
|
||||||
|
func interleaveSlices[T any](a, b []T) []T {
|
||||||
|
var (
|
||||||
|
i int
|
||||||
|
ret = make([]T, 0, len(a)+len(b))
|
||||||
|
)
|
||||||
|
for i = 0; i < len(a) && i < len(b); i++ {
|
||||||
|
ret = append(ret, a[i], b[i])
|
||||||
|
}
|
||||||
|
ret = append(ret, a[i:]...)
|
||||||
|
ret = append(ret, b[i:]...)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func v4addrs(aa []net.IPAddr) (ret []netip.Addr) {
|
func v4addrs(aa []net.IPAddr) (ret []netip.Addr) {
|
||||||
for _, a := range aa {
|
for _, a := range aa {
|
||||||
ip, ok := netip.AddrFromSlice(a.IP)
|
ip, ok := netip.AddrFromSlice(a.IP)
|
||||||
|
|
|
@ -140,3 +140,27 @@ func TestResolverAllHostStaticResult(t *testing.T) {
|
||||||
t.Errorf("bad dial error got %q; want %q", got, want)
|
t.Errorf("bad dial error got %q; want %q", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInterleaveSlices(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
a, b []int
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{name: "equal", a: []int{1, 3, 5}, b: []int{2, 4, 6}, want: []int{1, 2, 3, 4, 5, 6}},
|
||||||
|
{name: "short_b", a: []int{1, 3, 5}, b: []int{2, 4}, want: []int{1, 2, 3, 4, 5}},
|
||||||
|
{name: "short_a", a: []int{1, 3}, b: []int{2, 4, 6}, want: []int{1, 2, 3, 4, 6}},
|
||||||
|
{name: "len_1", a: []int{1}, b: []int{2, 4, 6}, want: []int{1, 2, 4, 6}},
|
||||||
|
{name: "nil_a", a: nil, b: []int{2, 4, 6}, want: []int{2, 4, 6}},
|
||||||
|
{name: "nil_all", a: nil, b: nil, want: []int{}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
merged := interleaveSlices(tc.a, tc.b)
|
||||||
|
if !reflect.DeepEqual(merged, tc.want) {
|
||||||
|
t.Errorf("got %v; want %v", merged, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue