forked from hswaw/hscloud
363 lines
9.3 KiB
Go
363 lines
9.3 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/hex"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
|
||
|
pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
|
||
|
"code.hackerspace.pl/hscloud/bgpwtf/cccampix/verifier/model"
|
||
|
"github.com/golang/glog"
|
||
|
"google.golang.org/grpc/codes"
|
||
|
"google.golang.org/grpc/status"
|
||
|
)
|
||
|
|
||
|
func (s *service) ProcessorStatus(ctx context.Context, req *pb.ProcessorStatusRequest) (*pb.ProcessorStatusResponse, error) {
|
||
|
s.processorsMu.RLock()
|
||
|
defer s.processorsMu.RUnlock()
|
||
|
|
||
|
res := &pb.ProcessorStatusResponse{
|
||
|
Processors: make([]*pb.ProcessorStatusResponse_Processor, len(s.processors)),
|
||
|
}
|
||
|
|
||
|
i := 0
|
||
|
for _, p := range s.processors {
|
||
|
res.Processors[i] = &pb.ProcessorStatusResponse_Processor{
|
||
|
Name: p.name,
|
||
|
Status: pb.ProcessorStatusResponse_Processor_STATUS_OK,
|
||
|
LastRun: 0,
|
||
|
NextRun: 0,
|
||
|
}
|
||
|
|
||
|
if p.lastRun != nil {
|
||
|
res.Processors[i].LastRun = p.lastRun.UnixNano()
|
||
|
res.Processors[i].NextRun = p.p.NextRun(*p.lastRun, p.lastErr != nil).UnixNano()
|
||
|
}
|
||
|
|
||
|
if p.lastErr != nil {
|
||
|
res.Processors[i].Status = pb.ProcessorStatusResponse_Processor_STATUS_ERROR
|
||
|
}
|
||
|
|
||
|
i += 1
|
||
|
}
|
||
|
return res, nil
|
||
|
}
|
||
|
|
||
|
func (s *service) PeerSummary(req *pb.PeerSummaryRequest, stream pb.Verifier_PeerSummaryServer) error {
|
||
|
peers, err := s.model.GetCheckablePeers(stream.Context())
|
||
|
if err != nil {
|
||
|
glog.Errorf("model.GetCheckablePeers: %v", err)
|
||
|
return status.Error(codes.Unavailable, "model error")
|
||
|
}
|
||
|
|
||
|
asns := make([]int64, len(peers))
|
||
|
asnToRes := make(map[int64]*pb.PeerSummaryResponse)
|
||
|
|
||
|
for i, peer := range peers {
|
||
|
routers := make([]*pb.PeeringDBMember_Router, len(peer.Routers))
|
||
|
for i, router := range peer.Routers {
|
||
|
routers[i] = &pb.PeeringDBMember_Router{}
|
||
|
if router.V4 != nil {
|
||
|
routers[i].Ipv4 = router.V4.String()
|
||
|
}
|
||
|
if router.V6 != nil {
|
||
|
routers[i].Ipv6 = router.V6.String()
|
||
|
}
|
||
|
}
|
||
|
p := &pb.PeeringDBMember{
|
||
|
Asn: peer.ASN,
|
||
|
Name: peer.Name,
|
||
|
Routers: routers,
|
||
|
}
|
||
|
res := &pb.PeerSummaryResponse{
|
||
|
PeeringdbInfo: p,
|
||
|
CheckStatus: pb.PeerSummaryResponse_STATUS_OK,
|
||
|
}
|
||
|
asnToRes[peer.ASN] = res
|
||
|
asns[i] = peer.ASN
|
||
|
}
|
||
|
|
||
|
checkres, err := s.model.GetPeerCheckResults(stream.Context(), asns)
|
||
|
if err != nil {
|
||
|
glog.Errorf("GetPeerCheckResults(%v): %v", asns, err)
|
||
|
for _, res := range asnToRes {
|
||
|
res.CheckStatus = pb.PeerSummaryResponse_STATUS_UNKNOWN
|
||
|
}
|
||
|
} else {
|
||
|
passedChecks := make(map[int64]map[string]bool)
|
||
|
for _, c := range checkres {
|
||
|
if _, ok := passedChecks[c.PeerASN]; !ok {
|
||
|
passedChecks[c.PeerASN] = make(map[string]bool)
|
||
|
}
|
||
|
passedChecks[c.PeerASN][c.CheckName] = c.Status == model.PeerCheckStatus_Okay
|
||
|
}
|
||
|
|
||
|
for asn, checks := range passedChecks {
|
||
|
for _, required := range s.requiredChecks {
|
||
|
if !checks[required] {
|
||
|
asnToRes[asn].CheckStatus = pb.PeerSummaryResponse_STATUS_FAILED
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for _, res := range asnToRes {
|
||
|
if err := stream.Send(res); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *service) PeerDetails(ctx context.Context, req *pb.PeerDetailsRequest) (*pb.PeerDetailsResponse, error) {
|
||
|
if req.Asn <= 0 {
|
||
|
return nil, status.Error(codes.InvalidArgument, "asn must be set")
|
||
|
}
|
||
|
|
||
|
res := &pb.PeerDetailsResponse{}
|
||
|
|
||
|
peeringdb, err := s.model.GetPeeringDBPeer(ctx, req.Asn)
|
||
|
if err != nil {
|
||
|
glog.Errorf("GetPeeringDBPeer(%v): %v", req.Asn, err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not get allowed prefixes")
|
||
|
}
|
||
|
|
||
|
if peeringdb.Asn != req.Asn {
|
||
|
return nil, status.Error(codes.NotFound, "no such ASN")
|
||
|
}
|
||
|
|
||
|
res.PeeringdbInfo = peeringdb
|
||
|
|
||
|
checkres, err := s.model.GetPeerCheckResults(ctx, []int64{req.Asn})
|
||
|
if err != nil {
|
||
|
glog.Errorf("GetPeerCheckResults(%v): %v", req.Asn, err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not get check results")
|
||
|
}
|
||
|
|
||
|
res.Checks = make([]*pb.PeerDetailsResponse_Check, len(checkres))
|
||
|
for i, check := range checkres {
|
||
|
status := pb.PeerDetailsResponse_Check_STATUS_INVALID
|
||
|
switch check.Status {
|
||
|
case model.PeerCheckStatus_Okay:
|
||
|
status = pb.PeerDetailsResponse_Check_STATUS_OK
|
||
|
case model.PeerCheckStatus_SoftFailed:
|
||
|
status = pb.PeerDetailsResponse_Check_STATUS_OK
|
||
|
case model.PeerCheckStatus_Failed:
|
||
|
status = pb.PeerDetailsResponse_Check_STATUS_FAILED
|
||
|
}
|
||
|
res.Checks[i] = &pb.PeerDetailsResponse_Check{
|
||
|
Name: check.CheckName,
|
||
|
Status: status,
|
||
|
Time: check.Time.UnixNano(),
|
||
|
Msg: check.Message,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
prefixes, err := s.model.GetAllowedPrefixes(ctx, req.Asn)
|
||
|
if err != nil {
|
||
|
glog.Errorf("GetAllowedPrefixes(%v): %v", req.Asn, err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not get allowed prefixes")
|
||
|
}
|
||
|
|
||
|
res.AllowedPrefixes = make([]*pb.PeerDetailsResponse_AllowedPrefix, len(prefixes))
|
||
|
for i, prefix := range prefixes {
|
||
|
res.AllowedPrefixes[i] = &pb.PeerDetailsResponse_AllowedPrefix{
|
||
|
Prefix: prefix.Prefix.String(),
|
||
|
MaxLength: prefix.MaxLength,
|
||
|
Ta: prefix.TA,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return res, nil
|
||
|
}
|
||
|
|
||
|
func (s *service) RouterHeartbeat(ctx context.Context, req *pb.RouterHeartbeatRequest) (*pb.RouterHeartbeatResponse, error) {
|
||
|
if req.Name == "" {
|
||
|
return nil, status.Error(codes.InvalidArgument, "name must be set")
|
||
|
}
|
||
|
|
||
|
pcfgM := make(map[string]*model.PeerConfiguration)
|
||
|
pcfgs, err := s.model.GetPeerConfiguration(ctx)
|
||
|
if err != nil {
|
||
|
glog.Errorf("GetPeerConfiguration: %v", err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not get peer configs")
|
||
|
}
|
||
|
|
||
|
for _, pcfg := range pcfgs {
|
||
|
ask := fmt.Sprintf("AS%d", pcfg.Peer.ASN)
|
||
|
pcfgM[ask] = pcfg
|
||
|
}
|
||
|
|
||
|
peers, err := s.model.GetCheckablePeers(ctx)
|
||
|
if err != nil {
|
||
|
glog.Errorf("GetChecablePeers: %v", err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not get peers")
|
||
|
}
|
||
|
|
||
|
asconfs := make(map[string]*pb.RouterHeartbeatResponse_ASConfig)
|
||
|
|
||
|
for _, peer := range peers {
|
||
|
as := fmt.Sprintf("AS%d", peer.ASN)
|
||
|
|
||
|
pcfg, ok := pcfgM[as]
|
||
|
if !ok {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
asconfs[as] = &pb.RouterHeartbeatResponse_ASConfig{
|
||
|
Asn: peer.ASN,
|
||
|
Routers: make([]*pb.RouterHeartbeatResponse_ASConfig_Router, len(pcfg.Peer.Routers)),
|
||
|
Prefixes: []*pb.RouterHeartbeatResponse_ASConfig_AllowedPrefix{},
|
||
|
}
|
||
|
|
||
|
glog.Infof("%+v", pcfg.Peer.Routers)
|
||
|
for i, r := range pcfg.Peer.Routers {
|
||
|
ipv6 := ""
|
||
|
if r.V6 != nil {
|
||
|
ipv6 = r.V6.String()
|
||
|
}
|
||
|
ipv4 := ""
|
||
|
if r.V4 != nil {
|
||
|
ipv4 = r.V4.String()
|
||
|
}
|
||
|
asconfs[as].Routers[i] = &pb.RouterHeartbeatResponse_ASConfig_Router{
|
||
|
Ipv6: ipv6,
|
||
|
Ipv4: ipv4,
|
||
|
Password: r.Config.BGPSecret,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
prefixes, err := s.model.GetAllowedPrefixes(ctx, peer.ASN)
|
||
|
if err != nil {
|
||
|
glog.Errorf("GetAllowedPrefixes(_, %d): %v", peer.ASN, err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not get peer prefixes")
|
||
|
}
|
||
|
|
||
|
for _, prefix := range prefixes {
|
||
|
asconfs[as].Prefixes = append(asconfs[as].Prefixes, &pb.RouterHeartbeatResponse_ASConfig_AllowedPrefix{
|
||
|
Prefix: prefix.Prefix.String(),
|
||
|
MaxLength: prefix.MaxLength,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
res := &pb.RouterHeartbeatResponse{
|
||
|
AsConfigs: make([]*pb.RouterHeartbeatResponse_ASConfig, len(asconfs)),
|
||
|
}
|
||
|
|
||
|
i := 0
|
||
|
for _, asconf := range asconfs {
|
||
|
res.AsConfigs[i] = asconf
|
||
|
i += 1
|
||
|
}
|
||
|
|
||
|
return res, nil
|
||
|
}
|
||
|
|
||
|
func (s *service) PeerSecrets(ctx context.Context, req *pb.PeerSecretsRequest) (*pb.PeerSecretsResponse, error) {
|
||
|
if req.Asn <= 0 {
|
||
|
return nil, status.Error(codes.InvalidArgument, "asn must be set")
|
||
|
}
|
||
|
pcrs, err := s.model.GetPeerConfiguration(ctx)
|
||
|
if err != nil {
|
||
|
glog.Errorf("GetPeerConfiguration: %v", err)
|
||
|
return nil, status.Error(codes.Unavailable, "error when retrieving peer configs")
|
||
|
}
|
||
|
|
||
|
var pcr *model.PeerConfiguration
|
||
|
for _, p := range pcrs {
|
||
|
if p.Peer.ASN == req.Asn {
|
||
|
pcr = p
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if pcr == nil {
|
||
|
return nil, status.Error(codes.NotFound, "no such ASN")
|
||
|
}
|
||
|
|
||
|
plain := fmt.Sprintf(`
|
||
|
Hello AS %d!
|
||
|
|
||
|
Here are your config settings:
|
||
|
`, req.Asn)
|
||
|
|
||
|
for _, router := range pcr.Peer.Routers {
|
||
|
if router.V4 != nil {
|
||
|
plain += fmt.Sprintf(`
|
||
|
our addresses: 185.236.243.5 (rs1), 185.236.243.6 (rs2)
|
||
|
our asn: 208521
|
||
|
your address: %s
|
||
|
your asn: %d
|
||
|
bgp secret: %s
|
||
|
`, router.V4.String(), req.Asn, router.Config.BGPSecret)
|
||
|
}
|
||
|
if router.V6 != nil {
|
||
|
plain += fmt.Sprintf(`
|
||
|
our addresses: 2a0d:eb02:4242:4242::5 (rs1), 2a0d:eb02:4242:4242::6 (rs2)
|
||
|
our asn: 208521
|
||
|
your address: %s
|
||
|
your asn: %d
|
||
|
bgp secret: %s
|
||
|
`, router.V6.String(), req.Asn, router.Config.BGPSecret)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
plain += `
|
||
|
Happy exchanging!
|
||
|
bgp.wtf (DECT: 4735)
|
||
|
`
|
||
|
|
||
|
key, err := s.model.GetPeerPGPKey(ctx, req.Asn)
|
||
|
if err != nil {
|
||
|
glog.Errorf("GetPeerPGPKey: %v", err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not get pgp key")
|
||
|
}
|
||
|
|
||
|
plainB := []byte(plain)
|
||
|
|
||
|
stream, err := s.pgp.Encrypt(ctx)
|
||
|
if err != nil {
|
||
|
glog.Errorf("Encrypt: %v", err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not encrypt")
|
||
|
}
|
||
|
|
||
|
fingerprint, err := hex.DecodeString(key.Fingerprint)
|
||
|
if err != nil {
|
||
|
glog.Errorf("Invalid fingerprint %q: %v", key.Fingerprint, err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not encrypt")
|
||
|
}
|
||
|
|
||
|
reqE := &pb.EncryptRequest{
|
||
|
Data: plainB,
|
||
|
Info: pb.EncryptRequest_CHUNK_LAST,
|
||
|
Fingerprint: fingerprint,
|
||
|
}
|
||
|
|
||
|
if err := stream.Send(reqE); err != nil {
|
||
|
glog.Errorf("Encrypt.Send: %v", err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not encrypt")
|
||
|
}
|
||
|
stream.CloseSend()
|
||
|
|
||
|
cipher := []byte{}
|
||
|
for {
|
||
|
in, err := stream.Recv()
|
||
|
if err == io.EOF {
|
||
|
break
|
||
|
}
|
||
|
if err != nil {
|
||
|
glog.Errorf("Encrypt.Recv: %v", err)
|
||
|
return nil, status.Error(codes.Unavailable, "could not encrypt")
|
||
|
}
|
||
|
cipher = append(cipher, in.Data...)
|
||
|
}
|
||
|
|
||
|
return &pb.PeerSecretsResponse{
|
||
|
PgpData: cipher,
|
||
|
}, nil
|
||
|
}
|