p0: IANA special-purpose deny table, extra deny prefixes

Block 6to4/Teredo/NAT64-local/site-local/benchmarking/reserved
ranges from the IANA registries (families, not decoded embeds).
Guard.DenyPrefixes / -deny-prefix covers hairpin WAN /32s; home
addresses stay on-box, not in git. Control is tcp/tcp4/tcp6 only.
This commit is contained in:
2026-09-22 21:33:52 -07:00
parent 3b41d6ffbc
commit 099b94979c
6 changed files with 236 additions and 34 deletions
+63
View File
@@ -0,0 +1,63 @@
package guard
import "net/netip"
// IANA special-purpose registries (IPv4 and IPv6). Families that can embed
// IPv4 (6to4, Teredo, well-known NAT64, local-use NAT64) are blocked outright
// rather than decoded. IsGlobalUnicast remains a final gate in CheckAddr.
//
// Sources:
// https://www.iana.org/assignments/iana-ipv4-special-registry
// https://www.iana.org/assignments/iana-ipv6-special-registry
var ianaDeny = []struct {
prefix netip.Prefix
reason string
}{
// IPv4
{p("0.0.0.0/8"), "unspecified address"},
{p("10.0.0.0/8"), "private address"},
{p("100.64.0.0/10"), "tailnet address"},
{p("127.0.0.0/8"), "loopback address"},
{p("169.254.0.0/16"), "link-local address"},
{p("172.16.0.0/12"), "private address"},
{p("192.0.0.0/24"), "special-purpose address"},
{p("192.0.2.0/24"), "special-purpose address"},
{p("192.31.196.0/24"), "special-purpose address"},
{p("192.52.193.0/24"), "special-purpose address"},
{p("192.88.99.0/24"), "special-purpose address"},
{p("192.168.0.0/16"), "private address"},
{p("192.175.48.0/24"), "special-purpose address"},
{p("198.18.0.0/15"), "special-purpose address"},
{p("198.51.100.0/24"), "special-purpose address"},
{p("203.0.113.0/24"), "special-purpose address"},
{p("224.0.0.0/4"), "multicast address"},
{p("240.0.0.0/4"), "special-purpose address"},
{p("255.255.255.255/32"), "special-purpose address"},
// IPv6
{p("::/128"), "unspecified address"},
{p("::1/128"), "loopback address"},
{p("64:ff9b::/96"), "NAT64 prefix"},
{p("64:ff9b:1::/48"), "NAT64 prefix"},
{p("100::/64"), "special-purpose address"},
{p("2001::/23"), "special-purpose address"}, // IETF protocol: Teredo, ORCHID, ORCHIDv2, …
{p("2001:db8::/32"), "special-purpose address"},
{p("2002::/16"), "special-purpose address"}, // 6to4; do not decode embedded v4
{p("3fff::/20"), "special-purpose address"}, // documentation (RFC 9637)
{p("fc00::/7"), "private address"},
{p("fe80::/10"), "link-local address"},
{p("fec0::/10"), "special-purpose address"}, // deprecated site-local
{p("ff00::/8"), "multicast address"},
{p("fd7a:115c:a1e0::/48"), "tailnet address"},
}
func p(s string) netip.Prefix { return netip.MustParsePrefix(s) }
func checkIANA(addr netip.Addr) error {
for _, e := range ianaDeny {
if e.prefix.Contains(addr) {
return blockedAddr(e.reason, addr)
}
}
return nil
}