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  }