From f1a42fe2d1078143755f77adcff3150d07364714 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Thu, 7 Dec 2023 17:08:29 +0900 Subject: [PATCH 1/2] feat: support connect with ipv6 link-local address --- p2p/net/reuseport/dialer.go | 2 +- p2p/net/swarm/swarm_dial.go | 15 ++++++-- p2p/transport/tcp/tcp.go | 5 ++- p2p/transport/tcp/tcp_test.go | 64 +++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/p2p/net/reuseport/dialer.go b/p2p/net/reuseport/dialer.go index 2efc02d393..102f3e75cc 100644 --- a/p2p/net/reuseport/dialer.go +++ b/p2p/net/reuseport/dialer.go @@ -50,7 +50,7 @@ func (d *dialer) DialContext(ctx context.Context, network, addr string) (net.Con return nil, err } ip := tcpAddr.IP - if !ip.IsLoopback() && !ip.IsGlobalUnicast() { + if !ip.IsLoopback() && !ip.IsGlobalUnicast() && !ip.IsLinkLocalUnicast() { return nil, fmt.Errorf("undialable IP: %s", ip) } diff --git a/p2p/net/swarm/swarm_dial.go b/p2p/net/swarm/swarm_dial.go index 3fb15383a2..bce12e7376 100644 --- a/p2p/net/swarm/swarm_dial.go +++ b/p2p/net/swarm/swarm_dial.go @@ -489,8 +489,19 @@ func (s *Swarm) filterKnownUndialables(p peer.ID, addrs []ma.Multiaddr) (goodAdd } return true }, - // TODO: Consider allowing link-local addresses - func(addr ma.Multiaddr) bool { return !manet.IsIP6LinkLocal(addr) }, + func(addr ma.Multiaddr) bool { + if manet.IsIP6LinkLocal(addr) { + var hasZone bool + ma.ForEach(addr, func(c ma.Component) bool { + if c.Protocol().Code == ma.P_IP6ZONE { + hasZone = true + } + return false + }) + return hasZone + } + return true + }, func(addr ma.Multiaddr) bool { if s.gater != nil && !s.gater.InterceptAddrDial(p, addr) { addrErrs = append(addrErrs, TransportError{Address: addr, Cause: ErrGaterDisallowedConnection}) diff --git a/p2p/transport/tcp/tcp.go b/p2p/transport/tcp/tcp.go index d52bb96019..45b0274a6f 100644 --- a/p2p/transport/tcp/tcp.go +++ b/p2p/transport/tcp/tcp.go @@ -152,7 +152,10 @@ func NewTCPTransport(upgrader transport.Upgrader, rcmgr network.ResourceManager, return tr, nil } -var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_TCP)) +var dialMatcher = mafmt.Or( + mafmt.And(mafmt.IP, mafmt.Base(ma.P_TCP)), + mafmt.And(mafmt.Base(ma.P_IP6ZONE), mafmt.IP, mafmt.Base(ma.P_TCP)), +) // CanDial returns true if this transport believes it can dial the given // multiaddr. diff --git a/p2p/transport/tcp/tcp_test.go b/p2p/transport/tcp/tcp_test.go index d96c34317b..00e27c6e13 100644 --- a/p2p/transport/tcp/tcp_test.go +++ b/p2p/transport/tcp/tcp_test.go @@ -3,6 +3,7 @@ package tcp import ( "context" "errors" + "net" "testing" "github.com/libp2p/go-libp2p/core/crypto" @@ -46,6 +47,69 @@ func TestTcpTransport(t *testing.T) { envReuseportVal = true } +func TestTcpTransportCanDialToLinkLocalAddress(t *testing.T) { + addr := ma.StringCast("/ip6zone/eth0/ip6/fe80::fc54:ff:fe43:e553/tcp/1234") + + var u transport.Upgrader + tpt, err := NewTCPTransport(u, nil) + require.NoError(t, err) + + if !tpt.CanDial(addr) { + t.Fatal("should be able to dial ip6zone") + } +} + +func TestTcpTransportWithLinkLocalAddress(t *testing.T) { + ifaces, err := net.Interfaces() + if err != nil { + t.Error(err) + return + } + + var targetAddr ma.Multiaddr + +findInterface: + for _, iface := range ifaces { + addrs, err := iface.Addrs() + if err == nil { + for _, a := range addrs { + addr, err := manet.FromNetAddr(a) + if err == nil { + if manet.IsIP6LinkLocal(addr) { + targetAddr = ma.StringCast("/ip6zone/" + iface.Name + addr.String()) + break findInterface + } + } + } + } + } + + if targetAddr == nil { + t.Fail() + return + } + + for i := 0; i < 2; i++ { + peerA, ia := makeInsecureMuxer(t) + _, ib := makeInsecureMuxer(t) + + ua, err := tptu.New(ia, muxers, nil, nil, nil) + require.NoError(t, err) + ta, err := NewTCPTransport(ua, nil) + require.NoError(t, err) + ub, err := tptu.New(ib, muxers, nil, nil, nil) + require.NoError(t, err) + tb, err := NewTCPTransport(ub, nil) + require.NoError(t, err) + + zero := targetAddr.String() + "/tcp/0" + ttransport.SubtestTransport(t, ta, tb, zero, peerA) + + envReuseportVal = false + } + envReuseportVal = true +} + func TestTcpTransportWithMetrics(t *testing.T) { peerA, ia := makeInsecureMuxer(t) _, ib := makeInsecureMuxer(t) From 11a7e125384ce45ca944c0bbca1aa2284ce5dd0d Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Thu, 7 Dec 2023 21:31:35 +0900 Subject: [PATCH 2/2] feat: support ip6zone address for quic --- p2p/transport/quic/conn_test.go | 29 ++++++++++++++++++++++++++++ p2p/transport/quic/transport.go | 5 ++++- p2p/transport/quic/transport_test.go | 1 + 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/p2p/transport/quic/conn_test.go b/p2p/transport/quic/conn_test.go index d3e27a7e16..305e0e8d8a 100644 --- a/p2p/transport/quic/conn_test.go +++ b/p2p/transport/quic/conn_test.go @@ -21,6 +21,7 @@ import ( "github.com/libp2p/go-libp2p/p2p/transport/quicreuse" ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/stretchr/testify/require" @@ -122,6 +123,34 @@ func testHandshake(t *testing.T, tc *connTestCase) { defer ln.Close() handshake(t, ln) }) + + ifaces, _ := net.Interfaces() + var linkLocalAddr ma.Multiaddr +findInterface: + for _, iface := range ifaces { + addrs, err := iface.Addrs() + if err == nil { + for _, a := range addrs { + addr, err := manet.FromNetAddr(a) + if err == nil { + if manet.IsIP6LinkLocal(addr) { + linkLocalAddr = ma.StringCast("/ip6zone/" + iface.Name + addr.String()) + break findInterface + } + } + } + } + } + + if linkLocalAddr == nil { + t.Fail() + return + } + t.Run("on IPv6 link-local address", func(t *testing.T) { + ln := runServer(t, serverTransport, linkLocalAddr.String() + "/udp/0/quic-v1") + defer ln.Close() + handshake(t, ln) + }) } func TestResourceManagerSuccess(t *testing.T) { diff --git a/p2p/transport/quic/transport.go b/p2p/transport/quic/transport.go index 18d198bbea..f71b3433d4 100644 --- a/p2p/transport/quic/transport.go +++ b/p2p/transport/quic/transport.go @@ -269,7 +269,10 @@ loop: } // Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic-v1 -var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC_V1)) +var dialMatcher = mafmt.Or( + mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC_V1)), + mafmt.And(mafmt.Base(ma.P_IP6ZONE), mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC_V1)), +) // CanDial determines if we can dial to an address func (t *transport) CanDial(addr ma.Multiaddr) bool { diff --git a/p2p/transport/quic/transport_test.go b/p2p/transport/quic/transport_test.go index 41e7e4e416..fe6bc14c8e 100644 --- a/p2p/transport/quic/transport_test.go +++ b/p2p/transport/quic/transport_test.go @@ -51,6 +51,7 @@ func TestCanDial(t *testing.T) { valid := []string{ "/ip4/127.0.0.1/udp/1234/quic-v1", "/ip4/5.5.5.5/udp/0/quic-v1", + "/ip6zone/eth0/ip6/fe80::fc54:ff:fe43:e553/udp/1234/quic-v1", } for _, s := range invalid { invalidAddr, err := ma.NewMultiaddr(s)