1
0
mirror of https://github.com/alice-lg/alice-lg.git synced 2024-05-11 05:55:03 +00:00
Benedikt Rudolph 24ae84c545 Fix deduplication of routes for peer with only filtered routes.
Effectively de-activate routes deduplication in case a peer announces
just filtered routes. Because no legitimate (accepted) route is
announced, we cannot reliably infer the neighbours gateway / nextHop IP.

This needs changes in the future  when another means of obtaining the
neighours gateway address (e.g. global peer status) becomes available.
2018-06-27 18:50:28 +02:00

219 lines
5.5 KiB
Go

package birdwatcher
import (
"github.com/alice-lg/alice-lg/backend/api"
"log"
"sort"
)
type Birdwatcher struct {
config Config
client *Client
}
func NewBirdwatcher(config Config) *Birdwatcher {
client := NewClient(config.Api)
birdwatcher := &Birdwatcher{
config: config,
client: client,
}
return birdwatcher
}
func (self *Birdwatcher) Status() (api.StatusResponse, error) {
bird, err := self.client.GetJson("/status")
if err != nil {
return api.StatusResponse{}, err
}
apiStatus, err := parseApiStatus(bird, self.config)
if err != nil {
return api.StatusResponse{}, err
}
birdStatus, err := parseBirdwatcherStatus(bird, self.config)
if err != nil {
return api.StatusResponse{}, err
}
response := api.StatusResponse{
Api: apiStatus,
Status: birdStatus,
}
return response, nil
}
// Get bird BGP protocols
func (self *Birdwatcher) Neighbours() (api.NeighboursResponse, error) {
bird, err := self.client.GetJson("/protocols/bgp")
if err != nil {
return api.NeighboursResponse{}, err
}
apiStatus, err := parseApiStatus(bird, self.config)
if err != nil {
return api.NeighboursResponse{}, err
}
neighbours, err := parseNeighbours(bird, self.config)
if err != nil {
return api.NeighboursResponse{}, err
}
return api.NeighboursResponse{
Api: apiStatus,
Neighbours: neighbours,
}, nil
}
// Get filtered and exported routes
func (self *Birdwatcher) Routes(neighbourId string) (api.RoutesResponse, error) {
// Exported
bird, err := self.client.GetJson("/routes/protocol/" + neighbourId)
if err != nil {
return api.RoutesResponse{}, err
}
// Use api status from first request
apiStatus, err := parseApiStatus(bird, self.config)
if err != nil {
return api.RoutesResponse{}, err
}
imported, err := parseRoutes(bird, self.config)
if err != nil {
return api.RoutesResponse{}, err
}
gateway := ""
if len(imported) > 0 { // infer next_hop ip address from imported[0]
gateway = imported[0].Gateway //TODO: change mechanism to infor gateway when state becomes available elsewhere.
}
// Optional: Filtered
bird, _ = self.client.GetJson("/routes/filtered/" + neighbourId)
filtered, err := parseRoutes(bird, self.config)
if err != nil {
log.Println("WARNING Could not retrieve filtered routes:", err)
log.Println("Is the 'routes_filtered' module active in birdwatcher?")
} else { // we got a filtered routes response => perform routes deduplication
result_filtered := make(api.Routes, 0, len(filtered))
result_imported := make(api.Routes, 0, len(imported))
importedMap := make(map[string]api.Route) // for O(1) access
for _, route := range imported {
importedMap[route.Id] = route
}
// choose routes with next_hop == gateway of this neighbour
for _, route := range filtered {
if route.Gateway == gateway {
result_filtered = append(result_filtered, route)
delete(importedMap, route.Id) // remove routes that are filtered on pipe
} else if len(imported) == 0 { // in case there are just filtered routes
result_filtered = append(result_filtered, route)
}
}
sort.Sort(result_filtered)
filtered = result_filtered
// map to slice
for _, route := range importedMap {
result_imported = append(result_imported, route)
}
sort.Sort(result_imported)
imported = result_imported
}
// Optional: NoExport
bird, _ = self.client.GetJson("/routes/noexport/" + neighbourId)
noexport, err := parseRoutes(bird, self.config)
if err != nil {
log.Println("WARNING Could not retrieve routes not exported:", err)
log.Println("Is the 'routes_noexport' module active in birdwatcher?")
} else {
result_noexport := make([]api.Route, 0, len(noexport))
// choose routes with next_hop == gateway of this neighbour
for _, route := range noexport {
if route.Gateway == gateway {
result_noexport = append(result_noexport, route)
} else if len(imported) == 0 { // in case there are just filtered routes
result_noexport = append(result_noexport, route)
}
}
}
return api.RoutesResponse{
Api: apiStatus,
Imported: imported,
Filtered: filtered,
NotExported: noexport,
}, nil
}
// Make routes lookup
func (self *Birdwatcher) LookupPrefix(prefix string) (api.RoutesLookupResponse, error) {
// 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 {
return api.RoutesLookupResponse{}, err
}
// Parse API status
apiStatus, err := parseApiStatus(bird, self.config)
if err != nil {
return api.RoutesLookupResponse{}, err
}
// Parse routes
routes, err := parseRoutes(bird, self.config)
// Add corresponding neighbour and source rs to result
results := []api.LookupRoute{}
for _, src := range routes {
// Okay. This is actually really hacky.
// A less bruteforce approach would be highly appreciated
route := api.LookupRoute{
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
response := api.RoutesLookupResponse{
Api: apiStatus,
Routes: results,
}
return response, nil
}
func (self *Birdwatcher) AllRoutes() (api.RoutesResponse, error) {
bird, err := self.client.GetJson("/routes/dump")
if err != nil {
return api.RoutesResponse{}, err
}
result, err := parseRoutesDump(bird, self.config)
return result, err
}