diff --git a/cmd/tailscale/cli/web.go b/cmd/tailscale/cli/web.go index 235b0bb56..536febe40 100644 --- a/cmd/tailscale/cli/web.go +++ b/cmd/tailscale/cli/web.go @@ -58,6 +58,7 @@ type tmplData struct { AdvertiseExitNode bool AdvertiseRoutes string LicensesURL string + TUNMode bool } var webCmd = &ffcli.Command{ @@ -338,7 +339,7 @@ func webHandler(w http.ResponseWriter, r *http.Request) { return } - st, err := localClient.Status(ctx) + st, err := localClient.StatusWithoutPeers(ctx) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -408,6 +409,7 @@ func webHandler(w http.ResponseWriter, r *http.Request) { Status: st.BackendState, DeviceName: deviceName, LicensesURL: licensesURL(), + TUNMode: st.TUN, } exitNodeRouteV4 := netip.MustParsePrefix("0.0.0.0/0") exitNodeRouteV6 := netip.MustParsePrefix("::/0") diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index a0847230c..64c48bff8 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -579,6 +579,7 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func defer b.mu.Unlock() sb.MutateStatus(func(s *ipnstate.Status) { s.Version = version.Long + s.TUN = !wgengine.IsNetstack(b.e) s.BackendState = b.state.String() s.AuthURL = b.authURLSticky if err := health.OverallError(); err != nil { diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go index 40362e8cf..3250fc1dc 100644 --- a/ipn/ipnstate/ipnstate.go +++ b/ipn/ipnstate/ipnstate.go @@ -31,6 +31,10 @@ type Status struct { // Version is the daemon's long version (see version.Long). Version string + // TUN is whether /dev/net/tun (or equivalent kernel interface) is being + // used. If false, it's running in userspace mode. + TUN bool + // BackendState is an ipn.State string value: // "NoState", "NeedsLogin", "NeedsMachineAuth", "Stopped", // "Starting", "Running".