diff --git a/README.md b/README.md index 2352287..1e3b8ec 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,9 @@ Alice-LG is a BGP looking glass which gets its data from external APIs. Currently Alice-LG supports the following APIs: - [birdwatcher API](https://github.com/alice-lg/birdwatcher) for [BIRD](http://bird.network.cz/) +- [GoBGP](https://osrg.github.io/gobgp/) +### Birdwatcher Normally you would first install the [birdwatcher API](https://github.com/alice-lg/birdwatcher) directly on the machine(s) where you run [BIRD](http://bird.network.cz/) on and then install Alice-LG on a seperate public facing server and point her to the afore mentioned [birdwatcher API](https://github.com/alice-lg/birdwatcher). @@ -28,6 +30,9 @@ just prior to [RIPE73](https://ripe73.ripe.net/) in Madrid, Spain. Major thanks to Barry O'Donovan who built the original [INEX Bird's Eye](https://github.com/inex/birdseye) BIRD API of which Alice-LG is a spinnoff +### GoBGP +Alice-LG supports direct integration with GoBGP instances using gRPC. See the configuration section for more detail. + ## Building Alice-LG from scratch __These examples include setting up your Go environment, if you already have set that up then you can obviously skip that__ @@ -74,8 +79,9 @@ You can copy it to any of the following locations: /etc/alice-lg/alice.conf # global -You will have to edit the configuration file as you need to point Alice-LG to the correct [APIs](https://github.com/alice-lg/birdwatcher): +You will have to edit the configuration file as you need to point Alice-LG to the correct backend source. Multiple sources can be configured. +[Birdwatcher](https://github.com/alice-lg/birdwatcher): ```ini [source.rs1-example-v4] name = rs1.example.com (IPv4) @@ -95,6 +101,23 @@ name = rs1.example.com (IPv6) api = http://rs1.example.com:29186/ ``` +[GoBGP](https://osrg.github.io/gobgp/): +```ini +[source.rs2-example] +name = rs2.example.com +group = AMS + +[source.rs2-example.gobgp] +# Host is the IP (or DNS name) and port for the remote GoBGP daemon +host = rs2.example.com:50051 +# ProcessingTimeout is a timeout in seconds configured per gRPC call to a given GoBGP daemon +processing_timeout = 300 +type = multi_table +peer_table_prefix = T +pipe_protocol_prefix = M +neighbors_refresh_timeout = 2 +``` + ## Running Launch the server by running diff --git a/backend/config_test.go b/backend/config_test.go index 254d6a1..733fd51 100644 --- a/backend/config_test.go +++ b/backend/config_test.go @@ -2,6 +2,9 @@ package main import ( "testing" + + "github.com/alice-lg/alice-lg/backend/sources/birdwatcher" + "github.com/alice-lg/alice-lg/backend/sources/gobgp" ) // Test configuration loading and parsing @@ -37,6 +40,42 @@ func TestLoadConfigs(t *testing.T) { } } +// TestSourceConfig checks that the proper backend type was identified for each +// example routeserver +func TestSourceConfig(t *testing.T) { + + config, err := loadConfig("../etc/alice-lg/alice.example.conf") + if err != nil { + t.Error("Could not load test config:", err) + } + + // Get sources + rs1 := config.Sources[0] // Birdwatcher v4 + rs2 := config.Sources[1] // Birdwatcher v6 + rs3 := config.Sources[2] // GoBGP + + nilBirdwatcherConfig := birdwatcher.Config{} + if rs1.Birdwatcher == nilBirdwatcherConfig { + t.Errorf( + "Example routeserver %s should have been identified as a birdwatcher source but was not", + rs1.Name, + ) + } + if rs2.Birdwatcher == nilBirdwatcherConfig { + t.Errorf( + "Example routeserver %s should have been identified as a birdwatcher source but was not", + rs2.Name, + ) + } + nilGoBGPConfig := gobgp.Config{} + if rs3.GoBGP == nilGoBGPConfig { + t.Errorf( + "Example routeserver %s should have been identified as a gobgp source but was not", + rs3.Name, + ) + } +} + func TestSourceConfigDefaultsOverride(t *testing.T) { config, err := loadConfig("../etc/alice-lg/alice.example.conf") @@ -45,8 +84,9 @@ func TestSourceConfigDefaultsOverride(t *testing.T) { } // Get sources - rs1 := config.Sources[0] - rs2 := config.Sources[1] + rs1 := config.Sources[0] // Birdwatcher v4 + rs2 := config.Sources[1] // Birdwatcher v6 + rs3 := config.Sources[2] // GoBGP // Source 1 should be on default time // Source 2 should have an override @@ -67,6 +107,13 @@ func TestSourceConfigDefaultsOverride(t *testing.T) { if rs2.Birdwatcher.Timezone != "Europe/Brussels" { t.Error("Expected 'Europe/Brussels', got", rs2.Birdwatcher.Timezone) } + + if rs3.GoBGP.ProcessingTimeout != 300 { + t.Error( + "Expected GoBGP example to set 300s 'processing_timeout', got", + rs3.GoBGP.ProcessingTimeout, + ) + } } func TestRejectAndNoexportReasons(t *testing.T) { diff --git a/backend/sources/gobgp/config.go b/backend/sources/gobgp/config.go index 6639887..9add903 100644 --- a/backend/sources/gobgp/config.go +++ b/backend/sources/gobgp/config.go @@ -4,8 +4,10 @@ type Config struct { Id string Name string - Host string `ini:"host"` - Insecure bool `ini:"insecure"` - TLSCert string `ini:"tls_crt"` - TLSCommonName string `ini:"tls_common_name"` + Host string `ini:"host"` + Insecure bool `ini:"insecure"` + // ProcessingTimeout is a timeout in seconds configured per gRPC call to a given GoBGP daemon + ProcessingTimeout int `ini:"processing_timeout"` + TLSCert string `ini:"tls_crt"` + TLSCommonName string `ini:"tls_common_name"` } diff --git a/backend/sources/gobgp/routes.go b/backend/sources/gobgp/routes.go index a6fb017..99a6569 100644 --- a/backend/sources/gobgp/routes.go +++ b/backend/sources/gobgp/routes.go @@ -48,7 +48,7 @@ func (gobgp *GoBGP) lookupNeighbour(neighborId string) (*gobgpapi.Peer, error) { } func (gobgp *GoBGP) GetNeighbours() ([]*gobgpapi.Peer, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(gobgp.config.ProcessingTimeout)) defer cancel() peerStream, err := gobgp.client.ListPeer(ctx, &gobgpapi.ListPeerRequest{EnableAdvertised: true}) @@ -144,7 +144,7 @@ func (gobgp *GoBGP) parsePathIntoRoute(path *gobgpapi.Path, prefix string) (erro } func (gobgp *GoBGP) GetRoutes(peer *gobgpapi.Peer, tableType gobgpapi.TableType, response *api.RoutesResponse) error { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(gobgp.config.ProcessingTimeout)) defer cancel() for _, family := range families { diff --git a/backend/sources/gobgp/source.go b/backend/sources/gobgp/source.go index 3291729..52d4d13 100644 --- a/backend/sources/gobgp/source.go +++ b/backend/sources/gobgp/source.go @@ -88,7 +88,7 @@ func (gobgp *GoBGP) ExpireCaches() int { } func (gobgp *GoBGP) NeighboursStatus() (*api.NeighboursStatusResponse, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(gobgp.config.ProcessingTimeout)) defer cancel() response := api.NeighboursStatusResponse{} @@ -123,7 +123,7 @@ func (gobgp *GoBGP) NeighboursStatus() (*api.NeighboursStatusResponse, error) { } func (gobgp *GoBGP) Status() (*api.StatusResponse, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(gobgp.config.ProcessingTimeout)) defer cancel() resp, err := gobgp.client.GetBgp(ctx, &gobgpapi.GetBgpRequest{}) @@ -138,7 +138,7 @@ func (gobgp *GoBGP) Status() (*api.StatusResponse, error) { } func (gobgp *GoBGP) Neighbours() (*api.NeighboursResponse, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(gobgp.config.ProcessingTimeout)) defer cancel() response := api.NeighboursResponse{} diff --git a/etc/alice-lg/alice.example.conf b/etc/alice-lg/alice.example.conf index 7e90231..a1ff2eb 100644 --- a/etc/alice-lg/alice.example.conf +++ b/etc/alice-lg/alice.example.conf @@ -139,7 +139,7 @@ routeserver.name = RS # Routeservers - +# Birdwatcher Example [source.rs0-example-v4] name = rs1.example.com (IPv4) # Optional: a group for the routeservers list @@ -158,7 +158,6 @@ neighbors_refresh_timeout = 2 # Optional: show_last_reboot = true - [source.rs1-example-v6] name = rs1.example.com (IPv6) [source.rs1-example-v6.birdwatcher] @@ -177,3 +176,20 @@ neighbors_refresh_timeout = 2 servertime = 2006-01-02T15:04:05Z07:00 servertime_short = 02.01.2006 servertime_ext = Mon, 02 Jan 2006 15:04:05 -0700 + + +# Routeservers +# GoBGP Example +[source.rs2-example] +name = rs2.example.com +group = AMS + +[source.rs2-example.gobgp] +# host is the IP (or DNS name) and port for the remote GoBGP daemon +host = rs2.example.com:50051 +# processing_timeout is a timeout in seconds configured per gRPC call to a given GoBGP daemon +processing_timeout = 300 +type = multi_table +peer_table_prefix = T +pipe_protocol_prefix = M +neighbors_refresh_timeout = 2 \ No newline at end of file diff --git a/go.mod b/go.mod index 7eabfa6..e1230fa 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,7 @@ module github.com/alice-lg/alice-lg +go 1.14 + require ( github.com/GeertJohan/go.rice v0.0.0-20181229193832-0af3f3b09a0a github.com/daaku/go.zipexe v0.0.0-20150329023125-a5fe2436ffcb // indirect