Keep a trailing AS set out of the origin and the peerings (closes #75)
check / check (push) Canceled after 0s
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. An announcement with an empty path is still not stored. 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. The prefix and prefix length pages show origin 0 as no known origin, the IP lookup asks no WHOIS lookup for it, and the README says what it means. Model: opus-5-5
This commit is contained in:
+44
-21
@@ -23,26 +23,43 @@ const (
|
||||
StateDown = "down"
|
||||
)
|
||||
|
||||
// ASPath represents a BGP AS path as a slice of AS numbers.
|
||||
// It handles JSON unmarshaling of both simple arrays and nested AS sets,
|
||||
// flattening any nested structures into a single sequence of AS numbers.
|
||||
type ASPath []int
|
||||
// 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.
|
||||
// It handles both simple integer arrays [1, 2, 3] and nested AS sets
|
||||
// like [1, [2, 3], 4], flattening them into a single slice of integers.
|
||||
func (p *ASPath) UnmarshalJSON(data []byte) error {
|
||||
// First try to unmarshal as a simple array of integers
|
||||
var simple []int
|
||||
// Most paths have no AS set and decode as a plain array of integers.
|
||||
var sequence []int
|
||||
|
||||
err := json.Unmarshal(data, &simple)
|
||||
err := json.Unmarshal(data, &sequence)
|
||||
if err == nil {
|
||||
*p = ASPath(simple)
|
||||
*p = ASPath{Sequence: sequence}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// If that fails, unmarshal as array of interfaces and flatten
|
||||
var raw []any
|
||||
|
||||
err = json.Unmarshal(data, &raw)
|
||||
@@ -50,24 +67,30 @@ func (p *ASPath) UnmarshalJSON(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Flatten the array
|
||||
result := make([]int, 0)
|
||||
path := ASPath{Sequence: make([]int, 0, len(raw))}
|
||||
|
||||
for _, item := range raw {
|
||||
for i, item := range raw {
|
||||
switch v := item.(type) {
|
||||
case float64:
|
||||
result = append(result, int(v))
|
||||
path.Sequence = append(path.Sequence, int(v))
|
||||
case []any:
|
||||
// Nested array - flatten it
|
||||
for _, nested := range v {
|
||||
if num, ok := nested.(float64); ok {
|
||||
result = append(result, int(num))
|
||||
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 = ASPath(result)
|
||||
*p = path
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -97,7 +120,7 @@ type RISMessage struct {
|
||||
RRC string `json:"rrc,omitempty"`
|
||||
MrtTime float64 `json:"mrt_time,omitempty"`
|
||||
SocketTime float64 `json:"socket_time,omitempty"`
|
||||
Path ASPath `json:"path,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
|
||||
|
||||
Reference in New Issue
Block a user