diff --git a/wgengine/magicsock/endpoint.go b/wgengine/magicsock/endpoint.go index eda589e14..d2b5537f1 100644 --- a/wgengine/magicsock/endpoint.go +++ b/wgengine/magicsock/endpoint.go @@ -75,12 +75,13 @@ type endpoint struct { // mu protects all following fields. mu syncs.Mutex // Lock ordering: Conn.mu, then endpoint.mu - heartBeatTimer *time.Timer // nil when idle - lastSendExt mono.Time // last time there were outgoing packets sent to this peer from an external trigger (e.g. wireguard-go or disco pingCLI) - lastSendAny mono.Time // last time there were outgoing packets sent this peer from any trigger, internal or external to magicsock - lastFullPing mono.Time // last time we pinged all disco or wireguard only endpoints - lastUDPRelayPathDiscovery mono.Time // last time we ran UDP relay path discovery - derpAddr netip.AddrPort // fallback/bootstrap path, if non-zero (non-zero for well-behaved clients) + heartBeatTimer *time.Timer // nil when idle + lastSendExt mono.Time // last time there were outgoing packets sent to this peer from an external trigger (e.g. wireguard-go or disco pingCLI) + lastSendAny mono.Time // last time there were outgoing packets sent this peer from any trigger, internal or external to magicsock + lastFullPing mono.Time // last time we pinged all disco or wireguard only endpoints + lastUDPRelayPathDiscovery mono.Time // last time we ran UDP relay path discovery + lastTSMPDiscoAdvertisement mono.Time + derpAddr netip.AddrPort // fallback/bootstrap path, if non-zero (non-zero for well-behaved clients) bestAddr addrQuality // best non-DERP path; zero if none; mutate via setBestAddrLocked() bestAddrAt mono.Time // time best address re-confirmed diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index b8a5f7da2..0699bd762 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -178,9 +178,10 @@ type Conn struct { // A publisher for synchronization points to ensure correct ordering of // config changes between magicsock and wireguard. - syncPub *eventbus.Publisher[syncPoint] - allocRelayEndpointPub *eventbus.Publisher[UDPRelayAllocReq] - portUpdatePub *eventbus.Publisher[router.PortUpdate] + syncPub *eventbus.Publisher[syncPoint] + allocRelayEndpointPub *eventbus.Publisher[UDPRelayAllocReq] + portUpdatePub *eventbus.Publisher[router.PortUpdate] + tsmpDiscoKeyAvailablePub *eventbus.Publisher[NewDiscoKeyAvailable] // pconn4 and pconn6 are the underlying UDP sockets used to // send/receive packets for wireguard and other magicsock @@ -691,6 +692,7 @@ func NewConn(opts Options) (*Conn, error) { c.syncPub = eventbus.Publish[syncPoint](ec) c.allocRelayEndpointPub = eventbus.Publish[UDPRelayAllocReq](ec) c.portUpdatePub = eventbus.Publish[router.PortUpdate](ec) + c.tsmpDiscoKeyAvailablePub = eventbus.Publish[NewDiscoKeyAvailable](ec) eventbus.SubscribeFunc(ec, c.onPortMapChanged) eventbus.SubscribeFunc(ec, c.onFilterUpdate) eventbus.SubscribeFunc(ec, c.onNodeViewsUpdate) @@ -2239,6 +2241,7 @@ func (c *Conn) handleDiscoMessage(msg []byte, src epAddr, shouldBeRelayHandshake if debugDisco() { c.logf("magicsock: disco: failed to open naclbox from %v (wrong rcpt?) via %s", sender, via) } + metricRecvDiscoBadKey.Add(1) return } @@ -2646,6 +2649,8 @@ func (c *Conn) enqueueCallMeMaybe(derpAddr netip.AddrPort, de *endpoint) { return } + c.maybeSendTSMPDiscoAdvert(de) + eps := make([]netip.AddrPort, 0, len(c.lastEndpoints)) for _, ep := range c.lastEndpoints { eps = append(eps, ep.Addr) @@ -4306,3 +4311,16 @@ func (c *Conn) HandleDiscoKeyAdvertisement(node tailcfg.NodeView, update packet. c.logf("magicsock: updated disco key for peer %v to %v", nodeKey.ShortString(), discoKey.ShortString()) metricTSMPDiscoKeyAdvertisementApplied.Add(1) } + +type NewDiscoKeyAvailable struct { + EpAddr netip.Addr +} + +func (c *Conn) maybeSendTSMPDiscoAdvert(de *endpoint) { + if de.lastTSMPDiscoAdvertisement == 0 { + de.lastTSMPDiscoAdvertisement = mono.Now() + c.tsmpDiscoKeyAvailablePub.Publish(NewDiscoKeyAvailable{ + EpAddr: de.nodeAddr, + }) + } +} diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 647923775..4fb322afb 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -54,6 +54,7 @@ import ( "tailscale.com/util/execqueue" "tailscale.com/util/mak" "tailscale.com/util/set" + "tailscale.com/util/singleflight" "tailscale.com/util/testenv" "tailscale.com/util/usermetric" "tailscale.com/version" @@ -568,6 +569,14 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) } e.magicConn.HandleDiscoKeyAdvertisement(peer.Node, pkt) }) + var tsmpRequestGroup singleflight.Group[netip.Addr, struct{}] + eventbus.SubscribeFunc(ec, func(req magicsock.NewDiscoKeyAvailable) { + go tsmpRequestGroup.Do(req.EpAddr, func() (struct{}, error) { + e.sendTSMPDiscoAdvertisement(req.EpAddr) + e.logf("wgengine: sending TSMP disco key advertisement to %v", req.EpAddr) + return struct{}{}, nil + }) + }) e.eventClient = ec e.logf("Engine created.") return e, nil