mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 08:11:32 +01:00 
			
		
		
		
	Baby steps. This permits building without much of gvisor, but not all of it. Updates #17283 Change-Id: I8433146e259918cc901fe86b4ea29be22075b32c Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			242 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			242 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| // Package tsd (short for "Tailscale Daemon") contains a System type that
 | |
| // containing all the subsystems a Tailscale node (tailscaled or platform
 | |
| // equivalent) uses.
 | |
| //
 | |
| // The goal of this package (as of 2023-05-03) is to eventually unify
 | |
| // initialization across tailscaled, tailscaled as a Windows services, the mac
 | |
| // GUI, tsnet, wasm, tests, and other places that wire up all the subsystems.
 | |
| // And doing so without weird optional interface accessors on some subsystems
 | |
| // that return other subsystems. It's all a work in progress.
 | |
| //
 | |
| // This package depends on nearly all parts of Tailscale, so it should not be
 | |
| // imported by (or thus passed to) any package that does not want to depend on
 | |
| // the world. In practice this means that only things like cmd/tailscaled,
 | |
| // ipn/ipnlocal, and ipn/ipnserver should import this package.
 | |
| package tsd
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 
 | |
| 	"tailscale.com/control/controlknobs"
 | |
| 	"tailscale.com/drive"
 | |
| 	"tailscale.com/health"
 | |
| 	"tailscale.com/ipn"
 | |
| 	"tailscale.com/ipn/conffile"
 | |
| 	"tailscale.com/net/dns"
 | |
| 	"tailscale.com/net/netmon"
 | |
| 	"tailscale.com/net/tsdial"
 | |
| 	"tailscale.com/net/tstun"
 | |
| 	"tailscale.com/proxymap"
 | |
| 	"tailscale.com/types/netmap"
 | |
| 	"tailscale.com/util/eventbus"
 | |
| 	"tailscale.com/util/syspolicy/policyclient"
 | |
| 	"tailscale.com/util/usermetric"
 | |
| 	"tailscale.com/wgengine"
 | |
| 	"tailscale.com/wgengine/magicsock"
 | |
| 	"tailscale.com/wgengine/router"
 | |
| )
 | |
| 
 | |
| // System contains all the subsystems of a Tailscale node (tailscaled, etc.)
 | |
| //
 | |
| // A valid System value must always have a non-nil Bus populated.  Callers must
 | |
| // ensure this before using the value further. Call [NewSystem] to obtain a
 | |
| // value ready to use.
 | |
| type System struct {
 | |
| 	Bus            SubSystem[*eventbus.Bus]
 | |
| 	Dialer         SubSystem[*tsdial.Dialer]
 | |
| 	DNSManager     SubSystem[*dns.Manager] // can get its *resolver.Resolver from DNSManager.Resolver
 | |
| 	Engine         SubSystem[wgengine.Engine]
 | |
| 	NetMon         SubSystem[*netmon.Monitor]
 | |
| 	MagicSock      SubSystem[*magicsock.Conn]
 | |
| 	NetstackRouter SubSystem[bool] // using Netstack at all (either entirely or at least for subnets)
 | |
| 	Router         SubSystem[router.Router]
 | |
| 	Tun            SubSystem[*tstun.Wrapper]
 | |
| 	StateStore     SubSystem[ipn.StateStore]
 | |
| 	Netstack       SubSystem[NetstackImpl] // actually a *netstack.Impl
 | |
| 	DriveForLocal  SubSystem[drive.FileSystemForLocal]
 | |
| 	DriveForRemote SubSystem[drive.FileSystemForRemote]
 | |
| 	PolicyClient   SubSystem[policyclient.Client]
 | |
| 	HealthTracker  SubSystem[*health.Tracker]
 | |
| 
 | |
| 	// InitialConfig is initial server config, if any.
 | |
| 	// It is nil if the node is not in declarative mode.
 | |
| 	// This value is never updated after startup.
 | |
| 	// LocalBackend tracks the current config after any reloads.
 | |
| 	InitialConfig *conffile.Config
 | |
| 
 | |
| 	// onlyNetstack is whether the Tun value is a fake TUN device
 | |
| 	// and we're using netstack for everything.
 | |
| 	onlyNetstack bool
 | |
| 
 | |
| 	controlKnobs controlknobs.Knobs
 | |
| 	proxyMap     proxymap.Mapper
 | |
| 
 | |
| 	userMetricsRegistry usermetric.Registry
 | |
| }
 | |
| 
 | |
| // NewSystem constructs a new otherwise-empty [System] with a
 | |
| // freshly-constructed event bus populated.
 | |
| func NewSystem() *System { return NewSystemWithBus(eventbus.New()) }
 | |
| 
 | |
| // NewSystemWithBus constructs a new otherwise-empty [System] with an
 | |
| // eventbus provided by the caller. The provided bus must not be nil.
 | |
| // This is mainly intended for testing; for production use call [NewBus].
 | |
| func NewSystemWithBus(bus *eventbus.Bus) *System {
 | |
| 	if bus == nil {
 | |
| 		panic("nil eventbus")
 | |
| 	}
 | |
| 	sys := new(System)
 | |
| 	sys.Set(bus)
 | |
| 
 | |
| 	tracker := health.NewTracker(bus)
 | |
| 	sys.Set(tracker)
 | |
| 
 | |
| 	return sys
 | |
| }
 | |
| 
 | |
| // LocalBackend is a fake name for *ipnlocal.LocalBackend to avoid an import cycle.
 | |
| type LocalBackend = any
 | |
| 
 | |
| // NetstackImpl is the interface that *netstack.Impl implements.
 | |
| // It's an interface for circular dependency reasons: netstack.Impl
 | |
| // references LocalBackend, and LocalBackend has a tsd.System.
 | |
| type NetstackImpl interface {
 | |
| 	Start(LocalBackend) error
 | |
| 	UpdateNetstackIPs(*netmap.NetworkMap)
 | |
| }
 | |
| 
 | |
| // Set is a convenience method to set a subsystem value.
 | |
| // It panics if the type is unknown or has that type
 | |
| // has already been set.
 | |
| func (s *System) Set(v any) {
 | |
| 	switch v := v.(type) {
 | |
| 	case *eventbus.Bus:
 | |
| 		s.Bus.Set(v)
 | |
| 	case *netmon.Monitor:
 | |
| 		s.NetMon.Set(v)
 | |
| 	case *dns.Manager:
 | |
| 		s.DNSManager.Set(v)
 | |
| 	case *tsdial.Dialer:
 | |
| 		s.Dialer.Set(v)
 | |
| 	case wgengine.Engine:
 | |
| 		s.Engine.Set(v)
 | |
| 	case router.Router:
 | |
| 		s.Router.Set(v)
 | |
| 	case *tstun.Wrapper:
 | |
| 		type ft interface {
 | |
| 			IsFakeTun() bool
 | |
| 		}
 | |
| 		if _, ok := v.Unwrap().(ft); ok {
 | |
| 			s.onlyNetstack = true
 | |
| 		}
 | |
| 		s.Tun.Set(v)
 | |
| 	case *magicsock.Conn:
 | |
| 		s.MagicSock.Set(v)
 | |
| 	case ipn.StateStore:
 | |
| 		s.StateStore.Set(v)
 | |
| 	case NetstackImpl:
 | |
| 		s.Netstack.Set(v)
 | |
| 	case drive.FileSystemForLocal:
 | |
| 		s.DriveForLocal.Set(v)
 | |
| 	case drive.FileSystemForRemote:
 | |
| 		s.DriveForRemote.Set(v)
 | |
| 	case policyclient.Client:
 | |
| 		s.PolicyClient.Set(v)
 | |
| 	case *health.Tracker:
 | |
| 		s.HealthTracker.Set(v)
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("unknown type %T", v))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // IsNetstackRouter reports whether Tailscale is either fully netstack based
 | |
| // (without TUN) or is at least using netstack for routing.
 | |
| func (s *System) IsNetstackRouter() bool {
 | |
| 	if v, ok := s.NetstackRouter.GetOK(); ok && v {
 | |
| 		return true
 | |
| 	}
 | |
| 	return s.IsNetstack()
 | |
| }
 | |
| 
 | |
| // IsNetstack reports whether Tailscale is running as a netstack-based TUN-free engine.
 | |
| func (s *System) IsNetstack() bool {
 | |
| 	return s.onlyNetstack
 | |
| }
 | |
| 
 | |
| // ControlKnobs returns the control knobs for this node.
 | |
| func (s *System) ControlKnobs() *controlknobs.Knobs {
 | |
| 	return &s.controlKnobs
 | |
| }
 | |
| 
 | |
| // ProxyMapper returns the ephemeral ip:port mapper.
 | |
| func (s *System) ProxyMapper() *proxymap.Mapper {
 | |
| 	return &s.proxyMap
 | |
| }
 | |
| 
 | |
| // UserMetricsRegistry returns the system usermetrics.
 | |
| func (s *System) UserMetricsRegistry() *usermetric.Registry {
 | |
| 	return &s.userMetricsRegistry
 | |
| }
 | |
| 
 | |
| // PolicyClientOrDefault returns the policy client if set or a no-op default
 | |
| // otherwise. It always returns a non-nil value.
 | |
| func (s *System) PolicyClientOrDefault() policyclient.Client {
 | |
| 	if client, ok := s.PolicyClient.GetOK(); ok {
 | |
| 		return client
 | |
| 	}
 | |
| 	return policyclient.Get()
 | |
| }
 | |
| 
 | |
| // SubSystem represents some subsystem of the Tailscale node daemon.
 | |
| //
 | |
| // A subsystem can be set to a value, and then later retrieved. A subsystem
 | |
| // value tracks whether it's been set and, once set, doesn't allow the value to
 | |
| // change.
 | |
| type SubSystem[T any] struct {
 | |
| 	set bool
 | |
| 	v   T
 | |
| }
 | |
| 
 | |
| // Set sets p to v.
 | |
| //
 | |
| // It panics if p is already set to a different value.
 | |
| //
 | |
| // Set must not be called concurrently with other Sets or Gets.
 | |
| func (p *SubSystem[T]) Set(v T) {
 | |
| 	if p.set {
 | |
| 		var oldVal any = p.v
 | |
| 		var newVal any = v
 | |
| 		if oldVal == newVal {
 | |
| 			// Allow setting to the same value.
 | |
| 			// Note we had to box them through "any" to force them to be comparable.
 | |
| 			// We can't set the type constraint T to be "comparable" because the interfaces
 | |
| 			// aren't comparable. (See https://github.com/golang/go/issues/52531 and
 | |
| 			// https://github.com/golang/go/issues/52614 for some background)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		var z *T
 | |
| 		panic(fmt.Sprintf("%v is already set", reflect.TypeOf(z).Elem().String()))
 | |
| 	}
 | |
| 	p.v = v
 | |
| 	p.set = true
 | |
| }
 | |
| 
 | |
| // Get returns the value of p, panicking if it hasn't been set.
 | |
| func (p *SubSystem[T]) Get() T {
 | |
| 	if !p.set {
 | |
| 		var z *T
 | |
| 		panic(fmt.Sprintf("%v is not set", reflect.TypeOf(z).Elem().String()))
 | |
| 	}
 | |
| 	return p.v
 | |
| }
 | |
| 
 | |
| // GetOK returns the value of p (if any) and whether it's been set.
 | |
| func (p *SubSystem[T]) GetOK() (_ T, ok bool) {
 | |
| 	return p.v, p.set
 | |
| }
 |