mirror of https://github.com/tailscale/tailscale/
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
7.3 KiB
Go
173 lines
7.3 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package controlknobs contains client options configurable from control which can be turned on
|
|
// or off. The ability to turn options on and off is for incrementally adding features in.
|
|
package controlknobs
|
|
|
|
import (
|
|
"sync/atomic"
|
|
|
|
"tailscale.com/syncs"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/types/opt"
|
|
)
|
|
|
|
// 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
|
|
|
|
// DisableDRPO is whether control says to disable the
|
|
// DERP route optimization (Issue 150).
|
|
DisableDRPO 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
|
|
// in the OS rather than one /32 per peer.
|
|
OneCGNAT syncs.AtomicValue[opt.Bool]
|
|
|
|
// ForceBackgroundSTUN forces netcheck STUN queries to keep
|
|
// running in magicsock, even when idle.
|
|
ForceBackgroundSTUN atomic.Bool
|
|
|
|
// DisableDeltaUpdates is whether the node should not process
|
|
// incremental (delta) netmap updates and should treat all netmap
|
|
// changes as "full" ones as tailscaled did in 1.48.x and earlier.
|
|
DisableDeltaUpdates atomic.Bool
|
|
|
|
// PeerMTUEnable is whether the node should do peer path MTU discovery.
|
|
PeerMTUEnable atomic.Bool
|
|
|
|
// DisableDNSForwarderTCPRetries is whether the DNS forwarder should
|
|
// skip retrying truncated queries over TCP.
|
|
DisableDNSForwarderTCPRetries atomic.Bool
|
|
|
|
// SilentDisco is whether the node should suppress disco heartbeats to its
|
|
// peers.
|
|
SilentDisco atomic.Bool
|
|
|
|
// LinuxForceIPTables is whether the node should use iptables for Linux
|
|
// netfiltering, unless overridden by the user.
|
|
LinuxForceIPTables atomic.Bool
|
|
|
|
// LinuxForceNfTables is whether the node should use nftables for Linux
|
|
// netfiltering, unless overridden by the user.
|
|
LinuxForceNfTables atomic.Bool
|
|
|
|
// SeamlessKeyRenewal is whether to enable the alpha functionality of
|
|
// renewing node keys without breaking connections.
|
|
// 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
|
|
|
|
// AppCStoreRoutes is whether the node should store RouteInfo to StateStore
|
|
// if it's an app connector.
|
|
AppCStoreRoutes atomic.Bool
|
|
|
|
// UserDialUseRoutes is whether tsdial.Dialer.UserDial should use routes to determine
|
|
// how to dial the destination address. When true, it also makes the DNS forwarder
|
|
// use UserDial instead of SystemDial when dialing resolvers.
|
|
UserDialUseRoutes atomic.Bool
|
|
|
|
// DisableSplitDNSWhenNoCustomResolvers indicates that the node's DNS manager
|
|
// should not adopt a split DNS configuration even though the Config of the
|
|
// resolver only contains routes that do not specify custom resolver(s), hence
|
|
// all DNS queries can be safely sent to the upstream DNS resolver and the
|
|
// node's DNS forwarder doesn't need to handle all DNS traffic.
|
|
// This is for now (2024-06-06) an iOS-specific battery life optimization,
|
|
// and this knob allows us to disable the optimization remotely if needed.
|
|
DisableSplitDNSWhenNoCustomResolvers atomic.Bool
|
|
}
|
|
|
|
// UpdateFromNodeAttributes updates k (if non-nil) based on the provided self
|
|
// node attributes (Node.Capabilities).
|
|
func (k *Knobs) UpdateFromNodeAttributes(capMap tailcfg.NodeCapMap) {
|
|
if k == nil {
|
|
return
|
|
}
|
|
has := capMap.Contains
|
|
var (
|
|
keepFullWG = has(tailcfg.NodeAttrDebugDisableWGTrim)
|
|
disableDRPO = has(tailcfg.NodeAttrDebugDisableDRPO)
|
|
disableUPnP = has(tailcfg.NodeAttrDisableUPnP)
|
|
randomizeClientPort = has(tailcfg.NodeAttrRandomizeClientPort)
|
|
disableDeltaUpdates = has(tailcfg.NodeAttrDisableDeltaUpdates)
|
|
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)
|
|
probeUDPLifetime = has(tailcfg.NodeAttrProbeUDPLifetime)
|
|
appCStoreRoutes = has(tailcfg.NodeAttrStoreAppCRoutes)
|
|
userDialUseRoutes = has(tailcfg.NodeAttrUserDialUseRoutes)
|
|
disableSplitDNSWhenNoCustomResolvers = has(tailcfg.NodeAttrDisableSplitDNSWhenNoCustomResolvers)
|
|
)
|
|
|
|
if has(tailcfg.NodeAttrOneCGNATEnable) {
|
|
oneCGNAT.Set(true)
|
|
} else if has(tailcfg.NodeAttrOneCGNATDisable) {
|
|
oneCGNAT.Set(false)
|
|
}
|
|
|
|
k.KeepFullWGConfig.Store(keepFullWG)
|
|
k.DisableDRPO.Store(disableDRPO)
|
|
k.DisableUPnP.Store(disableUPnP)
|
|
k.RandomizeClientPort.Store(randomizeClientPort)
|
|
k.OneCGNAT.Store(oneCGNAT)
|
|
k.ForceBackgroundSTUN.Store(forceBackgroundSTUN)
|
|
k.DisableDeltaUpdates.Store(disableDeltaUpdates)
|
|
k.PeerMTUEnable.Store(peerMTUEnable)
|
|
k.DisableDNSForwarderTCPRetries.Store(dnsForwarderDisableTCPRetries)
|
|
k.SilentDisco.Store(silentDisco)
|
|
k.LinuxForceIPTables.Store(forceIPTables)
|
|
k.LinuxForceNfTables.Store(forceNfTables)
|
|
k.SeamlessKeyRenewal.Store(seamlessKeyRenewal)
|
|
k.ProbeUDPLifetime.Store(probeUDPLifetime)
|
|
k.AppCStoreRoutes.Store(appCStoreRoutes)
|
|
k.UserDialUseRoutes.Store(userDialUseRoutes)
|
|
k.DisableSplitDNSWhenNoCustomResolvers.Store(disableSplitDNSWhenNoCustomResolvers)
|
|
}
|
|
|
|
// AsDebugJSON returns k as something that can be marshalled with json.Marshal
|
|
// for debug.
|
|
func (k *Knobs) AsDebugJSON() map[string]any {
|
|
if k == nil {
|
|
return nil
|
|
}
|
|
return map[string]any{
|
|
"DisableUPnP": k.DisableUPnP.Load(),
|
|
"DisableDRPO": k.DisableDRPO.Load(),
|
|
"KeepFullWGConfig": k.KeepFullWGConfig.Load(),
|
|
"RandomizeClientPort": k.RandomizeClientPort.Load(),
|
|
"OneCGNAT": k.OneCGNAT.Load(),
|
|
"ForceBackgroundSTUN": k.ForceBackgroundSTUN.Load(),
|
|
"DisableDeltaUpdates": k.DisableDeltaUpdates.Load(),
|
|
"PeerMTUEnable": k.PeerMTUEnable.Load(),
|
|
"DisableDNSForwarderTCPRetries": k.DisableDNSForwarderTCPRetries.Load(),
|
|
"SilentDisco": k.SilentDisco.Load(),
|
|
"LinuxForceIPTables": k.LinuxForceIPTables.Load(),
|
|
"LinuxForceNfTables": k.LinuxForceNfTables.Load(),
|
|
"SeamlessKeyRenewal": k.SeamlessKeyRenewal.Load(),
|
|
"ProbeUDPLifetime": k.ProbeUDPLifetime.Load(),
|
|
"AppCStoreRoutes": k.AppCStoreRoutes.Load(),
|
|
"UserDialUseRoutes": k.UserDialUseRoutes.Load(),
|
|
"DisableSplitDNSWhenNoCustomResolvers": k.DisableSplitDNSWhenNoCustomResolvers.Load(),
|
|
}
|
|
}
|