From 27fe2969d78668ba826eaa08a5f329dab06e5a95 Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Wed, 19 Sep 2018 11:21:02 +0200 Subject: [PATCH 01/12] Avoid unnecessary cache writes in /status Introduce check to avoid unneccesarry cache writes. --- bird/bird.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bird/bird.go b/bird/bird.go index 867fe99..536b2f8 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -132,10 +132,15 @@ func RunAndParse(cmd string, parser func(io.Reader) Parsed) (Parsed, bool) { } func Status() (Parsed, bool) { - birdStatus, ok := RunAndParse("status", parseStatus) + birdStatus, from_cache := RunAndParse("status", parseStatus) if isSpecial(birdStatus) { - return birdStatus, ok + return birdStatus, from_cache } + + if from_cache { + return birdStatus, from_cache + } + status := birdStatus["status"].(Parsed) // Last Reconfig Timestamp source: @@ -164,7 +169,7 @@ func Status() (Parsed, bool) { birdStatus["status"] = status - return birdStatus, ok + return birdStatus, from_cache } func Protocols() (Parsed, bool) { From 4ffebab2c4598f868f1448b562b143a8c475ef5f Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Wed, 19 Sep 2018 11:21:35 +0200 Subject: [PATCH 02/12] Fixed race condition between main() and InstallRateLimitReset(). --- birdwatcher.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/birdwatcher.go b/birdwatcher.go index bb6d7ca..8f4de3f 100644 --- a/birdwatcher.go +++ b/birdwatcher.go @@ -156,7 +156,9 @@ func main() { // Configuration bird.ClientConf = birdConf bird.StatusConf = conf.Status + bird.RateLimitConf.Lock() bird.RateLimitConf.Conf = conf.Ratelimit + bird.RateLimitConf.Unlock() bird.ParserConf = conf.Parser endpoints.Conf = conf.Server From b8b9b5d021b5cd561c6c52ecabb4b6875396edfc Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Wed, 19 Sep 2018 12:36:12 +0200 Subject: [PATCH 03/12] Fixed /routes/count/table Endpoint which was returning routes instead of a route count because the wrong method was called. --- endpoints/routes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/endpoints/routes.go b/endpoints/routes.go index 5aadcb5..a7c8c79 100644 --- a/endpoints/routes.go +++ b/endpoints/routes.go @@ -59,7 +59,7 @@ func ProtoCount(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { } func TableCount(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { - return bird.RoutesTable(ps.ByName("table")) + return bird.RoutesTableCount(ps.ByName("table")) } func RouteNet(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { From 439ee0d5d081c2ddc3d4ef8da3382b8a55c4e928 Mon Sep 17 00:00:00 2001 From: Johannes Moos Date: Wed, 26 Sep 2018 16:28:01 +0200 Subject: [PATCH 04/12] Fix RoutesPrefixed This version ignored the prefix which is passed as an URL parameter. --- bird/bird.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bird/bird.go b/bird/bird.go index 536b2f8..216c301 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -200,7 +200,7 @@ func Symbols() (Parsed, bool) { } func RoutesPrefixed(prefix string) (Parsed, bool) { - cmd := routeQueryForChannel("route all") + cmd := routeQueryForChannel("route " + prefix + " all") return RunAndParse(cmd, parseRoutes) } From 6e8734fa904ee4d917ff7e36b33b25406712edcb Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Thu, 27 Sep 2018 13:59:40 +0200 Subject: [PATCH 05/12] Fixed /symbols endpoint. --- bird/parser.go | 7 ++++++- endpoints/symbols.go | 11 +++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/bird/parser.go b/bird/parser.go index 408c8db..ff6e775 100644 --- a/bird/parser.go +++ b/bird/parser.go @@ -167,7 +167,12 @@ func parseSymbols(reader io.Reader) Parsed { if regex.symbols.keyRx.MatchString(line) { groups := regex.symbols.keyRx.FindStringSubmatch(line) - res[groups[2]] = groups[1] + + if _, ok := res[groups[2]]; !ok { + res[groups[2]] = []string{} + } + + res[groups[2]] = append(res[groups[2]].([]string), groups[1]) } } diff --git a/endpoints/symbols.go b/endpoints/symbols.go index 6a8d41d..d41ffe9 100644 --- a/endpoints/symbols.go +++ b/endpoints/symbols.go @@ -1,6 +1,7 @@ package endpoints import ( + "reflect" "net/http" "github.com/alice-lg/birdwatcher/bird" @@ -13,10 +14,16 @@ func Symbols(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { func SymbolTables(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { val, from_cache := bird.Symbols() - return bird.Parsed{"symbols": val["routing table"]}, from_cache + if reflect.DeepEqual(val, bird.NilParse) || reflect.DeepEqual(val, bird.BirdError) { + return val, from_cache + } + return bird.Parsed{"symbols": val["symbols"].(bird.Parsed)["routing table"]}, from_cache } func SymbolProtocols(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { val, from_cache := bird.Symbols() - return bird.Parsed{"symbols": val["protocols"]}, from_cache + if reflect.DeepEqual(val, bird.NilParse) || reflect.DeepEqual(val, bird.BirdError) { + return val, from_cache + } + return bird.Parsed{"symbols": val["symbols"].(bird.Parsed)["protocol"]}, from_cache } From 08c63fc3a3b17b25b24258e21a49ea968e835e19 Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Thu, 27 Sep 2018 14:02:29 +0200 Subject: [PATCH 06/12] Fixed /routes/count/protocol Endpoint which was returning no data, because the wrong parser was called. --- bird/bird.go | 8 ++++---- endpoints/symbols.go | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/bird/bird.go b/bird/bird.go index 216c301..439462c 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -28,7 +28,7 @@ var Cache = struct { var NilParse Parsed = (Parsed)(nil) var BirdError Parsed = Parsed{"error": "bird unreachable"} -func isSpecial(ret Parsed) bool { +func IsSpecial(ret Parsed) bool { return reflect.DeepEqual(ret, NilParse) || reflect.DeepEqual(ret, BirdError) } @@ -133,7 +133,7 @@ func RunAndParse(cmd string, parser func(io.Reader) Parsed) (Parsed, bool) { func Status() (Parsed, bool) { birdStatus, from_cache := RunAndParse("status", parseStatus) - if isSpecial(birdStatus) { + if IsSpecial(birdStatus) { return birdStatus, from_cache } @@ -178,7 +178,7 @@ func Protocols() (Parsed, bool) { func ProtocolsBgp() (Parsed, bool) { protocols, from_cache := Protocols() - if isSpecial(protocols) { + if IsSpecial(protocols) { return protocols, from_cache } @@ -211,7 +211,7 @@ func RoutesProto(protocol string) (Parsed, bool) { func RoutesProtoCount(protocol string) (Parsed, bool) { cmd := routeQueryForChannel("route protocol "+protocol) + " count" - return RunAndParse(cmd, parseRoutes) + return RunAndParse(cmd, parseRoutesCount) } func RoutesFiltered(protocol string) (Parsed, bool) { diff --git a/endpoints/symbols.go b/endpoints/symbols.go index d41ffe9..df9245b 100644 --- a/endpoints/symbols.go +++ b/endpoints/symbols.go @@ -1,7 +1,6 @@ package endpoints import ( - "reflect" "net/http" "github.com/alice-lg/birdwatcher/bird" @@ -14,7 +13,7 @@ func Symbols(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { func SymbolTables(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { val, from_cache := bird.Symbols() - if reflect.DeepEqual(val, bird.NilParse) || reflect.DeepEqual(val, bird.BirdError) { + if bird.IsSpecial(val) { return val, from_cache } return bird.Parsed{"symbols": val["symbols"].(bird.Parsed)["routing table"]}, from_cache @@ -22,7 +21,7 @@ func SymbolTables(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { func SymbolProtocols(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { val, from_cache := bird.Symbols() - if reflect.DeepEqual(val, bird.NilParse) || reflect.DeepEqual(val, bird.BirdError) { + if bird.IsSpecial(val) { return val, from_cache } return bird.Parsed{"symbols": val["symbols"].(bird.Parsed)["protocol"]}, from_cache From b7952f9061af1a9fd1aa1e7d749e3d0735af2660 Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Fri, 28 Sep 2018 12:08:36 +0200 Subject: [PATCH 07/12] Fix TestParseProtocolBgp() Fix test method TestParseProtocolBgp() to match the output of the current ProtocolsBgp() method wich returns Parsed records instead of a string array. --- bird/parser_test.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/bird/parser_test.go b/bird/parser_test.go index b16744f..0229b5c 100644 --- a/bird/parser_test.go +++ b/bird/parser_test.go @@ -53,14 +53,7 @@ func TestParseProtocolBgp(t *testing.T) { p := parseProtocols(f) log.Printf("%# v", pretty.Formatter(p)) - lines := p["protocols"].([]string) - - protocols := []Parsed{} - - for _, v := range lines { - p2 := parseProtocol(v) - protocols = append(protocols, p2) - } + protocols := p["protocols"].(Parsed) if len(protocols) != 3 { //log.Printf("%# v", pretty.Formatter(protocols)) @@ -68,7 +61,6 @@ func TestParseProtocolBgp(t *testing.T) { } fmt.Println(protocols) - } func TestParseRoutesAllIpv4Bird1(t *testing.T) { From c1a07cc985d0a82cef24fd34c9d843ccb016aa3b Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Fri, 28 Sep 2018 12:09:03 +0200 Subject: [PATCH 08/12] Implemented support for parsing extended communities. --- bird/parser.go | 36 ++++++++++++++++------ bird/parser_test.go | 56 ++++++++++++++++++++++++++++------- test/routes_bird1_ipv4.sample | 4 +++ test/routes_bird1_ipv6.sample | 5 +++- test/routes_bird2_ipv4.sample | 4 +++ test/routes_bird2_ipv6.sample | 5 +++- 6 files changed, 89 insertions(+), 21 deletions(-) diff --git a/bird/parser.go b/bird/parser.go index ff6e775..ad20c6e 100644 --- a/bird/parser.go +++ b/bird/parser.go @@ -37,15 +37,16 @@ var ( countRx *regexp.Regexp } routes struct { - startDefinition *regexp.Regexp - second *regexp.Regexp - routeType *regexp.Regexp - bgp *regexp.Regexp - community *regexp.Regexp - largeCommunity *regexp.Regexp - origin *regexp.Regexp - prefixBird2 *regexp.Regexp - gatewayBird2 *regexp.Regexp + startDefinition *regexp.Regexp + second *regexp.Regexp + routeType *regexp.Regexp + bgp *regexp.Regexp + community *regexp.Regexp + largeCommunity *regexp.Regexp + extendedCommunity *regexp.Regexp + origin *regexp.Regexp + prefixBird2 *regexp.Regexp + gatewayBird2 *regexp.Regexp } } ) @@ -76,6 +77,7 @@ func init() { regex.routes.bgp = regexp.MustCompile(`^\s+BGP.(\w+):\s+(.+)\s*$`) regex.routes.community = regexp.MustCompile(`^\((\d+),\s*(\d+)\)`) regex.routes.largeCommunity = regexp.MustCompile(`^\((\d+),\s*(\d+),\s*(\d+)\)`) + regex.routes.extendedCommunity = regexp.MustCompile(`^\(([^,]+),\s*(\d+),\s*(\d+)\)`) regex.routes.origin = regexp.MustCompile(`\([^\(]*\)\s*`) regex.routes.prefixBird2 = regexp.MustCompile(`^([0-9a-f\.\:\/]+)?\s+unicast\s+\[([\w\.:]+)\s+([0-9\-\:\s]+)(?:\s+from\s+([0-9a-f\.\:\/]+))?\]\s+(?:(\*)\s+)?\((\d+)(?:\/\d+)?(?:\/[^\)]*)?\).*$`) regex.routes.gatewayBird2 = regexp.MustCompile(`^\s+via\s+([0-9a-f\.\:]+)\s+on\s+([\w\.]+)\s*$`) @@ -400,6 +402,8 @@ func parseRoutesBgp(line string, bgp Parsed) { parseRoutesCommunities(groups, bgp) } else if groups[1] == "large_community" { parseRoutesLargeCommunities(groups, bgp) + } else if groups[1] == "ext_community" { + parseRoutesExtendedCommunities(groups, bgp) } else if groups[1] == "as_path" { bgp["as_path"] = strings.Split(groups[2], " ") } else { @@ -436,6 +440,20 @@ func parseRoutesLargeCommunities(groups []string, res Parsed) { res["large_communities"] = communities } +func parseRoutesExtendedCommunities(groups []string, res Parsed) { + communities := []Parsed{} + for _, community := range regex.routes.origin.FindAllString(groups[2], -1) { + if regex.routes.extendedCommunity.MatchString(community) { + communityGroups := regex.routes.extendedCommunity.FindStringSubmatch(community) + c := Parsed{communityGroups[1]: []int64{parseInt(communityGroups[2]), parseInt(communityGroups[3])}} + communities = append(communities, c) + } + } + + res["ext_communities"] = communities +} + + func parseRoutesCount(reader io.Reader) Parsed { res := Parsed{} diff --git a/bird/parser_test.go b/bird/parser_test.go index 0229b5c..0e7dc9f 100644 --- a/bird/parser_test.go +++ b/bird/parser_test.go @@ -100,6 +100,9 @@ func runTestForIpv4WithFile(file string, t *testing.T) { {9033, 65666, 12}, {9033, 65666, 9}, }, + extendedCommunities: []Parsed{ + {"rt": []int64{48858, 50}}, + }, metric: 100, localPref: "100", protocol: "ID8503_AS1340", @@ -118,6 +121,11 @@ func runTestForIpv4WithFile(file string, t *testing.T) { {9033, 65666, 12}, {9033, 65666, 9}, }, + extendedCommunities: []Parsed{ + {"ro": []int64{21414, 52001}}, + {"ro": []int64{21414, 52004}}, + {"ro": []int64{21414, 64515}}, + }, metric: 100, localPref: "100", protocol: "ID8497_AS1339", @@ -136,6 +144,11 @@ func runTestForIpv4WithFile(file string, t *testing.T) { {9033, 65666, 12}, {9033, 65666, 9}, }, + extendedCommunities: []Parsed{ + {"ro": []int64{21414, 52001}}, + {"ro": []int64{21414, 52004}}, + {"ro": []int64{21414, 64515}}, + }, metric: 100, localPref: "100", protocol: "ID8503_AS1340", @@ -154,6 +167,9 @@ func runTestForIpv4WithFile(file string, t *testing.T) { {9033, 65666, 12}, {9033, 65666, 9}, }, + extendedCommunities: []Parsed{ + {"rt": []int64{48858, 50}}, + }, metric: 100, localPref: "100", protocol: "ID8503_AS1340", @@ -199,6 +215,11 @@ func runTestForIpv6WithFile(file string, t *testing.T) { {48821, 0, 2000}, {48821, 0, 2100}, }, + extendedCommunities: []Parsed{ + {"ro": []int64{21414, 52001}}, + {"ro": []int64{21414, 52004}}, + {"ro": []int64{21414, 64515}}, + }, metric: 100, localPref: "500", primary: true, @@ -217,6 +238,11 @@ func runTestForIpv6WithFile(file string, t *testing.T) { {48821, 0, 3000}, {48821, 0, 3100}, }, + extendedCommunities: []Parsed{ + {"ro": []int64{21414, 52001}}, + {"ro": []int64{21414, 52004}}, + {"ro": []int64{21414, 64515}}, + }, localPref: "100", metric: 100, primary: false, @@ -235,6 +261,9 @@ func runTestForIpv6WithFile(file string, t *testing.T) { {48821, 0, 2000}, {48821, 0, 2100}, }, + extendedCommunities: []Parsed{ + {"unknown 0x4300": []int64{0, 1}}, + }, metric: 100, localPref: "5000", primary: true, @@ -280,6 +309,12 @@ func assertRouteIsEqual(expected expectedRoute, actual Parsed, name string, t *t if largeCommunity := value(bgp, "large_communities", name, t).([][]int64); !reflect.DeepEqual(largeCommunity, expected.largeCommunities) { t.Fatal(name, ": Expected large_community to be:", expected.largeCommunities, "not", largeCommunity) } + + if _, ok := bgp["ext_communities"]; ok { + if extendedCommunity := value(bgp, "ext_communities", name, t).([]Parsed); !reflect.DeepEqual(extendedCommunity, expected.extendedCommunities) { + t.Fatal(name, ": Expected ext_community to be:", expected.extendedCommunities, "not", extendedCommunity) + } + } } func value(parsed Parsed, key, name string, t *testing.T) interface{} { @@ -292,14 +327,15 @@ func value(parsed Parsed, key, name string, t *testing.T) interface{} { } type expectedRoute struct { - network string - gateway string - asPath []string - community [][]int64 - largeCommunities [][]int64 - metric int64 - protocol string - primary bool - localPref string - iface string + network string + gateway string + asPath []string + community [][]int64 + largeCommunities [][]int64 + extendedCommunities []Parsed + metric int64 + protocol string + primary bool + localPref string + iface string } diff --git a/test/routes_bird1_ipv4.sample b/test/routes_bird1_ipv4.sample index 69f34bf..8689cce 100644 --- a/test/routes_bird1_ipv4.sample +++ b/test/routes_bird1_ipv4.sample @@ -7,6 +7,7 @@ BIRD 1.6.3 ready. BGP.local_pref: 100 BGP.community: (65011,3) (9033,3251) BGP.large_community: (9033, 65666, 12) (9033, 65666, 9) + BGP.ext_community: (rt, 48858, 50) 200.0.0.0/24 via 1.2.3.15 on eno7 [ID8497_AS1339 2017-06-21 08:17:31] * (100) [AS1339i] Type: BGP unicast univ BGP.origin: IGP @@ -15,6 +16,7 @@ BIRD 1.6.3 ready. BGP.local_pref: 100 BGP.community: (65011,40) (9033,3251) BGP.large_community: (9033, 65666, 12) (9033, 65666, 9) + BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515) via 1.2.3.16 on eno8 [ID8503_AS1340 2017-06-21 08:17:33] (100) [AS1340i] Type: BGP unicast univ BGP.origin: IGP @@ -23,6 +25,7 @@ BIRD 1.6.3 ready. BGP.local_pref: 100 BGP.community: (65011,3) (9033,3251) BGP.large_community: (9033, 65666, 12) (9033, 65666, 9) + BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515) 16.0.0.0/24 via 1.2.3.16 on eno7 [ID8503_AS1340 2017-06-21 08:17:33] * (100) [AS1340i] Type: BGP unicast univ BGP.origin: IGP @@ -31,3 +34,4 @@ BIRD 1.6.3 ready. BGP.local_pref: 100 BGP.community: (65011,3) (9033,3251) BGP.large_community: (9033, 65666, 12) (9033, 65666, 9) + BGP.ext_community: (rt, 48858, 50) diff --git a/test/routes_bird1_ipv6.sample b/test/routes_bird1_ipv6.sample index c9722d0..1f52769 100644 --- a/test/routes_bird1_ipv6.sample +++ b/test/routes_bird1_ipv6.sample @@ -8,6 +8,7 @@ BIRD 1.6.3 ready. BGP.local_pref: 500 BGP.community: (9033,3001) (65000,680) BGP.large_community: (48821, 0, 2000) (48821, 0, 2100) + BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515) via fe80:ffff:ffff::2 on eth3 [upstream2 2018-01-14 14:33:52] (100) [AS15169i] Type: BGP unicast univ BGP.origin: IGP @@ -17,6 +18,7 @@ BIRD 1.6.3 ready. BGP.local_pref: 100 BGP.community: (50629,200) (50629,201) BGP.large_community: (48821, 0, 3000) (48821, 0, 3100) + BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515) 2001:678:1e0::/48 via fe80:ffff:ffff::2 on eth2 [upstream2 2018-01-14 15:04:17 from 2001:678:1e0::2] * (100) [AS202739i] Type: BGP unicast univ BGP.origin: IGP @@ -24,4 +26,5 @@ BIRD 1.6.3 ready. BGP.next_hop: 2001:678:1e0::2 BGP.local_pref: 5000 BGP.community: (48821,2000) (48821,2100) - BGP.large_community: (48821, 0, 2000) (48821, 0, 2100) \ No newline at end of file + BGP.large_community: (48821, 0, 2000) (48821, 0, 2100) + BGP.ext_community: (unknown 0x4300, 0, 1) diff --git a/test/routes_bird2_ipv4.sample b/test/routes_bird2_ipv4.sample index 7a34f1a..8a9c552 100644 --- a/test/routes_bird2_ipv4.sample +++ b/test/routes_bird2_ipv4.sample @@ -8,6 +8,7 @@ BIRD 1.6.3 ready. BGP.local_pref: 100 BGP.community: (65011,3) (9033,3251) BGP.large_community: (9033, 65666, 12) (9033, 65666, 9) + BGP.ext_community: (rt, 48858, 50) 200.0.0.0/24 unicast [ID8497_AS1339 2017-06-21 08:17:31] * (100) [AS1339i] via 1.2.3.15 on eno7 Type: BGP univ @@ -17,6 +18,7 @@ BIRD 1.6.3 ready. BGP.local_pref: 100 BGP.community: (65011,40) (9033,3251) BGP.large_community: (9033, 65666, 12) (9033, 65666, 9) + BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515) unicast [ID8503_AS1340 2017-06-21 08:17:33] (100/?) [AS1340i] via 1.2.3.16 on eno8 Type: BGP univ @@ -26,6 +28,7 @@ BIRD 1.6.3 ready. BGP.local_pref: 100 BGP.community: (65011,3) (9033,3251) BGP.large_community: (9033, 65666, 12) (9033, 65666, 9) + BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515) 16.0.0.0/24 unicast [ID8503_AS1340 2017-06-21 08:17:33] * (100) [AS1340i] via 1.2.3.16 on eno7 Type: BGP univ @@ -35,3 +38,4 @@ BIRD 1.6.3 ready. BGP.local_pref: 100 BGP.community: (65011,3) (9033,3251) BGP.large_community: (9033, 65666, 12) (9033, 65666, 9) + BGP.ext_community: (rt, 48858, 50) diff --git a/test/routes_bird2_ipv6.sample b/test/routes_bird2_ipv6.sample index b66ce80..e6c864c 100644 --- a/test/routes_bird2_ipv6.sample +++ b/test/routes_bird2_ipv6.sample @@ -9,6 +9,7 @@ BIRD 2.0.0 ready. BGP.local_pref: 500 BGP.community: (9033,3001) (65000,680) BGP.large_community: (48821, 0, 2000) (48821, 0, 2100) + BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515) unicast [upstream2 2018-01-14 13:07:26 from fe80:ffff:ffff::2] (100/?) [AS15169i] via fe80:ffff:ffff::2 on eth3 Type: BGP univ @@ -19,6 +20,7 @@ BIRD 2.0.0 ready. BGP.local_pref: 100 BGP.community: (50629,200) (50629,201) BGP.large_community: (48821, 0, 3000) (48821, 0, 3100) + BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515) 2001:678:1e0::/48 unicast [upstream2 2018-01-15 20:31:39 from fe80:ffff:ffff::2] * (100/?) [i] via fe80:ffff:ffff::2 on eth2 Type: BGP univ @@ -27,4 +29,5 @@ BIRD 2.0.0 ready. BGP.next_hop: 2001:678:1e0::2 BGP.local_pref: 5000 BGP.community: (48821,2000) (48821,2100) - BGP.large_community: (48821, 0, 2000) (48821, 0, 2100) \ No newline at end of file + BGP.large_community: (48821, 0, 2000) (48821, 0, 2100) + BGP.ext_community: (unknown 0x4300, 0, 1) From 541ea6d84d2fc6a65f7b4ae58b9e0fd6291328ef Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Fri, 28 Sep 2018 14:10:04 +0200 Subject: [PATCH 09/12] Fix RoutesDumpPerPeerTable() Fix RoutesDumpPerPeerTable to return results again and the filtered routes. --- bird/bird.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bird/bird.go b/bird/bird.go index 439462c..16167af 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -299,10 +299,8 @@ func RoutesDumpPerPeerTable() (Parsed, bool) { protocols := protocolsRes["protocols"].(Parsed) for protocol, details := range protocols { - details, ok := details.(Parsed) - if !ok { - continue - } + details := details.(Parsed) + counters, ok := details["routes"].(Parsed) if !ok { continue @@ -312,7 +310,10 @@ func RoutesDumpPerPeerTable() (Parsed, bool) { continue // nothing to do here. } // Lookup filtered routes - pfilteredRes, _ := RoutesFiltered(protocol) + pfilteredRes, from_cache := RoutesFiltered(protocol) + if reflect.DeepEqual(pfilteredRes, BirdError) { + return pfilteredRes, from_cache + } pfiltered, ok := pfilteredRes["routes"].([]Parsed) if !ok { From 7fd2dfd0cf3d4f2fe6a825a868c5ca884e5c404d Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Fri, 28 Sep 2018 16:44:08 +0200 Subject: [PATCH 10/12] Implement new endpoint /routes/count/primary --- bird/bird.go | 5 +++++ birdwatcher.go | 3 +++ endpoints/routes.go | 8 ++++++++ etc/ecix/birdwatcher.conf | 1 + 4 files changed, 17 insertions(+) diff --git a/bird/bird.go b/bird/bird.go index 16167af..cf18015 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -214,6 +214,11 @@ func RoutesProtoCount(protocol string) (Parsed, bool) { return RunAndParse(cmd, parseRoutesCount) } +func RoutesProtoPrimaryCount(protocol string) (Parsed, bool) { + cmd := routeQueryForChannel("route primary protocol "+protocol) + " count" + return RunAndParse(cmd, parseRoutesCount) +} + func RoutesFiltered(protocol string) (Parsed, bool) { cmd := routeQueryForChannel("route all filtered protocol " + protocol) return RunAndParse(cmd, parseRoutes) diff --git a/birdwatcher.go b/birdwatcher.go index 8f4de3f..efe9710 100644 --- a/birdwatcher.go +++ b/birdwatcher.go @@ -65,6 +65,9 @@ func makeRouter(config endpoints.ServerConfig) *httprouter.Router { if isModuleEnabled("routes_count_table", whitelist) { r.GET("/routes/count/table/:table", endpoints.Endpoint(endpoints.TableCount)) } + if isModuleEnabled("routes_count_primary", whitelist) { + r.GET("/routes/count/primary/:protocol", endpoints.Endpoint(endpoints.ProtoPrimaryCount)) + } if isModuleEnabled("routes_filtered", whitelist) { r.GET("/routes/filtered/:protocol", endpoints.Endpoint(endpoints.RoutesFiltered)) } diff --git a/endpoints/routes.go b/endpoints/routes.go index a7c8c79..1dee4b3 100644 --- a/endpoints/routes.go +++ b/endpoints/routes.go @@ -58,6 +58,14 @@ func ProtoCount(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { return bird.RoutesProtoCount(protocol) } +func ProtoPrimaryCount(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { + protocol, err := ValidateProtocolParam(ps.ByName("protocol")) + if err != nil { + return bird.Parsed{"error": fmt.Sprintf("%s", err)}, false + } + return bird.RoutesProtoPrimaryCount(protocol) +} + func TableCount(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) { return bird.RoutesTableCount(ps.ByName("table")) } diff --git a/etc/ecix/birdwatcher.conf b/etc/ecix/birdwatcher.conf index eb727f5..a1cc871 100644 --- a/etc/ecix/birdwatcher.conf +++ b/etc/ecix/birdwatcher.conf @@ -18,6 +18,7 @@ allow_from = [] # routes_table # routes_count_protocol # routes_count_table +# routes_count_primary # routes_filtered # routes_prefixed # routes_noexport From 95cf1a71afb078abaa9e08cd34c0ef108dd04a01 Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Mon, 1 Oct 2018 10:25:26 +0200 Subject: [PATCH 11/12] Fixed a bug in the parser that would return an integer of the wrong type (int vs. int64) in an error case. --- bird/parser.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bird/parser.go b/bird/parser.go index ad20c6e..863a824 100644 --- a/bird/parser.go +++ b/bird/parser.go @@ -514,10 +514,10 @@ func parseProtocol(lines string) Parsed { if _, ok := res["routes"]; !ok { routes := Parsed{} - routes["accepted"] = 0 - routes["filtered"] = 0 - routes["exported"] = 0 - routes["preferred"] = 0 + routes["accepted"] = int64(0) + routes["filtered"] = int64(0) + routes["exported"] = int64(0) + routes["preferred"] = int64(0) res["routes"] = routes } @@ -620,7 +620,7 @@ func treatKey(key string) string { func parseInt(from string) int64 { val, err := strconv.ParseInt(from, 10, 64) if err != nil { - return 0 + return int64(0) } return val From 980fcff9c2dc4b780b1a29178d685797c8ee2c56 Mon Sep 17 00:00:00 2001 From: Patrick Seeburger Date: Thu, 4 Oct 2018 21:09:50 +0200 Subject: [PATCH 12/12] Changed the data structure for the extended communities to be a single slice of {string, int64, int64} instead of a parsed record. --- bird/parser.go | 5 ++--- bird/parser_test.go | 50 ++++++++++++++++++++++----------------------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/bird/parser.go b/bird/parser.go index 863a824..0f2ccc2 100644 --- a/bird/parser.go +++ b/bird/parser.go @@ -441,12 +441,11 @@ func parseRoutesLargeCommunities(groups []string, res Parsed) { } func parseRoutesExtendedCommunities(groups []string, res Parsed) { - communities := []Parsed{} + communities := []interface{}{} for _, community := range regex.routes.origin.FindAllString(groups[2], -1) { if regex.routes.extendedCommunity.MatchString(community) { communityGroups := regex.routes.extendedCommunity.FindStringSubmatch(community) - c := Parsed{communityGroups[1]: []int64{parseInt(communityGroups[2]), parseInt(communityGroups[3])}} - communities = append(communities, c) + communities = append(communities, []interface{}{communityGroups[1], parseInt(communityGroups[2]), parseInt(communityGroups[3])}) } } diff --git a/bird/parser_test.go b/bird/parser_test.go index 0e7dc9f..81d5bc2 100644 --- a/bird/parser_test.go +++ b/bird/parser_test.go @@ -100,8 +100,8 @@ func runTestForIpv4WithFile(file string, t *testing.T) { {9033, 65666, 12}, {9033, 65666, 9}, }, - extendedCommunities: []Parsed{ - {"rt": []int64{48858, 50}}, + extendedCommunities: []interface{}{ + []interface{}{"rt", int64(48858), int64(50)}, }, metric: 100, localPref: "100", @@ -121,10 +121,10 @@ func runTestForIpv4WithFile(file string, t *testing.T) { {9033, 65666, 12}, {9033, 65666, 9}, }, - extendedCommunities: []Parsed{ - {"ro": []int64{21414, 52001}}, - {"ro": []int64{21414, 52004}}, - {"ro": []int64{21414, 64515}}, + extendedCommunities: []interface{}{ + []interface{}{"ro", int64(21414), int64(52001)}, + []interface{}{"ro", int64(21414), int64(52004)}, + []interface{}{"ro", int64(21414), int64(64515)}, }, metric: 100, localPref: "100", @@ -144,10 +144,10 @@ func runTestForIpv4WithFile(file string, t *testing.T) { {9033, 65666, 12}, {9033, 65666, 9}, }, - extendedCommunities: []Parsed{ - {"ro": []int64{21414, 52001}}, - {"ro": []int64{21414, 52004}}, - {"ro": []int64{21414, 64515}}, + extendedCommunities: []interface{}{ + []interface{}{"ro", int64(21414), int64(52001)}, + []interface{}{"ro", int64(21414), int64(52004)}, + []interface{}{"ro", int64(21414), int64(64515)}, }, metric: 100, localPref: "100", @@ -167,8 +167,8 @@ func runTestForIpv4WithFile(file string, t *testing.T) { {9033, 65666, 12}, {9033, 65666, 9}, }, - extendedCommunities: []Parsed{ - {"rt": []int64{48858, 50}}, + extendedCommunities: []interface{}{ + []interface{}{"rt", int64(48858), int64(50)}, }, metric: 100, localPref: "100", @@ -215,10 +215,10 @@ func runTestForIpv6WithFile(file string, t *testing.T) { {48821, 0, 2000}, {48821, 0, 2100}, }, - extendedCommunities: []Parsed{ - {"ro": []int64{21414, 52001}}, - {"ro": []int64{21414, 52004}}, - {"ro": []int64{21414, 64515}}, + extendedCommunities: []interface{}{ + []interface{}{"ro", int64(21414), int64(52001)}, + []interface{}{"ro", int64(21414), int64(52004)}, + []interface{}{"ro", int64(21414), int64(64515)}, }, metric: 100, localPref: "500", @@ -238,10 +238,10 @@ func runTestForIpv6WithFile(file string, t *testing.T) { {48821, 0, 3000}, {48821, 0, 3100}, }, - extendedCommunities: []Parsed{ - {"ro": []int64{21414, 52001}}, - {"ro": []int64{21414, 52004}}, - {"ro": []int64{21414, 64515}}, + extendedCommunities: []interface{}{ + []interface{}{"ro", int64(21414), int64(52001)}, + []interface{}{"ro", int64(21414), int64(52004)}, + []interface{}{"ro", int64(21414), int64(64515)}, }, localPref: "100", metric: 100, @@ -261,8 +261,8 @@ func runTestForIpv6WithFile(file string, t *testing.T) { {48821, 0, 2000}, {48821, 0, 2100}, }, - extendedCommunities: []Parsed{ - {"unknown 0x4300": []int64{0, 1}}, + extendedCommunities: []interface{}{ + []interface{}{"unknown 0x4300", int64(0), int64(1)}, }, metric: 100, localPref: "5000", @@ -310,8 +310,8 @@ func assertRouteIsEqual(expected expectedRoute, actual Parsed, name string, t *t t.Fatal(name, ": Expected large_community to be:", expected.largeCommunities, "not", largeCommunity) } - if _, ok := bgp["ext_communities"]; ok { - if extendedCommunity := value(bgp, "ext_communities", name, t).([]Parsed); !reflect.DeepEqual(extendedCommunity, expected.extendedCommunities) { + if extendedCommunity, ok := bgp["ext_communities"]; ok { + if !reflect.DeepEqual(extendedCommunity.([]interface{}), expected.extendedCommunities) { t.Fatal(name, ": Expected ext_community to be:", expected.extendedCommunities, "not", extendedCommunity) } } @@ -332,7 +332,7 @@ type expectedRoute struct { asPath []string community [][]int64 largeCommunities [][]int64 - extendedCommunities []Parsed + extendedCommunities []interface{} metric int64 protocol string primary bool