diff --git a/cmd/tailscaled/tailscaled.go b/cmd/tailscaled/tailscaled.go index e0aaa72e8..33da24e04 100644 --- a/cmd/tailscaled/tailscaled.go +++ b/cmd/tailscaled/tailscaled.go @@ -308,6 +308,8 @@ func run() error { socksListener, httpProxyListener := mustStartProxyListeners(args.socksAddr, args.httpProxyAddr) dialer := new(tsdial.Dialer) // mutated below (before used) + dialer.SetLinkMonitor(linkMon) + e, useNetstack, err := createEngine(logf, linkMon, dialer) if err != nil { logf("wgengine.New: %v", err) diff --git a/cmd/tailscaled/tailscaled_windows.go b/cmd/tailscaled/tailscaled_windows.go index a6c9f8c04..73f918c64 100644 --- a/cmd/tailscaled/tailscaled_windows.go +++ b/cmd/tailscaled/tailscaled_windows.go @@ -39,6 +39,7 @@ "tailscale.com/version" "tailscale.com/wf" "tailscale.com/wgengine" + "tailscale.com/wgengine/monitor" "tailscale.com/wgengine/netstack" "tailscale.com/wgengine/router" ) @@ -177,6 +178,11 @@ func beFirewallKillswitch() bool { func startIPNServer(ctx context.Context, logid string) error { var logf logger.Logf = log.Printf + linkMon, err := monitor.New(logf) + if err != nil { + return err + } + getEngineRaw := func() (wgengine.Engine, error) { dev, devName, err := tstun.New(logf, "Tailscale") if err != nil { @@ -197,10 +203,11 @@ func startIPNServer(ctx context.Context, logid string) error { return nil, fmt.Errorf("DNS: %w", err) } eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{ - Tun: dev, - Router: r, - DNS: d, - ListenPort: 41641, + Tun: dev, + Router: r, + DNS: d, + ListenPort: 41641, + LinkMonitor: linkMon, }) if err != nil { r.Close() @@ -287,7 +294,7 @@ type engineOrError struct { return fmt.Errorf("safesocket.Listen: %v", err) } - err = ipnserver.Run(ctx, logf, ln, store, logid, getEngine, ipnServerOpts()) + err = ipnserver.Run(ctx, logf, ln, store, linkMon, logid, getEngine, ipnServerOpts()) if err != nil { logf("ipnserver.Run: %v", err) } diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 5c005460e..3951f46a7 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -2185,17 +2185,9 @@ func (b *LocalBackend) initPeerAPIListener() { return } - var tunName string - if ge, ok := b.e.(wgengine.InternalsGetter); ok { - if tunWrap, _, ok := ge.GetInternals(); ok { - tunName, _ = tunWrap.Name() - } - } - ps := &peerAPIServer{ b: b, rootDir: fileRoot, - tunName: tunName, selfNode: selfNode, directFileMode: b.directFileRoot != "", } diff --git a/ipn/ipnlocal/peerapi.go b/ipn/ipnlocal/peerapi.go index 6d90f94a3..2146eba38 100644 --- a/ipn/ipnlocal/peerapi.go +++ b/ipn/ipnlocal/peerapi.go @@ -49,7 +49,6 @@ type peerAPIServer struct { b *LocalBackend rootDir string - tunName string selfNode *tailcfg.Node knownEmpty syncs.AtomicBool resolver *resolver.Resolver @@ -363,7 +362,7 @@ func (s *peerAPIServer) listen(ip netaddr.IP, ifState *interfaces.State) (ln net // On iOS/macOS, this sets the lc.Control hook to // setsockopt the interface index to bind to, to get // out of the network sandbox. - if err := initListenConfig(&lc, ip, ifState, s.tunName); err != nil { + if err := initListenConfig(&lc, ip, ifState, s.b.dialer.TUNName()); err != nil { return nil, err } if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { diff --git a/ipn/ipnserver/server.go b/ipn/ipnserver/server.go index 1c5d272f4..49134e00c 100644 --- a/ipn/ipnserver/server.go +++ b/ipn/ipnserver/server.go @@ -50,6 +50,7 @@ "tailscale.com/version" "tailscale.com/version/distro" "tailscale.com/wgengine" + "tailscale.com/wgengine/monitor" ) // Options is the configuration of the Tailscale node agent. @@ -653,7 +654,7 @@ func StateStore(path string, logf logger.Logf) (ipn.StateStore, error) { // The getEngine func is called repeatedly, once per connection, until it returns an engine successfully. // // Deprecated: use New and Server.Run instead. -func Run(ctx context.Context, logf logger.Logf, ln net.Listener, store ipn.StateStore, logid string, getEngine func() (wgengine.Engine, error), opts Options) error { +func Run(ctx context.Context, logf logger.Logf, ln net.Listener, store ipn.StateStore, linkMon *monitor.Mon, logid string, getEngine func() (wgengine.Engine, error), opts Options) error { getEngine = getEngineUntilItWorksWrapper(getEngine) runDone := make(chan struct{}) defer close(runDone) @@ -738,6 +739,7 @@ func Run(ctx context.Context, logf logger.Logf, ln net.Listener, store ipn.State } dialer := new(tsdial.Dialer) + dialer.SetLinkMonitor(linkMon) eng.AddNetworkMapCallback(func(nm *netmap.NetworkMap) { dialer.SetDNSMap(tsdial.DNSMapFromNetworkMap(nm)) }) diff --git a/ipn/ipnserver/server_test.go b/ipn/ipnserver/server_test.go index 3675876c7..790d720f0 100644 --- a/ipn/ipnserver/server_test.go +++ b/ipn/ipnserver/server_test.go @@ -72,6 +72,6 @@ func TestRunMultipleAccepts(t *testing.T) { } defer ln.Close() - err = ipnserver.Run(ctx, logTriggerTestf, ln, store, "dummy_logid", ipnserver.FixedEngine(eng), opts) + err = ipnserver.Run(ctx, logTriggerTestf, ln, store, nil /* mon */, "dummy_logid", ipnserver.FixedEngine(eng), opts) t.Logf("ipnserver.Run = %v", err) } diff --git a/net/tsdial/tsdial.go b/net/tsdial/tsdial.go index dfd403902..cd5002f75 100644 --- a/net/tsdial/tsdial.go +++ b/net/tsdial/tsdial.go @@ -17,6 +17,7 @@ "inet.af/netaddr" "tailscale.com/net/netknob" + "tailscale.com/wgengine/monitor" ) // Dialer dials out of tailscaled, while taking care of details while @@ -37,8 +38,33 @@ type Dialer struct { peerClientOnce sync.Once peerClient *http.Client - mu sync.Mutex - dns DNSMap + mu sync.Mutex + dns DNSMap + tunName string // tun device name + linkMon *monitor.Mon +} + +// SetTUNName sets the name of the tun device in use ("tailscale0", "utun6", +// etc). This is needed on some platforms to set sockopts to bind +// to the same interface index. +func (d *Dialer) SetTUNName(name string) { + d.mu.Lock() + defer d.mu.Unlock() + d.tunName = name +} + +// TUNName returns the name of the tun device in use, if any. +// Example format ("tailscale0", "utun6"). +func (d *Dialer) TUNName() string { + d.mu.Lock() + defer d.mu.Unlock() + return d.tunName +} + +func (d *Dialer) SetLinkMonitor(mon *monitor.Mon) { + d.mu.Lock() + defer d.mu.Unlock() + d.linkMon = mon } // PeerDialControlFunc returns a function diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 3abd798f2..16034ea2d 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -318,6 +318,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) } tunName, _ := conf.Tun.Name() + conf.Dialer.SetTUNName(tunName) e.dns = dns.NewManager(logf, conf.DNS, e.linkMon, conf.Dialer, fwdDNSLinkSelector{e, tunName}) logf("link state: %+v", e.linkMon.InterfaceState())