mirror of
https://github.com/tailscale/tailscale.git
synced 2025-03-28 12:02:23 +00:00
control: make Hostinfo accessed by pointer
Fix potential races in copying aliased slices by value. Also few little doc updates. Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
824f825552
commit
d8de11a01b
@ -148,7 +148,7 @@ func main() {
|
|||||||
c, err := controlclient.New(controlclient.Options{
|
c, err := controlclient.New(controlclient.Options{
|
||||||
Persist: cfg,
|
Persist: cfg,
|
||||||
ServerURL: *server,
|
ServerURL: *server,
|
||||||
Hostinfo: &hi,
|
Hostinfo: hi,
|
||||||
NewDecompressor: func() (controlclient.Decompressor, error) {
|
NewDecompressor: func() (controlclient.Decompressor, error) {
|
||||||
return zstd.NewReader(nil)
|
return zstd.NewReader(nil)
|
||||||
},
|
},
|
||||||
|
@ -47,7 +47,7 @@ func main() {
|
|||||||
c, err := controlclient.New(controlclient.Options{
|
c, err := controlclient.New(controlclient.Options{
|
||||||
Persist: cfg,
|
Persist: cfg,
|
||||||
ServerURL: *server,
|
ServerURL: *server,
|
||||||
Hostinfo: &hi,
|
Hostinfo: hi,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -64,9 +64,9 @@ type Status struct {
|
|||||||
LoginFinished *empty.Message
|
LoginFinished *empty.Message
|
||||||
Err string
|
Err string
|
||||||
URL string
|
URL string
|
||||||
Persist *Persist // locally persisted configuration
|
Persist *Persist // locally persisted configuration
|
||||||
NetMap *NetworkMap // server-pushed configuration
|
NetMap *NetworkMap // server-pushed configuration
|
||||||
Hostinfo tailcfg.Hostinfo // current Hostinfo data
|
Hostinfo *tailcfg.Hostinfo // current Hostinfo data
|
||||||
state state
|
state state
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ type Client struct {
|
|||||||
loggedIn bool // true if currently logged in
|
loggedIn bool // true if currently logged in
|
||||||
loginGoal *LoginGoal // non-nil if some login activity is desired
|
loginGoal *LoginGoal // non-nil if some login activity is desired
|
||||||
synced bool // true if our netmap is up-to-date
|
synced bool // true if our netmap is up-to-date
|
||||||
hostinfo tailcfg.Hostinfo
|
hostinfo *tailcfg.Hostinfo
|
||||||
inPollNetMap bool // true if currently running a PollNetMap
|
inPollNetMap bool // true if currently running a PollNetMap
|
||||||
inSendStatus int // number of sendStatus calls currently in progress
|
inSendStatus int // number of sendStatus calls currently in progress
|
||||||
state state
|
state state
|
||||||
@ -489,7 +489,10 @@ func (c *Client) SetStatusFunc(fn func(Status)) {
|
|||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SetHostinfo(hi tailcfg.Hostinfo) {
|
func (c *Client) SetHostinfo(hi *tailcfg.Hostinfo) {
|
||||||
|
if hi == nil {
|
||||||
|
panic("nil Hostinfo")
|
||||||
|
}
|
||||||
c.direct.SetHostinfo(hi)
|
c.direct.SetHostinfo(hi)
|
||||||
// Send new Hostinfo to server
|
// Send new Hostinfo to server
|
||||||
c.cancelMapSafely()
|
c.cancelMapSafely()
|
||||||
|
@ -85,17 +85,17 @@ type Direct struct {
|
|||||||
persist Persist
|
persist Persist
|
||||||
tryingNewKey wgcfg.PrivateKey
|
tryingNewKey wgcfg.PrivateKey
|
||||||
expiry *time.Time
|
expiry *time.Time
|
||||||
hostinfo tailcfg.Hostinfo
|
hostinfo *tailcfg.Hostinfo // always non-nil
|
||||||
endpoints []string
|
endpoints []string
|
||||||
localPort uint16 // or zero to mean auto
|
localPort uint16 // or zero to mean auto
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Persist Persist // initial persistent data
|
Persist Persist // initial persistent data
|
||||||
HTTPC *http.Client // HTTP client used to talk to tailcontrol
|
HTTPC *http.Client // HTTP client used to talk to tailcontrol
|
||||||
ServerURL string // URL of the tailcontrol server
|
ServerURL string // URL of the tailcontrol server
|
||||||
TimeNow func() time.Time // time.Now implementation used by Client
|
TimeNow func() time.Time // time.Now implementation used by Client
|
||||||
Hostinfo *tailcfg.Hostinfo
|
Hostinfo *tailcfg.Hostinfo // non-nil passes ownership, nil means to use default using os.Hostname, etc
|
||||||
NewDecompressor func() (Decompressor, error)
|
NewDecompressor func() (Decompressor, error)
|
||||||
KeepAlive bool
|
KeepAlive bool
|
||||||
Logf logger.Logf
|
Logf logger.Logf
|
||||||
@ -120,9 +120,9 @@ func NewDirect(opts Options) (*Direct, error) {
|
|||||||
}
|
}
|
||||||
if opts.Logf == nil {
|
if opts.Logf == nil {
|
||||||
// TODO(apenwarr): remove this default and fail instead.
|
// TODO(apenwarr): remove this default and fail instead.
|
||||||
|
// TODO(bradfitz): ... but then it shouldn't be in Options.
|
||||||
opts.Logf = log.Printf
|
opts.Logf = log.Printf
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &Direct{
|
c := &Direct{
|
||||||
httpc: opts.HTTPC,
|
httpc: opts.HTTPC,
|
||||||
serverURL: opts.ServerURL,
|
serverURL: opts.ServerURL,
|
||||||
@ -135,38 +135,46 @@ func NewDirect(opts Options) (*Direct, error) {
|
|||||||
if opts.Hostinfo == nil {
|
if opts.Hostinfo == nil {
|
||||||
c.SetHostinfo(NewHostinfo())
|
c.SetHostinfo(NewHostinfo())
|
||||||
} else {
|
} else {
|
||||||
c.SetHostinfo(*opts.Hostinfo)
|
c.SetHostinfo(opts.Hostinfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHostinfo() tailcfg.Hostinfo {
|
func hostinfoOS() string {
|
||||||
hostname, _ := os.Hostname()
|
|
||||||
os := runtime.GOOS
|
os := runtime.GOOS
|
||||||
switch os {
|
switch os {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
switch runtime.GOARCH {
|
switch runtime.GOARCH {
|
||||||
case "arm", "arm64":
|
case "arm", "arm64":
|
||||||
os = "iOS"
|
return "iOS"
|
||||||
default:
|
default:
|
||||||
os = "macOS"
|
return "macOS"
|
||||||
}
|
}
|
||||||
}
|
default:
|
||||||
|
return os
|
||||||
return tailcfg.Hostinfo{
|
|
||||||
IPNVersion: version.LONG,
|
|
||||||
Hostname: hostname,
|
|
||||||
OS: os,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Direct) SetHostinfo(hi tailcfg.Hostinfo) {
|
func NewHostinfo() *tailcfg.Hostinfo {
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
return &tailcfg.Hostinfo{
|
||||||
|
IPNVersion: version.LONG,
|
||||||
|
Hostname: hostname,
|
||||||
|
OS: hostinfoOS(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHostinfo clones the provided Hostinfo and remembers it for the
|
||||||
|
// next update.
|
||||||
|
func (c *Direct) SetHostinfo(hi *tailcfg.Hostinfo) {
|
||||||
|
if hi == nil {
|
||||||
|
panic("nil Hostinfo")
|
||||||
|
}
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
c.logf("Hostinfo: %v\n", hi)
|
c.logf("Hostinfo: %v\n", hi)
|
||||||
c.hostinfo = hi
|
c.hostinfo = hi.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Direct) GetPersist() Persist {
|
func (c *Direct) GetPersist() Persist {
|
||||||
|
@ -37,8 +37,11 @@ type EngineStatus struct {
|
|||||||
|
|
||||||
type NetworkMap = controlclient.NetworkMap
|
type NetworkMap = controlclient.NetworkMap
|
||||||
|
|
||||||
|
// Notify is a communication from a backend (e.g. tailscaled) to a frontend
|
||||||
|
// (cmd/tailscale, iOS, macOS, Win Tasktray).
|
||||||
// In any given notification, any or all of these may be nil, meaning
|
// In any given notification, any or all of these may be nil, meaning
|
||||||
// that they have not changed.
|
// that they have not changed.
|
||||||
|
// They are JSON-encoded on the wire, despite the lack of struct tags.
|
||||||
type Notify struct {
|
type Notify struct {
|
||||||
Version string // version number of IPN backend
|
Version string // version number of IPN backend
|
||||||
ErrMessage *string // critical error message, if any
|
ErrMessage *string // critical error message, if any
|
||||||
@ -49,6 +52,8 @@ type Notify struct {
|
|||||||
Engine *EngineStatus // wireguard engine stats
|
Engine *EngineStatus // wireguard engine stats
|
||||||
BrowseToURL *string // UI should open a browser right now
|
BrowseToURL *string // UI should open a browser right now
|
||||||
BackendLogID *string // public logtail id used by backend
|
BackendLogID *string // public logtail id used by backend
|
||||||
|
|
||||||
|
// type is mirrored in xcode/Shared/IPN.swift
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateKey is an opaque identifier for a set of LocalBackend state
|
// StateKey is an opaque identifier for a set of LocalBackend state
|
||||||
|
19
ipn/local.go
19
ipn/local.go
@ -24,11 +24,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// LocalBackend is the scaffolding between the Tailscale cloud control
|
// LocalBackend is the scaffolding between the Tailscale cloud control
|
||||||
// plane and the local network stack.
|
// plane and the local network stack, wiring up NetworkMap updates
|
||||||
|
// from the cloud to the local WireGuard engine.
|
||||||
type LocalBackend struct {
|
type LocalBackend struct {
|
||||||
logf logger.Logf
|
logf logger.Logf
|
||||||
notify func(n Notify)
|
notify func(Notify)
|
||||||
c *controlclient.Client
|
c *controlclient.Client // TODO: appears to be (inconsistently) guarded by mu
|
||||||
e wgengine.Engine
|
e wgengine.Engine
|
||||||
store StateStore
|
store StateStore
|
||||||
serverURL string
|
serverURL string
|
||||||
@ -42,7 +43,7 @@ type LocalBackend struct {
|
|||||||
stateKey StateKey
|
stateKey StateKey
|
||||||
prefs *Prefs
|
prefs *Prefs
|
||||||
state State
|
state State
|
||||||
hiCache tailcfg.Hostinfo
|
hiCache *tailcfg.Hostinfo
|
||||||
netMapCache *controlclient.NetworkMap
|
netMapCache *controlclient.NetworkMap
|
||||||
engineStatus EngineStatus
|
engineStatus EngineStatus
|
||||||
endPoints []string
|
endPoints []string
|
||||||
@ -146,7 +147,9 @@ func (b *LocalBackend) Start(opts Options) error {
|
|||||||
hi.FrontendLogID = opts.FrontendLogID
|
hi.FrontendLogID = opts.FrontendLogID
|
||||||
|
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
hi.Services = b.hiCache.Services // keep any previous session
|
if b.hiCache != nil {
|
||||||
|
hi.Services = b.hiCache.Services // keep any previous session
|
||||||
|
}
|
||||||
b.hiCache = hi
|
b.hiCache = hi
|
||||||
b.state = NoState
|
b.state = NoState
|
||||||
|
|
||||||
@ -176,7 +179,7 @@ func (b *LocalBackend) Start(opts Options) error {
|
|||||||
},
|
},
|
||||||
Persist: *persist,
|
Persist: *persist,
|
||||||
ServerURL: b.serverURL,
|
ServerURL: b.serverURL,
|
||||||
Hostinfo: &hi,
|
Hostinfo: hi,
|
||||||
KeepAlive: true,
|
KeepAlive: true,
|
||||||
NewDecompressor: b.newDecompressor,
|
NewDecompressor: b.newDecompressor,
|
||||||
})
|
})
|
||||||
@ -519,12 +522,12 @@ func (b *LocalBackend) SetPrefs(new *Prefs) {
|
|||||||
oldHi := b.hiCache
|
oldHi := b.hiCache
|
||||||
newHi := oldHi.Copy()
|
newHi := oldHi.Copy()
|
||||||
newHi.RoutableIPs = append([]wgcfg.CIDR(nil), b.prefs.AdvertiseRoutes...)
|
newHi.RoutableIPs = append([]wgcfg.CIDR(nil), b.prefs.AdvertiseRoutes...)
|
||||||
b.hiCache = *newHi
|
b.hiCache = newHi
|
||||||
cli := b.c
|
cli := b.c
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
|
|
||||||
if cli != nil && !oldHi.Equal(newHi) {
|
if cli != nil && !oldHi.Equal(newHi) {
|
||||||
cli.SetHostinfo(*newHi)
|
cli.SetHostinfo(newHi)
|
||||||
}
|
}
|
||||||
|
|
||||||
if old.WantRunning != new.WantRunning {
|
if old.WantRunning != new.WantRunning {
|
||||||
|
@ -208,10 +208,14 @@ type Service struct {
|
|||||||
// require changes to Hostinfo.Copy.
|
// require changes to Hostinfo.Copy.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hostinfo contains a summary of a Tailscale host.
|
||||||
|
//
|
||||||
|
// Because it contains pointers (slices), this type should not be used
|
||||||
|
// as a value type.
|
||||||
type Hostinfo struct {
|
type Hostinfo struct {
|
||||||
// TODO(crawshaw): mark all these fields ",omitempty" when all the
|
// TODO(crawshaw): mark all these fields ",omitempty" when all the
|
||||||
// iOS apps are updated with the latest swift version of this struct.
|
// iOS apps are updated with the latest swift version of this struct.
|
||||||
IPNVersion string // version number of this code
|
IPNVersion string // version of this code
|
||||||
FrontendLogID string // logtail ID of frontend instance
|
FrontendLogID string // logtail ID of frontend instance
|
||||||
BackendLogID string // logtail ID of backend instance
|
BackendLogID string // logtail ID of backend instance
|
||||||
OS string // operating system the client runs on
|
OS string // operating system the client runs on
|
||||||
@ -225,12 +229,12 @@ type Hostinfo struct {
|
|||||||
|
|
||||||
// Copy makes a deep copy of Hostinfo.
|
// Copy makes a deep copy of Hostinfo.
|
||||||
// The result aliases no memory with the original.
|
// The result aliases no memory with the original.
|
||||||
func (hinfo *Hostinfo) Copy() (res *Hostinfo) {
|
func (h *Hostinfo) Copy() (res *Hostinfo) {
|
||||||
res = new(Hostinfo)
|
res = new(Hostinfo)
|
||||||
*res = *hinfo
|
*res = *h
|
||||||
|
|
||||||
res.RoutableIPs = append([]wgcfg.CIDR{}, res.RoutableIPs...)
|
res.RoutableIPs = append([]wgcfg.CIDR{}, h.RoutableIPs...)
|
||||||
res.Services = append([]Service{}, res.Services...)
|
res.Services = append([]Service{}, h.Services...)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,19 +259,22 @@ type RegisterRequest struct {
|
|||||||
}
|
}
|
||||||
Expiry time.Time // requested key expiry, server policy may override
|
Expiry time.Time // requested key expiry, server policy may override
|
||||||
Followup string // response waits until AuthURL is visited
|
Followup string // response waits until AuthURL is visited
|
||||||
Hostinfo Hostinfo
|
Hostinfo *Hostinfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy makes a deep copy of RegisterRequest.
|
// Copy makes a deep copy of RegisterRequest.
|
||||||
// The result aliases no memory with the original.
|
// The result aliases no memory with the original.
|
||||||
func (req *RegisterRequest) Copy() *RegisterRequest {
|
func (req *RegisterRequest) Copy() *RegisterRequest {
|
||||||
res := *req
|
res := new(RegisterRequest)
|
||||||
res.Hostinfo = *res.Hostinfo.Copy()
|
*res = *req
|
||||||
|
if res.Hostinfo != nil {
|
||||||
|
res.Hostinfo = res.Hostinfo.Copy()
|
||||||
|
}
|
||||||
if res.Auth.Oauth2Token != nil {
|
if res.Auth.Oauth2Token != nil {
|
||||||
tok := *res.Auth.Oauth2Token
|
tok := *res.Auth.Oauth2Token
|
||||||
res.Auth.Oauth2Token = &tok
|
res.Auth.Oauth2Token = &tok
|
||||||
}
|
}
|
||||||
return &res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterResponse is returned by the server in response to a RegisterRequest.
|
// RegisterResponse is returned by the server in response to a RegisterRequest.
|
||||||
@ -293,7 +300,7 @@ type MapRequest struct {
|
|||||||
NodeKey NodeKey
|
NodeKey NodeKey
|
||||||
Endpoints []string
|
Endpoints []string
|
||||||
Stream bool // if true, multiple MapResponse objects are returned
|
Stream bool // if true, multiple MapResponse objects are returned
|
||||||
Hostinfo Hostinfo
|
Hostinfo *Hostinfo
|
||||||
}
|
}
|
||||||
|
|
||||||
type MapResponse struct {
|
type MapResponse struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user