diff --git a/p2p/protocol/identify/obsaddr.go b/p2p/protocol/identify/obsaddr.go index 451af096d9..012f48ca9e 100644 --- a/p2p/protocol/identify/obsaddr.go +++ b/p2p/protocol/identify/obsaddr.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "sort" + "strings" "sync" "time" @@ -378,6 +379,11 @@ func shouldRecordObservation(host addrsProvider, network listenAddrsProvider, co return false } + // Provided by NAT64 peers, these addresses are specific to the peer and not publicly routable + if isNAT64IPv4ConvertedIPv6Addr(observed) { + return false + } + // we should only use ObservedAddr when our connection's LocalAddr is one // of our ListenAddrs. If we Dial out using an ephemeral addr, knowing that // address's external mapping is not very useful because the port will not be @@ -435,6 +441,20 @@ func shouldRecordObservation(host addrsProvider, network listenAddrsProvider, co return true } +// isNAT64IPv4ConvertedIPv6Addr returns whether addr is an IPv6 address that begins with +// the well known prefix "64:ff9b" used for NAT64 Translation +// see RFC 6052 +func isNAT64IPv4ConvertedIPv6Addr(addr ma.Multiaddr) bool { + ip, err := addr.ValueForProtocol(ma.P_IP6) + if err != nil { + return false + } + if strings.HasPrefix(ip, "64:ff9b::") { + return true + } + return false +} + func (oas *ObservedAddrManager) maybeRecordObservation(conn network.Conn, observed ma.Multiaddr) { shouldRecord := shouldRecordObservation(oas.host, oas.host.Network(), conn, observed) if shouldRecord { diff --git a/p2p/protocol/identify/obsaddr_glass_test.go b/p2p/protocol/identify/obsaddr_glass_test.go index 497b08e0bd..308e99824a 100644 --- a/p2p/protocol/identify/obsaddr_glass_test.go +++ b/p2p/protocol/identify/obsaddr_glass_test.go @@ -4,6 +4,7 @@ package identify // can access internal types. import ( + "fmt" "testing" ma "github.com/multiformats/go-multiaddr" @@ -103,3 +104,39 @@ func TestShouldRecordObservationWithWebTransport(t *testing.T) { require.True(t, shouldRecordObservation(h, h, c, observedAddr)) } + +func TestIsWellKnownPrefixIPv4ConvertedIPv6Address(t *testing.T) { + cases := []struct { + addr ma.Multiaddr + want bool + failureReason string + }{ + { + addr: ma.StringCast("/ip4/1.2.3.4/tcp/1234"), + want: false, + failureReason: "ip4 addresses should return false", + }, + { + addr: ma.StringCast("/ip6/1::4/tcp/1234"), + want: false, + failureReason: "ip6 addresses doesn't have wellknown prefix", + }, + { + addr: ma.StringCast("/ip6/::1/tcp/1234"), + want: false, + failureReason: "localhost addresses should return false", + }, + { + addr: ma.StringCast("/ip6/64:ff9b::192.0.1.2/tcp/1234"), + want: true, + failureReason: "ip6 address begins with well-known prefix", + }, + } + for i, tc := range cases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + if isNAT64IPv4ConvertedIPv6Addr(tc.addr) != tc.want { + t.Fatalf("%s %s", tc.addr, tc.failureReason) + } + }) + } +}