check / check (push) Canceled after 0s
The home page gets cards for the newest and oldest kept route, the newest and oldest updated AS, the AS with the most prefixes, the ASes with a single prefix and fifteen well-known networks. Two more cards count the bogon announcements and the prefixes with several origin ASes. They link to the new pages /bogons and /multiple-origins, which list up to 1,000 entries each. The new package internal/bogon tells reserved prefixes and AS numbers, from the IANA special-purpose registries. Table.Overview walks the table and lets the read lock go every 10,000 prefixes. The server works the cards out once a minute and serves that copy. A new index on asns(last_seen) finds the newest and oldest updated AS. Model: opus-5-5
192 lines
7.5 KiB
Go
192 lines
7.5 KiB
Go
// Package bogon tells whether a prefix or an AS number is reserved: set aside
|
|
// for a special purpose, so that no network should announce it on the
|
|
// internet. A route is a bogon announcement when its prefix is reserved or its
|
|
// origin AS is.
|
|
//
|
|
// The reserved prefixes come from the IANA special-purpose address registries
|
|
// for IPv4 and IPv6. The reserved AS numbers come from the IANA special-purpose
|
|
// AS numbers registry:
|
|
// - the AS numbers for private use
|
|
// - the AS numbers for documentation
|
|
// - AS 23456, which stands in for a 32-bit AS number
|
|
// - the last AS number of the 16-bit and of the 32-bit range
|
|
//
|
|
// Address space and AS numbers that the IANA has not handed out yet are not
|
|
// counted.
|
|
package bogon
|
|
|
|
import (
|
|
"net/netip"
|
|
"strconv"
|
|
)
|
|
|
|
// Prefix returns why prefix is reserved, and true, when it lies inside a
|
|
// reserved block. The most specific registry block that holds prefix decides,
|
|
// because some blocks that may be announced lie inside reserved ones. It
|
|
// returns "" and false when prefix is not reserved.
|
|
func Prefix(prefix netip.Prefix) (string, bool) {
|
|
var holder *block
|
|
|
|
for i, b := range specialPurposeBlocks {
|
|
inside := b.prefix.Bits() <= prefix.Bits() && b.prefix.Contains(prefix.Addr())
|
|
if inside && (holder == nil || b.prefix.Bits() > holder.prefix.Bits()) {
|
|
holder = &specialPurposeBlocks[i]
|
|
}
|
|
}
|
|
|
|
if holder == nil || !holder.reserved {
|
|
return "", false
|
|
}
|
|
|
|
return holder.name + " (" + holder.prefix.String() + ")", true
|
|
}
|
|
|
|
// ASN returns why asn is reserved, and true, when it is. It returns "" and
|
|
// false when asn is not reserved.
|
|
func ASN(asn int) (string, bool) {
|
|
for _, r := range reservedASRanges {
|
|
if asn < r.first || asn > r.last {
|
|
continue
|
|
}
|
|
|
|
if r.first == r.last {
|
|
return r.name + " (AS" + strconv.Itoa(asn) + ")", true
|
|
}
|
|
|
|
return r.name + " (AS" + strconv.Itoa(r.first) + "-AS" +
|
|
strconv.Itoa(r.last) + ")", true
|
|
}
|
|
|
|
return "", false
|
|
}
|
|
|
|
// The names that the registries give to more than one row.
|
|
const (
|
|
privateUse = "Private-Use"
|
|
documentation = "Documentation"
|
|
benchmarking = "Benchmarking"
|
|
ietfProtocolAssignments = "IETF Protocol Assignments"
|
|
portControlAnycast = "Port Control Protocol Anycast"
|
|
natTraversalAnycast = "Traversal Using Relays around NAT Anycast"
|
|
nat64Discovery = "NAT64/DNS64 Discovery"
|
|
amt = "AMT"
|
|
as112DirectDelegation = "Direct Delegation AS112 Service"
|
|
ipv4IPv6Translation = "IPv4-IPv6 Translat."
|
|
)
|
|
|
|
// block is one row of an IANA special-purpose address registry.
|
|
type block struct {
|
|
prefix netip.Prefix
|
|
name string
|
|
// reserved says that the row's Globally Reachable column is False.
|
|
reserved bool
|
|
}
|
|
|
|
// reservedBlock returns a row whose Globally Reachable column is False.
|
|
func reservedBlock(prefix, name string) block {
|
|
return block{prefix: netip.MustParsePrefix(prefix), name: name, reserved: true}
|
|
}
|
|
|
|
// reachableBlock returns a row whose Globally Reachable column is True or N/A.
|
|
// Such a row matters only inside a reserved block, whose addresses it lets be
|
|
// announced.
|
|
func reachableBlock(prefix, name string) block {
|
|
return block{prefix: netip.MustParsePrefix(prefix), name: name}
|
|
}
|
|
|
|
// specialPurposeBlocks are the rows of the IANA special-purpose address
|
|
// registries, without the deprecated ones:
|
|
// - https://www.iana.org/assignments/iana-ipv4-special-registry/
|
|
// - https://www.iana.org/assignments/iana-ipv6-special-registry/
|
|
//
|
|
//nolint:gochecknoglobals // a fixed list, parsed once
|
|
var specialPurposeBlocks = []block{
|
|
reservedBlock("0.0.0.0/8", "This network"),
|
|
reservedBlock("0.0.0.0/32", "This host on this network"),
|
|
reservedBlock("10.0.0.0/8", privateUse),
|
|
reservedBlock("100.64.0.0/10", "Shared Address Space"),
|
|
reservedBlock("127.0.0.0/8", "Loopback"),
|
|
reservedBlock("169.254.0.0/16", "Link Local"),
|
|
reservedBlock("172.16.0.0/12", privateUse),
|
|
reservedBlock("192.0.0.0/24", ietfProtocolAssignments),
|
|
reservedBlock("192.0.0.0/29", "IPv4 Service Continuity Prefix"),
|
|
reservedBlock("192.0.0.8/32", "IPv4 dummy address"),
|
|
reachableBlock("192.0.0.9/32", portControlAnycast),
|
|
reachableBlock("192.0.0.10/32", natTraversalAnycast),
|
|
reservedBlock("192.0.0.170/32", nat64Discovery),
|
|
reservedBlock("192.0.0.171/32", nat64Discovery),
|
|
reservedBlock("192.0.2.0/24", "Documentation (TEST-NET-1)"),
|
|
reachableBlock("192.31.196.0/24", "AS112-v4"),
|
|
reachableBlock("192.52.193.0/24", amt),
|
|
reservedBlock("192.88.99.2/32", "6a44-relay anycast address"),
|
|
reservedBlock("192.168.0.0/16", privateUse),
|
|
reachableBlock("192.175.48.0/24", as112DirectDelegation),
|
|
reservedBlock("198.18.0.0/15", benchmarking),
|
|
reservedBlock("198.51.100.0/24", "Documentation (TEST-NET-2)"),
|
|
reservedBlock("203.0.113.0/24", "Documentation (TEST-NET-3)"),
|
|
reservedBlock("240.0.0.0/4", "Reserved"),
|
|
reservedBlock("255.255.255.255/32", "Limited Broadcast"),
|
|
|
|
reservedBlock("::1/128", "Loopback Address"),
|
|
reservedBlock("::/128", "Unspecified Address"),
|
|
reservedBlock("::ffff:0:0/96", "IPv4-mapped Address"),
|
|
reachableBlock("64:ff9b::/96", ipv4IPv6Translation),
|
|
reservedBlock("64:ff9b:1::/48", ipv4IPv6Translation),
|
|
reservedBlock("100::/64", "Discard-Only Address Block"),
|
|
reservedBlock("100:0:0:1::/64", "Dummy IPv6 Prefix"),
|
|
reservedBlock("2001::/23", ietfProtocolAssignments),
|
|
reachableBlock("2001::/32", "TEREDO"),
|
|
reachableBlock("2001:1::1/128", portControlAnycast),
|
|
reachableBlock("2001:1::2/128", natTraversalAnycast),
|
|
reachableBlock("2001:1::3/128", "DNS-SD Service Registration Protocol Anycast"),
|
|
reservedBlock("2001:2::/48", benchmarking),
|
|
reachableBlock("2001:3::/32", amt),
|
|
reachableBlock("2001:4:112::/48", "AS112-v6"),
|
|
reachableBlock("2001:20::/28", "ORCHIDv2"),
|
|
reachableBlock("2001:30::/28", "Drone Remote ID Protocol Entity Tags (DETs) Prefix"),
|
|
reservedBlock("2001:db8::/32", documentation),
|
|
reachableBlock("2002::/16", "6to4"),
|
|
reachableBlock("2620:4f:8000::/48", as112DirectDelegation),
|
|
reservedBlock("3fff::/20", documentation),
|
|
reservedBlock("5f00::/16", "Segment Routing (SRv6) SIDs"),
|
|
reservedBlock("fc00::/7", "Unique-Local"),
|
|
reservedBlock("fe80::/10", "Link-Local Unicast"),
|
|
}
|
|
|
|
// asRange is a range of reserved AS numbers, first to last.
|
|
type asRange struct {
|
|
first, last int
|
|
name string
|
|
}
|
|
|
|
// The reserved AS numbers of the IANA special-purpose AS numbers registry.
|
|
const (
|
|
asTrans = 23456
|
|
firstDocumentationAS16 = 64496
|
|
lastDocumentationAS16 = 64511
|
|
firstPrivateUseAS16 = 64512
|
|
lastPrivateUseAS16 = 65534
|
|
lastAS16 = 65535
|
|
firstDocumentationAS32 = 65536
|
|
lastDocumentationAS32 = 65551
|
|
firstPrivateUseAS32 = 4200000000
|
|
lastPrivateUseAS32 = 4294967294
|
|
lastAS32 = 4294967295
|
|
)
|
|
|
|
// reservedASRanges are the reserved rows of the IANA special-purpose AS numbers
|
|
// registry, https://www.iana.org/assignments/iana-as-numbers-special-registry/.
|
|
// AS 0 is left out: RouteWatch uses it for a route with no known origin. AS 112
|
|
// is left out because the AS112 project announces routes with it.
|
|
//
|
|
//nolint:gochecknoglobals // a fixed list
|
|
var reservedASRanges = []asRange{
|
|
{first: asTrans, last: asTrans, name: "AS_TRANS"},
|
|
{first: firstDocumentationAS16, last: lastDocumentationAS16, name: documentation},
|
|
{first: firstPrivateUseAS16, last: lastPrivateUseAS16, name: privateUse},
|
|
{first: lastAS16, last: lastAS16, name: "Last 16-bit AS number"},
|
|
{first: firstDocumentationAS32, last: lastDocumentationAS32, name: documentation},
|
|
{first: firstPrivateUseAS32, last: lastPrivateUseAS32, name: privateUse},
|
|
{first: lastAS32, last: lastAS32, name: "Last 32-bit AS number"},
|
|
}
|