mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-15 12:02:31 +00:00
tailcfg, control/controlhttp, control/controlclient: add ControlDialPlan field (#5648)
* tailcfg, control/controlhttp, control/controlclient: add ControlDialPlan field This field allows the control server to provide explicit information about how to connect to it; useful if the client's link status can change after the initial connection, or if the DNS settings pushed by the control server break future connections. Change-Id: I720afe6289ec27d40a41b3dcb310ec45bd7e5f3e Signed-off-by: Andrew Dunham <andrew@tailscale.com>
This commit is contained in:
@@ -76,6 +76,8 @@ type Direct struct {
|
||||
popBrowser func(url string) // or nil
|
||||
c2nHandler http.Handler // or nil
|
||||
|
||||
dialPlan ControlDialPlanner // can be nil
|
||||
|
||||
mu sync.Mutex // mutex guards the following fields
|
||||
serverKey key.MachinePublic // original ("legacy") nacl crypto_box-based public key
|
||||
serverNoiseKey key.MachinePublic
|
||||
@@ -133,6 +135,34 @@ type Options struct {
|
||||
// MapResponse.PingRequest queries from the control plane.
|
||||
// If nil, PingRequest queries are not answered.
|
||||
Pinger Pinger
|
||||
|
||||
// DialPlan contains and stores a previous dial plan that we received
|
||||
// from the control server; if nil, we fall back to using DNS.
|
||||
//
|
||||
// If we receive a new DialPlan from the server, this value will be
|
||||
// updated.
|
||||
DialPlan ControlDialPlanner
|
||||
}
|
||||
|
||||
// ControlDialPlanner is the interface optionally supplied when creating a
|
||||
// control client to control exactly how TCP connections to the control plane
|
||||
// are dialed.
|
||||
//
|
||||
// It is usually implemented by an atomic.Pointer.
|
||||
type ControlDialPlanner interface {
|
||||
// Load returns the current plan for how to connect to control.
|
||||
//
|
||||
// The returned plan can be nil. If so, connections should be made by
|
||||
// resolving the control URL using DNS.
|
||||
Load() *tailcfg.ControlDialPlan
|
||||
|
||||
// Store updates the dial plan with new directions from the control
|
||||
// server.
|
||||
//
|
||||
// The dial plan can span multiple connections to the control server.
|
||||
// That is, a dial plan received when connected over Wi-Fi is still
|
||||
// valid for a subsequent connection over LTE after a network switch.
|
||||
Store(*tailcfg.ControlDialPlan)
|
||||
}
|
||||
|
||||
// Pinger is the LocalBackend.Ping method.
|
||||
@@ -216,6 +246,7 @@ func NewDirect(opts Options) (*Direct, error) {
|
||||
popBrowser: opts.PopBrowserURL,
|
||||
c2nHandler: opts.C2NHandler,
|
||||
dialer: opts.Dialer,
|
||||
dialPlan: opts.DialPlan,
|
||||
}
|
||||
if opts.Hostinfo == nil {
|
||||
c.SetHostinfo(hostinfo.New())
|
||||
@@ -915,6 +946,14 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, readOnly bool
|
||||
} else {
|
||||
vlogf("netmap: got new map")
|
||||
}
|
||||
if resp.ControlDialPlan != nil {
|
||||
if c.dialPlan != nil {
|
||||
c.logf("netmap: got new dial plan from control")
|
||||
c.dialPlan.Store(resp.ControlDialPlan)
|
||||
} else {
|
||||
c.logf("netmap: [unexpected] new dial plan; nowhere to store it")
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case timeoutReset <- struct{}{}:
|
||||
@@ -1365,12 +1404,17 @@ func (c *Direct) getNoiseClient() (*noiseClient, error) {
|
||||
if nc != nil {
|
||||
return nc, nil
|
||||
}
|
||||
var dp func() *tailcfg.ControlDialPlan
|
||||
if c.dialPlan != nil {
|
||||
dp = c.dialPlan.Load
|
||||
}
|
||||
nc, err, _ := c.sfGroup.Do(struct{}{}, func() (*noiseClient, error) {
|
||||
k, err := c.getMachinePrivKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nc, err := newNoiseClient(k, serverNoiseKey, c.serverURL, c.dialer)
|
||||
c.logf("creating new noise client")
|
||||
nc, err := newNoiseClient(k, serverNoiseKey, c.serverURL, c.dialer, dp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user