327 lines
6.7 KiB
Go
327 lines
6.7 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package magicsock
|
|
|
|
import (
|
|
"net/netip"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/dsnet/try"
|
|
"tailscale.com/types/key"
|
|
)
|
|
|
|
func TestProbeUDPLifetimeConfig_Equals(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
a *ProbeUDPLifetimeConfig
|
|
b *ProbeUDPLifetimeConfig
|
|
want bool
|
|
}{
|
|
{
|
|
"both sides nil",
|
|
nil,
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"equal pointers",
|
|
defaultProbeUDPLifetimeConfig,
|
|
defaultProbeUDPLifetimeConfig,
|
|
true,
|
|
},
|
|
{
|
|
"a nil",
|
|
nil,
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{time.Second},
|
|
CycleCanStartEvery: time.Hour,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"b nil",
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{time.Second},
|
|
CycleCanStartEvery: time.Hour,
|
|
},
|
|
nil,
|
|
false,
|
|
},
|
|
{
|
|
"Cliffs unequal",
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{time.Second},
|
|
CycleCanStartEvery: time.Hour,
|
|
},
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{time.Second * 2},
|
|
CycleCanStartEvery: time.Hour,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"CycleCanStartEvery unequal",
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{time.Second},
|
|
CycleCanStartEvery: time.Hour,
|
|
},
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{time.Second},
|
|
CycleCanStartEvery: time.Hour * 2,
|
|
},
|
|
false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := tt.a.Equals(tt.b); got != tt.want {
|
|
t.Errorf("Equals() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProbeUDPLifetimeConfig_Valid(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
p *ProbeUDPLifetimeConfig
|
|
want bool
|
|
}{
|
|
{
|
|
"default config valid",
|
|
defaultProbeUDPLifetimeConfig,
|
|
true,
|
|
},
|
|
{
|
|
"no cliffs",
|
|
&ProbeUDPLifetimeConfig{
|
|
CycleCanStartEvery: time.Hour,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"zero CycleCanStartEvery",
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{time.Second * 10},
|
|
CycleCanStartEvery: 0,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"cliff too small",
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{min(udpLifetimeProbeCliffSlack*2, heartbeatInterval)},
|
|
CycleCanStartEvery: time.Hour,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"duplicate Cliffs values",
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{time.Second * 2, time.Second * 2},
|
|
CycleCanStartEvery: time.Hour,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Cliffs not ascending",
|
|
&ProbeUDPLifetimeConfig{
|
|
Cliffs: []time.Duration{time.Second * 2, time.Second * 1},
|
|
CycleCanStartEvery: time.Hour,
|
|
},
|
|
false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := tt.p.Valid(); got != tt.want {
|
|
t.Errorf("Valid() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_endpoint_maybeProbeUDPLifetimeLocked(t *testing.T) {
|
|
var lower, higher key.DiscoPublic
|
|
a := key.NewDisco().Public()
|
|
b := key.NewDisco().Public()
|
|
if a.String() < b.String() {
|
|
lower = a
|
|
higher = b
|
|
} else {
|
|
lower = b
|
|
higher = a
|
|
}
|
|
addr := addrQuality{AddrPort: try.E1[netip.AddrPort](netip.ParseAddrPort("1.1.1.1:1"))}
|
|
newProbeUDPLifetime := func() *probeUDPLifetime {
|
|
return &probeUDPLifetime{
|
|
config: *defaultProbeUDPLifetimeConfig,
|
|
}
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
localDisco key.DiscoPublic
|
|
remoteDisco *key.DiscoPublic
|
|
probeUDPLifetimeFn func() *probeUDPLifetime
|
|
bestAddr addrQuality
|
|
wantAfterInactivityForFn func(*probeUDPLifetime) time.Duration
|
|
wantMaybe bool
|
|
}{
|
|
{
|
|
"nil probeUDPLifetime",
|
|
higher,
|
|
&lower,
|
|
func() *probeUDPLifetime {
|
|
return nil
|
|
},
|
|
addr,
|
|
func(lifetime *probeUDPLifetime) time.Duration {
|
|
return 0
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"local higher disco key",
|
|
higher,
|
|
&lower,
|
|
newProbeUDPLifetime,
|
|
addr,
|
|
func(lifetime *probeUDPLifetime) time.Duration {
|
|
return 0
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"remote no disco key",
|
|
higher,
|
|
nil,
|
|
newProbeUDPLifetime,
|
|
addr,
|
|
func(lifetime *probeUDPLifetime) time.Duration {
|
|
return 0
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"invalid bestAddr",
|
|
lower,
|
|
&higher,
|
|
newProbeUDPLifetime,
|
|
addrQuality{},
|
|
func(lifetime *probeUDPLifetime) time.Duration {
|
|
return 0
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"cycle started too recently",
|
|
lower,
|
|
&higher,
|
|
func() *probeUDPLifetime {
|
|
l := newProbeUDPLifetime()
|
|
l.cycleActive = false
|
|
l.cycleStartedAt = time.Now()
|
|
return l
|
|
},
|
|
addr,
|
|
func(lifetime *probeUDPLifetime) time.Duration {
|
|
return 0
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"maybe cliff 0 cycle not active",
|
|
lower,
|
|
&higher,
|
|
func() *probeUDPLifetime {
|
|
l := newProbeUDPLifetime()
|
|
l.cycleActive = false
|
|
l.cycleStartedAt = time.Now().Add(-l.config.CycleCanStartEvery).Add(-time.Second)
|
|
return l
|
|
},
|
|
addr,
|
|
func(lifetime *probeUDPLifetime) time.Duration {
|
|
return lifetime.config.Cliffs[0] - udpLifetimeProbeCliffSlack
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"maybe cliff 0",
|
|
lower,
|
|
&higher,
|
|
func() *probeUDPLifetime {
|
|
l := newProbeUDPLifetime()
|
|
l.cycleActive = true
|
|
l.currentCliff = 0
|
|
return l
|
|
},
|
|
addr,
|
|
func(lifetime *probeUDPLifetime) time.Duration {
|
|
return lifetime.config.Cliffs[0] - udpLifetimeProbeCliffSlack
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"maybe cliff 1",
|
|
lower,
|
|
&higher,
|
|
func() *probeUDPLifetime {
|
|
l := newProbeUDPLifetime()
|
|
l.cycleActive = true
|
|
l.currentCliff = 1
|
|
return l
|
|
},
|
|
addr,
|
|
func(lifetime *probeUDPLifetime) time.Duration {
|
|
return lifetime.config.Cliffs[1] - udpLifetimeProbeCliffSlack
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"maybe cliff 2",
|
|
lower,
|
|
&higher,
|
|
func() *probeUDPLifetime {
|
|
l := newProbeUDPLifetime()
|
|
l.cycleActive = true
|
|
l.currentCliff = 2
|
|
return l
|
|
},
|
|
addr,
|
|
func(lifetime *probeUDPLifetime) time.Duration {
|
|
return lifetime.config.Cliffs[2] - udpLifetimeProbeCliffSlack
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
de := &endpoint{
|
|
c: &Conn{
|
|
discoPublic: tt.localDisco,
|
|
},
|
|
bestAddr: tt.bestAddr,
|
|
}
|
|
if tt.remoteDisco != nil {
|
|
remote := &endpointDisco{
|
|
key: *tt.remoteDisco,
|
|
}
|
|
de.disco.Store(remote)
|
|
}
|
|
p := tt.probeUDPLifetimeFn()
|
|
de.probeUDPLifetime = p
|
|
gotAfterInactivityFor, gotMaybe := de.maybeProbeUDPLifetimeLocked()
|
|
wantAfterInactivityFor := tt.wantAfterInactivityForFn(p)
|
|
if gotAfterInactivityFor != wantAfterInactivityFor {
|
|
t.Errorf("maybeProbeUDPLifetimeLocked() gotAfterInactivityFor = %v, want %v", gotAfterInactivityFor, wantAfterInactivityFor)
|
|
}
|
|
if gotMaybe != tt.wantMaybe {
|
|
t.Errorf("maybeProbeUDPLifetimeLocked() gotMaybe = %v, want %v", gotMaybe, tt.wantMaybe)
|
|
}
|
|
})
|
|
}
|
|
}
|