Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: restrict unicast ip6 public address space #235

Merged
merged 2 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 1 addition & 11 deletions net/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
5 changes: 5 additions & 0 deletions net/ip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
35 changes: 33 additions & 2 deletions net/private.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions net/private_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Jorropo marked this conversation as resolved.
Show resolved Hide resolved
}
for i, tt := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
Expand Down