blob: 3f16602e6e78327ca3dfea70e3cba73f82aa1a29 [file] [log] [blame]
package main
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"strconv"
"strings"
"time"
"code.hackerspace.pl/hscloud/bgpwtf/cccampix/verifier/model"
"github.com/golang/glog"
)
type rpki struct {
octorpki string
}
func newRPKI(octorpki string) (processor, error) {
return &rpki{
octorpki: octorpki,
}, nil
}
func (p *rpki) Name() string {
return "RPKI"
}
func (p *rpki) NextRun(now time.Time, lastFailed bool) time.Time {
return now.Add(1 * time.Minute)
}
type octorpkiRes struct {
Metadata struct {
Counts int64 `json:"counts"`
Generated int64 `json:"counts"`
Valid int64 `json:"counts"`
} `json:"metadata"`
ROAs []octorpkiROA `json:"roas"`
}
type octorpkiROA struct {
Prefix string `json:"prefix"`
MaxLength int64 `json:"maxLength"`
ASN string `json:"asn"`
TA string `json:"ta"`
}
func (p *rpki) RunAll(ctx context.Context, m model.Model) error {
peers, err := m.GetCheckablePeers(ctx)
if err != nil {
return err
}
wantASNs := make(map[string]bool)
for _, peer := range peers {
wantASNs[fmt.Sprintf("AS%d", peer.ASN)] = true
}
// Get RPKI data dump from OctoRPKI.
url := fmt.Sprintf("http://%s/output.json", p.octorpki)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("NewRequest(GET %q): %v", url, err)
}
req = req.WithContext(ctx)
client := http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("GET %q: %v", url, err)
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("GET %q: %v", url, err)
}
if strings.HasPrefix(string(data), "File not ready yet") {
return fmt.Errorf("OctoRPKI not yet ready")
}
var res octorpkiRes
if err := json.Unmarshal(data, &res); err != nil {
return fmt.Errorf("Could not decode OctoRPKI output: %v", err)
}
// Make list of prefixes we should honor.
prefixes := make(map[int64][]*model.AllowedPrefix)
for _, roa := range res.ROAs {
if !wantASNs[strings.ToUpper(roa.ASN)] {
continue
}
asn, err := strconv.ParseInt(roa.ASN[2:], 10, 64)
if err != nil {
glog.Errorf("Invalid ASN: %s %q", roa.ASN, roa.ASN)
continue
}
if _, ok := prefixes[asn]; !ok {
prefixes[asn] = []*model.AllowedPrefix{}
}
_, prefix, err := net.ParseCIDR(roa.Prefix)
if err != nil {
glog.Errorf("Invalid prefix: %s %q", roa.ASN, roa.Prefix)
continue
}
prefixes[asn] = append(prefixes[asn], &model.AllowedPrefix{
Prefix: *prefix,
MaxLength: roa.MaxLength,
TA: roa.TA,
})
}
for asn, p := range prefixes {
err := m.UpdateAllowedPrefixes(ctx, asn, p)
if err != nil {
return err
}
}
return nil
}