github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/inttesting/frr/frr.go (about) 1 // Copyright 2017 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package frr implements the "Fake Rapid Response" service. It contains 16 // Fleetspeak modules that can be used to simulate GRR traffic through the 17 // Fleetspeak system for integration and load testing. 18 package frr 19 20 import ( 21 "bytes" 22 "context" 23 "encoding/hex" 24 "fmt" 25 "io" 26 "math/rand" 27 "sync" 28 "time" 29 30 log "github.com/golang/glog" 31 "google.golang.org/grpc" 32 "google.golang.org/grpc/codes" 33 "google.golang.org/grpc/credentials/insecure" 34 anypb "google.golang.org/protobuf/types/known/anypb" 35 36 cservice "github.com/google/fleetspeak/fleetspeak/src/client/service" 37 "github.com/google/fleetspeak/fleetspeak/src/common" 38 "github.com/google/fleetspeak/fleetspeak/src/server/ids" 39 "github.com/google/fleetspeak/fleetspeak/src/server/service" 40 41 fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 42 fgrpc "github.com/google/fleetspeak/fleetspeak/src/inttesting/frr/proto/fleetspeak_frr" 43 fpb "github.com/google/fleetspeak/fleetspeak/src/inttesting/frr/proto/fleetspeak_frr" 44 sgrpc "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server" 45 srpb "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server" 46 ) 47 48 const retryDelay = 15 * time.Second 49 50 // ClientServiceFactory is a client.ServiceFactory which produces a frr client 51 // component. 52 func ClientServiceFactory(conf *fspb.ClientServiceConfig) (cservice.Service, error) { 53 return &frrClientService{}, nil 54 } 55 56 type frrClientService struct { 57 sc cservice.Context 58 w sync.WaitGroup 59 done chan struct{} 60 ctx context.Context 61 cancel context.CancelFunc 62 } 63 64 func (s *frrClientService) Start(sc cservice.Context) error { 65 s.sc = sc 66 s.done = make(chan struct{}) 67 return nil 68 } 69 70 func (s *frrClientService) ProcessMessage(ctx context.Context, m *fspb.Message) error { 71 // Currently, all messages require data. 72 if m.Data == nil { 73 log.Fatalf("Received message with nil Data: %v", m) 74 } 75 76 switch m.MessageType { 77 case "TrafficRequest": 78 return s.processTrafficRequest(m) 79 case "FileRequest": 80 return s.processFileRequest(ctx, m) 81 default: 82 return fmt.Errorf("unknown message_type: %v", m.MessageType) 83 } 84 } 85 86 func (s *frrClientService) processTrafficRequest(m *fspb.Message) error { 87 rd := &fpb.TrafficRequestData{} 88 if err := m.Data.UnmarshalTo(rd); err != nil { 89 return fmt.Errorf("unable to parse data as TrafficRequestData: %v", err) 90 } 91 if rd.NumMessages == 0 { 92 rd.NumMessages = 1 93 } 94 if rd.MessageSize == 0 { 95 rd.MessageSize = 1024 96 } 97 s.w.Add(1) 98 dataBuf := bytes.Repeat([]byte{42}, int(float32(rd.MessageSize)*(1.0+rd.Jitter))+1) 99 go func() { 100 defer s.w.Done() 101 102 cnt := jitter(rd.NumMessages, rd.Jitter) 103 log.V(1).Infof("%v: creating %v responses for request %v", s.sc.GetLocalInfo().ClientID, cnt, rd.RequestId) 104 for i := int64(0); i < cnt; i++ { 105 delay := time.Millisecond * time.Duration(jitter(rd.MessageDelayMs, rd.Jitter)) 106 t := time.NewTimer(delay) 107 select { 108 case <-s.done: 109 return 110 case <-t.C: 111 } 112 113 res := &fpb.TrafficResponseData{ 114 MasterId: rd.MasterId, 115 RequestId: rd.RequestId, 116 ResponseIndex: i, 117 Data: dataBuf[:jitter(rd.MessageSize, rd.Jitter)], 118 Fin: i == cnt-1, 119 } 120 d, err := anypb.New(res) 121 if err != nil { 122 log.Fatalf("Failed to marshal TrafficResponseData: %v", err) 123 } 124 m := &fspb.Message{ 125 Destination: &fspb.Address{ServiceName: "FRR"}, 126 Data: d, 127 MessageType: "TrafficResponse", 128 } 129 for { 130 ctx, c := context.WithTimeout(context.Background(), time.Second) 131 err := s.sc.Send(ctx, cservice.AckMessage{M: m}) 132 c() 133 if err == nil { 134 break 135 } 136 select { 137 case <-s.done: 138 return 139 default: 140 } 141 if ctx.Err() == nil { 142 log.Fatalf("Unexpected error sending message: %v", err) 143 } 144 } 145 } 146 }() 147 return nil 148 } 149 150 func (s *frrClientService) processFileRequest(ctx context.Context, m *fspb.Message) error { 151 rd := &fpb.FileRequestData{} 152 if err := m.Data.UnmarshalTo(rd); err != nil { 153 return fmt.Errorf("unable to parse data as TrafficRequestData: %v", err) 154 } 155 data, _, err := s.sc.GetFileIfModified(ctx, rd.Name, time.Time{}) 156 if err != nil { 157 return fmt.Errorf("unable to get file [%v]: %v", rd.Name, err) 158 } 159 defer data.Close() 160 b, err := io.ReadAll(data) 161 if err != nil { 162 return fmt.Errorf("unable to read file body [%v]: %v", rd.Name, err) 163 } 164 res := &fpb.FileResponseData{ 165 MasterId: rd.MasterId, 166 Name: rd.Name, 167 Size: uint64(len(b)), 168 } 169 d, err := anypb.New(res) 170 if err != nil { 171 log.Fatalf("Failed to marshal FileResponseData: %v", err) 172 } 173 return s.sc.Send(ctx, cservice.AckMessage{M: &fspb.Message{ 174 Destination: &fspb.Address{ServiceName: "FRR"}, 175 Data: d, 176 MessageType: "FileResponse", 177 }}) 178 } 179 180 func (s *frrClientService) Stop() error { 181 close(s.done) 182 s.w.Wait() 183 return nil 184 } 185 186 func jitter(base int64, j float32) int64 { 187 if j == 0.0 || base == 0 { 188 return base 189 } 190 return int64(float64(base) * (1.0 + rand.Float64()*float64(j))) 191 } 192 193 // DefaultFRRMaster sets a default connection to the FRR master server. This 194 // default will be used by the FRR ServerService if a configuration is not 195 // provided. It exists to allow special connection types. 196 var DefaultFRRMaster fgrpc.MasterClient 197 198 type frrServerService struct { 199 conn *grpc.ClientConn 200 m fgrpc.MasterClient 201 sc service.Context 202 } 203 204 // ServerServiceFactory is a server.ServiceFactory which produces a FRR server 205 // component. This component receives messages from clients and forwards them 206 // (via grpc calls) to a MasterServer. 207 func ServerServiceFactory(sc *srpb.ServiceConfig) (service.Service, error) { 208 var r frrServerService 209 210 if sc.Config == nil && DefaultFRRMaster == nil { 211 return nil, fmt.Errorf("FRR server component requires a Config attribute, got: %v", sc) 212 } 213 if sc.Config == nil { 214 r.m = DefaultFRRMaster 215 } else { 216 c := &fpb.Config{} 217 if err := sc.Config.UnmarshalTo(c); err != nil { 218 return nil, fmt.Errorf("Unable to parse Config attribute as frr.Config: %v", err) 219 } 220 conn, err := grpc.Dial(c.MasterServer, grpc.WithTransportCredentials(insecure.NewCredentials())) 221 if err != nil { 222 return nil, fmt.Errorf("Unable to connect to master server[%v]: %v", c.MasterServer, err) 223 } 224 r.conn = conn 225 r.m = fgrpc.NewMasterClient(conn) 226 } 227 228 return &r, nil 229 } 230 231 func (s *frrServerService) Start(sc service.Context) error { 232 s.sc = sc 233 return nil 234 } 235 236 func (s *frrServerService) Stop() error { 237 if s.conn != nil { 238 return s.conn.Close() 239 } 240 return nil 241 } 242 243 func (s *frrServerService) ProcessMessage(ctx context.Context, m *fspb.Message) error { 244 // This is an integration/performance testing tool. Just fail hard if we 245 // get really bad data. 246 switch m.MessageType { 247 case "TrafficResponse": 248 rd := &fpb.TrafficResponseData{} 249 if err := m.Data.UnmarshalTo(rd); err != nil { 250 log.Fatalf("Unable to parse data as TrafficResponseData: %v", err) 251 } 252 // Zero the data field - save bandwidth and cpu communicating with master. 253 rd.Data = nil 254 if _, err := s.m.RecordTrafficResponse(ctx, &fpb.MessageInfo{ClientId: m.Source.ClientId, Data: rd}); err != nil { 255 return service.TemporaryError{E: fmt.Errorf("failed to reach FRR master server: %v", err)} 256 } 257 case "FileResponse": 258 rd := &fpb.FileResponseData{} 259 if err := m.Data.UnmarshalTo(rd); err != nil { 260 log.Fatalf("Unable to parse data as FileResponseData: %v", err) 261 } 262 if _, err := s.m.RecordFileResponse(ctx, &fpb.FileResponseInfo{ClientId: m.Source.ClientId, Data: rd}); err != nil { 263 return service.TemporaryError{E: fmt.Errorf("failed to reach FRR master server: %v", err)} 264 } 265 default: 266 log.Fatalf("Unknown message type: [%v]", m.MessageType) 267 } 268 269 return nil 270 } 271 272 // A MasterServer implements fgrpc.MasterServer which records (in ram) 273 // metadata about received messages. It also provides methods to examine this 274 // metadata, check it for consistency and trigger FRR operations. 275 type MasterServer struct { 276 fgrpc.UnimplementedMasterServer 277 278 clients map[common.ClientID]*clientInfo 279 lock sync.RWMutex // protects clients 280 completed chan common.ClientID 281 admin sgrpc.AdminClient 282 masterID int64 283 } 284 285 // NewMasterServer returns MasterServer object. 286 func NewMasterServer(admin sgrpc.AdminClient) *MasterServer { 287 id := rand.Int63() 288 log.Infof("Creating master server with id: %v", id) 289 return &MasterServer{ 290 clients: make(map[common.ClientID]*clientInfo), 291 completed: nil, 292 admin: admin, 293 masterID: id, 294 } 295 } 296 297 type clientInfo struct { 298 requests map[int64]*requestInfo 299 fileDownloads map[string]uint64 300 lock sync.Mutex // protects everything in clientInfo 301 } 302 303 type requestInfo struct { 304 responses map[int64]bool 305 fin int64 306 } 307 308 func (i *requestInfo) completed() bool { 309 return i.fin != -1 && len(i.responses) == int(i.fin+1) 310 } 311 312 func (s *MasterServer) getClientInfo(id common.ClientID) *clientInfo { 313 s.lock.RLock() 314 ci := s.clients[id] 315 s.lock.RUnlock() 316 317 if ci == nil { 318 s.lock.Lock() 319 ci = s.clients[id] 320 if ci == nil { 321 ci = &clientInfo{ 322 requests: make(map[int64]*requestInfo), 323 fileDownloads: make(map[string]uint64), 324 } 325 s.clients[id] = ci 326 } 327 s.lock.Unlock() 328 } 329 return ci 330 } 331 332 // RecordTrafficResponse implements fgrpc.MasterServer and records that the message 333 // was received. 334 func (s *MasterServer) RecordTrafficResponse(ctx context.Context, i *fpb.MessageInfo) (*fspb.EmptyMessage, error) { 335 id, err := common.BytesToClientID(i.ClientId) 336 if err != nil { 337 log.Fatalf("Received message with invalid ClientId[%v]: %v", i.ClientId, err) 338 } 339 if i.Data == nil { 340 log.Fatalf("Received MessageInfo without Data") 341 } 342 if i.Data.MasterId != s.masterID { 343 return &fspb.EmptyMessage{}, nil 344 } 345 log.V(2).Infof("%v: processing message: %v, %v, %v", id, i.Data.RequestId, i.Data.ResponseIndex, i.Data.Fin) 346 347 ci := s.getClientInfo(id) 348 ci.lock.Lock() 349 defer ci.lock.Unlock() 350 351 ri := ci.requests[i.Data.RequestId] 352 if ri == nil { 353 ri = &requestInfo{responses: make(map[int64]bool), fin: -1} 354 ci.requests[i.Data.RequestId] = ri 355 } 356 ri.responses[i.Data.ResponseIndex] = true 357 if i.Data.Fin { 358 ri.fin = i.Data.ResponseIndex 359 } 360 361 if ri.completed() { 362 log.V(1).Infof("%v: completed request %v", id, i.Data.RequestId) 363 if s.completed != nil { 364 select { 365 case <-ctx.Done(): 366 return nil, ctx.Err() 367 case s.completed <- id: 368 } 369 } 370 } 371 372 return &fspb.EmptyMessage{}, nil 373 } 374 375 // RecordFileResponse implements fgrpc.MasterServer and records that the message 376 // was received. 377 func (s *MasterServer) RecordFileResponse(ctx context.Context, i *fpb.FileResponseInfo) (*fspb.EmptyMessage, error) { 378 id, err := common.BytesToClientID(i.ClientId) 379 if err != nil { 380 log.Fatalf("Received message with invalid ClientId[%v]: %v", i.ClientId, err) 381 } 382 if i.Data == nil { 383 log.Fatalf("Received FileResponseInfo without Data") 384 } 385 if i.Data.MasterId != s.masterID { 386 return &fspb.EmptyMessage{}, nil 387 } 388 if i.Data.Name == "" { 389 log.Fatalf("Received FileResponseInfo without Name") 390 } 391 392 ci := s.getClientInfo(id) 393 ci.lock.Lock() 394 defer ci.lock.Unlock() 395 396 ci.fileDownloads[i.Data.Name] = i.Data.Size 397 if s.completed != nil { 398 s.completed <- id 399 } 400 return &fspb.EmptyMessage{}, nil 401 } 402 403 // SetAdminClient changes the stub that is used to contact the FS server. 404 func (s *MasterServer) SetAdminClient(admin sgrpc.AdminClient) { 405 s.admin = admin 406 } 407 408 // AllRequests returns a list of all requests for the client which we've 409 // received any data for. 410 func (s *MasterServer) AllRequests(id common.ClientID) []int64 { 411 var r []int64 412 413 s.lock.RLock() 414 ci := s.clients[id] 415 s.lock.RUnlock() 416 417 if ci == nil { 418 return r 419 } 420 421 ci.lock.Lock() 422 defer ci.lock.Unlock() 423 424 for id := range ci.requests { 425 r = append(r, id) 426 } 427 return r 428 } 429 430 // GetCompletedRequests returns a list of requests made to a client which have been 431 // completed. 432 func (s *MasterServer) GetCompletedRequests(id common.ClientID) []int64 { 433 var r []int64 434 435 s.lock.RLock() 436 ci := s.clients[id] 437 s.lock.RUnlock() 438 439 if ci == nil { 440 return r 441 } 442 443 ci.lock.Lock() 444 defer ci.lock.Unlock() 445 446 for id, info := range ci.requests { 447 if info.completed() { 448 r = append(r, id) 449 } 450 } 451 return r 452 } 453 454 // CompletedRequests implements fgrpc.MasterServer and returns a list of 455 // requests made to a client which have been completed. 456 func (s *MasterServer) CompletedRequests(ctx context.Context, id *fpb.CompletedRequestsRequest) (*fpb.CompletedRequestsResponse, error) { 457 var r fpb.CompletedRequestsResponse 458 459 cid, err := common.StringToClientID(id.ClientId) 460 if err != nil { 461 return &r, err 462 } 463 464 r.RequestIds = s.GetCompletedRequests(cid) 465 return &r, nil 466 } 467 468 // WatchCompleted creates, and returns a channel which notifies when a request 469 // to a client is completed. Repeated calls return the same channel. Should only 470 // be called before the server is exported. (i.e. when it is idle) 471 func (s *MasterServer) WatchCompleted() <-chan common.ClientID { 472 if s.completed == nil { 473 // Use a large capacity - ids are small and we want to minimize 474 // any blocking caused by instrumentation. 475 s.completed = make(chan common.ClientID, 1000) 476 } 477 return s.completed 478 } 479 480 // CreateBroadcastRequest initiates a hunt which sends the provided TrafficRequestData to 481 // every client, up to limit. 482 func (s *MasterServer) CreateBroadcastRequest(ctx context.Context, rd *fpb.TrafficRequestData, limit uint64) error { 483 rd.MasterId = s.masterID 484 d, err := anypb.New(rd) 485 if err != nil { 486 return fmt.Errorf("unable to marshal TrafficRequestData: %v", err) 487 } 488 bid, err := ids.RandomBroadcastID() 489 if err != nil { 490 return fmt.Errorf("unable to create BroadcastID: %v", err) 491 } 492 req := srpb.CreateBroadcastRequest{ 493 Broadcast: &srpb.Broadcast{ 494 BroadcastId: bid.Bytes(), 495 Source: &fspb.Address{ServiceName: "FRR"}, 496 MessageType: "TrafficRequest", 497 Data: d, 498 }, 499 Limit: limit, 500 } 501 for { 502 _, err := s.admin.CreateBroadcast(ctx, &req) 503 if err == nil { 504 break 505 } 506 if grpc.Code(err) == codes.Unavailable { 507 log.Warningf("FS server unavailable, retrying in %v", retryDelay) 508 time.Sleep(retryDelay) 509 continue 510 } 511 return fmt.Errorf("CreateBroadcast(%v) failed: %v", req.String(), err) 512 } 513 return nil 514 } 515 516 func (s *MasterServer) createUnicastRequest(ctx context.Context, rd *fpb.TrafficRequestData, clientIDs []string) error { 517 rd.MasterId = s.masterID 518 dat, err := anypb.New(rd) 519 if err != nil { 520 return fmt.Errorf("unable to marshal TrafficRequestData: %v", err) 521 } 522 523 for _, cid := range clientIDs { 524 bcid, err := hex.DecodeString(cid) 525 if err != nil { 526 return fmt.Errorf("failed to decode client id: %v", err) 527 } 528 m := &fspb.Message{ 529 Destination: &fspb.Address{ServiceName: "FRR", ClientId: bcid}, 530 Source: &fspb.Address{ServiceName: "FRR"}, 531 Data: dat, 532 MessageType: "TrafficRequest", 533 } 534 _, err = s.admin.InsertMessage(ctx, m) 535 if err != nil { 536 return fmt.Errorf("failed to Insert message: %v", err) 537 } 538 } 539 return nil 540 } 541 542 // CreateHunt implements fgrpc.MasterServer and initiates a hunt which sends the provided 543 // TrafficRequestData to provided clients, or to every client, up to limit, if ClientIds is empty 544 func (s *MasterServer) CreateHunt(ctx context.Context, hr *fpb.CreateHuntRequest) (*fpb.CreateHuntResponse, error) { 545 if len(hr.ClientIds) == 0 { 546 return &fpb.CreateHuntResponse{}, s.CreateBroadcastRequest(ctx, hr.Data, hr.Limit) 547 } 548 return &fpb.CreateHuntResponse{}, s.createUnicastRequest(ctx, hr.Data, hr.ClientIds) 549 } 550 551 // CreateFileDownloadHunt initiates a hunt which requests that up to limit clients download 552 // the file identified by name. 553 func (s *MasterServer) CreateFileDownloadHunt(ctx context.Context, name string, limit uint64) error { 554 rd := &fpb.FileRequestData{ 555 MasterId: s.masterID, 556 Name: name, 557 } 558 d, err := anypb.New(rd) 559 if err != nil { 560 return fmt.Errorf("unable to marshal FileRequestData: %v", err) 561 } 562 563 bid, err := ids.RandomBroadcastID() 564 if err != nil { 565 return fmt.Errorf("unable to create BroadcastID: %v", err) 566 } 567 req := srpb.CreateBroadcastRequest{ 568 Broadcast: &srpb.Broadcast{ 569 BroadcastId: bid.Bytes(), 570 Source: &fspb.Address{ServiceName: "FRR"}, 571 MessageType: "FileRequest", 572 Data: d, 573 }, 574 Limit: limit, 575 } 576 for { 577 _, err := s.admin.CreateBroadcast(ctx, &req) 578 if err == nil { 579 break 580 } 581 if grpc.Code(err) == codes.Unavailable { 582 log.Warningf("FS server unavailable, retrying in %v", retryDelay) 583 time.Sleep(retryDelay) 584 continue 585 } 586 return fmt.Errorf("CreateBroadcast(%v) failed: %v", req.String(), err) 587 } 588 return nil 589 }