Serge Bazanski | ec71cb5 | 2019-08-22 18:13:13 +0200 | [diff] [blame] | 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "encoding/hex" |
| 6 | "fmt" |
| 7 | "io" |
| 8 | |
| 9 | pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto" |
| 10 | "code.hackerspace.pl/hscloud/bgpwtf/cccampix/verifier/model" |
| 11 | "github.com/golang/glog" |
| 12 | "google.golang.org/grpc/codes" |
| 13 | "google.golang.org/grpc/status" |
| 14 | ) |
| 15 | |
| 16 | func (s *service) ProcessorStatus(ctx context.Context, req *pb.ProcessorStatusRequest) (*pb.ProcessorStatusResponse, error) { |
| 17 | s.processorsMu.RLock() |
| 18 | defer s.processorsMu.RUnlock() |
| 19 | |
| 20 | res := &pb.ProcessorStatusResponse{ |
| 21 | Processors: make([]*pb.ProcessorStatusResponse_Processor, len(s.processors)), |
| 22 | } |
| 23 | |
| 24 | i := 0 |
| 25 | for _, p := range s.processors { |
| 26 | res.Processors[i] = &pb.ProcessorStatusResponse_Processor{ |
| 27 | Name: p.name, |
| 28 | Status: pb.ProcessorStatusResponse_Processor_STATUS_OK, |
| 29 | LastRun: 0, |
| 30 | NextRun: 0, |
| 31 | } |
| 32 | |
| 33 | if p.lastRun != nil { |
| 34 | res.Processors[i].LastRun = p.lastRun.UnixNano() |
| 35 | res.Processors[i].NextRun = p.p.NextRun(*p.lastRun, p.lastErr != nil).UnixNano() |
| 36 | } |
| 37 | |
| 38 | if p.lastErr != nil { |
| 39 | res.Processors[i].Status = pb.ProcessorStatusResponse_Processor_STATUS_ERROR |
| 40 | } |
| 41 | |
| 42 | i += 1 |
| 43 | } |
| 44 | return res, nil |
| 45 | } |
| 46 | |
| 47 | func (s *service) PeerSummary(req *pb.PeerSummaryRequest, stream pb.Verifier_PeerSummaryServer) error { |
| 48 | peers, err := s.model.GetCheckablePeers(stream.Context()) |
| 49 | if err != nil { |
| 50 | glog.Errorf("model.GetCheckablePeers: %v", err) |
| 51 | return status.Error(codes.Unavailable, "model error") |
| 52 | } |
| 53 | |
| 54 | asns := make([]int64, len(peers)) |
| 55 | asnToRes := make(map[int64]*pb.PeerSummaryResponse) |
| 56 | |
| 57 | for i, peer := range peers { |
| 58 | routers := make([]*pb.PeeringDBMember_Router, len(peer.Routers)) |
| 59 | for i, router := range peer.Routers { |
| 60 | routers[i] = &pb.PeeringDBMember_Router{} |
| 61 | if router.V4 != nil { |
| 62 | routers[i].Ipv4 = router.V4.String() |
| 63 | } |
| 64 | if router.V6 != nil { |
| 65 | routers[i].Ipv6 = router.V6.String() |
| 66 | } |
| 67 | } |
| 68 | p := &pb.PeeringDBMember{ |
| 69 | Asn: peer.ASN, |
| 70 | Name: peer.Name, |
| 71 | Routers: routers, |
| 72 | } |
| 73 | res := &pb.PeerSummaryResponse{ |
| 74 | PeeringdbInfo: p, |
| 75 | CheckStatus: pb.PeerSummaryResponse_STATUS_OK, |
| 76 | } |
| 77 | asnToRes[peer.ASN] = res |
| 78 | asns[i] = peer.ASN |
| 79 | } |
| 80 | |
| 81 | checkres, err := s.model.GetPeerCheckResults(stream.Context(), asns) |
| 82 | if err != nil { |
| 83 | glog.Errorf("GetPeerCheckResults(%v): %v", asns, err) |
| 84 | for _, res := range asnToRes { |
| 85 | res.CheckStatus = pb.PeerSummaryResponse_STATUS_UNKNOWN |
| 86 | } |
| 87 | } else { |
| 88 | passedChecks := make(map[int64]map[string]bool) |
| 89 | for _, c := range checkres { |
| 90 | if _, ok := passedChecks[c.PeerASN]; !ok { |
| 91 | passedChecks[c.PeerASN] = make(map[string]bool) |
| 92 | } |
| 93 | passedChecks[c.PeerASN][c.CheckName] = c.Status == model.PeerCheckStatus_Okay |
| 94 | } |
| 95 | |
| 96 | for asn, checks := range passedChecks { |
| 97 | for _, required := range s.requiredChecks { |
| 98 | if !checks[required] { |
| 99 | asnToRes[asn].CheckStatus = pb.PeerSummaryResponse_STATUS_FAILED |
| 100 | break |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | for _, res := range asnToRes { |
| 107 | if err := stream.Send(res); err != nil { |
| 108 | return err |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | return nil |
| 113 | } |
| 114 | |
| 115 | func (s *service) PeerDetails(ctx context.Context, req *pb.PeerDetailsRequest) (*pb.PeerDetailsResponse, error) { |
| 116 | if req.Asn <= 0 { |
| 117 | return nil, status.Error(codes.InvalidArgument, "asn must be set") |
| 118 | } |
| 119 | |
| 120 | res := &pb.PeerDetailsResponse{} |
| 121 | |
| 122 | peeringdb, err := s.model.GetPeeringDBPeer(ctx, req.Asn) |
| 123 | if err != nil { |
| 124 | glog.Errorf("GetPeeringDBPeer(%v): %v", req.Asn, err) |
| 125 | return nil, status.Error(codes.Unavailable, "could not get allowed prefixes") |
| 126 | } |
| 127 | |
| 128 | if peeringdb.Asn != req.Asn { |
| 129 | return nil, status.Error(codes.NotFound, "no such ASN") |
| 130 | } |
| 131 | |
| 132 | res.PeeringdbInfo = peeringdb |
| 133 | |
| 134 | checkres, err := s.model.GetPeerCheckResults(ctx, []int64{req.Asn}) |
| 135 | if err != nil { |
| 136 | glog.Errorf("GetPeerCheckResults(%v): %v", req.Asn, err) |
| 137 | return nil, status.Error(codes.Unavailable, "could not get check results") |
| 138 | } |
| 139 | |
| 140 | res.Checks = make([]*pb.PeerDetailsResponse_Check, len(checkres)) |
| 141 | for i, check := range checkres { |
| 142 | status := pb.PeerDetailsResponse_Check_STATUS_INVALID |
| 143 | switch check.Status { |
| 144 | case model.PeerCheckStatus_Okay: |
| 145 | status = pb.PeerDetailsResponse_Check_STATUS_OK |
| 146 | case model.PeerCheckStatus_SoftFailed: |
| 147 | status = pb.PeerDetailsResponse_Check_STATUS_OK |
| 148 | case model.PeerCheckStatus_Failed: |
| 149 | status = pb.PeerDetailsResponse_Check_STATUS_FAILED |
| 150 | } |
| 151 | res.Checks[i] = &pb.PeerDetailsResponse_Check{ |
| 152 | Name: check.CheckName, |
| 153 | Status: status, |
| 154 | Time: check.Time.UnixNano(), |
| 155 | Msg: check.Message, |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | prefixes, err := s.model.GetAllowedPrefixes(ctx, req.Asn) |
| 160 | if err != nil { |
| 161 | glog.Errorf("GetAllowedPrefixes(%v): %v", req.Asn, err) |
| 162 | return nil, status.Error(codes.Unavailable, "could not get allowed prefixes") |
| 163 | } |
| 164 | |
| 165 | res.AllowedPrefixes = make([]*pb.PeerDetailsResponse_AllowedPrefix, len(prefixes)) |
| 166 | for i, prefix := range prefixes { |
| 167 | res.AllowedPrefixes[i] = &pb.PeerDetailsResponse_AllowedPrefix{ |
| 168 | Prefix: prefix.Prefix.String(), |
| 169 | MaxLength: prefix.MaxLength, |
| 170 | Ta: prefix.TA, |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | return res, nil |
| 175 | } |
| 176 | |
| 177 | func (s *service) RouterHeartbeat(ctx context.Context, req *pb.RouterHeartbeatRequest) (*pb.RouterHeartbeatResponse, error) { |
| 178 | if req.Name == "" { |
| 179 | return nil, status.Error(codes.InvalidArgument, "name must be set") |
| 180 | } |
| 181 | |
| 182 | pcfgM := make(map[string]*model.PeerConfiguration) |
| 183 | pcfgs, err := s.model.GetPeerConfiguration(ctx) |
| 184 | if err != nil { |
| 185 | glog.Errorf("GetPeerConfiguration: %v", err) |
| 186 | return nil, status.Error(codes.Unavailable, "could not get peer configs") |
| 187 | } |
| 188 | |
| 189 | for _, pcfg := range pcfgs { |
| 190 | ask := fmt.Sprintf("AS%d", pcfg.Peer.ASN) |
| 191 | pcfgM[ask] = pcfg |
| 192 | } |
| 193 | |
| 194 | peers, err := s.model.GetCheckablePeers(ctx) |
| 195 | if err != nil { |
| 196 | glog.Errorf("GetChecablePeers: %v", err) |
| 197 | return nil, status.Error(codes.Unavailable, "could not get peers") |
| 198 | } |
| 199 | |
| 200 | asconfs := make(map[string]*pb.RouterHeartbeatResponse_ASConfig) |
| 201 | |
| 202 | for _, peer := range peers { |
| 203 | as := fmt.Sprintf("AS%d", peer.ASN) |
| 204 | |
| 205 | pcfg, ok := pcfgM[as] |
| 206 | if !ok { |
| 207 | continue |
| 208 | } |
| 209 | |
| 210 | asconfs[as] = &pb.RouterHeartbeatResponse_ASConfig{ |
| 211 | Asn: peer.ASN, |
| 212 | Routers: make([]*pb.RouterHeartbeatResponse_ASConfig_Router, len(pcfg.Peer.Routers)), |
| 213 | Prefixes: []*pb.RouterHeartbeatResponse_ASConfig_AllowedPrefix{}, |
| 214 | } |
| 215 | |
| 216 | glog.Infof("%+v", pcfg.Peer.Routers) |
| 217 | for i, r := range pcfg.Peer.Routers { |
| 218 | ipv6 := "" |
| 219 | if r.V6 != nil { |
| 220 | ipv6 = r.V6.String() |
| 221 | } |
| 222 | ipv4 := "" |
| 223 | if r.V4 != nil { |
| 224 | ipv4 = r.V4.String() |
| 225 | } |
| 226 | asconfs[as].Routers[i] = &pb.RouterHeartbeatResponse_ASConfig_Router{ |
| 227 | Ipv6: ipv6, |
| 228 | Ipv4: ipv4, |
| 229 | Password: r.Config.BGPSecret, |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | prefixes, err := s.model.GetAllowedPrefixes(ctx, peer.ASN) |
| 234 | if err != nil { |
| 235 | glog.Errorf("GetAllowedPrefixes(_, %d): %v", peer.ASN, err) |
| 236 | return nil, status.Error(codes.Unavailable, "could not get peer prefixes") |
| 237 | } |
| 238 | |
| 239 | for _, prefix := range prefixes { |
| 240 | asconfs[as].Prefixes = append(asconfs[as].Prefixes, &pb.RouterHeartbeatResponse_ASConfig_AllowedPrefix{ |
| 241 | Prefix: prefix.Prefix.String(), |
| 242 | MaxLength: prefix.MaxLength, |
| 243 | }) |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | res := &pb.RouterHeartbeatResponse{ |
| 248 | AsConfigs: make([]*pb.RouterHeartbeatResponse_ASConfig, len(asconfs)), |
| 249 | } |
| 250 | |
| 251 | i := 0 |
| 252 | for _, asconf := range asconfs { |
| 253 | res.AsConfigs[i] = asconf |
| 254 | i += 1 |
| 255 | } |
| 256 | |
| 257 | return res, nil |
| 258 | } |
| 259 | |
| 260 | func (s *service) PeerSecrets(ctx context.Context, req *pb.PeerSecretsRequest) (*pb.PeerSecretsResponse, error) { |
| 261 | if req.Asn <= 0 { |
| 262 | return nil, status.Error(codes.InvalidArgument, "asn must be set") |
| 263 | } |
| 264 | pcrs, err := s.model.GetPeerConfiguration(ctx) |
| 265 | if err != nil { |
| 266 | glog.Errorf("GetPeerConfiguration: %v", err) |
| 267 | return nil, status.Error(codes.Unavailable, "error when retrieving peer configs") |
| 268 | } |
| 269 | |
| 270 | var pcr *model.PeerConfiguration |
| 271 | for _, p := range pcrs { |
| 272 | if p.Peer.ASN == req.Asn { |
| 273 | pcr = p |
| 274 | break |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | if pcr == nil { |
| 279 | return nil, status.Error(codes.NotFound, "no such ASN") |
| 280 | } |
| 281 | |
| 282 | plain := fmt.Sprintf(` |
| 283 | Hello AS %d! |
| 284 | |
| 285 | Here are your config settings: |
| 286 | `, req.Asn) |
| 287 | |
| 288 | for _, router := range pcr.Peer.Routers { |
| 289 | if router.V4 != nil { |
| 290 | plain += fmt.Sprintf(` |
Serge Bazanski | ef93747 | 2019-08-29 14:53:18 +0200 | [diff] [blame] | 291 | our addresses: 185.236.243.5 (rs1), 185.236.243.6 (rs2), 185.236.243.7 (upstream) |
Serge Bazanski | ec71cb5 | 2019-08-22 18:13:13 +0200 | [diff] [blame] | 292 | our asn: 208521 |
| 293 | your address: %s |
| 294 | your asn: %d |
| 295 | bgp secret: %s |
| 296 | `, router.V4.String(), req.Asn, router.Config.BGPSecret) |
| 297 | } |
| 298 | if router.V6 != nil { |
| 299 | plain += fmt.Sprintf(` |
Serge Bazanski | ef93747 | 2019-08-29 14:53:18 +0200 | [diff] [blame] | 300 | our addresses: 2a0d:eb02:4242:4242::5 (rs1), 2a0d:eb02:4242:4242::6 (rs2), 2a0d:eb02:4242:4242::7 (upstream) |
Serge Bazanski | ec71cb5 | 2019-08-22 18:13:13 +0200 | [diff] [blame] | 301 | our asn: 208521 |
| 302 | your address: %s |
| 303 | your asn: %d |
| 304 | bgp secret: %s |
| 305 | `, router.V6.String(), req.Asn, router.Config.BGPSecret) |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | plain += ` |
| 310 | Happy exchanging! |
| 311 | bgp.wtf (DECT: 4735) |
| 312 | ` |
| 313 | |
| 314 | key, err := s.model.GetPeerPGPKey(ctx, req.Asn) |
| 315 | if err != nil { |
| 316 | glog.Errorf("GetPeerPGPKey: %v", err) |
| 317 | return nil, status.Error(codes.Unavailable, "could not get pgp key") |
| 318 | } |
| 319 | |
| 320 | plainB := []byte(plain) |
| 321 | |
| 322 | stream, err := s.pgp.Encrypt(ctx) |
| 323 | if err != nil { |
| 324 | glog.Errorf("Encrypt: %v", err) |
| 325 | return nil, status.Error(codes.Unavailable, "could not encrypt") |
| 326 | } |
| 327 | |
| 328 | fingerprint, err := hex.DecodeString(key.Fingerprint) |
| 329 | if err != nil { |
| 330 | glog.Errorf("Invalid fingerprint %q: %v", key.Fingerprint, err) |
| 331 | return nil, status.Error(codes.Unavailable, "could not encrypt") |
| 332 | } |
| 333 | |
| 334 | reqE := &pb.EncryptRequest{ |
| 335 | Data: plainB, |
| 336 | Info: pb.EncryptRequest_CHUNK_LAST, |
| 337 | Fingerprint: fingerprint, |
| 338 | } |
| 339 | |
| 340 | if err := stream.Send(reqE); err != nil { |
| 341 | glog.Errorf("Encrypt.Send: %v", err) |
| 342 | return nil, status.Error(codes.Unavailable, "could not encrypt") |
| 343 | } |
| 344 | stream.CloseSend() |
| 345 | |
| 346 | cipher := []byte{} |
| 347 | for { |
| 348 | in, err := stream.Recv() |
| 349 | if err == io.EOF { |
| 350 | break |
| 351 | } |
| 352 | if err != nil { |
| 353 | glog.Errorf("Encrypt.Recv: %v", err) |
| 354 | return nil, status.Error(codes.Unavailable, "could not encrypt") |
| 355 | } |
| 356 | cipher = append(cipher, in.Data...) |
| 357 | } |
| 358 | |
| 359 | return &pb.PeerSecretsResponse{ |
| 360 | PgpData: cipher, |
| 361 | }, nil |
| 362 | } |