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), 185.236.243.7 (upstream) 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), 2a0d:eb02:4242:4242::7 (upstream) 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 }