@@ -195,3 +195,124 @@ func TestConn_PreferredDERP(t *testing.T) {
195
195
t .Fatal ("timed out waiting for node" )
196
196
}
197
197
}
198
+
199
+ // TestConn_UpdateDERP tests that when update the DERP map we pick a new
200
+ // preferred DERP server and new connections can be made from clients.
201
+ func TestConn_UpdateDERP (t * testing.T ) {
202
+ t .Parallel ()
203
+ logger := slogtest .Make (t , nil ).Leveled (slog .LevelDebug )
204
+
205
+ derpMap1 := tailnettest .RunDERPAndSTUN (t )
206
+ ip := tailnet .IP ()
207
+ conn , err := tailnet .NewConn (& tailnet.Options {
208
+ Addresses : []netip.Prefix {netip .PrefixFrom (ip , 128 )},
209
+ Logger : logger .Named ("w1" ),
210
+ DERPMap : derpMap1 ,
211
+ BlockEndpoints : true ,
212
+ })
213
+ require .NoError (t , err )
214
+ defer func () {
215
+ err := conn .Close ()
216
+ assert .NoError (t , err )
217
+ }()
218
+
219
+ // Buffer channel so callback doesn't block
220
+ nodes := make (chan * tailnet.Node , 50 )
221
+ conn .SetNodeCallback (func (node * tailnet.Node ) {
222
+ nodes <- node
223
+ })
224
+
225
+ ctx1 , cancel1 := context .WithTimeout (context .Background (), testutil .WaitShort )
226
+ defer cancel1 ()
227
+ select {
228
+ case node := <- nodes :
229
+ require .Equal (t , 1 , node .PreferredDERP )
230
+ case <- ctx1 .Done ():
231
+ t .Fatal ("timed out waiting for node" )
232
+ }
233
+
234
+ // Connect from a different client.
235
+ client1 , err := tailnet .NewConn (& tailnet.Options {
236
+ Addresses : []netip.Prefix {netip .PrefixFrom (tailnet .IP (), 128 )},
237
+ Logger : logger .Named ("client1" ),
238
+ DERPMap : derpMap1 ,
239
+ BlockEndpoints : true ,
240
+ })
241
+ require .NoError (t , err )
242
+ defer func () {
243
+ err := client1 .Close ()
244
+ assert .NoError (t , err )
245
+ }()
246
+ client1 .SetNodeCallback (func (node * tailnet.Node ) {
247
+ err := conn .UpdateNodes ([]* tailnet.Node {node }, false )
248
+ assert .NoError (t , err )
249
+ })
250
+ client1 .UpdateNodes ([]* tailnet.Node {conn .Node ()}, false )
251
+
252
+ awaitReachableCtx1 , awaitReachableCancel1 := context .WithTimeout (context .Background (), testutil .WaitShort )
253
+ defer awaitReachableCancel1 ()
254
+ require .True (t , client1 .AwaitReachable (awaitReachableCtx1 , ip ))
255
+
256
+ // Update the DERP map and wait for the preferred DERP server to change.
257
+ derpMap2 := tailnettest .RunDERPAndSTUN (t )
258
+ // Change the region ID.
259
+ derpMap2 .Regions [2 ] = derpMap2 .Regions [1 ]
260
+ delete (derpMap2 .Regions , 1 )
261
+ derpMap2 .Regions [2 ].RegionID = 2
262
+ for _ , node := range derpMap2 .Regions [2 ].Nodes {
263
+ node .RegionID = 2
264
+ }
265
+ conn .SetDERPMap (derpMap2 )
266
+
267
+ ctx2 , cancel2 := context .WithTimeout (context .Background (), testutil .WaitShort )
268
+ defer cancel2 ()
269
+ parentLoop:
270
+ for {
271
+ select {
272
+ case node := <- nodes :
273
+ if node .PreferredDERP != 2 {
274
+ t .Logf ("waiting for preferred DERP server to change, got %v" , node .PreferredDERP )
275
+ continue
276
+ }
277
+ t .Log ("preferred DERP server changed!" )
278
+ break parentLoop
279
+ case <- ctx2 .Done ():
280
+ t .Fatal ("timed out waiting for preferred DERP server to change" )
281
+ }
282
+ }
283
+
284
+ // Client1 should be dropped...
285
+ awaitReachableCtx2 , awaitReachableCancel2 := context .WithTimeout (context .Background (), testutil .WaitShort )
286
+ defer awaitReachableCancel2 ()
287
+ require .False (t , client1 .AwaitReachable (awaitReachableCtx2 , ip ))
288
+
289
+ // ... unless the client updates it's derp map and nodes.
290
+ client1 .SetDERPMap (derpMap2 )
291
+ client1 .UpdateNodes ([]* tailnet.Node {conn .Node ()}, false )
292
+ awaitReachableCtx3 , awaitReachableCancel3 := context .WithTimeout (context .Background (), testutil .WaitShort )
293
+ defer awaitReachableCancel3 ()
294
+ require .True (t , client1 .AwaitReachable (awaitReachableCtx3 , ip ))
295
+
296
+ // Connect from a different different client with up-to-date derp map and
297
+ // nodes.
298
+ client2 , err := tailnet .NewConn (& tailnet.Options {
299
+ Addresses : []netip.Prefix {netip .PrefixFrom (tailnet .IP (), 128 )},
300
+ Logger : logger .Named ("client2" ),
301
+ DERPMap : derpMap2 ,
302
+ BlockEndpoints : true ,
303
+ })
304
+ require .NoError (t , err )
305
+ defer func () {
306
+ err := client2 .Close ()
307
+ assert .NoError (t , err )
308
+ }()
309
+ client2 .SetNodeCallback (func (node * tailnet.Node ) {
310
+ err := conn .UpdateNodes ([]* tailnet.Node {node }, false )
311
+ assert .NoError (t , err )
312
+ })
313
+ client2 .UpdateNodes ([]* tailnet.Node {conn .Node ()}, false )
314
+
315
+ awaitReachableCtx4 , awaitReachableCancel4 := context .WithTimeout (context .Background (), testutil .WaitShort )
316
+ defer awaitReachableCancel4 ()
317
+ require .True (t , client2 .AwaitReachable (awaitReachableCtx4 , ip ))
318
+ }
0 commit comments