diff --git a/cmd/rtrmon/rtrmon.go b/cmd/rtrmon/rtrmon.go index 7b312f5..f518871 100644 --- a/cmd/rtrmon/rtrmon.go +++ b/cmd/rtrmon/rtrmon.go @@ -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: ©Of, + } + } + // 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 diff --git a/cmd/rtrmon/rtrmon_test.go b/cmd/rtrmon/rtrmon_test.go index 4ecccfc..bbe090e 100644 --- a/cmd/rtrmon/rtrmon_test.go +++ b/cmd/rtrmon/rtrmon_test.go @@ -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 +}