1
0
mirror of https://github.com/bgp/stayrtr.git synced 2024-05-06 15:54:54 +00:00

Add basic BGPsec support to RTRMon

It is not pretty, I didnt really want to overhaul the whole tool
since I also use this tool to test my own releases of stayrtr.

So instead bgpsec router keys are injected as "special" VRPs.

Since RTRMon is not complying to a standard, I feel we can be more
flexible here.

Tag: https://github.com/bgp/stayrtr/issues/57
This commit is contained in:
Ben Cartwright-Cox
2023-02-22 11:40:15 +00:00
parent 539a99d76c
commit 9edbfb3ba1
2 changed files with 91 additions and 25 deletions

View File

@ -4,6 +4,7 @@ import (
"bytes"
"crypto/tls"
_ "embed"
"encoding/hex"
"encoding/json"
"errors"
"flag"
@ -345,7 +346,7 @@ func (c *Client) Start(id int, ch chan int) {
continue
}
updatedVrpMap, inGracePeriod = BuildNewVrpMap(log.WithField("client", c.id), c.vrps, decoded.Data, tCurrentUpdate)
updatedVrpMap, inGracePeriod = BuildNewVrpMap(log.WithField("client", c.id), c.vrps, decoded, tCurrentUpdate)
}
VRPInGracePeriod.With(prometheus.Labels{"url": c.Path}).Set(float64(inGracePeriod))
@ -368,7 +369,8 @@ func (c *Client) Start(id int, ch chan int) {
// * contains all the VRPs in newVRPs
// * keeps the firstSeen value for VRPs already in the old map
// * keeps elements around for GracePeriod after they are not in the input.
func BuildNewVrpMap(log *log.Entry, currentVrps VRPMap, newVrps []prefixfile.VRPJson, now time.Time) (VRPMap, int) {
func BuildNewVrpMap(log *log.Entry, currentVrps VRPMap, pfxFile *prefixfile.VRPList, now time.Time) (VRPMap, int) {
var newVrps = pfxFile.Data
tCurrentUpdate := now.Unix()
res := make(VRPMap)
@ -404,6 +406,27 @@ func BuildNewVrpMap(log *log.Entry, currentVrps VRPMap, newVrps []prefixfile.VRP
}
}
for _, bgpsecKey := range pfxFile.BgpSecKeys {
key := fmt.Sprintf("%s-%d-%s", bgpsecKey.Ski, bgpsecKey.Asn, bgpsecKey.Pubkey)
firstSeen := tCurrentUpdate
currentEntry, ok := currentVrps[key]
if ok && currentEntry.Visible {
// VRP is still visible, so keep the existing `FirstSeen`.
firstSeen = currentEntry.FirstSeen
}
copyOf := bgpsecKey
res[key] = &VRPJsonSimple{
ASN: bgpsecKey.Asn,
FirstSeen: firstSeen,
LastSeen: tCurrentUpdate,
Visible: true,
BGPSecData: &copyOf,
}
}
// Copy objects that are within the grace period to the new map
gracePeriodEnds := tCurrentUpdate - int64(GracePeriod.Seconds())
inGracePeriod := 0
@ -482,6 +505,28 @@ func (c *Client) HandlePDU(cs *rtr.ClientSession, pdu rtr.PDU) {
delete(c.vrpsRtr, key)
}
c.compRtrLock.Unlock()
case *rtr.PDURouterKey:
vrp := VRPJsonSimple{
ASN: pdu.ASN,
FirstSeen: time.Now().Unix(),
Visible: true,
BGPSecData: &prefixfile.BgpSecKeyJson{
Asn: pdu.ASN,
Pubkey: pdu.SubjectPublicKeyInfo,
Ski: hex.EncodeToString(pdu.SubjectKeyIdentifier),
},
}
key := fmt.Sprintf("%s-%d-%s", vrp.BGPSecData.Ski, pdu.ASN, pdu.SubjectPublicKeyInfo)
c.compRtrLock.Lock()
if pdu.Flags == rtr.FLAG_ADDED {
c.vrpsRtr[key] = &vrp
} else {
delete(c.vrpsRtr, key)
}
c.compRtrLock.Unlock()
case *rtr.PDUEndOfData:
log.Infof("%d: Received: %v", c.id, pdu)
@ -678,12 +723,13 @@ type diffMetadata struct {
}
type VRPJsonSimple struct {
ASN uint32 `json:"asn"`
Length uint8 `json:"max-length"`
Prefix string `json:"prefix"`
FirstSeen int64 `json:"first-seen"`
LastSeen int64 `json:"last-seen"`
Visible bool `json:"visible"`
ASN uint32 `json:"asn"`
Length uint8 `json:"max-length"`
Prefix string `json:"prefix"`
FirstSeen int64 `json:"first-seen"`
LastSeen int64 `json:"last-seen"`
Visible bool `json:"visible"`
BGPSecData *prefixfile.BgpSecKeyJson `json:"bgpsec,omitempty"`
}
type VRPMap map[string]*VRPJsonSimple

View File

@ -10,7 +10,13 @@ import (
)
func TestBuildNewVrpMap_expiry(t *testing.T) {
stuff := testData()
stuff := testDataFile()
emptyFile := &prefixfile.VRPList{
Metadata: prefixfile.MetaData{},
Data: []prefixfile.VRPJson{},
BgpSecKeys: []prefixfile.BgpSecKeyJson{},
}
now := time.Now()
log := log.WithField("client", "TestBuildNewVrpMap_expiry")
@ -19,7 +25,7 @@ func TestBuildNewVrpMap_expiry(t *testing.T) {
t.Errorf("Initial build does not have objects in grace period")
}
_, inGracePeriodPreserved := BuildNewVrpMap(log, res, []prefixfile.VRPJson{}, now.Add(time.Minute*10))
_, inGracePeriodPreserved := BuildNewVrpMap(log, res, emptyFile, now.Add(time.Minute*10))
if inGracePeriodPreserved != len(res) {
t.Errorf("All objects are in grace period")
}
@ -27,16 +33,16 @@ func TestBuildNewVrpMap_expiry(t *testing.T) {
// Objects are kept in grace period
// 1s before grace period ends
t1 := now.Add(*GracePeriod).Add(-time.Second * 1)
res, inGracePeriod = BuildNewVrpMap(log, res, []prefixfile.VRPJson{}, t1)
res, inGracePeriod = BuildNewVrpMap(log, res, emptyFile, t1)
assertLastSeenMatchesTimeCount(t, res, t1, 0)
assertLastSeenMatchesTimeCount(t, res, now, len(stuff))
if inGracePeriod != len(stuff) {
t.Errorf("All objects should be in grace period. Expected: %d, actual: %d", len(stuff), inGracePeriod)
assertLastSeenMatchesTimeCount(t, res, now, len(stuff.Data))
if inGracePeriod != len(stuff.Data) {
t.Errorf("All objects should be in grace period. Expected: %d, actual: %d", len(stuff.Data), inGracePeriod)
}
// 1s after grace period ends, they are removed
res, inGracePeriod = BuildNewVrpMap(log, res, []prefixfile.VRPJson{}, now.Add(*GracePeriod).Add(time.Second*1))
res, inGracePeriod = BuildNewVrpMap(log, res, emptyFile, now.Add(*GracePeriod).Add(time.Second*1))
if len(res) != 0 {
t.Errorf("Expected no objects to be left after grace period, actual: %d", len(res))
}
@ -48,23 +54,23 @@ func TestBuildNewVrpMap_expiry(t *testing.T) {
func TestBuildNewVrpMap_firsSeen_lastSeen(t *testing.T) {
t0 := time.Now()
log := log.WithField("client", "TestBuildNewVrpMap_firstSeen_lastSeen")
stuff := testData()
stuff := testDataFile()
var res, _ = BuildNewVrpMap(log, make(VRPMap), stuff, t0)
// All have firstSeen + lastSeen equal to t0
assertFirstSeenMatchesTimeCount(t, res, t0, len(stuff))
assertLastSeenMatchesTimeCount(t, res, t0, len(stuff))
assertVisibleMatchesTimeCount(t, res, len(stuff))
assertFirstSeenMatchesTimeCount(t, res, t0, len(stuff.Data))
assertLastSeenMatchesTimeCount(t, res, t0, len(stuff.Data))
assertVisibleMatchesTimeCount(t, res, len(stuff.Data))
// Supply same data again later
t1 := t0.Add(time.Minute * 10)
res, _ = BuildNewVrpMap(log, res, stuff, t1)
// FirstSeen is constant, LastSeen gets updated, none removed
assertFirstSeenMatchesTimeCount(t, res, t0, len(stuff))
assertLastSeenMatchesTimeCount(t, res, t1, len(stuff))
assertVisibleMatchesTimeCount(t, res, len(stuff))
assertFirstSeenMatchesTimeCount(t, res, t0, len(stuff.Data))
assertLastSeenMatchesTimeCount(t, res, t1, len(stuff.Data))
assertVisibleMatchesTimeCount(t, res, len(stuff.Data))
// Supply one new VRP, expect one at new time, others at old time
otherStuff := []prefixfile.VRPJson{
@ -75,12 +81,17 @@ func TestBuildNewVrpMap_firsSeen_lastSeen(t *testing.T) {
TA: "testrir",
},
}
otherStuffFile := prefixfile.VRPList{
Metadata: prefixfile.MetaData{},
Data: otherStuff,
BgpSecKeys: []prefixfile.BgpSecKeyJson{},
}
t2 := t1.Add(time.Minute * 10)
res, _ = BuildNewVrpMap(log, res, otherStuff, t2)
res, _ = BuildNewVrpMap(log, res, &otherStuffFile, t2)
// LastSeen gets updated just for the new item
assertFirstSeenMatchesTimeCount(t, res, t0, len(stuff))
assertLastSeenMatchesTimeCount(t, res, t1, len(stuff))
assertFirstSeenMatchesTimeCount(t, res, t0, len(stuff.Data))
assertLastSeenMatchesTimeCount(t, res, t1, len(stuff.Data))
assertFirstSeenMatchesTimeCount(t, res, t2, len(otherStuff))
assertLastSeenMatchesTimeCount(t, res, t2, len(otherStuff))
@ -152,3 +163,12 @@ func testData() []prefixfile.VRPJson {
return stuff
}
func testDataFile() *prefixfile.VRPList {
stuff := prefixfile.VRPList{
Metadata: prefixfile.MetaData{},
Data: testData(),
BgpSecKeys: []prefixfile.BgpSecKeyJson{},
}
return &stuff
}