FIX: In some cases the search returned routes for neighbors not queried for.

This commit is contained in:
Annika Hannig 2024-01-12 11:34:05 +01:00
parent e6c4b42bfc
commit 6c5c7ea14f
12 changed files with 129 additions and 37 deletions

View File

@ -157,6 +157,13 @@ type RouteServersResponse struct {
RouteServers RouteServers `json:"routeservers"`
}
// A LookupRouteServer is a shorter representation of the
// route server data source.
type LookupRouteServer struct {
ID *string `json:"id"`
Name string `json:"name"`
}
// Community is a BGP community
type Community []int

View File

@ -72,7 +72,7 @@ func (routes Routes) Swap(i, j int) {
// ToLookupRoutes prepares routes for lookup
func (routes Routes) ToLookupRoutes(
state string,
rs *RouteServer,
rs *LookupRouteServer,
neighbors map[string]*Neighbor,
) LookupRoutes {
lookupRoutes := make(LookupRoutes, 0, len(routes))
@ -142,6 +142,13 @@ const (
RouteStateImported = "imported"
)
// NeighborQuery is used in finding routes by neighbors.
// Source and Neighbor IDs are pointers to string pools.
type NeighborQuery struct {
NeighborID *string
SourceID *string
}
// LookupRoute is a route with additional
// neighbor and state information
type LookupRoute struct {
@ -149,13 +156,13 @@ type LookupRoute struct {
State string `json:"state"` // Filtered, Imported, ...
Neighbor *Neighbor `json:"neighbor"`
RouteServer *RouteServer `json:"routeserver"`
Neighbor *Neighbor `json:"neighbor"`
RouteServer *LookupRouteServer `json:"routeserver"`
}
// MatchSourceID implements filterable interface for lookup routes
func (r *LookupRoute) MatchSourceID(id string) bool {
return r.RouteServer.ID == id
return *r.RouteServer.ID == id
}
// MatchASN matches the neighbor's ASN
@ -178,6 +185,17 @@ func (r *LookupRoute) MatchLargeCommunity(community Community) bool {
return r.Route.BGP.HasLargeCommunity(community)
}
// MatchNeighborQuery matches a neighbor query
func (r *LookupRoute) MatchNeighborQuery(query *NeighborQuery) bool {
if r.RouteServer.ID != query.SourceID {
return false
}
if r.NeighborID != query.NeighborID {
return false
}
return true
}
// LookupRoutes is a collection of lookup routes.
type LookupRoutes []*LookupRoute

View File

@ -48,7 +48,32 @@ func searchFilterCmpInt(a FilterValue, b FilterValue) bool {
// Compare strings
func searchFilterCmpString(a FilterValue, b FilterValue) bool {
return a.(string) == b.(string)
var (
valA string
valB string
)
_, ptrA := a.(*string)
_, ptrB := b.(*string)
// Compare pointers, this is ok because we can assume
// using pool values for both.
if ptrA && ptrB {
return a == b
}
// Otherwise fall back to string compare
if ptrA {
valA = *a.(*string)
} else {
valA = a.(string)
}
if ptrB {
valB = *b.(*string)
} else {
valB = b.(string)
}
return valA == valB
}
// Compare communities
@ -136,6 +161,8 @@ func filterValueAsString(value interface{}) string {
switch v := value.(type) {
case int:
return strconv.Itoa(v)
case *string:
return *v
case string:
return v
case Community:

View File

@ -5,6 +5,10 @@ import (
"testing"
)
var (
testRsID string = "3"
)
func makeTestRoute() *Route {
route := &Route{
BGP: &BGPInfo{
@ -44,8 +48,8 @@ func makeTestLookupRoute() *LookupRoute {
ASN: 23042,
Description: "Security Solutions Ltd.",
},
RouteServer: &RouteServer{
ID: "3",
RouteServer: &LookupRouteServer{
ID: &testRsID,
Name: "test.rs.ixp",
},
}

View File

@ -17,6 +17,7 @@ import (
"github.com/alice-lg/alice-lg/pkg/api"
"github.com/alice-lg/alice-lg/pkg/decoders"
"github.com/alice-lg/alice-lg/pkg/pools"
"github.com/alice-lg/alice-lg/pkg/sources"
"github.com/alice-lg/alice-lg/pkg/sources/birdwatcher"
"github.com/alice-lg/alice-lg/pkg/sources/gobgp"
@ -747,6 +748,9 @@ func getSources(config *ini.File) ([]*SourceConfig, error) {
Type: sourceType,
}
// Register route server ID with pool
pools.RouteServers.Acquire(sourceID)
// Set backend
switch backendType {
case SourceBackendBirdwatcher:

View File

@ -7,6 +7,9 @@ import "log"
// Default pools: These pools are defined globally
// and are defined per intended usage
// RouteServers stores route server IDs
var RouteServers *StringPool
// Neighbors stores neighbor IDs
var Neighbors *StringPool
@ -47,6 +50,7 @@ var LargeCommunities *CommunitiesPool
func init() {
log.Println("initializing memory pools")
RouteServers = NewStringPool()
Neighbors = NewStringPool()
Networks4 = NewStringPool()
Networks6 = NewStringPool()

View File

@ -213,7 +213,7 @@ func (b *RoutesBackend) CountRoutesAt(
// list of neighbors identified by ID.
func (b *RoutesBackend) FindByNeighbors(
ctx context.Context,
neighborIDs []string,
neighbors []*api.NeighborQuery,
) (api.LookupRoutes, error) {
tx, err := b.pool.BeginTx(ctx, pgx.TxOptions{
IsoLevel: pgx.ReadCommitted,
@ -223,23 +223,22 @@ func (b *RoutesBackend) FindByNeighbors(
}
defer tx.Rollback(ctx)
vals := make([]interface{}, len(neighborIDs))
for i := range neighborIDs {
vals[i] = neighborIDs[i]
}
vars := make([]string, len(neighborIDs))
for i := range neighborIDs {
vars[i] = fmt.Sprintf("$%d", i+1)
}
listQry := strings.Join(vars, ",")
vals := make([]interface{}, 0, len(neighbors))
vars := 0
qrys := []string{}
for _, src := range b.sources {
tbl := b.routesTable(src.ID)
for _, neighborQuery := range neighbors {
tbl := b.routesTable(*neighborQuery.SourceID)
param := fmt.Sprintf("$%d", vars+1)
vals = append(vals, *neighborQuery.NeighborID)
qry := `
SELECT route FROM ` + tbl + `
WHERE neighbor_id IN (` + listQry + `)`
WHERE neighbor_id = ` + param
qrys = append(qrys, qry)
vars++
}
qry := strings.Join(qrys, " UNION ")

View File

@ -113,9 +113,16 @@ func TestFindByNeighbors(t *testing.T) {
t.Fatal(err)
}
routes, err := b.FindByNeighbors(ctx, []string{
"n24", "n25",
})
nq1 := &api.NeighborQuery{
NeighborID: pools.Neighbors.Acquire("n24"),
SourceID: pools.RouteServers.Acquire("rs1"),
}
nq2 := &api.NeighborQuery{
NeighborID: pools.Neighbors.Acquire("n25"),
SourceID: pools.RouteServers.Acquire("rs2"),
}
routes, err := b.FindByNeighbors(ctx, []*api.NeighborQuery{nq1, nq2})
if err != nil {
t.Fatal(err)
}

View File

@ -247,8 +247,6 @@ func (s *NeighborsStore) lookupNeighborsAt(
sourceID string,
query string,
) (api.Neighbors, error) {
results := api.Neighbors{}
neighbors, err := s.backend.GetNeighborsAt(ctx, sourceID)
if err != nil {
return nil, err
@ -262,6 +260,7 @@ func (s *NeighborsStore) lookupNeighborsAt(
}
}
results := api.Neighbors{}
for _, neighbor := range neighbors {
if asn >= 0 && neighbor.ASN == asn { // only executed if valid AS query is detected
results = append(results, neighbor)

View File

@ -9,9 +9,27 @@ import (
"github.com/alice-lg/alice-lg/pkg/api"
"github.com/alice-lg/alice-lg/pkg/config"
"github.com/alice-lg/alice-lg/pkg/pools"
"github.com/alice-lg/alice-lg/pkg/sources"
)
// newNeighborQuery creates a new NeighborQuery
func newNeighborQuery(neighborID string, sourceID string) *api.NeighborQuery {
ptrNeighborID := pools.Neighbors.Get(neighborID)
if ptrNeighborID == nil {
return nil
}
ptrSourceID := pools.RouteServers.Get(sourceID)
if ptrSourceID == nil {
return nil
}
return &api.NeighborQuery{
NeighborID: ptrNeighborID,
SourceID: ptrSourceID,
}
}
// RoutesStoreBackend interface
type RoutesStoreBackend interface {
// SetRoutes updates the routes in the store after a refresh.
@ -33,7 +51,7 @@ type RoutesStoreBackend interface {
// announced by the neighbor at a given source
FindByNeighbors(
ctx context.Context,
neighborIDs []string,
neighbors []*api.NeighborQuery,
) (api.LookupRoutes, error)
// FindByPrefix
@ -182,8 +200,8 @@ func (s *RoutesStore) updateSource(
"accepted and", len(res.Filtered), "filtered routes for:", src.Name)
// Prepare imported routes for lookup
srcRS := &api.RouteServer{
ID: src.ID,
srcRS := &api.LookupRouteServer{
ID: pools.RouteServers.Acquire(src.ID),
Name: src.Name,
}
imported := res.Imported.ToLookupRoutes("imported", srcRS, neighbors)
@ -320,11 +338,16 @@ func (s *RoutesStore) LookupPrefixForNeighbors(
ctx context.Context,
neighbors api.NeighborsLookupResults,
) (api.LookupRoutes, error) {
neighborIDs := []string{}
for _, rs := range neighbors {
for _, neighbor := range rs {
neighborIDs = append(neighborIDs, neighbor.ID)
query := make([]*api.NeighborQuery, 0, len(neighbors))
for sourceID, sourceNeighbors := range neighbors {
for _, neighbor := range sourceNeighbors {
q := newNeighborQuery(neighbor.ID, sourceID)
if q == nil {
continue
}
query = append(query, q)
}
}
return s.backend.FindByNeighbors(ctx, neighborIDs)
return s.backend.FindByNeighbors(ctx, query)
}

View File

@ -29,8 +29,8 @@ func importRoutes(
ID: "ID7254_AS31334",
},
}
srcRS := &api.RouteServer{
ID: src.ID,
srcRS := &api.LookupRouteServer{
ID: pools.RouteServers.Acquire(src.ID),
Name: src.Name,
}
imported := res.Imported.ToLookupRoutes("imported", srcRS, neighbors)

View File

@ -42,8 +42,8 @@ func LoadTestLookupRoutes(srcID, srcName string) api.LookupRoutes {
ID: "ID7254_AS31334",
},
}
rs := &api.RouteServer{
ID: srcID,
rs := &api.LookupRouteServer{
ID: pools.RouteServers.Acquire(srcID),
Name: srcName,
}
imported := res.Imported.ToLookupRoutes("imported", rs, neighbors)