Unverified Commit 4ef86561 authored by sukun's avatar sukun Committed by GitHub

net: restrict unicast ip6 public address space (#235)

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.
parent cece70d4
......@@ -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)
}
......@@ -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) {
......
......@@ -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
......
......@@ -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) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment