From 5d76bde102ad58082f7a1e6eeeb84b866589bcc2 Mon Sep 17 00:00:00 2001 From: Kioubit Date: Tue, 16 Jul 2024 19:31:26 +0300 Subject: [PATCH] Simplify --- Makefile | 11 +- bgp/common/other.go | 5 + bgp/main.go | 7 -- bgp/update/update.go | 11 +- main.go | 112 ++++++------------ .../httpAPI/dashboard/assets/js/dashboard.js | 36 ++++-- modules/httpAPI/dashboard/index.html | 2 +- modules/httpAPI/mod_httpAPI.go | 2 +- modules/httpAPI/statstream.go | 2 +- monitor/analyze.go | 15 +-- 10 files changed, 87 insertions(+), 116 deletions(-) create mode 100644 bgp/common/other.go diff --git a/Makefile b/Makefile index 62b9db2..76efab5 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,20 @@ # Makefile for FlapAlerted BINARY=FlapAlerted -MODULES=mod_httpAPI VERSION=`git describe --tags` LDFLAGS=-ldflags "-X main.Version=${VERSION} -s -w" BUILDFLAGS=-trimpath build: - go build -tags=${MODULES} ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY} . + go build ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY} . release: - CGO_ENABLED=0 go build -tags=${MODULES} ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY} . + CGO_ENABLED=0 go build ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY} . release-all: - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags=${MODULES} ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY}_${VERSION}_linux_amd64 - CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -tags=${MODULES} ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY}_${VERSION}_linux_arm64 - CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -tags=${MODULES} ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY}_${VERSION}_linux_arm + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY}_${VERSION}_linux_amd64 + CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY}_${VERSION}_linux_arm64 + CGO_ENABLED=0 GOOS=linux GOARCH=arm go build ${BUILDFLAGS} ${LDFLAGS} -o bin/${BINARY}_${VERSION}_linux_arm clean: if [ -d "bin/" ]; then find bin/ -type f -delete ;fi diff --git a/bgp/common/other.go b/bgp/common/other.go new file mode 100644 index 0000000..c073bd9 --- /dev/null +++ b/bgp/common/other.go @@ -0,0 +1,5 @@ +package common + +type AsPathList struct { + Asn []uint32 +} diff --git a/bgp/main.go b/bgp/main.go index 35c86c8..6ff357f 100644 --- a/bgp/main.go +++ b/bgp/main.go @@ -7,7 +7,6 @@ import ( "log/slog" "net" "net/netip" - "os" ) // RFCs implemented @@ -31,12 +30,6 @@ func StartBGP(updateChannel chan update.Msg) { slog.Error("Failed to accept TCP connection", "error", err.Error()) } - if config.GlobalConf.Debug { - slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))) - } else { - slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{}))) - } - logger := slog.With("remote", conn.RemoteAddr()) logger.Info("New connection") diff --git a/bgp/update/update.go b/bgp/update/update.go index e99af60..d30d787 100644 --- a/bgp/update/update.go +++ b/bgp/update/update.go @@ -1,6 +1,7 @@ package update import ( + "FlapAlerted/bgp/common" "bytes" "encoding/binary" "errors" @@ -282,12 +283,8 @@ func (u Msg) GetMpUnReachNLRI() (MPUnReachNLRI, bool, error) { return MPUnReachNLRI{}, false, nil } -type AsPathList struct { - Asn []uint32 -} - -func (u Msg) GetAsPaths() ([]AsPathList, error) { - paths := make([]AsPathList, 0) +func (u Msg) GetAsPaths() ([]common.AsPathList, error) { + paths := make([]common.AsPathList, 0) for _, a := range u.PathAttributes { if a.TypeCode == AsPathAttr { attribute, err := a.GetAttribute() @@ -295,7 +292,7 @@ func (u Msg) GetAsPaths() ([]AsPathList, error) { return nil, err } if attribute.(asPathAttribute).PathSegmentType == AsSequence { - paths = append(paths, AsPathList{attribute.(asPathAttribute).Value}) + paths = append(paths, common.AsPathList{Asn: attribute.(asPathAttribute).Value}) } } } diff --git a/main.go b/main.go index 414d45e..4921356 100644 --- a/main.go +++ b/main.go @@ -4,58 +4,37 @@ import ( "FlapAlerted/config" _ "FlapAlerted/modules" "FlapAlerted/monitor" + "flag" "fmt" - "log" + "log/slog" "os" - "reflect" - "strconv" - "strings" "time" ) var Version = "3.8" func main() { - fmt.Println("Version", Version) + fmt.Println("FlapAlerted version", Version) monitor.SetVersion(Version) - conf := &config.UserConfig{} - v := reflect.Indirect(reflect.ValueOf(conf)) - for i := 0; i < v.NumField(); i++ { - if len(os.Args) != v.NumField()+1 { - showUsage("invalid number of commandline arguments") - } - field := v.Field(i) - fieldName := v.Type().Field(i).Name - fieldTag := v.Type().Field(i).Tag - overloadStringTag := fieldTag.Get("overloadString") - switch field.Kind() { - case reflect.Int64: - input, err := strconv.Atoi(os.Args[i+1]) - if err != nil { - if overloadStringTag == "true" { - if os.Args[i+1] != "auto" { - showUsage(fmt.Sprintf("The value entered for %s is not a number or 'auto'", fieldName)) - } - input = -1 - } else { - showUsage(fmt.Sprintf("The value entered for %s is not a number", fieldName)) - } - } - if !field.OverflowInt(int64(input)) { - field.SetInt(int64(input)) - } else { - showUsage(fmt.Sprintf("The value entered for %s is too high", fieldName)) - } - case reflect.Bool: - if !checkIsInputBool(os.Args[i+1]) { - showUsage(fmt.Sprintf("The value entered for %s must be either 'true' or 'false'", fieldName)) - } - input := os.Args[i+1] == "true" - field.SetBool(input) - } - } + routeChangeCounter := flag.Int("routeChangeCounter", 0, "Number of times a route path needs to change to list a prefix") + flapPeriod := flag.Int("period", 60, "Interval in seconds within which the routeChangeCounter value is evaluated") + asn := flag.Int("asn", 0, "Your ASN number") + noPathInfo := flag.Bool("noPathInfo", false, "Disable keeping path information. (Only disable if performance is a concern)") + disableAddPath := flag.Bool("disableAddPath", false, "Disable BGP AddPath support. (Setting must be replicated in BGP daemon)") + relevantAsnPosition := flag.Int("", -1, "The position of the last static ASN (and for which to keep separate state for)"+ + " in each path. If AddPath support has been enabled this value is '1', otherwise it is '0'. For special cases like route collectors the value may differ.") + enableDebug := flag.Bool("debug", false, "Enable debug mode (produces a lot of output)") + flag.Parse() + + conf := config.UserConfig{} + conf.RouteChangeCounter = int64(*routeChangeCounter) + conf.FlapPeriod = int64(*flapPeriod) + conf.Asn = int64(*asn) + conf.KeepPathInfo = !*noPathInfo + conf.UseAddPath = !*disableAddPath + conf.RelevantAsnPosition = int64(*relevantAsnPosition) if conf.RelevantAsnPosition == -1 { if conf.UseAddPath { conf.RelevantAsnPosition = 1 @@ -63,54 +42,31 @@ func main() { conf.RelevantAsnPosition = 0 } } + conf.Debug = *enableDebug + + if conf.Asn == 0 { + fmt.Println("ASN value not specified") + os.Exit(1) + } - empty := true modules := monitor.GetRegisteredModules() for _, m := range modules { fmt.Println("Enabled module:", m.Name) - empty = false - } - if empty { - fmt.Println("Error: No modules enabled during compilation!") - fmt.Printf("It is recommended to use the included Makefile") - os.Exit(1) } if conf.Debug { - fmt.Println("WARNING: Debug mode has been activated which will generate a lot of output") - fmt.Println("Waiting for 10 seconds...") - time.Sleep(10 * time.Second) + fmt.Println("Debug mode has been activated which will generate a lot of output") + fmt.Println("Waiting for 4 seconds...") + time.Sleep(4 * time.Second) + slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true}))) + } else { + slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{}))) } fmt.Println("Using the following parameters:") fmt.Println("Detecting a flap if the route to a prefix changes within", conf.FlapPeriod, "seconds at least", conf.RouteChangeCounter, "time(s)") fmt.Println("ASN:", conf.Asn, "| Keep Path Info:", conf.KeepPathInfo, "| AddPath Capability:", conf.UseAddPath, "| Relevant ASN Position:", conf.RelevantAsnPosition) - log.Println("Started") - monitor.StartMonitoring(*conf) -} - -func checkIsInputBool(input string) bool { - if input == "true" || input == "false" { - return true - } - return false -} - -func getArguments() []string { - v := reflect.ValueOf(config.UserConfig{}) - args := make([]string, v.NumField()) - for i := 0; i < v.NumField(); i++ { - args[i] = v.Type().Field(i).Name - } - return args -} - -func showUsage(reason string) { - if reason != "" { - fmt.Println("Error:", reason) - } - fmt.Println("Usage:", os.Args[0], "<"+strings.Join(getArguments(), `> <`)+`>`) - fmt.Println("Refer to the documentation for the meaning of those arguments") - os.Exit(1) + slog.Info("Started") + monitor.StartMonitoring(conf) } diff --git a/modules/httpAPI/dashboard/assets/js/dashboard.js b/modules/httpAPI/dashboard/assets/js/dashboard.js index 0b691c9..ecefe14 100644 --- a/modules/httpAPI/dashboard/assets/js/dashboard.js +++ b/modules/httpAPI/dashboard/assets/js/dashboard.js @@ -141,16 +141,20 @@ function addToChart(liveChart, point, unixTime) { } -updateInfo(); -setInterval(updateInfo, 5000); -updateCapabilities(); -getStats(); -document.getElementById("loadingScreen").style.display = 'none'; + +window.onload = () => { + updateCapabilities(); + updateInfo(); + setInterval(updateInfo, 5000); + getStats(); + document.getElementById("loadingScreen").style.display = 'none'; +} + function updateInfo() { fetch("flaps/active/compact").then(function (response) { return response.json(); }).then(function (flapList) { - document.getElementById('connectionLost').style.display = 'none'; + displayConnectionLost(false, "updateInfo"); flapList.sort(function compareFn(a, b) { if (a.TotalCount > b.TotalCount) { return -1; @@ -175,7 +179,7 @@ function updateInfo() { document.getElementById("prefixTableBox").innerHTML = prefixTableHtml; }).catch(function (error) { - document.getElementById('connectionLost').style.display = 'block'; + displayConnectionLost(true, "updateInfo"); console.log(error); }); } @@ -185,7 +189,6 @@ function getStats() { const avgArray = []; evtSource.addEventListener("u", (event) => { try { - console.log(event.data) const js = JSON.parse(event.data) addToChart(liveRouteChart, js["Changes"], js["Time"]); @@ -204,9 +207,11 @@ function getStats() { } }); evtSource.onerror = (err) => { + displayConnectionLost(true, "getStats"); console.log(err) }; evtSource.onopen = () => { + displayConnectionLost(false, "getStats"); }; } @@ -214,4 +219,19 @@ function toTimeElapsed(seconds) { let date = new Date(null); date.setSeconds(seconds); return date.toISOString().slice(11, 19); +} + +let lostType = []; +function displayConnectionLost(lost, type) { + if (lost) { + if (lostType.indexOf(type) === -1) { + lostType.push(type) + } + document.getElementById('connectionLost').style.display = 'block'; + } else { + lostType = lostType.filter(e => e !== type); + if (lostType.length === 0) { + document.getElementById('connectionLost').style.display = 'none'; + } + } } \ No newline at end of file diff --git a/modules/httpAPI/dashboard/index.html b/modules/httpAPI/dashboard/index.html index cecc20e..7c52572 100644 --- a/modules/httpAPI/dashboard/index.html +++ b/modules/httpAPI/dashboard/index.html @@ -4,7 +4,7 @@ FlapAlerted + content="default-src 'none'; base-uri 'none'; form-action 'none'; style-src 'self'; style-src-elem 'self'; script-src 'self'; connect-src 'self'; img-src 'self';"> diff --git a/modules/httpAPI/mod_httpAPI.go b/modules/httpAPI/mod_httpAPI.go index d0daafc..ba8f0ba 100644 --- a/modules/httpAPI/mod_httpAPI.go +++ b/modules/httpAPI/mod_httpAPI.go @@ -1,4 +1,4 @@ -//go:build mod_httpAPI +//go:build !no_httpAPI package httpAPI diff --git a/modules/httpAPI/statstream.go b/modules/httpAPI/statstream.go index 3d90c2d..0d36dca 100644 --- a/modules/httpAPI/statstream.go +++ b/modules/httpAPI/statstream.go @@ -1,4 +1,4 @@ -//go:build mod_httpAPI +//go:build !no_httpAPI package httpAPI diff --git a/monitor/analyze.go b/monitor/analyze.go index e01c1ac..4d1a810 100644 --- a/monitor/analyze.go +++ b/monitor/analyze.go @@ -2,6 +2,7 @@ package monitor import ( "FlapAlerted/bgp" + "FlapAlerted/bgp/common" "FlapAlerted/bgp/update" "FlapAlerted/config" "encoding/json" @@ -21,7 +22,7 @@ const PathLimit = 1000 type Flap struct { sync.RWMutex Cidr string - LastPath map[string]update.AsPathList + LastPath map[string]common.AsPathList Paths map[string]*PathInfo PathChangeCount uint64 PathChangeCountTotal uint64 @@ -30,7 +31,7 @@ type Flap struct { } type PathInfo struct { - Path update.AsPathList + Path common.AsPathList Count uint64 } @@ -133,7 +134,7 @@ func cleanUpFlapList() { } } -func updateList(prefix netip.Prefix, asPath []update.AsPathList) { +func updateList(prefix netip.Prefix, asPath []common.AsPathList) { cidr := prefix.String() if len(asPath) == 0 { return @@ -149,7 +150,7 @@ func updateList(prefix netip.Prefix, asPath []update.AsPathList) { FirstSeen: currentTime, PathChangeCount: 0, PathChangeCountTotal: 0, - LastPath: make(map[string]update.AsPathList), + LastPath: make(map[string]common.AsPathList), Paths: make(map[string]*PathInfo), } if config.GlobalConf.KeepPathInfo { @@ -215,7 +216,7 @@ func updateList(prefix netip.Prefix, asPath []update.AsPathList) { } } -func getRelevantASN(asPath update.AsPathList) string { +func getRelevantASN(asPath common.AsPathList) string { pathLen := len(asPath.Asn) if pathLen < int(config.GlobalConf.RelevantAsnPosition) || config.GlobalConf.RelevantAsnPosition == 0 { @@ -229,7 +230,7 @@ func getRelevantASN(asPath update.AsPathList) string { return string(b) } -func pathToString(asPath update.AsPathList) string { +func pathToString(asPath common.AsPathList) string { b := make([]byte, 0, len(asPath.Asn)*11) for i := range asPath.Asn { b = strconv.AppendInt(b, int64(asPath.Asn[i]), 10) @@ -238,7 +239,7 @@ func pathToString(asPath update.AsPathList) string { return string(b) } -func pathsEqual(path1, path2 update.AsPathList) bool { +func pathsEqual(path1, path2 common.AsPathList) bool { if len(path1.Asn) != len(path2.Asn) { return false }