diff --git a/net/ip.go b/net/ip.go index ef936c5..def9321 100644 --- a/net/ip.go +++ b/net/ip.go @@ -123,20 +123,10 @@ func zoneless(m ma.Multiaddr) ma.Multiaddr { } } -var nat64WellKnownPrefix net.IPNet - -func init() { - _, np, err := net.ParseCIDR("64:ff9b::/96") - if err != nil { - panic(err) - } - nat64WellKnownPrefix = *np -} - // IsNAT64IPv4ConvertedIPv6Addr returns whether addr is a well-known prefix "64:ff9b::/96" addr // used for NAT64 Translation. See RFC 6052 func IsNAT64IPv4ConvertedIPv6Addr(addr ma.Multiaddr) bool { c, _ := ma.SplitFirst(addr) return c != nil && c.Protocol().Code == ma.P_IP6 && - nat64WellKnownPrefix.Contains(net.IP(c.RawValue())) + inAddrRange(c.RawValue(), nat64) } diff --git a/net/ip_test.go b/net/ip_test.go index 9aa9954..4d394b7 100644 --- a/net/ip_test.go +++ b/net/ip_test.go @@ -38,6 +38,11 @@ func TestIsWellKnownPrefixIPv4ConvertedIPv6Address(t *testing.T) { want: false, failureReason: "64:ff9b::1 is not well-known prefix", }, + { + addr: ma.StringCast("/ip6/64:ff9b:1::1:192.0.1.2/tcp/1234"), + want: true, + failureReason: "64:ff9b:1::1 is allowed for NAT64 translation", + }, } for i, tc := range cases { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { diff --git a/net/private.go b/net/private.go index 9f966de..af55c2a 100644 --- a/net/private.go +++ b/net/private.go @@ -44,9 +44,22 @@ var unroutableCIDR4 = []string{ "255.255.255.255/32", } var unroutableCIDR6 = []string{ - "ff00::/8", + "ff00::/8", // multicast + "2001:db8::/32", // documentation } +var globalUnicast []*net.IPNet +var globalUnicastCIDR6 = []string{ + "2000::/3", +} + +var nat64CIDRs = []string{ + "64:ff9b:1::/48", // RFC 8215 + "64:ff9b::/96", // RFC 6052 +} + +var nat64 []*net.IPNet + // unResolvableDomains do not resolve to an IP address. // Ref: https://en.wikipedia.org/wiki/Special-use_domain_name#Reserved_domain_names var unResolvableDomains = []string{ @@ -82,6 +95,8 @@ func init() { Private6 = parseCIDR(privateCIDR6) Unroutable4 = parseCIDR(unroutableCIDR4) Unroutable6 = parseCIDR(unroutableCIDR6) + globalUnicast = parseCIDR(globalUnicastCIDR6) + nat64 = parseCIDR(nat64CIDRs) } func parseCIDR(cidrs []string) []*net.IPNet { @@ -109,7 +124,23 @@ func IsPublicAddr(a ma.Multiaddr) bool { isPublic = !inAddrRange(ip, Private4) && !inAddrRange(ip, Unroutable4) case ma.P_IP6: ip := net.IP(c.RawValue()) - isPublic = !inAddrRange(ip, Private6) && !inAddrRange(ip, Unroutable6) + // IP6 documentation prefix(part of Unroutable6) is a subset of the ip6 + // global unicast allocation so we ensure that it's not a documentation + // prefix by diffing with Unroutable6 + isPublicUnicastAddr := inAddrRange(ip, globalUnicast) && !inAddrRange(ip, Unroutable6) + if isPublicUnicastAddr { + isPublic = true + return false + } + // The WellKnown NAT64 prefix(RFC 6052) can only reference a public IPv4 + // address. + // The Local use NAT64 prefix(RFC 8215) can reference private IPv4 + // addresses. But since the translation from Local use NAT64 prefix to IPv4 + // address is left to the user we have no way of knowing which IPv4 address + // is referenced. We count these as Public addresses because a false + // negative for this method here is generally worse than a false positive. + isPublic = inAddrRange(ip, nat64) + return false case ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR: dnsAddr := c.Value() isPublic = true diff --git a/net/private_test.go b/net/private_test.go index fdc34dd..c76b5db 100644 --- a/net/private_test.go +++ b/net/private_test.go @@ -53,6 +53,21 @@ func TestIsPublicAddr(t *testing.T) { isPublic: false, isPrivate: true, }, + { + addr: ma.StringCast("/ip6/2400::1/tcp/10"), + isPublic: true, + isPrivate: false, + }, + { + addr: ma.StringCast("/ip6/2001:db8::42/tcp/10"), + isPublic: false, + isPrivate: false, + }, + { + addr: ma.StringCast("/ip6/64:ff9b::1.1.1.1/tcp/10"), + isPublic: true, + isPrivate: false, + }, } for i, tt := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {