bgpwtf/cccampix: add IRR daemon
We add a small IRR service for getting a parsed RPSL from IRRs. For now,
we only support RIPE and ARIN, and only the following attributes:
- remarks
- import
- export
Since RPSL/RFC2622 is fucking insane, there is no guarantee that the
parser, especially the import/export parser, is correct. But it should
be good enough for our use. We even throw in some tests for good
measure.
$ grpcurl -format text -plaintext -d 'as: "26625"' 127.0.0.1:4200 ix.IRR.Query
source: SOURCE_ARIN
attributes: <
import: <
expressions: <
peering: "AS6083"
actions: "pref=10"
>
filter: "ANY"
>
>
attributes: <
import: <
expressions: <
peering: "AS12491"
actions: "pref=10"
>
filter: "ANY"
>
>
Change-Id: I8b240ffe2cd3553a25ce33dbd3917c0aef64e804
diff --git a/bgpwtf/cccampix/irr/main.go b/bgpwtf/cccampix/irr/main.go
new file mode 100644
index 0000000..1492e6c
--- /dev/null
+++ b/bgpwtf/cccampix/irr/main.go
@@ -0,0 +1,76 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "strconv"
+ "strings"
+
+ "github.com/golang/glog"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ "code.hackerspace.pl/hscloud/bgpwtf/cccampix/irr/provider"
+ pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
+ "code.hackerspace.pl/hscloud/go/mirko"
+)
+
+type service struct {
+ iana *provider.IANA
+ providers map[provider.IRR]provider.Provider
+}
+
+func main() {
+ flag.Parse()
+ mi := mirko.New()
+
+ if err := mi.Listen(); err != nil {
+ glog.Exitf("Listen failed: %v", err)
+ }
+
+ s := &service{
+ iana: provider.NewIANA(),
+ providers: map[provider.IRR]provider.Provider{
+ provider.IRR_RIPE: provider.NewRIPE(),
+ provider.IRR_ARIN: provider.NewARIN(),
+ },
+ }
+ pb.RegisterIRRServer(mi.GRPC(), s)
+
+ if err := mi.Serve(); err != nil {
+ glog.Exitf("Serve failed: %v", err)
+ }
+
+ <-mi.Done()
+}
+
+// Query returns parsed RPSL data for a given aut-num objects in any of the
+// supported IRRs.
+func (s *service) Query(ctx context.Context, req *pb.IRRQueryRequest) (*pb.IRRQueryResponse, error) {
+ if req.As == "" {
+ return nil, status.Error(codes.InvalidArgument, "as must be given")
+ }
+
+ req.As = strings.ToLower(req.As)
+ if strings.HasPrefix(req.As, "as") {
+ req.As = req.As[2:]
+ }
+
+ asn, err := strconv.ParseUint(req.As, 10, 64)
+ if err != nil {
+ return nil, status.Error(codes.InvalidArgument, "as is invalid")
+ }
+
+ irr, err := s.iana.Who(ctx, asn)
+ if err != nil {
+ return nil, status.Errorf(codes.Unavailable, "could not find AS block delegation from IANA: %v", err)
+ }
+
+ prov, ok := s.providers[irr]
+ if !ok {
+ return nil, status.Errorf(codes.NotFound, "AS belongs to unhandled IRR %s", irr.String())
+ }
+
+ res, err := prov.Query(ctx, asn)
+ return res, err
+}