fixed tests, added bgp community enumeration

This commit is contained in:
Annika Hannig 2021-09-15 15:44:08 +02:00
parent e0083b806f
commit ab8698e112
No known key found for this signature in database
GPG Key ID: 62E226E47DDCE58D
5 changed files with 276 additions and 41 deletions

View 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

View File

@ -11,7 +11,7 @@ func TestApiLogSourceError(t *testing.T) {
conf := &Config{
Sources: []*SourceConfig{
&SourceConfig{
Id: "rs1v4",
ID: "rs1v4",
Name: "rs1.example.net (IPv4)",
},
},

View File

@ -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
}

View File

@ -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)
}

View File

@ -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",