diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index c8e885799..ac799e2d9 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -95,15 +95,16 @@ type Direct struct { sfGroup singleflight.Group[struct{}, *NoiseClient] // protects noiseClient creation. noiseClient *NoiseClient - persist persist.PersistView - authKey string - tryingNewKey key.NodePrivate - expiry time.Time // or zero value if none/unknown - hostinfo *tailcfg.Hostinfo // always non-nil - netinfo *tailcfg.NetInfo - endpoints []tailcfg.Endpoint - tkaHead string - lastPingURL string // last PingRequest.URL received, for dup suppression + persist persist.PersistView + authKey string + tryingNewKey key.NodePrivate + expiry time.Time // or zero value if none/unknown + hostinfo *tailcfg.Hostinfo // always non-nil + netinfo *tailcfg.NetInfo + endpoints []tailcfg.Endpoint + tkaHead string + lastPingURL string // last PingRequest.URL received, for dup suppression + connectionHandleForTest string // sent in MapRequest.ConnectionHandleForTest } // Observer is implemented by users of the control client (such as LocalBackend) @@ -403,6 +404,14 @@ func (c *Direct) SetTKAHead(tkaHead string) bool { return true } +// SetConnectionHandleForTest stores a new MapRequest.ConnectionHandleForTest +// value for the next update. +func (c *Direct) SetConnectionHandleForTest(handle string) { + c.mu.Lock() + defer c.mu.Unlock() + c.connectionHandleForTest = handle +} + func (c *Direct) GetPersist() persist.PersistView { c.mu.Lock() defer c.mu.Unlock() @@ -851,6 +860,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, isStreaming bool, nu Netmap serverNoiseKey := c.serverNoiseKey hi := c.hostInfoLocked() backendLogID := hi.BackendLogID + connectionHandleForTest := c.connectionHandleForTest var epStrs []string var eps []netip.AddrPort var epTypes []tailcfg.EndpointType @@ -891,17 +901,18 @@ func (c *Direct) sendMapRequest(ctx context.Context, isStreaming bool, nu Netmap nodeKey := persist.PublicNodeKey() request := &tailcfg.MapRequest{ - Version: tailcfg.CurrentCapabilityVersion, - KeepAlive: true, - NodeKey: nodeKey, - DiscoKey: c.discoPubKey, - Endpoints: eps, - EndpointTypes: epTypes, - Stream: isStreaming, - Hostinfo: hi, - DebugFlags: c.debugFlags, - OmitPeers: nu == nil, - TKAHead: c.tkaHead, + Version: tailcfg.CurrentCapabilityVersion, + KeepAlive: true, + NodeKey: nodeKey, + DiscoKey: c.discoPubKey, + Endpoints: eps, + EndpointTypes: epTypes, + Stream: isStreaming, + Hostinfo: hi, + DebugFlags: c.debugFlags, + OmitPeers: nu == nil, + TKAHead: c.tkaHead, + ConnectionHandleForTest: connectionHandleForTest, } var extraDebugFlags []string if hi != nil && c.netMon != nil && !c.skipIPForwardingCheck && diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 11a0d0830..0a58d8f0c 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -1413,6 +1413,12 @@ type MapRequest struct { // * "warn-router-unhealthy": client's Router implementation is // having problems. DebugFlags []string `json:",omitempty"` + + // ConnectionHandleForTest, if non-empty, is an opaque string sent by the client that + // identifies this specific connection to the server. The server may choose to + // use this handle to identify the connection for debugging or testing + // purposes. It has no semantic meaning. + ConnectionHandleForTest string `json:",omitempty"` } // PortRange represents a range of UDP or TCP port numbers.