check / check (push) Canceled after 0s
A path that ends in an AS set of several members, such as [64496, 64497, [64498, 64499]], now has no origin: its route is stored with origin 0 (none), and no member of the set is in a peering. A set of one member counts as that AS. ASPath keeps such a set apart from the sequence and gives the origin. The prefix and AS handlers take the origin from it; the peering handler uses the sequence only. The stored AS path still lists the set's members at the end. 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"`
|
|
}
|