derp: increase update frequency and harden on failures (#2741)

This commit is contained in:
Kristoffer Dalby
2025-08-22 10:40:38 +02:00
committed by GitHub
parent 51c6367bb1
commit b87567628a
10 changed files with 417 additions and 67 deletions

View File

@@ -1,16 +1,22 @@
package derp
import (
"cmp"
"context"
"encoding/json"
"hash/crc64"
"io"
"maps"
"math/rand"
"net/http"
"net/url"
"os"
"reflect"
"sync"
"time"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
"tailscale.com/tailcfg"
)
@@ -79,26 +85,16 @@ func mergeDERPMaps(derpMaps []*tailcfg.DERPMap) *tailcfg.DERPMap {
return &result
}
func GetDERPMap(cfg types.DERPConfig) *tailcfg.DERPMap {
func GetDERPMap(cfg types.DERPConfig) (*tailcfg.DERPMap, error) {
var derpMaps []*tailcfg.DERPMap
if cfg.DERPMap != nil {
derpMaps = append(derpMaps, cfg.DERPMap)
}
for _, path := range cfg.Paths {
log.Debug().
Str("func", "GetDERPMap").
Str("path", path).
Msg("Loading DERPMap from path")
derpMap, err := loadDERPMapFromPath(path)
if err != nil {
log.Error().
Str("func", "GetDERPMap").
Str("path", path).
Err(err).
Msg("Could not load DERP map from path")
break
return nil, err
}
derpMaps = append(derpMaps, derpMap)
@@ -106,26 +102,59 @@ func GetDERPMap(cfg types.DERPConfig) *tailcfg.DERPMap {
for _, addr := range cfg.URLs {
derpMap, err := loadDERPMapFromURL(addr)
log.Debug().
Str("func", "GetDERPMap").
Str("url", addr.String()).
Msg("Loading DERPMap from path")
if err != nil {
log.Error().
Str("func", "GetDERPMap").
Str("url", addr.String()).
Err(err).
Msg("Could not load DERP map from path")
break
return nil, err
}
derpMaps = append(derpMaps, derpMap)
}
derpMap := mergeDERPMaps(derpMaps)
shuffleDERPMap(derpMap)
log.Trace().Interface("derpMap", derpMap).Msg("DERPMap loaded")
return derpMap
return derpMap, nil
}
func shuffleDERPMap(dm *tailcfg.DERPMap) {
if dm == nil || len(dm.Regions) == 0 {
return
}
for id, region := range dm.Regions {
if len(region.Nodes) == 0 {
continue
}
dm.Regions[id] = shuffleRegionNoClone(region)
}
}
var crc64Table = crc64.MakeTable(crc64.ISO)
var (
derpRandomOnce sync.Once
derpRandomInst *rand.Rand
derpRandomMu sync.RWMutex
)
func derpRandom() *rand.Rand {
derpRandomOnce.Do(func() {
seed := cmp.Or(viper.GetString("dns.base_domain"), time.Now().String())
rnd := rand.New(rand.NewSource(0))
rnd.Seed(int64(crc64.Checksum([]byte(seed), crc64Table)))
derpRandomInst = rnd
})
return derpRandomInst
}
func resetDerpRandomForTesting() {
derpRandomMu.Lock()
defer derpRandomMu.Unlock()
derpRandomOnce = sync.Once{}
derpRandomInst = nil
}
func shuffleRegionNoClone(r *tailcfg.DERPRegion) *tailcfg.DERPRegion {
derpRandom().Shuffle(len(r.Nodes), reflect.Swapper(r.Nodes))
return r
}