@@ -56,11 +56,10 @@ type phased struct {
56
56
57
57
type configMaps struct {
58
58
phased
59
- netmapDirty bool
60
- derpMapDirty bool
61
- filterDirty bool
62
- closing bool
63
- waitReadyForHandshake bool
59
+ netmapDirty bool
60
+ derpMapDirty bool
61
+ filterDirty bool
62
+ closing bool
64
63
65
64
engine engineConfigurable
66
65
static netmap.NetworkMap
@@ -218,8 +217,9 @@ func (c *configMaps) peerConfigLocked() []*tailcfg.Node {
218
217
out := make ([]* tailcfg.Node , 0 , len (c .peers ))
219
218
for _ , p := range c .peers {
220
219
// Don't add nodes that we havent received a READY_FOR_HANDSHAKE for
221
- // yet, if we want to wait for them.
222
- if ! p .readyForHandshake && c .waitReadyForHandshake {
220
+ // yet, if they're a destination. If we received a READY_FOR_HANDSHAKE
221
+ // for a peer before we receive their node, the node will be nil.
222
+ if (! p .readyForHandshake && p .isDestination ) || p .node == nil {
223
223
continue
224
224
}
225
225
n := p .node .Clone ()
@@ -231,10 +231,17 @@ func (c *configMaps) peerConfigLocked() []*tailcfg.Node {
231
231
return out
232
232
}
233
233
234
- func (c * configMaps ) setWaitForHandshake ( wait bool ) {
234
+ func (c * configMaps ) setTunnelDestinaion ( id uuid. UUID ) {
235
235
c .L .Lock ()
236
236
defer c .L .Unlock ()
237
- c .waitReadyForHandshake = wait
237
+ lc , ok := c .peers [id ]
238
+ if ! ok {
239
+ lc = & peerLifecycle {
240
+ peerID : id ,
241
+ }
242
+ c .peers [id ] = lc
243
+ }
244
+ lc .isDestination = true
238
245
}
239
246
240
247
// setAddresses sets the addresses belonging to this node to the given slice. It
@@ -343,8 +350,10 @@ func (c *configMaps) updatePeers(updates []*proto.CoordinateResponse_PeerUpdate)
343
350
// worry about them being up-to-date when handling updates below, and it covers
344
351
// all peers, not just the ones we got updates about.
345
352
for _ , lc := range c .peers {
346
- if peerStatus , ok := status .Peer [lc .node .Key ]; ok {
347
- lc .lastHandshake = peerStatus .LastHandshake
353
+ if lc .node != nil {
354
+ if peerStatus , ok := status .Peer [lc .node .Key ]; ok {
355
+ lc .lastHandshake = peerStatus .LastHandshake
356
+ }
348
357
}
349
358
}
350
359
@@ -399,7 +408,7 @@ func (c *configMaps) updatePeerLocked(update *proto.CoordinateResponse_PeerUpdat
399
408
// SSH connections don't send packets while idle, so we use keep alives
400
409
// to avoid random hangs while we set up the connection again after
401
410
// inactivity.
402
- node .KeepAlive = (statusOk && peerStatus .Active ) || (peerOk && lc .node .KeepAlive )
411
+ node .KeepAlive = (statusOk && peerStatus .Active ) || (peerOk && lc .node != nil && lc . node .KeepAlive )
403
412
}
404
413
switch {
405
414
case ! peerOk && update .Kind == proto .CoordinateResponse_PeerUpdate_NODE :
@@ -409,17 +418,14 @@ func (c *configMaps) updatePeerLocked(update *proto.CoordinateResponse_PeerUpdat
409
418
lastHandshake = ps .LastHandshake
410
419
}
411
420
lc = & peerLifecycle {
412
- peerID : id ,
413
- node : node ,
414
- lastHandshake : lastHandshake ,
415
- lost : false ,
416
- readyForHandshake : ! c .waitReadyForHandshake ,
417
- }
418
- if c .waitReadyForHandshake {
419
- lc .readyForHandshakeTimer = c .clock .AfterFunc (5 * time .Second , func () {
420
- logger .Debug (context .Background (), "ready for handshake timeout" )
421
- c .peerReadyForHandshakeTimeout (id )
422
- })
421
+ peerID : id ,
422
+ node : node ,
423
+ lastHandshake : lastHandshake ,
424
+ lost : false ,
425
+ // If we're receiving a NODE update for a peer we don't already have
426
+ // a lifecycle for, it's likely the source of a tunnel. We don't
427
+ // need to wait for a READY_FOR_HANDSHAKE.
428
+ readyForHandshake : true ,
423
429
}
424
430
c .peers [id ] = lc
425
431
logger .Debug (context .Background (), "adding new peer" )
@@ -428,27 +434,50 @@ func (c *configMaps) updatePeerLocked(update *proto.CoordinateResponse_PeerUpdat
428
434
return lc .readyForHandshake
429
435
case peerOk && update .Kind == proto .CoordinateResponse_PeerUpdate_NODE :
430
436
// update
431
- node .Created = lc .node .Created
437
+ if lc .node != nil {
438
+ node .Created = lc .node .Created
439
+ }
432
440
dirty = ! lc .node .Equal (node ) && lc .readyForHandshake
433
441
lc .node = node
434
442
lc .lost = false
435
443
lc .resetTimer ()
444
+ if lc .isDestination {
445
+ if ! lc .readyForHandshake {
446
+ lc .setReadyForHandshakeTimer (c )
447
+ } else {
448
+ lc .node .KeepAlive = true
449
+ }
450
+ }
436
451
logger .Debug (context .Background (), "node update to existing peer" , slog .F ("dirty" , dirty ))
437
452
return dirty
438
453
case peerOk && update .Kind == proto .CoordinateResponse_PeerUpdate_READY_FOR_HANDSHAKE :
439
- wasReady := lc .readyForHandshake
454
+ dirty := ! lc .readyForHandshake
440
455
lc .readyForHandshake = true
441
456
if lc .readyForHandshakeTimer != nil {
442
457
lc .readyForHandshakeTimer .Stop ()
443
458
lc .readyForHandshakeTimer = nil
444
459
}
445
- lc .node .KeepAlive = true
460
+ if lc .node != nil {
461
+ dirty = dirty || ! lc .node .KeepAlive
462
+ lc .node .KeepAlive = true
463
+ }
446
464
logger .Debug (context .Background (), "peer ready for handshake" )
447
- return ! wasReady
465
+ // only force a reconfig if the node populated
466
+ return dirty && lc .node != nil
448
467
case ! peerOk && update .Kind == proto .CoordinateResponse_PeerUpdate_READY_FOR_HANDSHAKE :
449
- // TODO: should we keep track of ready for handshake messages we get
450
- // from unknown nodes?
468
+ // When we receive a READY_FOR_HANDSHAKE for a peer we don't know about,
469
+ // we create a peerLifecycle with the peerID and set readyForHandshake
470
+ // to true. Eventually we should receive a NODE update for this peer,
471
+ // and it'll be programmed into wireguard.
451
472
logger .Debug (context .Background (), "got peer ready for handshake for unknown peer" )
473
+ lc = & peerLifecycle {
474
+ peerID : id ,
475
+ lost : true ,
476
+ readyForHandshake : true ,
477
+ }
478
+ // Timeout the peer in case we never receive a NODE update for it.
479
+ lc .setLostTimer (c )
480
+ c .peers [id ] = lc
452
481
return false
453
482
case ! peerOk :
454
483
// disconnected or lost, but we don't have the node. No op
@@ -606,35 +635,50 @@ func (c *configMaps) peerReadyForHandshakeTimeout(peerID uuid.UUID) {
606
635
}
607
636
608
637
type peerLifecycle struct {
609
- peerID uuid.UUID
638
+ peerID uuid.UUID
639
+ // isDestination specifies if the peer is a destination, meaning we
640
+ // initiated a tunnel to the peer. When the peer is a destination, we do not
641
+ // respond to node updates with READY_FOR_HANDSHAKEs, and we wait to program
642
+ // the peer into wireguard until we receive a READY_FOR_HANDSHAKE from the
643
+ // peer or the timeout is reached.
644
+ isDestination bool
645
+ // node is the tailcfg.Node for the peer. It may be nil until we receive a
646
+ // NODE update for it.
610
647
node * tailcfg.Node
611
648
lost bool
612
649
lastHandshake time.Time
613
- timer * clock.Timer
650
+ lostTimer * clock.Timer
614
651
readyForHandshake bool
615
652
readyForHandshakeTimer * clock.Timer
616
653
}
617
654
618
655
func (l * peerLifecycle ) resetTimer () {
619
- if l .timer != nil {
620
- l .timer .Stop ()
621
- l .timer = nil
656
+ if l .lostTimer != nil {
657
+ l .lostTimer .Stop ()
658
+ l .lostTimer = nil
622
659
}
623
660
}
624
661
625
662
func (l * peerLifecycle ) setLostTimer (c * configMaps ) {
626
- if l .timer != nil {
627
- l .timer .Stop ()
663
+ if l .lostTimer != nil {
664
+ l .lostTimer .Stop ()
628
665
}
629
666
ttl := lostTimeout - c .clock .Since (l .lastHandshake )
630
667
if ttl <= 0 {
631
668
ttl = time .Nanosecond
632
669
}
633
- l .timer = c .clock .AfterFunc (ttl , func () {
670
+ l .lostTimer = c .clock .AfterFunc (ttl , func () {
634
671
c .peerLostTimeout (l .peerID )
635
672
})
636
673
}
637
674
675
+ func (l * peerLifecycle ) setReadyForHandshakeTimer (c * configMaps ) {
676
+ l .readyForHandshakeTimer = c .clock .AfterFunc (5 * time .Second , func () {
677
+ c .logger .Debug (context .Background (), "ready for handshake timeout" , slog .F ("peer_id" , l .peerID ))
678
+ c .peerReadyForHandshakeTimeout (l .peerID )
679
+ })
680
+ }
681
+
638
682
// prefixesDifferent returns true if the two slices contain different prefixes
639
683
// where order doesn't matter.
640
684
func prefixesDifferent (a , b []netip.Prefix ) bool {
0 commit comments