Implment ASPA as defined in draft-ietf-sidrops-8210bis-10

Tag: https://github.com/bgp/stayrtr/issues/79
This commit is contained in:
Ben Cartwright-Cox
2023-02-22 17:18:46 +00:00
parent 3b73956a9c
commit 187410d9b6
8 changed files with 184 additions and 17 deletions
+2
View File
@@ -39,3 +39,5 @@ test/scenario_test/nosetest*.xml
# IDE workspace
.idea
.vscode
output.json
+36 -1
View File
@@ -38,6 +38,8 @@ var (
Serial = flag.Int("serial.value", 0, "Serial number")
Session = flag.Int("session.id", 0, "Session ID")
FlagVersion = flag.Int("rtr.version", 1, "What RTR version you want to use, Version 2 is draft-ietf-sidrops-8210bis-10")
ConnType = flag.String("type", "plain", "Type of connection: plain, tls or ssh")
ValidateCert = flag.Bool("tls.validate", true, "Validate TLS")
@@ -109,6 +111,29 @@ func (c *Client) HandlePDU(cs *rtr.ClientSession, pdu rtr.PDU) {
}
c.Data.BgpSecKeys = append(c.Data.BgpSecKeys, rj)
if *LogDataPDU {
log.Debugf("Received: %v", pdu)
}
case *rtr.PDUASPA:
if c.Data.ASPA == nil {
c.Data.ASPA = &prefixfile.ProviderAuthorizationsJson{
IPv4: make([]prefixfile.ASPAJson, 0),
IPv6: make([]prefixfile.ASPAJson, 0),
}
}
aj := prefixfile.ASPAJson{
CustomerAsid: pdu.CustomerASNumber,
Providers: pdu.ProviderASNumbers,
}
switch pdu.AFIFlags {
case rtr.AFI_IPv4:
c.Data.ASPA.IPv4 = append(c.Data.ASPA.IPv4, aj)
case rtr.AFI_IPv6:
c.Data.ASPA.IPv6 = append(c.Data.ASPA.IPv6, aj)
}
if *LogDataPDU {
log.Debugf("Received: %v", pdu)
}
@@ -146,11 +171,21 @@ func main() {
os.Exit(0)
}
targetVersion := rtr.PROTOCOL_VERSION_0
if *FlagVersion > 2 {
log.Fatalf("Invalid RTR Version provided, the highest version this release supports is 2")
}
if *FlagVersion == 1 {
targetVersion = rtr.PROTOCOL_VERSION_1
} else if *FlagVersion == 2 {
targetVersion = rtr.PROTOCOL_VERSION_2
}
lvl, _ := log.ParseLevel(*LogLevel)
log.SetLevel(lvl)
cc := rtr.ClientConfiguration{
ProtocolVersion: rtr.PROTOCOL_VERSION_1,
ProtocolVersion: uint8(targetVersion),
Log: log.StandardLogger(),
}
+49 -10
View File
@@ -14,6 +14,7 @@ import (
"net/http"
"os"
"os/signal"
"sort"
"strings"
"sync"
"syscall"
@@ -52,7 +53,7 @@ var (
ExportPath = flag.String("export.path", "/rpki.json", "Export path")
RTRVersion = flag.Int("protocol", 1, "RTR protocol version")
RTRVersion = flag.Int("protocol", 1, "RTR protocol version. Version 2 is draft-ietf-sidrops-8210bis-10")
RefreshRTR = flag.Int("rtr.refresh", 3600, "Refresh interval")
RetryRTR = flag.Int("rtr.retry", 600, "Retry interval")
ExpireRTR = flag.Int("rtr.expire", 7200, "Expire interval")
@@ -146,6 +147,7 @@ var (
protoverToLib = map[int]uint8{
0: rtr.PROTOCOL_VERSION_0,
1: rtr.PROTOCOL_VERSION_1,
2: rtr.PROTOCOL_VERSION_2,
}
)
@@ -195,8 +197,11 @@ func isValidPrefixLength(prefix *net.IPNet, maxLength uint8) bool {
// 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, bsklistjson []prefixfile.BgpSecKeyJson) ([]rtr.VRP, []rtr.BgpsecKey, int, int, int) {
// Will return a deduped slice, as well as total VRPs, IPv4 VRPs, IPv6 VRPs, BGPsec Keys and ASPA records
func processData(vrplistjson []prefixfile.VRPJson,
bsklistjson []prefixfile.BgpSecKeyJson,
aspajson *prefixfile.ProviderAuthorizationsJson) /*Export*/ ([]rtr.VRP, []rtr.BgpsecKey, []rtr.ASPARecord, int, int, int) {
//
filterDuplicates := make(map[string]bool)
// It may be tempting to change this to a simple time.Since() but that will
@@ -207,6 +212,7 @@ func processData(vrplistjson []prefixfile.VRPJson, bsklistjson []prefixfile.BgpS
var vrplist []rtr.VRP
var bsklist = make([]rtr.BgpsecKey, 0)
var aspalist = make([]rtr.ASPARecord, 0)
var countv4 int
var countv6 int
@@ -276,7 +282,35 @@ func processData(vrplistjson []prefixfile.VRPJson, bsklistjson []prefixfile.BgpS
})
}
return vrplist, bsklist, countv4 + countv6, countv4, countv6
if aspajson != nil {
aspalist = handleASPAList(aspajson.IPv4, NowUnix, aspalist, rtr.AFI_IPv4)
aspalist = handleASPAList(aspajson.IPv6, NowUnix, aspalist, rtr.AFI_IPv6)
}
return vrplist, bsklist, aspalist, countv4 + countv6, countv4, countv6
}
func handleASPAList(list []prefixfile.ASPAJson, NowUnix int64, aspalist []rtr.ASPARecord, AFI uint8) []rtr.ASPARecord {
for _, v := range list {
if v.Expires != nil {
if int(NowUnix) > int(*v.Expires) {
continue
}
}
// Ensure that these are sorted, otherwise they
// don't has right.
sort.Slice(v.Providers, func(i, j int) bool {
return v.Providers[i] < v.Providers[j]
})
aspalist = append(aspalist, rtr.ASPARecord{
AFI: AFI,
CustomerASN: v.CustomerAsid,
Providers: v.Providers,
})
}
return aspalist
}
type IdenticalFile struct {
@@ -317,10 +351,10 @@ func (s *state) updateFromNewState() error {
vrpsjson = append(kept, asserted...)
}
vrps, bsks, count, countv4, countv6 := processData(vrpsjson, s.lastdata.BgpSecKeys)
vrps, bsks, aspas, count, countv4, countv6 := processData(vrpsjson, s.lastdata.BgpSecKeys, s.lastdata.ASPA)
log.Infof("New update (%v uniques, %v total prefixes).", len(vrps), count)
return s.applyUpdateFromNewState(vrps, bsks, sessid, vrpsjson, s.lastdata.BgpSecKeys, countv4, countv6)
return s.applyUpdateFromNewState(vrps, bsks, aspas, sessid, vrpsjson, s.lastdata.BgpSecKeys, s.lastdata.ASPA, countv4, countv6)
}
// Update the state based on the currently loaded files
@@ -351,16 +385,17 @@ func (s *state) reloadFromCurrentState() error {
vrpsjson = append(kept, asserted...)
}
vrps, bsks, count, countv4, countv6 := processData(vrpsjson, s.lastdata.BgpSecKeys)
vrps, bsks, aspas, count, countv4, countv6 := processData(vrpsjson, s.lastdata.BgpSecKeys, s.lastdata.ASPA)
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, bsks, sessid, vrpsjson, s.lastdata.BgpSecKeys, countv4, countv6)
return s.applyUpdateFromNewState(vrps, bsks, aspas, sessid, vrpsjson, s.lastdata.BgpSecKeys, s.lastdata.ASPA, countv4, countv6)
}
return nil
}
func (s *state) applyUpdateFromNewState(vrps []rtr.VRP, bsks []rtr.BgpsecKey, sessid uint16,
vrpsjson []prefixfile.VRPJson, bsksjson []prefixfile.BgpSecKeyJson,
func (s *state) applyUpdateFromNewState(vrps []rtr.VRP, bsks []rtr.BgpsecKey, aspas []rtr.ASPARecord,
sessid uint16,
vrpsjson []prefixfile.VRPJson, bsksjson []prefixfile.BgpSecKeyJson, aspajson *prefixfile.ProviderAuthorizationsJson,
countv4 int, countv6 int) error {
SDs := make([]rtr.SendableData, 0)
@@ -370,6 +405,9 @@ func (s *state) applyUpdateFromNewState(vrps []rtr.VRP, bsks []rtr.BgpsecKey, se
for _, v := range bsks {
SDs = append(SDs, v.Copy())
}
for _, v := range aspas {
SDs = append(SDs, v.Copy())
}
s.server.AddData(SDs)
serial, _ := s.server.GetCurrentSerial(sessid)
@@ -387,6 +425,7 @@ func (s *state) applyUpdateFromNewState(vrps []rtr.VRP, bsks []rtr.BgpsecKey, se
},
Data: vrpsjson,
BgpSecKeys: bsksjson,
ASPA: aspajson,
}
s.lockJson.Unlock()
+1 -1
View File
@@ -100,7 +100,7 @@ func TestProcessData(t *testing.T) {
Expires: &ExpiredTime,
},
)
got, _, count, v4count, v6count := processData(stuff, nil)
got, _, _, count, v4count, v6count := processData(stuff, nil, nil)
want := []rtr.VRP{
{
Prefix: mustParseIPNet("192.168.0.0/24"),
+14
View File
@@ -119,7 +119,21 @@ func (c *ClientSession) StartRW(rd io.Reader, wr io.Writer) error {
if c.version == PROTOCOL_VERSION_1 && dec.GetVersion() == PROTOCOL_VERSION_0 {
if c.log != nil {
c.log.Infof("Downgrading to version 0")
}
c.version = PROTOCOL_VERSION_0
}
if c.version == PROTOCOL_VERSION_2 {
downgraded := false
switch dec.GetVersion() {
case PROTOCOL_VERSION_0:
c.version = PROTOCOL_VERSION_0
downgraded = true
case PROTOCOL_VERSION_1:
c.version = PROTOCOL_VERSION_1
downgraded = true
}
if c.log != nil && downgraded {
c.log.Infof("Downgrading to version %d", c.version)
}
}
+62 -1
View File
@@ -772,7 +772,7 @@ func (c *Client) SetDisableVersionCheck(disableCheck bool) {
}
func (c *Client) checkVersion(newversion uint8) {
if (!c.versionset || newversion == c.version) && (newversion == PROTOCOL_VERSION_1 || newversion == PROTOCOL_VERSION_0) {
if (!c.versionset || newversion == c.version) && (newversion == PROTOCOL_VERSION_2 || newversion == PROTOCOL_VERSION_1 || newversion == PROTOCOL_VERSION_0) {
c.SetVersion(newversion)
} else {
if c.log != nil {
@@ -972,6 +972,53 @@ func (bsk *BgpsecKey) GetFlag() uint8 {
return bsk.Flags
}
type ASPARecord struct {
Flags uint8
AFI uint8
CustomerASN uint32
Providers []uint32
}
func (ASPAr *ASPARecord) Type() string {
return "ASPA"
}
func (ASPAr *ASPARecord) String() string {
return fmt.Sprintf("ASPA AS%v -> AFI %d, Providers: %v", ASPAr.CustomerASN, ASPAr.AFI, ASPAr.Providers)
}
func (ASPAr *ASPARecord) HashKey() string {
return fmt.Sprintf("%v-%x-%v", ASPAr.CustomerASN, ASPAr.AFI, ASPAr.Providers)
}
func (r1 *ASPARecord) Equals(r2 SendableData) bool {
if r1.Type() != r2.Type() {
return false
}
r2True := r2.(*ASPARecord)
return r1.CustomerASN == r2True.CustomerASN && fmt.Sprint(r1.Providers) == fmt.Sprint(r2True.Providers) /*This could be made faster*/
}
func (ASPAr *ASPARecord) Copy() SendableData {
cop := ASPARecord{
CustomerASN: ASPAr.CustomerASN,
AFI: ASPAr.AFI,
Flags: ASPAr.Flags,
Providers: make([]uint32, 0),
}
cop.Providers = append(cop.Providers, ASPAr.Providers...)
return &cop
}
func (ASPAr *ASPARecord) SetFlag(f uint8) {
ASPAr.Flags = f
}
func (ASPAr *ASPARecord) GetFlag() uint8 {
return ASPAr.Flags
}
func (c *Client) SendSDs(sessionId uint16, serialNumber uint32, data []SendableData) {
pduBegin := &PDUCacheResponse{
SessionId: sessionId,
@@ -1062,6 +1109,20 @@ func (c *Client) SendData(sd SendableData) {
SubjectPublicKeyInfo: t.Pubkey,
}
c.SendPDU(pdu)
case *ASPARecord:
if c.version < 2 || c.dontSendBGPsecKeys {
return
}
pdu := &PDUASPA{
Version: c.version,
Flags: t.Flags,
AFIFlags: t.AFI,
ProviderASCount: uint16(len(t.Providers)),
CustomerASNumber: t.CustomerASN,
ProviderASNumbers: t.Providers,
}
c.SendPDU(pdu)
}
}
+4 -1
View File
@@ -49,6 +49,9 @@ const (
PDU_ERROR_WITHDRAWUNKNOWN = 6
PDU_ERROR_DUPANNOUNCE = 7
AFI_IPv4 = uint8(0)
AFI_IPv6 = uint8(1)
TYPE_UNKNOWN = iota
TYPE_PLAIN
TYPE_TLS
@@ -376,7 +379,7 @@ func (pdu *PDUEndOfData) Write(wr io.Writer) {
if pdu.Version == PROTOCOL_VERSION_0 {
binary.Write(wr, binary.BigEndian, uint32(12))
binary.Write(wr, binary.BigEndian, pdu.SerialNumber)
} else if pdu.Version == PROTOCOL_VERSION_1 {
} else if pdu.Version == PROTOCOL_VERSION_1 || pdu.Version == PROTOCOL_VERSION_2 {
binary.Write(wr, binary.BigEndian, uint32(24))
binary.Write(wr, binary.BigEndian, pdu.SerialNumber)
binary.Write(wr, binary.BigEndian, pdu.RefreshInterval)
+16 -3
View File
@@ -30,9 +30,10 @@ func (md MetaData) GetBuildTime() time.Time {
}
type VRPList struct {
Metadata MetaData `json:"metadata,omitempty"`
Data []VRPJson `json:"roas"` // for historical reasons this is called 'roas', but should've been called vrps
BgpSecKeys []BgpSecKeyJson `json:"bgpsec_keys,omitempty"`
Metadata MetaData `json:"metadata,omitempty"`
Data []VRPJson `json:"roas"` // for historical reasons this is called 'roas', but should've been called vrps
BgpSecKeys []BgpSecKeyJson `json:"bgpsec_keys,omitempty"`
ASPA *ProviderAuthorizationsJson `json:"provider_authorizations,omitempty"`
}
type BgpSecKeyJson struct {
@@ -48,6 +49,18 @@ type BgpSecKeyJson struct {
Ski string `json:"ski"`
}
// ASPA
type ProviderAuthorizationsJson struct {
IPv4 []ASPAJson `json:"ipv4"`
IPv6 []ASPAJson `json:"ipv6"`
}
type ASPAJson struct {
CustomerAsid uint32 `json:"customer_asid"`
Expires *uint32 `json:"expires,omitempty"`
Providers []uint32 `json:"providers"`
}
func (vrp *VRPJson) GetASN2() (uint32, error) {
switch asnc := vrp.ASN.(type) {
case string: