2
0

controlknobs.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package controlknobs contains client options configurable from control which can be turned on
  4. // or off. The ability to turn options on and off is for incrementally adding features in.
  5. package controlknobs
  6. import (
  7. "fmt"
  8. "reflect"
  9. "sync/atomic"
  10. "tailscale.com/syncs"
  11. "tailscale.com/tailcfg"
  12. "tailscale.com/types/opt"
  13. )
  14. // Knobs is the set of knobs that the control plane's coordination server can
  15. // adjust at runtime.
  16. type Knobs struct {
  17. // DisableUPnP indicates whether to attempt UPnP mapping.
  18. DisableUPnP atomic.Bool
  19. // KeepFullWGConfig is whether we should disable the lazy wireguard
  20. // programming and instead give WireGuard the full netmap always, even for
  21. // idle peers.
  22. KeepFullWGConfig atomic.Bool
  23. // RandomizeClientPort is whether control says we should randomize
  24. // the client port.
  25. RandomizeClientPort atomic.Bool
  26. // OneCGNAT is whether the the node should make one big CGNAT route
  27. // in the OS rather than one /32 per peer.
  28. OneCGNAT syncs.AtomicValue[opt.Bool]
  29. // ForceBackgroundSTUN forces netcheck STUN queries to keep
  30. // running in magicsock, even when idle.
  31. ForceBackgroundSTUN atomic.Bool
  32. // DisableDeltaUpdates is whether the node should not process
  33. // incremental (delta) netmap updates and should treat all netmap
  34. // changes as "full" ones as tailscaled did in 1.48.x and earlier.
  35. DisableDeltaUpdates atomic.Bool
  36. // PeerMTUEnable is whether the node should do peer path MTU discovery.
  37. PeerMTUEnable atomic.Bool
  38. // DisableDNSForwarderTCPRetries is whether the DNS forwarder should
  39. // skip retrying truncated queries over TCP.
  40. DisableDNSForwarderTCPRetries atomic.Bool
  41. // SilentDisco is whether the node should suppress disco heartbeats to its
  42. // peers.
  43. SilentDisco atomic.Bool
  44. // LinuxForceIPTables is whether the node should use iptables for Linux
  45. // netfiltering, unless overridden by the user.
  46. LinuxForceIPTables atomic.Bool
  47. // LinuxForceNfTables is whether the node should use nftables for Linux
  48. // netfiltering, unless overridden by the user.
  49. LinuxForceNfTables atomic.Bool
  50. // SeamlessKeyRenewal is whether to enable the alpha functionality of
  51. // renewing node keys without breaking connections.
  52. // http://go/seamless-key-renewal
  53. SeamlessKeyRenewal atomic.Bool
  54. // ProbeUDPLifetime is whether the node should probe UDP path lifetime on
  55. // the tail end of an active direct connection in magicsock.
  56. ProbeUDPLifetime atomic.Bool
  57. // AppCStoreRoutes is whether the node should store RouteInfo to StateStore
  58. // if it's an app connector.
  59. AppCStoreRoutes atomic.Bool
  60. // UserDialUseRoutes is whether tsdial.Dialer.UserDial should use routes to determine
  61. // how to dial the destination address. When true, it also makes the DNS forwarder
  62. // use UserDial instead of SystemDial when dialing resolvers.
  63. UserDialUseRoutes atomic.Bool
  64. // DisableSplitDNSWhenNoCustomResolvers indicates that the node's DNS manager
  65. // should not adopt a split DNS configuration even though the Config of the
  66. // resolver only contains routes that do not specify custom resolver(s), hence
  67. // all DNS queries can be safely sent to the upstream DNS resolver and the
  68. // node's DNS forwarder doesn't need to handle all DNS traffic.
  69. // This is for now (2024-06-06) an iOS-specific battery life optimization,
  70. // and this knob allows us to disable the optimization remotely if needed.
  71. DisableSplitDNSWhenNoCustomResolvers atomic.Bool
  72. // DisableLocalDNSOverrideViaNRPT indicates that the node's DNS manager should not
  73. // create a default (catch-all) Windows NRPT rule when "Override local DNS" is enabled.
  74. // Without this rule, Windows 8.1 and newer devices issue parallel DNS requests to DNS servers
  75. // associated with all network adapters, even when "Override local DNS" is enabled and/or
  76. // a Mullvad exit node is being used, resulting in DNS leaks.
  77. // We began creating this rule on 2024-06-14, and this knob
  78. // allows us to disable the new behavior remotely if needed.
  79. DisableLocalDNSOverrideViaNRPT atomic.Bool
  80. // DisableCryptorouting indicates that the node should not use the
  81. // magicsock crypto routing feature.
  82. DisableCryptorouting atomic.Bool
  83. // DisableCaptivePortalDetection is whether the node should not perform captive portal detection
  84. // automatically when the network state changes.
  85. DisableCaptivePortalDetection atomic.Bool
  86. // DisableSkipStatusQueue is whether the node should disable skipping
  87. // of queued netmap.NetworkMap between the controlclient and LocalBackend.
  88. // See tailscale/tailscale#14768.
  89. DisableSkipStatusQueue atomic.Bool
  90. }
  91. // UpdateFromNodeAttributes updates k (if non-nil) based on the provided self
  92. // node attributes (Node.Capabilities).
  93. func (k *Knobs) UpdateFromNodeAttributes(capMap tailcfg.NodeCapMap) {
  94. if k == nil {
  95. return
  96. }
  97. has := capMap.Contains
  98. var (
  99. keepFullWG = has(tailcfg.NodeAttrDebugDisableWGTrim)
  100. disableUPnP = has(tailcfg.NodeAttrDisableUPnP)
  101. randomizeClientPort = has(tailcfg.NodeAttrRandomizeClientPort)
  102. disableDeltaUpdates = has(tailcfg.NodeAttrDisableDeltaUpdates)
  103. oneCGNAT opt.Bool
  104. forceBackgroundSTUN = has(tailcfg.NodeAttrDebugForceBackgroundSTUN)
  105. peerMTUEnable = has(tailcfg.NodeAttrPeerMTUEnable)
  106. dnsForwarderDisableTCPRetries = has(tailcfg.NodeAttrDNSForwarderDisableTCPRetries)
  107. silentDisco = has(tailcfg.NodeAttrSilentDisco)
  108. forceIPTables = has(tailcfg.NodeAttrLinuxMustUseIPTables)
  109. forceNfTables = has(tailcfg.NodeAttrLinuxMustUseNfTables)
  110. seamlessKeyRenewal = has(tailcfg.NodeAttrSeamlessKeyRenewal)
  111. probeUDPLifetime = has(tailcfg.NodeAttrProbeUDPLifetime)
  112. appCStoreRoutes = has(tailcfg.NodeAttrStoreAppCRoutes)
  113. userDialUseRoutes = has(tailcfg.NodeAttrUserDialUseRoutes)
  114. disableSplitDNSWhenNoCustomResolvers = has(tailcfg.NodeAttrDisableSplitDNSWhenNoCustomResolvers)
  115. disableLocalDNSOverrideViaNRPT = has(tailcfg.NodeAttrDisableLocalDNSOverrideViaNRPT)
  116. disableCryptorouting = has(tailcfg.NodeAttrDisableMagicSockCryptoRouting)
  117. disableCaptivePortalDetection = has(tailcfg.NodeAttrDisableCaptivePortalDetection)
  118. disableSkipStatusQueue = has(tailcfg.NodeAttrDisableSkipStatusQueue)
  119. )
  120. if has(tailcfg.NodeAttrOneCGNATEnable) {
  121. oneCGNAT.Set(true)
  122. } else if has(tailcfg.NodeAttrOneCGNATDisable) {
  123. oneCGNAT.Set(false)
  124. }
  125. k.KeepFullWGConfig.Store(keepFullWG)
  126. k.DisableUPnP.Store(disableUPnP)
  127. k.RandomizeClientPort.Store(randomizeClientPort)
  128. k.OneCGNAT.Store(oneCGNAT)
  129. k.ForceBackgroundSTUN.Store(forceBackgroundSTUN)
  130. k.DisableDeltaUpdates.Store(disableDeltaUpdates)
  131. k.PeerMTUEnable.Store(peerMTUEnable)
  132. k.DisableDNSForwarderTCPRetries.Store(dnsForwarderDisableTCPRetries)
  133. k.SilentDisco.Store(silentDisco)
  134. k.LinuxForceIPTables.Store(forceIPTables)
  135. k.LinuxForceNfTables.Store(forceNfTables)
  136. k.SeamlessKeyRenewal.Store(seamlessKeyRenewal)
  137. k.ProbeUDPLifetime.Store(probeUDPLifetime)
  138. k.AppCStoreRoutes.Store(appCStoreRoutes)
  139. k.UserDialUseRoutes.Store(userDialUseRoutes)
  140. k.DisableSplitDNSWhenNoCustomResolvers.Store(disableSplitDNSWhenNoCustomResolvers)
  141. k.DisableLocalDNSOverrideViaNRPT.Store(disableLocalDNSOverrideViaNRPT)
  142. k.DisableCryptorouting.Store(disableCryptorouting)
  143. k.DisableCaptivePortalDetection.Store(disableCaptivePortalDetection)
  144. k.DisableSkipStatusQueue.Store(disableSkipStatusQueue)
  145. }
  146. // AsDebugJSON returns k as something that can be marshalled with json.Marshal
  147. // for debug.
  148. func (k *Knobs) AsDebugJSON() map[string]any {
  149. if k == nil {
  150. return nil
  151. }
  152. ret := map[string]any{}
  153. rt := reflect.TypeFor[Knobs]()
  154. rv := reflect.ValueOf(k).Elem() // of *k
  155. for i := 0; i < rt.NumField(); i++ {
  156. name := rt.Field(i).Name
  157. switch v := rv.Field(i).Addr().Interface().(type) {
  158. case *atomic.Bool:
  159. ret[name] = v.Load()
  160. case *syncs.AtomicValue[opt.Bool]:
  161. ret[name] = v.Load()
  162. default:
  163. panic(fmt.Sprintf("unknown field type %T for %v", v, name))
  164. }
  165. }
  166. return ret
  167. }