This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+103 -57
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
// Package controlknobs contains client options configurable from control which can be turned on
@@ -12,6 +12,7 @@ import (
"tailscale.com/syncs"
"tailscale.com/tailcfg"
"tailscale.com/tailcfg/nodecap"
"tailscale.com/types/opt"
)
@@ -21,16 +22,11 @@ type Knobs struct {
// DisableUPnP indicates whether to attempt UPnP mapping.
DisableUPnP atomic.Bool
// 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
// OneCGNAT is whether the node should make one big CGNAT route
// in the OS rather than one /32 per peer.
OneCGNAT syncs.AtomicValue[opt.Bool]
@@ -62,12 +58,6 @@ type Knobs struct {
// netfiltering, unless overridden by the user.
LinuxForceNfTables atomic.Bool
// SeamlessKeyRenewal is whether to renew node keys without breaking connections.
// This is enabled by default in 1.90 and later, but we but we can remotely disable
// it from the control plane if there's a problem.
// http://go/seamless-key-renewal
SeamlessKeyRenewal atomic.Bool
// ProbeUDPLifetime is whether the node should probe UDP path lifetime on
// the tail end of an active direct connection in magicsock.
ProbeUDPLifetime atomic.Bool
@@ -107,6 +97,57 @@ type Knobs struct {
// of queued netmap.NetworkMap between the controlclient and LocalBackend.
// See tailscale/tailscale#14768.
DisableSkipStatusQueue atomic.Bool
// DisableHostsFileUpdates indicates that the node's DNS manager should not create
// hosts file entries when it normally would, such as when we're not the primary
// resolver on Windows or when the host is domain-joined and its primary domain
// takes precedence over MagicDNS. As of 2026-02-13, it is only used on Windows.
DisableHostsFileUpdates atomic.Bool
// ForceRegisterMagicDNSIPv4Only is whether the node should only register
// its IPv4 MagicDNS service IP and not its IPv6 one. The IPv6 one,
// tsaddr.TailscaleServiceIPv6String, still works in either case. This knob
// controls only whether we tell systemd/etc about the IPv6 one.
// See https://github.com/tailscale/tailscale/issues/15404.
// TODO(bradfitz): remove this a few releases after 2026-02-16.
ForceRegisterMagicDNSIPv4Only atomic.Bool
// EmitRuntimeMetrics is whether the node should poll and emit [runtime/metrics]
// as [tailscale.com/util/clientmetric]'s.
EmitRuntimeMetrics atomic.Bool
// DisableUDPGRO disables UDP GRO on the magicsock UDP socket. See
// [tailcfg.NodeAttrDisableUDPGRO].
DisableUDPGRO atomic.Bool
// DisableUDPGSO disables UDP GSO on the magicsock UDP socket. See
// [tailcfg.NodeAttrDisableUDPGSO].
DisableUDPGSO atomic.Bool
// DisableTUNUDPGRO disables UDP GRO on the Tailscale TUN device. See
// [tailcfg.NodeAttrDisableTUNUDPGRO].
DisableTUNUDPGRO atomic.Bool
// DisableTUNTCPGRO disables TCP GRO on the Tailscale TUN device. See
// [tailcfg.NodeAttrDisableTUNTCPGRO].
DisableTUNTCPGRO atomic.Bool
// NeverGSOEqualTail enables a UDP GSO sentinel-tail workaround in the
// underlay UDP packet TX path on Linux. Applies to magicsock and peer relay
// UDP sockets. See [tailcfg.NodeAttrNeverGSOEqualTail].
NeverGSOEqualTail atomic.Bool
// CacheNetworkMaps is whether the node should persistently cache network
// maps and use them to establish peer connectivity on start, if doing so
// is supported by the client and storage is available.
CacheNetworkMaps atomic.Bool
// ScopeQuad100OnMacOS lets sandboxed macOS scope quad-100 for simple DNS
// configs, so public queries can use the OS resolver (including DoH).
// Custom split resolvers or unrouted forward/PTR records still require
// quad-100 as primary. Off by default; has no effect on other platforms.
// See tailscale/corp#45534 and tailscale/corp#48693.
ScopeQuad100OnMacOS atomic.Bool
}
// UpdateFromNodeAttributes updates k (if non-nil) based on the provided self
@@ -117,35 +158,41 @@ func (k *Knobs) UpdateFromNodeAttributes(capMap tailcfg.NodeCapMap) {
}
has := capMap.Contains
var (
keepFullWG = has(tailcfg.NodeAttrDebugDisableWGTrim)
disableUPnP = has(tailcfg.NodeAttrDisableUPnP)
randomizeClientPort = has(tailcfg.NodeAttrRandomizeClientPort)
disableDeltaUpdates = has(tailcfg.NodeAttrDisableDeltaUpdates)
disableUPnP = has(nodecap.DisableUPnP)
randomizeClientPort = has(nodecap.RandomizeClientPort)
disableDeltaUpdates = has(nodecap.DisableDeltaUpdates)
oneCGNAT opt.Bool
forceBackgroundSTUN = has(tailcfg.NodeAttrDebugForceBackgroundSTUN)
peerMTUEnable = has(tailcfg.NodeAttrPeerMTUEnable)
dnsForwarderDisableTCPRetries = has(tailcfg.NodeAttrDNSForwarderDisableTCPRetries)
silentDisco = has(tailcfg.NodeAttrSilentDisco)
forceIPTables = has(tailcfg.NodeAttrLinuxMustUseIPTables)
forceNfTables = has(tailcfg.NodeAttrLinuxMustUseNfTables)
seamlessKeyRenewal = has(tailcfg.NodeAttrSeamlessKeyRenewal)
disableSeamlessKeyRenewal = has(tailcfg.NodeAttrDisableSeamlessKeyRenewal)
probeUDPLifetime = has(tailcfg.NodeAttrProbeUDPLifetime)
appCStoreRoutes = has(tailcfg.NodeAttrStoreAppCRoutes)
userDialUseRoutes = has(tailcfg.NodeAttrUserDialUseRoutes)
disableSplitDNSWhenNoCustomResolvers = has(tailcfg.NodeAttrDisableSplitDNSWhenNoCustomResolvers)
disableLocalDNSOverrideViaNRPT = has(tailcfg.NodeAttrDisableLocalDNSOverrideViaNRPT)
disableCaptivePortalDetection = has(tailcfg.NodeAttrDisableCaptivePortalDetection)
disableSkipStatusQueue = has(tailcfg.NodeAttrDisableSkipStatusQueue)
forceBackgroundSTUN = has(nodecap.DebugForceBackgroundSTUN)
peerMTUEnable = has(nodecap.PeerMTUEnable)
dnsForwarderDisableTCPRetries = has(nodecap.DNSForwarderDisableTCPRetries)
silentDisco = has(nodecap.SilentDisco)
forceIPTables = has(nodecap.LinuxMustUseIPTables)
forceNfTables = has(nodecap.LinuxMustUseNfTables)
probeUDPLifetime = has(nodecap.ProbeUDPLifetime)
appCStoreRoutes = has(nodecap.StoreAppCRoutes)
userDialUseRoutes = has(nodecap.UserDialUseRoutes)
disableSplitDNSWhenNoCustomResolvers = has(nodecap.DisableSplitDNSWhenNoCustomResolvers)
disableLocalDNSOverrideViaNRPT = has(nodecap.DisableLocalDNSOverrideViaNRPT)
disableCaptivePortalDetection = has(nodecap.DisableCaptivePortalDetection)
disableSkipStatusQueue = has(nodecap.DisableSkipStatusQueue)
disableHostsFileUpdates = has(nodecap.DisableHostsFileUpdates)
forceRegisterMagicDNSIPv4Only = has(nodecap.ForceRegisterMagicDNSIPv4Only)
emitRuntimeMetrics = has(nodecap.EmitRuntimeMetrics)
disableUDPGRO = has(nodecap.DisableUDPGRO)
disableUDPGSO = has(nodecap.DisableUDPGSO)
disableTUNUDPGRO = has(nodecap.DisableTUNUDPGRO)
disableTUNTCPGRO = has(nodecap.DisableTUNTCPGRO)
neverGSOEqualTail = has(nodecap.NeverGSOEqualTail)
cacheNetworkMaps = has(nodecap.CacheNetworkMaps)
scopeQuad100OnMacOS = has(nodecap.ScopeQuad100OnMacOS)
)
if has(tailcfg.NodeAttrOneCGNATEnable) {
if has(nodecap.OneCGNATEnable) {
oneCGNAT.Set(true)
} else if has(tailcfg.NodeAttrOneCGNATDisable) {
} else if has(nodecap.OneCGNATDisable) {
oneCGNAT.Set(false)
}
k.KeepFullWGConfig.Store(keepFullWG)
k.DisableUPnP.Store(disableUPnP)
k.RandomizeClientPort.Store(randomizeClientPort)
k.OneCGNAT.Store(oneCGNAT)
@@ -163,21 +210,16 @@ func (k *Knobs) UpdateFromNodeAttributes(capMap tailcfg.NodeCapMap) {
k.DisableLocalDNSOverrideViaNRPT.Store(disableLocalDNSOverrideViaNRPT)
k.DisableCaptivePortalDetection.Store(disableCaptivePortalDetection)
k.DisableSkipStatusQueue.Store(disableSkipStatusQueue)
// If both attributes are present, then "enable" should win. This reflects
// the history of seamless key renewal.
//
// Before 1.90, seamless was a private alpha, opt-in feature. Devices would
// only seamless do if customers opted in using the seamless renewal attr.
//
// In 1.90 and later, seamless is the default behaviour, and devices will use
// seamless unless explicitly told not to by control (e.g. if we discover
// a bug and want clients to use the prior behaviour).
//
// If a customer has opted in to the pre-1.90 seamless implementation, we
// don't want to switch it off for them -- we only want to switch it off for
// devices that haven't opted in.
k.SeamlessKeyRenewal.Store(seamlessKeyRenewal || !disableSeamlessKeyRenewal)
k.DisableHostsFileUpdates.Store(disableHostsFileUpdates)
k.ForceRegisterMagicDNSIPv4Only.Store(forceRegisterMagicDNSIPv4Only)
k.EmitRuntimeMetrics.Store(emitRuntimeMetrics)
k.DisableUDPGRO.Store(disableUDPGRO)
k.DisableUDPGSO.Store(disableUDPGSO)
k.DisableTUNUDPGRO.Store(disableTUNUDPGRO)
k.DisableTUNTCPGRO.Store(disableTUNTCPGRO)
k.NeverGSOEqualTail.Store(neverGSOEqualTail)
k.CacheNetworkMaps.Store(cacheNetworkMaps)
k.ScopeQuad100OnMacOS.Store(scopeQuad100OnMacOS)
}
// AsDebugJSON returns k as something that can be marshalled with json.Marshal
@@ -187,18 +229,22 @@ func (k *Knobs) AsDebugJSON() map[string]any {
return nil
}
ret := map[string]any{}
rt := reflect.TypeFor[Knobs]()
rv := reflect.ValueOf(k).Elem() // of *k
for i := 0; i < rt.NumField(); i++ {
name := rt.Field(i).Name
switch v := rv.Field(i).Addr().Interface().(type) {
for sf, fv := range rv.Fields() {
switch v := fv.Addr().Interface().(type) {
case *atomic.Bool:
ret[name] = v.Load()
ret[sf.Name] = v.Load()
case *syncs.AtomicValue[opt.Bool]:
ret[name] = v.Load()
ret[sf.Name] = v.Load()
default:
panic(fmt.Sprintf("unknown field type %T for %v", v, name))
panic(fmt.Sprintf("unknown field type %T for %v", v, sf.Name))
}
}
return ret
}
// ShouldForceRegisterMagicDNSIPv4Only reports the value of
// ForceRegisterMagicDNSIPv4Only, or false if k is nil.
func (k *Knobs) ShouldForceRegisterMagicDNSIPv4Only() bool {
return k != nil && k.ForceRegisterMagicDNSIPv4Only.Load()
}