addrmgr: treat RFC7343 (ORCHIDv2) addresses as unroutable
What changed, and why it matters
This commit fixes a network-address classification bug in btcd, a Bitcoin implementation. It makes sure a special type of IPv6 address (ORCHIDv2, defined in RFC 7343) is treated as non-routable, so btcd will not try to connect to it as a regular peer. The change aligns btcd with Bitcoin Core and was found by comparing the two implementations through differential fuzzing.
Review whether any other unroutable IPv6 special-use prefixes are missing from IsRoutable, and consider backporting this change to stable btcd releases. No immediate emergency response appears necessary.
Security signals we found
Differential fuzzing discovery against Bitcoin Core
Address-routing classification change
Prevents treating ORCHIDv2 overlay identifiers as routable peers
Aligns with upstream Bitcoin Core behavior
Evidence from the diff
The patch adds detection for the IPv6 ORCHIDv2 prefix 2001:20::/28 (RFC 7343) in addrmgr/network.go. A new IsRFC7343 helper and rfc7343Net variable are introduced, and IsRoutable now excludes RFC 7343 addresses. Tests are updated to cover the new classification and to fix an unrelated incorrect error message in TestIPTypes (IsRFC1918 -> IsRFC6145).
Changed components
addrmgr/network.goaddrmgr/network_test.goInspect captured patch +42 / −24
diff --git a/addrmgr/network.go b/addrmgr/network.go
index 95555a6..a99d784 100644
--- a/addrmgr/network.go
+++ b/addrmgr/network.go
@@ -48,6 +48,10 @@ var (
// RFC4843 (2001:10::/28).
rfc4843Net = ipNet("2001:10::", 28, 128)
+ // rfc7343Net specifies the IPv6 ORCHIDv2 address block as defined by
+ // RFC7343 (2001:20::/28).
+ rfc7343Net = ipNet("2001:20::", 28, 128)
+
// rfc4862Net specifies the IPv6 stateless address autoconfiguration
// address block as defined by RFC4862 (FE80::/64).
rfc4862Net = ipNet("FE80::", 64, 128)
@@ -171,6 +175,12 @@ func IsRFC4843(na *wire.NetAddress) bool {
return rfc4843Net.Contains(na.IP)
}
+// IsRFC7343 returns whether or not the passed address is part of the IPv6
+// ORCHIDv2 range as defined by RFC7343 (2001:20::/28).
+func IsRFC7343(na *wire.NetAddress) bool {
+ return rfc7343Net.Contains(na.IP)
+}
+
// IsRFC4862 returns whether or not the passed address is part of the IPv6
// stateless address autoconfiguration range as defined by RFC4862 (FE80::/64).
func IsRFC4862(na *wire.NetAddress) bool {
@@ -233,8 +243,8 @@ func IsRoutable(na *wire.NetAddressV2) bool {
lna := na.ToLegacy()
return IsValid(lna) && !(IsRFC1918(lna) || IsRFC2544(lna) ||
IsRFC3927(lna) || IsRFC4862(lna) || IsRFC3849(lna) ||
- IsRFC4843(lna) || IsRFC5737(lna) || IsRFC6598(lna) ||
- IsLocal(lna) || (IsRFC4193(lna) &&
+ IsRFC4843(lna) || IsRFC7343(lna) || IsRFC5737(lna) ||
+ IsRFC6598(lna) || IsLocal(lna) || (IsRFC4193(lna) &&
!IsOnionCatTor(lna)))
}
diff --git a/addrmgr/network_test.go b/addrmgr/network_test.go
index f4bc5d8..0b7b33c 100644
--- a/addrmgr/network_test.go
+++ b/addrmgr/network_test.go
@@ -31,6 +31,7 @@ func TestIPTypes(t *testing.T) {
rfc6052 bool
rfc6145 bool
rfc6598 bool
+ rfc7343 bool
local bool
valid bool
routable bool
@@ -38,53 +39,55 @@ func TestIPTypes(t *testing.T) {
newIPTest := func(ip string, rfc1918, rfc2544, rfc3849, rfc3927, rfc3964,
rfc4193, rfc4380, rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598,
- local, valid, routable bool) ipTest {
+ rfc7343, local, valid, routable bool) ipTest {
nip := net.ParseIP(ip)
na := *wire.NewNetAddressIPPort(nip, 8333, wire.SFNodeNetwork)
test := ipTest{na, rfc1918, rfc2544, rfc3849, rfc3927, rfc3964, rfc4193, rfc4380,
- rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598, local, valid, routable}
+ rfc4843, rfc4862, rfc5737, rfc6052, rfc6145, rfc6598, rfc7343, local, valid, routable}
return test
}
tests := []ipTest{
newIPTest("10.255.255.255", true, false, false, false, false, false,
- false, false, false, false, false, false, false, false, true, false),
+ false, false, false, false, false, false, false, false, false, true, false),
newIPTest("192.168.0.1", true, false, false, false, false, false,
- false, false, false, false, false, false, false, false, true, false),
+ false, false, false, false, false, false, false, false, false, true, false),
newIPTest("172.31.255.1", true, false, false, false, false, false,
- false, false, false, false, false, false, false, false, true, false),
+ false, false, false, false, false, false, false, false, false, true, false),
newIPTest("172.32.1.1", false, false, false, false, false, false, false, false,
- false, false, false, false, false, false, true, true),
+ false, false, false, false, false, false, false, true, true),
newIPTest("169.254.250.120", false, false, false, true, false, false,
- false, false, false, false, false, false, false, false, true, false),
+ false, false, false, false, false, false, false, false, false, true, false),
newIPTest("0.0.0.0", false, false, false, false, false, false, false,
- false, false, false, false, false, false, true, false, false),
+ false, false, false, false, false, false, false, true, false, false),
newIPTest("255.255.255.255", false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, false, false),
+ false, false, false, false, false, false, false, false, false, false, false),
newIPTest("127.0.0.1", false, false, false, false, false, false,
- false, false, false, false, false, false, false, true, true, false),
+ false, false, false, false, false, false, false, false, true, true, false),
newIPTest("fd00:dead::1", false, false, false, false, false, true,
- false, false, false, false, false, false, false, false, true, false),
+ false, false, false, false, false, false, false, false, false, true, false),
newIPTest("2001::1", false, false, false, false, false, false,
- true, false, false, false, false, false, false, false, true, true),
+ true, false, false, false, false, false, false, false, false, true, true),
newIPTest("2001:10:abcd::1:1", false, false, false, false, false, false,
- false, true, false, false, false, false, false, false, true, false),
+ false, true, false, false, false, false, false, false, false, true, false),
+ newIPTest("2001:20:abcd::1:1", false, false, false, false, false, false,
+ false, false, false, false, false, false, false, true, false, true, false),
newIPTest("fe80::1", false, false, false, false, false, false,
- false, false, true, false, false, false, false, false, true, false),
+ false, false, true, false, false, false, false, false, false, true, false),
newIPTest("fe80:1::1", false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, true, true),
+ false, false, false, false, false, false, false, false, false, true, true),
newIPTest("64:ff9b::1", false, false, false, false, false, false,
- false, false, false, false, true, false, false, false, true, true),
+ false, false, false, false, true, false, false, false, false, true, true),
newIPTest("::ffff:abcd:ef12:1", false, false, false, false, false, false,
- false, false, false, false, false, false, false, false, true, true),
+ false, false, false, false, false, false, false, false, false, true, true),
newIPTest("::1", false, false, false, false, false, false, false, false,
- false, false, false, false, false, true, true, false),
+ false, false, false, false, false, false, true, true, false),
newIPTest("198.18.0.1", false, true, false, false, false, false, false,
- false, false, false, false, false, false, false, true, false),
+ false, false, false, false, false, false, false, false, true, false),
newIPTest("100.127.255.1", false, false, false, false, false, false, false,
- false, false, false, false, false, true, false, true, false),
+ false, false, false, false, false, true, false, false, true, false),
newIPTest("203.0.113.1", false, false, false, false, false, false, false,
- false, false, false, false, false, false, false, true, false),
+ false, false, false, false, false, false, false, false, true, false),
}
t.Logf("Running %d tests", len(tests))
@@ -126,7 +129,11 @@ func TestIPTypes(t *testing.T) {
}
if rv := addrmgr.IsRFC6145(&test.in); rv != test.rfc6145 {
- t.Errorf("IsRFC1918 %s\n got: %v want: %v", test.in.IP, rv, test.rfc6145)
+ t.Errorf("IsRFC6145 %s\n got: %v want: %v", test.in.IP, rv, test.rfc6145)
+ }
+
+ if rv := addrmgr.IsRFC7343(&test.in); rv != test.rfc7343 {
+ t.Errorf("IsRFC7343 %s\n got: %v want: %v", test.in.IP, rv, test.rfc7343)
}
if rv := addrmgr.IsLocal(&test.in); rv != test.local {
@@ -169,6 +176,7 @@ func TestGroupKey(t *testing.T) {
{name: "ipv4 rfc3927 169.254/16", ip: "169.254.1.2", expected: "unroutable"},
{name: "ipv6 rfc4193 fc00::/7", ip: "fc00::1234", expected: "unroutable"},
{name: "ipv6 rfc4843 2001:10::/28", ip: "2001:10::1234", expected: "unroutable"},
+ {name: "ipv6 rfc7343 2001:20::/28", ip: "2001:20::1234", expected: "unroutable"},
{name: "ipv6 rfc4862 fe80::/64", ip: "fe80::1234", expected: "unroutable"},
// IPv4 normal.
Why this scored 40/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.