all: make client use server-provided DERP map, add DERP region support

Instead of hard-coding the DERP map (except for cmd/tailscale netcheck
for now), get it from the control server at runtime.

And make the DERP map support multiple nodes per region with clients
picking the first one that's available. (The server will balance the
order presented to clients for load balancing)

This deletes the stunner package, merging it into the netcheck package
instead, to minimize all the config hooks that would've been
required.

Also fix some test flakes & races.

Fixes #387 (Don't hard-code the DERP map)
Updates #388 (Add DERP region support)
Fixes #399 (wgengine: flaky tests)

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2020-05-17 09:51:38 -07:00
committed by Brad Fitzpatrick
parent e8b3a5e7a1
commit e6b84f2159
20 changed files with 1439 additions and 1201 deletions
+54 -6
View File
@@ -6,12 +6,16 @@
package stuntest
import (
"fmt"
"net"
"strconv"
"strings"
"sync"
"testing"
"inet.af/netaddr"
"tailscale.com/stun"
"tailscale.com/tailcfg"
)
type stunStats struct {
@@ -20,7 +24,7 @@ type stunStats struct {
readIPv6 int
}
func Serve(t *testing.T) (addr string, cleanupFn func()) {
func Serve(t *testing.T) (addr *net.UDPAddr, cleanupFn func()) {
t.Helper()
// TODO(crawshaw): use stats to test re-STUN logic
@@ -30,13 +34,13 @@ func Serve(t *testing.T) (addr string, cleanupFn func()) {
if err != nil {
t.Fatalf("failed to open STUN listener: %v", err)
}
stunAddr := pc.LocalAddr().String()
stunAddr = strings.Replace(stunAddr, "0.0.0.0:", "127.0.0.1:", 1)
addr = &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: pc.LocalAddr().(*net.UDPAddr).Port,
}
doneCh := make(chan struct{})
go runSTUN(t, pc, &stats, doneCh)
return stunAddr, func() {
return addr, func() {
pc.Close()
<-doneCh
}
@@ -79,3 +83,47 @@ func runSTUN(t *testing.T, pc net.PacketConn, stats *stunStats, done chan<- stru
}
}
}
func DERPMapOf(stun ...string) *tailcfg.DERPMap {
m := &tailcfg.DERPMap{
Regions: map[int]*tailcfg.DERPRegion{},
}
for i, hostPortStr := range stun {
regionID := i + 1
host, portStr, err := net.SplitHostPort(hostPortStr)
if err != nil {
panic(fmt.Sprintf("bogus STUN hostport: %q", hostPortStr))
}
port, err := strconv.Atoi(portStr)
if err != nil {
panic(fmt.Sprintf("bogus port %q in %q", portStr, hostPortStr))
}
var ipv4, ipv6 string
ip, err := netaddr.ParseIP(host)
if err != nil {
panic(fmt.Sprintf("bogus non-IP STUN host %q in %q", host, hostPortStr))
}
if ip.Is4() {
ipv4 = host
ipv6 = "none"
}
if ip.Is6() {
ipv6 = host
ipv4 = "none"
}
node := &tailcfg.DERPNode{
Name: fmt.Sprint(regionID) + "a",
RegionID: regionID,
HostName: fmt.Sprintf("d%d.invalid", regionID),
IPv4: ipv4,
IPv6: ipv6,
STUNPort: port,
STUNOnly: true,
}
m.Regions[regionID] = &tailcfg.DERPRegion{
RegionID: regionID,
Nodes: []*tailcfg.DERPNode{node},
}
}
return m
}