forked from hswaw/hscloud
131 lines
2.6 KiB
Go
131 lines
2.6 KiB
Go
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
|
|
}
|