1
0
mirror of https://github.com/alice-lg/alice-lg.git synced 2024-05-11 05:55:03 +00:00

345 lines
8.1 KiB
Go
Raw Normal View History

2017-05-16 14:10:19 +02:00
package birdwatcher
import (
"fmt"
"sort"
"time"
2021-03-22 17:35:20 +01:00
"github.com/alice-lg/alice-lg/pkg/api"
"github.com/alice-lg/alice-lg/pkg/caches"
"github.com/alice-lg/alice-lg/pkg/sources"
2017-05-16 14:10:19 +02:00
)
2021-10-15 19:24:22 +02:00
// A Birdwatcher source is a variant of an alice
// source and implements different strategies for fetching
// route information from bird.
type Birdwatcher interface {
sources.Source
}
2021-10-15 19:24:22 +02:00
// GenericBirdwatcher is an Alice data source.
type GenericBirdwatcher struct {
2018-07-13 16:40:28 +02:00
config Config
client *Client
// Caches: Neighbors
2018-07-11 14:56:38 +02:00
neighborsCache *caches.NeighborsCache
2018-07-13 16:40:28 +02:00
// Caches: Routes
2018-07-13 17:47:41 +02:00
routesRequiredCache *caches.RoutesCache
2018-07-13 16:40:28 +02:00
routesNotExportedCache *caches.RoutesCache
// Mutices:
2018-11-09 12:13:47 +01:00
routesFetchMutex *LockMap
2017-05-16 14:10:19 +02:00
}
2021-10-15 19:24:22 +02:00
// NewBirdwatcher creates a new Birdwatcher instance.
// This might be either a GenericBirdWatcher or a MultiTableBirdwatcher.
func NewBirdwatcher(config Config) Birdwatcher {
2021-07-02 14:30:43 +02:00
client := NewClient(config.API)
2017-05-19 17:40:22 +02:00
2018-07-13 16:40:28 +02:00
// Cache settings:
// TODO: Maybe read from config file
neighborsCacheDisable := false
routesCacheDisabled := false
routesCacheMaxSize := 128
// Initialize caches
neighborsCache := caches.NewNeighborsCache(neighborsCacheDisable)
2018-07-13 17:47:41 +02:00
routesRequiredCache := caches.NewRoutesCache(
2018-07-13 16:40:28 +02:00
routesCacheDisabled, routesCacheMaxSize)
routesNotExportedCache := caches.NewRoutesCache(
routesCacheDisabled, routesCacheMaxSize)
var birdwatcher Birdwatcher
if config.Type == "single_table" {
singleTableBirdwatcher := new(SingleTableBirdwatcher)
2018-07-13 16:40:28 +02:00
singleTableBirdwatcher.config = config
singleTableBirdwatcher.client = client
2018-07-13 16:40:28 +02:00
singleTableBirdwatcher.neighborsCache = neighborsCache
2017-05-16 14:10:19 +02:00
singleTableBirdwatcher.routesRequiredCache = routesRequiredCache
singleTableBirdwatcher.routesNotExportedCache = routesNotExportedCache
2017-05-22 11:09:22 +02:00
singleTableBirdwatcher.routesFetchMutex = NewLockMap()
2017-05-19 17:40:22 +02:00
birdwatcher = singleTableBirdwatcher
} else if config.Type == "multi_table" {
multiTableBirdwatcher := new(MultiTableBirdwatcher)
2017-05-19 17:40:22 +02:00
multiTableBirdwatcher.config = config
multiTableBirdwatcher.client = client
2017-05-18 18:10:55 +02:00
multiTableBirdwatcher.neighborsCache = neighborsCache
2017-05-16 14:10:19 +02:00
multiTableBirdwatcher.routesRequiredCache = routesRequiredCache
multiTableBirdwatcher.routesNotExportedCache = routesNotExportedCache
multiTableBirdwatcher.routesFetchMutex = NewLockMap()
birdwatcher = multiTableBirdwatcher
}
return birdwatcher
}
2021-10-15 19:24:22 +02:00
func (b *GenericBirdwatcher) filterProtocols(
protocols map[string]interface{},
protocol string,
) map[string]interface{} {
response := make(map[string]interface{})
response["protocols"] = make(map[string]interface{})
2021-10-15 19:24:22 +02:00
for protocolID, protocolData := range protocols {
if protocolData.(map[string]interface{})["bird_protocol"] == protocol {
2021-10-15 19:24:22 +02:00
response["protocols"].(map[string]interface{})[protocolID] = protocolData
}
}
return response
}
2021-10-15 19:24:22 +02:00
func (b *GenericBirdwatcher) filterProtocolsBgp(
bird ClientResponse,
) map[string]interface{} {
return b.filterProtocols(bird["protocols"].(map[string]interface{}), "BGP")
2017-05-16 14:10:19 +02:00
}
2021-10-15 19:24:22 +02:00
func (b *GenericBirdwatcher) filterProtocolsPipe(
bird ClientResponse,
) map[string]interface{} {
return b.filterProtocols(bird["protocols"].(map[string]interface{}), "Pipe")
2018-07-13 16:40:28 +02:00
}
2021-10-15 19:24:22 +02:00
func (b *GenericBirdwatcher) filterRoutesByPeerOrLearntFrom(
routes api.Routes,
peer string,
learntFrom string,
) api.Routes {
resultRoutes := make(api.Routes, 0, len(routes))
2018-07-13 17:47:41 +02:00
2021-10-15 19:24:22 +02:00
// Choose routes with next_hop == gateway of this neighbor
for _, route := range routes {
if (route.Gateway == peer) ||
(route.Gateway == learntFrom) ||
(route.Details["learnt_from"] == peer) {
2021-10-15 19:24:22 +02:00
resultRoutes = append(resultRoutes, route)
2018-07-13 17:47:41 +02:00
}
}
// Sort routes for deterministic ordering
2021-10-15 19:24:22 +02:00
sort.Sort(resultRoutes)
routes = resultRoutes
2018-07-13 16:40:28 +02:00
return routes
2018-07-13 16:40:28 +02:00
}
2021-10-15 19:24:22 +02:00
func (b *GenericBirdwatcher) filterRoutesByDuplicates(
routes api.Routes,
filterRoutes api.Routes,
) api.Routes {
resultRoutes := make(api.Routes, 0, len(routes))
2018-07-13 16:40:28 +02:00
routesMap := make(map[string]*api.Route) // for O(1) access
for _, route := range routes {
2021-10-15 19:24:22 +02:00
routesMap[route.ID] = route
2018-07-13 17:47:41 +02:00
}
// Remove routes from "routes" that are contained within filterRoutes
for _, filterRoute := range filterRoutes {
2021-10-15 19:44:18 +02:00
delete(routesMap, filterRoute.ID)
// in theorey this guard is unneccessary
//if _, ok := routesMap[filterRoute.ID]; ok {
// }
2018-07-13 16:40:28 +02:00
}
for _, route := range routesMap {
2021-10-15 19:24:22 +02:00
resultRoutes = append(resultRoutes, route)
2018-07-13 16:40:28 +02:00
}
// Sort routes for deterministic ordering
2021-10-15 19:24:22 +02:00
sort.Sort(resultRoutes)
routes = resultRoutes // TODO: Check if this even makes sense...
2018-07-13 16:40:28 +02:00
return routes
2018-07-13 16:40:28 +02:00
}
2021-10-15 19:44:18 +02:00
/*
linter says: dead code.
2021-10-15 19:24:22 +02:00
func (b *GenericBirdwatcher) filterRoutesByNeighborID(
routes api.Routes,
neighborID string,
) api.Routes {
resultRoutes := make(api.Routes, 0, len(routes))
2021-10-15 19:24:22 +02:00
// Choose routes with next_hop == gateway of this neighbor
for _, route := range routes {
2021-10-15 19:24:22 +02:00
if route.Details["from_protocol"] == neighborID {
resultRoutes = append(resultRoutes, route)
}
}
// Sort routes for deterministic ordering
2021-10-15 19:24:22 +02:00
sort.Sort(resultRoutes)
routes = resultRoutes
return routes
}
2021-10-15 19:44:18 +02:00
*/
2021-10-15 19:24:22 +02:00
func (b *GenericBirdwatcher) fetchProtocolsShort() (
*api.Meta,
map[string]interface{},
error,
) {
// Query birdwatcher
timeout := 2 * time.Second
2021-10-15 19:24:22 +02:00
if b.config.NeighborsRefreshTimeout > 0 {
timeout = time.Duration(b.config.NeighborsRefreshTimeout) * time.Second
}
2021-10-15 19:44:18 +02:00
bird, err := b.client.GetJSONTimeout(timeout, "/protocols/short?uncached=true")
if err != nil {
return nil, nil, err
}
// Use api status from first request
2021-10-15 19:24:22 +02:00
apiStatus, err := parseAPIStatus(bird, b.config)
if err != nil {
return nil, nil, err
}
if _, ok := bird["protocols"]; !ok {
2021-10-15 19:24:22 +02:00
return nil, nil, fmt.Errorf("failed to fetch protocols")
}
return &apiStatus, bird, nil
}
2021-10-15 19:24:22 +02:00
// ExpireCaches clears all local caches
func (b *GenericBirdwatcher) ExpireCaches() int {
count := b.routesRequiredCache.Expire()
count += b.routesNotExportedCache.Expire()
return count
}
2021-10-15 19:24:22 +02:00
// Status retrievs the current backend status
func (b *GenericBirdwatcher) Status() (*api.StatusResponse, error) {
2021-10-15 19:44:18 +02:00
bird, err := b.client.GetJSON("/status")
2018-07-13 16:40:28 +02:00
if err != nil {
return nil, err
}
// Use api status from first request
2021-10-15 19:24:22 +02:00
apiStatus, err := parseAPIStatus(bird, b.config)
2018-07-13 16:40:28 +02:00
if err != nil {
return nil, err
}
// Parse the status
2021-10-15 19:24:22 +02:00
birdStatus, err := parseBirdwatcherStatus(bird, b.config)
2018-07-13 16:40:28 +02:00
if err != nil {
return nil, err
}
response := &api.StatusResponse{
2021-10-15 19:24:22 +02:00
Response: api.Response{
Meta: apiStatus,
},
Status: birdStatus,
2018-07-13 16:40:28 +02:00
}
2018-07-11 18:25:42 +02:00
return response, nil
2017-05-16 14:10:19 +02:00
}
2017-05-23 13:58:58 +02:00
2021-10-15 19:24:22 +02:00
// NeighborsStatus retrieves neighbor status infos
func (b *GenericBirdwatcher) NeighborsStatus() (
*api.NeighborsStatusResponse,
error,
) {
// Query birdwatcher
2021-10-15 19:24:22 +02:00
apiStatus, birdProtocols, err := b.fetchProtocolsShort()
if err != nil {
return nil, err
}
// Parse the neighbors short
2021-10-15 19:24:22 +02:00
neighbors, err := parseNeighborsShort(birdProtocols, b.config)
if err != nil {
return nil, err
}
2021-10-15 19:24:22 +02:00
response := &api.NeighborsStatusResponse{
Response: api.Response{
Meta: *apiStatus,
},
Neighbors: neighbors,
}
return response, nil // dereference for now
}
2021-10-15 19:24:22 +02:00
// LookupPrefix makes a routes lookup
func (b *GenericBirdwatcher) LookupPrefix(
prefix string,
) (*api.RoutesLookupResponse, error) {
2017-06-19 15:27:11 +02:00
// Get RS info
2021-10-15 19:24:22 +02:00
rs := &api.RouteServer{
ID: b.config.ID,
Name: b.config.Name,
2017-06-19 15:27:11 +02:00
}
// Query prefix on RS
2021-10-15 19:44:18 +02:00
bird, err := b.client.GetJSON("/routes/prefix?prefix=" + prefix)
2017-06-19 15:27:11 +02:00
if err != nil {
2018-07-11 18:25:42 +02:00
return nil, err
2017-06-19 15:27:11 +02:00
}
// Parse API status
2021-10-15 19:24:22 +02:00
apiStatus, err := parseAPIStatus(bird, b.config)
2017-06-19 15:27:11 +02:00
if err != nil {
2018-07-11 18:25:42 +02:00
return nil, err
2017-06-19 15:27:11 +02:00
}
// Parse routes
2021-10-15 19:44:18 +02:00
routes, _ := parseRoutes(bird, b.config)
2017-06-19 15:27:11 +02:00
2021-10-15 19:24:22 +02:00
// Add corresponding neighbor and source rs to result
2018-07-07 11:45:34 +02:00
results := api.LookupRoutes{}
2017-06-19 15:27:11 +02:00
for _, src := range routes {
// Okay. This is actually really hacky.
// A less bruteforce approach would be highly appreciated
2018-07-06 17:04:09 +02:00
route := &api.LookupRoute{
2021-10-15 19:24:22 +02:00
RouteServer: rs,
Route: api.Route{
ID: src.ID,
NeighborID: src.NeighborID,
Network: src.Network,
Interface: src.Interface,
Gateway: src.Gateway,
Metric: src.Metric,
BGP: src.BGP,
Age: src.Age,
Type: src.Type,
Details: src.Details,
},
2017-06-19 15:27:11 +02:00
}
results = append(results, route)
}
// Make result
2018-07-11 18:25:42 +02:00
response := &api.RoutesLookupResponse{
2021-10-15 19:24:22 +02:00
Response: api.Response{
Meta: apiStatus,
},
2017-06-19 15:27:11 +02:00
Routes: results,
}
return response, nil
2017-05-23 13:58:58 +02:00
}