check / check (push) Canceled after 0s
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
134 lines
3.1 KiB
Go
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")
|
|
}
|
|
}
|