2017-05-16 14:10:19 +02:00
|
|
|
package birdwatcher
|
|
|
|
|
|
|
|
import (
|
2018-06-19 10:02:16 +02:00
|
|
|
"github.com/alice-lg/alice-lg/backend/api"
|
2018-07-11 14:56:38 +02:00
|
|
|
"github.com/alice-lg/alice-lg/backend/caches"
|
2019-01-21 10:21:59 +01:00
|
|
|
"github.com/alice-lg/alice-lg/backend/sources"
|
2018-01-31 15:17:20 +01:00
|
|
|
|
2019-03-04 18:52:49 +01:00
|
|
|
"fmt"
|
2018-03-16 15:23:52 +01:00
|
|
|
"sort"
|
2019-03-04 18:52:49 +01:00
|
|
|
"time"
|
2017-05-16 14:10:19 +02:00
|
|
|
)
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
type Birdwatcher interface {
|
|
|
|
sources.Source
|
|
|
|
}
|
2018-07-14 17:01:12 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
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
|
2018-07-13 17:59:03 +02:00
|
|
|
|
|
|
|
// Mutices:
|
2018-11-09 12:13:47 +01:00
|
|
|
routesFetchMutex *LockMap
|
2017-05-16 14:10:19 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
func NewBirdwatcher(config Config) Birdwatcher {
|
2017-05-19 17:40:22 +02:00
|
|
|
client := NewClient(config.Api)
|
|
|
|
|
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)
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
var birdwatcher Birdwatcher
|
2018-07-14 17:01:12 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
if config.Type == "single_table" {
|
|
|
|
singleTableBirdwatcher := new(SingleTableBirdwatcher)
|
2018-07-13 16:40:28 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
singleTableBirdwatcher.config = config
|
|
|
|
singleTableBirdwatcher.client = client
|
2018-07-13 16:40:28 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
singleTableBirdwatcher.neighborsCache = neighborsCache
|
2017-05-16 14:10:19 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
singleTableBirdwatcher.routesRequiredCache = routesRequiredCache
|
|
|
|
singleTableBirdwatcher.routesNotExportedCache = routesNotExportedCache
|
2017-05-22 11:09:22 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
singleTableBirdwatcher.routesFetchMutex = NewLockMap()
|
2017-05-19 17:40:22 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
birdwatcher = singleTableBirdwatcher
|
|
|
|
} else if config.Type == "multi_table" {
|
|
|
|
multiTableBirdwatcher := new(MultiTableBirdwatcher)
|
2017-05-19 17:40:22 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
multiTableBirdwatcher.config = config
|
|
|
|
multiTableBirdwatcher.client = client
|
2017-05-18 18:10:55 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
multiTableBirdwatcher.neighborsCache = neighborsCache
|
2017-05-16 14:10:19 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
multiTableBirdwatcher.routesRequiredCache = routesRequiredCache
|
|
|
|
multiTableBirdwatcher.routesNotExportedCache = routesNotExportedCache
|
2018-07-14 17:01:12 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
multiTableBirdwatcher.routesFetchMutex = NewLockMap()
|
2018-07-11 13:56:02 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
birdwatcher = multiTableBirdwatcher
|
2018-07-14 17:01:12 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
return birdwatcher
|
2018-07-14 17:01:12 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
func (self *GenericBirdwatcher) filterProtocols(protocols map[string]interface{}, protocol string) map[string]interface{} {
|
|
|
|
response := make(map[string]interface{})
|
|
|
|
response["protocols"] = make(map[string]interface{})
|
2018-07-14 17:01:12 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
for protocolId, protocolData := range protocols {
|
|
|
|
if protocolData.(map[string]interface{})["bird_protocol"] == protocol {
|
|
|
|
response["protocols"].(map[string]interface{})[protocolId] = protocolData
|
|
|
|
}
|
2018-07-11 13:56:02 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
return response
|
2018-07-14 17:01:12 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
func (self *GenericBirdwatcher) filterProtocolsBgp(bird ClientResponse) map[string]interface{} {
|
|
|
|
return self.filterProtocols(bird["protocols"].(map[string]interface{}), "BGP")
|
2017-05-16 14:10:19 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
func (self *GenericBirdwatcher) filterProtocolsPipe(bird ClientResponse) map[string]interface{} {
|
|
|
|
return self.filterProtocols(bird["protocols"].(map[string]interface{}), "Pipe")
|
2018-07-13 16:40:28 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
func (self *GenericBirdwatcher) filterRoutesByPeerOrLearntFrom(routes api.Routes, peer string, learntFrom string) api.Routes {
|
|
|
|
result_routes := make(api.Routes, 0, len(routes))
|
2018-07-13 17:47:41 +02:00
|
|
|
|
|
|
|
// Choose routes with next_hop == gateway of this neighbour
|
2019-01-21 10:21:59 +01:00
|
|
|
for _, route := range routes {
|
|
|
|
if (route.Gateway == peer) ||
|
|
|
|
(route.Gateway == learntFrom) ||
|
|
|
|
(route.Details["learnt_from"] == peer) {
|
|
|
|
result_routes = append(result_routes, route)
|
2018-07-13 17:47:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort routes for deterministic ordering
|
2019-01-21 10:21:59 +01:00
|
|
|
sort.Sort(result_routes)
|
|
|
|
routes = result_routes
|
2018-07-13 16:40:28 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
return routes
|
2018-07-13 16:40:28 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
func (self *GenericBirdwatcher) filterRoutesByDuplicates(routes api.Routes, filterRoutes api.Routes) api.Routes {
|
|
|
|
result_routes := make(api.Routes, 0, len(routes))
|
2018-07-13 16:40:28 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
routesMap := make(map[string]*api.Route) // for O(1) access
|
|
|
|
for _, route := range routes {
|
|
|
|
routesMap[route.Id] = route
|
2018-07-13 17:47:41 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
// Remove routes from "routes" that are contained within filterRoutes
|
|
|
|
for _, filterRoute := range filterRoutes {
|
|
|
|
if _, ok := routesMap[filterRoute.Id]; ok {
|
|
|
|
delete(routesMap, filterRoute.Id)
|
|
|
|
}
|
2018-07-13 16:40:28 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
for _, route := range routesMap {
|
|
|
|
result_routes = append(result_routes, route)
|
2018-07-13 16:40:28 +02:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
// Sort routes for deterministic ordering
|
|
|
|
sort.Sort(result_routes)
|
|
|
|
routes = result_routes
|
2018-07-13 16:40:28 +02:00
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
return routes
|
2018-07-13 16:40:28 +02:00
|
|
|
}
|
|
|
|
|
2019-03-04 18:52:49 +01:00
|
|
|
func (self *GenericBirdwatcher) filterRoutesByNeighborId(routes api.Routes, neighborId string) api.Routes {
|
|
|
|
result_routes := make(api.Routes, 0, len(routes))
|
|
|
|
|
|
|
|
// Choose routes with next_hop == gateway of this neighbour
|
|
|
|
for _, route := range routes {
|
|
|
|
if route.Details["from_protocol"] == neighborId {
|
|
|
|
result_routes = append(result_routes, route)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort routes for deterministic ordering
|
|
|
|
sort.Sort(result_routes)
|
|
|
|
routes = result_routes
|
|
|
|
|
|
|
|
return routes
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *GenericBirdwatcher) fetchProtocolsShort() (*api.ApiStatus, map[string]interface{}, error) {
|
|
|
|
// Query birdwatcher
|
|
|
|
timeout := 2 * time.Second
|
|
|
|
if self.config.NeighborsRefreshTimeout > 0 {
|
|
|
|
timeout = time.Duration(self.config.NeighborsRefreshTimeout) * time.Second
|
|
|
|
}
|
|
|
|
bird, err := self.client.GetJsonTimeout(timeout, "/protocols/short?uncached=true")
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use api status from first request
|
|
|
|
apiStatus, err := parseApiStatus(bird, self.config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := bird["protocols"]; !ok {
|
|
|
|
return nil, nil, fmt.Errorf("Failed to fetch protocols")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &apiStatus, bird, nil
|
|
|
|
}
|
|
|
|
|
2019-02-18 20:20:26 +01:00
|
|
|
func (self *GenericBirdwatcher) ExpireCaches() int {
|
|
|
|
count := self.routesRequiredCache.Expire()
|
|
|
|
count += self.routesNotExportedCache.Expire()
|
|
|
|
|
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
func (self *GenericBirdwatcher) Status() (*api.StatusResponse, error) {
|
|
|
|
// Query birdwatcher
|
|
|
|
bird, err := self.client.GetJson("/status")
|
2018-07-13 16:40:28 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use api status from first request
|
|
|
|
apiStatus, err := parseApiStatus(bird, self.config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
// Parse the status
|
|
|
|
birdStatus, err := parseBirdwatcherStatus(bird, self.config)
|
2018-07-13 16:40:28 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-01-21 10:21:59 +01:00
|
|
|
response := &api.StatusResponse{
|
|
|
|
Api: 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
|
|
|
|
2019-03-04 18:52:49 +01:00
|
|
|
// Get live neighbor status
|
|
|
|
func (self *GenericBirdwatcher) NeighboursStatus() (*api.NeighboursStatusResponse, error) {
|
|
|
|
// Query birdwatcher
|
|
|
|
apiStatus, birdProtocols, err := self.fetchProtocolsShort()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the neighbors short
|
|
|
|
neighbours, err := parseNeighboursShort(birdProtocols, self.config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
response := &api.NeighboursStatusResponse{
|
|
|
|
Api: *apiStatus,
|
|
|
|
Neighbours: neighbours,
|
|
|
|
}
|
|
|
|
|
|
|
|
return response, nil // dereference for now
|
|
|
|
}
|
|
|
|
|
2017-05-23 13:58:58 +02:00
|
|
|
// Make routes lookup
|
2019-01-21 10:21:59 +01:00
|
|
|
func (self *GenericBirdwatcher) LookupPrefix(prefix string) (*api.RoutesLookupResponse, error) {
|
2017-06-19 15:27:11 +02:00
|
|
|
// Get RS info
|
|
|
|
rs := api.Routeserver{
|
|
|
|
Id: self.config.Id,
|
|
|
|
Name: self.config.Name,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query prefix on RS
|
|
|
|
bird, err := self.client.GetJson("/routes/prefix?prefix=" + prefix)
|
|
|
|
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
|
|
|
|
apiStatus, err := parseApiStatus(bird, self.config)
|
|
|
|
if err != nil {
|
2018-07-11 18:25:42 +02:00
|
|
|
return nil, err
|
2017-06-19 15:27:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse routes
|
|
|
|
routes, err := parseRoutes(bird, self.config)
|
|
|
|
|
|
|
|
// Add corresponding neighbour 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{
|
2017-06-19 15:27:11 +02:00
|
|
|
Id: src.Id,
|
|
|
|
|
|
|
|
Routeserver: rs,
|
|
|
|
|
|
|
|
NeighbourId: src.NeighbourId,
|
|
|
|
|
|
|
|
Network: src.Network,
|
|
|
|
Interface: src.Interface,
|
|
|
|
Gateway: src.Gateway,
|
|
|
|
Metric: src.Metric,
|
|
|
|
Bgp: src.Bgp,
|
|
|
|
Age: src.Age,
|
|
|
|
Type: src.Type,
|
|
|
|
|
|
|
|
Details: src.Details,
|
|
|
|
}
|
|
|
|
results = append(results, route)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make result
|
2018-07-11 18:25:42 +02:00
|
|
|
response := &api.RoutesLookupResponse{
|
2017-06-19 15:27:11 +02:00
|
|
|
Api: apiStatus,
|
|
|
|
Routes: results,
|
|
|
|
}
|
|
|
|
return response, nil
|
2017-05-23 13:58:58 +02:00
|
|
|
}
|