Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions p2p/test/transport/deadline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func TestReadWriteDeadlines(t *testing.T) {
sendBuf := make([]byte, 10<<20)
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
listener := tc.HostGenerator(t, TransportTestCaseOpts{})
defer listener.Close()
dialer := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
Expand Down
7 changes: 7 additions & 0 deletions p2p/test/transport/gating_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func TestInterceptPeerDial(t *testing.T) {
}
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
connGater := NewMockConnectionGater(ctrl)
Expand All @@ -90,6 +91,7 @@ func TestInterceptAddrDial(t *testing.T) {
}
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
connGater := NewMockConnectionGater(ctrl)
Expand All @@ -115,6 +117,7 @@ func TestInterceptSecuredOutgoing(t *testing.T) {
}
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
connGater := NewMockConnectionGater(ctrl)
Expand Down Expand Up @@ -148,6 +151,7 @@ func TestInterceptUpgradedOutgoing(t *testing.T) {
}
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
connGater := NewMockConnectionGater(ctrl)
Expand Down Expand Up @@ -184,6 +188,7 @@ func TestInterceptAccept(t *testing.T) {
}
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
connGater := NewMockConnectionGater(ctrl)
Expand Down Expand Up @@ -232,6 +237,7 @@ func TestInterceptSecuredIncoming(t *testing.T) {
}
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
connGater := NewMockConnectionGater(ctrl)
Expand Down Expand Up @@ -265,6 +271,7 @@ func TestInterceptUpgradedIncoming(t *testing.T) {
}
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
connGater := NewMockConnectionGater(ctrl)
Expand Down
1 change: 1 addition & 0 deletions p2p/test/transport/rcmgr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func TestResourceManagerIsUsed(t *testing.T) {
t.Run(tc.Name, func(t *testing.T) {
for _, testDialer := range []bool{true, false} {
t.Run(tc.Name+fmt.Sprintf(" test_dialer=%v", testDialer), func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)

var reservedMemory, releasedMemory atomic.Int32
defer func() {
Expand Down
130 changes: 130 additions & 0 deletions p2p/test/transport/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,29 @@ func selfSignedTLSConfig(t *testing.T) *tls.Config {
return tlsConfig
}

func skipIfNoIPv6(t *testing.T) {
t.Helper()
ln, err := net.Listen("tcp6", "[::1]:0")
if err != nil {
t.Skip("IPv6 loopback not available")
}
ln.Close()
}

// skipWebRTCIPv6OnWindows skips IPv6 loopback integration scenarios on Windows for
// transports whose name ends in "- IP6" (WebRTC - IP6, WebTransport - IP6).
Copy link
Copy Markdown
Collaborator

@MarcoPolo MarcoPolo Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why skip the other transports on windows and not just webrtc?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call this function skipWindows, and move the comment below to inside the host generator for ipv6 webrtc.

//
// Both transports bind the listener on /ip6/::1. Windows rejects UDP sends from
// link-local/global IPv6 interfaces to ::1 with wsasendto "The requested address is
// not valid in its context" (scope mismatch), so packets never leave the stack and
// peer connection setup times out. Linux CI continues to run these cases.
func skipWebRTCIPv6OnWindows(t *testing.T, transportName string) {
t.Helper()
if strings.HasSuffix(transportName, "- IP6") && runtime.GOOS == "windows" {
t.Skipf("%s over ::1 skipped on Windows: link-local/global IPv6 locals cause wsasendto to fail with \"The requested address is not valid in its context\" (scope mismatch). See PR discussion.", transportName)
}
}

var transportsToTest = []TransportTestCase{
{
Name: "TCP / Noise / Yamux",
Expand Down Expand Up @@ -353,11 +376,109 @@ var transportsToTest = []TransportTestCase{
return h
},
},
{
Name: "TCP / Noise / Yamux - IP6",
HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host {
skipIfNoIPv6(t)
libp2pOpts := transformOpts(opts)
libp2pOpts = append(libp2pOpts, libp2p.Security(noise.ID, noise.New))
libp2pOpts = append(libp2pOpts, libp2p.Muxer(yamux.ID, yamux.DefaultTransport))
if opts.NoListen {
libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs)
} else {
libp2pOpts = append(libp2pOpts, libp2p.ListenAddrStrings("/ip6/::1/tcp/0"))
}
h, err := libp2p.New(libp2pOpts...)
require.NoError(t, err)
return h
},
},
{
Name: "TCP / TLS / Yamux - IP6",
HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host {
skipIfNoIPv6(t)
libp2pOpts := transformOpts(opts)
libp2pOpts = append(libp2pOpts, libp2p.Security(libp2ptls.ID, libp2ptls.New))
libp2pOpts = append(libp2pOpts, libp2p.Muxer(yamux.ID, yamux.DefaultTransport))
if opts.NoListen {
libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs)
} else {
libp2pOpts = append(libp2pOpts, libp2p.ListenAddrStrings("/ip6/::1/tcp/0"))
}
h, err := libp2p.New(libp2pOpts...)
require.NoError(t, err)
return h
},
},
{
Name: "WebSocket - IP6",
HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host {
skipIfNoIPv6(t)
libp2pOpts := transformOpts(opts)
if opts.NoListen {
libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs)
} else {
libp2pOpts = append(libp2pOpts, libp2p.ListenAddrStrings("/ip6/::1/tcp/0/ws"))
}
h, err := libp2p.New(libp2pOpts...)
require.NoError(t, err)
return h
},
},
{
Name: "QUIC - IP6",
HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host {
skipIfNoIPv6(t)
libp2pOpts := transformOpts(opts)
if opts.NoListen {
libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs)
} else {
libp2pOpts = append(libp2pOpts, libp2p.ListenAddrStrings("/ip6/::1/udp/0/quic-v1"))
}
h, err := libp2p.New(libp2pOpts...)
require.NoError(t, err)
return h
},
},
{
Name: "WebTransport - IP6",
HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host {
skipIfNoIPv6(t)
skipWebRTCIPv6OnWindows(t, "WebTransport - IP6")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this in the webtransport case?

libp2pOpts := transformOpts(opts)
if opts.NoListen {
libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs)
} else {
libp2pOpts = append(libp2pOpts, libp2p.ListenAddrStrings("/ip6/::1/udp/0/quic-v1/webtransport"))
}
h, err := libp2p.New(libp2pOpts...)
require.NoError(t, err)
return h
},
},
{
Name: "WebRTC - IP6",
HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host {
skipIfNoIPv6(t)
skipWebRTCIPv6OnWindows(t, "WebRTC - IP6")
libp2pOpts := transformOpts(opts)
libp2pOpts = append(libp2pOpts, libp2p.Transport(libp2pwebrtc.New))
if opts.NoListen {
libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs)
} else {
libp2pOpts = append(libp2pOpts, libp2p.ListenAddrStrings("/ip6/::1/udp/0/webrtc-direct"))
}
h, err := libp2p.New(libp2pOpts...)
require.NoError(t, err)
return h
},
},
}

func TestPing(t *testing.T) {
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
h1 := tc.HostGenerator(t, TransportTestCaseOpts{})
h2 := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
defer h1.Close()
Expand Down Expand Up @@ -387,6 +508,7 @@ func TestBigPing(t *testing.T) {

for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
h1 := tc.HostGenerator(t, TransportTestCaseOpts{})
h2 := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
defer h1.Close()
Expand Down Expand Up @@ -515,6 +637,7 @@ func TestManyStreams(t *testing.T) {
const streamCount = 128
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
h1 := tc.HostGenerator(t, TransportTestCaseOpts{NoRcmgr: true})
h2 := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true, NoRcmgr: true})
defer h1.Close()
Expand Down Expand Up @@ -739,6 +862,7 @@ func TestMoreStreamsThanOurLimits(t *testing.T) {
func TestListenerStreamResets(t *testing.T) {
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
h1 := tc.HostGenerator(t, TransportTestCaseOpts{})
h2 := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
defer h1.Close()
Expand Down Expand Up @@ -768,6 +892,7 @@ func TestListenerStreamResets(t *testing.T) {
func TestDialerStreamResets(t *testing.T) {
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
h1 := tc.HostGenerator(t, TransportTestCaseOpts{})
h2 := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
defer h1.Close()
Expand Down Expand Up @@ -799,6 +924,7 @@ func TestDialerStreamResets(t *testing.T) {
func TestStreamReadDeadline(t *testing.T) {
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
h1 := tc.HostGenerator(t, TransportTestCaseOpts{})
h2 := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
defer h1.Close()
Expand Down Expand Up @@ -853,6 +979,7 @@ func TestDiscoverPeerIDFromSecurityNegotiation(t *testing.T) {

for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
h1 := tc.HostGenerator(t, TransportTestCaseOpts{})
h2 := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
defer h1.Close()
Expand Down Expand Up @@ -897,6 +1024,7 @@ func TestCloseConnWhenBlocked(t *testing.T) {
continue
}
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockRcmgr := mocknetwork.NewMockResourceManager(ctrl)
Expand Down Expand Up @@ -977,6 +1105,7 @@ func TestConnDroppedWhenBlocked(t *testing.T) {
func TestConnClosedWhenRemoteCloses(t *testing.T) {
for _, tc := range transportsToTest {
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
server := tc.HostGenerator(t, TransportTestCaseOpts{})
client := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
defer server.Close()
Expand Down Expand Up @@ -1017,6 +1146,7 @@ func TestErrorCodes(t *testing.T) {
continue
}
t.Run(tc.Name, func(t *testing.T) {
skipWebRTCIPv6OnWindows(t, tc.Name)
server := tc.HostGenerator(t, TransportTestCaseOpts{})
client := tc.HostGenerator(t, TransportTestCaseOpts{NoListen: true})
defer server.Close()
Expand Down