diff --git a/cmd/stayrtr/stayrtr.go b/cmd/stayrtr/stayrtr.go index 93a8967..a934f67 100644 --- a/cmd/stayrtr/stayrtr.go +++ b/cmd/stayrtr/stayrtr.go @@ -180,24 +180,29 @@ func decodeJSON(data []byte) (*prefixfile.VRPList, error) { return &vrplistjson, err } -func checkPrefixLengths(prefix *net.IPNet, maxLength uint8) bool { +func isValidPrefixLength(prefix *net.IPNet, maxLength uint8) bool { plen, max := net.IPMask.Size(prefix.Mask) - if uint8(plen) > maxLength || maxLength > uint8(max) { + if plen == 0 || uint8(plen) > maxLength || maxLength > uint8(max) { log.Errorf("%s Maxlength wrong: %d - %d", prefix, plen, maxLength) return false } return true } +// processData will take a slice of prefix.VRPJson and attempt to convert them to a slice of rtr.VRP. +// Will check the following: +// 1 - The prefix is a valid prefix +// 2 - The ASN is a valid ASN +// 3 - The MaxLength is valid +// Will return a deduped slice, as well as total VRPs, IPv4 VRPs, and IPv6 VRPs func processData(vrplistjson []prefixfile.VRPJson) ([]rtr.VRP, int, int, int) { filterDuplicates := make(map[string]bool) - vrplist := make([]rtr.VRP, 0) - - var count int + var vrplist []rtr.VRP var countv4 int var countv6 int + for _, v := range vrplistjson { prefix, err := v.GetPrefix2() if err != nil { @@ -210,24 +215,22 @@ func processData(vrplistjson []prefixfile.VRPJson) ([]rtr.VRP, int, int, int) { continue } - if !checkPrefixLengths(prefix, v.Length) { + if !isValidPrefixLength(prefix, v.Length) { continue } if prefix.IP.To4() != nil { countv4++ - } else if prefix.IP.To16() != nil { + } else { countv6++ } - count++ key := fmt.Sprintf("%s,%d,%d", prefix, asn, v.Length) _, exists := filterDuplicates[key] - if !exists { - filterDuplicates[key] = true - } else { + if exists { continue } + filterDuplicates[key] = true vrp := rtr.VRP{ Prefix: *prefix, @@ -236,7 +239,7 @@ func processData(vrplistjson []prefixfile.VRPJson) ([]rtr.VRP, int, int, int) { } vrplist = append(vrplist, vrp) } - return vrplist, count, countv4, countv6 + return vrplist, countv4 + countv6, countv4, countv6 } type IdenticalFile struct { @@ -272,8 +275,7 @@ func (s *state) updateFromNewState() error { vrps, count, countv4, countv6 := processData(vrpsjson) - log.Infof("New update (%v uniques, %v total prefixes).", - len(vrps), count) + log.Infof("New update (%v uniques, %v total prefixes).", len(vrps), count) s.server.AddVRPs(vrps) diff --git a/cmd/stayrtr/stayrtr_test.go b/cmd/stayrtr/stayrtr_test.go new file mode 100644 index 0000000..5865a6b --- /dev/null +++ b/cmd/stayrtr/stayrtr_test.go @@ -0,0 +1,124 @@ +package main + +import ( + "net" + "testing" + + rtr "github.com/bgp/stayrtr/lib" + "github.com/bgp/stayrtr/prefixfile" + "github.com/google/go-cmp/cmp" +) + +func TestProcessData(t *testing.T) { + var stuff []prefixfile.VRPJson + stuff = append(stuff, + prefixfile.VRPJson{ + Prefix: "192.168.0.0/24", + Length: 24, + ASN: 123, + TA: "testrir", + }, + prefixfile.VRPJson{ + Prefix: "192.168.0.0/24", + Length: 24, + TA: "testrir", + }, + prefixfile.VRPJson{ + Prefix: "2001:db8::/32", + Length: 33, + ASN: "AS123", + TA: "testrir", + }, + prefixfile.VRPJson{ + Prefix: "192.168.1.0/24", + Length: 25, + ASN: 123, + TA: "testrir", + }, + // Invalid. Length is 0 + prefixfile.VRPJson{ + Prefix: "192.168.1.0/24", + Length: 0, + ASN: 123, + TA: "testrir", + }, + // Invalid. Length less than prefix length + prefixfile.VRPJson{ + Prefix: "192.168.1.0/24", + Length: 16, + ASN: 123, + TA: "testrir", + }, + // Invalid. 129 is invalid for IPv6 + prefixfile.VRPJson{ + Prefix: "2001:db8::/32", + Length: 129, + ASN: 123, + TA: "testrir", + }, + // Invalid. 33 is invalid for IPv4 + prefixfile.VRPJson{ + Prefix: "192.168.1.0/24", + Length: 33, + ASN: 123, + TA: "testrir", + }, + // Invalid. Not a prefix + prefixfile.VRPJson{ + Prefix: "192.168.1.0", + Length: 24, + ASN: 123, + TA: "testrir", + }, + // Invalid. Not a prefix + prefixfile.VRPJson{ + Prefix: "👻", + Length: 24, + ASN: 123, + TA: "testrir", + }, + // Invalid. Invalid ASN string + prefixfile.VRPJson{ + Prefix: "192.168.1.0/22", + Length: 22, + ASN: "ASN123", + TA: "testrir", + }, + ) + got, count, v4count, v6count := processData(stuff) + want := []rtr.VRP{ + { + Prefix: MustParseIPNet("192.168.0.0/24"), + MaxLen: 24, + ASN: 123, + }, + { + Prefix: MustParseIPNet("2001:db8::/32"), + MaxLen: 33, + ASN: 123, + }, + { + Prefix: MustParseIPNet("192.168.1.0/24"), + MaxLen: 25, + ASN: 123, + }, + } + if count != 3 || v4count != 2 || v6count != 1 { + t.Errorf("Wanted count = 3, v4count = 2, v6count = 1, but got %d, %d, %d", count, v4count, v6count) + } + + if !cmp.Equal(got, want) { + t.Errorf("Want (%+v), Got (%+v)", want, got) + } +} + +// MustParseIPNet is a test helper function to return a net.IPNet +// This should only be called in test code, and it'll panic on test set up +// if unable to parse. +func MustParseIPNet(prefix string) net.IPNet { + _, ipnet, err := net.ParseCIDR(prefix) + if err != nil { + panic(err) + } + return *ipnet +} diff --git a/prefixfile/slurm.go b/prefixfile/slurm.go index 168526f..03036b2 100644 --- a/prefixfile/slurm.go +++ b/prefixfile/slurm.go @@ -1,3 +1,5 @@ +// rfc8416 + package prefixfile import (