mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-08 09:07:44 +00:00
tsd: add package with System type to unify subsystem init, discovery
This is part of an effort to clean up tailscaled initialization between tailscaled, tailscaled Windows service, tsnet, and the mac GUI. Updates #8036 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
0d7303b798
commit
6e967446e4
@ -282,6 +282,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
LD tailscale.com/tempfork/gliderlabs/ssh from tailscale.com/ssh/tailssh
|
LD tailscale.com/tempfork/gliderlabs/ssh from tailscale.com/ssh/tailssh
|
||||||
tailscale.com/tka from tailscale.com/ipn/ipnlocal+
|
tailscale.com/tka from tailscale.com/ipn/ipnlocal+
|
||||||
W tailscale.com/tsconst from tailscale.com/net/interfaces
|
W tailscale.com/tsconst from tailscale.com/net/interfaces
|
||||||
|
tailscale.com/tsd from tailscale.com/cmd/tailscaled+
|
||||||
tailscale.com/tstime from tailscale.com/wgengine/magicsock
|
tailscale.com/tstime from tailscale.com/wgengine/magicsock
|
||||||
💣 tailscale.com/tstime/mono from tailscale.com/net/tstun+
|
💣 tailscale.com/tstime/mono from tailscale.com/net/tstun+
|
||||||
tailscale.com/tstime/rate from tailscale.com/wgengine/filter+
|
tailscale.com/tstime/rate from tailscale.com/wgengine/filter+
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
"tailscale.com/safesocket"
|
"tailscale.com/safesocket"
|
||||||
"tailscale.com/smallzstd"
|
"tailscale.com/smallzstd"
|
||||||
"tailscale.com/syncs"
|
"tailscale.com/syncs"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/tsweb/varz"
|
"tailscale.com/tsweb/varz"
|
||||||
"tailscale.com/types/flagtype"
|
"tailscale.com/types/flagtype"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
@ -330,12 +331,16 @@ func ipnServerOpts() (o serverOptions) {
|
|||||||
|
|
||||||
func run() error {
|
func run() error {
|
||||||
var logf logger.Logf = log.Printf
|
var logf logger.Logf = log.Printf
|
||||||
|
|
||||||
|
sys := new(tsd.System)
|
||||||
|
|
||||||
netMon, err := netmon.New(func(format string, args ...any) {
|
netMon, err := netmon.New(func(format string, args ...any) {
|
||||||
logf(format, args...)
|
logf(format, args...)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("netmon.New: %w", err)
|
return fmt.Errorf("netmon.New: %w", err)
|
||||||
}
|
}
|
||||||
|
sys.Set(netMon)
|
||||||
|
|
||||||
pol := logpolicy.New(logtail.CollectionNode, netMon)
|
pol := logpolicy.New(logtail.CollectionNode, netMon)
|
||||||
pol.SetVerbosityLevel(args.verbose)
|
pol.SetVerbosityLevel(args.verbose)
|
||||||
@ -386,10 +391,10 @@ func run() error {
|
|||||||
debugMux = newDebugMux()
|
debugMux = newDebugMux()
|
||||||
}
|
}
|
||||||
|
|
||||||
return startIPNServer(context.Background(), logf, pol.PublicID, netMon)
|
return startIPNServer(context.Background(), logf, pol.PublicID, sys)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) error {
|
func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID, sys *tsd.System) error {
|
||||||
ln, err := safesocket.Listen(args.socketpath)
|
ln, err := safesocket.Listen(args.socketpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("safesocket.Listen: %v", err)
|
return fmt.Errorf("safesocket.Listen: %v", err)
|
||||||
@ -415,7 +420,7 @@ func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID,
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
srv := ipnserver.New(logf, logID, netMon)
|
srv := ipnserver.New(logf, logID, sys.NetMon.Get())
|
||||||
if debugMux != nil {
|
if debugMux != nil {
|
||||||
debugMux.HandleFunc("/debug/ipn", srv.ServeHTMLStatus)
|
debugMux.HandleFunc("/debug/ipn", srv.ServeHTMLStatus)
|
||||||
}
|
}
|
||||||
@ -433,7 +438,7 @@ func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lb, err := getLocalBackend(ctx, logf, logID, netMon)
|
lb, err := getLocalBackend(ctx, logf, logID, sys)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logf("got LocalBackend in %v", time.Since(t0).Round(time.Millisecond))
|
logf("got LocalBackend in %v", time.Since(t0).Round(time.Millisecond))
|
||||||
srv.SetLocalBackend(lb)
|
srv.SetLocalBackend(lb)
|
||||||
@ -457,31 +462,28 @@ func startIPNServer(ctx context.Context, logf logger.Logf, logID logid.PublicID,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) (_ *ipnlocal.LocalBackend, retErr error) {
|
func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID, sys *tsd.System) (_ *ipnlocal.LocalBackend, retErr error) {
|
||||||
if logPol != nil {
|
if logPol != nil {
|
||||||
logPol.Logtail.SetNetMon(netMon)
|
logPol.Logtail.SetNetMon(sys.NetMon.Get())
|
||||||
}
|
}
|
||||||
|
|
||||||
socksListener, httpProxyListener := mustStartProxyListeners(args.socksAddr, args.httpProxyAddr)
|
socksListener, httpProxyListener := mustStartProxyListeners(args.socksAddr, args.httpProxyAddr)
|
||||||
|
|
||||||
dialer := &tsdial.Dialer{Logf: logf} // mutated below (before used)
|
dialer := &tsdial.Dialer{Logf: logf} // mutated below (before used)
|
||||||
e, onlyNetstack, err := createEngine(logf, netMon, dialer)
|
sys.Set(dialer)
|
||||||
|
|
||||||
|
onlyNetstack, err := createEngine(logf, sys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("createEngine: %w", err)
|
return nil, fmt.Errorf("createEngine: %w", err)
|
||||||
}
|
}
|
||||||
if _, ok := e.(wgengine.ResolvingEngine).GetResolver(); !ok {
|
|
||||||
panic("internal error: exit node resolver not wired up")
|
|
||||||
}
|
|
||||||
if debugMux != nil {
|
if debugMux != nil {
|
||||||
if ig, ok := e.(wgengine.InternalsGetter); ok {
|
if ms, ok := sys.MagicSock.GetOK(); ok {
|
||||||
if _, mc, _, ok := ig.GetInternals(); ok {
|
debugMux.HandleFunc("/debug/magicsock", ms.ServeHTTPDebug)
|
||||||
debugMux.HandleFunc("/debug/magicsock", mc.ServeHTTPDebug)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
go runDebugServer(debugMux, args.debug)
|
go runDebugServer(debugMux, args.debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
ns, err := newNetstack(logf, dialer, e)
|
ns, err := newNetstack(logf, sys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("newNetstack: %w", err)
|
return nil, fmt.Errorf("newNetstack: %w", err)
|
||||||
}
|
}
|
||||||
@ -489,6 +491,7 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID
|
|||||||
ns.ProcessSubnets = onlyNetstack || handleSubnetsInNetstack()
|
ns.ProcessSubnets = onlyNetstack || handleSubnetsInNetstack()
|
||||||
|
|
||||||
if onlyNetstack {
|
if onlyNetstack {
|
||||||
|
e := sys.Engine.Get()
|
||||||
dialer.UseNetstackForIP = func(ip netip.Addr) bool {
|
dialer.UseNetstackForIP = func(ip netip.Addr) bool {
|
||||||
_, ok := e.PeerForIP(ip)
|
_, ok := e.PeerForIP(ip)
|
||||||
return ok
|
return ok
|
||||||
@ -519,16 +522,15 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID
|
|||||||
tshttpproxy.SetSelfProxy(addrs...)
|
tshttpproxy.SetSelfProxy(addrs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
e = wgengine.NewWatchdog(e)
|
|
||||||
|
|
||||||
opts := ipnServerOpts()
|
opts := ipnServerOpts()
|
||||||
|
|
||||||
store, err := store.New(logf, statePathOrDefault())
|
store, err := store.New(logf, statePathOrDefault())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("store.New: %w", err)
|
return nil, fmt.Errorf("store.New: %w", err)
|
||||||
}
|
}
|
||||||
|
sys.Set(store)
|
||||||
|
|
||||||
lb, err := ipnlocal.NewLocalBackend(logf, logID, store, dialer, e, opts.LoginFlags)
|
lb, err := ipnlocal.NewLocalBackend(logf, logID, sys, opts.LoginFlags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ipnlocal.NewLocalBackend: %w", err)
|
return nil, fmt.Errorf("ipnlocal.NewLocalBackend: %w", err)
|
||||||
}
|
}
|
||||||
@ -554,21 +556,21 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logID logid.PublicID
|
|||||||
//
|
//
|
||||||
// onlyNetstack is true if the user has explicitly requested that we use netstack
|
// onlyNetstack is true if the user has explicitly requested that we use netstack
|
||||||
// for all networking.
|
// for all networking.
|
||||||
func createEngine(logf logger.Logf, netMon *netmon.Monitor, dialer *tsdial.Dialer) (e wgengine.Engine, onlyNetstack bool, err error) {
|
func createEngine(logf logger.Logf, sys *tsd.System) (onlyNetstack bool, err error) {
|
||||||
if args.tunname == "" {
|
if args.tunname == "" {
|
||||||
return nil, false, errors.New("no --tun value specified")
|
return false, errors.New("no --tun value specified")
|
||||||
}
|
}
|
||||||
var errs []error
|
var errs []error
|
||||||
for _, name := range strings.Split(args.tunname, ",") {
|
for _, name := range strings.Split(args.tunname, ",") {
|
||||||
logf("wgengine.NewUserspaceEngine(tun %q) ...", name)
|
logf("wgengine.NewUserspaceEngine(tun %q) ...", name)
|
||||||
e, onlyNetstack, err = tryEngine(logf, netMon, dialer, name)
|
onlyNetstack, err = tryEngine(logf, sys, name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return e, onlyNetstack, nil
|
return onlyNetstack, nil
|
||||||
}
|
}
|
||||||
logf("wgengine.NewUserspaceEngine(tun %q) error: %v", name, err)
|
logf("wgengine.NewUserspaceEngine(tun %q) error: %v", name, err)
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
return nil, false, multierr.New(errs...)
|
return false, multierr.New(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleSubnetsInNetstack reports whether netstack should handle subnet routers
|
// handleSubnetsInNetstack reports whether netstack should handle subnet routers
|
||||||
@ -593,21 +595,23 @@ func handleSubnetsInNetstack() bool {
|
|||||||
|
|
||||||
var tstunNew = tstun.New
|
var tstunNew = tstun.New
|
||||||
|
|
||||||
func tryEngine(logf logger.Logf, netMon *netmon.Monitor, dialer *tsdial.Dialer, name string) (e wgengine.Engine, onlyNetstack bool, err error) {
|
func tryEngine(logf logger.Logf, sys *tsd.System, name string) (onlyNetstack bool, err error) {
|
||||||
conf := wgengine.Config{
|
conf := wgengine.Config{
|
||||||
ListenPort: args.port,
|
ListenPort: args.port,
|
||||||
NetMon: netMon,
|
NetMon: sys.NetMon.Get(),
|
||||||
Dialer: dialer,
|
Dialer: sys.Dialer.Get(),
|
||||||
|
SetSubsystem: sys.Set,
|
||||||
}
|
}
|
||||||
|
|
||||||
onlyNetstack = name == "userspace-networking"
|
onlyNetstack = name == "userspace-networking"
|
||||||
|
netstackSubnetRouter := onlyNetstack // but mutated later on some platforms
|
||||||
netns.SetEnabled(!onlyNetstack)
|
netns.SetEnabled(!onlyNetstack)
|
||||||
|
|
||||||
if args.birdSocketPath != "" && createBIRDClient != nil {
|
if args.birdSocketPath != "" && createBIRDClient != nil {
|
||||||
log.Printf("Connecting to BIRD at %s ...", args.birdSocketPath)
|
log.Printf("Connecting to BIRD at %s ...", args.birdSocketPath)
|
||||||
conf.BIRDClient, err = createBIRDClient(args.birdSocketPath)
|
conf.BIRDClient, err = createBIRDClient(args.birdSocketPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("createBIRDClient: %w", err)
|
return false, fmt.Errorf("createBIRDClient: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if onlyNetstack {
|
if onlyNetstack {
|
||||||
@ -620,44 +624,55 @@ func tryEngine(logf logger.Logf, netMon *netmon.Monitor, dialer *tsdial.Dialer,
|
|||||||
// 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, "") // empty interface name
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("dns.NewOSConfigurator: %w", err)
|
return false, fmt.Errorf("dns.NewOSConfigurator: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dev, devName, err := tstunNew(logf, name)
|
dev, devName, err := tstunNew(logf, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tstun.Diagnose(logf, name, err)
|
tstun.Diagnose(logf, name, err)
|
||||||
return nil, false, fmt.Errorf("tstun.New(%q): %w", name, err)
|
return false, fmt.Errorf("tstun.New(%q): %w", name, err)
|
||||||
}
|
}
|
||||||
conf.Tun = dev
|
conf.Tun = dev
|
||||||
if strings.HasPrefix(name, "tap:") {
|
if strings.HasPrefix(name, "tap:") {
|
||||||
conf.IsTAP = true
|
conf.IsTAP = true
|
||||||
e, err := wgengine.NewUserspaceEngine(logf, conf)
|
e, err := wgengine.NewUserspaceEngine(logf, conf)
|
||||||
return e, false, err
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
sys.Set(e)
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := router.New(logf, dev, netMon)
|
r, err := router.New(logf, dev, sys.NetMon.Get())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dev.Close()
|
dev.Close()
|
||||||
return nil, false, fmt.Errorf("creating router: %w", err)
|
return false, fmt.Errorf("creating router: %w", err)
|
||||||
}
|
}
|
||||||
|
sys.Set(r)
|
||||||
|
|
||||||
d, err := dns.NewOSConfigurator(logf, devName)
|
d, err := dns.NewOSConfigurator(logf, devName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dev.Close()
|
dev.Close()
|
||||||
r.Close()
|
r.Close()
|
||||||
return nil, false, fmt.Errorf("dns.NewOSConfigurator: %w", err)
|
return false, fmt.Errorf("dns.NewOSConfigurator: %w", err)
|
||||||
}
|
}
|
||||||
conf.DNS = d
|
conf.DNS = d
|
||||||
conf.Router = r
|
conf.Router = r
|
||||||
if handleSubnetsInNetstack() {
|
if handleSubnetsInNetstack() {
|
||||||
conf.Router = netstack.NewSubnetRouterWrapper(conf.Router)
|
conf.Router = netstack.NewSubnetRouterWrapper(conf.Router)
|
||||||
|
netstackSubnetRouter = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e, err = wgengine.NewUserspaceEngine(logf, conf)
|
e, err := wgengine.NewUserspaceEngine(logf, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, onlyNetstack, err
|
return onlyNetstack, err
|
||||||
}
|
}
|
||||||
return e, onlyNetstack, nil
|
e = wgengine.NewWatchdog(e)
|
||||||
|
sys.Set(e)
|
||||||
|
sys.NetstackRouter.Set(netstackSubnetRouter)
|
||||||
|
|
||||||
|
return onlyNetstack, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDebugMux() *http.ServeMux {
|
func newDebugMux() *http.ServeMux {
|
||||||
@ -687,12 +702,8 @@ func runDebugServer(mux *http.ServeMux, addr string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNetstack(logf logger.Logf, dialer *tsdial.Dialer, e wgengine.Engine) (*netstack.Impl, error) {
|
func newNetstack(logf logger.Logf, sys *tsd.System) (*netstack.Impl, error) {
|
||||||
tunDev, magicConn, dns, ok := e.(wgengine.InternalsGetter).GetInternals()
|
return netstack.Create(logf, sys.Tun.Get(), sys.Engine.Get(), sys.MagicSock.Get(), sys.Dialer.Get(), sys.DNSManager.Get())
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("%T is not a wgengine.InternalsGetter", e)
|
|
||||||
}
|
|
||||||
return netstack.Create(logf, tunDev, e, magicConn, dialer, dns)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mustStartProxyListeners creates listeners for local SOCKS and HTTP
|
// mustStartProxyListeners creates listeners for local SOCKS and HTTP
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
"tailscale.com/net/dns"
|
"tailscale.com/net/dns"
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
"tailscale.com/net/tstun"
|
"tailscale.com/net/tstun"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/logid"
|
"tailscale.com/types/logid"
|
||||||
"tailscale.com/util/winutil"
|
"tailscale.com/util/winutil"
|
||||||
@ -292,13 +293,15 @@ func beWindowsSubprocess() bool {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
sys := new(tsd.System)
|
||||||
netMon, err := netmon.New(log.Printf)
|
netMon, err := netmon.New(log.Printf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Could not create netMon: %v", err)
|
log.Fatalf("Could not create netMon: %v", err)
|
||||||
netMon = nil
|
|
||||||
}
|
}
|
||||||
|
sys.Set(netMon)
|
||||||
|
|
||||||
publicLogID, _ := logid.ParsePublicID(logID)
|
publicLogID, _ := logid.ParsePublicID(logID)
|
||||||
err = startIPNServer(ctx, log.Printf, publicLogID, netMon)
|
err = startIPNServer(ctx, log.Printf, publicLogID, sys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("ipnserver: %v", err)
|
log.Fatalf("ipnserver: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
"tailscale.com/safesocket"
|
"tailscale.com/safesocket"
|
||||||
"tailscale.com/smallzstd"
|
"tailscale.com/smallzstd"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
"tailscale.com/wgengine/netstack"
|
"tailscale.com/wgengine/netstack"
|
||||||
"tailscale.com/words"
|
"tailscale.com/words"
|
||||||
@ -96,6 +97,8 @@ func newIPN(jsConfig js.Value) map[string]any {
|
|||||||
logtail := logtail.NewLogger(c, log.Printf)
|
logtail := logtail.NewLogger(c, log.Printf)
|
||||||
logf := logtail.Logf
|
logf := logtail.Logf
|
||||||
|
|
||||||
|
sys := new(tsd.System)
|
||||||
|
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,
|
||||||
@ -103,12 +106,9 @@ func newIPN(jsConfig js.Value) map[string]any {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
sys.Set(eng)
|
||||||
|
|
||||||
tunDev, magicConn, dnsManager, ok := eng.(wgengine.InternalsGetter).GetInternals()
|
ns, err := netstack.Create(logf, sys.Tun.Get(), eng, sys.MagicSock.Get(), dialer, sys.DNSManager.Get())
|
||||||
if !ok {
|
|
||||||
log.Fatalf("%T is not a wgengine.InternalsGetter", eng)
|
|
||||||
}
|
|
||||||
ns, err := netstack.Create(logf, tunDev, eng, magicConn, dialer, dnsManager)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("netstack.Create: %v", err)
|
log.Fatalf("netstack.Create: %v", err)
|
||||||
}
|
}
|
||||||
@ -121,10 +121,11 @@ func newIPN(jsConfig js.Value) map[string]any {
|
|||||||
dialer.NetstackDialTCP = func(ctx context.Context, dst netip.AddrPort) (net.Conn, error) {
|
dialer.NetstackDialTCP = func(ctx context.Context, dst netip.AddrPort) (net.Conn, error) {
|
||||||
return ns.DialContextTCP(ctx, dst)
|
return ns.DialContextTCP(ctx, dst)
|
||||||
}
|
}
|
||||||
|
sys.NetstackRouter.Set(true)
|
||||||
|
|
||||||
logid := lpc.PublicID
|
logid := lpc.PublicID
|
||||||
srv := ipnserver.New(logf, logid, nil /* no netMon */)
|
srv := ipnserver.New(logf, logid, nil /* no netMon */)
|
||||||
lb, err := ipnlocal.NewLocalBackend(logf, logid, store, dialer, eng, controlclient.LoginEphemeral)
|
lb, err := ipnlocal.NewLocalBackend(logf, logid, sys, controlclient.LoginEphemeral)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("ipnlocal.NewLocalBackend: %v", err)
|
log.Fatalf("ipnlocal.NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
"tailscale.com/syncs"
|
"tailscale.com/syncs"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/tka"
|
"tailscale.com/tka"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/types/dnstype"
|
"tailscale.com/types/dnstype"
|
||||||
"tailscale.com/types/empty"
|
"tailscale.com/types/empty"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
@ -137,10 +138,11 @@ type LocalBackend struct {
|
|||||||
logf logger.Logf // general logging
|
logf logger.Logf // general logging
|
||||||
keyLogf logger.Logf // for printing list of peers on change
|
keyLogf logger.Logf // for printing list of peers on change
|
||||||
statsLogf logger.Logf // for printing peers stats on change
|
statsLogf logger.Logf // for printing peers stats on change
|
||||||
e wgengine.Engine
|
sys *tsd.System
|
||||||
|
e wgengine.Engine // non-nil; TODO(bradfitz): remove; use sys
|
||||||
pm *profileManager
|
pm *profileManager
|
||||||
store ipn.StateStore
|
store ipn.StateStore // non-nil; TODO(bradfitz): remove; use sys
|
||||||
dialer *tsdial.Dialer // non-nil
|
dialer *tsdial.Dialer // non-nil; TODO(bradfitz): remove; use sys
|
||||||
backendLogID logid.PublicID
|
backendLogID logid.PublicID
|
||||||
unregisterNetMon func()
|
unregisterNetMon func()
|
||||||
unregisterHealthWatch func()
|
unregisterHealthWatch func()
|
||||||
@ -267,10 +269,10 @@ type LocalBackend struct {
|
|||||||
// but is not actually running.
|
// but is not actually running.
|
||||||
//
|
//
|
||||||
// If dialer is nil, a new one is made.
|
// If dialer is nil, a new one is made.
|
||||||
func NewLocalBackend(logf logger.Logf, logID logid.PublicID, store ipn.StateStore, dialer *tsdial.Dialer, e wgengine.Engine, loginFlags controlclient.LoginFlags) (*LocalBackend, error) {
|
func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, loginFlags controlclient.LoginFlags) (*LocalBackend, error) {
|
||||||
if e == nil {
|
e := sys.Engine.Get()
|
||||||
panic("ipn.NewLocalBackend: engine must not be nil")
|
store := sys.StateStore.Get()
|
||||||
}
|
dialer := sys.Dialer.Get()
|
||||||
|
|
||||||
pm, err := newProfileManager(store, logf)
|
pm, err := newProfileManager(store, logf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -301,10 +303,11 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, store ipn.StateStor
|
|||||||
logf: logf,
|
logf: logf,
|
||||||
keyLogf: logger.LogOnChange(logf, 5*time.Minute, time.Now),
|
keyLogf: logger.LogOnChange(logf, 5*time.Minute, time.Now),
|
||||||
statsLogf: logger.LogOnChange(logf, 5*time.Minute, time.Now),
|
statsLogf: logger.LogOnChange(logf, 5*time.Minute, time.Now),
|
||||||
|
sys: sys,
|
||||||
e: e,
|
e: e,
|
||||||
pm: pm,
|
|
||||||
store: store,
|
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
|
store: store,
|
||||||
|
pm: pm,
|
||||||
backendLogID: logID,
|
backendLogID: logID,
|
||||||
state: ipn.NoState,
|
state: ipn.NoState,
|
||||||
portpoll: portpoll,
|
portpoll: portpoll,
|
||||||
@ -313,7 +316,8 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, store ipn.StateStor
|
|||||||
loginFlags: loginFlags,
|
loginFlags: loginFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
b.sockstatLogger, err = sockstatlog.NewLogger(logpolicy.LogsDir(logf), logf, logID, e.GetNetMon())
|
netMon := sys.NetMon.Get()
|
||||||
|
b.sockstatLogger, err = sockstatlog.NewLogger(logpolicy.LogsDir(logf), logf, logID, netMon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error setting up sockstat logger: %v", err)
|
log.Printf("error setting up sockstat logger: %v", err)
|
||||||
}
|
}
|
||||||
@ -330,7 +334,6 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, store ipn.StateStor
|
|||||||
b.statusChanged = sync.NewCond(&b.statusLock)
|
b.statusChanged = sync.NewCond(&b.statusLock)
|
||||||
b.e.SetStatusCallback(b.setWgengineStatus)
|
b.e.SetStatusCallback(b.setWgengineStatus)
|
||||||
|
|
||||||
netMon := e.GetNetMon()
|
|
||||||
b.prevIfState = netMon.InterfaceState()
|
b.prevIfState = netMon.InterfaceState()
|
||||||
// Call our linkChange code once with the current state, and
|
// Call our linkChange code once with the current state, and
|
||||||
// then also whenever it changes:
|
// then also whenever it changes:
|
||||||
@ -339,14 +342,9 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, store ipn.StateStor
|
|||||||
|
|
||||||
b.unregisterHealthWatch = health.RegisterWatcher(b.onHealthChange)
|
b.unregisterHealthWatch = health.RegisterWatcher(b.onHealthChange)
|
||||||
|
|
||||||
wiredPeerAPIPort := false
|
if tunWrap, ok := b.sys.Tun.GetOK(); ok {
|
||||||
if ig, ok := e.(wgengine.InternalsGetter); ok {
|
tunWrap.PeerAPIPort = b.GetPeerAPIPort
|
||||||
if tunWrap, _, _, ok := ig.GetInternals(); ok {
|
} else {
|
||||||
tunWrap.PeerAPIPort = b.GetPeerAPIPort
|
|
||||||
wiredPeerAPIPort = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !wiredPeerAPIPort {
|
|
||||||
b.logf("[unexpected] failed to wire up PeerAPI port for engine %T", e)
|
b.logf("[unexpected] failed to wire up PeerAPI port for engine %T", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,6 +462,7 @@ func (b *LocalBackend) GetComponentDebugLogging(component string) time.Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dialer returns the backend's dialer.
|
// Dialer returns the backend's dialer.
|
||||||
|
// It is always non-nil.
|
||||||
func (b *LocalBackend) Dialer() *tsdial.Dialer {
|
func (b *LocalBackend) Dialer() *tsdial.Dialer {
|
||||||
return b.dialer
|
return b.dialer
|
||||||
}
|
}
|
||||||
@ -644,7 +643,7 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func
|
|||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
sb.MutateStatus(func(s *ipnstate.Status) {
|
sb.MutateStatus(func(s *ipnstate.Status) {
|
||||||
s.Version = version.Long()
|
s.Version = version.Long()
|
||||||
s.TUN = !wgengine.IsNetstack(b.e)
|
s.TUN = !b.sys.IsNetstack()
|
||||||
s.BackendState = b.state.String()
|
s.BackendState = b.state.String()
|
||||||
s.AuthURL = b.authURLSticky
|
s.AuthURL = b.authURLSticky
|
||||||
if err := health.OverallError(); err != nil {
|
if err := health.OverallError(); err != nil {
|
||||||
@ -1315,8 +1314,8 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
hostinfo := hostinfo.New()
|
hostinfo := hostinfo.New()
|
||||||
hostinfo.BackendLogID = b.backendLogID.String()
|
hostinfo.BackendLogID = b.backendLogID.String()
|
||||||
hostinfo.FrontendLogID = opts.FrontendLogID
|
hostinfo.FrontendLogID = opts.FrontendLogID
|
||||||
hostinfo.Userspace.Set(wgengine.IsNetstack(b.e))
|
hostinfo.Userspace.Set(b.sys.IsNetstack())
|
||||||
hostinfo.UserspaceRouter.Set(wgengine.IsNetstackRouter(b.e))
|
hostinfo.UserspaceRouter.Set(b.sys.IsNetstackRouter())
|
||||||
|
|
||||||
if b.cc != nil {
|
if b.cc != nil {
|
||||||
// TODO(apenwarr): avoid the need to reinit controlclient.
|
// TODO(apenwarr): avoid the need to reinit controlclient.
|
||||||
@ -1401,7 +1400,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
isNetstack := wgengine.IsNetstackRouter(b.e)
|
isNetstack := b.sys.IsNetstackRouter()
|
||||||
debugFlags := controlDebugFlags
|
debugFlags := controlDebugFlags
|
||||||
if isNetstack {
|
if isNetstack {
|
||||||
debugFlags = append([]string{"netstack"}, debugFlags...)
|
debugFlags = append([]string{"netstack"}, debugFlags...)
|
||||||
@ -1423,7 +1422,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
HTTPTestClient: httpTestClient,
|
HTTPTestClient: httpTestClient,
|
||||||
DiscoPublicKey: discoPublic,
|
DiscoPublicKey: discoPublic,
|
||||||
DebugFlags: debugFlags,
|
DebugFlags: debugFlags,
|
||||||
NetMon: b.e.GetNetMon(),
|
NetMon: b.sys.NetMon.Get(),
|
||||||
Pinger: b,
|
Pinger: b,
|
||||||
PopBrowserURL: b.tellClientToBrowseToURL,
|
PopBrowserURL: b.tellClientToBrowseToURL,
|
||||||
OnClientVersion: b.onClientVersion,
|
OnClientVersion: b.onClientVersion,
|
||||||
@ -3317,14 +3316,12 @@ func (b *LocalBackend) initPeerAPIListener() {
|
|||||||
directFileMode: b.directFileRoot != "",
|
directFileMode: b.directFileRoot != "",
|
||||||
directFileDoFinalRename: b.directFileDoFinalRename,
|
directFileDoFinalRename: b.directFileDoFinalRename,
|
||||||
}
|
}
|
||||||
if re, ok := b.e.(wgengine.ResolvingEngine); ok {
|
if dm, ok := b.sys.DNSManager.GetOK(); ok {
|
||||||
if r, ok := re.GetResolver(); ok {
|
ps.resolver = dm.Resolver()
|
||||||
ps.resolver = r
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
b.peerAPIServer = ps
|
b.peerAPIServer = ps
|
||||||
|
|
||||||
isNetstack := wgengine.IsNetstack(b.e)
|
isNetstack := b.sys.IsNetstack()
|
||||||
for i, a := range b.netMap.Addresses {
|
for i, a := range b.netMap.Addresses {
|
||||||
var ln net.Listener
|
var ln net.Listener
|
||||||
var err error
|
var err error
|
||||||
@ -4040,7 +4037,7 @@ func (b *LocalBackend) setTCPPortsInterceptedFromNetmapAndPrefsLocked(prefs ipn.
|
|||||||
b.setServeProxyHandlersLocked()
|
b.setServeProxyHandlersLocked()
|
||||||
|
|
||||||
// don't listen on netmap addresses if we're in userspace mode
|
// don't listen on netmap addresses if we're in userspace mode
|
||||||
if !wgengine.IsNetstack(b.e) {
|
if !b.sys.IsNetstack() {
|
||||||
b.updateServeTCPPortNetMapAddrListenersLocked(servePorts)
|
b.updateServeTCPPortNetMapAddrListenersLocked(servePorts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4391,7 +4388,7 @@ func nodeIP(n *tailcfg.Node, pred func(netip.Addr) bool) netip.Addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) CheckIPForwarding() error {
|
func (b *LocalBackend) CheckIPForwarding() error {
|
||||||
if wgengine.IsNetstackRouter(b.e) {
|
if b.sys.IsNetstackRouter() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4537,13 +4534,9 @@ func (b *LocalBackend) DebugReSTUN() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) magicConn() (*magicsock.Conn, error) {
|
func (b *LocalBackend) magicConn() (*magicsock.Conn, error) {
|
||||||
ig, ok := b.e.(wgengine.InternalsGetter)
|
mc, ok := b.sys.MagicSock.GetOK()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("engine isn't InternalsGetter")
|
return nil, errors.New("failed to get magicsock from sys")
|
||||||
}
|
|
||||||
_, mc, _, ok := ig.GetInternals()
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("failed to get internals")
|
|
||||||
}
|
}
|
||||||
return mc, nil
|
return mc, nil
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"tailscale.com/net/interfaces"
|
"tailscale.com/net/interfaces"
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
@ -501,13 +502,16 @@ func TestLazyMachineKeyGeneration(t *testing.T) {
|
|||||||
tstest.Replace(t, &panicOnMachineKeyGeneration, func() bool { return true })
|
tstest.Replace(t, &panicOnMachineKeyGeneration, func() bool { return true })
|
||||||
|
|
||||||
var logf logger.Logf = logger.Discard
|
var logf logger.Logf = logger.Discard
|
||||||
|
sys := new(tsd.System)
|
||||||
store := new(mem.Store)
|
store := new(mem.Store)
|
||||||
eng, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
sys.Set(store)
|
||||||
|
eng, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
||||||
}
|
}
|
||||||
t.Cleanup(eng.Close)
|
t.Cleanup(eng.Close)
|
||||||
lb, err := NewLocalBackend(logf, logid.PublicID{}, store, nil, eng, 0)
|
sys.Set(eng)
|
||||||
|
lb, err := NewLocalBackend(logf, logid.PublicID{}, sys, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewLocalBackend: %v", err)
|
t.Fatalf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
@ -765,13 +769,16 @@ func TestPacketFilterPermitsUnlockedNodes(t *testing.T) {
|
|||||||
func TestStatusWithoutPeers(t *testing.T) {
|
func TestStatusWithoutPeers(t *testing.T) {
|
||||||
logf := tstest.WhileTestRunningLogger(t)
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
store := new(testStateStorage)
|
store := new(testStateStorage)
|
||||||
e, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
sys := new(tsd.System)
|
||||||
|
sys.Set(store)
|
||||||
|
e, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
||||||
}
|
}
|
||||||
|
sys.Set(e)
|
||||||
t.Cleanup(e.Close)
|
t.Cleanup(e.Close)
|
||||||
|
|
||||||
b, err := NewLocalBackend(logf, logid.PublicID{}, store, nil, e, 0)
|
b, err := NewLocalBackend(logf, logid.PublicID{}, sys, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewLocalBackend: %v", err)
|
t.Fatalf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/ipn/store/mem"
|
"tailscale.com/ipn/store/mem"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
@ -47,14 +48,17 @@ func TestLocalLogLines(t *testing.T) {
|
|||||||
idA := logid(0xaa)
|
idA := logid(0xaa)
|
||||||
|
|
||||||
// set up a LocalBackend, super bare bones. No functional data.
|
// set up a LocalBackend, super bare bones. No functional data.
|
||||||
|
sys := new(tsd.System)
|
||||||
store := new(mem.Store)
|
store := new(mem.Store)
|
||||||
e, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
sys.Set(store)
|
||||||
|
e, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Cleanup(e.Close)
|
t.Cleanup(e.Close)
|
||||||
|
sys.Set(e)
|
||||||
|
|
||||||
lb, err := NewLocalBackend(logf, idA, store, nil, e, 0)
|
lb, err := NewLocalBackend(logf, idA, sys, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,6 @@
|
|||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
"tailscale.com/util/multierr"
|
"tailscale.com/util/multierr"
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
"tailscale.com/wgengine"
|
|
||||||
"tailscale.com/wgengine/filter"
|
"tailscale.com/wgengine/filter"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -469,7 +468,7 @@ func (s *peerAPIServer) listen(ip netip.Addr, ifState *interfaces.State) (ln net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if wgengine.IsNetstack(s.b.e) {
|
if s.b.sys.IsNetstack() {
|
||||||
ipStr = ""
|
ipStr = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1239,12 +1238,9 @@ func (h *peerAPIHandler) handleServeMagicsock(w http.ResponseWriter, r *http.Req
|
|||||||
http.Error(w, "denied; no debug access", http.StatusForbidden)
|
http.Error(w, "denied; no debug access", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
eng := h.ps.b.e
|
if mc, ok := h.ps.b.sys.MagicSock.GetOK(); ok {
|
||||||
if ig, ok := eng.(wgengine.InternalsGetter); ok {
|
mc.ServeHTTPDebug(w, r)
|
||||||
if _, mc, _, ok := ig.GetInternals(); ok {
|
return
|
||||||
mc.ServeHTTPDebug(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
http.Error(w, "miswired", 500)
|
http.Error(w, "miswired", 500)
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ func (s *serveListener) Run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *serveListener) shouldWarnAboutListenError(err error) bool {
|
func (s *serveListener) shouldWarnAboutListenError(err error) bool {
|
||||||
if !s.b.e.GetNetMon().InterfaceState().HasIP(s.ap.Addr()) {
|
if !s.b.sys.NetMon.Get().InterfaceState().HasIP(s.ap.Addr()) {
|
||||||
// Machine likely doesn't have IPv6 enabled (or the IP is still being
|
// Machine likely doesn't have IPv6 enabled (or the IP is still being
|
||||||
// assigned). No need to warn. Notably, WSL2 (Issue 6303).
|
// assigned). No need to warn. Notably, WSL2 (Issue 6303).
|
||||||
return false
|
return false
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/store/mem"
|
"tailscale.com/ipn/store/mem"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
"tailscale.com/types/empty"
|
"tailscale.com/types/empty"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
@ -297,14 +298,17 @@ func TestStateMachine(t *testing.T) {
|
|||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
|
|
||||||
logf := tstest.WhileTestRunningLogger(t)
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
|
sys := new(tsd.System)
|
||||||
store := new(testStateStorage)
|
store := new(testStateStorage)
|
||||||
e, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
sys.Set(store)
|
||||||
|
e, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
||||||
}
|
}
|
||||||
t.Cleanup(e.Close)
|
t.Cleanup(e.Close)
|
||||||
|
sys.Set(e)
|
||||||
|
|
||||||
b, err := NewLocalBackend(logf, logid.PublicID{}, store, nil, e, 0)
|
b, err := NewLocalBackend(logf, logid.PublicID{}, sys, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewLocalBackend: %v", err)
|
t.Fatalf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
@ -941,13 +945,16 @@ func TestStateMachine(t *testing.T) {
|
|||||||
|
|
||||||
func TestEditPrefsHasNoKeys(t *testing.T) {
|
func TestEditPrefsHasNoKeys(t *testing.T) {
|
||||||
logf := tstest.WhileTestRunningLogger(t)
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
e, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
sys := new(tsd.System)
|
||||||
|
sys.Set(new(mem.Store))
|
||||||
|
e, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
t.Fatalf("NewFakeUserspaceEngine: %v", err)
|
||||||
}
|
}
|
||||||
t.Cleanup(e.Close)
|
t.Cleanup(e.Close)
|
||||||
|
sys.Set(e)
|
||||||
|
|
||||||
b, err := NewLocalBackend(logf, logid.PublicID{}, new(mem.Store), nil, e, 0)
|
b, err := NewLocalBackend(logf, logid.PublicID{}, sys, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewLocalBackend: %v", err)
|
t.Fatalf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
@ -1023,10 +1030,14 @@ func TestWGEngineStatusRace(t *testing.T) {
|
|||||||
t.Skip("test fails")
|
t.Skip("test fails")
|
||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
logf := tstest.WhileTestRunningLogger(t)
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
eng, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
sys := new(tsd.System)
|
||||||
|
sys.Set(new(mem.Store))
|
||||||
|
|
||||||
|
eng, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set)
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
t.Cleanup(eng.Close)
|
t.Cleanup(eng.Close)
|
||||||
b, err := NewLocalBackend(logf, logid.PublicID{}, new(mem.Store), nil, eng, 0)
|
sys.Set(eng)
|
||||||
|
b, err := NewLocalBackend(logf, logid.PublicID{}, sys, 0)
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
|
|
||||||
var cc *mockControl
|
var cc *mockControl
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
type Server struct {
|
type Server struct {
|
||||||
lb atomic.Pointer[ipnlocal.LocalBackend]
|
lb atomic.Pointer[ipnlocal.LocalBackend]
|
||||||
logf logger.Logf
|
logf logger.Logf
|
||||||
netMon *netmon.Monitor // optional; nil means interfaces will be looked up on-demand
|
netMon *netmon.Monitor // must be non-nil
|
||||||
backendLogID logid.PublicID
|
backendLogID logid.PublicID
|
||||||
// resetOnZero is whether to call bs.Reset on transition from
|
// resetOnZero is whether to call bs.Reset on transition from
|
||||||
// 1->0 active HTTP requests. That is, this is whether the backend is
|
// 1->0 active HTTP requests. That is, this is whether the backend is
|
||||||
@ -410,14 +410,15 @@ func (s *Server) addActiveHTTPRequest(req *http.Request, ci *ipnauth.ConnIdentit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Server.
|
// New returns a new Server.
|
||||||
// The netMon parameter is optional; if non-nil it's used to do faster interface
|
|
||||||
// lookups.
|
|
||||||
//
|
//
|
||||||
// To start it, use the Server.Run method.
|
// To start it, use the Server.Run method.
|
||||||
//
|
//
|
||||||
// At some point, either before or after Run, the Server's SetLocalBackend
|
// At some point, either before or after Run, the Server's SetLocalBackend
|
||||||
// method must also be called before Server can do anything useful.
|
// method must also be called before Server can do anything useful.
|
||||||
func New(logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) *Server {
|
func New(logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) *Server {
|
||||||
|
if netMon == nil {
|
||||||
|
panic("nil netMon")
|
||||||
|
}
|
||||||
return &Server{
|
return &Server{
|
||||||
backendLogID: logID,
|
backendLogID: logID,
|
||||||
logf: logf,
|
logf: logf,
|
||||||
|
@ -47,8 +47,11 @@ func (t *fakeTUN) Write(b [][]byte, n int) (int, error) {
|
|||||||
return 1, nil
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FakeTUNName is the name of the fake TUN device.
|
||||||
|
const FakeTUNName = "FakeTUN"
|
||||||
|
|
||||||
func (t *fakeTUN) Flush() error { return nil }
|
func (t *fakeTUN) Flush() error { return nil }
|
||||||
func (t *fakeTUN) MTU() (int, error) { return 1500, nil }
|
func (t *fakeTUN) MTU() (int, error) { return 1500, nil }
|
||||||
func (t *fakeTUN) Name() (string, error) { return "FakeTUN", nil }
|
func (t *fakeTUN) Name() (string, error) { return FakeTUNName, nil }
|
||||||
func (t *fakeTUN) Events() <-chan tun.Event { return t.evchan }
|
func (t *fakeTUN) Events() <-chan tun.Event { return t.evchan }
|
||||||
func (t *fakeTUN) BatchSize() int { return 1 }
|
func (t *fakeTUN) BatchSize() int { return 1 }
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
"tailscale.com/net/tsdial"
|
"tailscale.com/net/tsdial"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/tempfork/gliderlabs/ssh"
|
"tailscale.com/tempfork/gliderlabs/ssh"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/logid"
|
"tailscale.com/types/logid"
|
||||||
@ -815,14 +816,14 @@ func TestSSHAuthFlow(t *testing.T) {
|
|||||||
|
|
||||||
func TestSSH(t *testing.T) {
|
func TestSSH(t *testing.T) {
|
||||||
var logf logger.Logf = t.Logf
|
var logf logger.Logf = t.Logf
|
||||||
eng, err := wgengine.NewFakeUserspaceEngine(logf, 0)
|
sys := &tsd.System{}
|
||||||
|
eng, err := wgengine.NewFakeUserspaceEngine(logf, sys.Set)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
lb, err := ipnlocal.NewLocalBackend(logf, logid.PublicID{},
|
sys.Set(eng)
|
||||||
new(mem.Store),
|
sys.Set(new(mem.Store))
|
||||||
new(tsdial.Dialer),
|
lb, err := ipnlocal.NewLocalBackend(logf, logid.PublicID{}, sys, 0)
|
||||||
eng, 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
135
tsd/tsd.go
Normal file
135
tsd/tsd.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// 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/ipn"
|
||||||
|
"tailscale.com/net/dns"
|
||||||
|
"tailscale.com/net/netmon"
|
||||||
|
"tailscale.com/net/tsdial"
|
||||||
|
"tailscale.com/net/tstun"
|
||||||
|
"tailscale.com/wgengine"
|
||||||
|
"tailscale.com/wgengine/magicsock"
|
||||||
|
"tailscale.com/wgengine/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
// System contains all the subsystems of a Tailscale node (tailscaled, etc.)
|
||||||
|
type System struct {
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *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:
|
||||||
|
s.Tun.Set(v)
|
||||||
|
case *magicsock.Conn:
|
||||||
|
s.MagicSock.Set(v)
|
||||||
|
case ipn.StateStore:
|
||||||
|
s.StateStore.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 {
|
||||||
|
name, _ := s.Tun.Get().Name()
|
||||||
|
return name == tstun.FakeTUNName
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
@ -47,6 +47,7 @@
|
|||||||
"tailscale.com/net/socks5"
|
"tailscale.com/net/socks5"
|
||||||
"tailscale.com/net/tsdial"
|
"tailscale.com/net/tsdial"
|
||||||
"tailscale.com/smallzstd"
|
"tailscale.com/smallzstd"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/logid"
|
"tailscale.com/types/logid"
|
||||||
"tailscale.com/types/nettype"
|
"tailscale.com/types/nettype"
|
||||||
@ -482,23 +483,21 @@ 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: 0,
|
ListenPort: 0,
|
||||||
NetMon: s.netMon,
|
NetMon: s.netMon,
|
||||||
Dialer: s.dialer,
|
Dialer: s.dialer,
|
||||||
|
SetSubsystem: sys.Set,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
closePool.add(s.dialer)
|
closePool.add(s.dialer)
|
||||||
|
sys.Set(eng)
|
||||||
|
|
||||||
tunDev, magicConn, dns, ok := eng.(wgengine.InternalsGetter).GetInternals()
|
ns, err := netstack.Create(logf, sys.Tun.Get(), eng, sys.MagicSock.Get(), s.dialer, sys.DNSManager.Get())
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("%T is not a wgengine.InternalsGetter", eng)
|
|
||||||
}
|
|
||||||
|
|
||||||
ns, err := netstack.Create(logf, tunDev, eng, magicConn, s.dialer, dns)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("netstack.Create: %w", err)
|
return fmt.Errorf("netstack.Create: %w", err)
|
||||||
}
|
}
|
||||||
@ -522,12 +521,13 @@ func (s *Server) start() (reterr error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sys.Set(s.Store)
|
||||||
|
|
||||||
loginFlags := controlclient.LoginDefault
|
loginFlags := controlclient.LoginDefault
|
||||||
if s.Ephemeral {
|
if s.Ephemeral {
|
||||||
loginFlags = controlclient.LoginEphemeral
|
loginFlags = controlclient.LoginEphemeral
|
||||||
}
|
}
|
||||||
lb, err := ipnlocal.NewLocalBackend(logf, s.logid, s.Store, s.dialer, eng, loginFlags)
|
lb, err := ipnlocal.NewLocalBackend(logf, s.logid, sys, loginFlags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("NewLocalBackend: %v", err)
|
return fmt.Errorf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
_ "tailscale.com/ssh/tailssh"
|
_ "tailscale.com/ssh/tailssh"
|
||||||
_ "tailscale.com/syncs"
|
_ "tailscale.com/syncs"
|
||||||
_ "tailscale.com/tailcfg"
|
_ "tailscale.com/tailcfg"
|
||||||
|
_ "tailscale.com/tsd"
|
||||||
_ "tailscale.com/tsweb/varz"
|
_ "tailscale.com/tsweb/varz"
|
||||||
_ "tailscale.com/types/flagtype"
|
_ "tailscale.com/types/flagtype"
|
||||||
_ "tailscale.com/types/key"
|
_ "tailscale.com/types/key"
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
_ "tailscale.com/ssh/tailssh"
|
_ "tailscale.com/ssh/tailssh"
|
||||||
_ "tailscale.com/syncs"
|
_ "tailscale.com/syncs"
|
||||||
_ "tailscale.com/tailcfg"
|
_ "tailscale.com/tailcfg"
|
||||||
|
_ "tailscale.com/tsd"
|
||||||
_ "tailscale.com/tsweb/varz"
|
_ "tailscale.com/tsweb/varz"
|
||||||
_ "tailscale.com/types/flagtype"
|
_ "tailscale.com/types/flagtype"
|
||||||
_ "tailscale.com/types/key"
|
_ "tailscale.com/types/key"
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
_ "tailscale.com/ssh/tailssh"
|
_ "tailscale.com/ssh/tailssh"
|
||||||
_ "tailscale.com/syncs"
|
_ "tailscale.com/syncs"
|
||||||
_ "tailscale.com/tailcfg"
|
_ "tailscale.com/tailcfg"
|
||||||
|
_ "tailscale.com/tsd"
|
||||||
_ "tailscale.com/tsweb/varz"
|
_ "tailscale.com/tsweb/varz"
|
||||||
_ "tailscale.com/types/flagtype"
|
_ "tailscale.com/types/flagtype"
|
||||||
_ "tailscale.com/types/key"
|
_ "tailscale.com/types/key"
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
_ "tailscale.com/ssh/tailssh"
|
_ "tailscale.com/ssh/tailssh"
|
||||||
_ "tailscale.com/syncs"
|
_ "tailscale.com/syncs"
|
||||||
_ "tailscale.com/tailcfg"
|
_ "tailscale.com/tailcfg"
|
||||||
|
_ "tailscale.com/tsd"
|
||||||
_ "tailscale.com/tsweb/varz"
|
_ "tailscale.com/tsweb/varz"
|
||||||
_ "tailscale.com/types/flagtype"
|
_ "tailscale.com/types/flagtype"
|
||||||
_ "tailscale.com/types/key"
|
_ "tailscale.com/types/key"
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
_ "tailscale.com/smallzstd"
|
_ "tailscale.com/smallzstd"
|
||||||
_ "tailscale.com/syncs"
|
_ "tailscale.com/syncs"
|
||||||
_ "tailscale.com/tailcfg"
|
_ "tailscale.com/tailcfg"
|
||||||
|
_ "tailscale.com/tsd"
|
||||||
_ "tailscale.com/tsweb/varz"
|
_ "tailscale.com/tsweb/varz"
|
||||||
_ "tailscale.com/types/flagtype"
|
_ "tailscale.com/types/flagtype"
|
||||||
_ "tailscale.com/types/key"
|
_ "tailscale.com/types/key"
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/net/tsdial"
|
"tailscale.com/net/tsdial"
|
||||||
"tailscale.com/net/tstun"
|
"tailscale.com/net/tstun"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
"tailscale.com/types/ipproto"
|
"tailscale.com/types/ipproto"
|
||||||
"tailscale.com/types/logid"
|
"tailscale.com/types/logid"
|
||||||
@ -33,29 +34,26 @@ func TestInjectInboundLeak(t *testing.T) {
|
|||||||
t.Logf(format, args...)
|
t.Logf(format, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sys := new(tsd.System)
|
||||||
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
|
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
|
||||||
Tun: tunDev,
|
Tun: tunDev,
|
||||||
Dialer: dialer,
|
Dialer: dialer,
|
||||||
|
SetSubsystem: sys.Set,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer eng.Close()
|
defer eng.Close()
|
||||||
ig, ok := eng.(wgengine.InternalsGetter)
|
sys.Set(eng)
|
||||||
if !ok {
|
sys.Set(new(mem.Store))
|
||||||
t.Fatal("not an InternalsGetter")
|
|
||||||
}
|
|
||||||
tunWrap, magicSock, dns, ok := ig.GetInternals()
|
|
||||||
if !ok {
|
|
||||||
t.Fatal("failed to get internals")
|
|
||||||
}
|
|
||||||
|
|
||||||
lb, err := ipnlocal.NewLocalBackend(logf, logid.PublicID{}, new(mem.Store), dialer, eng, 0)
|
tunWrap := sys.Tun.Get()
|
||||||
|
lb, err := ipnlocal.NewLocalBackend(logf, logid.PublicID{}, sys, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ns, err := Create(logf, tunWrap, eng, magicSock, dialer, dns)
|
ns, err := Create(logf, tunWrap, eng, sys.MagicSock.Get(), dialer, sys.DNSManager.Get())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -89,32 +87,28 @@ func getMemStats() (ms runtime.MemStats) {
|
|||||||
|
|
||||||
func makeNetstack(t *testing.T, config func(*Impl)) *Impl {
|
func makeNetstack(t *testing.T, config func(*Impl)) *Impl {
|
||||||
tunDev := tstun.NewFake()
|
tunDev := tstun.NewFake()
|
||||||
|
sys := &tsd.System{}
|
||||||
|
sys.Set(new(mem.Store))
|
||||||
dialer := new(tsdial.Dialer)
|
dialer := new(tsdial.Dialer)
|
||||||
logf := tstest.WhileTestRunningLogger(t)
|
logf := tstest.WhileTestRunningLogger(t)
|
||||||
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
|
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
|
||||||
Tun: tunDev,
|
Tun: tunDev,
|
||||||
Dialer: dialer,
|
Dialer: dialer,
|
||||||
|
SetSubsystem: sys.Set,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Cleanup(func() { eng.Close() })
|
t.Cleanup(func() { eng.Close() })
|
||||||
ig, ok := eng.(wgengine.InternalsGetter)
|
sys.Set(eng)
|
||||||
if !ok {
|
|
||||||
t.Fatal("not an InternalsGetter")
|
|
||||||
}
|
|
||||||
tunWrap, magicSock, dns, ok := ig.GetInternals()
|
|
||||||
if !ok {
|
|
||||||
t.Fatal("failed to get internals")
|
|
||||||
}
|
|
||||||
|
|
||||||
ns, err := Create(logf, tunWrap, eng, magicSock, dialer, dns)
|
ns, err := Create(logf, sys.Tun.Get(), eng, sys.MagicSock.Get(), dialer, sys.DNSManager.Get())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Cleanup(func() { ns.Close() })
|
t.Cleanup(func() { ns.Close() })
|
||||||
|
|
||||||
lb, err := ipnlocal.NewLocalBackend(logf, logid.PublicID{}, new(mem.Store), dialer, eng, 0)
|
lb, err := ipnlocal.NewLocalBackend(logf, logid.PublicID{}, sys, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewLocalBackend: %v", err)
|
t.Fatalf("NewLocalBackend: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,9 @@
|
|||||||
package netstack
|
package netstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"tailscale.com/wgengine"
|
|
||||||
"tailscale.com/wgengine/router"
|
"tailscale.com/wgengine/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
wgengine.NetstackRouterType = reflect.TypeOf(&subnetRouter{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type subnetRouter struct {
|
type subnetRouter struct {
|
||||||
router.Router
|
router.Router
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"reflect"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -25,7 +25,6 @@
|
|||||||
"tailscale.com/health"
|
"tailscale.com/health"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/net/dns"
|
"tailscale.com/net/dns"
|
||||||
"tailscale.com/net/dns/resolver"
|
|
||||||
"tailscale.com/net/flowtrack"
|
"tailscale.com/net/flowtrack"
|
||||||
"tailscale.com/net/interfaces"
|
"tailscale.com/net/interfaces"
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
@ -150,29 +149,6 @@ type userspaceEngine struct {
|
|||||||
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
||||||
}
|
}
|
||||||
|
|
||||||
// InternalsGetter is implemented by Engines that can export their internals.
|
|
||||||
type InternalsGetter interface {
|
|
||||||
GetInternals() (_ *tstun.Wrapper, _ *magicsock.Conn, _ *dns.Manager, ok bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *userspaceEngine) GetInternals() (_ *tstun.Wrapper, _ *magicsock.Conn, _ *dns.Manager, ok bool) {
|
|
||||||
return e.tundev, e.magicConn, e.dns, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolvingEngine is implemented by Engines that have DNS resolvers.
|
|
||||||
type ResolvingEngine interface {
|
|
||||||
GetResolver() (_ *resolver.Resolver, ok bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ ResolvingEngine = (*userspaceEngine)(nil)
|
|
||||||
_ ResolvingEngine = (*watchdogEngine)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (e *userspaceEngine) GetResolver() (r *resolver.Resolver, ok bool) {
|
|
||||||
return e.dns.Resolver(), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// BIRDClient handles communication with the BIRD Internet Routing Daemon.
|
// BIRDClient handles communication with the BIRD Internet Routing Daemon.
|
||||||
type BIRDClient interface {
|
type BIRDClient interface {
|
||||||
EnableProtocol(proto string) error
|
EnableProtocol(proto string) error
|
||||||
@ -219,47 +195,37 @@ type Config struct {
|
|||||||
// BIRDClient, if non-nil, will be used to configure BIRD whenever
|
// BIRDClient, if non-nil, will be used to configure BIRD whenever
|
||||||
// this node is a primary subnet router.
|
// this node is a primary subnet router.
|
||||||
BIRDClient BIRDClient
|
BIRDClient BIRDClient
|
||||||
|
|
||||||
|
// SetSubsystem, if non-nil, is called for each new subsystem created, just before a successful return.
|
||||||
|
SetSubsystem func(any)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16) (Engine, error) {
|
// NewFakeUserspaceEngine returns a new userspace engine for testing.
|
||||||
logf("Starting userspace WireGuard engine (with fake TUN device)")
|
//
|
||||||
return NewUserspaceEngine(logf, Config{
|
// The opts may contain the following types:
|
||||||
ListenPort: listenPort,
|
//
|
||||||
|
// - int or uint16: to set the ListenPort.
|
||||||
|
func NewFakeUserspaceEngine(logf logger.Logf, opts ...any) (Engine, error) {
|
||||||
|
conf := Config{
|
||||||
RespondToPing: true,
|
RespondToPing: true,
|
||||||
})
|
}
|
||||||
}
|
for _, o := range opts {
|
||||||
|
switch v := o.(type) {
|
||||||
// NetstackRouterType is a gross cross-package init-time registration
|
case uint16:
|
||||||
// from netstack to here, informing this package of netstack's router
|
conf.ListenPort = v
|
||||||
// type.
|
case int:
|
||||||
var NetstackRouterType reflect.Type
|
if v < 0 || v > math.MaxUint16 {
|
||||||
|
return nil, fmt.Errorf("invalid ListenPort: %d", v)
|
||||||
// IsNetstackRouter reports whether e is either fully netstack based
|
}
|
||||||
// (without TUN) or is at least using netstack for routing.
|
conf.ListenPort = uint16(v)
|
||||||
func IsNetstackRouter(e Engine) bool {
|
case func(any):
|
||||||
switch e := e.(type) {
|
conf.SetSubsystem = v
|
||||||
case *userspaceEngine:
|
default:
|
||||||
if reflect.TypeOf(e.router) == NetstackRouterType {
|
return nil, fmt.Errorf("unknown option type %T", v)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
case *watchdogEngine:
|
|
||||||
return IsNetstackRouter(e.wrap)
|
|
||||||
}
|
}
|
||||||
return IsNetstack(e)
|
logf("Starting userspace WireGuard engine (with fake TUN device)")
|
||||||
}
|
return NewUserspaceEngine(logf, conf)
|
||||||
|
|
||||||
// IsNetstack reports whether e is a netstack-based TUN-free engine.
|
|
||||||
func IsNetstack(e Engine) bool {
|
|
||||||
ig, ok := e.(InternalsGetter)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
tw, _, _, ok := ig.GetInternals()
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
name, err := tw.Name()
|
|
||||||
return err == nil && name == "FakeTUN"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserspaceEngine creates the named tun device and returns a
|
// NewUserspaceEngine creates the named tun device and returns a
|
||||||
@ -458,6 +424,15 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
|
|||||||
e.logf("Starting network monitor...")
|
e.logf("Starting network monitor...")
|
||||||
e.netMon.Start()
|
e.netMon.Start()
|
||||||
|
|
||||||
|
if conf.SetSubsystem != nil {
|
||||||
|
conf.SetSubsystem(e.tundev)
|
||||||
|
conf.SetSubsystem(e.magicConn)
|
||||||
|
conf.SetSubsystem(e.dns)
|
||||||
|
conf.SetSubsystem(conf.Router)
|
||||||
|
conf.SetSubsystem(conf.Dialer)
|
||||||
|
conf.SetSubsystem(e.netMon)
|
||||||
|
}
|
||||||
|
|
||||||
e.logf("Engine created.")
|
e.logf("Engine created.")
|
||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
@ -1119,10 +1094,6 @@ func (e *userspaceEngine) Wait() {
|
|||||||
<-e.waitCh
|
<-e.waitCh
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *userspaceEngine) GetNetMon() *netmon.Monitor {
|
|
||||||
return e.netMon
|
|
||||||
}
|
|
||||||
|
|
||||||
// LinkChange signals a network change event. It's currently
|
// LinkChange signals a network change event. It's currently
|
||||||
// (2021-03-03) only called on Android. On other platforms, netMon
|
// (2021-03-03) only called on Android. On other platforms, netMon
|
||||||
// generates link change events for us.
|
// generates link change events for us.
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
"github.com/tailscale/wireguard-go/tun"
|
"github.com/tailscale/wireguard-go/tun"
|
||||||
"tailscale.com/net/tstun"
|
"tailscale.com/net/tstun"
|
||||||
|
"tailscale.com/tsd"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
"tailscale.com/wgengine/netstack"
|
"tailscale.com/wgengine/netstack"
|
||||||
@ -15,21 +16,23 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestIsNetstack(t *testing.T) {
|
func TestIsNetstack(t *testing.T) {
|
||||||
e, err := wgengine.NewUserspaceEngine(t.Logf, wgengine.Config{})
|
sys := new(tsd.System)
|
||||||
|
e, err := wgengine.NewUserspaceEngine(t.Logf, wgengine.Config{SetSubsystem: sys.Set})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer e.Close()
|
defer e.Close()
|
||||||
if !wgengine.IsNetstack(e) {
|
if !sys.IsNetstack() {
|
||||||
t.Errorf("IsNetstack = false; want true")
|
t.Errorf("IsNetstack = false; want true")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsNetstackRouter(t *testing.T) {
|
func TestIsNetstackRouter(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
conf wgengine.Config
|
conf wgengine.Config
|
||||||
want bool
|
setNetstackRouter bool
|
||||||
|
want bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no_netstack",
|
name: "no_netstack",
|
||||||
@ -50,23 +53,26 @@ func TestIsNetstackRouter(t *testing.T) {
|
|||||||
Tun: newFakeOSTUN(),
|
Tun: newFakeOSTUN(),
|
||||||
Router: netstack.NewSubnetRouterWrapper(newFakeOSRouter()),
|
Router: netstack.NewSubnetRouterWrapper(newFakeOSRouter()),
|
||||||
},
|
},
|
||||||
want: true,
|
setNetstackRouter: true,
|
||||||
|
want: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
e, err := wgengine.NewUserspaceEngine(logger.Discard, tt.conf)
|
sys := &tsd.System{}
|
||||||
|
if tt.setNetstackRouter {
|
||||||
|
sys.NetstackRouter.Set(true)
|
||||||
|
}
|
||||||
|
conf := tt.conf
|
||||||
|
conf.SetSubsystem = sys.Set
|
||||||
|
e, err := wgengine.NewUserspaceEngine(logger.Discard, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer e.Close()
|
defer e.Close()
|
||||||
if got := wgengine.IsNetstackRouter(e); got != tt.want {
|
if got := sys.IsNetstackRouter(); got != tt.want {
|
||||||
t.Errorf("IsNetstackRouter = %v; want %v", got, tt.want)
|
t.Errorf("IsNetstackRouter = %v; want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
|
|
||||||
if got := wgengine.IsNetstackRouter(wgengine.NewWatchdog(e)); got != tt.want {
|
|
||||||
t.Errorf("IsNetstackRouter(watchdog-wrapped) = %v; want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,11 @@
|
|||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/net/dns"
|
"tailscale.com/net/dns"
|
||||||
"tailscale.com/net/dns/resolver"
|
|
||||||
"tailscale.com/net/netmon"
|
|
||||||
"tailscale.com/net/tstun"
|
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/netmap"
|
"tailscale.com/types/netmap"
|
||||||
"tailscale.com/wgengine/capture"
|
"tailscale.com/wgengine/capture"
|
||||||
"tailscale.com/wgengine/filter"
|
"tailscale.com/wgengine/filter"
|
||||||
"tailscale.com/wgengine/magicsock"
|
|
||||||
"tailscale.com/wgengine/router"
|
"tailscale.com/wgengine/router"
|
||||||
"tailscale.com/wgengine/wgcfg"
|
"tailscale.com/wgengine/wgcfg"
|
||||||
)
|
)
|
||||||
@ -126,9 +122,6 @@ func (e *watchdogEngine) watchdog(name string, fn func()) {
|
|||||||
func (e *watchdogEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *dns.Config, debug *tailcfg.Debug) error {
|
func (e *watchdogEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *dns.Config, debug *tailcfg.Debug) error {
|
||||||
return e.watchdogErr("Reconfig", func() error { return e.wrap.Reconfig(cfg, routerCfg, dnsCfg, debug) })
|
return e.watchdogErr("Reconfig", func() error { return e.wrap.Reconfig(cfg, routerCfg, dnsCfg, debug) })
|
||||||
}
|
}
|
||||||
func (e *watchdogEngine) GetNetMon() *netmon.Monitor {
|
|
||||||
return e.wrap.GetNetMon()
|
|
||||||
}
|
|
||||||
func (e *watchdogEngine) GetFilter() *filter.Filter {
|
func (e *watchdogEngine) GetFilter() *filter.Filter {
|
||||||
return e.wrap.GetFilter()
|
return e.wrap.GetFilter()
|
||||||
}
|
}
|
||||||
@ -181,18 +174,6 @@ func (e *watchdogEngine) WhoIsIPPort(ipp netip.AddrPort) (tsIP netip.Addr, ok bo
|
|||||||
func (e *watchdogEngine) Close() {
|
func (e *watchdogEngine) Close() {
|
||||||
e.watchdog("Close", e.wrap.Close)
|
e.watchdog("Close", e.wrap.Close)
|
||||||
}
|
}
|
||||||
func (e *watchdogEngine) GetInternals() (tw *tstun.Wrapper, c *magicsock.Conn, d *dns.Manager, ok bool) {
|
|
||||||
if ig, ok := e.wrap.(InternalsGetter); ok {
|
|
||||||
return ig.GetInternals()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
func (e *watchdogEngine) GetResolver() (r *resolver.Resolver, ok bool) {
|
|
||||||
if re, ok := e.wrap.(ResolvingEngine); ok {
|
|
||||||
return re.GetResolver()
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
func (e *watchdogEngine) PeerForIP(ip netip.Addr) (ret PeerForIP, ok bool) {
|
func (e *watchdogEngine) PeerForIP(ip netip.Addr) (ret PeerForIP, ok bool) {
|
||||||
e.watchdog("PeerForIP", func() { ret, ok = e.wrap.PeerForIP(ip) })
|
e.watchdog("PeerForIP", func() { ret, ok = e.wrap.PeerForIP(ip) })
|
||||||
return ret, ok
|
return ret, ok
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/net/dns"
|
"tailscale.com/net/dns"
|
||||||
"tailscale.com/net/netmon"
|
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/netmap"
|
"tailscale.com/types/netmap"
|
||||||
@ -92,9 +91,6 @@ type Engine interface {
|
|||||||
// WireGuard status changes.
|
// WireGuard status changes.
|
||||||
SetStatusCallback(StatusCallback)
|
SetStatusCallback(StatusCallback)
|
||||||
|
|
||||||
// GetNetMon returns the network monitor.
|
|
||||||
GetNetMon() *netmon.Monitor
|
|
||||||
|
|
||||||
// RequestStatus requests a WireGuard status update right
|
// RequestStatus requests a WireGuard status update right
|
||||||
// away, sent to the callback registered via SetStatusCallback.
|
// away, sent to the callback registered via SetStatusCallback.
|
||||||
RequestStatus()
|
RequestStatus()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user