diff --git a/hostinfo/hostinfo.go b/hostinfo/hostinfo.go index 60cf3edc0..4a16876dc 100644 --- a/hostinfo/hostinfo.go +++ b/hostinfo/hostinfo.go @@ -11,6 +11,7 @@ import ( "io" "os" "runtime" + "runtime/debug" "strings" "sync" "sync/atomic" @@ -46,7 +47,9 @@ func New() *tailcfg.Hostinfo { Desktop: desktop(), Package: packageTypeCached(), GoArch: runtime.GOARCH, + GoArchVar: lazyGoArchVar.Get(), GoVersion: runtime.Version(), + Machine: condCall(unameMachine), DeviceModel: deviceModel(), Cloud: string(cloudenv.Get()), NoLogsNoSupport: envknob.NoLogsNoSupport(), @@ -60,6 +63,7 @@ var ( distroName func() string distroVersion func() string distroCodeName func() string + unameMachine func() string ) func condCall[T any](fn func() T) T { @@ -72,6 +76,7 @@ func condCall[T any](fn func() T) T { var ( lazyInContainer = &lazyAtomicValue[opt.Bool]{f: ptr.To(inContainer)} + lazyGoArchVar = &lazyAtomicValue[string]{f: ptr.To(goArchVar)} ) type lazyAtomicValue[T any] struct { @@ -321,6 +326,27 @@ func inDockerDesktop() bool { return false } +// goArchVar returns the GOARM or GOAMD64 etc value that the binary was built +// with. +func goArchVar() string { + bi, ok := debug.ReadBuildInfo() + if !ok { + return "" + } + // Look for GOARM, GOAMD64, GO386, etc. Note that the little-endian + // "le"-suffixed GOARCH values don't have their own environment variable. + // + // See https://pkg.go.dev/cmd/go#hdr-Environment_variables and the + // "Architecture-specific environment variables" section: + wantKey := "GO" + strings.ToUpper(strings.TrimSuffix(runtime.GOARCH, "le")) + for _, s := range bi.Settings { + if s.Key == wantKey { + return s.Value + } + } + return "" +} + type etcAptSrcResult struct { mod time.Time disabled bool diff --git a/hostinfo/hostinfo_uname.go b/hostinfo/hostinfo_uname.go new file mode 100644 index 000000000..50a92d4fa --- /dev/null +++ b/hostinfo/hostinfo_uname.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux || freebsd || openbsd || darwin + +package hostinfo + +import ( + "runtime" + + "golang.org/x/sys/unix" + "tailscale.com/types/ptr" +) + +func init() { + unameMachine = lazyUnameMachine.Get +} + +var lazyUnameMachine = &lazyAtomicValue[string]{f: ptr.To(unameMachineUnix)} + +func unameMachineUnix() string { + switch runtime.GOOS { + case "android": + // Don't call on Android for now. We're late in the 1.36 release cycle + // and don't want to test syscall filters on various Android versions to + // see what's permitted. Notably, the hostinfo_linux.go file has build + // tag !android, so maybe Uname is verboten. + return "" + case "ios": + // For similar reasons, don't call on iOS. There aren't many iOS devices + // and we know their CPU properties so calling this is only risk and no + // reward. + return "" + } + var un unix.Utsname + unix.Uname(&un) + return unix.ByteSliceToString(un.Machine[:]) +} diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 47936f51f..17617fdc6 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -528,7 +528,9 @@ type Hostinfo struct { ShareeNode bool `json:",omitempty"` // indicates this node exists in netmap because it's owned by a shared-to user NoLogsNoSupport bool `json:",omitempty"` // indicates that the user has opted out of sending logs and support WireIngress bool `json:",omitempty"` // indicates that the node wants the option to receive ingress connections - GoArch string `json:",omitempty"` // the host's GOARCH value (of the running binary) + Machine string `json:",omitempty"` // the current host's machine type (uname -m) + GoArch string `json:",omitempty"` // GOARCH value (of the built binary) + GoArchVar string `json:",omitempty"` // GOARM, GOAMD64, etc (of the built binary) GoVersion string `json:",omitempty"` // Go version binary was built with RoutableIPs []netip.Prefix `json:",omitempty"` // set of IP ranges this client can route RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim diff --git a/tailcfg/tailcfg_clone.go b/tailcfg/tailcfg_clone.go index 5f346ec02..8241e184d 100644 --- a/tailcfg/tailcfg_clone.go +++ b/tailcfg/tailcfg_clone.go @@ -137,7 +137,9 @@ var _HostinfoCloneNeedsRegeneration = Hostinfo(struct { ShareeNode bool NoLogsNoSupport bool WireIngress bool + Machine string GoArch string + GoArchVar string GoVersion string RoutableIPs []netip.Prefix RequestTags []string diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go index 438c8d1cb..d9a364859 100644 --- a/tailcfg/tailcfg_test.go +++ b/tailcfg/tailcfg_test.go @@ -50,7 +50,9 @@ func TestHostinfoEqual(t *testing.T) { "ShareeNode", "NoLogsNoSupport", "WireIngress", + "Machine", "GoArch", + "GoArchVar", "GoVersion", "RoutableIPs", "RequestTags", diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index c957cac82..012a617b8 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -276,7 +276,9 @@ func (v HostinfoView) ShieldsUp() bool { return v.ж.ShieldsUp } func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode } func (v HostinfoView) NoLogsNoSupport() bool { return v.ж.NoLogsNoSupport } func (v HostinfoView) WireIngress() bool { return v.ж.WireIngress } +func (v HostinfoView) Machine() string { return v.ж.Machine } func (v HostinfoView) GoArch() string { return v.ж.GoArch } +func (v HostinfoView) GoArchVar() string { return v.ж.GoArchVar } func (v HostinfoView) GoVersion() string { return v.ж.GoVersion } func (v HostinfoView) RoutableIPs() views.IPPrefixSlice { return views.IPPrefixSliceOf(v.ж.RoutableIPs) @@ -310,7 +312,9 @@ var _HostinfoViewNeedsRegeneration = Hostinfo(struct { ShareeNode bool NoLogsNoSupport bool WireIngress bool + Machine string GoArch string + GoArchVar string GoVersion string RoutableIPs []netip.Prefix RequestTags []string