mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 11:05:45 +00:00
21509db121
I saw some panics in CI, like: 2024-05-08T04:30:25.9553518Z ## WARNING: (non-fatal) nil health.Tracker (being strict in CI): 2024-05-08T04:30:25.9554043Z goroutine 801 [running]: 2024-05-08T04:30:25.9554489Z tailscale.com/health.(*Tracker).nil(0x0) 2024-05-08T04:30:25.9555086Z tailscale.com/health/health.go:185 +0x70 2024-05-08T04:30:25.9555688Z tailscale.com/health.(*Tracker).SetUDP4Unbound(0x0, 0x0) 2024-05-08T04:30:25.9556373Z tailscale.com/health/health.go:532 +0x2f 2024-05-08T04:30:25.9557296Z tailscale.com/wgengine/magicsock.(*Conn).bindSocket(0xc0003b4808, 0xc0003b4878, {0x1fbca53, 0x4}, 0x0) 2024-05-08T04:30:25.9558301Z tailscale.com/wgengine/magicsock/magicsock.go:2481 +0x12c5 2024-05-08T04:30:25.9559026Z tailscale.com/wgengine/magicsock.(*Conn).rebind(0xc0003b4808, 0x0) 2024-05-08T04:30:25.9559874Z tailscale.com/wgengine/magicsock/magicsock.go:2510 +0x16f 2024-05-08T04:30:25.9561038Z tailscale.com/wgengine/magicsock.NewConn({0xc000063c80, 0x0, 0xc000197930, 0xc000197950, 0xc000197960, {0x0, 0x0}, 0xc000197970, 0xc000198ee0, 0x0, ...}) 2024-05-08T04:30:25.9562402Z tailscale.com/wgengine/magicsock/magicsock.go:476 +0xd5f 2024-05-08T04:30:25.9563779Z tailscale.com/wgengine.NewUserspaceEngine(0xc000063c80, {{0x22c8750, 0xc0001976b0}, 0x0, {0x22c3210, 0xc000063c80}, {0x22c31d8, 0x2d3c900}, 0x0, 0x0, ...}) 2024-05-08T04:30:25.9564982Z tailscale.com/wgengine/userspace.go:389 +0x159d 2024-05-08T04:30:25.9565529Z tailscale.com/ipn/ipnlocal.newTestBackend(0xc000358b60) 2024-05-08T04:30:25.9566086Z tailscale.com/ipn/ipnlocal/serve_test.go:675 +0x2a5 2024-05-08T04:30:25.9566612Z ta Updates #11874 Change-Id: I3432ed52d670743e532be4642f38dbd6e3763b1b Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
228 lines
5.4 KiB
Go
228 lines
5.4 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"net/netip"
|
|
"os"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/tailscale/wireguard-go/tun"
|
|
|
|
"tailscale.com/net/dns"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/tsd"
|
|
"tailscale.com/types/key"
|
|
"tailscale.com/types/logger"
|
|
"tailscale.com/types/netmap"
|
|
"tailscale.com/wgengine"
|
|
"tailscale.com/wgengine/filter"
|
|
"tailscale.com/wgengine/router"
|
|
"tailscale.com/wgengine/wgcfg"
|
|
)
|
|
|
|
func epFromTyped(eps []tailcfg.Endpoint) (ret []netip.AddrPort) {
|
|
for _, ep := range eps {
|
|
ret = append(ret, ep.Addr)
|
|
}
|
|
return
|
|
}
|
|
|
|
func setupWGTest(b *testing.B, logf logger.Logf, traf *TrafficGen, a1, a2 netip.Prefix) {
|
|
l1 := logger.WithPrefix(logf, "e1: ")
|
|
k1 := key.NewNode()
|
|
|
|
c1 := wgcfg.Config{
|
|
Name: "e1",
|
|
PrivateKey: k1,
|
|
Addresses: []netip.Prefix{a1},
|
|
}
|
|
t1 := &sourceTun{
|
|
logf: logger.WithPrefix(logf, "tun1: "),
|
|
traf: traf,
|
|
}
|
|
s1 := new(tsd.System)
|
|
e1, err := wgengine.NewUserspaceEngine(l1, wgengine.Config{
|
|
Router: router.NewFake(l1),
|
|
NetMon: nil,
|
|
ListenPort: 0,
|
|
Tun: t1,
|
|
SetSubsystem: s1.Set,
|
|
HealthTracker: s1.HealthTracker(),
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("e1 init: %v", err)
|
|
}
|
|
if b != nil {
|
|
b.Cleanup(e1.Close)
|
|
}
|
|
|
|
l2 := logger.WithPrefix(logf, "e2: ")
|
|
k2 := key.NewNode()
|
|
c2 := wgcfg.Config{
|
|
Name: "e2",
|
|
PrivateKey: k2,
|
|
Addresses: []netip.Prefix{a2},
|
|
}
|
|
t2 := &sinkTun{
|
|
logf: logger.WithPrefix(logf, "tun2: "),
|
|
traf: traf,
|
|
}
|
|
s2 := new(tsd.System)
|
|
e2, err := wgengine.NewUserspaceEngine(l2, wgengine.Config{
|
|
Router: router.NewFake(l2),
|
|
NetMon: nil,
|
|
ListenPort: 0,
|
|
Tun: t2,
|
|
SetSubsystem: s2.Set,
|
|
HealthTracker: s2.HealthTracker(),
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("e2 init: %v", err)
|
|
}
|
|
if b != nil {
|
|
b.Cleanup(e2.Close)
|
|
}
|
|
|
|
e1.SetFilter(filter.NewAllowAllForTest(l1))
|
|
e2.SetFilter(filter.NewAllowAllForTest(l2))
|
|
|
|
var wait sync.WaitGroup
|
|
wait.Add(2)
|
|
|
|
var e1waitDoneOnce sync.Once
|
|
e1.SetStatusCallback(func(st *wgengine.Status, err error) {
|
|
if errors.Is(err, wgengine.ErrEngineClosing) {
|
|
return
|
|
}
|
|
if err != nil {
|
|
log.Fatalf("e1 status err: %v", err)
|
|
}
|
|
logf("e1 status: %v", *st)
|
|
|
|
n := &tailcfg.Node{
|
|
ID: tailcfg.NodeID(0),
|
|
Name: "n1",
|
|
Addresses: []netip.Prefix{a1},
|
|
AllowedIPs: []netip.Prefix{a1},
|
|
Endpoints: epFromTyped(st.LocalAddrs),
|
|
}
|
|
e2.SetNetworkMap(&netmap.NetworkMap{
|
|
NodeKey: k2.Public(),
|
|
PrivateKey: k2,
|
|
Peers: []tailcfg.NodeView{n.View()},
|
|
})
|
|
|
|
p := wgcfg.Peer{
|
|
PublicKey: c1.PrivateKey.Public(),
|
|
AllowedIPs: []netip.Prefix{a1},
|
|
}
|
|
c2.Peers = []wgcfg.Peer{p}
|
|
e2.Reconfig(&c2, &router.Config{}, new(dns.Config))
|
|
e1waitDoneOnce.Do(wait.Done)
|
|
})
|
|
|
|
var e2waitDoneOnce sync.Once
|
|
e2.SetStatusCallback(func(st *wgengine.Status, err error) {
|
|
if errors.Is(err, wgengine.ErrEngineClosing) {
|
|
return
|
|
}
|
|
if err != nil {
|
|
log.Fatalf("e2 status err: %v", err)
|
|
}
|
|
logf("e2 status: %v", *st)
|
|
|
|
n := &tailcfg.Node{
|
|
ID: tailcfg.NodeID(0),
|
|
Name: "n2",
|
|
Addresses: []netip.Prefix{a2},
|
|
AllowedIPs: []netip.Prefix{a2},
|
|
Endpoints: epFromTyped(st.LocalAddrs),
|
|
}
|
|
e1.SetNetworkMap(&netmap.NetworkMap{
|
|
NodeKey: k1.Public(),
|
|
PrivateKey: k1,
|
|
Peers: []tailcfg.NodeView{n.View()},
|
|
})
|
|
|
|
p := wgcfg.Peer{
|
|
PublicKey: c2.PrivateKey.Public(),
|
|
AllowedIPs: []netip.Prefix{a2},
|
|
}
|
|
c1.Peers = []wgcfg.Peer{p}
|
|
e1.Reconfig(&c1, &router.Config{}, new(dns.Config))
|
|
e2waitDoneOnce.Do(wait.Done)
|
|
})
|
|
|
|
// Not using DERP in this test (for now?).
|
|
s1.MagicSock.Get().SetDERPMap(&tailcfg.DERPMap{})
|
|
s2.MagicSock.Get().SetDERPMap(&tailcfg.DERPMap{})
|
|
|
|
wait.Wait()
|
|
}
|
|
|
|
type sourceTun struct {
|
|
logf logger.Logf
|
|
traf *TrafficGen
|
|
}
|
|
|
|
func (t *sourceTun) Close() error { return nil }
|
|
func (t *sourceTun) Events() <-chan tun.Event { return nil }
|
|
func (t *sourceTun) File() *os.File { return nil }
|
|
func (t *sourceTun) Flush() error { return nil }
|
|
func (t *sourceTun) MTU() (int, error) { return 1500, nil }
|
|
func (t *sourceTun) Name() (string, error) { return "source", nil }
|
|
|
|
// TODO(raggi): could be optimized for linux style batch sizes
|
|
func (t *sourceTun) BatchSize() int { return 1 }
|
|
|
|
func (t *sourceTun) Write(b [][]byte, ofs int) (int, error) {
|
|
// Discard all writes
|
|
return len(b), nil
|
|
}
|
|
|
|
func (t *sourceTun) Read(b [][]byte, sizes []int, ofs int) (int, error) {
|
|
for i, b := range b {
|
|
// Continually generate "input" packets
|
|
n := t.traf.Generate(b, ofs)
|
|
sizes[i] = n
|
|
if n == 0 {
|
|
return 0, io.EOF
|
|
}
|
|
}
|
|
return len(b), nil
|
|
}
|
|
|
|
type sinkTun struct {
|
|
logf logger.Logf
|
|
traf *TrafficGen
|
|
}
|
|
|
|
func (t *sinkTun) Close() error { return nil }
|
|
func (t *sinkTun) Events() <-chan tun.Event { return nil }
|
|
func (t *sinkTun) File() *os.File { return nil }
|
|
func (t *sinkTun) Flush() error { return nil }
|
|
func (t *sinkTun) MTU() (int, error) { return 1500, nil }
|
|
func (t *sinkTun) Name() (string, error) { return "sink", nil }
|
|
|
|
func (t *sinkTun) Read(b [][]byte, sizes []int, ofs int) (int, error) {
|
|
// Never returns
|
|
select {}
|
|
}
|
|
|
|
func (t *sinkTun) Write(b [][]byte, ofs int) (int, error) {
|
|
// Count packets, but discard them
|
|
for _, b := range b {
|
|
t.traf.GotPacket(b, ofs)
|
|
}
|
|
return len(b), nil
|
|
}
|
|
|
|
// TODO(raggi): could be optimized for linux style batch sizes
|
|
func (t *sinkTun) BatchSize() int { return 1 }
|