added blackhole community config parsing

This commit is contained in:
Annika Hannig 2022-11-24 16:12:13 +01:00
parent 27a6b9806d
commit b5b5148dec
5 changed files with 185 additions and 36 deletions

View File

@ -174,18 +174,32 @@ func (c BGPCommunityMap) Communities() Communities {
return communities
}
// BGPCommunityRange describes a range within a bgp community:
// It is a tuple with two values, the start and end.
type BGPCommunityRange []int
// BGPCommunity types: Standard, Extended and Large
const (
BGPCommunityTypeStd = iota
BGPCommunityTypeExt
BGPCommunityTypeLarge
)
// RangedBGPCommunity is a list of BGPCommunity ranges,
// with 3 ranges in large communities.
type RangedBGPCommunity []interface{}
// Type classifies the BGP Ranged BGP Community into: std, large, ext
func (c RangedBGPCommunity) Type() int {
if len(c) == 2 {
return BGPCommunityTypeStd
}
if _, ok := c[0].([]string); ok {
return BGPCommunityTypeExt
}
return BGPCommunityTypeLarge
}
// A BGPCommunitiesSet is a set of communities, large and extended.
// The communities are described as ranges.
type BGPCommunitiesSet struct {
Communities []RangedBGPCommunity `json:"communities"`
ExtCommunities []RangedBGPCommunity `json:"ext_communities"`
LargeCommunities []RangedBGPCommunity `json:"large_communities"`
Communities []RangedBGPCommunity `json:"standard"`
ExtCommunities []RangedBGPCommunity `json:"extended"`
LargeCommunities []RangedBGPCommunity `json:"large"`
}

View File

@ -0,0 +1,121 @@
package config
import (
"fmt"
"log"
"strconv"
"strings"
"github.com/alice-lg/alice-lg/pkg/api"
"github.com/alice-lg/alice-lg/pkg/decoders"
)
// ErrInvalidCommunity creates an invalid community error
func ErrInvalidCommunity(s string) error {
return fmt.Errorf("invalid community: %s", s)
}
// Helper parse communities from a section body
func parseAndMergeCommunities(
communities api.BGPCommunityMap, body string,
) api.BGPCommunityMap {
// Parse and merge communities
lines := strings.Split(body, "\n")
for _, line := range lines {
kv := strings.SplitN(line, "=", 2)
if len(kv) != 2 {
log.Println("Skipping malformed BGP community:", line)
continue
}
community := strings.TrimSpace(kv[0])
label := strings.TrimSpace(kv[1])
communities.Set(community, label)
}
return communities
}
// Parse a communities set with ranged communities
func parseRangeCommunitiesSet(body string) (*api.BGPCommunitiesSet, error) {
comms := []api.RangedBGPCommunity{}
large := []api.RangedBGPCommunity{}
ext := []api.RangedBGPCommunity{}
lines := strings.Split(body, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue // Empty
}
if strings.HasPrefix(line, "#") {
continue // Comment
}
comm, err := parseRangeCommunity(line)
if err != nil {
return nil, err
}
switch comm.Type() {
case api.BGPCommunityTypeStd:
comms = append(comms, comm)
case api.BGPCommunityTypeLarge:
large = append(large, comm)
case api.BGPCommunityTypeExt:
ext = append(ext, comm)
}
}
set := &api.BGPCommunitiesSet{
Communities: comms,
LargeCommunities: large,
ExtCommunities: ext,
}
return set, nil
}
func parseRangeCommunity(s string) (api.RangedBGPCommunity, error) {
tokens := strings.Split(s, ":")
if len(tokens) < 2 {
return nil, ErrInvalidCommunity(s)
}
// Extract ranges and make uniform structure
parts := make([][]string, 0, len(tokens))
for _, t := range tokens {
values := strings.SplitN(t, "-", 2)
if len(values) == 0 {
return nil, ErrInvalidCommunity(s)
}
if len(values) == 1 {
parts = append(parts, []string{values[0], values[0]})
} else {
parts = append(parts, []string{values[0], values[1]})
}
}
if len(parts) <= 1 {
return nil, ErrInvalidCommunity(s)
}
// Check if this might be an ext community
isExt := false
if _, err := strconv.Atoi(parts[0][0]); err != nil {
isExt = true // At least it looks like...
}
if isExt && len(parts) != 3 {
return nil, ErrInvalidCommunity(s)
}
if isExt {
return api.RangedBGPCommunity{
[]string{parts[0][0], parts[0][0]},
decoders.IntListFromStrings(parts[1]),
decoders.IntListFromStrings(parts[2]),
}, nil
}
comm := api.RangedBGPCommunity{}
for _, p := range parts {
comm = append(comm, decoders.IntListFromStrings(p))
}
return comm, nil
}

View File

@ -388,28 +388,6 @@ func getLookupColumns(config *ini.File) (
return columns, order, nil
}
// Helper parse communities from a section body
func parseAndMergeCommunities(
communities api.BGPCommunityMap, body string,
) api.BGPCommunityMap {
// Parse and merge communities
lines := strings.Split(body, "\n")
for _, line := range lines {
kv := strings.SplitN(line, "=", 2)
if len(kv) != 2 {
log.Println("Skipping malformed BGP community:", line)
continue
}
community := strings.TrimSpace(kv[0])
label := strings.TrimSpace(kv[1])
communities.Set(community, label)
}
return communities
}
// Get UI config: BGP Communities
func getBGPCommunityMap(config *ini.File) api.BGPCommunityMap {
// Load defaults
@ -460,6 +438,19 @@ func getRoutesNoexports(config *ini.File) (NoexportsConfig, error) {
return noexportsConfig, nil
}
// Get UI config: blackhole communities
func getBlackholeCommunities(config *ini.File) (api.BGPCommunitiesSet, error) {
section := config.Section("blackhole_communities")
if section == nil {
return api.BGPCommunitiesSet{}, nil
}
set, err := parseRangeCommunitiesSet(section.Body())
if err != nil {
return api.BGPCommunitiesSet{}, err
}
return *set, nil
}
// Get UI config: Reject candidates
func getRejectCandidatesConfig(config *ini.File) (RejectCandidatesConfig, error) {
candidateCommunities := config.Section(
@ -619,6 +610,12 @@ func getUIConfig(config *ini.File) (UIConfig, error) {
return uiConfig, err
}
// Blackhole communities
blackholeCommunities, err := getBlackholeCommunities(config)
if err != nil {
return uiConfig, err
}
// Theme configuration: Theming is optional, if no settings
// are found, it will be ignored
themeConfig := getThemeConfig(config)
@ -641,8 +638,9 @@ func getUIConfig(config *ini.File) (UIConfig, error) {
RoutesNoexports: noexports,
RoutesRejectCandidates: rejectCandidates,
BGPCommunities: getBGPCommunityMap(config),
Rpki: rpki,
BGPBlackholeCommunities: blackholeCommunities,
BGPCommunities: getBGPCommunityMap(config),
Rpki: rpki,
Theme: themeConfig,
@ -831,6 +829,7 @@ func LoadConfig(file string) (*Config, error) {
parsedConfig, err := ini.LoadSources(ini.LoadOptions{
UnparseableSections: []string{
"bgp_communities",
"blackhole_communities",
"rejection_reasons",
"noexport_reasons",
},

View File

@ -227,7 +227,7 @@ func TestRejectCandidatesConfig(t *testing.T) {
func TestDefaultHTTPTimeout(t *testing.T) {
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.HTTPTimeout != DefaultHTTPTimeout {
@ -249,3 +249,19 @@ func TestPostgresStoreConfig(t *testing.T) {
}
t.Log(config.Postgres)
}
func TestGetBlackholeCommunities(t *testing.T) {
config, _ := LoadConfig("testdata/alice.conf")
comms := config.UI.BGPBlackholeCommunities
if comms.Communities[0][0].([]int)[0] != 1337 {
t.Error("unexpected community:", comms.Communities[0])
}
if len(comms.ExtCommunities) != 1 {
t.Error("unexpected communities:", comms.ExtCommunities)
}
if len(comms.LargeCommunities) != 1 {
t.Error("unexpected communities:", comms.LargeCommunities)
}
t.Log(comms)
}

View File

@ -88,6 +88,10 @@ load_on_demand = true # Default: false
23:46:1 = Some other made up reason
[blackhole_communities]
1337:666
rt:1324:4200000000-4200010000
2342:65530-65535:665-667
[rpki]
# shows rpki validation status in the client, based on the presence of a large
@ -129,11 +133,6 @@ invalid = 23042:1000:4-*
# Description The neighbour's description with link to routes page
#
#
[blackhole_communities]
65535:666
12345:1105-1189:*
12345:1111:10-90
rt:1234:4200000000-4200010000
[neighbours_columns]
address = Neighbour