diff --git a/cmd/stayrtr/stayrtr.go b/cmd/stayrtr/stayrtr.go index d939331..627c08d 100644 --- a/cmd/stayrtr/stayrtr.go +++ b/cmd/stayrtr/stayrtr.go @@ -331,6 +331,17 @@ func (s *state) updateFromNewState() error { if vrpsjson == nil { return nil } + bgpsecjson := s.lastdata.BgpSecKeys + if bgpsecjson == nil { + bgpsecjson = make([]prefixfile.BgpSecKeyJson, 0) + } + aspajson := s.lastdata.ASPA + if aspajson == nil { + aspajson = &prefixfile.ProviderAuthorizationsJson{ + IPv4: make([]prefixfile.ASPAJson, 0), + IPv6: make([]prefixfile.ASPAJson, 0), + } + } buildtime, err := time.Parse(time.RFC3339, s.lastdata.Metadata.Buildtime) if s.lastdata.Metadata.GeneratedUnix != nil { @@ -348,16 +359,13 @@ func (s *state) updateFromNewState() error { } if s.slurm != nil { - kept, removed := s.slurm.FilterOnVRPs(vrpsjson) - asserted := s.slurm.AssertVRPs() - log.Infof("Slurm filtering: %v kept, %v removed, %v asserted", len(kept), len(removed), len(asserted)) - vrpsjson = append(kept, asserted...) + vrpsjson, aspajson.IPv4, aspajson.IPv6, bgpsecjson = s.slurm.FilterAssert(vrpsjson, aspajson.IPv4, aspajson.IPv6, bgpsecjson, log.StandardLogger()) } - vrps, brks, vaps, count, countv4, countv6 := processData(vrpsjson, s.lastdata.BgpSecKeys, s.lastdata.ASPA) + vrps, brks, vaps, count, countv4, countv6 := processData(vrpsjson, bgpsecjson, aspajson) log.Infof("New update (%v uniques, %v total prefixes).", len(vrps), count) - return s.applyUpdateFromNewState(vrps, brks, vaps, sessid, vrpsjson, s.lastdata.BgpSecKeys, s.lastdata.ASPA, countv4, countv6) + return s.applyUpdateFromNewState(vrps, brks, vaps, sessid, vrpsjson, bgpsecjson, aspajson, countv4, countv6) } // Update the state based on the currently loaded files @@ -368,8 +376,22 @@ func (s *state) reloadFromCurrentState() error { if vrpsjson == nil { return nil } + bgpsecjson := s.lastdata.BgpSecKeys + if bgpsecjson == nil { + bgpsecjson = make([]prefixfile.BgpSecKeyJson, 0) + } + aspajson := s.lastdata.ASPA + if aspajson == nil { + aspajson = &prefixfile.ProviderAuthorizationsJson{ + IPv4: make([]prefixfile.ASPAJson, 0), + IPv6: make([]prefixfile.ASPAJson, 0), + } + } buildtime, err := time.Parse(time.RFC3339, s.lastdata.Metadata.Buildtime) + if s.lastdata.Metadata.GeneratedUnix != nil { + buildtime, err = time.Unix(int64(*s.lastdata.Metadata.GeneratedUnix), 0), nil + } if s.checktime { if err != nil { return err @@ -382,16 +404,13 @@ func (s *state) reloadFromCurrentState() error { } if s.slurm != nil { - kept, removed := s.slurm.FilterOnVRPs(vrpsjson) - asserted := s.slurm.AssertVRPs() - log.Infof("Slurm filtering: %v kept, %v removed, %v asserted", len(kept), len(removed), len(asserted)) - vrpsjson = append(kept, asserted...) + vrpsjson, aspajson.IPv4, aspajson.IPv6, bgpsecjson = s.slurm.FilterAssert(vrpsjson, aspajson.IPv4, aspajson.IPv6, bgpsecjson, log.StandardLogger()) } - vrps, brks, vaps, count, countv4, countv6 := processData(vrpsjson, s.lastdata.BgpSecKeys, s.lastdata.ASPA) + vrps, brks, vaps, count, countv4, countv6 := processData(vrpsjson, bgpsecjson, aspajson) if s.server.CountVRPs() != count { log.Infof("New update to old state (%v uniques, %v total prefixes). (old %v - new %v)", len(vrps), count, s.server.CountVRPs(), count) - return s.applyUpdateFromNewState(vrps, brks, vaps, sessid, vrpsjson, s.lastdata.BgpSecKeys, s.lastdata.ASPA, countv4, countv6) + return s.applyUpdateFromNewState(vrps, brks, vaps, sessid, vrpsjson, bgpsecjson, aspajson, countv4, countv6) } return nil } diff --git a/prefixfile/slurm.go b/prefixfile/slurm.go index a895a77..bc35bf0 100644 --- a/prefixfile/slurm.go +++ b/prefixfile/slurm.go @@ -3,9 +3,12 @@ package prefixfile import ( + "bytes" + "encoding/hex" "encoding/json" "io" "net" + "strings" ) type SlurmPrefixFilter struct { @@ -15,8 +18,9 @@ type SlurmPrefixFilter struct { } type SlurmBGPsecFilter struct { - ASN uint32 `json:"asn"` - Comment string `json:"comment"` + ASN *uint32 `json:"asn,omitempty"` + SKI []byte `json:"SKI,omitempty"` + Comment string `json:"comment"` } type SlurmASPAFilter struct { @@ -101,9 +105,9 @@ func DecodeJSONSlurm(buf io.Reader) (*SlurmConfig, error) { return slurm, nil } -func (s *SlurmValidationOutputFilters) FilterOnVRPs(vrps []VRPJson) ([]VRPJson, []VRPJson) { - added := make([]VRPJson, 0) - removed := make([]VRPJson, 0) +func (s *SlurmValidationOutputFilters) FilterOnVRPs(vrps []VRPJson) (added, removed []VRPJson) { + added = make([]VRPJson, 0) + removed = make([]VRPJson, 0) if s.PrefixFilters == nil || len(s.PrefixFilters) == 0 { return vrps, removed } @@ -146,8 +150,92 @@ func (s *SlurmValidationOutputFilters) FilterOnVRPs(vrps []VRPJson) ([]VRPJson, return added, removed } -func (s *SlurmConfig) FilterOnVRPs(vrps []VRPJson) ([]VRPJson, []VRPJson) { - return s.ValidationOutputFilters.FilterOnVRPs(vrps) +func (s *SlurmValidationOutputFilters) FilterOnBRKs(brks []BgpSecKeyJson) (added, removed []BgpSecKeyJson) { + added = make([]BgpSecKeyJson, 0) + removed = make([]BgpSecKeyJson, 0) + if s.BgpsecFilters == nil || len(s.BgpsecFilters) == 0 { + return brks, removed + } + for _, brk := range brks { + var skiCache []byte + var wasRemoved bool + for _, filter := range s.BgpsecFilters { + if filter.ASN != nil { + if brk.Asn == *filter.ASN { + if len(filter.SKI) != 0 { + // We need to compare the SKIs then + if skiCache == nil { // We have not yet decoded the ski hex + var err error + skiCache, err = hex.DecodeString(brk.Ski) + if err != nil { + // Ski could not be decoded, so we can't filter + continue + } + } + if bytes.Equal(filter.SKI, skiCache) { + removed = append(removed, brk) + wasRemoved = true + break + } + } else { + // Only a ASN match was needed + removed = append(removed, brk) + wasRemoved = true + break + } + } + } + + if len(filter.SKI) != 0 && filter.ASN == nil { + // We need to compare just the SKIs then + if skiCache == nil { // We have not yet decoded the ski hex + var err error + skiCache, err = hex.DecodeString(brk.Ski) + if err != nil { + // Ski could not be decoded, so we can't filter + continue + } + } + if bytes.Equal(filter.SKI, skiCache) { + removed = append(removed, brk) + wasRemoved = true + break + } + } + } + + if !wasRemoved { + added = append(added, brk) + } + } + return added, removed +} + +func (s *SlurmValidationOutputFilters) FilterOnVAPs(vaps []ASPAJson, ipv6 bool) (added, removed []ASPAJson) { + added = make([]ASPAJson, 0) + removed = make([]ASPAJson, 0) + if s.AspaFilters == nil || len(s.AspaFilters) == 0 { + return vaps, removed + } + for _, vap := range vaps { + var wasRemoved bool + for _, filter := range s.AspaFilters { + if strings.Contains(filter.Afi, "6") && !ipv6 { + continue + } + + if vap.CustomerAsid == filter.CustomerASid { + removed = append(removed, vap) + wasRemoved = true + break + } + } + + if !wasRemoved { + added = append(added, vap) + } + } + return added, removed } func (s *SlurmLocallyAddedAssertions) AssertVRPs() []VRPJson { @@ -175,12 +263,87 @@ func (s *SlurmLocallyAddedAssertions) AssertVRPs() []VRPJson { return vrps } -func (s *SlurmConfig) AssertVRPs() []VRPJson { - return s.LocallyAddedAssertions.AssertVRPs() +func (s *SlurmLocallyAddedAssertions) AssertVAPs() (v4, v6 []ASPAJson) { + vapsv4, vapsv6 := make([]ASPAJson, 0), make([]ASPAJson, 0) + + if s.AspaAssertions == nil || len(s.AspaAssertions) == 0 { + return vapsv4, vapsv6 + } + for _, assertion := range s.AspaAssertions { + vap := ASPAJson{ + CustomerAsid: assertion.CustomerASNid, + Providers: assertion.ProviderSet, + } + if strings.Contains(assertion.Afi, "6") { + vapsv6 = append(vapsv6, vap) + } else { + vapsv4 = append(vapsv4, vap) + } + } + return vapsv4, vapsv6 +} + +func (s *SlurmLocallyAddedAssertions) AssertBRKs() []BgpSecKeyJson { + brks := make([]BgpSecKeyJson, 0) + + if s.BgpsecAssertions == nil || len(s.BgpsecAssertions) == 0 { + return brks + } + for _, assertion := range s.BgpsecAssertions { + hexSki := hex.EncodeToString(assertion.SKI) + brk := BgpSecKeyJson{ + Asn: assertion.ASN, + Pubkey: assertion.RouterPublicKey, + Ski: hexSki, + } + brks = append(brks, brk) + } + return brks +} + +func (s *SlurmConfig) GetAssertions() (vrps []VRPJson, VAPv4, VAPv6 []ASPAJson, BRKs []BgpSecKeyJson) { + vrps = s.LocallyAddedAssertions.AssertVRPs() + VAPv4, VAPv6 = s.LocallyAddedAssertions.AssertVAPs() + BRKs = s.LocallyAddedAssertions.AssertBRKs() + return +} + +func (s *SlurmConfig) FilterAssert(vrps []VRPJson, VAPv4, VAPv6 []ASPAJson, BRKs []BgpSecKeyJson, log Logger) ( + ovrps []VRPJson, oVAPv4, oVAPv6 []ASPAJson, oBRKs []BgpSecKeyJson) { + // + filteredVRPs, removedVRPs := s.ValidationOutputFilters.FilterOnVRPs(vrps) + filteredVAP4s, removedVAP4s := s.ValidationOutputFilters.FilterOnVAPs(VAPv4, false) + filteredVAP6s, removedVAP6s := s.ValidationOutputFilters.FilterOnVAPs(VAPv6, true) + filteredBRKs, removedBRKs := s.ValidationOutputFilters.FilterOnBRKs(BRKs) + + assertVRPs, assertVAP4, assertVAP6, assertBRKs := s.GetAssertions() + + ovrps = append(filteredVRPs, assertVRPs...) + oVAPv4 = append(filteredVAP4s, assertVAP4...) + oVAPv6 = append(filteredVAP6s, assertVAP6...) + oBRKs = append(filteredBRKs, assertBRKs...) + + if log != nil { + if len(s.ValidationOutputFilters.PrefixFilters) != 0 { + log.Infof("Slurm VRP filtering: %v kept, %v removed, %v asserted", len(filteredVRPs), len(removedVRPs), len(ovrps)) + } + + if len(s.ValidationOutputFilters.BgpsecFilters) != 0 { + log.Infof("Slurm Router Key filtering: %v kept, %v removed, %v asserted", len(filteredBRKs), len(removedBRKs), len(oBRKs)) + } + + if len(s.ValidationOutputFilters.AspaFilters) != 0 { + log.Infof("Slurm ASPA v4 filtering: %v kept, %v removed, %v asserted", len(filteredVAP4s), len(removedVAP4s), len(oVAPv4)) + log.Infof("Slurm ASPA v6 filtering: %v kept, %v removed, %v asserted", len(filteredVAP6s), len(removedVAP6s), len(oVAPv6)) + } + } + return } -func (s *SlurmConfig) FilterAssert(vrps []VRPJson) []VRPJson { - a, _ := s.FilterOnVRPs(vrps) - b := s.AssertVRPs() - return append(a, b...) +type Logger interface { + Debugf(string, ...interface{}) + Printf(string, ...interface{}) + Warnf(string, ...interface{}) + Errorf(string, ...interface{}) + Infof(string, ...interface{}) } diff --git a/prefixfile/slurm_test.go b/prefixfile/slurm_test.go index 18afdad..aa86e15 100644 --- a/prefixfile/slurm_test.go +++ b/prefixfile/slurm_test.go @@ -97,3 +97,120 @@ func TestAssertVRPs(t *testing.T) { vrps := slurm.AssertVRPs() assert.Len(t, vrps, 3) } + +func TestFilterOnBSKs(t *testing.T) { + vrps := []BgpSecKeyJson{ + { + Asn: 65001, + Pubkey: []byte{ + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x80, 0x57, 0x23, 0x43, 0xf8, + 0x3f, 0xfc, 0xb0, 0x10, 0x7a, 0xb0, 0x07, 0xd8, 0xca, 0x69, 0xf8, 0x6b, 0x9c, 0xa0, 0x30, 0x06, + 0x05, 0xb8, 0x48, 0xa8, 0x3d, 0xf7, 0xc0, 0xd3, 0xec, 0x5f, 0x19, 0xc0, 0x19, 0xbf, 0xa6, 0xb5, + 0x9e, 0xd7, 0x42, 0xb5, 0x4e, 0xf4, 0x34, 0x3a, 0x52, 0x50, 0x12, 0x86, 0xd8, 0xa0, 0xe7, 0xe4, + 0x1f, 0x10, 0xaa, 0x53, 0xb4, 0x58, 0x22, 0xa9, 0xf8, 0x80, 0x15, + }, + Ski: "5d4250e2d81d4448d8a29efce91d29ff075ec9e2", + }, + { + Asn: 65003, + Pubkey: []byte{ + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xe0, 0x5c, 0x49, 0xaf, 0x49, + 0xf6, 0x6e, 0xec, 0x75, 0xb9, 0x7d, 0x44, 0xbe, 0x5f, 0x90, 0x5b, 0x06, 0x58, 0xbc, 0x86, 0x9d, + 0x3e, 0x32, 0xee, 0x15, 0x7d, 0xa6, 0xc6, 0xa2, 0xae, 0x00, 0x65, 0x21, 0x2a, 0x7a, 0xfb, 0x54, + 0xb2, 0xc3, 0x82, 0xb1, 0x3e, 0xfa, 0x5f, 0x69, 0xe5, 0xe1, 0xf6, 0x91, 0x64, 0xcd, 0x54, 0x03, + 0x76, 0xd8, 0x55, 0x14, 0xdd, 0xd6, 0xff, 0x44, 0xaa, 0x44, 0xdb, + }, + Ski: "be889b55d0b737397d75c49f485b858fa98ad11f", + }, + { + Asn: 65002, + Pubkey: []byte{ + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x86, 0xfe, 0x47, 0x10, 0x11, + 0xa2, 0xc5, 0x48, 0xca, 0x25, 0x39, 0x5e, 0x9e, 0xf7, 0x03, 0xd4, 0x0c, 0x72, 0x8b, 0x4e, 0xeb, + 0x15, 0xd5, 0x58, 0xd4, 0xa8, 0x4d, 0xe2, 0xf3, 0x0f, 0x63, 0x2e, 0x72, 0xd0, 0xcc, 0x7a, 0xcd, + 0xf6, 0xa2, 0x12, 0xa2, 0x4d, 0xdb, 0xb8, 0xca, 0xfe, 0x5e, 0xb5, 0xc4, 0x2d, 0xfa, 0x56, 0xc6, + 0x9e, 0xcd, 0xde, 0xde, 0x5c, 0x0b, 0x19, 0xd4, 0x01, 0x04, 0xb1, + }, + Ski: "510f485d29a29db7b515f9c478f8ed3cb7aa7d23", + }, + { + Asn: 65004, + Pubkey: []byte{ + 0x30, 0x59, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + Ski: "111b485d29a29db7b515f9c471e1ed3cb7bb7dee", + }, + { + Asn: 65005, + Pubkey: []byte{ + 0x30, 0x59, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + Ski: "111b485d29a29db7b515f9c471e1ed3cb7bb7dee", + }, + } + + asA, asB := uint32(65001), uint32(65005) + slurm := SlurmValidationOutputFilters{ + BgpsecFilters: []SlurmBGPsecFilter{ + { + SKI: []byte{0xbe, 0x88, 0x9b, 0x55, 0xd0, 0xb7, 0x37, 0x39, 0x7d, 0x75, 0xc4, 0x9f, 0x48, 0x5b, 0x85, 0x8f, 0xa9, 0x8a, 0xd1, 0x1f}, + }, + { + ASN: &asA, + }, + { + SKI: []byte{0x11, 0x1b, 0x48, 0x5d, 0x29, 0xa2, 0x9d, 0xb7, 0xb5, 0x15, 0xf9, 0xc4, 0x71, 0xe1, 0xed, 0x3c, 0xb7, 0xbb, 0x7d, 0xee}, + ASN: &asB, + }, + }, + } + added, removed := slurm.FilterOnBRKs(vrps) + assert.Len(t, added, 2) + assert.Len(t, removed, 3) + assert.Equal(t, "5d4250e2d81d4448d8a29efce91d29ff075ec9e2", removed[0].Ski) + assert.Equal(t, "be889b55d0b737397d75c49f485b858fa98ad11f", removed[1].Ski) + assert.Equal(t, "111b485d29a29db7b515f9c471e1ed3cb7bb7dee", removed[2].Ski) + assert.Equal(t, uint32(65005), removed[2].Asn) +} +func TestFilterOnVAPs(t *testing.T) { + vrps := []ASPAJson{ + { + CustomerAsid: 65001, + Providers: []uint32{65002, 65003}, + }, + { + CustomerAsid: 65002, + Providers: []uint32{65001, 65003}, + }, + } + + slurm := SlurmValidationOutputFilters{ + AspaFilters: []SlurmASPAFilter{ + { + Afi: "IPv4", + CustomerASid: 65001, + }, + { + Afi: "IPv6", + CustomerASid: 65002, + }, + }, + } + added, removed := slurm.FilterOnVAPs(vrps, false) + assert.Len(t, added, 1) + assert.Len(t, removed, 1) + assert.Equal(t, uint32(65001), removed[0].CustomerAsid) +}