state: ensure netinfo is preserved and not removed

the client will send a lot of fields as `nil` if they have
not changed. NetInfo, which is inside Hostinfo, is one of those
fields and we often would override the whole hostinfo meaning that
we would remove netinfo if it hadnt changed.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby
2025-09-08 11:17:27 +02:00
committed by Kristoffer Dalby
parent 233dffc186
commit 476f30ab20
4 changed files with 362 additions and 45 deletions

View File

@@ -0,0 +1,50 @@
// Package state provides pure functions for processing MapRequest data.
// These functions are extracted from UpdateNodeFromMapRequest to improve
// testability and maintainability.
package state
import (
"github.com/juanfont/headscale/hscontrol/types"
"github.com/rs/zerolog/log"
"tailscale.com/tailcfg"
)
// NetInfoFromMapRequest determines the correct NetInfo to use.
// Returns the NetInfo that should be used for this request.
func NetInfoFromMapRequest(
nodeID types.NodeID,
currentHostinfo *tailcfg.Hostinfo,
reqHostinfo *tailcfg.Hostinfo,
) *tailcfg.NetInfo {
// If request has NetInfo, use it
if reqHostinfo != nil && reqHostinfo.NetInfo != nil {
return reqHostinfo.NetInfo
}
// Otherwise, use current NetInfo if available
if currentHostinfo != nil && currentHostinfo.NetInfo != nil {
log.Debug().
Caller().
Uint64("node.id", nodeID.Uint64()).
Int("preferredDERP", currentHostinfo.NetInfo.PreferredDERP).
Msg("using NetInfo from previous Hostinfo in MapRequest")
return currentHostinfo.NetInfo
}
// No NetInfo available anywhere - log for debugging
var hostname string
if reqHostinfo != nil {
hostname = reqHostinfo.Hostname
} else if currentHostinfo != nil {
hostname = currentHostinfo.Hostname
}
log.Debug().
Caller().
Uint64("node.id", nodeID.Uint64()).
Str("node.hostname", hostname).
Msg("node sent update but has no NetInfo in request or database")
return nil
}