fixed tests, added bgp community enumeration
This commit is contained in:
parent
e0083b806f
commit
ab8698e112
195
pkg/backend/_testdata/alice.conf
Normal file
195
pkg/backend/_testdata/alice.conf
Normal file
@ -0,0 +1,195 @@
|
||||
# ======================================
|
||||
# Alice-LG configuration example
|
||||
# ======================================
|
||||
|
||||
[server]
|
||||
# configures the built-in webserver and provides global application settings
|
||||
listen_http = 127.0.0.1:7340
|
||||
# enable the prefix-lookup endpoint / the global search feature
|
||||
enable_prefix_lookup = true
|
||||
# Try to refresh the neighbor status on every request to /neighbors
|
||||
enable_neighbors_status_refresh = false
|
||||
asn = 9033
|
||||
# this ASN is used as a fallback value in the RPKI feature and for route
|
||||
# filtering evaluation with large BGP communities
|
||||
|
||||
[housekeeping]
|
||||
# Interval for the housekeeping routine in minutes
|
||||
interval = 5
|
||||
# Try to release memory via a forced GC/SCVG run on every housekeeping run
|
||||
force_release_memory = true
|
||||
|
||||
[theme]
|
||||
path = /path/to/my/alice/theme/files
|
||||
# Optional:
|
||||
url_base = /theme
|
||||
|
||||
[pagination]
|
||||
# Routes tables can be paginated, which comes in handy with
|
||||
# peers announcing a lot of routes. Set to 0 to disable
|
||||
# pagination.
|
||||
routes_filtered_page_size = 250
|
||||
routes_accepted_page_size = 250
|
||||
routes_not_exported_page_size = 250
|
||||
|
||||
[rejection_reasons]
|
||||
# a pair of a large BGP community value and a string to signal the processing
|
||||
# results of route filtering
|
||||
9033:65666:1 = An IP Bogon was detected
|
||||
9033:65666:2 = Prefix is longer than 64
|
||||
9033:65666:3 = Prefix is longer than 24
|
||||
9033:65666:4 = AS path contains a bogon AS
|
||||
9033:65666:5 = AS path length is longer than 64
|
||||
9033:65666:6 = First AS in path is not the same as the Peer AS
|
||||
9033:65666:7 = ECIX prefix hijack
|
||||
9033:65666:8 = Origin AS not found in IRRDB for Peer AS-SET
|
||||
9033:65666:9 = Prefix not found in IRRDB for Origin AS
|
||||
9033:65666:10 = Advertised nexthop address is not the same as the peer
|
||||
|
||||
23:42:1 = Some made up reason
|
||||
|
||||
#
|
||||
# Optional: Define communities which might be filtered
|
||||
# in the future.
|
||||
[rejection_candidates]
|
||||
communities = 6695:1102:14, 6695:1102:15, 23:42:46
|
||||
|
||||
[noexport]
|
||||
load_on_demand = true # Default: false
|
||||
|
||||
[noexport_reasons]
|
||||
# a pair of a large BGP community value and a string to signal the processing
|
||||
# results of route distribution and the distribution policy applied to a route
|
||||
9033:65667:1 = The target peer policy is Fairly-open and the sender ASN is an exception
|
||||
9033:65667:2 = The target peer policy is Selective and the sender ASN is no exception
|
||||
9033:65667:3 = The target peer policy is set to restrictive
|
||||
9033:65667:4 = The sender has specifically refused export to the target peer, either through sending 65000:AS, or through the portal
|
||||
9033:65667:5 = The sender has refused export to all peers and the target is no exception, either through sending 65000:0, or through the portal
|
||||
9033:65667:6 = The Sender has set (peerRTTHigherDeny:ms) and the targets RTT ms >= then the ms in the community
|
||||
9033:65667:7 = The Sender has set (peerRTTLowerDeny:ms) and the targets RTT ms <= then the ms in the community
|
||||
|
||||
23:46:1 = Some other made up reason
|
||||
|
||||
|
||||
[rpki]
|
||||
# shows rpki validation status in the client, based on the presence of a large
|
||||
# BGP community on the route
|
||||
enabled = true
|
||||
|
||||
# Optional, falling back to defaults as defined in:
|
||||
# https://www.euro-ix.net/en/forixps/large-bgp-communities/
|
||||
valid = 23042:1000:1
|
||||
unknown = 23042:1000:2
|
||||
# not_checked = 23042:1000:3
|
||||
invalid = 23042:1000:4-*
|
||||
|
||||
|
||||
# Define other known bgp communities
|
||||
[bgp_communities]
|
||||
1:23 = some tag
|
||||
9033:65666:1 = ip bogon detected
|
||||
# Wildcards are supported aswell:
|
||||
0:* = do not redistribute to AS$1
|
||||
|
||||
#
|
||||
# Define columns for neighbours and routes table,
|
||||
# with <key> = <Table Header>
|
||||
#
|
||||
# and <key> := <object.path> Implicitly referencing the object,
|
||||
# e.g. route.bgp.as_path -> bgp.as_path)
|
||||
# |= <Widget> A widget with special rendering features,
|
||||
# to which the object is applied. E.g.
|
||||
# Uptime, which will be rendered as
|
||||
# Uptime(neighbour).
|
||||
#
|
||||
# As per convention: Widgets are in Uppercase, object properties are
|
||||
# in lowercase.
|
||||
#
|
||||
# Available Widgets for Neighbours:
|
||||
#
|
||||
# Uptime Displays the relative uptime of this neighbour
|
||||
# Description The neighbour's description with link to routes page
|
||||
#
|
||||
|
||||
[neighbours_columns]
|
||||
address = Neighbour
|
||||
asn = ASN
|
||||
state = State
|
||||
Uptime = Uptime
|
||||
Description = Description
|
||||
routes_received = Routes Received
|
||||
routes_filtered = Filtered
|
||||
|
||||
|
||||
[routes_columns]
|
||||
network = Network
|
||||
gateway = Gateway
|
||||
interface = Interface
|
||||
metric = Metric
|
||||
bgp.as_path = AS Path
|
||||
|
||||
|
||||
[lookup_columns]
|
||||
network = Network
|
||||
gateway = Gateway
|
||||
neighbour.asn = ASN
|
||||
neighbour.description = Description
|
||||
bgp.as_path = AS Path
|
||||
routeserver.name = RS
|
||||
|
||||
|
||||
# Routeservers
|
||||
# Birdwatcher Example
|
||||
[source.rs0-example-v4]
|
||||
name = rs1.example.com (IPv4)
|
||||
# Optional: a group for the routeservers list
|
||||
group = FRA
|
||||
blackholes = 10.23.6.666, 10.23.6.665
|
||||
|
||||
[source.rs0-example-v4.birdwatcher]
|
||||
api = http://rs1.example.com:29184/
|
||||
# single_table / multi_table
|
||||
type = multi_table
|
||||
peer_table_prefix = T
|
||||
pipe_protocol_prefix = M
|
||||
# Timeout in seconds to wait for the status data (only required if enable_neighbors_status_refresh is true)
|
||||
neighbors_refresh_timeout = 2
|
||||
|
||||
# Optional:
|
||||
show_last_reboot = true
|
||||
|
||||
[source.rs1-example-v6]
|
||||
name = rs1.example.com (IPv6)
|
||||
[source.rs1-example-v6.birdwatcher]
|
||||
timezone = Europe/Brussels
|
||||
api = http://rs1.example.com:29186/
|
||||
# single_table / multi_table
|
||||
type = multi_table
|
||||
peer_table_prefix = T
|
||||
pipe_protocol_prefix = M
|
||||
# Timeout in seconds to wait for the status data (only required if enable_neighbors_status_refresh is true)
|
||||
neighbors_refresh_timeout = 2
|
||||
|
||||
# Optional: Examples for time format
|
||||
# Please see https://golang.org/pkg/time/#pkg-constants for an
|
||||
# explanation on how time parsing in go works.
|
||||
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
|
@ -11,7 +11,7 @@ func TestApiLogSourceError(t *testing.T) {
|
||||
conf := &Config{
|
||||
Sources: []*SourceConfig{
|
||||
&SourceConfig{
|
||||
Id: "rs1v4",
|
||||
ID: "rs1v4",
|
||||
Name: "rs1.example.net (IPv4)",
|
||||
},
|
||||
},
|
||||
|
@ -2,7 +2,10 @@ package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alice-lg/alice-lg/pkg/api"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -130,3 +133,44 @@ func (c BgpCommunities) Set(community string, label string) {
|
||||
slookup := lookup.(BgpCommunities)
|
||||
slookup[path[len(path)-1]] = label
|
||||
}
|
||||
|
||||
// APICommunities enumerates all bgp communities into
|
||||
// a set of api.Communities.
|
||||
// CAVEAT: Wildcards are substituted by 0 and ** ARE NOT ** expanded.
|
||||
func (c BgpCommunities) APICommunities() api.Communities {
|
||||
communities := api.Communities{}
|
||||
// We could do this recursive, or assume that
|
||||
// the max depth is 3.
|
||||
for uVal, c1 := range c {
|
||||
u, err := strconv.Atoi(uVal)
|
||||
if err != nil {
|
||||
u = 0
|
||||
}
|
||||
for vVal, c2 := range c1.(BgpCommunities) {
|
||||
v, err := strconv.Atoi(vVal)
|
||||
if err != nil {
|
||||
v = 0
|
||||
}
|
||||
|
||||
com2, ok := c2.(BgpCommunities)
|
||||
if !ok {
|
||||
// we only have labels here
|
||||
communities = append(
|
||||
communities, api.Community{u, v})
|
||||
continue
|
||||
}
|
||||
|
||||
for wVal := range com2 {
|
||||
w, err := strconv.Atoi(wVal)
|
||||
if err != nil {
|
||||
w = 0
|
||||
}
|
||||
|
||||
communities = append(
|
||||
communities, api.Community{u, v, w})
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return communities
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ package backend
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alice-lg/alice-lg/backend/sources/birdwatcher"
|
||||
"github.com/alice-lg/alice-lg/backend/sources/gobgp"
|
||||
"github.com/alice-lg/alice-lg/pkg/sources/birdwatcher"
|
||||
"github.com/alice-lg/alice-lg/pkg/sources/gobgp"
|
||||
)
|
||||
|
||||
// Test configuration loading and parsing
|
||||
@ -12,25 +12,25 @@ import (
|
||||
|
||||
func TestLoadConfigs(t *testing.T) {
|
||||
|
||||
config, err := loadConfig("../etc/alice-lg/alice.example.conf")
|
||||
config, err := loadConfig("_testdata/alice.conf")
|
||||
if err != nil {
|
||||
t.Error("Could not load test config:", err)
|
||||
t.Fatal("Could not load test config:", err)
|
||||
}
|
||||
|
||||
if config.Server.Listen == "" {
|
||||
t.Error("Listen string not present.")
|
||||
}
|
||||
|
||||
if len(config.Ui.RoutesColumns) == 0 {
|
||||
if len(config.UI.RoutesColumns) == 0 {
|
||||
t.Error("Route columns settings missing")
|
||||
}
|
||||
|
||||
if len(config.Ui.RoutesRejections.Reasons) == 0 {
|
||||
if len(config.UI.RoutesRejections.Reasons) == 0 {
|
||||
t.Error("Rejection reasons missing")
|
||||
}
|
||||
|
||||
// Check communities
|
||||
label, err := config.Ui.BgpCommunities.Lookup("1:23")
|
||||
label, err := config.UI.BgpCommunities.Lookup("1:23")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@ -43,10 +43,9 @@ 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")
|
||||
config, err := loadConfig("_testdata/alice.conf")
|
||||
if err != nil {
|
||||
t.Error("Could not load test config:", err)
|
||||
t.Fatal("Could not load test config:", err)
|
||||
}
|
||||
|
||||
// Get sources
|
||||
@ -77,10 +76,9 @@ func TestSourceConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSourceConfigDefaultsOverride(t *testing.T) {
|
||||
|
||||
config, err := loadConfig("../etc/alice-lg/alice.example.conf")
|
||||
config, err := loadConfig("_testdata/alice.conf")
|
||||
if err != nil {
|
||||
t.Error("Could not load test config:", err)
|
||||
t.Fatal("Could not load test config:", err)
|
||||
}
|
||||
|
||||
// Get sources
|
||||
@ -117,13 +115,13 @@ func TestSourceConfigDefaultsOverride(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRejectAndNoexportReasons(t *testing.T) {
|
||||
config, err := loadConfig("../etc/alice-lg/alice.example.conf")
|
||||
config, err := loadConfig("_testdata/alice.conf")
|
||||
if err != nil {
|
||||
t.Error("Could not load test config:", err)
|
||||
t.Fatal("Could not load test config:", err)
|
||||
}
|
||||
|
||||
// Rejection reasons
|
||||
description, err := config.Ui.RoutesRejections.Reasons.Lookup("23:42:1")
|
||||
description, err := config.UI.RoutesRejections.Reasons.Lookup("23:42:1")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@ -133,7 +131,7 @@ func TestRejectAndNoexportReasons(t *testing.T) {
|
||||
}
|
||||
|
||||
// Noexport reasons
|
||||
description, err = config.Ui.RoutesNoexports.Reasons.Lookup("23:46:1")
|
||||
description, err = config.UI.RoutesNoexports.Reasons.Lookup("23:46:1")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@ -144,9 +142,9 @@ func TestRejectAndNoexportReasons(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBlackholeParsing(t *testing.T) {
|
||||
config, err := loadConfig("../etc/alice-lg/alice.example.conf")
|
||||
config, err := loadConfig("_testdata/alice.conf")
|
||||
if err != nil {
|
||||
t.Error("Could not load test config:", err)
|
||||
t.Fatal("Could not load test config:", err)
|
||||
}
|
||||
|
||||
// Get first source
|
||||
@ -163,9 +161,9 @@ func TestBlackholeParsing(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOwnASN(t *testing.T) {
|
||||
config, err := loadConfig("../etc/alice-lg/alice.example.conf")
|
||||
config, err := loadConfig("_testdata/alice.conf")
|
||||
if err != nil {
|
||||
t.Error("Could not load test config:", err)
|
||||
t.Fatal("Could not load test config:", err)
|
||||
}
|
||||
|
||||
if config.Server.Asn != 9033 {
|
||||
@ -174,45 +172,43 @@ func TestOwnASN(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRpkiConfig(t *testing.T) {
|
||||
config, err := loadConfig("../etc/alice-lg/alice.example.conf")
|
||||
config, err := loadConfig("_testdata/alice.conf")
|
||||
if err != nil {
|
||||
t.Error("Could not load test config:", err)
|
||||
t.Fatal("Could not load test config:", err)
|
||||
}
|
||||
|
||||
if len(config.Ui.Rpki.Valid) != 3 {
|
||||
t.Error("Unexpected RPKI:VALID,", config.Ui.Rpki.Valid)
|
||||
if len(config.UI.Rpki.Valid) != 3 {
|
||||
t.Error("Unexpected RPKI:VALID,", config.UI.Rpki.Valid)
|
||||
}
|
||||
if len(config.Ui.Rpki.Invalid) != 4 {
|
||||
t.Error("Unexpected RPKI:INVALID,", config.Ui.Rpki.Invalid)
|
||||
return // We would fail hard later
|
||||
if len(config.UI.Rpki.Invalid) != 4 {
|
||||
t.Fatal("Unexpected RPKI:INVALID,", config.UI.Rpki.Invalid)
|
||||
}
|
||||
|
||||
// Check fallback
|
||||
if config.Ui.Rpki.NotChecked[0] != "9033" {
|
||||
if config.UI.Rpki.NotChecked[0] != "9033" {
|
||||
t.Error(
|
||||
"Expected NotChecked to fall back to defaults, got:",
|
||||
config.Ui.Rpki.NotChecked,
|
||||
config.UI.Rpki.NotChecked,
|
||||
)
|
||||
}
|
||||
|
||||
// Check range postprocessing
|
||||
if config.Ui.Rpki.Invalid[3] != "*" {
|
||||
if config.UI.Rpki.Invalid[3] != "*" {
|
||||
t.Error("Missing wildcard from config")
|
||||
}
|
||||
|
||||
t.Log(config.Ui.Rpki)
|
||||
t.Log(config.UI.Rpki)
|
||||
}
|
||||
|
||||
func TestRejectCandidatesConfig(t *testing.T) {
|
||||
config, err := loadConfig("../etc/alice-lg/alice.example.conf")
|
||||
config, err := loadConfig("_testdata/alice.conf")
|
||||
if err != nil {
|
||||
t.Error("Could not load test config:", err)
|
||||
return
|
||||
t.Fatal("Could not load test config:", err)
|
||||
}
|
||||
|
||||
t.Log(config.Ui.RoutesRejectCandidates.Communities)
|
||||
t.Log(config.UI.RoutesRejectCandidates.Communities)
|
||||
|
||||
description, err := config.Ui.RoutesRejectCandidates.Communities.Lookup("23:42:46")
|
||||
description, err := config.UI.RoutesRejectCandidates.Communities.Lookup("23:42:46")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
@ -79,12 +79,12 @@ func makeTestRoutesStore() *RoutesStore {
|
||||
|
||||
configMap := map[string]*SourceConfig{
|
||||
"rs1": &SourceConfig{
|
||||
Id: "rs1",
|
||||
ID: "rs1",
|
||||
Name: "rs1.test",
|
||||
Type: SOURCE_BIRDWATCHER,
|
||||
Type: SourceTypeBird,
|
||||
|
||||
Birdwatcher: birdwatcher.Config{
|
||||
Api: "http://localhost:2342",
|
||||
API: "http://localhost:2342",
|
||||
Timezone: "UTC",
|
||||
ServerTime: "2006-01-02T15:04:05",
|
||||
ServerTimeShort: "2006-01-02",
|
||||
|
Loading…
x
Reference in New Issue
Block a user