blob: 3f16602e6e78327ca3dfea70e3cba73f82aa1a29 [file] [log] [blame]
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +02001package main
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "io/ioutil"
8 "net"
9 "net/http"
10 "strconv"
11 "strings"
12 "time"
13
14 "code.hackerspace.pl/hscloud/bgpwtf/cccampix/verifier/model"
15 "github.com/golang/glog"
16)
17
18type rpki struct {
19 octorpki string
20}
21
22func newRPKI(octorpki string) (processor, error) {
23 return &rpki{
24 octorpki: octorpki,
25 }, nil
26}
27
28func (p *rpki) Name() string {
29 return "RPKI"
30}
31
Serge Bazanskiec71cb52019-08-22 18:13:13 +020032func (p *rpki) NextRun(now time.Time, lastFailed bool) time.Time {
Sergiusz Bazanski1fad2e52019-08-01 20:16:27 +020033 return now.Add(1 * time.Minute)
34}
35
36type octorpkiRes struct {
37 Metadata struct {
38 Counts int64 `json:"counts"`
39 Generated int64 `json:"counts"`
40 Valid int64 `json:"counts"`
41 } `json:"metadata"`
42
43 ROAs []octorpkiROA `json:"roas"`
44}
45
46type octorpkiROA struct {
47 Prefix string `json:"prefix"`
48 MaxLength int64 `json:"maxLength"`
49 ASN string `json:"asn"`
50 TA string `json:"ta"`
51}
52
53func (p *rpki) RunAll(ctx context.Context, m model.Model) error {
54 peers, err := m.GetCheckablePeers(ctx)
55 if err != nil {
56 return err
57 }
58
59 wantASNs := make(map[string]bool)
60 for _, peer := range peers {
61 wantASNs[fmt.Sprintf("AS%d", peer.ASN)] = true
62 }
63
64 // Get RPKI data dump from OctoRPKI.
65 url := fmt.Sprintf("http://%s/output.json", p.octorpki)
66 req, err := http.NewRequest("GET", url, nil)
67 if err != nil {
68 return fmt.Errorf("NewRequest(GET %q): %v", url, err)
69 }
70 req = req.WithContext(ctx)
71 client := http.Client{}
72 resp, err := client.Do(req)
73 if err != nil {
74 return fmt.Errorf("GET %q: %v", url, err)
75 }
76 defer resp.Body.Close()
77
78 data, err := ioutil.ReadAll(resp.Body)
79 if err != nil {
80 return fmt.Errorf("GET %q: %v", url, err)
81 }
82
83 if strings.HasPrefix(string(data), "File not ready yet") {
84 return fmt.Errorf("OctoRPKI not yet ready")
85 }
86
87 var res octorpkiRes
88 if err := json.Unmarshal(data, &res); err != nil {
89 return fmt.Errorf("Could not decode OctoRPKI output: %v", err)
90 }
91
92 // Make list of prefixes we should honor.
93 prefixes := make(map[int64][]*model.AllowedPrefix)
94 for _, roa := range res.ROAs {
95 if !wantASNs[strings.ToUpper(roa.ASN)] {
96 continue
97 }
98
99 asn, err := strconv.ParseInt(roa.ASN[2:], 10, 64)
100 if err != nil {
101 glog.Errorf("Invalid ASN: %s %q", roa.ASN, roa.ASN)
102 continue
103 }
104
105 if _, ok := prefixes[asn]; !ok {
106 prefixes[asn] = []*model.AllowedPrefix{}
107 }
108
109 _, prefix, err := net.ParseCIDR(roa.Prefix)
110 if err != nil {
111 glog.Errorf("Invalid prefix: %s %q", roa.ASN, roa.Prefix)
112 continue
113 }
114
115 prefixes[asn] = append(prefixes[asn], &model.AllowedPrefix{
116 Prefix: *prefix,
117 MaxLength: roa.MaxLength,
118 TA: roa.TA,
119 })
120 }
121
122 for asn, p := range prefixes {
123 err := m.UpdateAllowedPrefixes(ctx, asn, p)
124 if err != nil {
125 return err
126 }
127 }
128
129 return nil
130}