health, all: remove health.Global, finish plumbing health.Tracker

Updates #11874
Updates #4136

Change-Id: I414470f71d90be9889d44c3afd53956d9f26cd61
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/11884/head
Brad Fitzpatrick 6 months ago
parent a4a282cd49
commit bf24e727c8

@ -358,7 +358,7 @@ func run() (err error) {
sys.Set(netMon) sys.Set(netMon)
} }
pol := logpolicy.New(logtail.CollectionNode, netMon, nil /* use log.Printf */) pol := logpolicy.New(logtail.CollectionNode, netMon, sys.HealthTracker(), nil /* use log.Printf */)
pol.SetVerbosityLevel(args.verbose) pol.SetVerbosityLevel(args.verbose)
logPol = pol logPol = pol
defer func() { defer func() {
@ -677,7 +677,7 @@ func tryEngine(logf logger.Logf, sys *tsd.System, name string) (onlyNetstack boo
// configuration being unavailable (from the noop // configuration being unavailable (from the noop
// manager). More in Issue 4017. // manager). More in Issue 4017.
// TODO(bradfitz): add a Synology-specific DNS manager. // TODO(bradfitz): add a Synology-specific DNS manager.
conf.DNS, err = dns.NewOSConfigurator(logf, "") // empty interface name conf.DNS, err = dns.NewOSConfigurator(logf, sys.HealthTracker(), "") // empty interface name
if err != nil { if err != nil {
return false, fmt.Errorf("dns.NewOSConfigurator: %w", err) return false, fmt.Errorf("dns.NewOSConfigurator: %w", err)
} }
@ -699,13 +699,13 @@ func tryEngine(logf logger.Logf, sys *tsd.System, name string) (onlyNetstack boo
return false, err return false, err
} }
r, err := router.New(logf, dev, sys.NetMon.Get()) r, err := router.New(logf, dev, sys.NetMon.Get(), sys.HealthTracker())
if err != nil { if err != nil {
dev.Close() dev.Close()
return false, fmt.Errorf("creating router: %w", err) return false, fmt.Errorf("creating router: %w", err)
} }
d, err := dns.NewOSConfigurator(logf, devName) d, err := dns.NewOSConfigurator(logf, sys.HealthTracker(), devName)
if err != nil { if err != nil {
dev.Close() dev.Close()
r.Close() r.Close()

@ -107,6 +107,7 @@ func newIPN(jsConfig js.Value) map[string]any {
Dialer: dialer, Dialer: dialer,
SetSubsystem: sys.Set, SetSubsystem: sys.Set,
ControlKnobs: sys.ControlKnobs(), ControlKnobs: sys.ControlKnobs(),
HealthTracker: sys.HealthTracker(),
}) })
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

@ -1460,6 +1460,7 @@ func (c *Direct) getNoiseClient() (*NoiseClient, error) {
DNSCache: c.dnsCache, DNSCache: c.dnsCache,
Logf: c.logf, Logf: c.logf,
NetMon: c.netMon, NetMon: c.netMon,
HealthTracker: c.health,
DialPlan: dp, DialPlan: dp,
}) })
if err != nil { if err != nil {

@ -19,6 +19,7 @@ import (
"golang.org/x/net/http2" "golang.org/x/net/http2"
"tailscale.com/control/controlbase" "tailscale.com/control/controlbase"
"tailscale.com/control/controlhttp" "tailscale.com/control/controlhttp"
"tailscale.com/health"
"tailscale.com/net/dnscache" "tailscale.com/net/dnscache"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/net/tsdial" "tailscale.com/net/tsdial"
@ -174,6 +175,7 @@ type NoiseClient struct {
logf logger.Logf logf logger.Logf
netMon *netmon.Monitor netMon *netmon.Monitor
health *health.Tracker
// mu only protects the following variables. // mu only protects the following variables.
mu sync.Mutex mu sync.Mutex
@ -204,6 +206,8 @@ type NoiseOpts struct {
// network interface state. This field can be nil; if so, the current // network interface state. This field can be nil; if so, the current
// state will be looked up dynamically. // state will be looked up dynamically.
NetMon *netmon.Monitor NetMon *netmon.Monitor
// HealthTracker, if non-nil, is the health tracker to use.
HealthTracker *health.Tracker
// DialPlan, if set, is a function that should return an explicit plan // DialPlan, if set, is a function that should return an explicit plan
// on how to connect to the server. // on how to connect to the server.
DialPlan func() *tailcfg.ControlDialPlan DialPlan func() *tailcfg.ControlDialPlan
@ -247,6 +251,7 @@ func NewNoiseClient(opts NoiseOpts) (*NoiseClient, error) {
dialPlan: opts.DialPlan, dialPlan: opts.DialPlan,
logf: opts.Logf, logf: opts.Logf,
netMon: opts.NetMon, netMon: opts.NetMon,
health: opts.HealthTracker,
} }
// Create the HTTP/2 Transport using a net/http.Transport // Create the HTTP/2 Transport using a net/http.Transport
@ -453,6 +458,7 @@ func (nc *NoiseClient) dial(ctx context.Context) (*noiseConn, error) {
DialPlan: dialPlan, DialPlan: dialPlan,
Logf: nc.logf, Logf: nc.logf,
NetMon: nc.netMon, NetMon: nc.netMon,
HealthTracker: nc.health,
Clock: tstime.StdClock{}, Clock: tstime.StdClock{},
}).Dial(ctx) }).Dial(ctx)
if err != nil { if err != nil {

@ -38,7 +38,6 @@ import (
"tailscale.com/control/controlbase" "tailscale.com/control/controlbase"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/health"
"tailscale.com/net/dnscache" "tailscale.com/net/dnscache"
"tailscale.com/net/dnsfallback" "tailscale.com/net/dnsfallback"
"tailscale.com/net/netutil" "tailscale.com/net/netutil"
@ -434,7 +433,7 @@ func (a *Dialer) tryURLUpgrade(ctx context.Context, u *url.URL, addr netip.Addr,
// Disable HTTP2, since h2 can't do protocol switching. // Disable HTTP2, since h2 can't do protocol switching.
tr.TLSClientConfig.NextProtos = []string{} tr.TLSClientConfig.NextProtos = []string{}
tr.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{} tr.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{}
tr.TLSClientConfig = tlsdial.Config(a.Hostname, health.Global, tr.TLSClientConfig) tr.TLSClientConfig = tlsdial.Config(a.Hostname, a.HealthTracker, tr.TLSClientConfig)
if !tr.TLSClientConfig.InsecureSkipVerify { if !tr.TLSClientConfig.InsecureSkipVerify {
panic("unexpected") // should be set by tlsdial.Config panic("unexpected") // should be set by tlsdial.Config
} }

@ -8,6 +8,7 @@ import (
"net/url" "net/url"
"time" "time"
"tailscale.com/health"
"tailscale.com/net/dnscache" "tailscale.com/net/dnscache"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
@ -79,6 +80,9 @@ type Dialer struct {
NetMon *netmon.Monitor NetMon *netmon.Monitor
// HealthTracker, if non-nil, is the health tracker to use.
HealthTracker *health.Tracker
// DialPlan, if set, contains instructions from the control server on // DialPlan, if set, contains instructions from the control server on
// how to connect to it. If present, we will try the methods in this // how to connect to it. If present, we will try the methods in this
// plan before falling back to DNS. // plan before falling back to DNS.

@ -30,15 +30,9 @@ var (
debugHandler map[string]http.Handler debugHandler map[string]http.Handler
) )
// Global is a global health tracker for the process. // Tracker tracks the health of various Tailscale subsystems,
// // comparing each subsystems' state with each other to make sure
// TODO(bradfitz): finish moving all reference to this plumb it (ultimately out // they're consistent based on the user's intended state.
// from tsd.System) so a process can have multiple tsnet/etc instances with
// their own health trackers. But for now (2024-04-25), the tsd.System value
// given out is just this one, until that's the only remaining Global reference
// remaining.
var Global = new(Tracker)
type Tracker struct { type Tracker struct {
// mu guards everything in this var block. // mu guards everything in this var block.
mu sync.Mutex mu sync.Mutex

@ -415,7 +415,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
} }
netMon := sys.NetMon.Get() netMon := sys.NetMon.Get()
b.sockstatLogger, err = sockstatlog.NewLogger(logpolicy.LogsDir(logf), logf, logID, netMon) b.sockstatLogger, err = sockstatlog.NewLogger(logpolicy.LogsDir(logf), logf, logID, netMon, sys.HealthTracker())
if err != nil { if err != nil {
log.Printf("error setting up sockstat logger: %v", err) log.Printf("error setting up sockstat logger: %v", err)
} }

@ -17,6 +17,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"tailscale.com/health"
"tailscale.com/logpolicy" "tailscale.com/logpolicy"
"tailscale.com/logtail" "tailscale.com/logtail"
"tailscale.com/logtail/filch" "tailscale.com/logtail/filch"
@ -93,7 +94,7 @@ func SockstatLogID(logID logid.PublicID) logid.PrivateID {
// The returned Logger is not yet enabled, and must be shut down with Shutdown when it is no longer needed. // The returned Logger is not yet enabled, and must be shut down with Shutdown when it is no longer needed.
// Logs will be uploaded to the log server using a new log ID derived from the provided backend logID. // Logs will be uploaded to the log server using a new log ID derived from the provided backend logID.
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups. // The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func NewLogger(logdir string, logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) (*Logger, error) { func NewLogger(logdir string, logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor, health *health.Tracker) (*Logger, error) {
if !sockstats.IsAvailable { if !sockstats.IsAvailable {
return nil, nil return nil, nil
} }
@ -113,7 +114,7 @@ func NewLogger(logdir string, logf logger.Logf, logID logid.PublicID, netMon *ne
logger := &Logger{ logger := &Logger{
logf: logf, logf: logf,
filch: filch, filch: filch,
tr: logpolicy.NewLogtailTransport(logtail.DefaultHost, netMon, logf), tr: logpolicy.NewLogtailTransport(logtail.DefaultHost, netMon, health, logf),
} }
logger.logger = logtail.NewLogger(logtail.Config{ logger.logger = logtail.NewLogger(logtail.Config{
BaseURL: logpolicy.LogURL(), BaseURL: logpolicy.LogURL(),

@ -23,7 +23,7 @@ func TestResourceCleanup(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
lg, err := NewLogger(td, logger.Discard, id.Public(), nil) lg, err := NewLogger(td, logger.Discard, id.Public(), nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -453,13 +453,13 @@ func tryFixLogStateLocation(dir, cmdname string, logf logger.Logf) {
// The logf parameter is optional; if non-nil, information logs (e.g. when // The logf parameter is optional; if non-nil, information logs (e.g. when
// migrating state) are sent to that logger, and global changes to the log // migrating state) are sent to that logger, and global changes to the log
// package are avoided. If nil, logs will be printed using log.Printf. // package are avoided. If nil, logs will be printed using log.Printf.
func New(collection string, netMon *netmon.Monitor, logf logger.Logf) *Policy { func New(collection string, netMon *netmon.Monitor, health *health.Tracker, logf logger.Logf) *Policy {
return NewWithConfigPath(collection, "", "", netMon, logf) return NewWithConfigPath(collection, "", "", netMon, health, logf)
} }
// NewWithConfigPath is identical to New, but uses the specified directory and // NewWithConfigPath is identical to New, but uses the specified directory and
// command name. If either is empty, it derives them automatically. // command name. If either is empty, it derives them automatically.
func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor, logf logger.Logf) *Policy { func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor, health *health.Tracker, logf logger.Logf) *Policy {
var lflags int var lflags int
if term.IsTerminal(2) || runtime.GOOS == "windows" { if term.IsTerminal(2) || runtime.GOOS == "windows" {
lflags = 0 lflags = 0
@ -555,7 +555,7 @@ func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor,
PrivateID: newc.PrivateID, PrivateID: newc.PrivateID,
Stderr: logWriter{console}, Stderr: logWriter{console},
CompressLogs: true, CompressLogs: true,
HTTPC: &http.Client{Transport: NewLogtailTransport(logtail.DefaultHost, netMon, logf)}, HTTPC: &http.Client{Transport: NewLogtailTransport(logtail.DefaultHost, netMon, health, logf)},
} }
if collection == logtail.CollectionNode { if collection == logtail.CollectionNode {
conf.MetricsDelta = clientmetric.EncodeLogTailMetricsDelta conf.MetricsDelta = clientmetric.EncodeLogTailMetricsDelta
@ -570,7 +570,7 @@ func NewWithConfigPath(collection, dir, cmdName string, netMon *netmon.Monitor,
logf("You have enabled a non-default log target. Doing without being told to by Tailscale staff or your network administrator will make getting support difficult.") logf("You have enabled a non-default log target. Doing without being told to by Tailscale staff or your network administrator will make getting support difficult.")
conf.BaseURL = val conf.BaseURL = val
u, _ := url.Parse(val) u, _ := url.Parse(val)
conf.HTTPC = &http.Client{Transport: NewLogtailTransport(u.Host, netMon, logf)} conf.HTTPC = &http.Client{Transport: NewLogtailTransport(u.Host, netMon, health, logf)}
} }
filchOptions := filch.Options{ filchOptions := filch.Options{
@ -742,7 +742,7 @@ func dialContext(ctx context.Context, netw, addr string, netMon *netmon.Monitor,
// //
// The logf parameter is optional; if non-nil, logs are printed using the // The logf parameter is optional; if non-nil, logs are printed using the
// provided function; if nil, log.Printf will be used instead. // provided function; if nil, log.Printf will be used instead.
func NewLogtailTransport(host string, netMon *netmon.Monitor, logf logger.Logf) http.RoundTripper { func NewLogtailTransport(host string, netMon *netmon.Monitor, health *health.Tracker, logf logger.Logf) http.RoundTripper {
if testenv.InTest() { if testenv.InTest() {
return noopPretendSuccessTransport{} return noopPretendSuccessTransport{}
} }
@ -783,7 +783,7 @@ func NewLogtailTransport(host string, netMon *netmon.Monitor, logf logger.Logf)
tr.TLSNextProto = map[string]func(authority string, c *tls.Conn) http.RoundTripper{} tr.TLSNextProto = map[string]func(authority string, c *tls.Conn) http.RoundTripper{}
} }
tr.TLSClientConfig = tlsdial.Config(host, health.Global, tr.TLSClientConfig) tr.TLSClientConfig = tlsdial.Config(host, health, tr.TLSClientConfig)
return tr return tr
} }

@ -21,6 +21,7 @@ import (
"sync" "sync"
"time" "time"
"tailscale.com/health"
"tailscale.com/net/dns/resolvconffile" "tailscale.com/net/dns/resolvconffile"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/types/logger" "tailscale.com/types/logger"
@ -117,6 +118,7 @@ func restartResolved() error {
// or as cleanup if the program terminates unexpectedly. // or as cleanup if the program terminates unexpectedly.
type directManager struct { type directManager struct {
logf logger.Logf logf logger.Logf
health *health.Tracker
fs wholeFileFS fs wholeFileFS
// renameBroken is set if fs.Rename to or from /etc/resolv.conf // renameBroken is set if fs.Rename to or from /etc/resolv.conf
// fails. This can happen in some container runtimes, where // fails. This can happen in some container runtimes, where
@ -140,14 +142,15 @@ type directManager struct {
} }
//lint:ignore U1000 used in manager_{freebsd,openbsd}.go //lint:ignore U1000 used in manager_{freebsd,openbsd}.go
func newDirectManager(logf logger.Logf) *directManager { func newDirectManager(logf logger.Logf, health *health.Tracker) *directManager {
return newDirectManagerOnFS(logf, directFS{}) return newDirectManagerOnFS(logf, health, directFS{})
} }
func newDirectManagerOnFS(logf logger.Logf, fs wholeFileFS) *directManager { func newDirectManagerOnFS(logf logger.Logf, health *health.Tracker, fs wholeFileFS) *directManager {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
m := &directManager{ m := &directManager{
logf: logf, logf: logf,
health: health,
fs: fs, fs: fs,
ctx: ctx, ctx: ctx,
ctxClose: cancel, ctxClose: cancel,

@ -78,7 +78,7 @@ func (m *directManager) checkForFileTrample() {
return return
} }
if bytes.Equal(cur, want) { if bytes.Equal(cur, want) {
health.Global.SetWarnable(warnTrample, nil) m.health.SetWarnable(warnTrample, nil)
if lastWarn != nil { if lastWarn != nil {
m.mu.Lock() m.mu.Lock()
m.lastWarnContents = nil m.lastWarnContents = nil
@ -101,7 +101,7 @@ func (m *directManager) checkForFileTrample() {
show = show[:1024] show = show[:1024]
} }
m.logf("trample: resolv.conf changed from what we expected. did some other program interfere? current contents: %q", show) m.logf("trample: resolv.conf changed from what we expected. did some other program interfere? current contents: %q", show)
health.Global.SetWarnable(warnTrample, errors.New("Linux DNS config not ideal. /etc/resolv.conf overwritten. See https://tailscale.com/s/dns-fight")) m.health.SetWarnable(warnTrample, errors.New("Linux DNS config not ideal. /etc/resolv.conf overwritten. See https://tailscale.com/s/dns-fight"))
} }
func (m *directManager) closeInotifyOnDone(ctx context.Context, in *gonotify.Inotify) { func (m *directManager) closeInotifyOnDone(ctx context.Context, in *gonotify.Inotify) {

@ -43,6 +43,7 @@ const maxActiveQueries = 256
// Manager manages system DNS settings. // Manager manages system DNS settings.
type Manager struct { type Manager struct {
logf logger.Logf logf logger.Logf
health *health.Tracker
activeQueriesAtomic int32 activeQueriesAtomic int32
@ -55,7 +56,7 @@ type Manager struct {
// NewManagers created a new manager from the given config. // NewManagers created a new manager from the given config.
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups. // The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func NewManager(logf logger.Logf, oscfg OSConfigurator, netMon *netmon.Monitor, dialer *tsdial.Dialer, linkSel resolver.ForwardLinkSelector, knobs *controlknobs.Knobs) *Manager { func NewManager(logf logger.Logf, oscfg OSConfigurator, netMon *netmon.Monitor, health *health.Tracker, dialer *tsdial.Dialer, linkSel resolver.ForwardLinkSelector, knobs *controlknobs.Knobs) *Manager {
if dialer == nil { if dialer == nil {
panic("nil Dialer") panic("nil Dialer")
} }
@ -64,6 +65,7 @@ func NewManager(logf logger.Logf, oscfg OSConfigurator, netMon *netmon.Monitor,
logf: logf, logf: logf,
resolver: resolver.New(logf, netMon, linkSel, dialer, knobs), resolver: resolver.New(logf, netMon, linkSel, dialer, knobs),
os: oscfg, os: oscfg,
health: health,
} }
m.ctx, m.ctxCancel = context.WithCancel(context.Background()) m.ctx, m.ctxCancel = context.WithCancel(context.Background())
m.logf("using %T", m.os) m.logf("using %T", m.os)
@ -94,10 +96,10 @@ func (m *Manager) Set(cfg Config) error {
return err return err
} }
if err := m.os.SetDNS(ocfg); err != nil { if err := m.os.SetDNS(ocfg); err != nil {
health.Global.SetDNSOSHealth(err) m.health.SetDNSOSHealth(err)
return err return err
} }
health.Global.SetDNSOSHealth(nil) m.health.SetDNSOSHealth(nil)
return nil return nil
} }
@ -248,7 +250,7 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
// This is currently (2022-10-13) expected on certain iOS and macOS // This is currently (2022-10-13) expected on certain iOS and macOS
// builds. // builds.
} else { } else {
health.Global.SetDNSOSHealth(err) m.health.SetDNSOSHealth(err)
return resolver.Config{}, OSConfig{}, err return resolver.Config{}, OSConfig{}, err
} }
} }
@ -453,12 +455,12 @@ func (m *Manager) FlushCaches() error {
// in case the Tailscale daemon terminated without closing the router. // in case the Tailscale daemon terminated without closing the router.
// No other state needs to be instantiated before this runs. // No other state needs to be instantiated before this runs.
func CleanUp(logf logger.Logf, interfaceName string) { func CleanUp(logf logger.Logf, interfaceName string) {
oscfg, err := NewOSConfigurator(logf, interfaceName) oscfg, err := NewOSConfigurator(logf, nil, interfaceName)
if err != nil { if err != nil {
logf("creating dns cleanup: %v", err) logf("creating dns cleanup: %v", err)
return return
} }
dns := NewManager(logf, oscfg, nil, &tsdial.Dialer{Logf: logf}, nil, nil) dns := NewManager(logf, oscfg, nil, nil, &tsdial.Dialer{Logf: logf}, nil, nil)
if err := dns.Down(); err != nil { if err := dns.Down(); err != nil {
logf("dns down: %v", err) logf("dns down: %v", err)
} }

@ -8,11 +8,12 @@ import (
"os" "os"
"go4.org/mem" "go4.org/mem"
"tailscale.com/health"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/util/mak" "tailscale.com/util/mak"
) )
func NewOSConfigurator(logf logger.Logf, ifName string) (OSConfigurator, error) { func NewOSConfigurator(logf logger.Logf, health *health.Tracker, ifName string) (OSConfigurator, error) {
return &darwinConfigurator{logf: logf, ifName: ifName}, nil return &darwinConfigurator{logf: logf, ifName: ifName}, nil
} }

@ -5,11 +5,11 @@
package dns package dns
import "tailscale.com/types/logger" import (
"tailscale.com/health"
"tailscale.com/types/logger"
)
func NewOSConfigurator(logger.Logf, string) (OSConfigurator, error) { func NewOSConfigurator(logger.Logf, *health.Tracker, string) (OSConfigurator, error) {
// TODO(dmytro): on darwin, we should use a macOS-specific method such as scutil.
// This is currently not implemented. Editing /etc/resolv.conf does not work,
// as most applications use the system resolver, which disregards it.
return NewNoopManager() return NewNoopManager()
} }

@ -7,13 +7,14 @@ import (
"fmt" "fmt"
"os" "os"
"tailscale.com/health"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
func NewOSConfigurator(logf logger.Logf, _ string) (OSConfigurator, error) { func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ string) (OSConfigurator, error) {
bs, err := os.ReadFile("/etc/resolv.conf") bs, err := os.ReadFile("/etc/resolv.conf")
if os.IsNotExist(err) { if os.IsNotExist(err) {
return newDirectManager(logf), nil return newDirectManager(logf, health), nil
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("reading /etc/resolv.conf: %w", err) return nil, fmt.Errorf("reading /etc/resolv.conf: %w", err)
@ -23,16 +24,16 @@ func NewOSConfigurator(logf logger.Logf, _ string) (OSConfigurator, error) {
case "resolvconf": case "resolvconf":
switch resolvconfStyle() { switch resolvconfStyle() {
case "": case "":
return newDirectManager(logf), nil return newDirectManager(logf, health), nil
case "debian": case "debian":
return newDebianResolvconfManager(logf) return newDebianResolvconfManager(logf)
case "openresolv": case "openresolv":
return newOpenresolvManager(logf) return newOpenresolvManager(logf)
default: default:
logf("[unexpected] got unknown flavor of resolvconf %q, falling back to direct manager", resolvconfStyle()) logf("[unexpected] got unknown flavor of resolvconf %q, falling back to direct manager", resolvconfStyle())
return newDirectManager(logf), nil return newDirectManager(logf, health), nil
} }
default: default:
return newDirectManager(logf), nil return newDirectManager(logf, health), nil
} }
} }

@ -31,7 +31,7 @@ func (kv kv) String() string {
var publishOnce sync.Once var publishOnce sync.Once
func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurator, err error) { func NewOSConfigurator(logf logger.Logf, health *health.Tracker, interfaceName string) (ret OSConfigurator, err error) {
env := newOSConfigEnv{ env := newOSConfigEnv{
fs: directFS{}, fs: directFS{},
dbusPing: dbusPing, dbusPing: dbusPing,
@ -40,7 +40,7 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat
nmVersionBetween: nmVersionBetween, nmVersionBetween: nmVersionBetween,
resolvconfStyle: resolvconfStyle, resolvconfStyle: resolvconfStyle,
} }
mode, err := dnsMode(logf, env) mode, err := dnsMode(logf, health, env)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -52,9 +52,9 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat
logf("dns: using %q mode", mode) logf("dns: using %q mode", mode)
switch mode { switch mode {
case "direct": case "direct":
return newDirectManagerOnFS(logf, env.fs), nil return newDirectManagerOnFS(logf, health, env.fs), nil
case "systemd-resolved": case "systemd-resolved":
return newResolvedManager(logf, interfaceName) return newResolvedManager(logf, health, interfaceName)
case "network-manager": case "network-manager":
return newNMManager(interfaceName) return newNMManager(interfaceName)
case "debian-resolvconf": case "debian-resolvconf":
@ -63,7 +63,7 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat
return newOpenresolvManager(logf) return newOpenresolvManager(logf)
default: default:
logf("[unexpected] detected unknown DNS mode %q, using direct manager as last resort", mode) logf("[unexpected] detected unknown DNS mode %q, using direct manager as last resort", mode)
return newDirectManagerOnFS(logf, env.fs), nil return newDirectManagerOnFS(logf, health, env.fs), nil
} }
} }
@ -77,7 +77,7 @@ type newOSConfigEnv struct {
resolvconfStyle func() string resolvconfStyle func() string
} }
func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) { func dnsMode(logf logger.Logf, health *health.Tracker, env newOSConfigEnv) (ret string, err error) {
var debug []kv var debug []kv
dbg := func(k, v string) { dbg := func(k, v string) {
debug = append(debug, kv{k, v}) debug = append(debug, kv{k, v})
@ -271,7 +271,7 @@ func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
return "direct", nil return "direct", nil
} }
health.Global.SetDNSManagerHealth(errors.New("systemd-resolved and NetworkManager are wired together incorrectly; MagicDNS will probably not work. For more info, see https://tailscale.com/s/resolved-nm")) health.SetDNSManagerHealth(errors.New("systemd-resolved and NetworkManager are wired together incorrectly; MagicDNS will probably not work. For more info, see https://tailscale.com/s/resolved-nm"))
dbg("nm-safe", "no") dbg("nm-safe", "no")
return "systemd-resolved", nil return "systemd-resolved", nil
default: default:

@ -286,7 +286,7 @@ func TestLinuxDNSMode(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
var logBuf tstest.MemLogger var logBuf tstest.MemLogger
got, err := dnsMode(logBuf.Logf, tt.env) got, err := dnsMode(logBuf.Logf, nil, tt.env)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"os" "os"
"tailscale.com/health"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
@ -19,8 +20,8 @@ func (kv kv) String() string {
return fmt.Sprintf("%s=%s", kv.k, kv.v) return fmt.Sprintf("%s=%s", kv.k, kv.v)
} }
func NewOSConfigurator(logf logger.Logf, interfaceName string) (OSConfigurator, error) { func NewOSConfigurator(logf logger.Logf, health *health.Tracker, interfaceName string) (OSConfigurator, error) {
return newOSConfigurator(logf, interfaceName, return newOSConfigurator(logf, health, interfaceName,
newOSConfigEnv{ newOSConfigEnv{
rcIsResolvd: rcIsResolvd, rcIsResolvd: rcIsResolvd,
fs: directFS{}, fs: directFS{},
@ -33,7 +34,7 @@ type newOSConfigEnv struct {
rcIsResolvd func(resolvConfContents []byte) bool rcIsResolvd func(resolvConfContents []byte) bool
} }
func newOSConfigurator(logf logger.Logf, interfaceName string, env newOSConfigEnv) (ret OSConfigurator, err error) { func newOSConfigurator(logf logger.Logf, health *health.Tracker, interfaceName string, env newOSConfigEnv) (ret OSConfigurator, err error) {
var debug []kv var debug []kv
dbg := func(k, v string) { dbg := func(k, v string) {
debug = append(debug, kv{k, v}) debug = append(debug, kv{k, v})
@ -48,7 +49,7 @@ func newOSConfigurator(logf logger.Logf, interfaceName string, env newOSConfigEn
bs, err := env.fs.ReadFile(resolvConf) bs, err := env.fs.ReadFile(resolvConf)
if os.IsNotExist(err) { if os.IsNotExist(err) {
dbg("rc", "missing") dbg("rc", "missing")
return newDirectManager(logf), nil return newDirectManager(logf, health), nil
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("reading /etc/resolv.conf: %w", err) return nil, fmt.Errorf("reading /etc/resolv.conf: %w", err)
@ -60,7 +61,7 @@ func newOSConfigurator(logf logger.Logf, interfaceName string, env newOSConfigEn
} }
dbg("resolvd", "missing") dbg("resolvd", "missing")
return newDirectManager(logf), nil return newDirectManager(logf, health), nil
} }
func rcIsResolvd(resolvConfContents []byte) bool { func rcIsResolvd(resolvConfContents []byte) bool {

@ -87,7 +87,7 @@ func TestDNSOverTCP(t *testing.T) {
SearchDomains: fqdns("coffee.shop"), SearchDomains: fqdns("coffee.shop"),
}, },
} }
m := NewManager(t.Logf, &f, nil, new(tsdial.Dialer), nil, nil) m := NewManager(t.Logf, &f, nil, nil, new(tsdial.Dialer), nil, nil)
m.resolver.TestOnlySetHook(f.SetResolver) m.resolver.TestOnlySetHook(f.SetResolver)
m.Set(Config{ m.Set(Config{
Hosts: hosts( Hosts: hosts(
@ -172,7 +172,7 @@ func TestDNSOverTCP_TooLarge(t *testing.T) {
SearchDomains: fqdns("coffee.shop"), SearchDomains: fqdns("coffee.shop"),
}, },
} }
m := NewManager(log, &f, nil, new(tsdial.Dialer), nil, nil) m := NewManager(log, &f, nil, nil, new(tsdial.Dialer), nil, nil)
m.resolver.TestOnlySetHook(f.SetResolver) m.resolver.TestOnlySetHook(f.SetResolver)
m.Set(Config{ m.Set(Config{
Hosts: hosts("andrew.ts.com.", "1.2.3.4"), Hosts: hosts("andrew.ts.com.", "1.2.3.4"),

@ -613,7 +613,7 @@ func TestManager(t *testing.T) {
SplitDNS: test.split, SplitDNS: test.split,
BaseConfig: test.bs, BaseConfig: test.bs,
} }
m := NewManager(t.Logf, &f, nil, new(tsdial.Dialer), nil, nil) m := NewManager(t.Logf, &f, nil, nil, new(tsdial.Dialer), nil, nil)
m.resolver.TestOnlySetHook(f.SetResolver) m.resolver.TestOnlySetHook(f.SetResolver)
if err := m.Set(test.in); err != nil { if err := m.Set(test.in); err != nil {

@ -23,6 +23,7 @@ import (
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"tailscale.com/atomicfile" "tailscale.com/atomicfile"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/health"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/util/dnsname" "tailscale.com/util/dnsname"
"tailscale.com/util/winutil" "tailscale.com/util/winutil"
@ -44,11 +45,11 @@ type windowsManager struct {
closing bool closing bool
} }
func NewOSConfigurator(logf logger.Logf, interfaceName string) (OSConfigurator, error) { func NewOSConfigurator(logf logger.Logf, health *health.Tracker, interfaceName string) (OSConfigurator, error) {
ret := &windowsManager{ ret := &windowsManager{
logf: logf, logf: logf,
guid: interfaceName, guid: interfaceName,
wslManager: newWSLManager(logf), wslManager: newWSLManager(logf, health),
} }
if isWindows10OrBetter() { if isWindows10OrBetter() {

@ -84,7 +84,7 @@ func TestManagerWindowsGPCopy(t *testing.T) {
} }
defer delIfKey() defer delIfKey()
cfg, err := NewOSConfigurator(logf, fakeInterface.String()) cfg, err := NewOSConfigurator(logf, nil, fakeInterface.String())
if err != nil { if err != nil {
t.Fatalf("NewOSConfigurator: %v\n", err) t.Fatalf("NewOSConfigurator: %v\n", err)
} }
@ -213,7 +213,7 @@ func runTest(t *testing.T, isLocal bool) {
} }
defer delIfKey() defer delIfKey()
cfg, err := NewOSConfigurator(logf, fakeInterface.String()) cfg, err := NewOSConfigurator(logf, nil, fakeInterface.String())
if err != nil { if err != nil {
t.Fatalf("NewOSConfigurator: %v\n", err) t.Fatalf("NewOSConfigurator: %v\n", err)
} }

@ -64,12 +64,13 @@ type resolvedManager struct {
cancel func() // terminate the context, for close cancel func() // terminate the context, for close
logf logger.Logf logf logger.Logf
health *health.Tracker
ifidx int ifidx int
configCR chan changeRequest // tracks OSConfigs changes and error responses configCR chan changeRequest // tracks OSConfigs changes and error responses
} }
func newResolvedManager(logf logger.Logf, interfaceName string) (*resolvedManager, error) { func newResolvedManager(logf logger.Logf, health *health.Tracker, interfaceName string) (*resolvedManager, error) {
iface, err := net.InterfaceByName(interfaceName) iface, err := net.InterfaceByName(interfaceName)
if err != nil { if err != nil {
return nil, err return nil, err
@ -83,6 +84,7 @@ func newResolvedManager(logf logger.Logf, interfaceName string) (*resolvedManage
cancel: cancel, cancel: cancel,
logf: logf, logf: logf,
health: health,
ifidx: iface.Index, ifidx: iface.Index,
configCR: make(chan changeRequest), configCR: make(chan changeRequest),
@ -163,7 +165,7 @@ func (m *resolvedManager) run(ctx context.Context) {
// Reset backoff and SetNSOSHealth after successful on reconnect. // Reset backoff and SetNSOSHealth after successful on reconnect.
bo.BackOff(ctx, nil) bo.BackOff(ctx, nil)
health.Global.SetDNSOSHealth(nil) m.health.SetDNSOSHealth(nil)
return nil return nil
} }
@ -241,7 +243,7 @@ func (m *resolvedManager) run(ctx context.Context) {
// Set health while holding the lock, because this will // Set health while holding the lock, because this will
// graciously serialize the resync's health outcome with a // graciously serialize the resync's health outcome with a
// concurrent SetDNS call. // concurrent SetDNS call.
health.Global.SetDNSOSHealth(err) m.health.SetDNSOSHealth(err)
if err != nil { if err != nil {
m.logf("failed to configure systemd-resolved: %v", err) m.logf("failed to configure systemd-resolved: %v", err)
} }

@ -16,6 +16,7 @@ import (
"time" "time"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"tailscale.com/health"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/util/winutil" "tailscale.com/util/winutil"
) )
@ -55,11 +56,13 @@ func wslDistros() ([]string, error) {
// It configures /etc/wsl.conf and /etc/resolv.conf. // It configures /etc/wsl.conf and /etc/resolv.conf.
type wslManager struct { type wslManager struct {
logf logger.Logf logf logger.Logf
health *health.Tracker
} }
func newWSLManager(logf logger.Logf) *wslManager { func newWSLManager(logf logger.Logf, health *health.Tracker) *wslManager {
m := &wslManager{ m := &wslManager{
logf: logf, logf: logf,
health: health,
} }
return m return m
} }
@ -73,7 +76,7 @@ func (wm *wslManager) SetDNS(cfg OSConfig) error {
} }
managers := make(map[string]*directManager) managers := make(map[string]*directManager)
for _, distro := range distros { for _, distro := range distros {
managers[distro] = newDirectManagerOnFS(wm.logf, wslFS{ managers[distro] = newDirectManagerOnFS(wm.logf, wm.health, wslFS{
user: "root", user: "root",
distro: distro, distro: distro,
}) })

@ -139,14 +139,6 @@ func (s *System) ProxyMapper() *proxymap.Mapper {
// HealthTracker returns the system health tracker. // HealthTracker returns the system health tracker.
func (s *System) HealthTracker() *health.Tracker { func (s *System) HealthTracker() *health.Tracker {
// TODO(bradfitz): plumb the tsd.System.HealthTracker() value
// everywhere and then then remove this use of the global
// and remove health.Global entirely. But for now we keep
// the two in sync during plumbing.
const stillPlumbing = true
if stillPlumbing {
return health.Global
}
return &s.healthTracker return &s.healthTracker
} }

@ -31,6 +31,7 @@ import (
"tailscale.com/client/tailscale" "tailscale.com/client/tailscale"
"tailscale.com/control/controlclient" "tailscale.com/control/controlclient"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/health"
"tailscale.com/hostinfo" "tailscale.com/hostinfo"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal" "tailscale.com/ipn/ipnlocal"
@ -504,7 +505,8 @@ func (s *Server) start() (reterr error) {
return fmt.Errorf("%v is not a directory", s.rootPath) return fmt.Errorf("%v is not a directory", s.rootPath)
} }
if err := s.startLogger(&closePool); err != nil { sys := new(tsd.System)
if err := s.startLogger(&closePool, sys.HealthTracker()); err != nil {
return err return err
} }
@ -514,7 +516,6 @@ func (s *Server) start() (reterr error) {
} }
closePool.add(s.netMon) closePool.add(s.netMon)
sys := new(tsd.System)
s.dialer = &tsdial.Dialer{Logf: logf} // mutated below (before used) s.dialer = &tsdial.Dialer{Logf: logf} // mutated below (before used)
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{ eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
ListenPort: s.Port, ListenPort: s.Port,
@ -627,7 +628,7 @@ func (s *Server) start() (reterr error) {
return nil return nil
} }
func (s *Server) startLogger(closePool *closeOnErrorPool) error { func (s *Server) startLogger(closePool *closeOnErrorPool, health *health.Tracker) error {
if testenv.InTest() { if testenv.InTest() {
return nil return nil
} }
@ -658,7 +659,7 @@ func (s *Server) startLogger(closePool *closeOnErrorPool) error {
Stderr: io.Discard, // log everything to Buffer Stderr: io.Discard, // log everything to Buffer
Buffer: s.logbuffer, Buffer: s.logbuffer,
CompressLogs: true, CompressLogs: true,
HTTPC: &http.Client{Transport: logpolicy.NewLogtailTransport(logtail.DefaultHost, s.netMon, s.logf)}, HTTPC: &http.Client{Transport: logpolicy.NewLogtailTransport(logtail.DefaultHost, s.netMon, health, s.logf)},
MetricsDelta: clientmetric.EncodeLogTailMetricsDelta, MetricsDelta: clientmetric.EncodeLogTailMetricsDelta,
} }
s.logtail = logtail.NewLogger(c, s.logf) s.logtail = logtail.NewLogger(c, s.logf)

@ -16,6 +16,7 @@ import (
"sync" "sync"
"time" "time"
"tailscale.com/health"
"tailscale.com/logpolicy" "tailscale.com/logpolicy"
"tailscale.com/logtail" "tailscale.com/logtail"
"tailscale.com/net/connstats" "tailscale.com/net/connstats"
@ -92,7 +93,7 @@ var testClient *http.Client
// The IP protocol and source port are always zero. // The IP protocol and source port are always zero.
// The sock is used to populated the PhysicalTraffic field in Message. // The sock is used to populated the PhysicalTraffic field in Message.
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups. // The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
func (nl *Logger) Startup(nodeID tailcfg.StableNodeID, nodeLogID, domainLogID logid.PrivateID, tun, sock Device, netMon *netmon.Monitor) error { func (nl *Logger) Startup(nodeID tailcfg.StableNodeID, nodeLogID, domainLogID logid.PrivateID, tun, sock Device, netMon *netmon.Monitor, health *health.Tracker) error {
nl.mu.Lock() nl.mu.Lock()
defer nl.mu.Unlock() defer nl.mu.Unlock()
if nl.logger != nil { if nl.logger != nil {
@ -101,7 +102,7 @@ func (nl *Logger) Startup(nodeID tailcfg.StableNodeID, nodeLogID, domainLogID lo
// Startup a log stream to Tailscale's logging service. // Startup a log stream to Tailscale's logging service.
logf := log.Printf logf := log.Printf
httpc := &http.Client{Transport: logpolicy.NewLogtailTransport(logtail.DefaultHost, netMon, logf)} httpc := &http.Client{Transport: logpolicy.NewLogtailTransport(logtail.DefaultHost, netMon, health, logf)}
if testClient != nil { if testClient != nil {
httpc = testClient httpc = testClient
} }

@ -237,7 +237,7 @@ func interfaceFromLUID(luid winipcfg.LUID, flags winipcfg.GAAFlags) (*winipcfg.I
var networkCategoryWarning = health.NewWarnable(health.WithMapDebugFlag("warn-network-category-unhealthy")) var networkCategoryWarning = health.NewWarnable(health.WithMapDebugFlag("warn-network-category-unhealthy"))
func configureInterface(cfg *Config, tun *tun.NativeTun) (retErr error) { func configureInterface(cfg *Config, tun *tun.NativeTun, health *health.Tracker) (retErr error) {
var mtu = tstun.DefaultTUNMTU() var mtu = tstun.DefaultTUNMTU()
luid := winipcfg.LUID(tun.LUID()) luid := winipcfg.LUID(tun.LUID())
iface, err := interfaceFromLUID(luid, iface, err := interfaceFromLUID(luid,
@ -268,10 +268,10 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) (retErr error) {
for i := range tries { for i := range tries {
found, err := setPrivateNetwork(luid) found, err := setPrivateNetwork(luid)
if err != nil { if err != nil {
health.Global.SetWarnable(networkCategoryWarning, fmt.Errorf("set-network-category: %w", err)) health.SetWarnable(networkCategoryWarning, fmt.Errorf("set-network-category: %w", err))
log.Printf("setPrivateNetwork(try=%d): %v", i, err) log.Printf("setPrivateNetwork(try=%d): %v", i, err)
} else { } else {
health.Global.SetWarnable(networkCategoryWarning, nil) health.SetWarnable(networkCategoryWarning, nil)
if found { if found {
if i > 0 { if i > 0 {
log.Printf("setPrivateNetwork(try=%d): success", i) log.Printf("setPrivateNetwork(try=%d): success", i)

@ -10,6 +10,7 @@ import (
"reflect" "reflect"
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"tailscale.com/health"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/preftype" "tailscale.com/types/preftype"
@ -44,9 +45,9 @@ type Router interface {
// //
// If netMon is nil, it's not used. It's currently (2021-07-20) only // If netMon is nil, it's not used. It's currently (2021-07-20) only
// used on Linux in some situations. // used on Linux in some situations.
func New(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor) (Router, error) { func New(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor, health *health.Tracker) (Router, error) {
logf = logger.WithPrefix(logf, "router: ") logf = logger.WithPrefix(logf, "router: ")
return newUserspaceRouter(logf, tundev, netMon) return newUserspaceRouter(logf, tundev, netMon, health)
} }
// CleanUp restores the system network configuration to its original state // CleanUp restores the system network configuration to its original state

@ -5,12 +5,13 @@ package router
import ( import (
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"tailscale.com/health"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor) (Router, error) { func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor, health *health.Tracker) (Router, error) {
return newUserspaceBSDRouter(logf, tundev, netMon) return newUserspaceBSDRouter(logf, tundev, netMon, health)
} }
func cleanUp(logger.Logf, string) { func cleanUp(logger.Logf, string) {

@ -10,11 +10,12 @@ import (
"runtime" "runtime"
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"tailscale.com/health"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
func newUserspaceRouter(logf logger.Logf, tunDev tun.Device, netMon *netmon.Monitor) (Router, error) { func newUserspaceRouter(logf logger.Logf, tunDev tun.Device, netMon *netmon.Monitor, health *health.Tracker) (Router, error) {
return nil, fmt.Errorf("unsupported OS %q", runtime.GOOS) return nil, fmt.Errorf("unsupported OS %q", runtime.GOOS)
} }

@ -5,6 +5,7 @@ package router
import ( import (
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"tailscale.com/health"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
@ -14,8 +15,8 @@ import (
// Work is currently underway for an in-kernel FreeBSD implementation of wireguard // Work is currently underway for an in-kernel FreeBSD implementation of wireguard
// https://svnweb.freebsd.org/base?view=revision&revision=357986 // https://svnweb.freebsd.org/base?view=revision&revision=357986
func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor) (Router, error) { func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor, health *health.Tracker) (Router, error) {
return newUserspaceBSDRouter(logf, tundev, netMon) return newUserspaceBSDRouter(logf, tundev, netMon, health)
} }
func cleanUp(logf logger.Logf, interfaceName string) { func cleanUp(logf logger.Logf, interfaceName string) {

@ -22,6 +22,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"golang.org/x/time/rate" "golang.org/x/time/rate"
"tailscale.com/envknob" "tailscale.com/envknob"
"tailscale.com/health"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/preftype" "tailscale.com/types/preftype"
@ -69,7 +70,7 @@ type linuxRouter struct {
magicsockPortV6 uint16 magicsockPortV6 uint16
} }
func newUserspaceRouter(logf logger.Logf, tunDev tun.Device, netMon *netmon.Monitor) (Router, error) { func newUserspaceRouter(logf logger.Logf, tunDev tun.Device, netMon *netmon.Monitor, health *health.Tracker) (Router, error) {
tunname, err := tunDev.Name() tunname, err := tunDev.Name()
if err != nil { if err != nil {
return nil, err return nil, err

@ -886,7 +886,7 @@ func newLinuxRootTest(t *testing.T) *linuxTest {
mon.Start() mon.Start()
lt.mon = mon lt.mon = mon
r, err := newUserspaceRouter(logf, lt.tun, mon) r, err := newUserspaceRouter(logf, lt.tun, mon, nil)
if err != nil { if err != nil {
lt.Close() lt.Close()
t.Fatal(err) t.Fatal(err)

@ -12,6 +12,7 @@ import (
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"go4.org/netipx" "go4.org/netipx"
"tailscale.com/health"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/util/set" "tailscale.com/util/set"
@ -30,7 +31,7 @@ type openbsdRouter struct {
routes set.Set[netip.Prefix] routes set.Set[netip.Prefix]
} }
func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor) (Router, error) { func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor, health *health.Tracker) (Router, error) {
tunname, err := tundev.Name() tunname, err := tundev.Name()
if err != nil { if err != nil {
return nil, err return nil, err

@ -14,6 +14,7 @@ import (
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"go4.org/netipx" "go4.org/netipx"
"tailscale.com/health"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/types/logger" "tailscale.com/types/logger"
@ -23,12 +24,13 @@ import (
type userspaceBSDRouter struct { type userspaceBSDRouter struct {
logf logger.Logf logf logger.Logf
netMon *netmon.Monitor netMon *netmon.Monitor
health *health.Tracker
tunname string tunname string
local []netip.Prefix local []netip.Prefix
routes map[netip.Prefix]bool routes map[netip.Prefix]bool
} }
func newUserspaceBSDRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor) (Router, error) { func newUserspaceBSDRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor, health *health.Tracker) (Router, error) {
tunname, err := tundev.Name() tunname, err := tundev.Name()
if err != nil { if err != nil {
return nil, err return nil, err
@ -37,6 +39,7 @@ func newUserspaceBSDRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.M
return &userspaceBSDRouter{ return &userspaceBSDRouter{
logf: logf, logf: logf,
netMon: netMon, netMon: netMon,
health: health,
tunname: tunname, tunname: tunname,
}, nil }, nil
} }

@ -22,6 +22,7 @@ import (
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"tailscale.com/health"
"tailscale.com/logtail/backoff" "tailscale.com/logtail/backoff"
"tailscale.com/net/dns" "tailscale.com/net/dns"
"tailscale.com/net/netmon" "tailscale.com/net/netmon"
@ -31,12 +32,13 @@ import (
type winRouter struct { type winRouter struct {
logf func(fmt string, args ...any) logf func(fmt string, args ...any)
netMon *netmon.Monitor // may be nil netMon *netmon.Monitor // may be nil
health *health.Tracker
nativeTun *tun.NativeTun nativeTun *tun.NativeTun
routeChangeCallback *winipcfg.RouteChangeCallback routeChangeCallback *winipcfg.RouteChangeCallback
firewall *firewallTweaker firewall *firewallTweaker
} }
func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor) (Router, error) { func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor, health *health.Tracker) (Router, error) {
nativeTun := tundev.(*tun.NativeTun) nativeTun := tundev.(*tun.NativeTun)
luid := winipcfg.LUID(nativeTun.LUID()) luid := winipcfg.LUID(nativeTun.LUID())
guid, err := luid.GUID() guid, err := luid.GUID()
@ -47,6 +49,7 @@ func newUserspaceRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Moni
return &winRouter{ return &winRouter{
logf: logf, logf: logf,
netMon: netMon, netMon: netMon,
health: health,
nativeTun: nativeTun, nativeTun: nativeTun,
firewall: &firewallTweaker{ firewall: &firewallTweaker{
logf: logger.WithPrefix(logf, "firewall: "), logf: logger.WithPrefix(logf, "firewall: "),
@ -80,7 +83,7 @@ func (r *winRouter) Set(cfg *Config) error {
} }
r.firewall.set(localAddrs, cfg.Routes, cfg.LocalRoutes) r.firewall.set(localAddrs, cfg.Routes, cfg.LocalRoutes)
err := configureInterface(cfg, r.nativeTun) err := configureInterface(cfg, r.nativeTun, r.health)
if err != nil { if err != nil {
r.logf("ConfigureInterface: %v", err) r.logf("ConfigureInterface: %v", err)
return err return err

@ -341,7 +341,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
tunName, _ := conf.Tun.Name() tunName, _ := conf.Tun.Name()
conf.Dialer.SetTUNName(tunName) conf.Dialer.SetTUNName(tunName)
conf.Dialer.SetNetMon(e.netMon) conf.Dialer.SetNetMon(e.netMon)
e.dns = dns.NewManager(logf, conf.DNS, e.netMon, conf.Dialer, fwdDNSLinkSelector{e, tunName}, conf.ControlKnobs) e.dns = dns.NewManager(logf, conf.DNS, e.netMon, e.health, conf.Dialer, fwdDNSLinkSelector{e, tunName}, conf.ControlKnobs)
// TODO: there's probably a better place for this // TODO: there's probably a better place for this
sockstats.SetNetMon(e.netMon) sockstats.SetNetMon(e.netMon)
@ -966,7 +966,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
nid := cfg.NetworkLogging.NodeID nid := cfg.NetworkLogging.NodeID
tid := cfg.NetworkLogging.DomainID tid := cfg.NetworkLogging.DomainID
e.logf("wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)", nid.Public(), tid.Public()) e.logf("wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)", nid.Public(), tid.Public())
if err := e.networkLogger.Startup(cfg.NodeID, nid, tid, e.tundev, e.magicConn, e.netMon); err != nil { if err := e.networkLogger.Startup(cfg.NodeID, nid, tid, e.tundev, e.magicConn, e.netMon, e.health); err != nil {
e.logf("wgengine: Reconfig: error starting up network logger: %v", err) e.logf("wgengine: Reconfig: error starting up network logger: %v", err)
} }
e.networkLogger.ReconfigRoutes(routerCfg) e.networkLogger.ReconfigRoutes(routerCfg)

Loading…
Cancel
Save