mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-15 23:56:57 +02:00
72bf111209
smoke-extra / freebsd-amd64 (push) Failing after 15s
smoke-extra / linux-amd64-ipv6disable (push) Failing after 15s
smoke-extra / netbsd-amd64 (push) Failing after 14s
smoke-extra / openbsd-amd64 (push) Failing after 15s
smoke-extra / linux-386 (push) Failing after 16s
smoke / Run multi node smoke test (push) Failing after 1m37s
Build and test / Static checks (push) Successful in 18s
Build and test / Test linux (push) Failing after 58s
Build and test / Test linux-boringcrypto (push) Failing after 2m45s
Build and test / Test linux-pkcs11 (push) Failing after 2m10s
Build and test / Cross-build linux-arm (push) Successful in 3m11s
Build and test / Cross-build linux-mips (push) Successful in 3m53s
Build and test / Cross-build linux-other (push) Successful in 3m16s
Build and test / Cross-build windows (push) Successful in 1m2s
Build and test / Cross-build freebsd (push) Successful in 1m36s
Build and test / Cross-build netbsd (push) Successful in 1m36s
Build and test / Cross-build openbsd (push) Successful in 1m37s
Build and test / Cross-build mobile (push) Successful in 3m23s
smoke-extra / Run windows smoke test (push) Has been cancelled
Build and test / Test macos (push) Has been cancelled
Build and test / Test windows (push) Has been cancelled
Build and test / CI status (push) Has been cancelled
137 lines
4.2 KiB
Go
137 lines
4.2 KiB
Go
//go:build e2e_testing
|
|
// +build e2e_testing
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/slackhq/nebula"
|
|
"github.com/slackhq/nebula/cert"
|
|
"github.com/slackhq/nebula/cert_test"
|
|
"github.com/slackhq/nebula/e2e/router"
|
|
"github.com/slackhq/nebula/udp"
|
|
)
|
|
|
|
// TestRecoveryTiming measures how long a tunnel takes to come back after the peer stops accepting our traffic,
|
|
// which is what a laptop waking on a new network looks like from the peer's side: its NAT has no state for where
|
|
// we are now, so everything we send disappears.
|
|
//
|
|
// It is a measurement, not a pass/fail assertion. Recovery is timed to the moment the peer punches back at us,
|
|
// since that is when its NAT opens and the tunnel is usable again.
|
|
//
|
|
// go test -tags e2e_testing -v -run TestRecoveryTiming ./e2e/
|
|
func TestRecoveryTiming(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
rebind bool
|
|
}{
|
|
{"no trigger", false},
|
|
{"rebind counter", true},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
d, lost := measureRecovery(t, tc.rebind)
|
|
t.Logf("RESULT %-16s recovered in %-9v (%d packets lost)", tc.name, d.Round(time.Millisecond), lost)
|
|
})
|
|
}
|
|
}
|
|
|
|
// measureRecovery returns how long until the peer punched back, and how many of our packets died meanwhile. When
|
|
// rebind is true we call RebindUDPServer once the tunnel goes dark, which is what the darwin network change
|
|
// monitor does and what iOS has always done. When false, nothing tells nebula anything is wrong.
|
|
func measureRecovery(t *testing.T, rebind bool) (time.Duration, int) {
|
|
t.Helper()
|
|
ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
|
|
|
|
lhControl, lhVpnIpNet, lhUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "lh", "10.128.0.1/24", m{
|
|
"lighthouse": m{"am_lighthouse": true},
|
|
})
|
|
|
|
peerCfg := m{
|
|
"lighthouse": m{
|
|
"hosts": []any{lhVpnIpNet[0].Addr().String()},
|
|
"interval": 600,
|
|
"local_allow_list": m{
|
|
"10.0.0.0/24": true,
|
|
"::/0": false,
|
|
},
|
|
},
|
|
"static_host_map": m{
|
|
lhVpnIpNet[0].Addr().String(): []any{lhUdpAddr.String()},
|
|
},
|
|
}
|
|
|
|
myControl, myVpnIpNet, myUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "me", "10.128.0.2/24", peerCfg)
|
|
theirControl, theirVpnIpNet, theirUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "them", "10.128.0.3/24", peerCfg)
|
|
|
|
r := router.NewR(t, lhControl, myControl, theirControl)
|
|
defer r.RenderFlow()
|
|
defer func() {
|
|
lhControl.Stop()
|
|
myControl.Stop()
|
|
theirControl.Stop()
|
|
}()
|
|
|
|
lhControl.Start()
|
|
myControl.Start()
|
|
theirControl.Start()
|
|
r.RouteFor(time.Millisecond * 500)
|
|
|
|
myControl.InjectLightHouseAddr(theirVpnIpNet[0].Addr(), theirUdpAddr)
|
|
theirControl.InjectLightHouseAddr(myVpnIpNet[0].Addr(), myUdpAddr)
|
|
|
|
myControl.InjectTunPacket(BuildTunUDPPacket(theirVpnIpNet[0].Addr(), 80, myVpnIpNet[0].Addr(), 80, []byte("establish")))
|
|
r.RouteFor(time.Second)
|
|
if myControl.GetHostInfoByVpnAddr(theirVpnIpNet[0].Addr(), false) == nil {
|
|
t.Fatal("failed to establish the tunnel we are measuring")
|
|
}
|
|
r.RouteFor(time.Millisecond * 500)
|
|
|
|
// From here the peer's NAT has no state for us, everything we send it disappears
|
|
start := time.Now()
|
|
blackholed := 0
|
|
var recovered time.Duration
|
|
|
|
if rebind {
|
|
myControl.RebindUDPServer()
|
|
}
|
|
|
|
// Keep the tun busy the way someone retrying a stalled connection would
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
go func() {
|
|
tick := time.NewTicker(time.Millisecond * 200)
|
|
defer tick.Stop()
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
case <-tick.C:
|
|
myControl.InjectTunPacket(BuildTunUDPPacket(
|
|
theirVpnIpNet[0].Addr(), 80, myVpnIpNet[0].Addr(), 80, []byte("retry")))
|
|
}
|
|
}
|
|
}()
|
|
|
|
r.RouteForAllExitFuncOrTimeout(time.Second*30, func(p *udp.Packet, c *nebula.Control) router.ExitType {
|
|
if c == theirControl && p.From == myControl.GetUDPAddr() {
|
|
blackholed++
|
|
return router.Drop
|
|
}
|
|
|
|
// The peer reaching us directly is the moment its NAT opened, whether that is a punch or a handshake
|
|
if c == myControl && p.From == theirUdpAddr {
|
|
recovered = time.Since(start)
|
|
return router.RouteAndExit
|
|
}
|
|
|
|
return router.KeepRouting
|
|
})
|
|
|
|
if recovered == 0 {
|
|
t.Fatalf("no recovery within 30s (%d packets blackholed)", blackholed)
|
|
}
|
|
return recovered, blackholed
|
|
}
|