@@ -2,9 +2,10 @@ package holepunch_test
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
5
6
"net"
6
- "slices"
7
7
"sync"
8
+ "sync/atomic"
8
9
"testing"
9
10
"time"
10
11
@@ -14,12 +15,14 @@ import (
14
15
"github.com/libp2p/go-libp2p/core/network"
15
16
"github.com/libp2p/go-libp2p/core/peer"
16
17
"github.com/libp2p/go-libp2p/core/peerstore"
18
+ "github.com/libp2p/go-libp2p/p2p/net/simconn"
17
19
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto"
18
20
relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"
19
21
"github.com/libp2p/go-libp2p/p2p/protocol/holepunch"
20
22
holepunch_pb "github.com/libp2p/go-libp2p/p2p/protocol/holepunch/pb"
21
23
"github.com/libp2p/go-libp2p/p2p/protocol/identify"
22
- "github.com/libp2p/go-libp2p/p2p/transport/tcp"
24
+ "github.com/libp2p/go-libp2p/p2p/protocol/ping"
25
+ "github.com/libp2p/go-libp2p/p2p/transport/quicreuse"
23
26
24
27
"github.com/libp2p/go-msgio/pbio"
25
28
ma "github.com/multiformats/go-multiaddr"
@@ -130,15 +133,34 @@ func TestDirectDialWorks(t *testing.T) {
130
133
}
131
134
132
135
func TestEndToEndSimConnect (t * testing.T ) {
133
- t .Skip ("This test is broken. It is hard to do an end-to-end test without mocking the separate networks that holepunching is supposed to connect. It only worked previously because one of the hosts was able to learn about a non-holepunched direct connection via identify." )
134
-
135
136
h1tr := & mockEventTracer {}
136
137
h2tr := & mockEventTracer {}
137
138
h1 , h2 , relay , _ := makeRelayedHosts (t , []holepunch.Option {holepunch .WithTracer (h1tr )}, []holepunch.Option {holepunch .WithTracer (h2tr )}, true )
138
139
defer h1 .Close ()
139
140
defer h2 .Close ()
140
141
defer relay .Close ()
141
142
143
+ // time.Sleep(100 * time.Millisecond)
144
+ p1 := ping .NewPingService (h1 )
145
+ require .NoError (t , h1 .Connect (context .Background (), peer.AddrInfo {
146
+ ID : relay .ID (),
147
+ Addrs : relay .Addrs (),
148
+ }))
149
+ // var raddr ma.Multiaddr
150
+ // for _, a := range h2.Addrs() {
151
+ // if _, err := a.ValueForProtocol(ma.P_CIRCUIT); err == nil {
152
+ // raddr = a
153
+ // break
154
+ // }
155
+ // }
156
+ // require.NoError(t, h1.Connect(context.Background(), peer.AddrInfo{
157
+ // ID: h2.ID(),
158
+ // Addrs: []ma.Multiaddr{raddr},
159
+ // }))
160
+ res := p1 .Ping (network .WithAllowLimitedConn (context .Background (), "test" ), h2 .ID ())
161
+ result := <- res
162
+ require .NoError (t , result .Error )
163
+
142
164
// wait till a direct connection is complete
143
165
ensureDirectConn (t , h1 , h2 )
144
166
// ensure no hole-punching streams are open on either side
@@ -147,14 +169,15 @@ func TestEndToEndSimConnect(t *testing.T) {
147
169
require .Eventually (t ,
148
170
func () bool {
149
171
h2Events = h2tr .getEvents ()
150
- return len (h2Events ) == 3
172
+ return len (h2Events ) == 4
151
173
},
152
174
time .Second ,
153
- 10 * time .Millisecond ,
175
+ 100 * time .Millisecond ,
154
176
)
155
- require .Equal (t , holepunch .StartHolePunchEvtT , h2Events [0 ].Type )
156
- require .Equal (t , holepunch .HolePunchAttemptEvtT , h2Events [1 ].Type )
157
- require .Equal (t , holepunch .EndHolePunchEvtT , h2Events [2 ].Type )
177
+ require .Equal (t , holepunch .DirectDialEvtT , h2Events [0 ].Type )
178
+ require .Equal (t , holepunch .StartHolePunchEvtT , h2Events [1 ].Type )
179
+ require .Equal (t , holepunch .HolePunchAttemptEvtT , h2Events [2 ].Type )
180
+ require .Equal (t , holepunch .EndHolePunchEvtT , h2Events [3 ].Type )
158
181
159
182
h1Events := h1tr .getEvents ()
160
183
// We don't really expect a hole-punched connection to be established in this test,
@@ -230,7 +253,7 @@ func TestFailuresOnInitiator(t *testing.T) {
230
253
opts = append (opts , holepunch .WithAddrFilter (f ))
231
254
}
232
255
233
- hps := addHolePunchService (t , h2 , opts ... )
256
+ hps := addHolePunchService (t , h2 , nil , opts ... )
234
257
// wait until the hole punching protocol has actually started
235
258
require .Eventually (t , func () bool {
236
259
protos , _ := h2 .Peerstore ().SupportsProtocols (h1 .ID (), holepunch .Protocol )
@@ -409,7 +432,7 @@ func ensureDirectConn(t *testing.T, h1, h2 host.Host) {
409
432
}, 5 * time .Second , 50 * time .Millisecond )
410
433
}
411
434
412
- func mkHostWithStaticAutoRelay (t * testing.T , relay host.Host ) host.Host {
435
+ func mkHostWithStaticAutoRelay (t * testing.T , ipAddr string , port int , relay host.Host , router * simconn. SimpleFirewallRouter ) host.Host {
413
436
if race .WithRace () {
414
437
t .Skip ("modifying manet.Private4 is racy" )
415
438
}
@@ -418,16 +441,13 @@ func mkHostWithStaticAutoRelay(t *testing.T, relay host.Host) host.Host {
418
441
Addrs : relay .Addrs (),
419
442
}
420
443
421
- cpy := manet .Private4
422
- manet .Private4 = []* net.IPNet {}
423
- defer func () { manet .Private4 = cpy }()
424
-
425
444
h , err := libp2p .New (
426
- libp2p .ListenAddrs (ma .StringCast ("/ip4/127.0.0.1/tcp/0" )),
445
+ libp2p .ListenAddrs (ma .StringCast (fmt . Sprintf ( "/ip4/%s/udp/%d/quic-v1" , ipAddr , port ) )),
427
446
libp2p .EnableRelay (),
428
447
libp2p .EnableAutoRelayWithStaticRelays ([]peer.AddrInfo {pi }),
429
448
libp2p .ForceReachabilityPrivate (),
430
449
libp2p .ResourceManager (& network.NullResourceManager {}),
450
+ quicReuseOpts (false , router ),
431
451
)
432
452
require .NoError (t , err )
433
453
@@ -443,23 +463,58 @@ func mkHostWithStaticAutoRelay(t *testing.T, relay host.Host) host.Host {
443
463
return h
444
464
}
445
465
466
+ var lastPort atomic.Uint32
467
+
468
+ type MockSourceIPSelector struct {
469
+ ip atomic.Pointer [net.IP ]
470
+ }
471
+
472
+ func (m * MockSourceIPSelector ) PreferredSourceIPForDestination (dst * net.UDPAddr ) (net.IP , error ) {
473
+ return * m .ip .Load (), nil
474
+ }
475
+
476
+ func quicReuseOpts (isPublic bool , router * simconn.SimpleFirewallRouter ) libp2p.Option {
477
+ m := & MockSourceIPSelector {}
478
+ return libp2p .QUICReuse (
479
+ quicreuse .NewConnManager ,
480
+ quicreuse .CustomSourceIPSelector (func () (quicreuse.SourceIPSelector , error ) {
481
+ return m , nil
482
+ }),
483
+ quicreuse .CustomListenUDP (func (network string , address * net.UDPAddr ) (net.PacketConn , error ) {
484
+ m .ip .Store (& address .IP )
485
+ if address .Port == 0 {
486
+ address .Port = int (lastPort .Add (1 ))
487
+ }
488
+ c := simconn .NewSimConn (address , router )
489
+ if isPublic {
490
+ router .AddPublicNode (address , c )
491
+ } else {
492
+ router .AddNode (address , c )
493
+ }
494
+ return c , nil
495
+ }))
496
+ }
497
+
446
498
func makeRelayedHosts (t * testing.T , h1opt , h2opt []holepunch.Option , addHolePuncher bool ) (h1 , h2 , relay host.Host , hps * holepunch.Service ) {
447
499
t .Helper ()
448
- h1 , _ = mkHostWithHolePunchSvc (t , h1opt ... )
500
+ router := & simconn.SimpleFirewallRouter {}
501
+ h1 , _ = mkHostWithHolePunchSvc2 (t , "2.0.0.1" , 8001 , router , h1opt ... )
449
502
var err error
503
+
450
504
relay , err = libp2p .New (
451
- libp2p .ListenAddrs (ma .StringCast ("/ip4/127.0 .0.1/tcp/0 " )),
505
+ libp2p .ListenAddrs (ma .StringCast ("/ip4/1.2 .0.1/udp/8000/quic-v1 " )),
452
506
libp2p .DisableRelay (),
453
507
libp2p .ResourceManager (& network.NullResourceManager {}),
508
+ quicReuseOpts (true , router ),
454
509
)
455
510
require .NoError (t , err )
456
511
_ , err = relayv2 .New (relay )
457
512
require .NoError (t , err )
458
513
459
514
// make sure the relay service is started and advertised by Identify
460
515
h , err := libp2p .New (
461
- libp2p .NoListenAddrs ,
462
- libp2p . Transport ( tcp . NewTCPTransport ),
516
+ libp2p .ListenAddrs ( ma . StringCast ( "/ip4/1.2.0.2/udp/8000/quic-v1" )) ,
517
+ quicReuseOpts ( false , router ),
463
518
libp2p .DisableRelay (),
464
519
)
465
520
require .NoError (t , err )
@@ -470,9 +525,9 @@ func makeRelayedHosts(t *testing.T, h1opt, h2opt []holepunch.Option, addHolePunc
470
525
return err == nil && len (supported ) > 0
471
526
}, 3 * time .Second , 100 * time .Millisecond )
472
527
473
- h2 = mkHostWithStaticAutoRelay (t , relay )
528
+ h2 = mkHostWithStaticAutoRelay (t , "2.0.0.2" , 8002 , relay , router )
474
529
if addHolePuncher {
475
- hps = addHolePunchService (t , h2 , h2opt ... )
530
+ hps = addHolePunchService (t , h2 , []ma. Multiaddr { ma . StringCast ( "/ip4/2.0.0.2/udp/8002/quic-v1" )}, h2opt ... )
476
531
}
477
532
478
533
// h2 has a relay addr
@@ -492,12 +547,14 @@ func makeRelayedHosts(t *testing.T, h1opt, h2opt []holepunch.Option, addHolePunc
492
547
return
493
548
}
494
549
495
- func addHolePunchService (t * testing.T , h host.Host , opts ... holepunch.Option ) * holepunch.Service {
550
+ func addHolePunchService (t * testing.T , h host.Host , extraAddrs []ma. Multiaddr , opts ... holepunch.Option ) * holepunch.Service {
496
551
t .Helper ()
497
552
hps , err := holepunch .NewService (h , newMockIDService (t , h ), func () []ma.Multiaddr {
498
553
addrs := h .Addrs ()
499
- addrs = slices .DeleteFunc (addrs , func (a ma.Multiaddr ) bool { return ! manet .IsPublicAddr (a ) })
500
- return append (addrs , ma .StringCast ("/ip4/1.2.3.4/tcp/1234" ))
554
+ addrs = append (addrs , extraAddrs ... )
555
+ return addrs
556
+ // addrs = slices.DeleteFunc(addrs, func(a ma.Multiaddr) bool { return !manet.IsPublicAddr(a) })
557
+ // return append(addrs, ma.StringCast("/ip4/1.2.3.4/tcp/1234"))
501
558
}, opts ... )
502
559
require .NoError (t , err )
503
560
return hps
@@ -511,6 +568,19 @@ func mkHostWithHolePunchSvc(t *testing.T, opts ...holepunch.Option) (host.Host,
511
568
libp2p .ResourceManager (& network.NullResourceManager {}),
512
569
)
513
570
require .NoError (t , err )
514
- hps := addHolePunchService (t , h , opts ... )
571
+ hps := addHolePunchService (t , h , nil , opts ... )
572
+ return h , hps
573
+ }
574
+
575
+ func mkHostWithHolePunchSvc2 (t * testing.T , ipAddr string , port int , router * simconn.SimpleFirewallRouter , opts ... holepunch.Option ) (host.Host , * holepunch.Service ) {
576
+ t .Helper ()
577
+ h , err := libp2p .New (
578
+ libp2p .ListenAddrs (ma .StringCast (fmt .Sprintf ("/ip4/%s/udp/%d/quic-v1" , ipAddr , port ))),
579
+ libp2p .ForceReachabilityPrivate (),
580
+ libp2p .ResourceManager (& network.NullResourceManager {}),
581
+ quicReuseOpts (false , router ),
582
+ )
583
+ require .NoError (t , err )
584
+ hps := addHolePunchService (t , h , nil , opts ... )
515
585
return h , hps
516
586
}
0 commit comments