@@ -10,11 +10,15 @@ import (
10
10
"fmt"
11
11
"net"
12
12
"net/netip"
13
+ "path/filepath"
14
+ "runtime"
13
15
"strings"
14
16
"testing"
17
+ "time"
15
18
16
19
"github.com/google/gopacket"
17
20
"github.com/google/gopacket/layers"
21
+ "tailscale.com/util/must"
18
22
)
19
23
20
24
// TestPacketSideEffects tests that upon receiving certain
@@ -32,13 +36,7 @@ func TestPacketSideEffects(t *testing.T) {
32
36
}{
33
37
{
34
38
netName : "basic" ,
35
- setup : func () (* Server , error ) {
36
- var c Config
37
- nw := c .AddNetwork ("192.168.0.1/24" )
38
- c .AddNode (nw )
39
- c .AddNode (nw )
40
- return New (& c )
41
- },
39
+ setup : newTwoNodesSameNetworkServer ,
42
40
tests : []netTest {
43
41
{
44
42
name : "drop-rando-ethertype" ,
@@ -129,6 +127,14 @@ func mkEth(dst, src MAC, ethType layers.EthernetType, payload []byte) []byte {
129
127
return append (ret , payload ... )
130
128
}
131
129
130
+ // mkLenPrefixed prepends a uint32 length to the given packet.
131
+ func mkLenPrefixed (pkt []byte ) []byte {
132
+ ret := make ([]byte , 4 + len (pkt ))
133
+ binary .BigEndian .PutUint32 (ret , uint32 (len (pkt )))
134
+ copy (ret [4 :], pkt )
135
+ return ret
136
+ }
137
+
132
138
// mkIPv6RouterSolicit makes a IPv6 router solicitation packet
133
139
// ethernet frame.
134
140
func mkIPv6RouterSolicit (srcMAC MAC , srcIP netip.Addr ) []byte {
@@ -230,3 +236,156 @@ func numPkts(want int) func(*sideEffects) error {
230
236
return fmt .Errorf ("got %d packets, want %d. packets were:\n %s" , len (se .got ), want , pkts .Bytes ())
231
237
}
232
238
}
239
+
240
+ func newTwoNodesSameNetworkServer () (* Server , error ) {
241
+ var c Config
242
+ nw := c .AddNetwork ("192.168.0.1/24" )
243
+ c .AddNode (nw )
244
+ c .AddNode (nw )
245
+ return New (& c )
246
+ }
247
+
248
+ // TestProtocolQEMU tests the protocol that qemu uses to connect to natlab's
249
+ // vnet. (uint32-length prefixed ethernet frames over a unix stream socket)
250
+ //
251
+ // This test makes two clients (as qemu would act) and has one send an ethernet
252
+ // packet to the other virtual LAN segment.
253
+ func TestProtocolQEMU (t * testing.T ) {
254
+ if runtime .GOOS == "windows" {
255
+ t .Skipf ("skipping on %s" , runtime .GOOS )
256
+ }
257
+ s := must .Get (newTwoNodesSameNetworkServer ())
258
+ defer s .Close ()
259
+ s .SetLoggerForTest (t .Logf )
260
+
261
+ td := t .TempDir ()
262
+ serverSock := filepath .Join (td , "vnet.sock" )
263
+
264
+ ln , err := net .Listen ("unix" , serverSock )
265
+ if err != nil {
266
+ t .Fatal (err )
267
+ }
268
+ defer ln .Close ()
269
+
270
+ var clientc [2 ]* net.UnixConn
271
+ for i := range clientc {
272
+ c , err := net .Dial ("unix" , serverSock )
273
+ if err != nil {
274
+ t .Fatal (err )
275
+ }
276
+ defer c .Close ()
277
+ clientc [i ] = c .(* net.UnixConn )
278
+ }
279
+
280
+ for range clientc {
281
+ conn , err := ln .Accept ()
282
+ if err != nil {
283
+ t .Fatal (err )
284
+ }
285
+ go s .ServeUnixConn (conn .(* net.UnixConn ), ProtocolQEMU )
286
+ }
287
+
288
+ sendBetweenClients (t , clientc , s , mkLenPrefixed )
289
+ }
290
+
291
+ // TestProtocolUnixDgram tests the protocol that macOS Virtualization.framework
292
+ // uses to connect to vnet. (unix datagram sockets)
293
+ //
294
+ // It is similar to TestProtocolQEMU but uses unix datagram sockets instead of
295
+ // streams.
296
+ func TestProtocolUnixDgram (t * testing.T ) {
297
+ if runtime .GOOS == "windows" {
298
+ t .Skipf ("skipping on %s" , runtime .GOOS )
299
+ }
300
+ s := must .Get (newTwoNodesSameNetworkServer ())
301
+ defer s .Close ()
302
+ s .SetLoggerForTest (t .Logf )
303
+
304
+ td := t .TempDir ()
305
+ serverSock := filepath .Join (td , "vnet.sock" )
306
+ serverAddr := must .Get (net .ResolveUnixAddr ("unixgram" , serverSock ))
307
+
308
+ var clientSock [2 ]string
309
+ for i := range clientSock {
310
+ clientSock [i ] = filepath .Join (td , fmt .Sprintf ("c%d.sock" , i ))
311
+ }
312
+
313
+ uc , err := net .ListenUnixgram ("unixgram" , serverAddr )
314
+ if err != nil {
315
+ t .Fatal (err )
316
+ }
317
+ go s .ServeUnixConn (uc , ProtocolUnixDGRAM )
318
+
319
+ var clientc [2 ]* net.UnixConn
320
+ for i := range clientc {
321
+ c , err := net .DialUnix ("unixgram" ,
322
+ must .Get (net .ResolveUnixAddr ("unixgram" , clientSock [i ])),
323
+ serverAddr )
324
+ if err != nil {
325
+ t .Fatal (err )
326
+ }
327
+ defer c .Close ()
328
+ clientc [i ] = c
329
+ }
330
+
331
+ sendBetweenClients (t , clientc , s , nil )
332
+ }
333
+
334
+ // sendBetweenClients is a test helper that tries to send an ethernet frame from
335
+ // one client to another.
336
+ //
337
+ // It first makes the two clients send a packet to a fictitious node 3, which
338
+ // forces their src MACs to be registered with a networkWriter internally so
339
+ // they can receive traffic.
340
+ //
341
+ // Normally a node starts up spamming DHCP + NDP but we don't get that as a side
342
+ // effect here, so this does it manually.
343
+ //
344
+ // It also then waits for them to be registered.
345
+ //
346
+ // wrap is an optional function that wraps the packet before sending it.
347
+ func sendBetweenClients (t testing.TB , clientc [2 ]* net.UnixConn , s * Server , wrap func ([]byte ) []byte ) {
348
+ t .Helper ()
349
+ if wrap == nil {
350
+ wrap = func (b []byte ) []byte { return b }
351
+ }
352
+ for i , c := range clientc {
353
+ must .Get (c .Write (wrap (mkEth (nodeMac (3 ), nodeMac (i + 1 ), testingEthertype , []byte ("hello" )))))
354
+ }
355
+ awaitCond (t , 5 * time .Second , func () error {
356
+ if n := s .RegisteredWritersForTest (); n != 2 {
357
+ return fmt .Errorf ("got %d registered writers, want 2" , n )
358
+ }
359
+ return nil
360
+ })
361
+
362
+ // Now see if node1 can write to node2 and node2 receives it.
363
+ pkt := wrap (mkEth (nodeMac (2 ), nodeMac (1 ), testingEthertype , []byte ("test-msg" )))
364
+ t .Logf ("writing % 02x" , pkt )
365
+ must .Get (clientc [0 ].Write (pkt ))
366
+
367
+ buf := make ([]byte , len (pkt ))
368
+ clientc [1 ].SetReadDeadline (time .Now ().Add (5 * time .Second ))
369
+ n , err := clientc [1 ].Read (buf )
370
+ if err != nil {
371
+ t .Fatal (err )
372
+ }
373
+ got := buf [:n ]
374
+ if ! bytes .Equal (got , pkt ) {
375
+ t .Errorf ("bad packet\n got: % 02x\n want: % 02x" , got , pkt )
376
+ }
377
+ }
378
+
379
+ func awaitCond (t testing.TB , timeout time.Duration , cond func () error ) {
380
+ t .Helper ()
381
+ t0 := time .Now ()
382
+ for {
383
+ if err := cond (); err == nil {
384
+ return
385
+ }
386
+ if time .Since (t0 ) > timeout {
387
+ t .Fatalf ("timed out after %v" , timeout )
388
+ }
389
+ time .Sleep (10 * time .Millisecond )
390
+ }
391
+ }
0 commit comments