Files
akvorado/outlet/routing/provider/bmp/rib_test.go
Vincent Bernat 92ee2e05b7 outlet/routing: use gaissmai/bart instead of kentik/patricia
For each prefix, list of routes is stored into a map (like what is done
with kentik/patricia). The benchmark shows an improvement, both in
insertion time and in memory.

Before:

```
goos: linux
goarch: amd64
pkg: akvorado/outlet/routing/provider/bmp
cpu: AMD Ryzen 5 5600X 6-Core Processor
BenchmarkRandomRealWorldRoutes4-12         10000               106.4 ns/route
BenchmarkRIBInsertion/1000_routes,_1_peers-12               2504               100.0 %ins              361.4 bytes/route               515.6 ns/route
BenchmarkRIBInsertion/1000_routes,_2_peers-12               1234               100.0 %ins              337.5 bytes/route               545.7 ns/route
BenchmarkRIBInsertion/1000_routes,_5_peers-12                412               100.0 %ins              377.6 bytes/route               646.9 ns/route
BenchmarkRIBInsertion/10000_routes,_1_peers-12               170                99.98 %ins             373.0 bytes/route               780.7 ns/route
BenchmarkRIBInsertion/10000_routes,_2_peers-12                52                99.99 %ins             373.2 bytes/route              1136 ns/route
BenchmarkRIBInsertion/10000_routes,_5_peers-12                13                99.99 %ins             299.8 bytes/route              1877 ns/route
BenchmarkRIBInsertion/100000_routes,_1_peers-12                4                99.84 %ins             300.0 bytes/route              2918 ns/route
BenchmarkRIBInsertion/100000_routes,_2_peers-12                2                99.83 %ins             300.2 bytes/route              5220 ns/route
BenchmarkRIBInsertion/100000_routes,_5_peers-12                1                99.81 %ins             340.4 bytes/route             22259 ns/route
BenchmarkRIBLookup/1000_routes,_1_peers-12                 56382               214.2 ns/op
BenchmarkRIBLookup/1000_routes,_2_peers-12                 52376               227.3 ns/op
BenchmarkRIBLookup/1000_routes,_5_peers-12                 46570               257.8 ns/op
BenchmarkRIBLookup/10000_routes,_1_peers-12                 4084               277.2 ns/op
BenchmarkRIBLookup/10000_routes,_2_peers-12                 3552               295.2 ns/op
BenchmarkRIBLookup/10000_routes,_5_peers-12                 3586               340.0 ns/op
BenchmarkRIBLookup/100000_routes,_1_peers-12                 300               382.2 ns/op
BenchmarkRIBLookup/100000_routes,_2_peers-12                 240               474.1 ns/op
BenchmarkRIBLookup/100000_routes,_5_peers-12                 156               752.9 ns/op
BenchmarkRIBFlush/1000_routes,_1_peers-12                   8642                 0.1422 ms/op
BenchmarkRIBFlush/1000_routes,_2_peers-12                   4234                 0.2829 ms/op
BenchmarkRIBFlush/1000_routes,_5_peers-12                   1995                 0.5927 ms/op
BenchmarkRIBFlush/10000_routes,_1_peers-12                   807                 1.411 ms/op
BenchmarkRIBFlush/10000_routes,_2_peers-12                   360                 3.341 ms/op
BenchmarkRIBFlush/10000_routes,_5_peers-12                   166                 7.186 ms/op
BenchmarkRIBFlush/100000_routes,_1_peers-12                   58                20.85 ms/op
BenchmarkRIBFlush/100000_routes,_2_peers-12                   22                51.13 ms/op
BenchmarkRIBFlush/100000_routes,_5_peers-12                    8               135.5 ms/op
```

After:

```
goos: linux
goarch: amd64
pkg: akvorado/outlet/routing/provider/bmp
cpu: AMD Ryzen 5 5600X 6-Core Processor
BenchmarkRandomRealWorldRoutes4-12         10000               110.2 ns/route
BenchmarkRIBInsertion/1000_routes,_1_peers-12               2299               100.0 %ins              348.7 bytes/route               578.4 ns/route
BenchmarkRIBInsertion/1000_routes,_2_peers-12               1112               100.0 %ins              328.7 bytes/route               579.0 ns/route
BenchmarkRIBInsertion/1000_routes,_5_peers-12                432               100.0 %ins              279.7 bytes/route               615.6 ns/route
BenchmarkRIBInsertion/10000_routes,_1_peers-12               182                99.98 %ins             278.1 bytes/route               722.5 ns/route
BenchmarkRIBInsertion/10000_routes,_2_peers-12                61                99.99 %ins             273.0 bytes/route              1013 ns/route
BenchmarkRIBInsertion/10000_routes,_5_peers-12                14                99.99 %ins             232.4 bytes/route              1717 ns/route
BenchmarkRIBInsertion/100000_routes,_1_peers-12                4                99.84 %ins             228.3 bytes/route              2857 ns/route
BenchmarkRIBInsertion/100000_routes,_2_peers-12                2                99.83 %ins             214.3 bytes/route              4944 ns/route
BenchmarkRIBInsertion/100000_routes,_5_peers-12                1                99.81 %ins             265.4 bytes/route             22098 ns/route
BenchmarkRIBLookup/1000_routes,_1_peers-12                 61369               190.1 ns/op
BenchmarkRIBLookup/1000_routes,_2_peers-12                 64584               186.5 ns/op
BenchmarkRIBLookup/1000_routes,_5_peers-12                 63253               190.2 ns/op
BenchmarkRIBLookup/10000_routes,_1_peers-12                 5934               188.7 ns/op
BenchmarkRIBLookup/10000_routes,_2_peers-12                 5386               207.7 ns/op
BenchmarkRIBLookup/10000_routes,_5_peers-12                 5348               220.3 ns/op
BenchmarkRIBLookup/100000_routes,_1_peers-12                 516               227.1 ns/op
BenchmarkRIBLookup/100000_routes,_2_peers-12                 477               241.7 ns/op
BenchmarkRIBLookup/100000_routes,_5_peers-12                 428               264.2 ns/op
BenchmarkRIBFlush/1000_routes,_1_peers-12                   5246                 0.2294 ms/op
BenchmarkRIBFlush/1000_routes,_2_peers-12                   2984                 0.3965 ms/op
BenchmarkRIBFlush/1000_routes,_5_peers-12                   1406                 0.8498 ms/op
BenchmarkRIBFlush/10000_routes,_1_peers-12                   578                 2.084 ms/op
BenchmarkRIBFlush/10000_routes,_2_peers-12                   295                 3.988 ms/op
BenchmarkRIBFlush/10000_routes,_5_peers-12                   100                10.15 ms/op
BenchmarkRIBFlush/100000_routes,_1_peers-12                   33                30.82 ms/op
BenchmarkRIBFlush/100000_routes,_2_peers-12                   18                61.41 ms/op
BenchmarkRIBFlush/100000_routes,_5_peers-12                    7               158.4 ms/op
```

This is a 20% improvement on insertion, 30% on lookups, but 36%
degradation for flushing.

Fix #253

Next steps:
- test lockless updates (with *Persist functions)
2025-08-16 17:06:36 +02:00

572 lines
17 KiB
Go

// SPDX-FileCopyrightText: 2022 Free Mobile
// SPDX-License-Identifier: AGPL-3.0-only
package bmp
import (
"fmt"
"hash/maphash"
"math/rand"
"net/netip"
"testing"
"unsafe"
"akvorado/common/helpers"
"github.com/osrg/gobgp/v3/pkg/packet/bgp"
)
func TestLargeCommunitiesAlign(t *testing.T) {
largeCommunities := []bgp.LargeCommunity{
{ASN: 1, LocalData1: 2, LocalData2: 3},
{ASN: 4, LocalData1: 5, LocalData2: 6},
}
first := unsafe.Pointer(&largeCommunities[0])
second := unsafe.Pointer(&largeCommunities[1])
diff := uintptr(second) - uintptr(first)
if diff != 12 {
t.Errorf("Alignment error for large community slices. Got %d, expected 12",
diff)
}
// Also check other stuff we think are true about "unsafe"
if unsafe.Sizeof(largeCommunities[0]) != 12 {
t.Errorf("Large community size: got %d, expected 12", unsafe.Sizeof(largeCommunities[0]))
}
const _ = unsafe.Sizeof(largeCommunities[0])
}
func TestRTAEqual(t *testing.T) {
cases := []struct {
pos helpers.Pos
rta1 routeAttributes
rta2 routeAttributes
equal bool
}{
{helpers.Mark(), routeAttributes{asn: 2038}, routeAttributes{asn: 2038}, true},
{helpers.Mark(), routeAttributes{asn: 2038}, routeAttributes{asn: 2039}, false},
{
helpers.Mark(),
routeAttributes{asn: 2038, asPath: []uint32{}},
routeAttributes{asn: 2038},
true,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, asPath: []uint32{}},
routeAttributes{asn: 2039},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, communities: []uint32{}},
routeAttributes{asn: 2038},
true,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, communities: []uint32{}},
routeAttributes{asn: 2039},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{}},
routeAttributes{asn: 2038},
true,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{}},
routeAttributes{asn: 2039},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3}},
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3}},
true,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3}},
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3, 4}},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3}},
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3, 0}},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3}},
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 4}},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3, 4}},
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3, 4}},
true,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34}},
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34}},
true,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34}},
routeAttributes{asn: 2038, asPath: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35}},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, communities: []uint32{100, 200, 300, 400}},
routeAttributes{asn: 2038, communities: []uint32{100, 200, 300, 400}},
true,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, communities: []uint32{100, 200, 300, 400}},
routeAttributes{asn: 2038, communities: []uint32{100, 200, 300, 402}},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, communities: []uint32{100, 200, 300}},
routeAttributes{asn: 2038, communities: []uint32{100, 200, 300, 400}},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{{ASN: 1, LocalData1: 2, LocalData2: 3}, {ASN: 3, LocalData1: 4, LocalData2: 5}, {ASN: 5, LocalData1: 6, LocalData2: 7}}},
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{{ASN: 1, LocalData1: 2, LocalData2: 3}, {ASN: 3, LocalData1: 4, LocalData2: 5}, {ASN: 5, LocalData1: 6, LocalData2: 7}}},
true,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{{ASN: 1, LocalData1: 2, LocalData2: 3}, {ASN: 3, LocalData1: 4, LocalData2: 5}, {ASN: 5, LocalData1: 6, LocalData2: 7}}},
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{{ASN: 1, LocalData1: 2, LocalData2: 3}, {ASN: 3, LocalData1: 4, LocalData2: 5}, {ASN: 5, LocalData1: 6, LocalData2: 8}}},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{{ASN: 1, LocalData1: 2, LocalData2: 3}, {ASN: 3, LocalData1: 4, LocalData2: 5}, {ASN: 5, LocalData1: 6, LocalData2: 7}}},
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{{ASN: 1, LocalData1: 2, LocalData2: 4}, {ASN: 3, LocalData1: 4, LocalData2: 5}, {ASN: 5, LocalData1: 6, LocalData2: 7}}},
false,
},
{
helpers.Mark(),
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{{ASN: 1, LocalData1: 2, LocalData2: 3}, {ASN: 3, LocalData1: 4, LocalData2: 5}}},
routeAttributes{asn: 2038, largeCommunities: []bgp.LargeCommunity{{ASN: 1, LocalData1: 2, LocalData2: 3}, {ASN: 3, LocalData1: 4, LocalData2: 5}, {ASN: 5, LocalData1: 6, LocalData2: 7}}},
false,
},
{
helpers.Mark(),
routeAttributes{plen: 48},
routeAttributes{plen: 48},
true,
},
{
helpers.Mark(),
routeAttributes{plen: 48},
routeAttributes{plen: 49},
false,
},
}
outer:
for try := 3; try >= 0; try-- {
// We may have to try a few times because of
// collisions due to reduced hash efficiency during
// tests.
for _, tc := range cases {
equal := tc.rta1.Equal(tc.rta2)
if equal && !tc.equal {
t.Errorf("%s%+v == %+v", tc.pos, tc.rta1, tc.rta2)
} else if !equal && tc.equal {
t.Errorf("%s%+v != %+v", tc.pos, tc.rta1, tc.rta2)
} else {
equal := tc.rta1.Hash() == tc.rta2.Hash()
if equal && !tc.equal {
if try > 0 {
// We may have a collision, change the seed and retry
rtaHashSeed = maphash.MakeSeed()
continue outer
}
t.Errorf("%s%+v.hash == %+v.hash", tc.pos, tc.rta1, tc.rta2)
} else if !equal && tc.equal {
t.Errorf("%s%+v.hash != %+v.hash", tc.pos, tc.rta1, tc.rta2)
}
}
}
}
}
func TestRemoveRoutes(t *testing.T) {
nr := func(r *rib, peer uint32) route {
return route{
peer: peer,
nlri: r.nlris.Put(nlri{family: bgp.RF_IPv4_UC, path: 1}),
nextHop: r.nextHops.Put(nextHop(netip.MustParseAddr("::ffff:198.51.100.8"))),
attributes: r.rtas.Put(routeAttributes{
asn: 65300,
plen: 96 + 24,
}),
}
}
t.Run("only route", func(t *testing.T) {
r := newRIB()
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), nr(r, 10))
idx, _ := r.tree.Lookup(netip.MustParseAddr("::ffff:192.168.144.10"))
count, empty := r.removeRoutes(idx, func(route) bool { return true }, true)
if !empty {
t.Error("removeRoutes() should have removed all routes from node")
}
if count != 1 {
t.Error("removeRoutes() should have removed 1 route")
}
if diff := helpers.Diff(r.routes, map[prefixIndex]route{}); diff != "" {
t.Errorf("removeRoutes() (-got, +want):\n%s", diff)
}
})
t.Run("first route", func(t *testing.T) {
r := newRIB()
r1 := nr(r, 10)
r2 := nr(r, 11)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r1)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r2)
idx, _ := r.tree.Lookup(netip.MustParseAddr("::ffff:192.168.144.10"))
count, empty := r.removeRoutes(idx, func(r route) bool { return r.peer == 10 }, true)
if empty {
t.Error("removeRoutes() should not have removed all routes from node")
}
if count != 1 {
t.Error("removeRoutes() should have removed 1 route")
}
if diff := helpers.Diff(r.routes, map[routeKey]route{
makeRouteKey(idx, 0): r2,
}); diff != "" {
t.Errorf("removeRoutes() (-got, +want):\n%s", diff)
}
})
t.Run("second route", func(t *testing.T) {
r := newRIB()
r1 := nr(r, 10)
r2 := nr(r, 11)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r1)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r2)
idx, _ := r.tree.Lookup(netip.MustParseAddr("::ffff:192.168.144.10"))
count, empty := r.removeRoutes(idx, func(r route) bool { return r.peer == 11 }, true)
if empty {
t.Error("removeRoutes() should not have removed all routes from node")
}
if count != 1 {
t.Error("removeRoutes() should have removed 1 route")
}
if diff := helpers.Diff(r.routes, map[routeKey]route{
makeRouteKey(idx, 0): r1,
}); diff != "" {
t.Errorf("removeRoutes() (-got, +want):\n%s", diff)
}
})
t.Run("middle route", func(t *testing.T) {
r := newRIB()
r1 := nr(r, 10)
r2 := nr(r, 11)
r3 := nr(r, 12)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r1)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r2)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r3)
idx, _ := r.tree.Lookup(netip.MustParseAddr("::ffff:192.168.144.10"))
count, empty := r.removeRoutes(idx, func(r route) bool { return r.peer == 11 }, true)
if empty {
t.Error("removeRoutes() should not have removed all routes from node")
}
if count != 1 {
t.Error("removeRoutes() should have removed 1 route")
}
if diff := helpers.Diff(r.routes, map[routeKey]route{
makeRouteKey(idx, 0): r1,
makeRouteKey(idx, 1): r3,
}); diff != "" {
t.Errorf("removeRoutes() (-got, +want):\n%s", diff)
}
})
t.Run("one route out of two", func(t *testing.T) {
r := newRIB()
r1 := nr(r, 10)
r2 := nr(r, 11)
r3 := nr(r, 12)
r4 := nr(r, 13)
r5 := nr(r, 14)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r1)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r2)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r3)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r4)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r5)
idx, _ := r.tree.Lookup(netip.MustParseAddr("::ffff:192.168.144.10"))
count, empty := r.removeRoutes(idx, func(r route) bool { return r.peer%2 == 0 }, false)
if empty {
t.Error("removeRoutes() should not have removed all routes from node")
}
if count != 3 {
t.Error("removeRoutes() should have removed 3 route")
}
if diff := helpers.Diff(r.routes, map[routeKey]route{
makeRouteKey(idx, 0): r2,
makeRouteKey(idx, 1): r4,
}); diff != "" {
t.Errorf("removeRoutes() (-got, +want):\n%s", diff)
}
})
t.Run("all routes", func(t *testing.T) {
r := newRIB()
r1 := nr(r, 10)
r2 := nr(r, 11)
r3 := nr(r, 12)
r4 := nr(r, 13)
r5 := nr(r, 14)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r1)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r2)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r3)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r4)
r.addPrefix(netip.MustParsePrefix("::ffff:192.168.144.0/120"), r5)
idx, _ := r.tree.Lookup(netip.MustParseAddr("::ffff:192.168.144.10"))
count, empty := r.removeRoutes(idx, func(route) bool { return true }, false)
if !empty {
t.Error("removeRoutes() should have removed all routes from node")
}
if count != 5 {
t.Error("removeRoutes() should have removed 5 route")
}
if diff := helpers.Diff(r.routes, map[routeKey]route{}); diff != "" {
t.Errorf("removeRoutes() (-got, +want):\n%s", diff)
}
})
}
func TestRIBHarness(t *testing.T) {
for run := range 5 {
random := rand.New(rand.NewSource(int64(run)))
run++
// Ramp up the test
totalExporters := run
peerPerExporter := 1 + run/2
maxInitialRoutePerPeer := 500 * run
maxRemovedRoutePerPeer := 100 * run
maxReaddedRoutePerPeer := 50 * run
t.Logf("Run %d. Exporters=%d, Peers=%d, Initial=max %d, Removed=max %d, Readded=max %d",
run, totalExporters, peerPerExporter,
maxInitialRoutePerPeer, maxRemovedRoutePerPeer, maxReaddedRoutePerPeer)
r := newRIB()
type lookup struct {
peer uint32
addr netip.Addr
nextHop netip.Addr
rd RD
asn uint32
removed bool
}
// We store all lookups that should succeed
lookups := []lookup{}
removeLookup := func(lookup lookup) {
for idx := range lookups {
if lookups[idx].peer != lookup.peer {
continue
}
if lookups[idx].addr != lookup.addr || lookups[idx].rd != lookup.rd {
continue
}
if lookups[idx].removed {
continue
}
lookups[idx].removed = true
break
}
}
peers := []uint32{}
for i := range totalExporters {
for j := range peerPerExporter {
peer := uint32((i << 16) + int(j))
peers = append(peers, peer)
toAdd := random.Intn(maxInitialRoutePerPeer)
added := 0
for range toAdd {
lookup := lookup{
peer: peer,
addr: netip.MustParseAddr(fmt.Sprintf("2001:db8:f:%x::",
random.Intn(300))),
nextHop: netip.MustParseAddr(
fmt.Sprintf("2001:db8:c::%x", random.Intn(500))),
rd: RD(random.Intn(3)),
asn: uint32(random.Intn(1000)),
}
added += r.addPrefix(netip.PrefixFrom(lookup.addr, 64),
route{
peer: peer,
nlri: r.nlris.Put(nlri{rd: lookup.rd}),
nextHop: r.nextHops.Put(nextHop(lookup.nextHop)),
attributes: r.rtas.Put(routeAttributes{
asn: lookup.asn,
}),
})
removeLookup(lookup)
lookups = append(lookups, lookup)
}
t.Logf("Run %d: added = %d/%d", run, added, toAdd)
toRemove := random.Intn(maxRemovedRoutePerPeer)
removed := 0
for range toRemove {
prefix := netip.MustParseAddr(fmt.Sprintf("2001:db8:f:%x::",
random.Intn(300)))
rd := RD(random.Intn(4))
if nlriRef, ok := r.nlris.Ref(nlri{
rd: rd,
}); ok {
removed += r.removePrefix(netip.PrefixFrom(prefix, 64),
route{
peer: peer,
nlri: nlriRef,
})
removeLookup(lookup{
peer: peer,
addr: prefix,
rd: rd,
})
}
}
t.Logf("Run %d: removed = %d/%d", run, removed, toRemove)
toAdd = random.Intn(maxReaddedRoutePerPeer)
added = 0
for range toAdd {
lookup := lookup{
peer: peer,
addr: netip.MustParseAddr(fmt.Sprintf("2001:db8:f:%x::",
random.Intn(300))),
nextHop: netip.MustParseAddr(
fmt.Sprintf("2001:db8:c::%x", random.Uint32()%500)),
asn: uint32(random.Intn(1010)),
}
added += r.addPrefix(netip.PrefixFrom(lookup.addr, 64),
route{
peer: peer,
nlri: r.nlris.Put(nlri{}),
nextHop: r.nextHops.Put(nextHop(lookup.nextHop)),
attributes: r.rtas.Put(routeAttributes{
asn: lookup.asn,
}),
})
removeLookup(lookup)
lookups = append(lookups, lookup)
}
t.Logf("Run %d: readedd = %d/%d", run, added, toAdd)
}
}
removed := 0
for _, lookup := range lookups {
if lookup.removed {
removed++
continue
}
// Find prefix in tree
prefixIdx, ok := r.tree.Lookup(lookup.addr)
if !ok {
t.Errorf("cannot find %s for %d",
lookup.addr, lookup.peer)
continue
}
// Check if routes exist for this prefix
found := false
routeFound := false
for route := range r.iterateRoutesForPrefixIndex(prefixIdx) {
routeFound = true // At least one route exists
if r.nextHops.Get(route.nextHop) != nextHop(lookup.nextHop) || r.nlris.Get(route.nlri).rd != lookup.rd {
continue
}
if r.rtas.Get(route.attributes).asn != lookup.asn {
continue
}
found = true
break
}
if !routeFound {
t.Errorf("no routes found for %s for %d",
lookup.addr, lookup.peer)
continue
}
if !found {
t.Logf("Available routes for %s:", lookup.addr)
for route := range r.iterateRoutesForPrefixIndex(prefixIdx) {
t.Logf("peer %d, NH: %s, RD: %s, ASN: %d",
route.peer,
netip.Addr(r.nextHops.Get(route.nextHop)),
r.nlris.Get(route.nlri).rd, r.rtas.Get(route.attributes).asn)
}
t.Errorf("cannot find %s for peer %d; NH: %s, RD: %s, ASN: %d",
lookup.addr, lookup.peer,
lookup.nextHop, lookup.rd, lookup.asn)
}
}
if removed < 5 {
t.Error("did not remove more than 5 prefixes, suspicious...")
}
// Remove everything
for _, peer := range peers {
r.flushPeer(peer)
}
// Check for leak of interned values
if r.nlris.Len() > 0 {
t.Errorf("%d NLRIs have leaked", r.nlris.Len())
}
if r.nextHops.Len() > 0 {
t.Errorf("%d next hops have leaked", r.nextHops.Len())
}
if r.rtas.Len() > 0 {
t.Errorf("%d route attributes have leaked", r.rtas.Len())
}
if t.Failed() {
break
}
}
}
func BenchmarkRTAHash(b *testing.B) {
rta := routeAttributes{
asn: 2038,
asPath: []uint32{1, 2, 3, 4, 5, 6, 7},
}
for b.Loop() {
rta.Hash()
}
}
func BenchmarkRTAEqual(b *testing.B) {
rta := routeAttributes{
asn: 2038,
asPath: []uint32{1, 2, 3, 4, 5, 6, 7},
}
for b.Loop() {
rta.Equal(rta)
}
}