forked from hswaw/hscloud
bgpwtf/cccampix/peeringdb: init
First pass at a proxy to expose PeeringDB data. Change-Id: I844973755473b3abc5d334586744004b86d1c3a3master
parent
57be3f7c40
commit
e653e6a620
|
@ -0,0 +1,22 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["main.go"],
|
||||||
|
importpath = "code.hackerspace.pl/hscloud/bgpwtf/cccampix/peeringdb",
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
deps = [
|
||||||
|
"//bgpwtf/cccampix/peeringdb/schema:go_default_library",
|
||||||
|
"//bgpwtf/cccampix/proto:go_default_library",
|
||||||
|
"//go/mirko:go_default_library",
|
||||||
|
"@com_github_golang_glog//:go_default_library",
|
||||||
|
"@org_golang_google_grpc//codes:go_default_library",
|
||||||
|
"@org_golang_google_grpc//status:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_binary(
|
||||||
|
name = "peeringdb",
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,41 @@
|
||||||
|
PeeringDBProxy
|
||||||
|
==============
|
||||||
|
|
||||||
|
Exposes PeeringDB data as gRPC.
|
||||||
|
|
||||||
|
API defined in [ix.proto](../proto/ix.proto).
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
$ bazel run //bgpwtf/cccampix/peeringdb:peeringdb -- -hspki_disable
|
||||||
|
$ grpcurl -plaintext -d '{"id": 2325}' 127.0.0.1:4200 ix.PeeringDBProxy.GetIXMembers
|
||||||
|
{
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"asn": 206924,
|
||||||
|
"ipv4": "185.230.223.195",
|
||||||
|
"name": "BENJOJONET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"asn": 207080,
|
||||||
|
"ipv4": "185.230.223.194",
|
||||||
|
"ipv6": "fe80::8651:4050:1715:bc4f",
|
||||||
|
"name": "Basil Fillan"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"asn": 39192,
|
||||||
|
"ipv4": "185.230.223.198",
|
||||||
|
"ipv6": "fe80::3:9192:1",
|
||||||
|
"name": "JackNet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"asn": 205271,
|
||||||
|
"ipv4": "185.230.223.199",
|
||||||
|
"ipv6": "fe80::20:5271:1",
|
||||||
|
"name": "Harry Reeder"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"code.hackerspace.pl/hscloud/bgpwtf/cccampix/peeringdb/schema"
|
||||||
|
pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
|
||||||
|
"code.hackerspace.pl/hscloud/go/mirko"
|
||||||
|
)
|
||||||
|
|
||||||
|
type service struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) GetIXMembers(ctx context.Context, req *pb.GetIXMembersRequest) (*pb.GetIXMembersResponse, error) {
|
||||||
|
if req.Id == 0 {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "IX id must be given")
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, get netixlans (membership info) for the given IX.
|
||||||
|
js := struct {
|
||||||
|
Data []schema.NetIXLan `json:"Data"`
|
||||||
|
}{}
|
||||||
|
err := schema.Get(ctx, &js, schema.NetIXLanInIXURL(req.Id))
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unavailable, "PeeringDB query error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build set of seen Nets/ASs.
|
||||||
|
netids := make(map[int64]bool)
|
||||||
|
for _, netixlan := range js.Data {
|
||||||
|
netids[netixlan.NetID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert set to unique list.
|
||||||
|
nets := make([]int64, len(netids))
|
||||||
|
i := 0
|
||||||
|
for id, _ := range netids {
|
||||||
|
nets[i] = id
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request information about nets/ASNs:
|
||||||
|
js2 := struct {
|
||||||
|
Data []schema.Net `json:"Data"`
|
||||||
|
}{}
|
||||||
|
err = schema.Get(ctx, &js2, schema.NetURLMulti(nets))
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unavailable, "PeeringDB query error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make map net id -> Net
|
||||||
|
netidsNet := make(map[int64]*schema.Net)
|
||||||
|
for _, net := range js2.Data {
|
||||||
|
net := net
|
||||||
|
netidsNet[net.ID] = &net
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build joined response.
|
||||||
|
|
||||||
|
res := &pb.GetIXMembersResponse{
|
||||||
|
Members: make([]*pb.GetIXMembersResponse_Member, len(js.Data)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, netixlan := range js.Data {
|
||||||
|
res.Members[i] = &pb.GetIXMembersResponse_Member{
|
||||||
|
Asn: netixlan.ASN,
|
||||||
|
Ipv4: netixlan.IPv4,
|
||||||
|
Ipv6: netixlan.IPv6,
|
||||||
|
Name: netidsNet[netixlan.NetID].Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
mi := mirko.New()
|
||||||
|
|
||||||
|
if err := mi.Listen(); err != nil {
|
||||||
|
glog.Exitf("Listen failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &service{}
|
||||||
|
pb.RegisterPeeringDBProxyServer(mi.GRPC(), s)
|
||||||
|
|
||||||
|
if err := mi.Serve(); err != nil {
|
||||||
|
glog.Exitf("Serve failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-mi.Done()
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"schema.go",
|
||||||
|
"urls.go",
|
||||||
|
],
|
||||||
|
importpath = "code.hackerspace.pl/hscloud/bgpwtf/cccampix/peeringdb/schema",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,34 @@
|
||||||
|
package schema
|
||||||
|
|
||||||
|
// Partial definition from https://www.peeringdb.com/apidocs/
|
||||||
|
|
||||||
|
type IX struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
OrgID int64 `json:"org_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
IXLanSet []IXLan `json:"ixlan_set"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IXLan struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
NetSet []Net `json:"net_set"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Net struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
OrgID int64 `json:"org_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ASN int64 `json:"asn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetIXLan struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
NetID int64 `json:"net_id"`
|
||||||
|
IXID int64 `json:"ix_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Speed int64 `json:"speed"`
|
||||||
|
ASN int64 `json:"asn"`
|
||||||
|
IPv4 string `json:"ipaddr4"`
|
||||||
|
IPv6 string `json:"ipaddr6"`
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IXURL(id int64) string {
|
||||||
|
return fmt.Sprintf("https://peeringdb.com/api/ix/%d.json?depth=4", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetIXLanInIXURL(id int64) string {
|
||||||
|
return fmt.Sprintf("https://peeringdb.com/api/netixlan?ix_id__in=%d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetURLMulti(ids []int64) string {
|
||||||
|
sid := make([]string, len(ids))
|
||||||
|
for i, id := range ids {
|
||||||
|
sid[i] = fmt.Sprintf("%d", id)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("https://peeringdb.com/api/net?id__in=%s", strings.Join(sid, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Get(ctx context.Context, obj interface{}, url string) error {
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("http.NewRequest(GET, %q): %v", url, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("User-Agent", "bgpwtf-cccampix-peeringdbproxy/1.0 (https://code.hackerspace.pl/hscloud/bgpwtf/cccampix/peeringdb)")
|
||||||
|
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
client := http.DefaultClient
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("client.Do(%v): %v", req, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
if res.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("got status code %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, &obj)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse response JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
|
||||||
|
|
||||||
|
proto_library(
|
||||||
|
name = "ix_proto",
|
||||||
|
srcs = ["ix.proto"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_proto_library(
|
||||||
|
name = "ix_go_proto",
|
||||||
|
compilers = ["@io_bazel_rules_go//proto:go_grpc"],
|
||||||
|
importpath = "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto",
|
||||||
|
proto = ":ix_proto",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
embed = [":ix_go_proto"],
|
||||||
|
importpath = "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,26 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
package ix;
|
||||||
|
|
||||||
|
message GetIXMembersRequest {
|
||||||
|
// IX ID from PeeringDB
|
||||||
|
int64 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetIXMembersResponse {
|
||||||
|
message Member {
|
||||||
|
int64 asn = 1;
|
||||||
|
// Per PeeringDB, at least one of the following two address families
|
||||||
|
// will be set.
|
||||||
|
string ipv4 = 2;
|
||||||
|
string ipv6 = 3;
|
||||||
|
// AS/network name.
|
||||||
|
string name = 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
repeated Member members = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service PeeringDBProxy {
|
||||||
|
// GetIXMembers returns information about membership of a given PeeringDB IX.
|
||||||
|
rpc GetIXMembers(GetIXMembersRequest) returns (GetIXMembersResponse);
|
||||||
|
}
|
Loading…
Reference in New Issue