bgpwtf/cccampix: draw the rest of the fucking owl

Change-Id: I49fd5906e69512e8f2d414f406edc0179522f225
diff --git a/bgpwtf/cccampix/verifier/processor_rpki.go b/bgpwtf/cccampix/verifier/processor_rpki.go
new file mode 100644
index 0000000..b00aed2
--- /dev/null
+++ b/bgpwtf/cccampix/verifier/processor_rpki.go
@@ -0,0 +1,130 @@
+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) 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
+}