4
4
"context"
5
5
"net/netip"
6
6
"testing"
7
+ "time"
7
8
8
9
"github.com/google/uuid"
9
10
"github.com/stretchr/testify/assert"
@@ -412,9 +413,78 @@ parentLoop:
412
413
require .True (t , client2 .AwaitReachable (awaitReachableCtx4 , ip ))
413
414
}
414
415
416
+ func TestConn_BlockEndpoints (t * testing.T ) {
417
+ t .Parallel ()
418
+ logger := slogtest .Make (t , nil ).Leveled (slog .LevelDebug )
419
+
420
+ derpMap , _ := tailnettest .RunDERPAndSTUN (t )
421
+
422
+ // Setup conn 1.
423
+ ip1 := tailnet .IP ()
424
+ conn1 , err := tailnet .NewConn (& tailnet.Options {
425
+ Addresses : []netip.Prefix {netip .PrefixFrom (ip1 , 128 )},
426
+ Logger : logger .Named ("w1" ),
427
+ DERPMap : derpMap ,
428
+ BlockEndpoints : true ,
429
+ })
430
+ require .NoError (t , err )
431
+ defer func () {
432
+ err := conn1 .Close ()
433
+ assert .NoError (t , err )
434
+ }()
435
+
436
+ // Setup conn 2.
437
+ ip2 := tailnet .IP ()
438
+ conn2 , err := tailnet .NewConn (& tailnet.Options {
439
+ Addresses : []netip.Prefix {netip .PrefixFrom (ip2 , 128 )},
440
+ Logger : logger .Named ("w2" ),
441
+ DERPMap : derpMap ,
442
+ BlockEndpoints : true ,
443
+ })
444
+ require .NoError (t , err )
445
+ defer func () {
446
+ err := conn2 .Close ()
447
+ assert .NoError (t , err )
448
+ }()
449
+
450
+ // Connect them together and wait for them to be reachable.
451
+ nodes1 := stitch (t , conn2 , conn1 )
452
+ nodes2 := stitch (t , conn1 , conn2 )
453
+ awaitReachableCtx , awaitReachableCancel := context .WithTimeout (context .Background (), testutil .WaitShort )
454
+ defer awaitReachableCancel ()
455
+ require .True (t , conn1 .AwaitReachable (awaitReachableCtx , ip2 ))
456
+
457
+ // Watch for 10 seconds to ensure that neither peer has any endpoints.
458
+ // There's no good way to force endpoint updates, so we just wait.
459
+ parentLoop:
460
+ for {
461
+ select {
462
+ case node := <- nodes1 :
463
+ require .Empty (t , node .Endpoints , "conn1 got endpoints" )
464
+ case node := <- nodes2 :
465
+ require .Empty (t , node .Endpoints , "conn2 got endpoints" )
466
+ case <- time .After (testutil .WaitShort ):
467
+ break parentLoop
468
+ }
469
+ }
470
+
471
+ // Double check that both peers don't have endpoints for the other peer
472
+ // according to magicsock.
473
+ conn1Status , ok := conn1 .Status ().Peer [conn2 .Node ().Key ]
474
+ require .True (t , ok )
475
+ require .Empty (t , conn1Status .Addrs )
476
+ require .Empty (t , conn1Status .CurAddr )
477
+ conn2Status , ok := conn2 .Status ().Peer [conn1 .Node ().Key ]
478
+ require .True (t , ok )
479
+ require .Empty (t , conn2Status .Addrs )
480
+ require .Empty (t , conn2Status .CurAddr )
481
+ }
482
+
415
483
// stitch sends node updates from src Conn as peer updates to dst Conn. Sort of
416
484
// like the Coordinator would, but without actually needing a Coordinator.
417
- func stitch (t * testing.T , dst , src * tailnet.Conn ) {
485
+ func stitch (t * testing.T , dst , src * tailnet.Conn ) <- chan * tailnet.Node {
486
+ // Buffered to avoid blocking the callback.
487
+ out := make (chan * tailnet.Node , 50 )
418
488
srcID := uuid .New ()
419
489
src .SetNodeCallback (func (node * tailnet.Node ) {
420
490
pn , err := tailnet .NodeToProto (node )
@@ -427,5 +497,11 @@ func stitch(t *testing.T, dst, src *tailnet.Conn) {
427
497
Kind : proto .CoordinateResponse_PeerUpdate_NODE ,
428
498
}})
429
499
assert .NoError (t , err )
500
+
501
+ select {
502
+ case out <- node :
503
+ default :
504
+ }
430
505
})
506
+ return out
431
507
}
0 commit comments