mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 13:18:53 +00:00
control/controlknobs, all: add plumbed Knobs type, not global variables
Previously two tsnet nodes in the same process couldn't have disjoint sets of controlknob settings from control as both would overwrite each other's global variables. This plumbs a new controlknobs.Knobs type around everywhere and hangs the knobs sent by control on that instead. Updates #9351 Change-Id: I75338646d36813ed971b4ffad6f9a8b41ec91560 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:

committed by
Brad Fitzpatrick

parent
d050700a3b
commit
4e91cf20a8
@@ -25,7 +25,6 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go4.org/mem"
|
||||
@@ -44,7 +43,6 @@ import (
|
||||
"tailscale.com/net/tsdial"
|
||||
"tailscale.com/net/tshttpproxy"
|
||||
"tailscale.com/smallzstd"
|
||||
"tailscale.com/syncs"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tka"
|
||||
"tailscale.com/tstime"
|
||||
@@ -66,7 +64,8 @@ type Direct struct {
|
||||
httpc *http.Client // HTTP client used to talk to tailcontrol
|
||||
dialer *tsdial.Dialer
|
||||
dnsCache *dnscache.Resolver
|
||||
serverURL string // URL of the tailcontrol server
|
||||
controlKnobs *controlknobs.Knobs // always non-nil
|
||||
serverURL string // URL of the tailcontrol server
|
||||
clock tstime.Clock
|
||||
lastPrintMap time.Time
|
||||
logf logger.Logf
|
||||
@@ -129,6 +128,7 @@ type Options struct {
|
||||
OnControlTime func(time.Time) // optional func to notify callers of new time from control
|
||||
Dialer *tsdial.Dialer // non-nil
|
||||
C2NHandler http.Handler // or nil
|
||||
ControlKnobs *controlknobs.Knobs // or nil to ignore
|
||||
|
||||
// Observer is called when there's a change in status to report
|
||||
// from the control client.
|
||||
@@ -202,6 +202,9 @@ func NewDirect(opts Options) (*Direct, error) {
|
||||
if opts.GetMachinePrivateKey == nil {
|
||||
return nil, errors.New("controlclient.New: no GetMachinePrivateKey specified")
|
||||
}
|
||||
if opts.ControlKnobs == nil {
|
||||
opts.ControlKnobs = &controlknobs.Knobs{}
|
||||
}
|
||||
opts.ServerURL = strings.TrimRight(opts.ServerURL, "/")
|
||||
serverURL, err := url.Parse(opts.ServerURL)
|
||||
if err != nil {
|
||||
@@ -249,6 +252,7 @@ func NewDirect(opts Options) (*Direct, error) {
|
||||
|
||||
c := &Direct{
|
||||
httpc: httpc,
|
||||
controlKnobs: opts.ControlKnobs,
|
||||
getMachinePrivKey: opts.GetMachinePrivateKey,
|
||||
serverURL: opts.ServerURL,
|
||||
clock: opts.Clock,
|
||||
@@ -946,7 +950,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, isStreaming bool, nu Netmap
|
||||
|
||||
var mapResIdx int // 0 for first message, then 1+ for deltas
|
||||
|
||||
sess := newMapSession(persist.PrivateNodeKey(), nu)
|
||||
sess := newMapSession(persist.PrivateNodeKey(), nu, c.controlKnobs)
|
||||
defer sess.Close()
|
||||
sess.cancel = cancel
|
||||
sess.logf = c.logf
|
||||
@@ -1287,38 +1291,11 @@ func initDevKnob() devKnobs {
|
||||
|
||||
var clock tstime.Clock = tstime.StdClock{}
|
||||
|
||||
// config from control.
|
||||
var (
|
||||
controlDisableDRPO atomic.Bool
|
||||
controlKeepFullWGConfig atomic.Bool
|
||||
controlRandomizeClientPort atomic.Bool
|
||||
controlOneCGNAT syncs.AtomicValue[opt.Bool]
|
||||
)
|
||||
|
||||
// DisableDRPO reports whether control says to disable the
|
||||
// DERP route optimization (Issue 150).
|
||||
func DisableDRPO() bool {
|
||||
return controlDisableDRPO.Load()
|
||||
}
|
||||
|
||||
// KeepFullWGConfig reports whether control says we should disable the lazy
|
||||
// wireguard programming and instead give it the full netmap always.
|
||||
func KeepFullWGConfig() bool {
|
||||
return controlKeepFullWGConfig.Load()
|
||||
}
|
||||
|
||||
// RandomizeClientPort reports whether control says we should randomize
|
||||
// the client port.
|
||||
func RandomizeClientPort() bool {
|
||||
return controlRandomizeClientPort.Load()
|
||||
}
|
||||
|
||||
// ControlOneCGNATSetting returns control's OneCGNAT setting, if any.
|
||||
func ControlOneCGNATSetting() opt.Bool {
|
||||
return controlOneCGNAT.Load()
|
||||
}
|
||||
|
||||
func setControlKnobsFromNodeAttrs(selfNodeAttrs []string) {
|
||||
func (ms *mapSession) setControlKnobsFromNodeAttrs(selfNodeAttrs []string) {
|
||||
k := ms.controlKnobs
|
||||
if k == nil {
|
||||
return
|
||||
}
|
||||
var (
|
||||
keepFullWG bool
|
||||
disableDRPO bool
|
||||
@@ -1342,11 +1319,11 @@ func setControlKnobsFromNodeAttrs(selfNodeAttrs []string) {
|
||||
oneCGNAT.Set(false)
|
||||
}
|
||||
}
|
||||
controlKeepFullWGConfig.Store(keepFullWG)
|
||||
controlDisableDRPO.Store(disableDRPO)
|
||||
controlknobs.SetDisableUPnP(disableUPnP)
|
||||
controlRandomizeClientPort.Store(randomizeClientPort)
|
||||
controlOneCGNAT.Store(oneCGNAT)
|
||||
k.KeepFullWGConfig.Store(keepFullWG)
|
||||
k.DisableDRPO.Store(disableDRPO)
|
||||
k.DisableUPnP.Store(disableUPnP)
|
||||
k.RandomizeClientPort.Store(randomizeClientPort)
|
||||
k.OneCGNAT.Store(oneCGNAT)
|
||||
}
|
||||
|
||||
// ipForwardingBroken reports whether the system's IP forwarding is disabled
|
||||
|
@@ -15,6 +15,7 @@ import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstime"
|
||||
@@ -38,7 +39,8 @@ import (
|
||||
// one MapRequest).
|
||||
type mapSession struct {
|
||||
// Immutable fields.
|
||||
nu NetmapUpdater // called on changes (in addition to the optional hooks below)
|
||||
nu NetmapUpdater // called on changes (in addition to the optional hooks below)
|
||||
controlKnobs *controlknobs.Knobs // or nil
|
||||
privateNodeKey key.NodePrivate
|
||||
publicNodeKey key.NodePublic
|
||||
logf logger.Logf
|
||||
@@ -94,9 +96,10 @@ type mapSession struct {
|
||||
// Modify its optional fields on the returned value before use.
|
||||
//
|
||||
// It must have its Close method called to release resources.
|
||||
func newMapSession(privateNodeKey key.NodePrivate, nu NetmapUpdater) *mapSession {
|
||||
func newMapSession(privateNodeKey key.NodePrivate, nu NetmapUpdater, controlKnobs *controlknobs.Knobs) *mapSession {
|
||||
ms := &mapSession{
|
||||
nu: nu,
|
||||
controlKnobs: controlKnobs,
|
||||
privateNodeKey: privateNodeKey,
|
||||
publicNodeKey: privateNodeKey.Public(),
|
||||
lastDNSConfig: new(tailcfg.DNSConfig),
|
||||
@@ -184,7 +187,7 @@ func (ms *mapSession) HandleNonKeepAliveMapResponse(ctx context.Context, resp *t
|
||||
if DevKnob.StripCaps() {
|
||||
resp.Node.Capabilities = nil
|
||||
}
|
||||
setControlKnobsFromNodeAttrs(resp.Node.Capabilities)
|
||||
ms.setControlKnobsFromNodeAttrs(resp.Node.Capabilities)
|
||||
}
|
||||
|
||||
// Call Node.InitDisplayNames on any changed nodes.
|
||||
|
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"go4.org/mem"
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/tstime"
|
||||
@@ -392,7 +393,7 @@ func formatNodes(nodes []*tailcfg.Node) string {
|
||||
}
|
||||
|
||||
func newTestMapSession(t testing.TB, nu NetmapUpdater) *mapSession {
|
||||
ms := newMapSession(key.NewNode(), nu)
|
||||
ms := newMapSession(key.NewNode(), nu, new(controlknobs.Knobs))
|
||||
t.Cleanup(ms.Close)
|
||||
ms.logf = t.Logf
|
||||
return ms
|
||||
|
@@ -8,22 +8,30 @@ package controlknobs
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/syncs"
|
||||
"tailscale.com/types/opt"
|
||||
)
|
||||
|
||||
// disableUPnP indicates whether to attempt UPnP mapping.
|
||||
var disableUPnPControl atomic.Bool
|
||||
// Knobs is the set of knobs that the control plane's coordination server can
|
||||
// adjust at runtime.
|
||||
type Knobs struct {
|
||||
// DisableUPnP indicates whether to attempt UPnP mapping.
|
||||
DisableUPnP atomic.Bool
|
||||
|
||||
var disableUPnpEnv = envknob.RegisterBool("TS_DISABLE_UPNP")
|
||||
// DisableDRPO is whether control says to disable the
|
||||
// DERP route optimization (Issue 150).
|
||||
DisableDRPO atomic.Bool
|
||||
|
||||
// DisableUPnP reports the last reported value from control
|
||||
// whether UPnP portmapping should be disabled.
|
||||
func DisableUPnP() bool {
|
||||
return disableUPnPControl.Load() || disableUPnpEnv()
|
||||
}
|
||||
|
||||
// SetDisableUPnP sets whether control says that UPnP should be
|
||||
// disabled.
|
||||
func SetDisableUPnP(v bool) {
|
||||
disableUPnPControl.Store(v)
|
||||
// KeepFullWGConfig is whether we should disable the lazy wireguard
|
||||
// programming and instead give WireGuard the full netmap always, even for
|
||||
// idle peers.
|
||||
KeepFullWGConfig atomic.Bool
|
||||
|
||||
// RandomizeClientPort is whether control says we should randomize
|
||||
// the client port.
|
||||
RandomizeClientPort atomic.Bool
|
||||
|
||||
// OneCGNAT is whether the the node should make one big CGNAT route
|
||||
// in the OS rather than one /32 per peer.
|
||||
OneCGNAT syncs.AtomicValue[opt.Bool]
|
||||
}
|
||||
|
Reference in New Issue
Block a user