check / check (push) Successful in 3m13s
Looking up an address on /ip/ and /api/v1/ip/ read every IPv6 live route and about half of the IPv4 range index, so on a day-sized database it passed the 30-second request timeout. The lookup is now one function for both families: from the longest mask length down, it looks up the address's network prefix on the existing prefix index, and the first live route wins. The feed sends IPv6 withdrawals uncompressed, so they never matched the stored compressed prefix and never removed a route. Prefixes are now stored in the text form net/netip prints, so IPv6 withdrawals take effect. ip_start, ip_end and GetASInfoForIP are removed; nothing read them. A database from before this change must be deleted. Model: opus-5-5
147 lines
4.8 KiB
Go
147 lines
4.8 KiB
Go
// Package database provides SQLite storage for BGP routing data.
|
|
package database
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// ASN represents an Autonomous System Number with its metadata including
|
|
// handle, description, WHOIS data, and first/last seen timestamps.
|
|
type ASN struct {
|
|
ASN int `json:"asn"`
|
|
Handle string `json:"handle"`
|
|
Description string `json:"description"`
|
|
// WHOIS parsed fields
|
|
ASName string `json:"as_name,omitempty"`
|
|
OrgName string `json:"org_name,omitempty"`
|
|
OrgID string `json:"org_id,omitempty"`
|
|
Address string `json:"address,omitempty"`
|
|
CountryCode string `json:"country_code,omitempty"`
|
|
AbuseEmail string `json:"abuse_email,omitempty"`
|
|
AbusePhone string `json:"abuse_phone,omitempty"`
|
|
TechEmail string `json:"tech_email,omitempty"`
|
|
TechPhone string `json:"tech_phone,omitempty"`
|
|
RIR string `json:"rir,omitempty"` // ARIN, RIPE, APNIC, LACNIC, AFRINIC
|
|
RIRRegDate *time.Time `json:"rir_registration_date,omitempty"`
|
|
RIRLastMod *time.Time `json:"rir_last_modified,omitempty"`
|
|
WHOISRaw string `json:"whois_raw,omitempty"`
|
|
// Timestamps
|
|
FirstSeen time.Time `json:"first_seen"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
WHOISUpdatedAt *time.Time `json:"whois_updated_at,omitempty"`
|
|
}
|
|
|
|
// Prefix represents an IP prefix (CIDR block) with its IP version (4 or 6)
|
|
// and first/last seen timestamps.
|
|
type Prefix struct {
|
|
ID uuid.UUID `json:"id"`
|
|
Prefix string `json:"prefix"`
|
|
IPVersion int `json:"ip_version"` // 4 or 6
|
|
FirstSeen time.Time `json:"first_seen"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
}
|
|
|
|
// Announcement represents a BGP announcement or withdrawal event,
|
|
// containing the prefix, AS path, origin ASN, peer ASN, next hop, and timestamp.
|
|
type Announcement struct {
|
|
ID uuid.UUID `json:"id"`
|
|
PrefixID uuid.UUID `json:"prefix_id"`
|
|
PeerASN int `json:"peer_asn"`
|
|
OriginASN int `json:"origin_asn"`
|
|
Path string `json:"path"` // JSON-encoded AS path
|
|
NextHop string `json:"next_hop"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
IsWithdrawal bool `json:"is_withdrawal"`
|
|
}
|
|
|
|
// ASNPeering represents a peering relationship between two ASNs,
|
|
// stored with the lower ASN as ASA and the higher as ASB.
|
|
type ASNPeering struct {
|
|
ID uuid.UUID `json:"id"`
|
|
ASA int `json:"as_a"`
|
|
ASB int `json:"as_b"`
|
|
FirstSeen time.Time `json:"first_seen"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
}
|
|
|
|
// LiveRoute represents a route in the live routing table
|
|
type LiveRoute struct {
|
|
ID uuid.UUID `json:"id"`
|
|
Prefix string `json:"prefix"`
|
|
MaskLength int `json:"mask_length"`
|
|
IPVersion int `json:"ip_version"`
|
|
OriginASN int `json:"origin_asn"`
|
|
PeerIP string `json:"peer_ip"`
|
|
ASPath []int `json:"as_path"`
|
|
NextHop string `json:"next_hop"`
|
|
LastUpdated time.Time `json:"last_updated"`
|
|
}
|
|
|
|
// PrefixDistribution represents the distribution of prefixes by mask length
|
|
type PrefixDistribution struct {
|
|
MaskLength int `json:"mask_length"`
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
// IPInfo represents comprehensive IP information for the /ip endpoint
|
|
type IPInfo struct {
|
|
IP string `json:"ip"`
|
|
PTR []string `json:"ptr,omitempty"`
|
|
Netblock string `json:"netblock"`
|
|
MaskLength int `json:"mask_length"`
|
|
IPVersion int `json:"ip_version"`
|
|
NumPeers int `json:"num_peers"`
|
|
// AS information
|
|
ASN int `json:"asn"`
|
|
ASName string `json:"as_name,omitempty"`
|
|
Handle string `json:"handle,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
OrgName string `json:"org_name,omitempty"`
|
|
OrgID string `json:"org_id,omitempty"`
|
|
Address string `json:"address,omitempty"`
|
|
CountryCode string `json:"country_code,omitempty"`
|
|
AbuseEmail string `json:"abuse_email,omitempty"`
|
|
RIR string `json:"rir,omitempty"`
|
|
// Timestamps
|
|
FirstSeen time.Time `json:"first_seen"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
// Indicates if WHOIS data needs refresh (not serialized)
|
|
NeedsWHOISRefresh bool `json:"-"`
|
|
}
|
|
|
|
// LiveRouteDeletion represents parameters for deleting a live route
|
|
type LiveRouteDeletion struct {
|
|
Prefix string
|
|
OriginASN int
|
|
PeerIP string
|
|
IPVersion int
|
|
}
|
|
|
|
// PeerUpdate represents parameters for updating a peer
|
|
type PeerUpdate struct {
|
|
PeerIP string
|
|
PeerASN int
|
|
MessageType string
|
|
Timestamp time.Time
|
|
}
|
|
|
|
// ASNWHOISUpdate contains WHOIS data for updating an ASN record.
|
|
type ASNWHOISUpdate struct {
|
|
ASN int
|
|
ASName string
|
|
OrgName string
|
|
OrgID string
|
|
Address string
|
|
CountryCode string
|
|
AbuseEmail string
|
|
AbusePhone string
|
|
TechEmail string
|
|
TechPhone string
|
|
RIR string
|
|
RIRRegDate *time.Time
|
|
RIRLastMod *time.Time
|
|
WHOISRaw string
|
|
}
|