Compare commits
6 Commits
04662a8078
...
9e192ca03e
Author | SHA1 | Date |
---|---|---|
Maisem Ali | 9e192ca03e | |
Brad Fitzpatrick | 4dece0c359 | |
Brad Fitzpatrick | 7f587d0321 | |
Jonathan Nobels | 71e9258ad9 | |
Brad Fitzpatrick | 745931415c | |
Maisem Ali | a0dadf584d |
|
@ -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()
|
||||||
|
|
|
@ -104,9 +104,10 @@ func newIPN(jsConfig js.Value) map[string]any {
|
||||||
sys.Set(store)
|
sys.Set(store)
|
||||||
dialer := &tsdial.Dialer{Logf: logf}
|
dialer := &tsdial.Dialer{Logf: logf}
|
||||||
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
|
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
|
||||||
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)
|
||||||
|
|
|
@ -1453,14 +1453,15 @@ func (c *Direct) getNoiseClient() (*NoiseClient, error) {
|
||||||
}
|
}
|
||||||
c.logf("[v1] creating new noise client")
|
c.logf("[v1] creating new noise client")
|
||||||
nc, err := NewNoiseClient(NoiseOpts{
|
nc, err := NewNoiseClient(NoiseOpts{
|
||||||
PrivKey: k,
|
PrivKey: k,
|
||||||
ServerPubKey: serverNoiseKey,
|
ServerPubKey: serverNoiseKey,
|
||||||
ServerURL: c.serverURL,
|
ServerURL: c.serverURL,
|
||||||
Dialer: c.dialer,
|
Dialer: c.dialer,
|
||||||
DNSCache: c.dnsCache,
|
DNSCache: c.dnsCache,
|
||||||
Logf: c.logf,
|
Logf: c.logf,
|
||||||
NetMon: c.netMon,
|
NetMon: c.netMon,
|
||||||
DialPlan: dp,
|
HealthTracker: c.health,
|
||||||
|
DialPlan: dp,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -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.
|
||||||
|
|
109
health/health.go
109
health/health.go
|
@ -30,17 +30,39 @@ var (
|
||||||
debugHandler map[string]http.Handler
|
debugHandler map[string]http.Handler
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global is a global health tracker for the process.
|
// ReceiveFunc is one of the three magicsock Receive funcs (IPv4, IPv6, or
|
||||||
//
|
// DERP).
|
||||||
// TODO(bradfitz): finish moving all reference to this plumb it (ultimately out
|
type ReceiveFunc int
|
||||||
// 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)
|
|
||||||
|
|
||||||
|
// ReceiveFunc indices for Tracker.MagicSockReceiveFuncs.
|
||||||
|
const (
|
||||||
|
ReceiveIPv4 ReceiveFunc = 0
|
||||||
|
ReceiveIPv6 ReceiveFunc = 1
|
||||||
|
ReceiveDERP ReceiveFunc = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f ReceiveFunc) String() string {
|
||||||
|
if f < 0 || int(f) >= len(receiveNames) {
|
||||||
|
return fmt.Sprintf("ReceiveFunc(%d)", f)
|
||||||
|
}
|
||||||
|
return receiveNames[f]
|
||||||
|
}
|
||||||
|
|
||||||
|
var receiveNames = []string{
|
||||||
|
ReceiveIPv4: "ReceiveIPv4",
|
||||||
|
ReceiveIPv6: "ReceiveIPv6",
|
||||||
|
ReceiveDERP: "ReceiveDERP",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tracker tracks the health of various Tailscale subsystems,
|
||||||
|
// comparing each subsystems' state with each other to make sure
|
||||||
|
// they're consistent based on the user's intended state.
|
||||||
type Tracker struct {
|
type Tracker struct {
|
||||||
// mu guards everything in this var block.
|
// MagicSockReceiveFuncs tracks the state of the three
|
||||||
|
// magicsock receive functions: IPv4, IPv6, and DERP.
|
||||||
|
MagicSockReceiveFuncs [3]ReceiveFuncStats // indexed by ReceiveFunc values
|
||||||
|
|
||||||
|
// mu guards everything that follows.
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
warnables []*Warnable // keys ever set
|
warnables []*Warnable // keys ever set
|
||||||
|
@ -530,7 +552,7 @@ func (t *Tracker) timerSelfCheck() {
|
||||||
}
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
checkReceiveFuncs()
|
t.checkReceiveFuncsLocked()
|
||||||
t.selfCheckLocked()
|
t.selfCheckLocked()
|
||||||
if t.timer != nil {
|
if t.timer != nil {
|
||||||
t.timer.Reset(time.Minute)
|
t.timer.Reset(time.Minute)
|
||||||
|
@ -632,9 +654,10 @@ func (t *Tracker) overallErrorLocked() error {
|
||||||
_ = t.lastMapRequestHeard
|
_ = t.lastMapRequestHeard
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
for _, recv := range receiveFuncs {
|
for i := range t.MagicSockReceiveFuncs {
|
||||||
if recv.missing {
|
f := &t.MagicSockReceiveFuncs[i]
|
||||||
errs = append(errs, fmt.Errorf("%s is not running", recv.name))
|
if f.missing {
|
||||||
|
errs = append(errs, fmt.Errorf("%s is not running", f.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for sys, err := range t.sysErr {
|
for sys, err := range t.sysErr {
|
||||||
|
@ -670,63 +693,69 @@ func (t *Tracker) overallErrorLocked() error {
|
||||||
return multierr.New(errs...)
|
return multierr.New(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
ReceiveIPv4 = ReceiveFuncStats{name: "ReceiveIPv4"}
|
|
||||||
ReceiveIPv6 = ReceiveFuncStats{name: "ReceiveIPv6"}
|
|
||||||
ReceiveDERP = ReceiveFuncStats{name: "ReceiveDERP"}
|
|
||||||
|
|
||||||
receiveFuncs = []*ReceiveFuncStats{&ReceiveIPv4, &ReceiveIPv6, &ReceiveDERP}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if runtime.GOOS == "js" {
|
|
||||||
receiveFuncs = receiveFuncs[2:] // ignore IPv4 and IPv6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReceiveFuncStats tracks the calls made to a wireguard-go receive func.
|
// ReceiveFuncStats tracks the calls made to a wireguard-go receive func.
|
||||||
type ReceiveFuncStats struct {
|
type ReceiveFuncStats struct {
|
||||||
// name is the name of the receive func.
|
// name is the name of the receive func.
|
||||||
|
// It's lazily populated.
|
||||||
name string
|
name string
|
||||||
// numCalls is the number of times the receive func has ever been called.
|
// numCalls is the number of times the receive func has ever been called.
|
||||||
// It is required because it is possible for a receive func's wireguard-go goroutine
|
// It is required because it is possible for a receive func's wireguard-go goroutine
|
||||||
// to be active even though the receive func isn't.
|
// to be active even though the receive func isn't.
|
||||||
// The wireguard-go goroutine alternates between calling the receive func and
|
// The wireguard-go goroutine alternates between calling the receive func and
|
||||||
// processing what the func returned.
|
// processing what the func returned.
|
||||||
numCalls uint64 // accessed atomically
|
numCalls atomic.Uint64
|
||||||
// prevNumCalls is the value of numCalls last time the health check examined it.
|
// prevNumCalls is the value of numCalls last time the health check examined it.
|
||||||
prevNumCalls uint64
|
prevNumCalls uint64
|
||||||
// inCall indicates whether the receive func is currently running.
|
// inCall indicates whether the receive func is currently running.
|
||||||
inCall uint32 // bool, accessed atomically
|
inCall atomic.Bool
|
||||||
// missing indicates whether the receive func is not running.
|
// missing indicates whether the receive func is not running.
|
||||||
missing bool
|
missing bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReceiveFuncStats) Enter() {
|
func (s *ReceiveFuncStats) Enter() {
|
||||||
atomic.AddUint64(&s.numCalls, 1)
|
s.numCalls.Add(1)
|
||||||
atomic.StoreUint32(&s.inCall, 1)
|
s.inCall.Store(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReceiveFuncStats) Exit() {
|
func (s *ReceiveFuncStats) Exit() {
|
||||||
atomic.StoreUint32(&s.inCall, 0)
|
s.inCall.Store(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkReceiveFuncs() {
|
// ReceiveFuncStats returns the ReceiveFuncStats tracker for the given func
|
||||||
for _, recv := range receiveFuncs {
|
// type.
|
||||||
recv.missing = false
|
//
|
||||||
prev := recv.prevNumCalls
|
// If t is nil, it returns nil.
|
||||||
numCalls := atomic.LoadUint64(&recv.numCalls)
|
func (t *Tracker) ReceiveFuncStats(which ReceiveFunc) *ReceiveFuncStats {
|
||||||
recv.prevNumCalls = numCalls
|
if t == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &t.MagicSockReceiveFuncs[which]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tracker) checkReceiveFuncsLocked() {
|
||||||
|
for i := range t.MagicSockReceiveFuncs {
|
||||||
|
f := &t.MagicSockReceiveFuncs[i]
|
||||||
|
if f.name == "" {
|
||||||
|
f.name = (ReceiveFunc(i)).String()
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "js" && i < 2 {
|
||||||
|
// Skip IPv4 and IPv6 on js.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
f.missing = false
|
||||||
|
prev := f.prevNumCalls
|
||||||
|
numCalls := f.numCalls.Load()
|
||||||
|
f.prevNumCalls = numCalls
|
||||||
if numCalls > prev {
|
if numCalls > prev {
|
||||||
// OK: the function has gotten called since last we checked
|
// OK: the function has gotten called since last we checked
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if atomic.LoadUint32(&recv.inCall) == 1 {
|
if f.inCall.Load() {
|
||||||
// OK: the function is active, probably blocked due to inactivity
|
// OK: the function is active, probably blocked due to inactivity
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Not OK: The function is not active, and not accumulating new calls.
|
// Not OK: The function is not active, and not accumulating new calls.
|
||||||
// It is probably MIA.
|
// It is probably MIA.
|
||||||
recv.missing = true
|
f.missing = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -6253,6 +6253,7 @@ func mayDeref[T any](p *T) (v T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrNoPreferredDERP = errors.New("no preferred DERP, try again later")
|
var ErrNoPreferredDERP = errors.New("no preferred DERP, try again later")
|
||||||
|
var ErrCannotSuggestExitNode = errors.New("unable to suggest an exit node, try again later")
|
||||||
|
|
||||||
// SuggestExitNode computes a suggestion based on the current netmap and last netcheck report. If
|
// SuggestExitNode computes a suggestion based on the current netmap and last netcheck report. If
|
||||||
// there are multiple equally good options, one is selected at random, so the result is not stable. To be
|
// there are multiple equally good options, one is selected at random, so the result is not stable. To be
|
||||||
|
@ -6266,6 +6267,9 @@ func (b *LocalBackend) SuggestExitNode() (response apitype.ExitNodeSuggestionRes
|
||||||
lastReport := b.MagicConn().GetLastNetcheckReport(b.ctx)
|
lastReport := b.MagicConn().GetLastNetcheckReport(b.ctx)
|
||||||
netMap := b.netMap
|
netMap := b.netMap
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
|
if lastReport == nil || netMap == nil {
|
||||||
|
return response, ErrCannotSuggestExitNode
|
||||||
|
}
|
||||||
seed := time.Now().UnixNano()
|
seed := time.Now().UnixNano()
|
||||||
r := rand.New(rand.NewSource(seed))
|
r := rand.New(rand.NewSource(seed))
|
||||||
return suggestExitNode(lastReport, netMap, r)
|
return suggestExitNode(lastReport, netMap, r)
|
||||||
|
|
|
@ -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"
|
||||||
|
@ -116,8 +117,9 @@ func restartResolved() error {
|
||||||
// The caller must call Down before program shutdown
|
// The caller must call Down before program shutdown
|
||||||
// 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
|
||||||
fs wholeFileFS
|
health *health.Tracker
|
||||||
|
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
|
||||||
// /etc/resolv.conf is bind-mounted from outside the container,
|
// /etc/resolv.conf is bind-mounted from outside the container,
|
||||||
|
@ -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) {
|
||||||
|
|
|
@ -42,7 +42,8 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,13 +63,14 @@ type resolvedManager struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel func() // terminate the context, for close
|
cancel func() // terminate the context, for close
|
||||||
|
|
||||||
logf logger.Logf
|
logf logger.Logf
|
||||||
ifidx int
|
health *health.Tracker
|
||||||
|
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
|
||||||
|
@ -82,8 +83,9 @@ func newResolvedManager(logf logger.Logf, interfaceName string) (*resolvedManage
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
|
|
||||||
logf: logf,
|
logf: logf,
|
||||||
ifidx: iface.Index,
|
health: health,
|
||||||
|
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"
|
||||||
)
|
)
|
||||||
|
@ -54,12 +55,14 @@ func wslDistros() ([]string, error) {
|
||||||
// wslManager is a DNS manager for WSL2 linux distributions.
|
// wslManager is a DNS manager for WSL2 linux distributions.
|
||||||
// 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,
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,6 +6,7 @@ package netutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
@ -145,8 +146,6 @@ func CheckIPForwarding(routes []netip.Prefix, state *interfaces.State) (warn, er
|
||||||
// disabled or set to 'loose' mode for exit node functionality on any
|
// disabled or set to 'loose' mode for exit node functionality on any
|
||||||
// interface.
|
// interface.
|
||||||
//
|
//
|
||||||
// The state param can be nil, in which case interfaces.GetState is used.
|
|
||||||
//
|
|
||||||
// The routes should only be advertised routes, and should not contain the
|
// The routes should only be advertised routes, and should not contain the
|
||||||
// node's Tailscale IPs.
|
// node's Tailscale IPs.
|
||||||
//
|
//
|
||||||
|
@ -159,11 +158,7 @@ func CheckReversePathFiltering(state *interfaces.State) (warn []string, err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
if state == nil {
|
if state == nil {
|
||||||
var err error
|
return nil, errors.New("no link state")
|
||||||
state, err = interfaces.GetState()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The kernel uses the maximum value for rp_filter between the 'all'
|
// The kernel uses the maximum value for rp_filter between the 'all'
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"tailscale.com/net/netmon"
|
||||||
)
|
)
|
||||||
|
|
||||||
type conn struct {
|
type conn struct {
|
||||||
|
@ -70,7 +72,13 @@ func TestCheckReversePathFiltering(t *testing.T) {
|
||||||
if runtime.GOOS != "linux" {
|
if runtime.GOOS != "linux" {
|
||||||
t.Skipf("skipping on %s", runtime.GOOS)
|
t.Skipf("skipping on %s", runtime.GOOS)
|
||||||
}
|
}
|
||||||
warn, err := CheckReversePathFiltering(nil)
|
netMon, err := netmon.New(t.Logf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer netMon.Close()
|
||||||
|
|
||||||
|
warn, err := CheckReversePathFiltering(netMon.InterfaceState())
|
||||||
t.Logf("err: %v", err)
|
t.Logf("err: %v", err)
|
||||||
t.Logf("warnings: %v", warn)
|
t.Logf("warnings: %v", warn)
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ import (
|
||||||
"tailscale.com/util/httpm"
|
"tailscale.com/util/httpm"
|
||||||
"tailscale.com/util/mak"
|
"tailscale.com/util/mak"
|
||||||
"tailscale.com/util/multierr"
|
"tailscale.com/util/multierr"
|
||||||
|
"tailscale.com/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -1226,10 +1227,18 @@ func (ss *sshSession) run() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// recordSSHToLocalDisk is a deprecated dev knob to allow recording SSH sessions
|
var debugLogSSH = envknob.RegisterBool("TS_DEBUG_LOG_SSH")
|
||||||
// to local storage. It is only used if there is no recording configured by the
|
|
||||||
// coordination server. This will be removed in the future.
|
// recordSSHToLocalDisk is a dev knob to allow recording SSH sessions to local
|
||||||
var recordSSHToLocalDisk = envknob.RegisterBool("TS_DEBUG_LOG_SSH")
|
// storage. It is only used if there is no recording configured by the
|
||||||
|
// coordination server. It is only enabled in dev builds for testing, for
|
||||||
|
// production use recorders should be configured by the coordination server.
|
||||||
|
func recordSSHToLocalDisk() bool {
|
||||||
|
if !version.GetMeta().IsDev {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return debugLogSSH()
|
||||||
|
}
|
||||||
|
|
||||||
// recorders returns the list of recorders to use for this session.
|
// recorders returns the list of recorders to use for this session.
|
||||||
// If the final action has a non-empty list of recorders, that list is
|
// If the final action has a non-empty list of recorders, that list is
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -681,8 +681,10 @@ func (c *Conn) runDerpWriter(ctx context.Context, dc *derphttp.Client, ch <-chan
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *connBind) receiveDERP(buffs [][]byte, sizes []int, eps []conn.Endpoint) (int, error) {
|
func (c *connBind) receiveDERP(buffs [][]byte, sizes []int, eps []conn.Endpoint) (int, error) {
|
||||||
health.ReceiveDERP.Enter()
|
if s := c.Conn.health.ReceiveFuncStats(health.ReceiveDERP); s != nil {
|
||||||
defer health.ReceiveDERP.Exit()
|
s.Enter()
|
||||||
|
defer s.Exit()
|
||||||
|
}
|
||||||
|
|
||||||
for dm := range c.derpRecvCh {
|
for dm := range c.derpRecvCh {
|
||||||
if c.isClosed() {
|
if c.isClosed() {
|
||||||
|
|
|
@ -1203,12 +1203,12 @@ func (c *Conn) putReceiveBatch(batch *receiveBatch) {
|
||||||
|
|
||||||
// receiveIPv4 creates an IPv4 ReceiveFunc reading from c.pconn4.
|
// receiveIPv4 creates an IPv4 ReceiveFunc reading from c.pconn4.
|
||||||
func (c *Conn) receiveIPv4() conn.ReceiveFunc {
|
func (c *Conn) receiveIPv4() conn.ReceiveFunc {
|
||||||
return c.mkReceiveFunc(&c.pconn4, &health.ReceiveIPv4, metricRecvDataIPv4)
|
return c.mkReceiveFunc(&c.pconn4, c.health.ReceiveFuncStats(health.ReceiveIPv4), metricRecvDataIPv4)
|
||||||
}
|
}
|
||||||
|
|
||||||
// receiveIPv6 creates an IPv6 ReceiveFunc reading from c.pconn6.
|
// receiveIPv6 creates an IPv6 ReceiveFunc reading from c.pconn6.
|
||||||
func (c *Conn) receiveIPv6() conn.ReceiveFunc {
|
func (c *Conn) receiveIPv6() conn.ReceiveFunc {
|
||||||
return c.mkReceiveFunc(&c.pconn6, &health.ReceiveIPv6, metricRecvDataIPv6)
|
return c.mkReceiveFunc(&c.pconn6, c.health.ReceiveFuncStats(health.ReceiveIPv6), metricRecvDataIPv6)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mkReceiveFunc creates a ReceiveFunc reading from ruc.
|
// mkReceiveFunc creates a ReceiveFunc reading from ruc.
|
||||||
|
|
|
@ -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…
Reference in New Issue