Files
routewatch/internal/routetable/overview_internal_test.go
T
clawbot 281d162e27
check / check (push) Canceled after 0s
Show figures of the routing table on the home page (closes #154)
The home page gets cards for the newest and oldest kept route, the newest
and oldest updated AS, the AS with the most prefixes, the ASes with a
single prefix and fifteen well-known networks. Two more cards count the
bogon announcements and the prefixes with several origin ASes. They link
to the new pages /bogons and /multiple-origins, which list up to 1,000
entries each.

The new package internal/bogon tells reserved prefixes and AS numbers,
from the IANA special-purpose and address space registries. Table.Overview
walks the table and lets the read lock go every 10,000 prefixes. The
server works the cards out once a minute and serves that copy. A new index
on asns(last_seen) finds the newest and oldest updated AS.

Model: opus-5-5
2026-10-27 18:27:01 +01:00

134 lines
3.1 KiB
Go

package routetable
import (
"net/netip"
"runtime"
"testing"
"time"
)
// TestWalkPrefixesVisitsEveryPrefix walks a table with more prefixes than two
// of the walk's batches, while another goroutine keeps adding and removing
// other prefixes of the same mask length. The walk lets the lock go between
// batches, so those updates get in while it runs. Every prefix that was in
// the table all along must be visited once.
func TestWalkPrefixesVisitsEveryPrefix(t *testing.T) {
t.Parallel()
const (
asn = 1000
peer = "192.0.2.1"
// prefixCount is more prefixes than two of the walk's batches.
prefixCount = 2*walkBatch + 1
)
routeTime := time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC)
table := New(Params{})
prefixes := make([]netip.Prefix, 0, prefixCount)
addr := netip.MustParseAddr("1.0.0.0")
for range prefixCount {
prefix := netip.PrefixFrom(addr, addr.BitLen())
table.Announce(prefix, asn, Route{Peer: peer, Time: routeTime})
prefixes = append(prefixes, prefix)
addr = addr.Next()
}
stop := make(chan struct{})
updated := make(chan struct{})
go func() {
defer close(updated)
other := netip.MustParseAddr("2.0.0.0")
for {
select {
case <-stop:
return
default:
}
prefix := netip.PrefixFrom(other, other.BitLen())
table.Announce(prefix, asn, Route{Peer: peer, Time: routeTime})
table.Withdraw(prefix, peer)
other = other.Next()
}
}()
visits := make(map[netip.Prefix]int)
table.walkPrefixes(func(prefix netip.Prefix, _ []*origin) {
visits[prefix]++
})
close(stop)
<-updated
for _, prefix := range prefixes {
if visits[prefix] != 1 {
t.Fatalf("the walk visited %s %d times, want once", prefix,
visits[prefix])
}
}
}
// TestWalkPrefixesLetsUpdatesIn starts an update during the walk's first
// batch. The update must have been made by the time the walk visits the first
// prefix of its second batch, because the walk lets the lock go in between.
func TestWalkPrefixesLetsUpdatesIn(t *testing.T) {
t.Parallel()
const (
asn = 1000
peer = "192.0.2.1"
)
routeTime := time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC)
table := New(Params{})
addr := netip.MustParseAddr("1.0.0.0")
for range walkBatch + 1 {
prefix := netip.PrefixFrom(addr, addr.BitLen())
table.Announce(prefix, asn, Route{Peer: peer, Time: routeTime})
addr = addr.Next()
}
updatePrefix := netip.MustParsePrefix("2.0.0.0/8")
updateDone := make(chan struct{})
visited := 0
madeBeforeSecondBatch := false
table.walkPrefixes(func(netip.Prefix, []*origin) {
visited++
switch visited {
case 1:
go func() {
defer close(updateDone)
table.Announce(updatePrefix, asn, Route{Peer: peer, Time: routeTime})
}()
// Wait until the update waits for the lock. TryRLock fails
// once a writer waits.
for table.mu.TryRLock() {
table.mu.RUnlock()
runtime.Gosched()
}
case walkBatch + 1:
// The walk holds the read lock, so the table can be read.
madeBeforeSecondBatch = table.origins(updatePrefix) != nil
}
})
<-updateDone
if !madeBeforeSecondBatch {
t.Fatal("an update started during the walk's first batch was not made " +
"before its second batch")
}
}