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 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
This commit was merged in pull request #91.
This commit is contained in:
@@ -40,7 +40,7 @@ func TestPrefixHandlerStoresPrefixesTheIPLookupFinds(t *testing.T) {
|
||||
ts := time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC)
|
||||
h.HandleMessage(&ristypes.RISMessage{
|
||||
Peer: testPeerIP,
|
||||
Path: ristypes.ASPath{testASNA, testASNB},
|
||||
Path: ristypes.ASPath{Sequence: []int{testASNA, testASNB}},
|
||||
ParsedTimestamp: ts,
|
||||
Announcements: []ristypes.RISAnnouncement{{
|
||||
NextHop: testPeerIP,
|
||||
@@ -99,7 +99,7 @@ func TestPrefixHandlerAppliesEachPeersLastUpdateInABatch(t *testing.T) {
|
||||
announce := func(peer string) *ristypes.RISMessage {
|
||||
return &ristypes.RISMessage{
|
||||
Peer: peer,
|
||||
Path: ristypes.ASPath{testASNA, testASNB},
|
||||
Path: ristypes.ASPath{Sequence: []int{testASNA, testASNB}},
|
||||
ParsedTimestamp: ts,
|
||||
Announcements: []ristypes.RISAnnouncement{
|
||||
{NextHop: peer, Prefixes: []string{prefix}},
|
||||
@@ -216,7 +216,7 @@ func TestPrefixHandlerKeepsOneRoutePerPrefixAndPeer(t *testing.T) {
|
||||
// One message that announces otherPrefix with origin AS testASNB and
|
||||
// withdraws prefix.
|
||||
announceOtherAndWithdraw := announce(
|
||||
ristypes.ASPath{testASNA, testASNB}, otherPrefix)
|
||||
ristypes.ASPath{Sequence: []int{testASNA, testASNB}}, otherPrefix)
|
||||
announceOtherAndWithdraw.Withdrawals = []string{prefix}
|
||||
|
||||
tests := []struct {
|
||||
@@ -230,8 +230,8 @@ func TestPrefixHandlerKeepsOneRoutePerPrefixAndPeer(t *testing.T) {
|
||||
{
|
||||
name: "the peer announces the prefix with a new origin AS",
|
||||
batches: [][]*ristypes.RISMessage{
|
||||
{announce(ristypes.ASPath{testASNA}, prefix)},
|
||||
{announce(ristypes.ASPath{testASNA, testASNB}, prefix)},
|
||||
{announce(ristypes.ASPath{Sequence: []int{testASNA}}, prefix)},
|
||||
{announce(ristypes.ASPath{Sequence: []int{testASNA, testASNB}}, prefix)},
|
||||
},
|
||||
wantOrigins: map[string]int{prefix: testASNB},
|
||||
},
|
||||
@@ -239,7 +239,7 @@ func TestPrefixHandlerKeepsOneRoutePerPrefixAndPeer(t *testing.T) {
|
||||
name: "one message announces a prefix with another origin AS " +
|
||||
"and withdraws the prefix",
|
||||
batches: [][]*ristypes.RISMessage{
|
||||
{announce(ristypes.ASPath{testASNA}, prefix)},
|
||||
{announce(ristypes.ASPath{Sequence: []int{testASNA}}, prefix)},
|
||||
{announceOtherAndWithdraw},
|
||||
},
|
||||
wantOrigins: map[string]int{otherPrefix: testASNB},
|
||||
@@ -264,6 +264,70 @@ func TestPrefixHandlerKeepsOneRoutePerPrefixAndPeer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestPrefixHandlerStoresNoOriginForAPathEndingInASet announces a prefix with
|
||||
// AS paths that end in an AS set through the prefix handler into a real
|
||||
// database. Any member of a set of several may be the origin, so the route is
|
||||
// stored with origin 0 (none); a set of one member counts as that AS. The
|
||||
// stored AS path lists the set's members after the rest of the path.
|
||||
func TestPrefixHandlerStoresNoOriginForAPathEndingInASet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
path []any
|
||||
wantOrigin int
|
||||
wantPath []int
|
||||
}{
|
||||
{
|
||||
name: "a set of several members",
|
||||
path: []any{testASNA, testASNB, []int{testASNC, testASND}},
|
||||
wantPath: []int{testASNA, testASNB, testASNC, testASND},
|
||||
},
|
||||
{
|
||||
name: "a set of one member",
|
||||
path: []any{testASNA, testASNB, []int{testASNC}},
|
||||
wantOrigin: testASNC,
|
||||
wantPath: []int{testASNA, testASNB, testASNC},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
msg := peerAnnouncement(testPeerIP, testV4Prefix)
|
||||
msg.Path = decodePath(t, tt.path...)
|
||||
db := handleBatches(t, [][]*ristypes.RISMessage{{msg}})
|
||||
|
||||
routes, _, err := db.GetPrefixDetailsContext(
|
||||
context.Background(), testV4Prefix)
|
||||
if err != nil {
|
||||
t.Fatalf("GetPrefixDetailsContext(%s): %v", testV4Prefix, err)
|
||||
}
|
||||
|
||||
if len(routes) != 1 || routes[0].OriginASN != tt.wantOrigin ||
|
||||
!slices.Equal(routes[0].ASPath, tt.wantPath) {
|
||||
t.Errorf("live routes for %s = %+v, "+
|
||||
"want one with origin AS%d and AS path %v",
|
||||
testV4Prefix, routes, tt.wantOrigin, tt.wantPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestPrefixHandlerDoesNotStoreAnAnnouncementWithAnEmptyPath announces a
|
||||
// prefix with an empty AS path, which gives no origin and does not end in an
|
||||
// AS set, so no live route is stored.
|
||||
func TestPrefixHandlerDoesNotStoreAnAnnouncementWithAnEmptyPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
msg := peerAnnouncement(testPeerIP, testV4Prefix)
|
||||
msg.Path = ristypes.ASPath{}
|
||||
db := handleBatches(t, [][]*ristypes.RISMessage{{msg}})
|
||||
|
||||
assertPeers(t, db, map[string][]string{testV4Prefix: nil})
|
||||
}
|
||||
|
||||
// TestStateMessagesGoToThePrefixHandlerNotThePeerHandler checks that STATE
|
||||
// messages go to the prefix handler, which acts on a down, and not to the peer
|
||||
// handler, which would record a peer whose session never comes up as active.
|
||||
@@ -540,7 +604,7 @@ func peerAnnouncement(peer string, prefixes ...string) *ristypes.RISMessage {
|
||||
return &ristypes.RISMessage{
|
||||
Type: ristypes.MessageTypeUpdate,
|
||||
Peer: peer,
|
||||
Path: ristypes.ASPath{testASNA, testASNB},
|
||||
Path: ristypes.ASPath{Sequence: []int{testASNA, testASNB}},
|
||||
ParsedTimestamp: time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC),
|
||||
Announcements: []ristypes.RISAnnouncement{
|
||||
{NextHop: peer, Prefixes: prefixes},
|
||||
|
||||
Reference in New Issue
Block a user