check / check (push) Canceled after 0s
A path ending in an AS set, such as [64496, 64497, [64498, 64499]], was flattened into the sequence, so the route was stored as originated by the last set member and the peering handler recorded peerings the path does not show. ASPath now keeps the sequence and a trailing set of several members apart; Origin() gives the last AS of the sequence, or 0 (no known origin) for such a set. The handlers use it; peerings come from the sequence only. The pages show origin 0 as no known origin, and the IP lookup asks no WHOIS lookup for it. Empty paths are unchanged. Model: opus-5-5
148 lines
4.7 KiB
Go
148 lines
4.7 KiB
Go
// Package ristypes defines the data structures for RIS Live BGP messages and
|
|
// announcements.
|
|
package ristypes
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// Message types and the session state the handlers act on, as the feed writes
|
|
// them in a message's type and state fields.
|
|
const (
|
|
// MessageTypeUpdate is a BGP UPDATE: announcements and withdrawals.
|
|
MessageTypeUpdate = "UPDATE"
|
|
|
|
// MessageTypeState is a change in the state of the BGP session between
|
|
// the collector and the peer.
|
|
MessageTypeState = "STATE"
|
|
|
|
// StateDown is the state of a STATE message whose session has ended: the
|
|
// collector no longer has any of the peer's routes, and sends no
|
|
// withdrawals for them.
|
|
StateDown = "down"
|
|
)
|
|
|
|
// ASPath is a BGP AS path as the feed writes it: an array of AS numbers in
|
|
// which an AS set is an array of its members, such as [1, 2, [3, 4]].
|
|
type ASPath struct {
|
|
// Sequence is the path's AS numbers in order. A set of one member that
|
|
// ends the path counts as that AS and is its last element. The members of
|
|
// a set anywhere else are flattened into it.
|
|
Sequence []int
|
|
|
|
// Set holds the members of a set of several members that ends the path.
|
|
// Any of them may be the origin, and they are not neighbours of each
|
|
// other or of the AS before them.
|
|
Set []int
|
|
}
|
|
|
|
// Origin returns the AS that originated the route: the last AS of the
|
|
// sequence, or 0 (none) when the path ends in a set of several members or is
|
|
// empty.
|
|
func (p *ASPath) Origin() int {
|
|
if len(p.Set) > 0 || len(p.Sequence) == 0 {
|
|
return 0
|
|
}
|
|
|
|
return p.Sequence[len(p.Sequence)-1]
|
|
}
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface for ASPath.
|
|
func (p *ASPath) UnmarshalJSON(data []byte) error {
|
|
// Most paths have no AS set and decode as a plain array of integers.
|
|
var sequence []int
|
|
|
|
err := json.Unmarshal(data, &sequence)
|
|
if err == nil {
|
|
*p = ASPath{Sequence: sequence}
|
|
|
|
return nil
|
|
}
|
|
|
|
var raw []any
|
|
|
|
err = json.Unmarshal(data, &raw)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
path := ASPath{Sequence: make([]int, 0, len(raw))}
|
|
|
|
for i, item := range raw {
|
|
switch v := item.(type) {
|
|
case float64:
|
|
path.Sequence = append(path.Sequence, int(v))
|
|
case []any:
|
|
set := make([]int, 0, len(v))
|
|
|
|
for _, member := range v {
|
|
if num, ok := member.(float64); ok {
|
|
set = append(set, int(num))
|
|
}
|
|
}
|
|
|
|
if i == len(raw)-1 && len(set) > 1 {
|
|
path.Set = set
|
|
} else {
|
|
path.Sequence = append(path.Sequence, set...)
|
|
}
|
|
}
|
|
}
|
|
|
|
*p = path
|
|
|
|
return nil
|
|
}
|
|
|
|
// RISLiveMessage represents the outer wrapper message from the RIPE RIS Live stream.
|
|
// Each message contains a Type field indicating the message type and a Data field
|
|
// containing the actual BGP message payload.
|
|
type RISLiveMessage struct {
|
|
Type string `json:"type"`
|
|
Data RISMessage `json:"data"`
|
|
}
|
|
|
|
// RISMessage represents a BGP update message from the RIPE RIS Live stream.
|
|
// It contains metadata about the BGP session (peer, ASN, host) along with
|
|
// the actual BGP update data including AS path, communities, announcements,
|
|
// and withdrawals.
|
|
//
|
|
//nolint:tagliatelle // field names are the RIS Live feed's
|
|
type RISMessage struct {
|
|
Type string `json:"type"`
|
|
Timestamp float64 `json:"timestamp"`
|
|
ParsedTimestamp time.Time `json:"-"` // Parsed from Timestamp field
|
|
Peer string `json:"peer"`
|
|
PeerASN string `json:"peer_asn"`
|
|
ID string `json:"id"`
|
|
Host string `json:"host"`
|
|
RRC string `json:"rrc,omitempty"`
|
|
MrtTime float64 `json:"mrt_time,omitempty"`
|
|
SocketTime float64 `json:"socket_time,omitempty"`
|
|
Path ASPath `json:"path,omitzero"`
|
|
// Community and Raw are present in the feed but read by no handler.
|
|
// They are the largest fields on a message that lives in up to four
|
|
// handler queues, so json:"-" keeps them out of the decoded message
|
|
// to save queue memory. Do not decode them without a consumer.
|
|
Community [][]int `json:"-"`
|
|
Origin string `json:"origin,omitempty"`
|
|
MED *int `json:"med,omitempty"`
|
|
LocalPref *int `json:"local_pref,omitempty"`
|
|
Announcements []RISAnnouncement `json:"announcements,omitempty"`
|
|
Withdrawals []string `json:"withdrawals,omitempty"`
|
|
Raw string `json:"-"`
|
|
// State is set on STATE messages: the session's new state, such as StateDown.
|
|
State string `json:"state,omitempty"`
|
|
}
|
|
|
|
// RISAnnouncement represents a BGP route announcement within a RIS message.
|
|
// It contains the next hop IP address and the list of prefixes being announced
|
|
// via that next hop.
|
|
//
|
|
//nolint:tagliatelle // field names are the RIS Live feed's
|
|
type RISAnnouncement struct {
|
|
NextHop string `json:"next_hop"`
|
|
Prefixes []string `json:"prefixes"`
|
|
}
|