Skip to content

Commit 077f575

Browse files
committed
net: restrict unicast ip6 public address space
Global Unicast IPv6 addresses only belong to prefix 2000::/3. This change also classifies NAT64 prefixes as Public Addresses as they may reference public IPv4 addresses.
1 parent e2ad674 commit 077f575

File tree

4 files changed

+40
-13
lines changed

4 files changed

+40
-13
lines changed

net/ip.go

+1-11
Original file line numberDiff line numberDiff line change
@@ -123,20 +123,10 @@ func zoneless(m ma.Multiaddr) ma.Multiaddr {
123123
}
124124
}
125125

126-
var nat64WellKnownPrefix net.IPNet
127-
128-
func init() {
129-
_, np, err := net.ParseCIDR("64:ff9b::/96")
130-
if err != nil {
131-
panic(err)
132-
}
133-
nat64WellKnownPrefix = *np
134-
}
135-
136126
// IsNAT64IPv4ConvertedIPv6Addr returns whether addr is a well-known prefix "64:ff9b::/96" addr
137127
// used for NAT64 Translation. See RFC 6052
138128
func IsNAT64IPv4ConvertedIPv6Addr(addr ma.Multiaddr) bool {
139129
c, _ := ma.SplitFirst(addr)
140130
return c != nil && c.Protocol().Code == ma.P_IP6 &&
141-
nat64WellKnownPrefix.Contains(net.IP(c.RawValue()))
131+
inAddrRange(c.RawValue(), nat64)
142132
}

net/ip_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func TestIsWellKnownPrefixIPv4ConvertedIPv6Address(t *testing.T) {
3838
want: false,
3939
failureReason: "64:ff9b::1 is not well-known prefix",
4040
},
41+
{
42+
addr: ma.StringCast("/ip6/64:ff9b:1::1:192.0.1.2/tcp/1234"),
43+
want: true,
44+
failureReason: "64:ff9b:1::1 is allowed for NAT64 translation",
45+
},
4146
}
4247
for i, tc := range cases {
4348
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {

net/private.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,22 @@ var unroutableCIDR4 = []string{
4444
"255.255.255.255/32",
4545
}
4646
var unroutableCIDR6 = []string{
47-
"ff00::/8",
47+
"ff00::/8", // multicast
48+
"2001:db8::/32", // documentation
4849
}
4950

51+
var globalUnicast []*net.IPNet
52+
var globalUnicastCIDR6 = []string{
53+
"2000::/3",
54+
}
55+
56+
var nat64CIDRs = []string{
57+
"64:ff9b:1::/48", // RFC 8215
58+
"64:ff9b::/96", // RFC 6052
59+
}
60+
61+
var nat64 []*net.IPNet
62+
5063
// unResolvableDomains do not resolve to an IP address.
5164
// Ref: https://en.wikipedia.org/wiki/Special-use_domain_name#Reserved_domain_names
5265
var unResolvableDomains = []string{
@@ -82,6 +95,8 @@ func init() {
8295
Private6 = parseCIDR(privateCIDR6)
8396
Unroutable4 = parseCIDR(unroutableCIDR4)
8497
Unroutable6 = parseCIDR(unroutableCIDR6)
98+
globalUnicast = parseCIDR(globalUnicastCIDR6)
99+
nat64 = parseCIDR(nat64CIDRs)
85100
}
86101

87102
func parseCIDR(cidrs []string) []*net.IPNet {
@@ -109,7 +124,9 @@ func IsPublicAddr(a ma.Multiaddr) bool {
109124
isPublic = !inAddrRange(ip, Private4) && !inAddrRange(ip, Unroutable4)
110125
case ma.P_IP6:
111126
ip := net.IP(c.RawValue())
112-
isPublic = !inAddrRange(ip, Private6) && !inAddrRange(ip, Unroutable6)
127+
// NAT64 addresses reference public IPv4 addresses.
128+
// Unroutable IPv6 documentation prefix is a subset of the globalUnicast prefix.
129+
isPublic = (inAddrRange(ip, globalUnicast) || inAddrRange(ip, nat64)) && !inAddrRange(ip, Unroutable6)
113130
case ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR:
114131
dnsAddr := c.Value()
115132
isPublic = true

net/private_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ func TestIsPublicAddr(t *testing.T) {
5353
isPublic: false,
5454
isPrivate: true,
5555
},
56+
{
57+
addr: ma.StringCast("/ip6/2400::1/tcp/10"),
58+
isPublic: true,
59+
isPrivate: false,
60+
},
61+
{
62+
addr: ma.StringCast("/ip6/2001:db8::42/tcp/10"),
63+
isPublic: false,
64+
isPrivate: false,
65+
},
66+
{
67+
addr: ma.StringCast("/ip6/64:ff9b::1.1.1.1/tcp/10"),
68+
isPublic: true,
69+
isPrivate: false,
70+
},
5671
}
5772
for i, tt := range tests {
5873
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {

0 commit comments

Comments
 (0)