| 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 |
| } |