check / check (push) Waiting to run
Some RIS collectors send updates days old while they catch up on a backlog. Every update to a stored AS, peering or peer set last_seen to the update's time, even an older one, and never lowered first_seen. An AS page could then list a peer first seen after it was last seen. The update statements for asns, peerings and bgp_peers now keep the earlier of the stored and new first_seen and the later of the stored and new last_seen, with MIN and MAX in the SQL. The batch and the single-row writes share each statement. GetOrCreateASN returns the times the row now holds. Route times are unchanged. Model: opus-5-5
174 lines
5.4 KiB
Go
174 lines
5.4 KiB
Go
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.
|
|
//
|
|
//nolint:tagliatelle // field names are the existing JSON API
|
|
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. They are zero, and left out of JSON, for an AS that has no
|
|
// record in the asns table.
|
|
FirstSeen time.Time `json:"first_seen,omitzero"`
|
|
LastSeen time.Time `json:"last_seen,omitzero"`
|
|
WHOISUpdatedAt *time.Time `json:"whois_updated_at,omitempty"`
|
|
}
|
|
|
|
// Prefix represents an IP prefix (CIDR block) with its IP version (4 or 6)
|
|
// and the time it was first seen.
|
|
type Prefix struct {
|
|
Prefix string
|
|
IPVersion int // 4 or 6
|
|
FirstSeen time.Time
|
|
}
|
|
|
|
// 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
|
|
PrefixID uuid.UUID
|
|
PeerASN int
|
|
OriginASN int
|
|
Path string // JSON-encoded AS path
|
|
NextHop string
|
|
Timestamp time.Time
|
|
IsWithdrawal bool
|
|
}
|
|
|
|
// 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
|
|
ASA int
|
|
ASB int
|
|
FirstSeen time.Time
|
|
LastSeen time.Time
|
|
}
|
|
|
|
// LiveRoute represents a route in the live routing table
|
|
//
|
|
//nolint:tagliatelle // field names are the existing JSON API
|
|
type LiveRoute struct {
|
|
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
|
|
//
|
|
//nolint:tagliatelle // field names are the existing JSON API
|
|
type PrefixDistribution struct {
|
|
MaskLength int `json:"mask_length"`
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
// IPInfo represents comprehensive IP information for the /ip endpoint
|
|
//
|
|
//nolint:tagliatelle // field names are the existing JSON API
|
|
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
|
|
PeerIP string
|
|
IPVersion int
|
|
}
|
|
|
|
// SeenTimes is the earliest and the latest time one batch of updates saw an AS,
|
|
// a peer or a peering. A row the batch adds is stored first seen at First and
|
|
// last seen at Last. A row already stored takes First as its first_seen only
|
|
// when First is earlier, and Last as its last_seen only when Last is later.
|
|
type SeenTimes struct {
|
|
First time.Time
|
|
Last time.Time
|
|
}
|
|
|
|
// Add makes t the earliest time of s when it is earlier than First, and the
|
|
// latest when it is later than Last. The zero SeenTimes holds no time yet, so
|
|
// the first Add sets both.
|
|
func (s *SeenTimes) Add(t time.Time) {
|
|
if s.First.IsZero() || t.Before(s.First) {
|
|
s.First = t
|
|
}
|
|
|
|
if s.Last.IsZero() || t.After(s.Last) {
|
|
s.Last = t
|
|
}
|
|
}
|
|
|
|
// PeerUpdate represents parameters for updating a peer. PeerASN and
|
|
// MessageType are those of the peer's latest update.
|
|
type PeerUpdate struct {
|
|
PeerIP string
|
|
PeerASN int
|
|
MessageType string
|
|
Seen SeenTimes
|
|
}
|
|
|
|
// 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
|
|
}
|