From 6a4f12bbba84326841020f416985d3fb60106139 Mon Sep 17 00:00:00 2001 From: asmit27rai Date: Mon, 1 Sep 2025 22:03:25 +0530 Subject: [PATCH 1/3] feat: add IPv6 variants to transport integration tests - Duplicate each existing transport test case with IPv6 localhost (::1) addresses - Append (IPv6) to the test case name for clarity - Ensure IPv6 ListenAddrStrings match the IPv4 equivalents - Maintain existing security, muxer, and transport options for IPv6 variants --- p2p/test/transport/transport_test.go | 236 +++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/p2p/test/transport/transport_test.go b/p2p/test/transport/transport_test.go index f529488f05..e60c59376c 100644 --- a/p2p/test/transport/transport_test.go +++ b/p2p/test/transport/transport_test.go @@ -133,6 +133,22 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "TCP / Noise / Yamux (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + 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", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -149,6 +165,22 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "TCP / TLS / Yamux (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + 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: "TCP-Shared / TLS / Yamux", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -166,6 +198,23 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "TCP-Shared / TLS / Yamux (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + libp2pOpts := transformOpts(opts) + libp2pOpts = append(libp2pOpts, libp2p.ShareTCPListener()) + 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: "TCP-Shared-WithMetrics / TLS / Yamux", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -184,6 +233,24 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "TCP-Shared-WithMetrics / TLS / Yamux (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + libp2pOpts := transformOpts(opts) + libp2pOpts = append(libp2pOpts, libp2p.ShareTCPListener()) + libp2pOpts = append(libp2pOpts, libp2p.Security(libp2ptls.ID, libp2ptls.New)) + libp2pOpts = append(libp2pOpts, libp2p.Muxer(yamux.ID, yamux.DefaultTransport)) + libp2pOpts = append(libp2pOpts, libp2p.Transport(tcp.NewTCPTransport, tcp.WithMetrics())) + 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-WithMetrics / TLS / Yamux", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -201,6 +268,23 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "TCP-WithMetrics / TLS / Yamux (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + libp2pOpts := transformOpts(opts) + libp2pOpts = append(libp2pOpts, libp2p.Security(libp2ptls.ID, libp2ptls.New)) + libp2pOpts = append(libp2pOpts, libp2p.Muxer(yamux.ID, yamux.DefaultTransport)) + libp2pOpts = append(libp2pOpts, libp2p.Transport(tcp.NewTCPTransport, tcp.WithMetrics())) + 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-Shared", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -216,6 +300,21 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "WebSocket-Shared (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + libp2pOpts := transformOpts(opts) + libp2pOpts = append(libp2pOpts, libp2p.ShareTCPListener()) + 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: "WebSocket-Secured-Shared", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -233,6 +332,23 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "WebSocket-Secured-Shared (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + libp2pOpts := transformOpts(opts) + libp2pOpts = append(libp2pOpts, libp2p.ShareTCPListener()) + if opts.NoListen { + config := tls.Config{InsecureSkipVerify: true} + libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs, libp2p.Transport(websocket.New, websocket.WithTLSClientConfig(&config))) + } else { + config := selfSignedTLSConfig(t) + libp2pOpts = append(libp2pOpts, libp2p.ListenAddrStrings("/ip6/::1/tcp/0/sni/localhost/tls/ws"), libp2p.Transport(websocket.New, websocket.WithTLSConfig(config))) + } + h, err := libp2p.New(libp2pOpts...) + require.NoError(t, err) + return h + }, + }, { Name: "WebSocket", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -247,6 +363,20 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "WebSocket (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + 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: "WebSocket-Secured", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -263,6 +393,22 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "WebSocket-Secured (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + libp2pOpts := transformOpts(opts) + if opts.NoListen { + config := tls.Config{InsecureSkipVerify: true} + libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs, libp2p.Transport(websocket.New, websocket.WithTLSClientConfig(&config))) + } else { + config := selfSignedTLSConfig(t) + libp2pOpts = append(libp2pOpts, libp2p.ListenAddrStrings("/ip6/::1/tcp/0/sni/localhost/tls/ws"), libp2p.Transport(websocket.New, websocket.WithTLSConfig(config))) + } + h, err := libp2p.New(libp2pOpts...) + require.NoError(t, err) + return h + }, + }, { Name: "QUIC", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -277,6 +423,20 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "QUIC (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + 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: "QUIC-CustomReuse", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -300,6 +460,29 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "QUIC-CustomReuse (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + libp2pOpts := transformOpts(opts) + if opts.NoListen { + libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs, libp2p.QUICReuse(quicreuse.NewConnManager)) + } else { + qr := libp2p.QUICReuse(quicreuse.NewConnManager) + if !opts.NoRcmgr && opts.ResourceManager != nil { + qr = libp2p.QUICReuse( + quicreuse.NewConnManager, + quicreuse.VerifySourceAddress(opts.ResourceManager.VerifySourceAddress)) + } + libp2pOpts = append(libp2pOpts, + qr, + libp2p.ListenAddrStrings("/ip6/::1/udp/0/quic-v1"), + ) + } + h, err := libp2p.New(libp2pOpts...) + require.NoError(t, err) + return h + }, + }, { Name: "WebTransport", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -314,6 +497,20 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "WebTransport (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + 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: "WebTransport-CustomReuse", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -338,6 +535,30 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "WebTransport-CustomReuse (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + libp2pOpts := transformOpts(opts) + if opts.NoListen { + libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs, libp2p.QUICReuse(quicreuse.NewConnManager)) + } else { + qr := libp2p.QUICReuse(quicreuse.NewConnManager) + if !opts.NoRcmgr && opts.ResourceManager != nil { + qr = libp2p.QUICReuse( + quicreuse.NewConnManager, + quicreuse.VerifySourceAddress(opts.ResourceManager.VerifySourceAddress), + ) + } + libp2pOpts = append(libp2pOpts, + qr, + libp2p.ListenAddrStrings("/ip6/::1/udp/0/quic-v1/webtransport"), + ) + } + h, err := libp2p.New(libp2pOpts...) + require.NoError(t, err) + return h + }, + }, { Name: "WebRTC", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { @@ -353,6 +574,21 @@ var transportsToTest = []TransportTestCase{ return h }, }, + { + Name: "WebRTC (IPv6)", + HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + 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) { From 62818ea26a34135425539d1208c993b737f53216 Mon Sep 17 00:00:00 2001 From: asmit27rai Date: Tue, 9 Sep 2025 17:59:52 +0530 Subject: [PATCH 2/3] CI/CD Fix --- p2p/test/transport/transport_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/p2p/test/transport/transport_test.go b/p2p/test/transport/transport_test.go index e60c59376c..75fb1af343 100644 --- a/p2p/test/transport/transport_test.go +++ b/p2p/test/transport/transport_test.go @@ -21,6 +21,7 @@ import ( "sync/atomic" "testing" "time" + "os" libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls" @@ -116,6 +117,19 @@ func selfSignedTLSConfig(t *testing.T) *tls.Config { return tlsConfig } +func isWebRTCIPv6Supported() bool { + if os.Getenv("CI") != "" || os.Getenv("GITHUB_ACTIONS") != "" { + return false + } + + conn, err := net.Dial("tcp6", "[::1]:0") + if err != nil { + return false + } + conn.Close() + return true +} + var transportsToTest = []TransportTestCase{ { Name: "TCP / Noise / Yamux", @@ -577,6 +591,9 @@ var transportsToTest = []TransportTestCase{ { Name: "WebRTC (IPv6)", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { + if !isWebRTCIPv6Supported() { + t.Skip("WebRTC IPv6 not supported in this environment") + } libp2pOpts := transformOpts(opts) libp2pOpts = append(libp2pOpts, libp2p.Transport(libp2pwebrtc.New)) if opts.NoListen { From 089b5c2ffcbfa98d929920207acf61561d7fb6a5 Mon Sep 17 00:00:00 2001 From: asmit27rai Date: Wed, 10 Sep 2025 00:16:26 +0530 Subject: [PATCH 3/3] Fix2 --- p2p/test/transport/transport_test.go | 76 +++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/p2p/test/transport/transport_test.go b/p2p/test/transport/transport_test.go index 75fb1af343..1da52c7615 100644 --- a/p2p/test/transport/transport_test.go +++ b/p2p/test/transport/transport_test.go @@ -130,6 +130,78 @@ func isWebRTCIPv6Supported() bool { return true } +func isIPv6WebRTCSupported(t *testing.T) bool { + t.Helper() + + if os.Getenv("CI") != "" || os.Getenv("GITHUB_ACTIONS") != "" { + t.Log("Running in CI environment - performing IPv6 connectivity checks") + + addrs, err := net.LookupHost("localhost") + if err != nil { + t.Logf("Cannot resolve localhost: %v", err) + return false + } + + hasIPv6Localhost := false + for _, addr := range addrs { + if strings.Contains(addr, "::1") { + hasIPv6Localhost = true + break + } + } + + if !hasIPv6Localhost { + t.Log("IPv6 localhost (::1) not found in localhost resolution") + return false + } + + conn, err := net.ListenPacket("udp6", "[::1]:0") + if err != nil { + t.Logf("Cannot create IPv6 UDP socket: %v", err) + return false + } + conn.Close() + + listener, err := net.Listen("tcp6", "[::1]:0") + if err != nil { + t.Logf("Cannot bind to IPv6 localhost TCP: %v", err) + return false + } + listener.Close() + + done := make(chan bool, 1) + go func() { + defer func() { done <- true }() + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + dialer := &net.Dialer{} + conn, err := dialer.DialContext(ctx, "tcp6", "[::1]:22") // SSH port, commonly open + if err == nil { + conn.Close() + } + }() + + select { + case <-done: + case <-time.After(3 * time.Second): + t.Log("IPv6 connectivity test timed out - network stack may be incomplete") + return false + } + + t.Log("IPv6 connectivity checks passed") + return true + } + + conn, err := net.ListenPacket("udp6", "[::1]:0") + if err != nil { + t.Logf("IPv6 UDP not available: %v", err) + return false + } + conn.Close() + return true +} + var transportsToTest = []TransportTestCase{ { Name: "TCP / Noise / Yamux", @@ -591,8 +663,8 @@ var transportsToTest = []TransportTestCase{ { Name: "WebRTC (IPv6)", HostGenerator: func(t *testing.T, opts TransportTestCaseOpts) host.Host { - if !isWebRTCIPv6Supported() { - t.Skip("WebRTC IPv6 not supported in this environment") + if !isIPv6WebRTCSupported(t) { + t.Skip("WebRTC over IPv6 not supported in this environment - skipping to avoid false failures") } libp2pOpts := transformOpts(opts) libp2pOpts = append(libp2pOpts, libp2p.Transport(libp2pwebrtc.New))